merge upstream and resolve conflicts
This commit is contained in:
commit
c4f4e9e620
@ -39,6 +39,7 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
|||||||
m_settings->registerSetting("lastLaunchTime", 0);
|
m_settings->registerSetting("lastLaunchTime", 0);
|
||||||
m_settings->registerSetting("totalTimePlayed", 0);
|
m_settings->registerSetting("totalTimePlayed", 0);
|
||||||
m_settings->registerSetting("lastTimePlayed", 0);
|
m_settings->registerSetting("lastTimePlayed", 0);
|
||||||
|
m_settings->registerSetting("InstanceType", "OneSix");
|
||||||
|
|
||||||
// Custom Commands
|
// Custom Commands
|
||||||
auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
|
auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
|
||||||
|
@ -286,13 +286,6 @@ set(MINECRAFT_SOURCES
|
|||||||
minecraft/launch/VerifyJavaInstall.cpp
|
minecraft/launch/VerifyJavaInstall.cpp
|
||||||
minecraft/launch/VerifyJavaInstall.h
|
minecraft/launch/VerifyJavaInstall.h
|
||||||
|
|
||||||
minecraft/legacy/LegacyModList.h
|
|
||||||
minecraft/legacy/LegacyModList.cpp
|
|
||||||
minecraft/legacy/LegacyInstance.h
|
|
||||||
minecraft/legacy/LegacyInstance.cpp
|
|
||||||
minecraft/legacy/LegacyUpgradeTask.h
|
|
||||||
minecraft/legacy/LegacyUpgradeTask.cpp
|
|
||||||
|
|
||||||
minecraft/GradleSpecifier.h
|
minecraft/GradleSpecifier.h
|
||||||
minecraft/MinecraftInstance.cpp
|
minecraft/MinecraftInstance.cpp
|
||||||
minecraft/MinecraftInstance.h
|
minecraft/MinecraftInstance.h
|
||||||
@ -701,8 +694,6 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/pages/instance/OtherLogsPage.h
|
ui/pages/instance/OtherLogsPage.h
|
||||||
ui/pages/instance/ServersPage.cpp
|
ui/pages/instance/ServersPage.cpp
|
||||||
ui/pages/instance/ServersPage.h
|
ui/pages/instance/ServersPage.h
|
||||||
ui/pages/instance/LegacyUpgradePage.cpp
|
|
||||||
ui/pages/instance/LegacyUpgradePage.h
|
|
||||||
ui/pages/instance/WorldListPage.cpp
|
ui/pages/instance/WorldListPage.cpp
|
||||||
ui/pages/instance/WorldListPage.h
|
ui/pages/instance/WorldListPage.h
|
||||||
|
|
||||||
@ -884,7 +875,6 @@ qt5_wrap_ui(LAUNCHER_UI
|
|||||||
ui/pages/instance/InstanceSettingsPage.ui
|
ui/pages/instance/InstanceSettingsPage.ui
|
||||||
ui/pages/instance/VersionPage.ui
|
ui/pages/instance/VersionPage.ui
|
||||||
ui/pages/instance/WorldListPage.ui
|
ui/pages/instance/WorldListPage.ui
|
||||||
ui/pages/instance/LegacyUpgradePage.ui
|
|
||||||
ui/pages/instance/ScreenshotsPage.ui
|
ui/pages/instance/ScreenshotsPage.ui
|
||||||
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui
|
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui
|
||||||
ui/pages/modplatform/atlauncher/AtlPage.ui
|
ui/pages/modplatform/atlauncher/AtlPage.ui
|
||||||
|
@ -42,7 +42,6 @@ void InstanceCopyTask::copyFinished()
|
|||||||
}
|
}
|
||||||
// FIXME: shouldn't this be able to report errors?
|
// FIXME: shouldn't this be able to report errors?
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
||||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
|
||||||
|
|
||||||
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
|
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
|
||||||
inst->setName(m_instName);
|
inst->setName(m_instName);
|
||||||
|
@ -17,8 +17,6 @@ void InstanceCreationTask::executeTask()
|
|||||||
{
|
{
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
||||||
instanceSettings->suspendSave();
|
instanceSettings->suspendSave();
|
||||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
|
||||||
instanceSettings->set("InstanceType", "OneSix");
|
|
||||||
MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
|
MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
|
||||||
auto components = inst.getPackProfile();
|
auto components = inst.getPackProfile();
|
||||||
components->buildingFromScratch();
|
components->buildingFromScratch();
|
||||||
|
@ -261,8 +261,6 @@ void InstanceImportTask::processFlame()
|
|||||||
|
|
||||||
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
||||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
|
||||||
instanceSettings->set("InstanceType", "OneSix");
|
|
||||||
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||||
auto mcVersion = pack.minecraft.version;
|
auto mcVersion = pack.minecraft.version;
|
||||||
// Hack to correct some 'special sauce'...
|
// Hack to correct some 'special sauce'...
|
||||||
@ -422,7 +420,6 @@ void InstanceImportTask::processMultiMC()
|
|||||||
{
|
{
|
||||||
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
||||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
|
||||||
|
|
||||||
NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||||
|
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
#include "InstanceTask.h"
|
#include "InstanceTask.h"
|
||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
#include "minecraft/legacy/LegacyInstance.h"
|
|
||||||
#include "NullInstance.h"
|
#include "NullInstance.h"
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftInstance.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
@ -545,7 +544,7 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id)
|
|||||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg"));
|
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg"));
|
||||||
InstancePtr inst;
|
InstancePtr inst;
|
||||||
|
|
||||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
instanceSettings->registerSetting("InstanceType", "Legacy"); // intentionally Legacy. We don't support it.
|
||||||
|
|
||||||
QString inst_type = instanceSettings->get("InstanceType").toString();
|
QString inst_type = instanceSettings->get("InstanceType").toString();
|
||||||
|
|
||||||
@ -553,10 +552,6 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id)
|
|||||||
{
|
{
|
||||||
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
|
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
|
||||||
}
|
}
|
||||||
else if (inst_type == "Legacy")
|
|
||||||
{
|
|
||||||
inst.reset(new LegacyInstance(m_globalSettings, instanceSettings, instanceRoot));
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot));
|
inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot));
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftInstance.h"
|
||||||
#include "minecraft/legacy/LegacyInstance.h"
|
|
||||||
#include <FileSystem.h>
|
#include <FileSystem.h>
|
||||||
#include "ui/pages/BasePage.h"
|
#include "ui/pages/BasePage.h"
|
||||||
#include "ui/pages/BasePageProvider.h"
|
#include "ui/pages/BasePageProvider.h"
|
||||||
@ -14,7 +13,6 @@
|
|||||||
#include "ui/pages/instance/ScreenshotsPage.h"
|
#include "ui/pages/instance/ScreenshotsPage.h"
|
||||||
#include "ui/pages/instance/InstanceSettingsPage.h"
|
#include "ui/pages/instance/InstanceSettingsPage.h"
|
||||||
#include "ui/pages/instance/OtherLogsPage.h"
|
#include "ui/pages/instance/OtherLogsPage.h"
|
||||||
#include "ui/pages/instance/LegacyUpgradePage.h"
|
|
||||||
#include "ui/pages/instance/WorldListPage.h"
|
#include "ui/pages/instance/WorldListPage.h"
|
||||||
#include "ui/pages/instance/ServersPage.h"
|
#include "ui/pages/instance/ServersPage.h"
|
||||||
#include "ui/pages/instance/GameOptionsPage.h"
|
#include "ui/pages/instance/GameOptionsPage.h"
|
||||||
@ -34,31 +32,20 @@ public:
|
|||||||
QList<BasePage *> values;
|
QList<BasePage *> values;
|
||||||
values.append(new LogPage(inst));
|
values.append(new LogPage(inst));
|
||||||
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
|
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
|
||||||
if(onesix)
|
values.append(new VersionPage(onesix.get()));
|
||||||
{
|
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList(), "mods", "loadermods", tr("Mods"), "Loader-mods");
|
||||||
values.append(new VersionPage(onesix.get()));
|
modsPage->setFilter("%1 (*.zip *.jar *.litemod)");
|
||||||
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList(), "mods", "loadermods", tr("Mods"), "Loader-mods");
|
values.append(modsPage);
|
||||||
modsPage->setFilter("%1 (*.zip *.jar *.litemod)");
|
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList(), "coremods", "coremods", tr("Core mods"), "Core-mods"));
|
||||||
values.append(modsPage);
|
values.append(new ResourcePackPage(onesix.get()));
|
||||||
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList(), "coremods", "coremods", tr("Core mods"), "Core-mods"));
|
values.append(new TexturePackPage(onesix.get()));
|
||||||
values.append(new ResourcePackPage(onesix.get()));
|
values.append(new ShaderPackPage(onesix.get()));
|
||||||
values.append(new TexturePackPage(onesix.get()));
|
values.append(new NotesPage(onesix.get()));
|
||||||
values.append(new ShaderPackPage(onesix.get()));
|
values.append(new WorldListPage(onesix.get(), onesix->worldList()));
|
||||||
values.append(new NotesPage(onesix.get()));
|
values.append(new ServersPage(onesix));
|
||||||
values.append(new WorldListPage(onesix.get(), onesix->worldList()));
|
// values.append(new GameOptionsPage(onesix.get()));
|
||||||
values.append(new ServersPage(onesix));
|
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
|
||||||
// values.append(new GameOptionsPage(onesix.get()));
|
values.append(new InstanceSettingsPage(onesix.get()));
|
||||||
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
|
|
||||||
values.append(new InstanceSettingsPage(onesix.get()));
|
|
||||||
}
|
|
||||||
std::shared_ptr<LegacyInstance> legacy = std::dynamic_pointer_cast<LegacyInstance>(inst);
|
|
||||||
if(legacy)
|
|
||||||
{
|
|
||||||
values.append(new LegacyUpgradePage(legacy));
|
|
||||||
values.append(new NotesPage(legacy.get()));
|
|
||||||
values.append(new WorldListPage(legacy.get(), legacy->worldList()));
|
|
||||||
values.append(new ScreenshotsPage(FS::PathCombine(legacy->gameRoot(), "screenshots")));
|
|
||||||
}
|
|
||||||
auto logMatcher = inst->getLogFileMatcher();
|
auto logMatcher = inst->getLogFileMatcher();
|
||||||
if(logMatcher)
|
if(logMatcher)
|
||||||
{
|
{
|
||||||
|
@ -124,18 +124,7 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO
|
|||||||
m_settings->registerSetting("JoinServerOnLaunch", false);
|
m_settings->registerSetting("JoinServerOnLaunch", false);
|
||||||
m_settings->registerSetting("JoinServerOnLaunchAddress", "");
|
m_settings->registerSetting("JoinServerOnLaunchAddress", "");
|
||||||
|
|
||||||
// DEPRECATED: Read what versions the user configuration thinks should be used
|
|
||||||
m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, "");
|
|
||||||
m_settings->registerSetting("LWJGLVersion", "");
|
|
||||||
m_settings->registerSetting("ForgeVersion", "");
|
|
||||||
m_settings->registerSetting("LiteloaderVersion", "");
|
|
||||||
|
|
||||||
m_components.reset(new PackProfile(this));
|
m_components.reset(new PackProfile(this));
|
||||||
m_components->setOldConfigVersion("net.minecraft", m_settings->get("IntendedVersion").toString());
|
|
||||||
auto setting = m_settings->getSetting("LWJGLVersion");
|
|
||||||
m_components->setOldConfigVersion("org.lwjgl", m_settings->get("LWJGLVersion").toString());
|
|
||||||
m_components->setOldConfigVersion("net.minecraftforge", m_settings->get("ForgeVersion").toString());
|
|
||||||
m_components->setOldConfigVersion("com.mumfrey.liteloader", m_settings->get("LiteloaderVersion").toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MinecraftInstance::saveNow()
|
void MinecraftInstance::saveNow()
|
||||||
|
@ -272,18 +272,6 @@ void PackProfile::save_internal()
|
|||||||
bool PackProfile::load()
|
bool PackProfile::load()
|
||||||
{
|
{
|
||||||
auto filename = componentsFilePath();
|
auto filename = componentsFilePath();
|
||||||
QFile componentsFile(filename);
|
|
||||||
|
|
||||||
// migrate old config to new one, if needed
|
|
||||||
if(!componentsFile.exists())
|
|
||||||
{
|
|
||||||
if(!migratePreComponentConfig())
|
|
||||||
{
|
|
||||||
// FIXME: the user should be notified...
|
|
||||||
qCritical() << "Failed to convert old pre-component config for instance" << d->m_instance->name();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// load the new component list and swap it with the current one...
|
// load the new component list and swap it with the current one...
|
||||||
ComponentContainer newComponents;
|
ComponentContainer newComponents;
|
||||||
@ -369,239 +357,6 @@ void PackProfile::updateFailed(const QString& error)
|
|||||||
invalidateLaunchProfile();
|
invalidateLaunchProfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE this is really old stuff, and only needs to be used when loading the old hardcoded component-unaware format (loadPreComponentConfig).
|
|
||||||
static void upgradeDeprecatedFiles(QString root, QString instanceName)
|
|
||||||
{
|
|
||||||
auto versionJsonPath = FS::PathCombine(root, "version.json");
|
|
||||||
auto customJsonPath = FS::PathCombine(root, "custom.json");
|
|
||||||
auto mcJson = FS::PathCombine(root, "patches" , "net.minecraft.json");
|
|
||||||
|
|
||||||
QString sourceFile;
|
|
||||||
QString renameFile;
|
|
||||||
|
|
||||||
// convert old crap.
|
|
||||||
if(QFile::exists(customJsonPath))
|
|
||||||
{
|
|
||||||
sourceFile = customJsonPath;
|
|
||||||
renameFile = versionJsonPath;
|
|
||||||
}
|
|
||||||
else if(QFile::exists(versionJsonPath))
|
|
||||||
{
|
|
||||||
sourceFile = versionJsonPath;
|
|
||||||
}
|
|
||||||
if(!sourceFile.isEmpty() && !QFile::exists(mcJson))
|
|
||||||
{
|
|
||||||
if(!FS::ensureFilePathExists(mcJson))
|
|
||||||
{
|
|
||||||
qWarning() << "Couldn't create patches folder for" << instanceName;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(!renameFile.isEmpty() && QFile::exists(renameFile))
|
|
||||||
{
|
|
||||||
if(!QFile::rename(renameFile, renameFile + ".old"))
|
|
||||||
{
|
|
||||||
qWarning() << "Couldn't rename" << renameFile << "to" << renameFile + ".old" << "in" << instanceName;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto file = ProfileUtils::parseJsonFile(QFileInfo(sourceFile), false);
|
|
||||||
ProfileUtils::removeLwjglFromPatch(file);
|
|
||||||
file->uid = "net.minecraft";
|
|
||||||
file->version = file->minecraftVersion;
|
|
||||||
file->name = "Minecraft";
|
|
||||||
|
|
||||||
Meta::Require needsLwjgl;
|
|
||||||
needsLwjgl.uid = "org.lwjgl";
|
|
||||||
file->requires.insert(needsLwjgl);
|
|
||||||
|
|
||||||
if(!ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), mcJson))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(!QFile::rename(sourceFile, sourceFile + ".old"))
|
|
||||||
{
|
|
||||||
qWarning() << "Couldn't rename" << sourceFile << "to" << sourceFile + ".old" << "in" << instanceName;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Migrate old layout to the component based one...
|
|
||||||
* - Part of the version information is taken from `instance.cfg` (fed to this class from outside).
|
|
||||||
* - Part is taken from the old order.json file.
|
|
||||||
* - Part is loaded from loose json files in the instance's `patches` directory.
|
|
||||||
*/
|
|
||||||
bool PackProfile::migratePreComponentConfig()
|
|
||||||
{
|
|
||||||
// upgrade the very old files from the beginnings of MultiMC 5
|
|
||||||
upgradeDeprecatedFiles(d->m_instance->instanceRoot(), d->m_instance->name());
|
|
||||||
|
|
||||||
QList<ComponentPtr> components;
|
|
||||||
QSet<QString> loaded;
|
|
||||||
|
|
||||||
auto addBuiltinPatch = [&](const QString &uid, bool asDependency, const QString & emptyVersion, const Meta::Require & req, const Meta::Require & conflict)
|
|
||||||
{
|
|
||||||
auto jsonFilePath = FS::PathCombine(d->m_instance->instanceRoot(), "patches" , uid + ".json");
|
|
||||||
auto intendedVersion = d->getOldConfigVersion(uid);
|
|
||||||
// load up the base minecraft patch
|
|
||||||
ComponentPtr component;
|
|
||||||
if(QFile::exists(jsonFilePath))
|
|
||||||
{
|
|
||||||
if(intendedVersion.isEmpty())
|
|
||||||
{
|
|
||||||
intendedVersion = emptyVersion;
|
|
||||||
}
|
|
||||||
auto file = ProfileUtils::parseJsonFile(QFileInfo(jsonFilePath), false);
|
|
||||||
// fix uid
|
|
||||||
file->uid = uid;
|
|
||||||
// if version is missing, add it from the outside.
|
|
||||||
if(file->version.isEmpty())
|
|
||||||
{
|
|
||||||
file->version = intendedVersion;
|
|
||||||
}
|
|
||||||
// if this is a dependency (LWJGL), mark it also as volatile
|
|
||||||
if(asDependency)
|
|
||||||
{
|
|
||||||
file->m_volatile = true;
|
|
||||||
}
|
|
||||||
// insert requirements if needed
|
|
||||||
if(!req.uid.isEmpty())
|
|
||||||
{
|
|
||||||
file->requires.insert(req);
|
|
||||||
}
|
|
||||||
// insert conflicts if needed
|
|
||||||
if(!conflict.uid.isEmpty())
|
|
||||||
{
|
|
||||||
file->conflicts.insert(conflict);
|
|
||||||
}
|
|
||||||
// FIXME: @QUALITY do not ignore return value
|
|
||||||
ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), jsonFilePath);
|
|
||||||
component = new Component(this, uid, file);
|
|
||||||
component->m_version = intendedVersion;
|
|
||||||
}
|
|
||||||
else if(!intendedVersion.isEmpty())
|
|
||||||
{
|
|
||||||
auto metaVersion = APPLICATION->metadataIndex()->get(uid, intendedVersion);
|
|
||||||
component = new Component(this, metaVersion);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
component->m_dependencyOnly = asDependency;
|
|
||||||
component->m_important = !asDependency;
|
|
||||||
components.append(component);
|
|
||||||
};
|
|
||||||
// TODO: insert depends and conflicts here if these are customized files...
|
|
||||||
Meta::Require reqLwjgl;
|
|
||||||
reqLwjgl.uid = "org.lwjgl";
|
|
||||||
reqLwjgl.suggests = "2.9.1";
|
|
||||||
Meta::Require conflictLwjgl3;
|
|
||||||
conflictLwjgl3.uid = "org.lwjgl3";
|
|
||||||
Meta::Require nullReq;
|
|
||||||
addBuiltinPatch("org.lwjgl", true, "2.9.1", nullReq, conflictLwjgl3);
|
|
||||||
addBuiltinPatch("net.minecraft", false, QString(), reqLwjgl, nullReq);
|
|
||||||
|
|
||||||
// first, collect all other file-based patches and load them
|
|
||||||
QMap<QString, ComponentPtr> loadedComponents;
|
|
||||||
QDir patchesDir(FS::PathCombine(d->m_instance->instanceRoot(),"patches"));
|
|
||||||
for (auto info : patchesDir.entryInfoList(QStringList() << "*.json", QDir::Files))
|
|
||||||
{
|
|
||||||
// parse the file
|
|
||||||
qDebug() << "Reading" << info.fileName();
|
|
||||||
auto file = ProfileUtils::parseJsonFile(info, true);
|
|
||||||
|
|
||||||
// correct missing or wrong uid based on the file name
|
|
||||||
QString uid = info.completeBaseName();
|
|
||||||
|
|
||||||
// ignore builtins, they've been handled already
|
|
||||||
if (uid == "net.minecraft")
|
|
||||||
continue;
|
|
||||||
if (uid == "org.lwjgl")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// handle horrible corner cases
|
|
||||||
if(uid.isEmpty())
|
|
||||||
{
|
|
||||||
// if you have a file named '.json', make it just go away.
|
|
||||||
// FIXME: @QUALITY do not ignore return value
|
|
||||||
QFile::remove(info.absoluteFilePath());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
file->uid = uid;
|
|
||||||
// FIXME: @QUALITY do not ignore return value
|
|
||||||
ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), info.absoluteFilePath());
|
|
||||||
|
|
||||||
auto component = new Component(this, file->uid, file);
|
|
||||||
auto version = d->getOldConfigVersion(file->uid);
|
|
||||||
if(!version.isEmpty())
|
|
||||||
{
|
|
||||||
component->m_version = version;
|
|
||||||
}
|
|
||||||
loadedComponents[file->uid] = component;
|
|
||||||
}
|
|
||||||
// try to load the other 'hardcoded' patches (forge, liteloader), if they weren't loaded from files
|
|
||||||
auto loadSpecial = [&](const QString & uid, int order)
|
|
||||||
{
|
|
||||||
auto patchVersion = d->getOldConfigVersion(uid);
|
|
||||||
if(!patchVersion.isEmpty() && !loadedComponents.contains(uid))
|
|
||||||
{
|
|
||||||
auto patch = new Component(this, APPLICATION->metadataIndex()->get(uid, patchVersion));
|
|
||||||
patch->setOrder(order);
|
|
||||||
loadedComponents[uid] = patch;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
loadSpecial("net.minecraftforge", 5);
|
|
||||||
loadSpecial("com.mumfrey.liteloader", 10);
|
|
||||||
|
|
||||||
// load the old order.json file, if present
|
|
||||||
ProfileUtils::PatchOrder userOrder;
|
|
||||||
ProfileUtils::readOverrideOrders(FS::PathCombine(d->m_instance->instanceRoot(), "order.json"), userOrder);
|
|
||||||
|
|
||||||
// now add all the patches by user sort order
|
|
||||||
for (auto uid : userOrder)
|
|
||||||
{
|
|
||||||
// ignore builtins
|
|
||||||
if (uid == "net.minecraft")
|
|
||||||
continue;
|
|
||||||
if (uid == "org.lwjgl")
|
|
||||||
continue;
|
|
||||||
// ordering has a patch that is gone?
|
|
||||||
if(!loadedComponents.contains(uid))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
components.append(loadedComponents.take(uid));
|
|
||||||
}
|
|
||||||
|
|
||||||
// is there anything left to sort? - this is used when there are leftover components that aren't part of the order.json
|
|
||||||
if(!loadedComponents.isEmpty())
|
|
||||||
{
|
|
||||||
// inserting into multimap by order number as key sorts the patches and detects duplicates
|
|
||||||
QMultiMap<int, ComponentPtr> files;
|
|
||||||
auto iter = loadedComponents.begin();
|
|
||||||
while(iter != loadedComponents.end())
|
|
||||||
{
|
|
||||||
files.insert((*iter)->getOrder(), *iter);
|
|
||||||
iter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// then just extract the patches and put them in the list
|
|
||||||
for (auto order : files.keys())
|
|
||||||
{
|
|
||||||
const auto &values = files.values(order);
|
|
||||||
for(auto &value: values)
|
|
||||||
{
|
|
||||||
// TODO: put back the insertion of problem messages here, so the user knows about the id duplication
|
|
||||||
components.append(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// new we have a complete list of components...
|
|
||||||
return savePackProfile(componentsFilePath(), components);
|
|
||||||
}
|
|
||||||
|
|
||||||
// END: save/load
|
// END: save/load
|
||||||
|
|
||||||
void PackProfile::appendComponent(ComponentPtr component)
|
void PackProfile::appendComponent(ComponentPtr component)
|
||||||
@ -1169,15 +924,6 @@ std::shared_ptr<LaunchProfile> PackProfile::getProfile() const
|
|||||||
return d->m_profile;
|
return d->m_profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PackProfile::setOldConfigVersion(const QString& uid, const QString& version)
|
|
||||||
{
|
|
||||||
if(version.isEmpty())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
d->m_oldConfigVersions[uid] = version;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PackProfile::setComponentVersion(const QString& uid, const QString& version, bool important)
|
bool PackProfile::setComponentVersion(const QString& uid, const QString& version, bool important)
|
||||||
{
|
{
|
||||||
auto iter = d->componentIndex.find(uid);
|
auto iter = d->componentIndex.find(uid);
|
||||||
|
@ -143,8 +143,6 @@ private:
|
|||||||
bool installCustomJar_internal(QString filepath);
|
bool installCustomJar_internal(QString filepath);
|
||||||
bool removeComponent_internal(ComponentPtr patch);
|
bool removeComponent_internal(ComponentPtr patch);
|
||||||
|
|
||||||
bool migratePreComponentConfig();
|
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
|
|
||||||
std::unique_ptr<PackProfileData> d;
|
std::unique_ptr<PackProfileData> d;
|
||||||
|
@ -18,18 +18,6 @@ struct PackProfileData
|
|||||||
// the launch profile (volatile, temporary thing created on demand)
|
// the launch profile (volatile, temporary thing created on demand)
|
||||||
std::shared_ptr<LaunchProfile> m_profile;
|
std::shared_ptr<LaunchProfile> m_profile;
|
||||||
|
|
||||||
// version information migrated from instance.cfg file. Single use on migration!
|
|
||||||
std::map<QString, QString> m_oldConfigVersions;
|
|
||||||
QString getOldConfigVersion(const QString& uid) const
|
|
||||||
{
|
|
||||||
const auto iter = m_oldConfigVersions.find(uid);
|
|
||||||
if(iter != m_oldConfigVersions.cend())
|
|
||||||
{
|
|
||||||
return (*iter).second;
|
|
||||||
}
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// persistent list of components and related machinery
|
// persistent list of components and related machinery
|
||||||
ComponentContainer components;
|
ComponentContainer components;
|
||||||
ComponentIndex componentIndex;
|
ComponentIndex componentIndex;
|
||||||
|
@ -1,256 +0,0 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <minecraft/launch/LauncherPartLaunch.h>
|
|
||||||
#include <QDir>
|
|
||||||
#include <settings/Setting.h>
|
|
||||||
|
|
||||||
#include "LegacyInstance.h"
|
|
||||||
|
|
||||||
#include "minecraft/legacy/LegacyModList.h"
|
|
||||||
#include "minecraft/WorldList.h"
|
|
||||||
#include <MMCZip.h>
|
|
||||||
#include <FileSystem.h>
|
|
||||||
|
|
||||||
LegacyInstance::LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
|
|
||||||
: BaseInstance(globalSettings, settings, rootDir)
|
|
||||||
{
|
|
||||||
settings->registerSetting("NeedsRebuild", true);
|
|
||||||
settings->registerSetting("ShouldUpdate", false);
|
|
||||||
settings->registerSetting("JarVersion", QString());
|
|
||||||
settings->registerSetting("IntendedJarVersion", QString());
|
|
||||||
/*
|
|
||||||
* custom base jar has no default. it is determined in code... see the accessor methods for
|
|
||||||
*it
|
|
||||||
*
|
|
||||||
* for instances that DO NOT have the CustomBaseJar setting (legacy instances),
|
|
||||||
* [.]minecraft/bin/mcbackup.jar is the default base jar
|
|
||||||
*/
|
|
||||||
settings->registerSetting("UseCustomBaseJar", true);
|
|
||||||
settings->registerSetting("CustomBaseJar", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::mainJarToPreserve() const
|
|
||||||
{
|
|
||||||
bool customJar = m_settings->get("UseCustomBaseJar").toBool();
|
|
||||||
if(customJar)
|
|
||||||
{
|
|
||||||
auto base = baseJar();
|
|
||||||
if(QFile::exists(base))
|
|
||||||
{
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto runnable = runnableJar();
|
|
||||||
if(QFile::exists(runnable))
|
|
||||||
{
|
|
||||||
return runnable;
|
|
||||||
}
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QString LegacyInstance::baseJar() const
|
|
||||||
{
|
|
||||||
bool customJar = m_settings->get("UseCustomBaseJar").toBool();
|
|
||||||
if (customJar)
|
|
||||||
{
|
|
||||||
return customBaseJar();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return defaultBaseJar();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::customBaseJar() const
|
|
||||||
{
|
|
||||||
QString value = m_settings->get("CustomBaseJar").toString();
|
|
||||||
if (value.isNull() || value.isEmpty())
|
|
||||||
{
|
|
||||||
return defaultCustomBaseJar();
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LegacyInstance::shouldUseCustomBaseJar() const
|
|
||||||
{
|
|
||||||
return m_settings->get("UseCustomBaseJar").toBool();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Task::Ptr LegacyInstance::createUpdateTask(Net::Mode)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<LegacyModList> LegacyInstance::jarModList() const
|
|
||||||
{
|
|
||||||
if (!jar_mod_list)
|
|
||||||
{
|
|
||||||
auto list = new LegacyModList(jarModsDir(), modListFile());
|
|
||||||
jar_mod_list.reset(list);
|
|
||||||
}
|
|
||||||
jar_mod_list->update();
|
|
||||||
return jar_mod_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::gameRoot() const
|
|
||||||
{
|
|
||||||
QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
|
|
||||||
QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft"));
|
|
||||||
|
|
||||||
if (mcDir.exists() && !dotMCDir.exists())
|
|
||||||
return mcDir.filePath();
|
|
||||||
else
|
|
||||||
return dotMCDir.filePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::binRoot() const
|
|
||||||
{
|
|
||||||
return FS::PathCombine(gameRoot(), "bin");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::modsRoot() const {
|
|
||||||
return FS::PathCombine(gameRoot(), "mods");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QString LegacyInstance::jarModsDir() const
|
|
||||||
{
|
|
||||||
return FS::PathCombine(instanceRoot(), "instMods");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::libDir() const
|
|
||||||
{
|
|
||||||
return FS::PathCombine(gameRoot(), "lib");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::savesDir() const
|
|
||||||
{
|
|
||||||
return FS::PathCombine(gameRoot(), "saves");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::coreModsDir() const
|
|
||||||
{
|
|
||||||
return FS::PathCombine(gameRoot(), "coremods");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::resourceDir() const
|
|
||||||
{
|
|
||||||
return FS::PathCombine(gameRoot(), "resources");
|
|
||||||
}
|
|
||||||
QString LegacyInstance::texturePacksDir() const
|
|
||||||
{
|
|
||||||
return FS::PathCombine(gameRoot(), "texturepacks");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::runnableJar() const
|
|
||||||
{
|
|
||||||
return FS::PathCombine(binRoot(), "minecraft.jar");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::modListFile() const
|
|
||||||
{
|
|
||||||
return FS::PathCombine(instanceRoot(), "modlist");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::instanceConfigFolder() const
|
|
||||||
{
|
|
||||||
return FS::PathCombine(gameRoot(), "config");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LegacyInstance::shouldRebuild() const
|
|
||||||
{
|
|
||||||
return m_settings->get("NeedsRebuild").toBool();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::currentVersionId() const
|
|
||||||
{
|
|
||||||
return m_settings->get("JarVersion").toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::intendedVersionId() const
|
|
||||||
{
|
|
||||||
return m_settings->get("IntendedJarVersion").toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LegacyInstance::shouldUpdate() const
|
|
||||||
{
|
|
||||||
QVariant var = settings()->get("ShouldUpdate");
|
|
||||||
if (!var.isValid() || var.toBool() == false)
|
|
||||||
{
|
|
||||||
return intendedVersionId() != currentVersionId();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::defaultBaseJar() const
|
|
||||||
{
|
|
||||||
return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::defaultCustomBaseJar() const
|
|
||||||
{
|
|
||||||
return FS::PathCombine(binRoot(), "mcbackup.jar");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<WorldList> LegacyInstance::worldList() const
|
|
||||||
{
|
|
||||||
if (!m_world_list)
|
|
||||||
{
|
|
||||||
m_world_list.reset(new WorldList(savesDir()));
|
|
||||||
}
|
|
||||||
return m_world_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::typeName() const
|
|
||||||
{
|
|
||||||
return tr("Legacy");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LegacyInstance::getStatusbarDescription()
|
|
||||||
{
|
|
||||||
return tr("Instance from previous versions.");
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList LegacyInstance::verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
|
|
||||||
{
|
|
||||||
QStringList out;
|
|
||||||
|
|
||||||
auto alltraits = traits();
|
|
||||||
if(alltraits.size())
|
|
||||||
{
|
|
||||||
out << "Traits:";
|
|
||||||
for (auto trait : alltraits)
|
|
||||||
{
|
|
||||||
out << " " + trait;
|
|
||||||
}
|
|
||||||
out << "";
|
|
||||||
}
|
|
||||||
|
|
||||||
QString windowParams;
|
|
||||||
if (settings()->get("LaunchMaximized").toBool())
|
|
||||||
{
|
|
||||||
out << "Window size: max (if available)";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto width = settings()->get("MinecraftWinWidth").toInt();
|
|
||||||
auto height = settings()->get("MinecraftWinHeight").toInt();
|
|
||||||
out << "Window size: " + QString::number(width) + " x " + QString::number(height);
|
|
||||||
}
|
|
||||||
out << "";
|
|
||||||
return out;
|
|
||||||
}
|
|
@ -1,142 +0,0 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "BaseInstance.h"
|
|
||||||
#include "launch/LaunchTask.h"
|
|
||||||
|
|
||||||
class ModFolderModel;
|
|
||||||
class LegacyModList;
|
|
||||||
class WorldList;
|
|
||||||
class Task;
|
|
||||||
/*
|
|
||||||
* WHY: Legacy instances - from MultiMC 3 and 4 - are here only to provide a way to upgrade them to the current format.
|
|
||||||
*/
|
|
||||||
class LegacyInstance : public BaseInstance
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
|
|
||||||
explicit LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
|
|
||||||
|
|
||||||
virtual void saveNow() override {}
|
|
||||||
|
|
||||||
/// Path to the instance's minecraft.jar
|
|
||||||
QString runnableJar() const;
|
|
||||||
|
|
||||||
//! Path to the instance's modlist file.
|
|
||||||
QString modListFile() const;
|
|
||||||
|
|
||||||
////// Directories //////
|
|
||||||
QString libDir() const;
|
|
||||||
QString savesDir() const;
|
|
||||||
QString texturePacksDir() const;
|
|
||||||
QString jarModsDir() const;
|
|
||||||
QString coreModsDir() const;
|
|
||||||
QString resourceDir() const;
|
|
||||||
|
|
||||||
QString instanceConfigFolder() const override;
|
|
||||||
|
|
||||||
QString gameRoot() const override; // Path to the instance's minecraft directory.
|
|
||||||
QString modsRoot() const override; // Path to the instance's minecraft directory.
|
|
||||||
QString binRoot() const; // Path to the instance's minecraft bin directory.
|
|
||||||
|
|
||||||
/// Get the curent base jar of this instance. By default, it's the
|
|
||||||
/// versions/$version/$version.jar
|
|
||||||
QString baseJar() const;
|
|
||||||
|
|
||||||
/// the default base jar of this instance
|
|
||||||
QString defaultBaseJar() const;
|
|
||||||
/// the default custom base jar of this instance
|
|
||||||
QString defaultCustomBaseJar() const;
|
|
||||||
|
|
||||||
// the main jar that we actually want to keep when migrating the instance
|
|
||||||
QString mainJarToPreserve() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Whether or not custom base jar is used
|
|
||||||
*/
|
|
||||||
bool shouldUseCustomBaseJar() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* The value of the custom base jar
|
|
||||||
*/
|
|
||||||
QString customBaseJar() const;
|
|
||||||
|
|
||||||
std::shared_ptr<LegacyModList> jarModList() const;
|
|
||||||
std::shared_ptr<WorldList> worldList() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Whether or not the instance's minecraft.jar needs to be rebuilt.
|
|
||||||
* If this is true, when the instance launches, its jar mods will be
|
|
||||||
* re-added to a fresh minecraft.jar file.
|
|
||||||
*/
|
|
||||||
bool shouldRebuild() const;
|
|
||||||
|
|
||||||
QString currentVersionId() const;
|
|
||||||
QString intendedVersionId() const;
|
|
||||||
|
|
||||||
QSet<QString> traits() const override
|
|
||||||
{
|
|
||||||
return {"legacy-instance", "texturepacks"};
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual bool shouldUpdate() const;
|
|
||||||
virtual Task::Ptr createUpdateTask(Net::Mode mode) override;
|
|
||||||
|
|
||||||
virtual QString typeName() const override;
|
|
||||||
|
|
||||||
bool canLaunch() const override
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool canEdit() const override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool canExport() const override
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
shared_qobject_ptr<LaunchTask> createLaunchTask(
|
|
||||||
AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
IPathMatcher::Ptr getLogFileMatcher() override
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
QString getLogFileRoot() override
|
|
||||||
{
|
|
||||||
return gameRoot();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString getStatusbarDescription() override;
|
|
||||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override;
|
|
||||||
|
|
||||||
QProcessEnvironment createEnvironment() override
|
|
||||||
{
|
|
||||||
return QProcessEnvironment();
|
|
||||||
}
|
|
||||||
QMap<QString, QString> getVariables() const override
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
protected:
|
|
||||||
mutable std::shared_ptr<LegacyModList> jar_mod_list;
|
|
||||||
mutable std::shared_ptr<WorldList> m_world_list;
|
|
||||||
};
|
|
@ -1,136 +0,0 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "LegacyModList.h"
|
|
||||||
#include <FileSystem.h>
|
|
||||||
#include <QString>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
LegacyModList::LegacyModList(const QString &dir, const QString &list_file)
|
|
||||||
: m_dir(dir), m_list_file(list_file)
|
|
||||||
{
|
|
||||||
FS::ensureFolderPathExists(m_dir.absolutePath());
|
|
||||||
m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
|
|
||||||
m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OrderItem
|
|
||||||
{
|
|
||||||
QString id;
|
|
||||||
bool enabled = false;
|
|
||||||
};
|
|
||||||
typedef QList<OrderItem> OrderList;
|
|
||||||
|
|
||||||
static void internalSort(QList<LegacyModList::Mod> &what)
|
|
||||||
{
|
|
||||||
auto predicate = [](const LegacyModList::Mod &left, const LegacyModList::Mod &right)
|
|
||||||
{
|
|
||||||
return left.fileName().localeAwareCompare(right.fileName()) < 0;
|
|
||||||
};
|
|
||||||
std::sort(what.begin(), what.end(), predicate);
|
|
||||||
}
|
|
||||||
|
|
||||||
static OrderList readListFile(const QString &m_list_file)
|
|
||||||
{
|
|
||||||
OrderList itemList;
|
|
||||||
if (m_list_file.isNull() || m_list_file.isEmpty())
|
|
||||||
return itemList;
|
|
||||||
|
|
||||||
QFile textFile(m_list_file);
|
|
||||||
if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text))
|
|
||||||
return OrderList();
|
|
||||||
|
|
||||||
QTextStream textStream;
|
|
||||||
textStream.setAutoDetectUnicode(true);
|
|
||||||
textStream.setDevice(&textFile);
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
QString line = textStream.readLine();
|
|
||||||
if (line.isNull() || line.isEmpty())
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OrderItem it;
|
|
||||||
it.enabled = !line.endsWith(".disabled");
|
|
||||||
if (!it.enabled)
|
|
||||||
{
|
|
||||||
line.chop(9);
|
|
||||||
}
|
|
||||||
it.id = line;
|
|
||||||
itemList.append(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
textFile.close();
|
|
||||||
return itemList;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LegacyModList::update()
|
|
||||||
{
|
|
||||||
if (!m_dir.exists() || !m_dir.isReadable())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
QList<Mod> orderedMods;
|
|
||||||
QList<Mod> newMods;
|
|
||||||
m_dir.refresh();
|
|
||||||
auto folderContents = m_dir.entryInfoList();
|
|
||||||
|
|
||||||
// first, process the ordered items (if any)
|
|
||||||
OrderList listOrder = readListFile(m_list_file);
|
|
||||||
for (auto item : listOrder)
|
|
||||||
{
|
|
||||||
QFileInfo infoEnabled(m_dir.filePath(item.id));
|
|
||||||
QFileInfo infoDisabled(m_dir.filePath(item.id + ".disabled"));
|
|
||||||
int idxEnabled = folderContents.indexOf(infoEnabled);
|
|
||||||
int idxDisabled = folderContents.indexOf(infoDisabled);
|
|
||||||
bool isEnabled;
|
|
||||||
// if both enabled and disabled versions are present, it's a special case...
|
|
||||||
if (idxEnabled >= 0 && idxDisabled >= 0)
|
|
||||||
{
|
|
||||||
// we only process the one we actually have in the order file.
|
|
||||||
// and exactly as we have it.
|
|
||||||
// THIS IS A CORNER CASE
|
|
||||||
isEnabled = item.enabled;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// only one is present.
|
|
||||||
// we pick the one that we found.
|
|
||||||
// we assume the mod was enabled/disabled by external means
|
|
||||||
isEnabled = idxEnabled >= 0;
|
|
||||||
}
|
|
||||||
int idx = isEnabled ? idxEnabled : idxDisabled;
|
|
||||||
QFileInfo &info = isEnabled ? infoEnabled : infoDisabled;
|
|
||||||
// if the file from the index file exists
|
|
||||||
if (idx != -1)
|
|
||||||
{
|
|
||||||
// remove from the actual folder contents list
|
|
||||||
folderContents.takeAt(idx);
|
|
||||||
// append the new mod
|
|
||||||
orderedMods.append(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if there are any untracked files... append them sorted at the end
|
|
||||||
if (folderContents.size())
|
|
||||||
{
|
|
||||||
for (auto entry : folderContents)
|
|
||||||
{
|
|
||||||
newMods.append(entry);
|
|
||||||
}
|
|
||||||
internalSort(newMods);
|
|
||||||
orderedMods.append(newMods);
|
|
||||||
}
|
|
||||||
mods.swap(orderedMods);
|
|
||||||
return true;
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QList>
|
|
||||||
#include <QString>
|
|
||||||
#include <QDir>
|
|
||||||
|
|
||||||
class LegacyModList
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
using Mod = QFileInfo;
|
|
||||||
|
|
||||||
LegacyModList(const QString &dir, const QString &list_file = QString());
|
|
||||||
|
|
||||||
/// Reloads the mod list and returns true if the list changed.
|
|
||||||
bool update();
|
|
||||||
|
|
||||||
QDir dir()
|
|
||||||
{
|
|
||||||
return m_dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QList<Mod> & allMods()
|
|
||||||
{
|
|
||||||
return mods;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
QDir m_dir;
|
|
||||||
QString m_list_file;
|
|
||||||
QList<Mod> mods;
|
|
||||||
};
|
|
@ -1,138 +0,0 @@
|
|||||||
#include "LegacyUpgradeTask.h"
|
|
||||||
#include "settings/INISettingsObject.h"
|
|
||||||
#include "FileSystem.h"
|
|
||||||
#include "NullInstance.h"
|
|
||||||
#include "pathmatcher/RegexpMatcher.h"
|
|
||||||
#include <QtConcurrentRun>
|
|
||||||
#include "LegacyInstance.h"
|
|
||||||
#include "minecraft/MinecraftInstance.h"
|
|
||||||
#include "minecraft/PackProfile.h"
|
|
||||||
#include "LegacyModList.h"
|
|
||||||
#include "classparser.h"
|
|
||||||
|
|
||||||
LegacyUpgradeTask::LegacyUpgradeTask(InstancePtr origInstance)
|
|
||||||
{
|
|
||||||
m_origInstance = origInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LegacyUpgradeTask::executeTask()
|
|
||||||
{
|
|
||||||
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
|
|
||||||
|
|
||||||
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
|
|
||||||
folderCopy.followSymlinks(true);
|
|
||||||
|
|
||||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy);
|
|
||||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &LegacyUpgradeTask::copyFinished);
|
|
||||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &LegacyUpgradeTask::copyAborted);
|
|
||||||
m_copyFutureWatcher.setFuture(m_copyFuture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString decideVersion(const QString& currentVersion, const QString& intendedVersion)
|
|
||||||
{
|
|
||||||
if(intendedVersion != currentVersion)
|
|
||||||
{
|
|
||||||
if(!intendedVersion.isEmpty())
|
|
||||||
{
|
|
||||||
return intendedVersion;
|
|
||||||
}
|
|
||||||
else if(!currentVersion.isEmpty())
|
|
||||||
{
|
|
||||||
return currentVersion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(!intendedVersion.isEmpty())
|
|
||||||
{
|
|
||||||
return intendedVersion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LegacyUpgradeTask::copyFinished()
|
|
||||||
{
|
|
||||||
auto successful = m_copyFuture.result();
|
|
||||||
if(!successful)
|
|
||||||
{
|
|
||||||
emitFailed(tr("Instance folder copy failed."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto legacyInst = std::dynamic_pointer_cast<LegacyInstance>(m_origInstance);
|
|
||||||
|
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
|
||||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
|
||||||
instanceSettings->set("InstanceType", "OneSix");
|
|
||||||
// NOTE: this scope ensures the instance is fully saved before we emitSucceeded
|
|
||||||
{
|
|
||||||
MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
|
|
||||||
inst.setName(m_instName);
|
|
||||||
|
|
||||||
QString preferredVersionNumber = decideVersion(legacyInst->currentVersionId(), legacyInst->intendedVersionId());
|
|
||||||
if(preferredVersionNumber.isNull())
|
|
||||||
{
|
|
||||||
// try to decide version based on the jar(s?)
|
|
||||||
preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->baseJar());
|
|
||||||
if(preferredVersionNumber.isNull())
|
|
||||||
{
|
|
||||||
preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->runnableJar());
|
|
||||||
if(preferredVersionNumber.isNull())
|
|
||||||
{
|
|
||||||
emitFailed(tr("Could not decide Minecraft version."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto components = inst.getPackProfile();
|
|
||||||
components->buildingFromScratch();
|
|
||||||
components->setComponentVersion("net.minecraft", preferredVersionNumber, true);
|
|
||||||
|
|
||||||
QString jarPath = legacyInst->mainJarToPreserve();
|
|
||||||
if(!jarPath.isNull())
|
|
||||||
{
|
|
||||||
qDebug() << "Preserving base jar! : " << jarPath;
|
|
||||||
// FIXME: handle case when the jar is unreadable?
|
|
||||||
// TODO: check the hash, if it's the same as the upstream jar, do not do this
|
|
||||||
components->installCustomJar(jarPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto jarMods = legacyInst->jarModList()->allMods();
|
|
||||||
for(auto & jarMod: jarMods)
|
|
||||||
{
|
|
||||||
QString modPath = jarMod.absoluteFilePath();
|
|
||||||
qDebug() << "jarMod: " << modPath;
|
|
||||||
components->installJarMods({modPath});
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove all the extra garbage we no longer need
|
|
||||||
auto removeAll = [&](const QString &root, const QStringList &things)
|
|
||||||
{
|
|
||||||
for(auto &thing : things)
|
|
||||||
{
|
|
||||||
auto removePath = FS::PathCombine(root, thing);
|
|
||||||
QFileInfo stat(removePath);
|
|
||||||
if(stat.isDir())
|
|
||||||
{
|
|
||||||
FS::deletePath(removePath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QFile::remove(removePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
QStringList rootRemovables = {"modlist", "version", "instMods"};
|
|
||||||
QStringList mcRemovables = {"bin", "MultiMCLauncher.jar", "icon.png"};
|
|
||||||
removeAll(inst.instanceRoot(), rootRemovables);
|
|
||||||
removeAll(inst.gameRoot(), mcRemovables);
|
|
||||||
}
|
|
||||||
emitSucceeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LegacyUpgradeTask::copyAborted()
|
|
||||||
{
|
|
||||||
emitFailed(tr("Instance folder copy has been aborted."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "InstanceTask.h"
|
|
||||||
#include "net/NetJob.h"
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QFuture>
|
|
||||||
#include <QFutureWatcher>
|
|
||||||
#include "settings/SettingsObject.h"
|
|
||||||
#include "BaseVersion.h"
|
|
||||||
#include "BaseInstance.h"
|
|
||||||
|
|
||||||
|
|
||||||
class LegacyUpgradeTask : public InstanceTask
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit LegacyUpgradeTask(InstancePtr origInstance);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
//! Entry point for tasks.
|
|
||||||
virtual void executeTask() override;
|
|
||||||
void copyFinished();
|
|
||||||
void copyAborted();
|
|
||||||
|
|
||||||
private: /* data */
|
|
||||||
InstancePtr m_origInstance;
|
|
||||||
QFuture<bool> m_copyFuture;
|
|
||||||
QFutureWatcher<bool> m_copyFutureWatcher;
|
|
||||||
};
|
|
@ -720,8 +720,6 @@ void PackInstallTask::install()
|
|||||||
auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
|
auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
|
||||||
instanceSettings->suspendSave();
|
instanceSettings->suspendSave();
|
||||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
|
||||||
instanceSettings->set("InstanceType", "OneSix");
|
|
||||||
|
|
||||||
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||||
auto components = instance.getPackProfile();
|
auto components = instance.getPackProfile();
|
||||||
|
@ -122,8 +122,6 @@ void PackInstallTask::install()
|
|||||||
QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
|
auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
|
||||||
instanceSettings->suspendSave();
|
instanceSettings->suspendSave();
|
||||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
|
||||||
instanceSettings->set("InstanceType", "OneSix");
|
|
||||||
|
|
||||||
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||||
auto components = instance.getPackProfile();
|
auto components = instance.getPackProfile();
|
||||||
|
@ -181,8 +181,6 @@ void PackInstallTask::install()
|
|||||||
auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
|
auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
|
||||||
instanceSettings->suspendSave();
|
instanceSettings->suspendSave();
|
||||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
|
||||||
instanceSettings->set("InstanceType", "OneSix");
|
|
||||||
|
|
||||||
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||||
auto components = instance.getPackProfile();
|
auto components = instance.getPackProfile();
|
||||||
|
@ -31,8 +31,6 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const
|
|||||||
QString minecraftPath = FS::PathCombine(stagingPath, ".minecraft");
|
QString minecraftPath = FS::PathCombine(stagingPath, ".minecraft");
|
||||||
QString configPath = FS::PathCombine(stagingPath, "instance.cfg");
|
QString configPath = FS::PathCombine(stagingPath, "instance.cfg");
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
||||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
|
||||||
instanceSettings->set("InstanceType", "OneSix");
|
|
||||||
MinecraftInstance instance(globalSettings, instanceSettings, stagingPath);
|
MinecraftInstance instance(globalSettings, instanceSettings, stagingPath);
|
||||||
|
|
||||||
instance.setName(instName);
|
instance.setName(instName);
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
#include "LegacyUpgradePage.h"
|
|
||||||
#include "ui_LegacyUpgradePage.h"
|
|
||||||
|
|
||||||
#include "InstanceList.h"
|
|
||||||
#include "minecraft/legacy/LegacyInstance.h"
|
|
||||||
#include "minecraft/legacy/LegacyUpgradeTask.h"
|
|
||||||
#include "Application.h"
|
|
||||||
|
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
|
||||||
#include "ui/dialogs/ProgressDialog.h"
|
|
||||||
|
|
||||||
LegacyUpgradePage::LegacyUpgradePage(InstancePtr inst, QWidget *parent)
|
|
||||||
: QWidget(parent), ui(new Ui::LegacyUpgradePage), m_inst(inst)
|
|
||||||
{
|
|
||||||
ui->setupUi(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
LegacyUpgradePage::~LegacyUpgradePage()
|
|
||||||
{
|
|
||||||
delete ui;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LegacyUpgradePage::runModalTask(Task *task)
|
|
||||||
{
|
|
||||||
connect(task, &Task::failed, [this](QString reason)
|
|
||||||
{
|
|
||||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Warning)->show();
|
|
||||||
});
|
|
||||||
ProgressDialog loadDialog(this);
|
|
||||||
loadDialog.setSkipButton(true, tr("Abort"));
|
|
||||||
if(loadDialog.execWithTask(task) == QDialog::Accepted)
|
|
||||||
{
|
|
||||||
m_container->requestClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LegacyUpgradePage::on_upgradeButton_clicked()
|
|
||||||
{
|
|
||||||
QString newName = tr("%1 (Migrated)").arg(m_inst->name());
|
|
||||||
auto upgradeTask = new LegacyUpgradeTask(m_inst);
|
|
||||||
upgradeTask->setName(newName);
|
|
||||||
upgradeTask->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id()));
|
|
||||||
upgradeTask->setIcon(m_inst->iconKey());
|
|
||||||
unique_qobject_ptr<Task> task(APPLICATION->instances()->wrapInstanceTask(upgradeTask));
|
|
||||||
runModalTask(task.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LegacyUpgradePage::shouldDisplay() const
|
|
||||||
{
|
|
||||||
return !m_inst->isRunning();
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include "minecraft/legacy/LegacyInstance.h"
|
|
||||||
#include "ui/pages/BasePage.h"
|
|
||||||
#include <Application.h>
|
|
||||||
#include "tasks/Task.h"
|
|
||||||
|
|
||||||
namespace Ui
|
|
||||||
{
|
|
||||||
class LegacyUpgradePage;
|
|
||||||
}
|
|
||||||
|
|
||||||
class LegacyUpgradePage : public QWidget, public BasePage
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit LegacyUpgradePage(InstancePtr inst, QWidget *parent = 0);
|
|
||||||
virtual ~LegacyUpgradePage();
|
|
||||||
virtual QString displayName() const override
|
|
||||||
{
|
|
||||||
return tr("Upgrade");
|
|
||||||
}
|
|
||||||
virtual QIcon icon() const override
|
|
||||||
{
|
|
||||||
return APPLICATION->getThemedIcon("checkupdate");
|
|
||||||
}
|
|
||||||
virtual QString id() const override
|
|
||||||
{
|
|
||||||
return "upgrade";
|
|
||||||
}
|
|
||||||
virtual QString helpPage() const override
|
|
||||||
{
|
|
||||||
return "Legacy-upgrade";
|
|
||||||
}
|
|
||||||
virtual bool shouldDisplay() const override;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void on_upgradeButton_clicked();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void runModalTask(Task *task);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ui::LegacyUpgradePage *ui;
|
|
||||||
InstancePtr m_inst;
|
|
||||||
};
|
|
@ -1,54 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>LegacyUpgradePage</class>
|
|
||||||
<widget class="QWidget" name="LegacyUpgradePage">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>546</width>
|
|
||||||
<height>405</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QTextBrowser" name="textBrowser">
|
|
||||||
<property name="html">
|
|
||||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
|
||||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
|
||||||
p, li { white-space: pre-wrap; }
|
|
||||||
</style></head><body style=" font-family:'Noto Sans'; font-size:11pt; font-weight:400; font-style:normal;">
|
|
||||||
<h1 style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:600;">Upgrade is required</span></h1>
|
|
||||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PolyMC now supports old Minecraft versions and all the required features in the new (OneSix) instance format. As a consequence, the old (Legacy) format has been entirely disabled and old instances need to be upgraded.</p>
|
|
||||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The upgrade will create a new instance with the same contents as the current one, in the new format. The original instance will remain untouched, in case anything goes wrong in the process.</p>
|
|
||||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Please report any issues on our <a href="https://github.com/PolyMC/PolyMC/issues"><span style=" text-decoration: underline; color:#3584e4;">github issues page</span></a>.</p></body></html></string>
|
|
||||||
</property>
|
|
||||||
<property name="openExternalLinks">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCommandLinkButton" name="upgradeButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>Upgrade the instance</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
@ -4,209 +4,211 @@
|
|||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Json.h"
|
|
||||||
#include "ui/dialogs/ModDownloadDialog.h"
|
|
||||||
#include "InstanceImportTask.h"
|
|
||||||
#include "FlameModModel.h"
|
#include "FlameModModel.h"
|
||||||
|
#include "InstanceImportTask.h"
|
||||||
|
#include "Json.h"
|
||||||
#include "ModDownloadTask.h"
|
#include "ModDownloadTask.h"
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftInstance.h"
|
||||||
#include "minecraft/PackProfile.h"
|
#include "minecraft/PackProfile.h"
|
||||||
|
#include "ui/dialogs/ModDownloadDialog.h"
|
||||||
|
|
||||||
FlameModPage::FlameModPage(ModDownloadDialog *dialog, BaseInstance *instance)
|
FlameModPage::FlameModPage(ModDownloadDialog *dialog, BaseInstance *instance)
|
||||||
: QWidget(dialog), m_instance(instance), ui(new Ui::FlameModPage), dialog(dialog)
|
: QWidget(dialog), m_instance(instance), ui(new Ui::FlameModPage),
|
||||||
{
|
dialog(dialog) {
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
connect(ui->searchButton, &QPushButton::clicked, this, &FlameModPage::triggerSearch);
|
connect(ui->searchButton, &QPushButton::clicked, this,
|
||||||
ui->searchEdit->installEventFilter(this);
|
&FlameModPage::triggerSearch);
|
||||||
listModel = new FlameMod::ListModel(this);
|
ui->searchEdit->installEventFilter(this);
|
||||||
ui->packView->setModel(listModel);
|
listModel = new FlameMod::ListModel(this);
|
||||||
|
ui->packView->setModel(listModel);
|
||||||
|
|
||||||
ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(
|
||||||
ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
|
Qt::ScrollBarAsNeeded);
|
||||||
|
ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
|
||||||
|
|
||||||
// index is used to set the sorting with the flame api
|
// index is used to set the sorting with the flame api
|
||||||
ui->sortByBox->addItem(tr("Sort by Featured"));
|
ui->sortByBox->addItem(tr("Sort by Featured"));
|
||||||
ui->sortByBox->addItem(tr("Sort by Popularity"));
|
ui->sortByBox->addItem(tr("Sort by Popularity"));
|
||||||
ui->sortByBox->addItem(tr("Sort by last updated"));
|
ui->sortByBox->addItem(tr("Sort by last updated"));
|
||||||
ui->sortByBox->addItem(tr("Sort by Name"));
|
ui->sortByBox->addItem(tr("Sort by Name"));
|
||||||
ui->sortByBox->addItem(tr("Sort by Author"));
|
ui->sortByBox->addItem(tr("Sort by Author"));
|
||||||
ui->sortByBox->addItem(tr("Sort by Downloads"));
|
ui->sortByBox->addItem(tr("Sort by Downloads"));
|
||||||
|
|
||||||
connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch()));
|
connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this,
|
||||||
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FlameModPage::onSelectionChanged);
|
SLOT(triggerSearch()));
|
||||||
connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FlameModPage::onVersionSelectionChanged);
|
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged,
|
||||||
connect(ui->modSelectionButton, &QPushButton::clicked, this, &FlameModPage::onModSelected);
|
this, &FlameModPage::onSelectionChanged);
|
||||||
|
connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this,
|
||||||
|
&FlameModPage::onVersionSelectionChanged);
|
||||||
|
connect(ui->modSelectionButton, &QPushButton::clicked, this,
|
||||||
|
&FlameModPage::onModSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
FlameModPage::~FlameModPage()
|
FlameModPage::~FlameModPage() { delete ui; }
|
||||||
{
|
|
||||||
delete ui;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FlameModPage::eventFilter(QObject* watched, QEvent* event)
|
bool FlameModPage::eventFilter(QObject *watched, QEvent *event) {
|
||||||
{
|
if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) {
|
||||||
if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) {
|
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
if (keyEvent->key() == Qt::Key_Return) {
|
||||||
if (keyEvent->key() == Qt::Key_Return) {
|
triggerSearch();
|
||||||
triggerSearch();
|
keyEvent->accept();
|
||||||
keyEvent->accept();
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return QWidget::eventFilter(watched, event);
|
}
|
||||||
|
return QWidget::eventFilter(watched, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FlameModPage::shouldDisplay() const
|
bool FlameModPage::shouldDisplay() const { return true; }
|
||||||
{
|
|
||||||
return true;
|
void FlameModPage::openedImpl() {
|
||||||
|
updateSelectionButton();
|
||||||
|
triggerSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlameModPage::openedImpl()
|
void FlameModPage::triggerSearch() {
|
||||||
{
|
listModel->searchWithTerm(ui->searchEdit->text(),
|
||||||
updateSelectionButton();
|
ui->sortByBox->currentIndex());
|
||||||
triggerSearch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlameModPage::triggerSearch()
|
void FlameModPage::onSelectionChanged(QModelIndex first, QModelIndex second) {
|
||||||
{
|
ui->versionSelectionBox->clear();
|
||||||
listModel->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
void FlameModPage::onSelectionChanged(QModelIndex first, QModelIndex second)
|
if (!first.isValid()) {
|
||||||
{
|
return;
|
||||||
ui->versionSelectionBox->clear();
|
}
|
||||||
|
|
||||||
if(!first.isValid())
|
current = listModel->data(first, Qt::UserRole).value<FlameMod::IndexedPack>();
|
||||||
{
|
QString text = "";
|
||||||
|
QString name = current.name;
|
||||||
|
|
||||||
|
if (current.websiteUrl.isEmpty())
|
||||||
|
text = name;
|
||||||
|
else
|
||||||
|
text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>";
|
||||||
|
if (!current.authors.empty()) {
|
||||||
|
auto authorToStr = [](FlameMod::ModpackAuthor &author) {
|
||||||
|
if (author.url.isEmpty()) {
|
||||||
|
return author.name;
|
||||||
|
}
|
||||||
|
return QString("<a href=\"%1\">%2</a>").arg(author.url, author.name);
|
||||||
|
};
|
||||||
|
QStringList authorStrs;
|
||||||
|
for (auto &author : current.authors) {
|
||||||
|
authorStrs.push_back(authorToStr(author));
|
||||||
|
}
|
||||||
|
text += "<br>" + tr(" by ") + authorStrs.join(", ");
|
||||||
|
}
|
||||||
|
text += "<br><br>";
|
||||||
|
|
||||||
|
ui->packDescription->setHtml(text + current.description);
|
||||||
|
|
||||||
|
if (!current.versionsLoaded) {
|
||||||
|
qDebug() << "Loading flame mod versions";
|
||||||
|
|
||||||
|
ui->modSelectionButton->setText(tr("Loading versions..."));
|
||||||
|
ui->modSelectionButton->setEnabled(false);
|
||||||
|
|
||||||
|
auto netJob =
|
||||||
|
new NetJob(QString("Flame::ModVersions(%1)").arg(current.name),
|
||||||
|
APPLICATION->network());
|
||||||
|
auto response = new QByteArray();
|
||||||
|
int addonId = current.addonId;
|
||||||
|
netJob->addNetAction(Net::Download::makeByteArray(
|
||||||
|
QString("https://addons-ecs.forgesvc.net/api/v2/addon/%1/files")
|
||||||
|
.arg(addonId),
|
||||||
|
response));
|
||||||
|
|
||||||
|
QObject::connect(netJob, &NetJob::succeeded, this, [this, response] {
|
||||||
|
QJsonParseError parse_error;
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||||
|
if (parse_error.error != QJsonParseError::NoError) {
|
||||||
|
qWarning() << "Error while parsing JSON response from Flame at "
|
||||||
|
<< parse_error.offset
|
||||||
|
<< " reason: " << parse_error.errorString();
|
||||||
|
qWarning() << *response;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
QJsonArray arr = doc.array();
|
||||||
current = listModel->data(first, Qt::UserRole).value<FlameMod::IndexedPack>();
|
try {
|
||||||
QString text = "";
|
FlameMod::loadIndexedPackVersions(current, arr, APPLICATION->network(),
|
||||||
QString name = current.name;
|
m_instance);
|
||||||
|
} catch (const JSONValidationError &e) {
|
||||||
if (current.websiteUrl.isEmpty())
|
qDebug() << *response;
|
||||||
text = name;
|
qWarning() << "Error while reading Flame mod version: " << e.cause();
|
||||||
else
|
}
|
||||||
text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>";
|
auto packProfile = ((MinecraftInstance *)m_instance)->getPackProfile();
|
||||||
if (!current.authors.empty()) {
|
QString mcVersion = packProfile->getComponentVersion("net.minecraft");
|
||||||
auto authorToStr = [](FlameMod::ModpackAuthor & author) {
|
QString loaderString =
|
||||||
if(author.url.isEmpty()) {
|
(packProfile->getComponentVersion("net.minecraftforge").isEmpty())
|
||||||
return author.name;
|
? "fabric"
|
||||||
}
|
: "forge";
|
||||||
return QString("<a href=\"%1\">%2</a>").arg(author.url, author.name);
|
for (int i = 0; i < current.versions.size(); i++) {
|
||||||
};
|
auto version = current.versions[i];
|
||||||
QStringList authorStrs;
|
if (!version.mcVersion.contains(mcVersion)) {
|
||||||
for(auto & author: current.authors) {
|
continue;
|
||||||
authorStrs.push_back(authorToStr(author));
|
|
||||||
}
|
}
|
||||||
text += "<br>" + tr(" by ") + authorStrs.join(", ");
|
ui->versionSelectionBox->addItem(version.version, QVariant(i));
|
||||||
|
}
|
||||||
|
if (ui->versionSelectionBox->count() == 0) {
|
||||||
|
ui->versionSelectionBox->addItem(tr("No Valid Version found!"),
|
||||||
|
QVariant(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->modSelectionButton->setText(tr("Cannot select invalid version :("));
|
||||||
|
updateSelectionButton();
|
||||||
|
});
|
||||||
|
QObject::connect(netJob, &NetJob::finished, this, [response, netJob] {
|
||||||
|
netJob->deleteLater();
|
||||||
|
delete response;
|
||||||
|
});
|
||||||
|
netJob->start();
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < current.versions.size(); i++) {
|
||||||
|
ui->versionSelectionBox->addItem(current.versions[i].version,
|
||||||
|
QVariant(i));
|
||||||
}
|
}
|
||||||
text += "<br><br>";
|
if (ui->versionSelectionBox->count() == 0) {
|
||||||
|
ui->versionSelectionBox->addItem(tr("No Valid Version found!"),
|
||||||
ui->packDescription->setHtml(text + current.description);
|
QVariant(-1));
|
||||||
|
|
||||||
if (!current.versionsLoaded)
|
|
||||||
{
|
|
||||||
qDebug() << "Loading flame mod versions";
|
|
||||||
|
|
||||||
ui->modSelectionButton->setText(tr("Loading versions..."));
|
|
||||||
ui->modSelectionButton->setEnabled(false);
|
|
||||||
|
|
||||||
auto netJob = new NetJob(QString("Flame::ModVersions(%1)").arg(current.name), APPLICATION->network());
|
|
||||||
std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>();
|
|
||||||
int addonId = current.addonId;
|
|
||||||
netJob->addNetAction(Net::Download::makeByteArray(QString("https://addons-ecs.forgesvc.net/api/v2/addon/%1/files").arg(addonId), response.get()));
|
|
||||||
|
|
||||||
QObject::connect(netJob, &NetJob::succeeded, this, [this, response, netJob]
|
|
||||||
{
|
|
||||||
netJob->deleteLater();
|
|
||||||
QJsonParseError parse_error;
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
|
||||||
if(parse_error.error != QJsonParseError::NoError) {
|
|
||||||
qWarning() << "Error while parsing JSON response from Flame at " << parse_error.offset << " reason: " << parse_error.errorString();
|
|
||||||
qWarning() << *response;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QJsonArray arr = doc.array();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
FlameMod::loadIndexedPackVersions(current, arr, APPLICATION->network(), m_instance);
|
|
||||||
}
|
|
||||||
catch(const JSONValidationError &e)
|
|
||||||
{
|
|
||||||
qDebug() << *response;
|
|
||||||
qWarning() << "Error while reading Flame mod version: " << e.cause();
|
|
||||||
}
|
|
||||||
auto packProfile = ((MinecraftInstance *)m_instance)->getPackProfile();
|
|
||||||
QString mcVersion = packProfile->getComponentVersion("net.minecraft");
|
|
||||||
QString loaderString = (packProfile->getComponentVersion("net.minecraftforge").isEmpty()) ? "fabric" : "forge";
|
|
||||||
for(int i = 0; i < current.versions.size(); i++) {
|
|
||||||
auto version = current.versions[i];
|
|
||||||
if(!version.mcVersion.contains(mcVersion)){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ui->versionSelectionBox->addItem(version.version, QVariant(i));
|
|
||||||
}
|
|
||||||
if(ui->versionSelectionBox->count() == 0){
|
|
||||||
ui->versionSelectionBox->addItem(tr("No Valid Version found!"), QVariant(-1));
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->modSelectionButton->setText(tr("Cannot select invalid version :("));
|
|
||||||
updateSelectionButton();
|
|
||||||
});
|
|
||||||
netJob->start();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for(int i = 0; i < current.versions.size(); i++) {
|
|
||||||
ui->versionSelectionBox->addItem(current.versions[i].version, QVariant(i));
|
|
||||||
}
|
|
||||||
if(ui->versionSelectionBox->count() == 0){
|
|
||||||
ui->versionSelectionBox->addItem(tr("No Valid Version found!"), QVariant(-1));
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSelectionButton();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FlameModPage::updateSelectionButton()
|
|
||||||
{
|
|
||||||
if(!isOpened || selectedVersion < 0){
|
|
||||||
ui->modSelectionButton->setEnabled(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->modSelectionButton->setEnabled(true);
|
|
||||||
auto& version = current.versions[selectedVersion];
|
|
||||||
if(!dialog->isModSelected(current.name, version.fileName)){
|
|
||||||
ui->modSelectionButton->setText(tr("Select mod for download"));
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
ui->modSelectionButton->setText(tr("Deselect mod for download"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FlameModPage::onVersionSelectionChanged(QString data)
|
|
||||||
{
|
|
||||||
if(data.isNull() || data.isEmpty())
|
|
||||||
{
|
|
||||||
selectedVersion = -1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
selectedVersion = ui->versionSelectionBox->currentData().toInt();
|
|
||||||
updateSelectionButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FlameModPage::onModSelected()
|
|
||||||
{
|
|
||||||
auto& version = current.versions[selectedVersion];
|
|
||||||
if (dialog->isModSelected(current.name, version.fileName)){
|
|
||||||
dialog->removeSelectedMod(current.name);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
dialog->addSelectedMod(current.name, new ModDownloadTask(version.downloadUrl, version.fileName , dialog->mods));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSelectionButton();
|
updateSelectionButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlameModPage::updateSelectionButton() {
|
||||||
|
if (!isOpened || selectedVersion < 0) {
|
||||||
|
ui->modSelectionButton->setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->modSelectionButton->setEnabled(true);
|
||||||
|
auto &version = current.versions[selectedVersion];
|
||||||
|
if (!dialog->isModSelected(current.name, version.fileName)) {
|
||||||
|
ui->modSelectionButton->setText(tr("Select mod for download"));
|
||||||
|
} else {
|
||||||
|
ui->modSelectionButton->setText(tr("Deselect mod for download"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlameModPage::onVersionSelectionChanged(QString data) {
|
||||||
|
if (data.isNull() || data.isEmpty()) {
|
||||||
|
selectedVersion = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedVersion = ui->versionSelectionBox->currentData().toInt();
|
||||||
|
updateSelectionButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlameModPage::onModSelected() {
|
||||||
|
auto &version = current.versions[selectedVersion];
|
||||||
|
if (dialog->isModSelected(current.name, version.fileName)) {
|
||||||
|
dialog->removeSelectedMod(current.name);
|
||||||
|
} else {
|
||||||
|
dialog->addSelectedMod(current.name,
|
||||||
|
new ModDownloadTask(version.downloadUrl,
|
||||||
|
version.fileName, dialog->mods));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSelectionButton();
|
||||||
}
|
}
|
||||||
|
@ -4,194 +4,199 @@
|
|||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Json.h"
|
|
||||||
#include "ui/dialogs/ModDownloadDialog.h"
|
|
||||||
#include "InstanceImportTask.h"
|
#include "InstanceImportTask.h"
|
||||||
#include "ModrinthModel.h"
|
#include "Json.h"
|
||||||
#include "ModDownloadTask.h"
|
#include "ModDownloadTask.h"
|
||||||
|
#include "ModrinthModel.h"
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftInstance.h"
|
||||||
#include "minecraft/PackProfile.h"
|
#include "minecraft/PackProfile.h"
|
||||||
|
#include "ui/dialogs/ModDownloadDialog.h"
|
||||||
|
|
||||||
ModrinthPage::ModrinthPage(ModDownloadDialog *dialog, BaseInstance *instance)
|
ModrinthPage::ModrinthPage(ModDownloadDialog *dialog, BaseInstance *instance)
|
||||||
: QWidget(dialog), m_instance(instance), ui(new Ui::ModrinthPage), dialog(dialog)
|
: QWidget(dialog), m_instance(instance), ui(new Ui::ModrinthPage),
|
||||||
{
|
dialog(dialog) {
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
connect(ui->searchButton, &QPushButton::clicked, this, &ModrinthPage::triggerSearch);
|
connect(ui->searchButton, &QPushButton::clicked, this,
|
||||||
ui->searchEdit->installEventFilter(this);
|
&ModrinthPage::triggerSearch);
|
||||||
listModel = new Modrinth::ListModel(this);
|
ui->searchEdit->installEventFilter(this);
|
||||||
ui->packView->setModel(listModel);
|
listModel = new Modrinth::ListModel(this);
|
||||||
|
ui->packView->setModel(listModel);
|
||||||
|
|
||||||
ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(
|
||||||
ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
|
Qt::ScrollBarAsNeeded);
|
||||||
|
ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
|
||||||
|
|
||||||
// index is used to set the sorting with the modrinth api
|
// index is used to set the sorting with the modrinth api
|
||||||
ui->sortByBox->addItem(tr("Sort by Relevence"));
|
ui->sortByBox->addItem(tr("Sort by Relevence"));
|
||||||
ui->sortByBox->addItem(tr("Sort by Downloads"));
|
ui->sortByBox->addItem(tr("Sort by Downloads"));
|
||||||
ui->sortByBox->addItem(tr("Sort by Follows"));
|
ui->sortByBox->addItem(tr("Sort by Follows"));
|
||||||
ui->sortByBox->addItem(tr("Sort by last updated"));
|
ui->sortByBox->addItem(tr("Sort by last updated"));
|
||||||
ui->sortByBox->addItem(tr("Sort by newest"));
|
ui->sortByBox->addItem(tr("Sort by newest"));
|
||||||
|
|
||||||
connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch()));
|
connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this,
|
||||||
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthPage::onSelectionChanged);
|
SLOT(triggerSearch()));
|
||||||
connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &ModrinthPage::onVersionSelectionChanged);
|
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged,
|
||||||
connect(ui->modSelectionButton, &QPushButton::clicked, this, &ModrinthPage::onModSelected);
|
this, &ModrinthPage::onSelectionChanged);
|
||||||
|
connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this,
|
||||||
|
&ModrinthPage::onVersionSelectionChanged);
|
||||||
|
connect(ui->modSelectionButton, &QPushButton::clicked, this,
|
||||||
|
&ModrinthPage::onModSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
ModrinthPage::~ModrinthPage()
|
ModrinthPage::~ModrinthPage() { delete ui; }
|
||||||
{
|
|
||||||
delete ui;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ModrinthPage::eventFilter(QObject* watched, QEvent* event)
|
bool ModrinthPage::eventFilter(QObject *watched, QEvent *event) {
|
||||||
{
|
if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) {
|
||||||
if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) {
|
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
if (keyEvent->key() == Qt::Key_Return) {
|
||||||
if (keyEvent->key() == Qt::Key_Return) {
|
triggerSearch();
|
||||||
triggerSearch();
|
keyEvent->accept();
|
||||||
keyEvent->accept();
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return QWidget::eventFilter(watched, event);
|
}
|
||||||
|
return QWidget::eventFilter(watched, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ModrinthPage::shouldDisplay() const
|
bool ModrinthPage::shouldDisplay() const { return true; }
|
||||||
{
|
|
||||||
return true;
|
void ModrinthPage::openedImpl() {
|
||||||
|
updateSelectionButton();
|
||||||
|
triggerSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModrinthPage::openedImpl()
|
void ModrinthPage::triggerSearch() {
|
||||||
{
|
listModel->searchWithTerm(ui->searchEdit->text(),
|
||||||
updateSelectionButton();
|
ui->sortByBox->currentIndex());
|
||||||
triggerSearch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModrinthPage::triggerSearch()
|
void ModrinthPage::onSelectionChanged(QModelIndex first, QModelIndex second) {
|
||||||
{
|
ui->versionSelectionBox->clear();
|
||||||
listModel->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModrinthPage::onSelectionChanged(QModelIndex first, QModelIndex second)
|
if (!first.isValid()) {
|
||||||
{
|
return;
|
||||||
ui->versionSelectionBox->clear();
|
}
|
||||||
|
|
||||||
if(!first.isValid())
|
current = listModel->data(first, Qt::UserRole).value<Modrinth::IndexedPack>();
|
||||||
{
|
QString text = "";
|
||||||
|
QString name = current.name;
|
||||||
|
|
||||||
|
if (current.websiteUrl.isEmpty())
|
||||||
|
text = name;
|
||||||
|
else
|
||||||
|
text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>";
|
||||||
|
text += "<br>" + tr(" by ") + "<a href=\"" + current.author.url + "\">" +
|
||||||
|
current.author.name + "</a><br><br>";
|
||||||
|
ui->packDescription->setHtml(text + current.description);
|
||||||
|
|
||||||
|
if (!current.versionsLoaded) {
|
||||||
|
qDebug() << "Loading Modrinth mod versions";
|
||||||
|
|
||||||
|
ui->modSelectionButton->setText(tr("Loading versions..."));
|
||||||
|
ui->modSelectionButton->setEnabled(false);
|
||||||
|
|
||||||
|
auto netJob =
|
||||||
|
new NetJob(QString("Modrinth::ModVersions(%1)").arg(current.name),
|
||||||
|
APPLICATION->network());
|
||||||
|
auto response = new QByteArray();
|
||||||
|
QString addonId = current.addonId;
|
||||||
|
netJob->addNetAction(Net::Download::makeByteArray(
|
||||||
|
QString("https://api.modrinth.com/v2/project/%1/version").arg(addonId),
|
||||||
|
response));
|
||||||
|
|
||||||
|
QObject::connect(netJob, &NetJob::succeeded, this, [this, response] {
|
||||||
|
QJsonParseError parse_error;
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||||
|
if (parse_error.error != QJsonParseError::NoError) {
|
||||||
|
qWarning() << "Error while parsing JSON response from Modrinth at "
|
||||||
|
<< parse_error.offset
|
||||||
|
<< " reason: " << parse_error.errorString();
|
||||||
|
qWarning() << *response;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
QJsonArray arr = doc.array();
|
||||||
current = listModel->data(first, Qt::UserRole).value<Modrinth::IndexedPack>();
|
try {
|
||||||
QString text = "";
|
Modrinth::loadIndexedPackVersions(current, arr, APPLICATION->network(),
|
||||||
QString name = current.name;
|
m_instance);
|
||||||
|
} catch (const JSONValidationError &e) {
|
||||||
if (current.websiteUrl.isEmpty())
|
qDebug() << *response;
|
||||||
text = name;
|
qWarning() << "Error while reading Modrinth mod version: " << e.cause();
|
||||||
else
|
}
|
||||||
text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>";
|
auto packProfile = ((MinecraftInstance *)m_instance)->getPackProfile();
|
||||||
text += "<br>"+ tr(" by ") + "<a href=\""+current.author.url+"\">"+current.author.name+"</a><br><br>";
|
QString mcVersion = packProfile->getComponentVersion("net.minecraft");
|
||||||
ui->packDescription->setHtml(text + current.description);
|
QString loaderString =
|
||||||
|
(packProfile->getComponentVersion("net.minecraftforge").isEmpty())
|
||||||
if (!current.versionsLoaded)
|
? "fabric"
|
||||||
{
|
: "forge";
|
||||||
qDebug() << "Loading Modrinth mod versions";
|
for (int i = 0; i < current.versions.size(); i++) {
|
||||||
|
auto version = current.versions[i];
|
||||||
ui->modSelectionButton->setText(tr("Loading versions..."));
|
if (!version.mcVersion.contains(mcVersion) ||
|
||||||
ui->modSelectionButton->setEnabled(false);
|
!version.loaders.contains(loaderString)) {
|
||||||
|
continue;
|
||||||
auto netJob = new NetJob(QString("Modrinth::ModVersions(%1)").arg(current.name), APPLICATION->network());
|
|
||||||
std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>();
|
|
||||||
QString addonId = current.addonId;
|
|
||||||
netJob->addNetAction(Net::Download::makeByteArray(QString("https://api.modrinth.com/v2/project/%1/version").arg(addonId), response.get()));
|
|
||||||
|
|
||||||
QObject::connect(netJob, &NetJob::succeeded, this, [this, response, netJob]
|
|
||||||
{
|
|
||||||
netJob->deleteLater();
|
|
||||||
QJsonParseError parse_error;
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
|
||||||
if(parse_error.error != QJsonParseError::NoError) {
|
|
||||||
qWarning() << "Error while parsing JSON response from Modrinth at " << parse_error.offset << " reason: " << parse_error.errorString();
|
|
||||||
qWarning() << *response;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QJsonArray arr = doc.array();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Modrinth::loadIndexedPackVersions(current, arr, APPLICATION->network(), m_instance);
|
|
||||||
}
|
|
||||||
catch(const JSONValidationError &e)
|
|
||||||
{
|
|
||||||
qDebug() << *response;
|
|
||||||
qWarning() << "Error while reading Modrinth mod version: " << e.cause();
|
|
||||||
}
|
|
||||||
auto packProfile = ((MinecraftInstance *)m_instance)->getPackProfile();
|
|
||||||
QString mcVersion = packProfile->getComponentVersion("net.minecraft");
|
|
||||||
QString loaderString = (packProfile->getComponentVersion("net.minecraftforge").isEmpty()) ? "fabric" : "forge";
|
|
||||||
for(int i = 0; i < current.versions.size(); i++) {
|
|
||||||
auto version = current.versions[i];
|
|
||||||
if(!version.mcVersion.contains(mcVersion) || !version.loaders.contains(loaderString)){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ui->versionSelectionBox->addItem(version.version, QVariant(i));
|
|
||||||
}
|
|
||||||
if(ui->versionSelectionBox->count() == 0){
|
|
||||||
ui->versionSelectionBox->addItem(tr("No Valid Version found !"), QVariant(-1));
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->modSelectionButton->setText(tr("Cannot select invalid version :("));
|
|
||||||
updateSelectionButton();
|
|
||||||
});
|
|
||||||
|
|
||||||
netJob->start();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for(int i = 0; i < current.versions.size(); i++) {
|
|
||||||
ui->versionSelectionBox->addItem(current.versions[i].version, QVariant(i));
|
|
||||||
}
|
|
||||||
if(ui->versionSelectionBox->count() == 0){
|
|
||||||
ui->versionSelectionBox->addItem(tr("No Valid Version found !"), QVariant(-1));
|
|
||||||
}
|
}
|
||||||
|
ui->versionSelectionBox->addItem(version.version, QVariant(i));
|
||||||
|
}
|
||||||
|
if (ui->versionSelectionBox->count() == 0) {
|
||||||
|
ui->versionSelectionBox->addItem(tr("No Valid Version found !"),
|
||||||
|
QVariant(-1));
|
||||||
|
}
|
||||||
|
|
||||||
updateSelectionButton();
|
ui->modSelectionButton->setText(tr("Cannot select invalid version :("));
|
||||||
}
|
updateSelectionButton();
|
||||||
}
|
});
|
||||||
|
|
||||||
void ModrinthPage::updateSelectionButton()
|
QObject::connect(netJob, &NetJob::finished, this, [response, netJob] {
|
||||||
{
|
netJob->deleteLater();
|
||||||
if(!isOpened || selectedVersion < 0){
|
delete response;
|
||||||
ui->modSelectionButton->setEnabled(false);
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->modSelectionButton->setEnabled(true);
|
netJob->start();
|
||||||
auto& version = current.versions[selectedVersion];
|
} else {
|
||||||
if(!dialog->isModSelected(current.name, version.fileName)){
|
for (int i = 0; i < current.versions.size(); i++) {
|
||||||
ui->modSelectionButton->setText(tr("Select mod for download"));
|
ui->versionSelectionBox->addItem(current.versions[i].version,
|
||||||
|
QVariant(i));
|
||||||
}
|
}
|
||||||
else{
|
if (ui->versionSelectionBox->count() == 0) {
|
||||||
ui->modSelectionButton->setText(tr("Deselect mod for download"));
|
ui->versionSelectionBox->addItem(tr("No Valid Version found !"),
|
||||||
}
|
QVariant(-1));
|
||||||
}
|
|
||||||
|
|
||||||
void ModrinthPage::onVersionSelectionChanged(QString data)
|
|
||||||
{
|
|
||||||
if (data.isNull() || data.isEmpty()){
|
|
||||||
selectedVersion = -1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
selectedVersion = ui->versionSelectionBox->currentData().toInt();
|
|
||||||
updateSelectionButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModrinthPage::onModSelected()
|
|
||||||
{
|
|
||||||
auto& version = current.versions[selectedVersion];
|
|
||||||
if (dialog->isModSelected(current.name, version.fileName)){
|
|
||||||
dialog->removeSelectedMod(current.name);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
dialog->addSelectedMod(current.name, new ModDownloadTask(version.downloadUrl, version.fileName , dialog->mods));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSelectionButton();
|
updateSelectionButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModrinthPage::updateSelectionButton() {
|
||||||
|
if (!isOpened || selectedVersion < 0) {
|
||||||
|
ui->modSelectionButton->setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->modSelectionButton->setEnabled(true);
|
||||||
|
auto &version = current.versions[selectedVersion];
|
||||||
|
if (!dialog->isModSelected(current.name, version.fileName)) {
|
||||||
|
ui->modSelectionButton->setText(tr("Select mod for download"));
|
||||||
|
} else {
|
||||||
|
ui->modSelectionButton->setText(tr("Deselect mod for download"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModrinthPage::onVersionSelectionChanged(QString data) {
|
||||||
|
if (data.isNull() || data.isEmpty()) {
|
||||||
|
selectedVersion = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedVersion = ui->versionSelectionBox->currentData().toInt();
|
||||||
|
updateSelectionButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModrinthPage::onModSelected() {
|
||||||
|
auto &version = current.versions[selectedVersion];
|
||||||
|
if (dialog->isModSelected(current.name, version.fileName)) {
|
||||||
|
dialog->removeSelectedMod(current.name);
|
||||||
|
} else {
|
||||||
|
dialog->addSelectedMod(current.name,
|
||||||
|
new ModDownloadTask(version.downloadUrl,
|
||||||
|
version.fileName, dialog->mods));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSelectionButton();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user