mirror of
https://github.com/UltimMC/Launcher.git
synced 2025-12-13 12:12:14 +00:00
Compare commits
1 Commits
feature/bu
...
feature/li
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bdf63b0ee |
@@ -32,7 +32,7 @@ set(CMAKE_C_STANDARD_REQUIRED true)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
include(GenerateExportHeader)
|
||||
set(CMAKE_CXX_FLAGS " -Wall -pedantic -Werror -Wno-deprecated-declarations -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 -O3 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS " -Wall -pedantic -Werror -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 -O3 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS}")
|
||||
if(UNIX AND APPLE)
|
||||
set(CMAKE_CXX_FLAGS " -stdlib=libc++ ${CMAKE_CXX_FLAGS}")
|
||||
endif()
|
||||
@@ -46,7 +46,7 @@ set(MultiMC_NEWS_RSS_URL "https://multimc.org/rss.xml" CACHE STRING "URL to fetc
|
||||
######## Set version numbers ########
|
||||
set(MultiMC_VERSION_MAJOR 0)
|
||||
set(MultiMC_VERSION_MINOR 6)
|
||||
set(MultiMC_VERSION_HOTFIX 6)
|
||||
set(MultiMC_VERSION_HOTFIX 5)
|
||||
|
||||
# Build number
|
||||
set(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
|
||||
|
||||
@@ -443,8 +443,6 @@ set(FLAME_SOURCES
|
||||
modplatform/flame/PackManifest.cpp
|
||||
modplatform/flame/FileResolvingTask.h
|
||||
modplatform/flame/FileResolvingTask.cpp
|
||||
modplatform/flame/UrlResolvingTask.h
|
||||
modplatform/flame/UrlResolvingTask.cpp
|
||||
)
|
||||
|
||||
add_unit_test(Index
|
||||
|
||||
@@ -304,6 +304,10 @@ QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
|
||||
{
|
||||
string[i] = replaceWith;
|
||||
}
|
||||
else if(string[i] > 127) // non ASCII
|
||||
{
|
||||
string[i] = replaceWith;
|
||||
}
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ void InstanceList::deleteInstance(const InstanceId& id)
|
||||
auto inst = getInstanceById(id);
|
||||
if(!inst)
|
||||
{
|
||||
qDebug() << "Cannot delete instance" << id << ". No such instance is present (deleted externally?).";
|
||||
qDebug() << "Cannot delete instance" << id << " No such instance is present.";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -819,7 +819,6 @@ bool InstanceList::commitStagedInstance(const QString& path, const QString& inst
|
||||
instanceSet.insert(instID);
|
||||
m_groups.insert(groupName);
|
||||
emit instancesChanged();
|
||||
emit instanceSelectRequest(instID);
|
||||
}
|
||||
saveGroupList();
|
||||
return true;
|
||||
|
||||
@@ -129,7 +129,6 @@ public:
|
||||
signals:
|
||||
void dataIsInvalid();
|
||||
void instancesChanged();
|
||||
void instanceSelectRequest(QString instanceId);
|
||||
void groupsChanged(QSet<QString> groups);
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -635,9 +635,6 @@ void ComponentList::componentDataChanged()
|
||||
qWarning() << "ComponentList got dataChenged signal from a non-Component!";
|
||||
return;
|
||||
}
|
||||
if(objPtr->getID() == "net.minecraft") {
|
||||
emit minecraftChanged();
|
||||
}
|
||||
// figure out which one is it... in a seriously dumb way.
|
||||
int index = 0;
|
||||
for (auto component: d->components)
|
||||
|
||||
@@ -104,9 +104,6 @@ public:
|
||||
/// if there is a save scheduled, do it now.
|
||||
void saveNow();
|
||||
|
||||
signals:
|
||||
void minecraftChanged();
|
||||
|
||||
public:
|
||||
/// get the profile component by id
|
||||
Component * getComponent(const QString &id);
|
||||
|
||||
@@ -648,7 +648,8 @@ QMap<QString, QString> MinecraftInstance::createCensorFilterFromSession(AuthSess
|
||||
auto i = sessionRef.u.properties.begin();
|
||||
while (i != sessionRef.u.properties.end())
|
||||
{
|
||||
if(i.value().length() <= 3) {
|
||||
if(i.key() == "preferredLanguage")
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -113,6 +113,9 @@ public:
|
||||
|
||||
virtual JavaVersion getJavaVersion() const;
|
||||
|
||||
signals:
|
||||
void versionReloaded();
|
||||
|
||||
protected:
|
||||
QMap<QString, QString> createCensorFilterFromSession(AuthSessionPtr session);
|
||||
QStringList validLaunchMethods();
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#include "FileResolvingTask.h"
|
||||
#include "Json.h"
|
||||
|
||||
namespace {
|
||||
const char * metabase = "https://cursemeta.dries007.net";
|
||||
}
|
||||
const char * metabase = "https://cursemeta.dries007.net";
|
||||
|
||||
Flame::FileResolvingTask::FileResolvingTask(Flame::Manifest& toProcess)
|
||||
: m_toProcess(toProcess)
|
||||
@@ -36,14 +34,70 @@ void Flame::FileResolvingTask::netJobFinished()
|
||||
int index = 0;
|
||||
for(auto & bytes: results)
|
||||
{
|
||||
auto & out = m_toProcess.files[index];
|
||||
try
|
||||
{
|
||||
failed &= (!out.parseFromBytes(bytes));
|
||||
auto doc = Json::requireDocument(bytes);
|
||||
auto obj = Json::requireObject(doc);
|
||||
auto & out = m_toProcess.files[index];
|
||||
// result code signifies true failure.
|
||||
if(obj.contains("code"))
|
||||
{
|
||||
qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of a negative result:";
|
||||
qCritical() << bytes;
|
||||
failed = true;
|
||||
continue;
|
||||
}
|
||||
out.fileName = Json::requireString(obj, "FileNameOnDisk");
|
||||
QString rawUrl = Json::requireString(obj, "DownloadURL");
|
||||
out.url = QUrl(rawUrl, QUrl::TolerantMode);
|
||||
if(!out.url.isValid())
|
||||
{
|
||||
throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl));
|
||||
}
|
||||
// This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience
|
||||
// It is also optional
|
||||
QJsonObject projObj = Json::ensureObject(obj, "_Project", {});
|
||||
if(!projObj.isEmpty())
|
||||
{
|
||||
QString strType = Json::ensureString(projObj, "PackageType", "mod").toLower();
|
||||
if(strType == "singlefile")
|
||||
{
|
||||
out.type = File::Type::SingleFile;
|
||||
}
|
||||
else if(strType == "ctoc")
|
||||
{
|
||||
out.type = File::Type::Ctoc;
|
||||
}
|
||||
else if(strType == "cmod2")
|
||||
{
|
||||
out.type = File::Type::Cmod2;
|
||||
}
|
||||
else if(strType == "mod")
|
||||
{
|
||||
out.type = File::Type::Mod;
|
||||
}
|
||||
else if(strType == "folder")
|
||||
{
|
||||
out.type = File::Type::Folder;
|
||||
}
|
||||
else if(strType == "modpack")
|
||||
{
|
||||
out.type = File::Type::Modpack;
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of unknown file type:" << strType;
|
||||
out.type = File::Type::Unknown;
|
||||
failed = true;
|
||||
continue;
|
||||
}
|
||||
out.targetFolder = Json::ensureString(projObj, "Path", "mods");
|
||||
}
|
||||
out.resolved = true;
|
||||
}
|
||||
catch (const JSONValidationError &e)
|
||||
{
|
||||
|
||||
auto & out = m_toProcess.files[index];
|
||||
qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of a parsing error:";
|
||||
qCritical() << e.cause();
|
||||
qCritical() << "JSON:";
|
||||
|
||||
@@ -64,63 +64,3 @@ void Flame::loadManifest(Flame::Manifest & m, const QString &filepath)
|
||||
}
|
||||
loadManifestV1(m, obj);
|
||||
}
|
||||
|
||||
bool Flame::File::parseFromBytes(const QByteArray& bytes)
|
||||
{
|
||||
auto doc = Json::requireDocument(bytes);
|
||||
auto obj = Json::requireObject(doc);
|
||||
// result code signifies true failure.
|
||||
if(obj.contains("code"))
|
||||
{
|
||||
qCritical() << "Resolving of" << projectId << fileId << "failed because of a negative result:";
|
||||
qCritical() << bytes;
|
||||
return false;
|
||||
}
|
||||
fileName = Json::requireString(obj, "FileNameOnDisk");
|
||||
QString rawUrl = Json::requireString(obj, "DownloadURL");
|
||||
url = QUrl(rawUrl, QUrl::TolerantMode);
|
||||
if(!url.isValid())
|
||||
{
|
||||
throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl));
|
||||
}
|
||||
// This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience
|
||||
// It is also optional
|
||||
QJsonObject projObj = Json::ensureObject(obj, "_Project", {});
|
||||
if(!projObj.isEmpty())
|
||||
{
|
||||
QString strType = Json::ensureString(projObj, "PackageType", "mod").toLower();
|
||||
if(strType == "singlefile")
|
||||
{
|
||||
type = File::Type::SingleFile;
|
||||
}
|
||||
else if(strType == "ctoc")
|
||||
{
|
||||
type = File::Type::Ctoc;
|
||||
}
|
||||
else if(strType == "cmod2")
|
||||
{
|
||||
type = File::Type::Cmod2;
|
||||
}
|
||||
else if(strType == "mod")
|
||||
{
|
||||
type = File::Type::Mod;
|
||||
}
|
||||
else if(strType == "folder")
|
||||
{
|
||||
type = File::Type::Folder;
|
||||
}
|
||||
else if(strType == "modpack")
|
||||
{
|
||||
type = File::Type::Modpack;
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical() << "Resolving of" << projectId << fileId << "failed because of unknown file type:" << strType;
|
||||
type = File::Type::Unknown;
|
||||
return false;
|
||||
}
|
||||
targetFolder = Json::ensureString(projObj, "Path", "mods");
|
||||
}
|
||||
resolved = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,9 +8,6 @@ namespace Flame
|
||||
{
|
||||
struct File
|
||||
{
|
||||
// NOTE: throws JSONValidationError
|
||||
bool parseFromBytes(const QByteArray &bytes);
|
||||
|
||||
int projectId = 0;
|
||||
int fileId = 0;
|
||||
// NOTE: the opposite to 'optional'. This is at the time of writing unused.
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
#include "UrlResolvingTask.h"
|
||||
#include <QtXml>
|
||||
#include <Json.h>
|
||||
|
||||
|
||||
namespace {
|
||||
const char * metabase = "https://cursemeta.dries007.net";
|
||||
}
|
||||
|
||||
Flame::UrlResolvingTask::UrlResolvingTask(const QString& toProcess)
|
||||
: m_url(toProcess)
|
||||
{
|
||||
}
|
||||
|
||||
void Flame::UrlResolvingTask::executeTask()
|
||||
{
|
||||
resolveUrl();
|
||||
}
|
||||
|
||||
void Flame::UrlResolvingTask::resolveUrl()
|
||||
{
|
||||
setStatus(tr("Resolving URL..."));
|
||||
setProgress(0, 1);
|
||||
QUrl actualUrl(m_url);
|
||||
if(actualUrl.host() != "www.curseforge.com") {
|
||||
emitFailed(tr("Not a Twitch URL."));
|
||||
return;
|
||||
}
|
||||
m_dljob.reset(new NetJob("URL resolver"));
|
||||
|
||||
bool weAreDigging = false;
|
||||
needle = QString();
|
||||
|
||||
if(m_url.startsWith("https://")) {
|
||||
if(m_url.endsWith("?client=y")) {
|
||||
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download?client=y
|
||||
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088?client=y
|
||||
m_url.chop(9);
|
||||
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download
|
||||
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088
|
||||
}
|
||||
if(m_url.endsWith("/download")) {
|
||||
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download -> need to dig inside html...
|
||||
weAreDigging = true;
|
||||
needle = m_url;
|
||||
needle.replace("https://", "twitch://");
|
||||
needle.replace("/download", "/download-client/");
|
||||
m_url.append("?client=y");
|
||||
} else if (m_url.contains("/download/")) {
|
||||
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088
|
||||
m_url.replace("/download/", "/download-client/");
|
||||
}
|
||||
}
|
||||
else if(m_url.startsWith("twitch://")) {
|
||||
// twitch://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088
|
||||
m_url.replace(0, 9, "https://");
|
||||
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088
|
||||
}
|
||||
auto dl = Net::Download::makeByteArray(QUrl(m_url), &results);
|
||||
m_dljob->addNetAction(dl);
|
||||
if(weAreDigging) {
|
||||
connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processHTML);
|
||||
} else {
|
||||
connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processCCIP);
|
||||
}
|
||||
m_dljob->start();
|
||||
}
|
||||
|
||||
void Flame::UrlResolvingTask::processHTML()
|
||||
{
|
||||
QString htmlDoc = QString::fromUtf8(results);
|
||||
auto index = htmlDoc.indexOf(needle);
|
||||
if(index < 0) {
|
||||
emitFailed(tr("Couldn't find the needle in the haystack..."));
|
||||
return;
|
||||
}
|
||||
auto indexStart = index;
|
||||
int indexEnd = -1;
|
||||
while((index + 1) < htmlDoc.size() && htmlDoc[index] != '"') {
|
||||
index ++;
|
||||
if(htmlDoc[index] == '"') {
|
||||
indexEnd = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(indexEnd > 0) {
|
||||
QString found = htmlDoc.mid(indexStart, indexEnd - indexStart);
|
||||
qDebug() << "Found needle: " << found;
|
||||
// twitch://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088
|
||||
m_url = found;
|
||||
resolveUrl();
|
||||
return;
|
||||
}
|
||||
emitFailed(tr("Couldn't find the end of the needle in the haystack..."));
|
||||
return;
|
||||
}
|
||||
|
||||
void Flame::UrlResolvingTask::processCCIP()
|
||||
{
|
||||
QDomDocument doc;
|
||||
if (!doc.setContent(results)) {
|
||||
qDebug() << results;
|
||||
emitFailed(tr("Resolving failed."));
|
||||
return;
|
||||
}
|
||||
auto packageNode = doc.namedItem("package");
|
||||
if(!packageNode.isElement()) {
|
||||
emitFailed(tr("Resolving failed: missing package root element."));
|
||||
return;
|
||||
}
|
||||
auto projectNode = packageNode.namedItem("project");
|
||||
if(!projectNode.isElement()) {
|
||||
emitFailed(tr("Resolving failed: missing project element."));
|
||||
return;
|
||||
}
|
||||
auto attribs = projectNode.attributes();
|
||||
|
||||
auto projectIdNode = attribs.namedItem("id");
|
||||
if(!projectIdNode.isAttr()) {
|
||||
emitFailed(tr("Resolving failed: missing id attribute."));
|
||||
return;
|
||||
}
|
||||
auto fileIdNode = attribs.namedItem("file");
|
||||
if(!fileIdNode.isAttr()) {
|
||||
emitFailed(tr("Resolving failed: missing file attribute."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto projectId = projectIdNode.nodeValue();
|
||||
auto fileId = fileIdNode.nodeValue();
|
||||
bool success = true;
|
||||
m_result.projectId = projectId.toInt(&success);
|
||||
if(!success) {
|
||||
emitFailed(tr("Failed to resove projectId as a number."));
|
||||
return;
|
||||
}
|
||||
m_result.fileId = fileId.toInt(&success);
|
||||
if(!success) {
|
||||
emitFailed(tr("Failed to resove fileId as a number."));
|
||||
return;
|
||||
}
|
||||
qDebug() << "Resolved" << m_url << "as" << m_result.projectId << "/" << m_result.fileId;
|
||||
resolveIDs();
|
||||
}
|
||||
|
||||
void Flame::UrlResolvingTask::resolveIDs()
|
||||
{
|
||||
setStatus(tr("Resolving mod IDs..."));
|
||||
m_dljob.reset(new NetJob("Mod id resolver"));
|
||||
auto projectIdStr = QString::number(m_result.projectId);
|
||||
auto fileIdStr = QString::number(m_result.fileId);
|
||||
QString metaurl = QString("%1/%2/%3.json").arg(metabase, projectIdStr, fileIdStr);
|
||||
auto dl = Net::Download::makeByteArray(QUrl(metaurl), &results);
|
||||
m_dljob->addNetAction(dl);
|
||||
connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processCursemeta);
|
||||
m_dljob->start();
|
||||
}
|
||||
|
||||
void Flame::UrlResolvingTask::processCursemeta()
|
||||
{
|
||||
try {
|
||||
if(m_result.parseFromBytes(results)) {
|
||||
emitSucceeded();
|
||||
qDebug() << results;
|
||||
return;
|
||||
}
|
||||
} catch (const JSONValidationError &e) {
|
||||
|
||||
qCritical() << "Resolving of" << m_result.projectId << m_result.fileId << "failed because of a parsing error:";
|
||||
qCritical() << e.cause();
|
||||
qCritical() << "JSON:";
|
||||
qCritical() << results;
|
||||
}
|
||||
emitFailed(tr("Failed to resolve the modpack file."));
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "tasks/Task.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "PackManifest.h"
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
namespace Flame
|
||||
{
|
||||
class MULTIMC_LOGIC_EXPORT UrlResolvingTask : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit UrlResolvingTask(const QString &toProcess);
|
||||
virtual ~UrlResolvingTask() {};
|
||||
|
||||
const Flame::File &getResults() const
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override;
|
||||
|
||||
protected slots:
|
||||
void processCCIP();
|
||||
void processHTML();
|
||||
void processCursemeta();
|
||||
|
||||
private:
|
||||
void resolveUrl();
|
||||
void resolveIDs();
|
||||
|
||||
private: /* data */
|
||||
QString m_url;
|
||||
QString needle;
|
||||
Flame::File m_result;
|
||||
QByteArray results;
|
||||
NetJobPtr m_dljob;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -36,10 +36,8 @@ QString INIFile::unescape(QString orig)
|
||||
{
|
||||
if(c == 'n')
|
||||
out += '\n';
|
||||
else if(c == 't')
|
||||
else if (c == 't')
|
||||
out += '\t';
|
||||
else if(c == '#')
|
||||
out += '#';
|
||||
else
|
||||
out += c;
|
||||
prev = 0;
|
||||
@@ -69,8 +67,6 @@ QString INIFile::escape(QString orig)
|
||||
out += "\\t";
|
||||
else if(c == '\\')
|
||||
out += "\\\\";
|
||||
else if(c == '#')
|
||||
out += "\\#";
|
||||
else
|
||||
out += c;
|
||||
}
|
||||
@@ -124,15 +120,7 @@ bool INIFile::loadFile(QByteArray file)
|
||||
{
|
||||
QString &lineRaw = lines[i];
|
||||
// Ignore comments.
|
||||
int commentIndex = 0;
|
||||
QString line = lineRaw;
|
||||
// Search for comments until no more escaped # are available
|
||||
while((commentIndex = line.indexOf('#', commentIndex + 1)) != -1) {
|
||||
if(commentIndex > 0 && line.at(commentIndex - 1) == '\\') {
|
||||
continue;
|
||||
}
|
||||
line = line.left(lineRaw.indexOf('#')).trimmed();
|
||||
}
|
||||
QString line = lineRaw.left(lineRaw.indexOf('#')).trimmed();
|
||||
|
||||
int eqPos = line.indexOf('=');
|
||||
if (eqPos == -1)
|
||||
|
||||
@@ -26,7 +26,6 @@ slots:
|
||||
QTest::newRow("Plain text") << "Lorem ipsum dolor sit amet.";
|
||||
QTest::newRow("Escape sequences") << "Lorem\n\t\n\\n\\tAAZ\nipsum dolor\n\nsit amet.";
|
||||
QTest::newRow("Escape sequences 2") << "\"\n\n\"";
|
||||
QTest::newRow("Hashtags") << "some data#something";
|
||||
}
|
||||
void test_Escape()
|
||||
{
|
||||
@@ -41,7 +40,7 @@ slots:
|
||||
void test_SaveLoad()
|
||||
{
|
||||
QString a = "a";
|
||||
QString b = "a\nb\t\n\\\\\\C:\\Program files\\terrible\\name\\of something\\#thisIsNotAComment";
|
||||
QString b = "a\nb\t\n\\\\\\C:\\Program files\\terrible\\name\\of something\\";
|
||||
QString filename = "test_SaveLoad.ini";
|
||||
|
||||
// save
|
||||
|
||||
@@ -131,14 +131,7 @@ void DownloadTask::processDownloadedVersionInfo()
|
||||
QObject::connect(netJob.get(), &NetJob::progress, this, &DownloadTask::fileDownloadProgressChanged);
|
||||
QObject::connect(netJob.get(), &NetJob::failed, this, &DownloadTask::fileDownloadFailed);
|
||||
|
||||
if(netJob->size() == 1) // Translation issues... see https://github.com/MultiMC/MultiMC5/issues/1701
|
||||
{
|
||||
setStatus(tr("Downloading one update file."));
|
||||
}
|
||||
else
|
||||
{
|
||||
setStatus(tr("Downloading %1 update files.").arg(QString::number(netJob->size())));
|
||||
}
|
||||
setStatus(tr("Downloading %1 update files.").arg(QString::number(netJob->size())));
|
||||
qDebug() << "Begin downloading update files to" << m_updateFilesDir.path();
|
||||
m_filesNetJob = netJob;
|
||||
m_filesNetJob->start();
|
||||
|
||||
@@ -702,9 +702,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow
|
||||
// model reset -> selection is invalid. All the instance pointers are wrong.
|
||||
connect(MMC->instances().get(), &InstanceList::dataIsInvalid, this, &MainWindow::selectionBad);
|
||||
|
||||
// handle newly added instances
|
||||
connect(MMC->instances().get(), &InstanceList::instanceSelectRequest, this, &MainWindow::instanceSelectRequest);
|
||||
|
||||
// When the global settings page closes, we want to know about it and update our state
|
||||
connect(MMC, &MultiMC::globalSettingsClosed, this, &MainWindow::globalSettingsClosed);
|
||||
|
||||
@@ -1667,7 +1664,6 @@ void MainWindow::on_actionDeleteInstance_triggered()
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto id = m_selectedInstance->id();
|
||||
auto response = CustomMessageBox::selectable(
|
||||
this,
|
||||
tr("CAREFUL!"),
|
||||
@@ -1678,7 +1674,7 @@ void MainWindow::on_actionDeleteInstance_triggered()
|
||||
)->exec();
|
||||
if (response == QMessageBox::Yes)
|
||||
{
|
||||
MMC->instances()->deleteInstance(id);
|
||||
MMC->instances()->deleteInstance(m_selectedInstance->id());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1840,11 +1836,6 @@ void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex &
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::instanceSelectRequest(QString id)
|
||||
{
|
||||
setSelectedInstanceById(id);
|
||||
}
|
||||
|
||||
void MainWindow::instanceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
|
||||
{
|
||||
auto current = view->selectionModel()->currentIndex();
|
||||
|
||||
@@ -152,8 +152,6 @@ private slots:
|
||||
|
||||
void instanceChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
|
||||
void instanceSelectRequest(QString id);
|
||||
|
||||
void instanceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
|
||||
|
||||
void selectionBad();
|
||||
|
||||
@@ -39,8 +39,6 @@
|
||||
#include <pages/modplatform/ImportPage.h>
|
||||
#include <pages/modplatform/TechnicPage.h>
|
||||
|
||||
|
||||
|
||||
NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString & url, QWidget *parent)
|
||||
: QDialog(parent), ui(new Ui::NewInstanceDialog)
|
||||
{
|
||||
@@ -96,15 +94,8 @@ NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString
|
||||
|
||||
if(!url.isEmpty())
|
||||
{
|
||||
QUrl actualUrl(url);
|
||||
if(actualUrl.host() == "www.curseforge.com") {
|
||||
m_container->selectPage("twitch");
|
||||
twitchPage->setUrl(url);
|
||||
}
|
||||
else {
|
||||
m_container->selectPage("import");
|
||||
importPage->setUrl(url);
|
||||
}
|
||||
m_container->selectPage("import");
|
||||
importPage->setUrl(url);
|
||||
}
|
||||
|
||||
updateDialogState();
|
||||
@@ -128,13 +119,13 @@ void NewInstanceDialog::accept()
|
||||
QList<BasePage *> NewInstanceDialog::getPages()
|
||||
{
|
||||
importPage = new ImportPage(this);
|
||||
twitchPage = new TwitchPage(this);
|
||||
return
|
||||
{
|
||||
new VanillaPage(this),
|
||||
new FTBPage(this),
|
||||
importPage,
|
||||
twitchPage,
|
||||
new FTBPage(this)
|
||||
new TwitchPage(this),
|
||||
new TechnicPage(this)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ class NewInstanceDialog;
|
||||
class PageContainer;
|
||||
class QDialogButtonBox;
|
||||
class ImportPage;
|
||||
class TwitchPage;
|
||||
|
||||
class NewInstanceDialog : public QDialog, public BasePageProvider
|
||||
{
|
||||
@@ -68,7 +67,6 @@ private:
|
||||
|
||||
QString InstIconKey;
|
||||
ImportPage *importPage = nullptr;
|
||||
TwitchPage *twitchPage = nullptr;
|
||||
std::unique_ptr<InstanceTask> creationTask;
|
||||
|
||||
bool importIcon = false;
|
||||
|
||||
@@ -220,8 +220,6 @@ VisualGroup *GroupView::categoryAt(const QPoint &pos, VisualGroup::HitResults &
|
||||
|
||||
QString GroupView::groupNameAt(const QPoint &point)
|
||||
{
|
||||
executeDelayedItemsLayout();
|
||||
|
||||
VisualGroup::HitResults hitresult;
|
||||
auto group = categoryAt(point + offset(), hitresult);
|
||||
if(group && (hitresult & (VisualGroup::HeaderHit | VisualGroup::BodyHit)))
|
||||
@@ -248,7 +246,7 @@ int GroupView::itemWidth() const
|
||||
|
||||
void GroupView::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
executeDelayedItemsLayout();
|
||||
// endCategoryEditor();
|
||||
|
||||
QPoint visualPos = event->pos();
|
||||
QPoint geometryPos = event->pos() + offset();
|
||||
@@ -297,8 +295,6 @@ void GroupView::mousePressEvent(QMouseEvent *event)
|
||||
|
||||
void GroupView::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
executeDelayedItemsLayout();
|
||||
|
||||
QPoint topLeft;
|
||||
QPoint visualPos = event->pos();
|
||||
QPoint geometryPos = event->pos() + offset();
|
||||
@@ -355,8 +351,6 @@ void GroupView::mouseMoveEvent(QMouseEvent *event)
|
||||
|
||||
void GroupView::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
executeDelayedItemsLayout();
|
||||
|
||||
QPoint visualPos = event->pos();
|
||||
QPoint geometryPos = event->pos() + offset();
|
||||
QPersistentModelIndex index = indexAt(visualPos);
|
||||
@@ -411,8 +405,6 @@ void GroupView::mouseReleaseEvent(QMouseEvent *event)
|
||||
|
||||
void GroupView::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
executeDelayedItemsLayout();
|
||||
|
||||
QModelIndex index = indexAt(event->pos());
|
||||
if (!index.isValid() || !(index.flags() & Qt::ItemIsEnabled) || (m_pressedIndex != index))
|
||||
{
|
||||
@@ -536,8 +528,6 @@ void GroupView::resizeEvent(QResizeEvent *event)
|
||||
|
||||
void GroupView::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
executeDelayedItemsLayout();
|
||||
|
||||
if (!isDragEventAccepted(event))
|
||||
{
|
||||
return;
|
||||
@@ -549,8 +539,6 @@ void GroupView::dragEnterEvent(QDragEnterEvent *event)
|
||||
|
||||
void GroupView::dragMoveEvent(QDragMoveEvent *event)
|
||||
{
|
||||
executeDelayedItemsLayout();
|
||||
|
||||
if (!isDragEventAccepted(event))
|
||||
{
|
||||
return;
|
||||
@@ -562,16 +550,12 @@ void GroupView::dragMoveEvent(QDragMoveEvent *event)
|
||||
|
||||
void GroupView::dragLeaveEvent(QDragLeaveEvent *event)
|
||||
{
|
||||
executeDelayedItemsLayout();
|
||||
|
||||
m_lastDragPosition = QPoint();
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
void GroupView::dropEvent(QDropEvent *event)
|
||||
{
|
||||
executeDelayedItemsLayout();
|
||||
|
||||
m_lastDragPosition = QPoint();
|
||||
|
||||
stopAutoScroll();
|
||||
@@ -622,8 +606,6 @@ void GroupView::dropEvent(QDropEvent *event)
|
||||
|
||||
void GroupView::startDrag(Qt::DropActions supportedActions)
|
||||
{
|
||||
executeDelayedItemsLayout();
|
||||
|
||||
QModelIndexList indexes = selectionModel()->selectedIndexes();
|
||||
if(indexes.count() == 0)
|
||||
return;
|
||||
@@ -669,15 +651,11 @@ void GroupView::startDrag(Qt::DropActions supportedActions)
|
||||
|
||||
QRect GroupView::visualRect(const QModelIndex &index) const
|
||||
{
|
||||
const_cast<GroupView*>(this)->executeDelayedItemsLayout();
|
||||
|
||||
return geometryRect(index).translated(-offset());
|
||||
}
|
||||
|
||||
QRect GroupView::geometryRect(const QModelIndex &index) const
|
||||
{
|
||||
const_cast<GroupView*>(this)->executeDelayedItemsLayout();
|
||||
|
||||
if (!index.isValid() || isIndexHidden(index) || index.column() > 0)
|
||||
{
|
||||
return QRect();
|
||||
@@ -717,10 +695,9 @@ QModelIndex GroupView::indexAt(const QPoint &point) const
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
void GroupView::setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands)
|
||||
void GroupView::setSelection(const QRect &rect,
|
||||
const QItemSelectionModel::SelectionFlags commands)
|
||||
{
|
||||
executeDelayedItemsLayout();
|
||||
|
||||
for (int i = 0; i < model()->rowCount(); ++i)
|
||||
{
|
||||
QModelIndex index = model()->index(i, 0);
|
||||
@@ -755,7 +732,8 @@ QPixmap GroupView::renderToPixmap(const QModelIndexList &indices, QRect *r) cons
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
QList<QPair<QRect, QModelIndex>> GroupView::draggablePaintPairs(const QModelIndexList &indices, QRect *r) const
|
||||
QList<QPair<QRect, QModelIndex>> GroupView::draggablePaintPairs(const QModelIndexList &indices,
|
||||
QRect *r) const
|
||||
{
|
||||
Q_ASSERT(r);
|
||||
QRect &rect = *r;
|
||||
|
||||
@@ -30,7 +30,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
#endif
|
||||
|
||||
// initialize Qt
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Package: multimc
|
||||
Version: 1.3-1
|
||||
Version: 1.2-1
|
||||
Architecture: all
|
||||
Maintainer: Petr Mrázek <peterix@gmail.com>
|
||||
Section: games
|
||||
Priority: optional
|
||||
Installed-Size: 75
|
||||
Depends: zenity, desktop-file-utils, qt5-default
|
||||
Depends: zenity, desktop-file-utils
|
||||
Recommends: openjdk-8-jre
|
||||
Homepage: http://multimc.org
|
||||
Description: A local install wrapper for MultiMC
|
||||
|
||||
@@ -4,9 +4,9 @@ A simple ubuntu package for MultiMC that wraps the contains a script that downlo
|
||||
It contains a `.desktop` file, an icon, and a simple script that does the heavy lifting.
|
||||
|
||||
# How to build this?
|
||||
You need dpkg utils. Rename the `multimc` folder to `multimc_1.3-1` and then run:
|
||||
You need dpkg utils. Rename the `multimc` folder to `multimc_1.2-1` and then run:
|
||||
```
|
||||
fakeroot dpkg-deb --build multimc_1.3-1
|
||||
fakeroot dpkg-deb --build multimc_1.2-1
|
||||
```
|
||||
|
||||
Replace the version with whatever is appropriate.
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "MultiMC.h"
|
||||
#include "Env.h"
|
||||
|
||||
ProxyPage::ProxyPage(QWidget *parent) : QWidget(parent), ui(new Ui::ProxyPage)
|
||||
{
|
||||
@@ -76,9 +75,6 @@ void ProxyPage::applySettings()
|
||||
s->set("ProxyPort", ui->proxyPortEdit->value());
|
||||
s->set("ProxyUser", ui->proxyUserEdit->text());
|
||||
s->set("ProxyPass", ui->proxyPassEdit->text());
|
||||
|
||||
ENV.updateProxySettings(proxyType, ui->proxyAddrEdit->text(), ui->proxyPortEdit->value(),
|
||||
ui->proxyUserEdit->text(), ui->proxyPassEdit->text());
|
||||
}
|
||||
void ProxyPage::loadSettings()
|
||||
{
|
||||
|
||||
@@ -286,38 +286,6 @@ void ScreenshotsPage::on_uploadBtn_clicked()
|
||||
|
||||
QList<ScreenshotPtr> uploaded;
|
||||
auto job = NetJobPtr(new NetJob("Screenshot Upload"));
|
||||
if(selection.size() < 2)
|
||||
{
|
||||
auto item = selection.at(0);
|
||||
auto info = m_model->fileInfo(item);
|
||||
auto screenshot = std::make_shared<ScreenShot>(info);
|
||||
job->addNetAction(ImgurUpload::make(screenshot));
|
||||
|
||||
m_uploadActive = true;
|
||||
ProgressDialog dialog(this);
|
||||
if(dialog.execWithTask(job.get()) != QDialog::Accepted)
|
||||
{
|
||||
CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"),
|
||||
tr("Unknown error"), QMessageBox::Warning)->exec();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto link = screenshot->m_url;
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
clipboard->setText(link);
|
||||
CustomMessageBox::selectable(
|
||||
this,
|
||||
tr("Upload finished"),
|
||||
tr("The <a href=\"%1\">link to the uploaded screenshot</a> has been placed in your clipboard.")
|
||||
.arg(link),
|
||||
QMessageBox::Information
|
||||
)->exec();
|
||||
}
|
||||
|
||||
m_uploadActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto item : selection)
|
||||
{
|
||||
auto info = m_model->fileInfo(item);
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
#include "minecraft/Mod.h"
|
||||
#include "icons/IconList.h"
|
||||
#include "Exception.h"
|
||||
#include "Version.h"
|
||||
|
||||
#include "MultiMC.h"
|
||||
|
||||
@@ -109,18 +108,26 @@ VersionPage::VersionPage(MinecraftInstance *inst, QWidget *parent)
|
||||
|
||||
reloadComponentList();
|
||||
|
||||
auto proxy = new IconProxy(ui->packageView);
|
||||
proxy->setSourceModel(m_profile.get());
|
||||
ui->packageView->setModel(proxy);
|
||||
ui->packageView->installEventFilter(this);
|
||||
ui->packageView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
connect(ui->packageView->selectionModel(), &QItemSelectionModel::currentChanged, this, &VersionPage::versionCurrent);
|
||||
auto smodel = ui->packageView->selectionModel();
|
||||
connect(smodel, &QItemSelectionModel::currentChanged, this, &VersionPage::packageCurrent);
|
||||
|
||||
updateVersionControls();
|
||||
preselect(0);
|
||||
connect(m_profile.get(), &ComponentList::minecraftChanged, this, &VersionPage::updateVersionControls);
|
||||
if (m_profile)
|
||||
{
|
||||
auto proxy = new IconProxy(ui->packageView);
|
||||
proxy->setSourceModel(m_profile.get());
|
||||
ui->packageView->setModel(proxy);
|
||||
ui->packageView->installEventFilter(this);
|
||||
ui->packageView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
connect(ui->packageView->selectionModel(), &QItemSelectionModel::currentChanged, this, &VersionPage::versionCurrent);
|
||||
auto smodel = ui->packageView->selectionModel();
|
||||
connect(smodel, &QItemSelectionModel::currentChanged, this, &VersionPage::packageCurrent);
|
||||
updateVersionControls();
|
||||
// select first item.
|
||||
preselect(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
disableVersionControls();
|
||||
}
|
||||
connect(m_inst, &MinecraftInstance::versionReloaded, this,
|
||||
&VersionPage::updateVersionControls);
|
||||
}
|
||||
|
||||
VersionPage::~VersionPage()
|
||||
@@ -173,21 +180,18 @@ void VersionPage::packageCurrent(const QModelIndex ¤t, const QModelIndex &
|
||||
|
||||
void VersionPage::updateVersionControls()
|
||||
{
|
||||
// FIXME: this is a dirty hack
|
||||
if(m_profile) {
|
||||
auto minecraftVersion = Version(m_profile->getComponentVersion("net.minecraft"));
|
||||
bool newCraft = minecraftVersion >= Version("1.14");
|
||||
bool oldCraft = minecraftVersion <= Version("1.12.2");
|
||||
ui->fabricBtn->setEnabled(newCraft);
|
||||
ui->forgeBtn->setEnabled(oldCraft);
|
||||
ui->liteloaderBtn->setEnabled(oldCraft);
|
||||
}
|
||||
else {
|
||||
ui->fabricBtn->setEnabled(false);
|
||||
ui->forgeBtn->setEnabled(false);
|
||||
ui->liteloaderBtn->setEnabled(false);
|
||||
ui->reloadBtn->setEnabled(false);
|
||||
}
|
||||
ui->fabricBtn->setEnabled(true);
|
||||
ui->forgeBtn->setEnabled(true);
|
||||
ui->liteloaderBtn->setEnabled(true);
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
void VersionPage::disableVersionControls()
|
||||
{
|
||||
ui->fabricBtn->setEnabled(false);
|
||||
ui->forgeBtn->setEnabled(false);
|
||||
ui->liteloaderBtn->setEnabled(false);
|
||||
ui->reloadBtn->setEnabled(false);
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ private slots:
|
||||
void on_downloadBtn_clicked();
|
||||
|
||||
void updateVersionControls();
|
||||
void disableVersionControls();
|
||||
void on_changeVersionBtn_clicked();
|
||||
|
||||
private:
|
||||
|
||||
@@ -75,11 +75,6 @@ void ImportPage::updateState()
|
||||
}
|
||||
else
|
||||
{
|
||||
if(input.endsWith("?client=y")) {
|
||||
input.chop(9);
|
||||
input.append("/file");
|
||||
url = QUrl::fromUserInput(input);
|
||||
}
|
||||
// hook, line and sinker.
|
||||
QFileInfo fi(url.fileName());
|
||||
dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url));
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
|
||||
#include "MultiMC.h"
|
||||
#include "dialogs/NewInstanceDialog.h"
|
||||
#include <InstanceImportTask.h>
|
||||
|
||||
TwitchPage::TwitchPage(NewInstanceDialog* dialog, QWidget *parent)
|
||||
: QWidget(parent), ui(new Ui::TwitchPage), dialog(dialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(ui->checkButton, &QPushButton::clicked, this, &TwitchPage::triggerCheck);
|
||||
}
|
||||
|
||||
TwitchPage::~TwitchPage()
|
||||
@@ -19,42 +17,10 @@ TwitchPage::~TwitchPage()
|
||||
|
||||
bool TwitchPage::shouldDisplay() const
|
||||
{
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void TwitchPage::openedImpl()
|
||||
{
|
||||
dialog->setSuggestedPack();
|
||||
}
|
||||
|
||||
void TwitchPage::triggerCheck(bool)
|
||||
{
|
||||
if(m_modIdResolver) {
|
||||
return;
|
||||
}
|
||||
auto task = new Flame::UrlResolvingTask(ui->lineEdit->text());
|
||||
connect(task, &Task::finished, this, &TwitchPage::checkDone);
|
||||
m_modIdResolver.reset(task);
|
||||
task->start();
|
||||
}
|
||||
|
||||
void TwitchPage::setUrl(const QString& url)
|
||||
{
|
||||
ui->lineEdit->setText(url);
|
||||
triggerCheck(true);
|
||||
}
|
||||
|
||||
void TwitchPage::checkDone()
|
||||
{
|
||||
auto result = m_modIdResolver->getResults();
|
||||
auto formatted = QString("Project %1, File %2").arg(result.projectId).arg(result.fileId);
|
||||
if(result.resolved && result.type == Flame::File::Type::Modpack) {
|
||||
ui->twitchLabel->setText(formatted);
|
||||
QFileInfo fi(result.fileName);
|
||||
dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(result.url));
|
||||
} else {
|
||||
ui->twitchLabel->setPixmap(QPixmap(QString::fromUtf8(":/assets/deadglitch")));
|
||||
dialog->setSuggestedPack();
|
||||
}
|
||||
m_modIdResolver.reset();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "pages/BasePage.h"
|
||||
#include <MultiMC.h>
|
||||
#include "tasks/Task.h"
|
||||
#include "modplatform/flame/UrlResolvingTask.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
@@ -38,7 +37,7 @@ public:
|
||||
virtual ~TwitchPage();
|
||||
virtual QString displayName() const override
|
||||
{
|
||||
return tr("Twitch URL");
|
||||
return tr("Twitch");
|
||||
}
|
||||
virtual QIcon icon() const override
|
||||
{
|
||||
@@ -56,14 +55,7 @@ public:
|
||||
|
||||
void openedImpl() override;
|
||||
|
||||
void setUrl(const QString & url);
|
||||
|
||||
private slots:
|
||||
void triggerCheck(bool checked);
|
||||
void checkDone();
|
||||
|
||||
private:
|
||||
Ui::TwitchPage *ui = nullptr;
|
||||
NewInstanceDialog* dialog = nullptr;
|
||||
shared_qobject_ptr<Flame::UrlResolvingTask> m_modIdResolver;
|
||||
};
|
||||
|
||||
@@ -10,25 +10,9 @@
|
||||
<height>405</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Twitch URL:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="3">
|
||||
<widget class="QLabel" name="twitchLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>40</pointsize>
|
||||
@@ -42,19 +26,8 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="checkButton">
|
||||
<property name="text">
|
||||
<string>Check</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>lineEdit</tabstop>
|
||||
<tabstop>checkButton</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../resources/assets/assets.qrc"/>
|
||||
</resources>
|
||||
|
||||
@@ -23,7 +23,6 @@ VanillaPage::VanillaPage(NewInstanceDialog *dialog, QWidget *parent)
|
||||
connect(ui->snapshotFilter, &QCheckBox::stateChanged, this, &VanillaPage::filterChanged);
|
||||
connect(ui->oldSnapshotFilter, &QCheckBox::stateChanged, this, &VanillaPage::filterChanged);
|
||||
connect(ui->releaseFilter, &QCheckBox::stateChanged, this, &VanillaPage::filterChanged);
|
||||
connect(ui->experimentsFilter, &QCheckBox::stateChanged, this, &VanillaPage::filterChanged);
|
||||
connect(ui->refreshBtn, &QPushButton::clicked, this, &VanillaPage::refresh);
|
||||
}
|
||||
|
||||
@@ -59,8 +58,6 @@ void VanillaPage::filterChanged()
|
||||
out << "(old_snapshot)";
|
||||
if(ui->releaseFilter->isChecked())
|
||||
out << "(release)";
|
||||
if(ui->experimentsFilter->isChecked())
|
||||
out << "(experiment)";
|
||||
auto regexp = out.join('|');
|
||||
ui->versionList->setFilter(BaseVersionList::TypeRole, new RegexpFilter(regexp, false));
|
||||
}
|
||||
|
||||
@@ -98,16 +98,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="experimentsFilter">
|
||||
<property name="text">
|
||||
<string>Experiments</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
@@ -154,16 +144,6 @@
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>tabWidget</tabstop>
|
||||
<tabstop>releaseFilter</tabstop>
|
||||
<tabstop>snapshotFilter</tabstop>
|
||||
<tabstop>oldSnapshotFilter</tabstop>
|
||||
<tabstop>betaFilter</tabstop>
|
||||
<tabstop>alphaFilter</tabstop>
|
||||
<tabstop>experimentsFilter</tabstop>
|
||||
<tabstop>refreshBtn</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -45,13 +45,7 @@ void JavaWizardPage::refresh()
|
||||
|
||||
void JavaWizardPage::initializePage()
|
||||
{
|
||||
auto s = MMC->settings();
|
||||
m_java_widget->initialize(
|
||||
s->get("JavaPath").toString(),
|
||||
s->get("MinMemAlloc").toInt(),
|
||||
s->get("MaxMemAlloc").toInt(),
|
||||
s->get("PermGen").toInt()
|
||||
);
|
||||
m_java_widget->initialize();
|
||||
}
|
||||
|
||||
bool JavaWizardPage::wantsRefreshButton()
|
||||
|
||||
@@ -113,15 +113,15 @@ void JavaSettingsWidget::setupUi()
|
||||
retranslate();
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::initialize(const QString& currentJavaPath, int minHeap, int maxHeap, int permGen)
|
||||
void JavaSettingsWidget::initialize()
|
||||
{
|
||||
m_versionWidget->initialize(MMC->javalist().get());
|
||||
m_versionWidget->setResizeOn(2);
|
||||
auto s = MMC->settings();
|
||||
// Memory
|
||||
observedMinMemory = minHeap;
|
||||
observedMaxMemory = maxHeap;
|
||||
observedPermGenMemory = permGen;
|
||||
m_javaPathTextBox->setText(currentJavaPath);
|
||||
observedMinMemory = s->get("MinMemAlloc").toInt();
|
||||
observedMaxMemory = s->get("MaxMemAlloc").toInt();
|
||||
observedPermGenMemory = s->get("PermGen").toInt();
|
||||
m_minMemSpinBox->setValue(observedMinMemory);
|
||||
m_maxMemSpinBox->setValue(observedMaxMemory);
|
||||
m_permGenSpinBox->setValue(observedPermGenMemory);
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
};
|
||||
|
||||
void refresh();
|
||||
void initialize(const QString& currentJavaPath, int minHeap, int maxHeap, int permGen);
|
||||
void initialize();
|
||||
ValidationStatus validate();
|
||||
void retranslate();
|
||||
|
||||
|
||||
90
changelog.md
90
changelog.md
@@ -1,91 +1,8 @@
|
||||
# MultiMC 0.6.6
|
||||
|
||||
This release is mostly the smaller things that have accumulated over time, along with a big change in linux packaging.
|
||||
|
||||
No 1.13+ Forge news yet. That's going to be a major overhaul of many of the internals of MultiMC.
|
||||
|
||||
## **IMPORTANT**
|
||||
|
||||
On linux, MultiMC no longer bundles the Qt libraries. This fixes many issues, but it might not run after the update unless you have the required libraries installed.
|
||||
|
||||
Make sure you have the following packages before you update:
|
||||
|
||||
- Arch: `qt5-base`
|
||||
- Debian/Ubuntu: `qt5-default`
|
||||
- CentOS/RHEL/Fedora: `qt5-qtbase-gui`
|
||||
- Suse: `libqt5-qtbase`
|
||||
|
||||
MultiMC on linux is built with Qt 5.4 and older versions of Qt will not work.
|
||||
|
||||
This should be a massive improvement to system integration on linux and resolves GH-1784, GH-2605, GH-1979, GH-2271, GH-1992, GH-1816 and their many duplicates.
|
||||
|
||||
### New or changed features
|
||||
|
||||
- GH-2487: No is now the default button when deleting instances.
|
||||
|
||||
- It is now possible to launch with profilers in offline mode.
|
||||
|
||||
- Massively improved support for icon formats when importing and exporting instances.
|
||||
|
||||
All of the formats MultiMC supports are now supported in exported instances too, instead of just PNG.
|
||||
|
||||
- Added the pocket fox icon.
|
||||
|
||||
We still have the big one under the staircase. It's cute. Just hide your chickens.
|
||||
|
||||
- Global settings can be opened from instance settings where appropriate.
|
||||
|
||||
Many people use the instance overrides where using the global settings would be more appropriate. Hopefully this makes it clearer that the instance settings are overrides for the global settings.
|
||||
|
||||
- Added direct Fabric loader support.
|
||||
|
||||
Much overdue. It's good. Fabric mod metadata is also supported in the mod pages.
|
||||
|
||||
- MultiMC now recognizes the new `experimental` Minecraft versions.
|
||||
|
||||
Go mess with the combat experiment. It's interesting.
|
||||
|
||||
- Added Twitch URL as an option to the Add Instance dialog.
|
||||
|
||||
You can now drag the purple download buttons from CurseForge into MultiMC and get a modpack out of it. Much easier!
|
||||
|
||||
### Bugfixes
|
||||
|
||||
- Translation folder is now created sooner, making first launch translation fetch work again.
|
||||
|
||||
- GH-2716: MultiMC will no longer try to censor values shorter than 4 characters in logs.
|
||||
|
||||
It was actually leaking information and destroying the logs instead of helping.
|
||||
|
||||
- GH-2551: Trim server name and IP before saving them.
|
||||
|
||||
- GH-2591: Fix multiple potential memory leaks and crashes related to destroying objects with Qt memory lifecycle model.
|
||||
|
||||
- `run.sh` on linux now passes all arguments to MultiMC.
|
||||
|
||||
- Adding a disabled mod duplicate now replaces the existing mod.
|
||||
|
||||
- GH-2592: Newly created instances are now selected again. This was a very old regression.
|
||||
|
||||
- GH-689: MultiMC no longer creates an imgur album for single screenshot uploads.
|
||||
|
||||
- GH-1813: `#` is now saved properly when used in instance notes.
|
||||
|
||||
- GH-2515: Deleting an instance externally while the delete dialog is open no longer leads to some other instance being deleted when you click OK.
|
||||
|
||||
- GH-2499: Proxy settings are applied immediately and no longer need an application restart.
|
||||
|
||||
- GH-1701: When downloading updates, the text now reflects the number of downloaded files better.
|
||||
|
||||
- Icon scaling issues on macOS should now be fixed.
|
||||
|
||||
# Previous releases
|
||||
|
||||
## MultiMC 0.6.5
|
||||
# MultiMC 0.6.5
|
||||
|
||||
Finalizing the translation workflow improvements and adding fixes for sounds missing in old game versions.
|
||||
|
||||
#### New or changed features
|
||||
### New or changed features
|
||||
|
||||
- UI for the language settings has been unified across the application
|
||||
|
||||
@@ -95,6 +12,7 @@ Finalizing the translation workflow improvements and adding fixes for sounds mis
|
||||
|
||||
Also, a minor issue with the reconstruction being done twice per launch has been fixed.
|
||||
|
||||
# Previous releases
|
||||
|
||||
## MultiMC 0.6.4
|
||||
|
||||
@@ -1170,4 +1088,4 @@ Long time coming, this release brought a lot of incremental improvements and fix
|
||||
- Added additional information to the about dialog.
|
||||
|
||||
## MultiMC 0.0
|
||||
- Initial release.
|
||||
- Initial release.
|
||||
Reference in New Issue
Block a user