From 13628e7a8260b9407b0d44069f5bc1ecab585f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 24 Jul 2017 09:01:37 +0200 Subject: NOISSUE merging of strategy into profile, onesix into minecraft --- api/logic/BaseInstaller.cpp | 8 +- api/logic/BaseInstaller.h | 10 +- api/logic/BaseInstance.cpp | 1 + api/logic/BaseInstance.h | 29 - api/logic/CMakeLists.txt | 33 +- api/logic/FolderInstanceProvider.cpp | 4 +- api/logic/InstanceCreationTask.cpp | 12 +- api/logic/InstanceImportTask.cpp | 6 +- api/logic/NullInstance.h | 27 - api/logic/meta/JsonFormat.cpp | 2 +- api/logic/minecraft/Library_test.cpp | 2 +- api/logic/minecraft/MinecraftInstance.cpp | 584 ++++++++++++++++- api/logic/minecraft/MinecraftInstance.h | 117 ++-- api/logic/minecraft/MinecraftProfile.cpp | 439 ++++++++++++- api/logic/minecraft/MinecraftProfile.h | 47 +- api/logic/minecraft/MinecraftUpdate.cpp | 203 ++++++ api/logic/minecraft/MinecraftUpdate.h | 55 ++ api/logic/minecraft/MojangVersionFormat.cpp | 2 +- api/logic/minecraft/OneSixVersionFormat.cpp | 332 ++++++++++ api/logic/minecraft/OneSixVersionFormat.h | 29 + api/logic/minecraft/ProfileStrategy.h | 39 -- api/logic/minecraft/ProfileUtils.cpp | 2 +- api/logic/minecraft/launch/ModMinecraftJar.cpp | 52 +- api/logic/minecraft/launch/ModMinecraftJar.h | 6 - api/logic/minecraft/onesix/OneSixInstance.cpp | 706 --------------------- api/logic/minecraft/onesix/OneSixInstance.h | 128 ---- .../minecraft/onesix/OneSixProfileStrategy.cpp | 471 -------------- api/logic/minecraft/onesix/OneSixProfileStrategy.h | 27 - api/logic/minecraft/onesix/OneSixUpdate.cpp | 203 ------ api/logic/minecraft/onesix/OneSixUpdate.h | 55 -- api/logic/minecraft/onesix/OneSixVersionFormat.cpp | 332 ---------- api/logic/minecraft/onesix/OneSixVersionFormat.h | 29 - .../minecraft/onesix/update/AssetUpdateTask.cpp | 99 --- .../minecraft/onesix/update/AssetUpdateTask.h | 26 - .../minecraft/onesix/update/FMLLibrariesTask.cpp | 133 ---- .../minecraft/onesix/update/FMLLibrariesTask.h | 28 - api/logic/minecraft/onesix/update/FoldersTask.cpp | 21 - api/logic/minecraft/onesix/update/FoldersTask.h | 15 - .../minecraft/onesix/update/LibrariesTask.cpp | 94 --- api/logic/minecraft/onesix/update/LibrariesTask.h | 25 - api/logic/minecraft/update/AssetUpdateTask.cpp | 100 +++ api/logic/minecraft/update/AssetUpdateTask.h | 26 + api/logic/minecraft/update/FMLLibrariesTask.cpp | 133 ++++ api/logic/minecraft/update/FMLLibrariesTask.h | 28 + api/logic/minecraft/update/FoldersTask.cpp | 21 + api/logic/minecraft/update/FoldersTask.h | 15 + api/logic/minecraft/update/LibrariesTask.cpp | 95 +++ api/logic/minecraft/update/LibrariesTask.h | 25 + 48 files changed, 2205 insertions(+), 2671 deletions(-) create mode 100644 api/logic/minecraft/MinecraftUpdate.cpp create mode 100644 api/logic/minecraft/MinecraftUpdate.h create mode 100644 api/logic/minecraft/OneSixVersionFormat.cpp create mode 100644 api/logic/minecraft/OneSixVersionFormat.h delete mode 100644 api/logic/minecraft/ProfileStrategy.h delete mode 100644 api/logic/minecraft/onesix/OneSixInstance.cpp delete mode 100644 api/logic/minecraft/onesix/OneSixInstance.h delete mode 100644 api/logic/minecraft/onesix/OneSixProfileStrategy.cpp delete mode 100644 api/logic/minecraft/onesix/OneSixProfileStrategy.h delete mode 100644 api/logic/minecraft/onesix/OneSixUpdate.cpp delete mode 100644 api/logic/minecraft/onesix/OneSixUpdate.h delete mode 100644 api/logic/minecraft/onesix/OneSixVersionFormat.cpp delete mode 100644 api/logic/minecraft/onesix/OneSixVersionFormat.h delete mode 100644 api/logic/minecraft/onesix/update/AssetUpdateTask.cpp delete mode 100644 api/logic/minecraft/onesix/update/AssetUpdateTask.h delete mode 100644 api/logic/minecraft/onesix/update/FMLLibrariesTask.cpp delete mode 100644 api/logic/minecraft/onesix/update/FMLLibrariesTask.h delete mode 100644 api/logic/minecraft/onesix/update/FoldersTask.cpp delete mode 100644 api/logic/minecraft/onesix/update/FoldersTask.h delete mode 100644 api/logic/minecraft/onesix/update/LibrariesTask.cpp delete mode 100644 api/logic/minecraft/onesix/update/LibrariesTask.h create mode 100644 api/logic/minecraft/update/AssetUpdateTask.cpp create mode 100644 api/logic/minecraft/update/AssetUpdateTask.h create mode 100644 api/logic/minecraft/update/FMLLibrariesTask.cpp create mode 100644 api/logic/minecraft/update/FMLLibrariesTask.h create mode 100644 api/logic/minecraft/update/FoldersTask.cpp create mode 100644 api/logic/minecraft/update/FoldersTask.h create mode 100644 api/logic/minecraft/update/LibrariesTask.cpp create mode 100644 api/logic/minecraft/update/LibrariesTask.h (limited to 'api') diff --git a/api/logic/BaseInstaller.cpp b/api/logic/BaseInstaller.cpp index a2a575dc..c8e586d0 100644 --- a/api/logic/BaseInstaller.cpp +++ b/api/logic/BaseInstaller.cpp @@ -16,19 +16,19 @@ #include #include "BaseInstaller.h" -#include "minecraft/onesix/OneSixInstance.h" +#include "minecraft/MinecraftInstance.h" BaseInstaller::BaseInstaller() { } -bool BaseInstaller::isApplied(OneSixInstance *on) +bool BaseInstaller::isApplied(MinecraftInstance *on) { return QFile::exists(filename(on->instanceRoot())); } -bool BaseInstaller::add(OneSixInstance *to) +bool BaseInstaller::add(MinecraftInstance *to) { if (!patchesDir(to->instanceRoot()).exists()) { @@ -46,7 +46,7 @@ bool BaseInstaller::add(OneSixInstance *to) return true; } -bool BaseInstaller::remove(OneSixInstance *from) +bool BaseInstaller::remove(MinecraftInstance *from) { return QFile::remove(filename(from->instanceRoot())); } diff --git a/api/logic/BaseInstaller.h b/api/logic/BaseInstaller.h index 65a2436f..9603c3c2 100644 --- a/api/logic/BaseInstaller.h +++ b/api/logic/BaseInstaller.h @@ -19,7 +19,7 @@ #include "multimc_logic_export.h" -class OneSixInstance; +class MinecraftInstance; class QDir; class QString; class QObject; @@ -32,12 +32,12 @@ class MULTIMC_LOGIC_EXPORT BaseInstaller public: BaseInstaller(); virtual ~BaseInstaller(){}; - bool isApplied(OneSixInstance *on); + bool isApplied(MinecraftInstance *on); - virtual bool add(OneSixInstance *to); - virtual bool remove(OneSixInstance *from); + virtual bool add(MinecraftInstance *to); + virtual bool remove(MinecraftInstance *from); - virtual Task *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) = 0; + virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersionPtr version, QObject *parent) = 0; protected: virtual QString id() const = 0; diff --git a/api/logic/BaseInstance.cpp b/api/logic/BaseInstance.cpp index ee9e919d..f52b2812 100644 --- a/api/logic/BaseInstance.cpp +++ b/api/logic/BaseInstance.cpp @@ -279,6 +279,7 @@ QString BaseInstance::windowTitle() const return "MultiMC: " + name(); } +// FIXME: why is this here? move it to MinecraftInstance!!! QStringList BaseInstance::extraArguments() const { return Commandline::splitArgs(settings()->get("JvmArgs").toString()); diff --git a/api/logic/BaseInstance.h b/api/logic/BaseInstance.h index 27b167a6..7b059536 100644 --- a/api/logic/BaseInstance.h +++ b/api/logic/BaseInstance.h @@ -129,23 +129,6 @@ public: virtual QStringList extraArguments() const; - virtual QString intendedVersionId() const = 0; - virtual bool setIntendedVersionId(QString version) = 0; - - /*! - * The instance's current version. - * This value represents the instance's current version. If this value is - * different from the intendedVersion, the instance should be updated. - * \warning Don't change this value unless you know what you're doing. - */ - virtual QString currentVersionId() const = 0; - - /*! - * Whether or not 'the game' should be downloaded when the instance is launched. - */ - virtual bool shouldUpdate() const = 0; - virtual void setShouldUpdate(bool val) = 0; - /// Traits. Normally inside the version, depends on instance implementation. virtual QSet traits() = 0; @@ -159,12 +142,6 @@ public: InstancePtr getSharedPtr(); - /*! - * \brief Gets a pointer to this instance's version list. - * \return A pointer to the available version list for this instance. - */ - virtual std::shared_ptr versionList() const = 0; - /*! * \brief Gets this instance's settings object. * This settings object stores instance-specific settings. @@ -181,12 +158,6 @@ public: /// returns the current launch task (if any) std::shared_ptr getLaunchTask(); - /*! - * Returns a task that should be done right before launch - * This task should do any extra preparations needed - */ - virtual std::shared_ptr createJarModdingTask() = 0; - /*! * Create envrironment variables for running the instance */ diff --git a/api/logic/CMakeLists.txt b/api/logic/CMakeLists.txt index 0fe42a1a..909e51a9 100644 --- a/api/logic/CMakeLists.txt +++ b/api/logic/CMakeLists.txt @@ -206,22 +206,14 @@ set(MINECRAFT_SOURCES minecraft/auth/flows/RefreshTask.cpp minecraft/auth/flows/ValidateTask.h minecraft/auth/flows/ValidateTask.cpp - minecraft/onesix/OneSixUpdate.h - minecraft/onesix/OneSixUpdate.cpp - minecraft/onesix/OneSixInstance.h - minecraft/onesix/OneSixInstance.cpp - minecraft/onesix/OneSixProfileStrategy.cpp - minecraft/onesix/OneSixProfileStrategy.h - minecraft/onesix/OneSixVersionFormat.cpp - minecraft/onesix/OneSixVersionFormat.h - minecraft/onesix/update/AssetUpdateTask.h - minecraft/onesix/update/AssetUpdateTask.cpp - minecraft/onesix/update/FMLLibrariesTask.cpp - minecraft/onesix/update/FMLLibrariesTask.h - minecraft/onesix/update/FoldersTask.cpp - minecraft/onesix/update/FoldersTask.h - minecraft/onesix/update/LibrariesTask.cpp - minecraft/onesix/update/LibrariesTask.h + minecraft/update/AssetUpdateTask.h + minecraft/update/AssetUpdateTask.cpp + minecraft/update/FMLLibrariesTask.cpp + minecraft/update/FMLLibrariesTask.h + minecraft/update/FoldersTask.cpp + minecraft/update/FoldersTask.h + minecraft/update/LibrariesTask.cpp + minecraft/update/LibrariesTask.h minecraft/launch/ClaimAccount.cpp minecraft/launch/ClaimAccount.h minecraft/launch/CreateServerResourcePacksFolder.cpp @@ -237,21 +229,24 @@ set(MINECRAFT_SOURCES minecraft/launch/PrintInstanceInfo.cpp minecraft/launch/PrintInstanceInfo.h minecraft/GradleSpecifier.h + minecraft/MinecraftInstance.cpp + minecraft/MinecraftInstance.h minecraft/MinecraftProfile.cpp minecraft/MinecraftProfile.h + minecraft/MinecraftUpdate.h + minecraft/MinecraftUpdate.cpp minecraft/MojangVersionFormat.cpp minecraft/MojangVersionFormat.h - minecraft/MinecraftInstance.cpp - minecraft/MinecraftInstance.h minecraft/Rule.cpp minecraft/Rule.h + minecraft/OneSixVersionFormat.cpp + minecraft/OneSixVersionFormat.h minecraft/OpSys.cpp minecraft/OpSys.h minecraft/ParseUtils.cpp minecraft/ParseUtils.h minecraft/ProfileUtils.cpp minecraft/ProfileUtils.h - minecraft/ProfileStrategy.h minecraft/Library.cpp minecraft/Library.h minecraft/MojangDownloadInfo.h diff --git a/api/logic/FolderInstanceProvider.cpp b/api/logic/FolderInstanceProvider.cpp index 414cf479..a0fbd46a 100644 --- a/api/logic/FolderInstanceProvider.cpp +++ b/api/logic/FolderInstanceProvider.cpp @@ -1,7 +1,7 @@ #include "FolderInstanceProvider.h" #include "settings/INISettingsObject.h" #include "FileSystem.h" -#include "minecraft/onesix/OneSixInstance.h" +#include "minecraft/MinecraftInstance.h" #include "NullInstance.h" #include @@ -88,7 +88,7 @@ InstancePtr FolderInstanceProvider::loadInstance(const InstanceId& id) if (inst_type == "OneSix" || inst_type == "Nostalgia") { - inst.reset(new OneSixInstance(m_globalSettings, instanceSettings, instanceRoot)); + inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot)); } else { diff --git a/api/logic/InstanceCreationTask.cpp b/api/logic/InstanceCreationTask.cpp index a7ea1e66..21892f82 100644 --- a/api/logic/InstanceCreationTask.cpp +++ b/api/logic/InstanceCreationTask.cpp @@ -4,7 +4,7 @@ #include "FileSystem.h" //FIXME: remove this -#include "minecraft/onesix/OneSixInstance.h" +#include "minecraft/MinecraftInstance.h" InstanceCreationTask::InstanceCreationTask(SettingsObjectPtr settings, const QString & stagingPath, BaseVersionPtr version, const QString& instName, const QString& instIcon, const QString& instGroup) @@ -25,11 +25,11 @@ void InstanceCreationTask::executeTask() instanceSettings->suspendSave(); instanceSettings->registerSetting("InstanceType", "Legacy"); instanceSettings->set("InstanceType", "OneSix"); - OneSixInstance inst(m_globalSettings, instanceSettings, m_stagingPath); - inst.setIntendedVersionId(m_version->descriptor()); - inst.setName(m_instName); - inst.setIconKey(m_instIcon); - inst.init(); + auto inst = new MinecraftInstance(m_globalSettings, instanceSettings, m_stagingPath); + inst->setComponentVersion("net.minecraft", m_version->descriptor()); + inst->setName(m_instName); + inst->setIconKey(m_instIcon); + inst->init(); instanceSettings->resumeSave(); } emitSucceeded(); diff --git a/api/logic/InstanceImportTask.cpp b/api/logic/InstanceImportTask.cpp index 2bbdd897..7963a335 100644 --- a/api/logic/InstanceImportTask.cpp +++ b/api/logic/InstanceImportTask.cpp @@ -1,5 +1,3 @@ -#include "minecraft/onesix/OneSixInstance.h" - #include "InstanceImportTask.h" #include "BaseInstance.h" #include "BaseInstanceProvider.h" @@ -12,6 +10,8 @@ #include // FIXME: this does not belong here, it's Minecraft/Flame specific +#include "minecraft/MinecraftInstance.h" +#include "minecraft/MinecraftProfile.h" #include "minecraft/flame/FileResolvingTask.h" #include "minecraft/flame/PackManifest.h" #include "Json.h" @@ -225,7 +225,7 @@ void InstanceImportTask::processFlame() auto instanceSettings = std::make_shared(configPath); instanceSettings->registerSetting("InstanceType", "Legacy"); instanceSettings->set("InstanceType", "OneSix"); - OneSixInstance instance(m_globalSettings, instanceSettings, m_stagingPath); + MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath); auto mcVersion = pack.minecraft.version; // Hack to correct some 'special sauce'... if(mcVersion.endsWith('.')) diff --git a/api/logic/NullInstance.h b/api/logic/NullInstance.h index b530acd3..892ecc01 100644 --- a/api/logic/NullInstance.h +++ b/api/logic/NullInstance.h @@ -10,18 +10,6 @@ public: setVersionBroken(true); } virtual ~NullInstance() {}; - virtual bool setIntendedVersionId(QString) override - { - return false; - } - virtual QString currentVersionId() const override - { - return "Null"; - }; - virtual QString intendedVersionId() const override - { - return "Null"; - }; virtual void init() override { }; @@ -29,10 +17,6 @@ public: { return tr("Unknown instance type"); }; - virtual bool shouldUpdate() const override - { - return false; - }; virtual QSet< QString > traits() override { return {}; @@ -49,17 +33,6 @@ public: { return nullptr; } - virtual std::shared_ptr createJarModdingTask() override - { - return nullptr; - } - virtual void setShouldUpdate(bool) override - { - }; - virtual std::shared_ptr< BaseVersionList > versionList() const override - { - return nullptr; - }; virtual QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); diff --git a/api/logic/meta/JsonFormat.cpp b/api/logic/meta/JsonFormat.cpp index fb78941f..d13c89df 100644 --- a/api/logic/meta/JsonFormat.cpp +++ b/api/logic/meta/JsonFormat.cpp @@ -16,7 +16,7 @@ #include "JsonFormat.h" // FIXME: remove this from here... somehow -#include "minecraft/onesix/OneSixVersionFormat.h" +#include "minecraft/OneSixVersionFormat.h" #include "Json.h" #include "Index.h" diff --git a/api/logic/minecraft/Library_test.cpp b/api/logic/minecraft/Library_test.cpp index 3f4828c9..54296849 100644 --- a/api/logic/minecraft/Library_test.cpp +++ b/api/logic/minecraft/Library_test.cpp @@ -2,7 +2,7 @@ #include "TestUtil.h" #include "minecraft/MojangVersionFormat.h" -#include "minecraft/onesix/OneSixVersionFormat.h" +#include "minecraft/OneSixVersionFormat.h" #include "minecraft/Library.h" #include "net/HttpMetaCache.h" #include "FileSystem.h" diff --git a/api/logic/minecraft/MinecraftInstance.cpp b/api/logic/minecraft/MinecraftInstance.cpp index 785ddb38..df51ed2c 100644 --- a/api/logic/minecraft/MinecraftInstance.cpp +++ b/api/logic/minecraft/MinecraftInstance.cpp @@ -17,16 +17,23 @@ #include "launch/steps/PreLaunchCommand.h" #include "launch/steps/TextPrint.h" #include "minecraft/launch/LauncherPartLaunch.h" +#include "minecraft/launch/DirectJavaLaunch.h" #include "minecraft/launch/ModMinecraftJar.h" #include "minecraft/launch/ClaimAccount.h" #include "java/launch/CheckJava.h" #include "java/JavaUtils.h" -#include -#include +#include "meta/Index.h" +#include "meta/VersionList.h" -#include +#include "ModList.h" +#include "WorldList.h" + +#include "icons/IIconList.h" #include +#include "MinecraftProfile.h" +#include "AssetsUtils.h" +#include "MinecraftUpdate.h" #define IBUS "@im=ibus" @@ -56,6 +63,12 @@ private: MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) : BaseInstance(globalSettings, settings, rootDir) { + // FIXME: remove these + m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, ""); + m_settings->registerSetting("LWJGLVersion", "2.9.1"); + m_settings->registerSetting("ForgeVersion", ""); + m_settings->registerSetting("LiteloaderVersion", ""); + // Java Settings auto javaOverride = m_settings->registerSetting("OverrideJava", false); auto locationOverride = m_settings->registerSetting("OverrideJavaLocation", false); @@ -90,6 +103,70 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO m_settings->registerOverride(globalSettings->getSetting("MCLaunchMethod"), launchMethodOverride); } +void MinecraftInstance::init() +{ + createProfile(); +} + +QString MinecraftInstance::typeName() const +{ + return "Minecraft"; +} + + +bool MinecraftInstance::reload() +{ + if (BaseInstance::reload()) + { + try + { + reloadProfile(); + return true; + } + catch (...) + { + return false; + } + } + return false; +} + +void MinecraftInstance::createProfile() +{ + m_profile.reset(new MinecraftProfile(this)); +} + +void MinecraftInstance::reloadProfile() +{ + m_profile->reload(); + setVersionBroken(m_profile->getProblemSeverity() == ProblemSeverity::Error); + emit versionReloaded(); +} + +void MinecraftInstance::clearProfile() +{ + m_profile->clear(); + emit versionReloaded(); +} + +std::shared_ptr MinecraftInstance::getMinecraftProfile() const +{ + return m_profile; +} + +QSet MinecraftInstance::traits() +{ + auto version = getMinecraftProfile(); + if (!version) + { + return {"version-incomplete"}; + } + else + { + return version->getTraits(); + } +} + QString MinecraftInstance::minecraftRoot() const { QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft")); @@ -106,9 +183,107 @@ QString MinecraftInstance::binRoot() const return FS::PathCombine(minecraftRoot(), "bin"); } -std::shared_ptr< BaseVersionList > MinecraftInstance::versionList() const +QString MinecraftInstance::getNativePath() const +{ + QDir natives_dir(FS::PathCombine(instanceRoot(), "natives/")); + return natives_dir.absolutePath(); +} + +QString MinecraftInstance::getLocalLibraryPath() const +{ + QDir libraries_dir(FS::PathCombine(instanceRoot(), "libraries/")); + return libraries_dir.absolutePath(); +} + +QString MinecraftInstance::loaderModsDir() const +{ + return FS::PathCombine(minecraftRoot(), "mods"); +} + +QString MinecraftInstance::coreModsDir() const +{ + return FS::PathCombine(minecraftRoot(), "coremods"); +} + +QString MinecraftInstance::resourcePacksDir() const +{ + return FS::PathCombine(minecraftRoot(), "resourcepacks"); +} + +QString MinecraftInstance::texturePacksDir() const +{ + return FS::PathCombine(minecraftRoot(), "texturepacks"); +} + +QString MinecraftInstance::instanceConfigFolder() const +{ + return FS::PathCombine(minecraftRoot(), "config"); +} + +QString MinecraftInstance::jarModsDir() const +{ + return FS::PathCombine(instanceRoot(), "jarmods"); +} + +QString MinecraftInstance::libDir() const { - return ENV.metadataIndex()->get("net.minecraft"); + return FS::PathCombine(minecraftRoot(), "lib"); +} + +QString MinecraftInstance::worldDir() const +{ + return FS::PathCombine(minecraftRoot(), "saves"); +} + +QDir MinecraftInstance::librariesPath() const +{ + return QDir::current().absoluteFilePath("libraries"); +} + +QDir MinecraftInstance::jarmodsPath() const +{ + return QDir(jarModsDir()); +} + +QDir MinecraftInstance::versionsPath() const +{ + return QDir::current().absoluteFilePath("versions"); +} + +QStringList MinecraftInstance::getClassPath() const +{ + QStringList jars, nativeJars; + auto javaArchitecture = settings()->get("JavaArchitecture").toString(); + m_profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot()); + return jars; +} + +QString MinecraftInstance::getMainClass() const +{ + return m_profile->getMainClass(); +} + +QStringList MinecraftInstance::getNativeJars() const +{ + QStringList jars, nativeJars; + auto javaArchitecture = settings()->get("JavaArchitecture").toString(); + m_profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot()); + return nativeJars; +} + +QStringList MinecraftInstance::extraArguments() const +{ + auto list = BaseInstance::extraArguments(); + auto version = getMinecraftProfile(); + if (!version) + return list; + auto jarMods = getJarMods(); + if (!jarMods.isEmpty()) + { + list.append({"-Dfml.ignoreInvalidMinecraftCertificates=true", + "-Dfml.ignorePatchDiscrepancies=true"}); + } + return list; } QStringList MinecraftInstance::javaArguments() const @@ -193,6 +368,270 @@ QProcessEnvironment MinecraftInstance::createEnvironment() return env; } +static QString replaceTokensIn(QString text, QMap with) +{ + QString result; + QRegExp token_regexp("\\$\\{(.+)\\}"); + token_regexp.setMinimal(true); + QStringList list; + int tail = 0; + int head = 0; + while ((head = token_regexp.indexIn(text, head)) != -1) + { + result.append(text.mid(tail, head - tail)); + QString key = token_regexp.cap(1); + auto iter = with.find(key); + if (iter != with.end()) + { + result.append(*iter); + } + head += token_regexp.matchedLength(); + tail = head; + } + result.append(text.mid(tail)); + return result; +} + +QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session) const +{ + QString args_pattern = m_profile->getMinecraftArguments(); + for (auto tweaker : m_profile->getTweakers()) + { + args_pattern += " --tweakClass " + tweaker; + } + + QMap token_mapping; + // yggdrasil! + if(session) + { + token_mapping["auth_username"] = session->username; + token_mapping["auth_session"] = session->session; + token_mapping["auth_access_token"] = session->access_token; + token_mapping["auth_player_name"] = session->player_name; + token_mapping["auth_uuid"] = session->uuid; + token_mapping["user_properties"] = session->serializeUserProperties(); + token_mapping["user_type"] = session->user_type; + } + + // blatant self-promotion. + token_mapping["profile_name"] = token_mapping["version_name"] = "MultiMC5"; + if(m_profile->isVanilla()) + { + token_mapping["version_type"] = m_profile->getMinecraftVersionType(); + } + else + { + token_mapping["version_type"] = "custom"; + } + + QString absRootDir = QDir(minecraftRoot()).absolutePath(); + token_mapping["game_directory"] = absRootDir; + QString absAssetsDir = QDir("assets/").absolutePath(); + auto assets = m_profile->getMinecraftAssets(); + // FIXME: this is wrong and should be run as an async task + token_mapping["game_assets"] = AssetsUtils::reconstructAssets(assets->id).absolutePath(); + + // 1.7.3+ assets tokens + token_mapping["assets_root"] = absAssetsDir; + token_mapping["assets_index_name"] = assets->id; + + QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts); + for (int i = 0; i < parts.length(); i++) + { + parts[i] = replaceTokensIn(parts[i], token_mapping); + } + return parts; +} + +QString MinecraftInstance::createLaunchScript(AuthSessionPtr session) +{ + QString launchScript; + + if (!m_profile) + return nullptr; + + auto mainClass = getMainClass(); + if (!mainClass.isEmpty()) + { + launchScript += "mainClass " + mainClass + "\n"; + } + auto appletClass = m_profile->getAppletClass(); + if (!appletClass.isEmpty()) + { + launchScript += "appletClass " + appletClass + "\n"; + } + + // generic minecraft params + for (auto param : processMinecraftArgs(session)) + { + launchScript += "param " + param + "\n"; + } + + // window size, title and state, legacy + { + QString windowParams; + if (settings()->get("LaunchMaximized").toBool()) + windowParams = "max"; + else + windowParams = QString("%1x%2") + .arg(settings()->get("MinecraftWinWidth").toInt()) + .arg(settings()->get("MinecraftWinHeight").toInt()); + launchScript += "windowTitle " + windowTitle() + "\n"; + launchScript += "windowParams " + windowParams + "\n"; + } + + // legacy auth + if(session) + { + launchScript += "userName " + session->player_name + "\n"; + launchScript += "sessionId " + session->session + "\n"; + } + + // libraries and class path. + { + QStringList jars, nativeJars; + auto javaArchitecture = settings()->get("JavaArchitecture").toString(); + m_profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot()); + for(auto file: jars) + { + launchScript += "cp " + file + "\n"; + } + for(auto file: nativeJars) + { + launchScript += "ext " + file + "\n"; + } + launchScript += "natives " + getNativePath() + "\n"; + } + + for (auto trait : m_profile->getTraits()) + { + launchScript += "traits " + trait + "\n"; + } + launchScript += "launcher onesix\n"; + return launchScript; +} + +QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session) +{ + QStringList out; + out << "Main Class:" << " " + getMainClass() << ""; + out << "Native path:" << " " + getNativePath() << ""; + + + auto alltraits = traits(); + if(alltraits.size()) + { + out << "Traits:"; + for (auto trait : alltraits) + { + out << "traits " + trait; + } + out << ""; + } + + // libraries and class path. + { + out << "Libraries:"; + QStringList jars, nativeJars; + auto javaArchitecture = settings()->get("JavaArchitecture").toString(); + m_profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot()); + auto printLibFile = [&](const QString & path) + { + QFileInfo info(path); + if(info.exists()) + { + out << " " + path; + } + else + { + out << " " + path + " (missing)"; + } + }; + for(auto file: jars) + { + printLibFile(file); + } + out << ""; + out << "Native libraries:"; + for(auto file: nativeJars) + { + printLibFile(file); + } + out << ""; + } + + if(loaderModList()->size()) + { + out << "Mods:"; + for(auto & mod: loaderModList()->allMods()) + { + if(!mod.enabled()) + continue; + if(mod.type() == Mod::MOD_FOLDER) + continue; + // TODO: proper implementation would need to descend into folders. + + out << " " + mod.filename().completeBaseName(); + } + out << ""; + } + + if(coreModList()->size()) + { + out << "Core Mods:"; + for(auto & coremod: coreModList()->allMods()) + { + if(!coremod.enabled()) + continue; + if(coremod.type() == Mod::MOD_FOLDER) + continue; + // TODO: proper implementation would need to descend into folders. + + out << " " + coremod.filename().completeBaseName(); + } + out << ""; + } + + auto & jarMods = m_profile->getJarMods(); + if(jarMods.size()) + { + out << "Jar Mods:"; + for(auto & jarmod: jarMods) + { + auto displayname = jarmod->displayName(currentSystem); + auto realname = jarmod->filename(currentSystem); + if(displayname != realname) + { + out << " " + displayname + " (" + realname + ")"; + } + else + { + out << " " + realname; + } + } + out << ""; + } + + auto params = processMinecraftArgs(nullptr); + out << "Params:"; + out << " " + params.join(' '); + 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; +} + QMap MinecraftInstance::createCensorFilterFromSession(AuthSessionPtr session) { if(!session) @@ -321,7 +760,7 @@ QString MinecraftInstance::getStatusbarDescription() } QString description; - description.append(tr("Minecraft %1 (%2)").arg(intendedVersionId()).arg(typeName())); + description.append(tr("Minecraft %1 (%2)").arg(getComponentVersion("net.minecraft")).arg(typeName())); if(totalTimePlayed() > 0) { description.append(tr(", played for %1").arg(prettifyTimeDuration(totalTimePlayed()))); @@ -333,6 +772,11 @@ QString MinecraftInstance::getStatusbarDescription() return description; } +shared_qobject_ptr MinecraftInstance::createUpdateTask() +{ + return shared_qobject_ptr(new OneSixUpdate(this)); +} + std::shared_ptr MinecraftInstance::createLaunchTask(AuthSessionPtr session) { auto process = LaunchTask::create(std::dynamic_pointer_cast(getSharedPtr())); @@ -352,7 +796,7 @@ std::shared_ptr MinecraftInstance::createLaunchTask(AuthSessionPtr s } // check launch method - QStringList validMethods = validLaunchMethods(); + QStringList validMethods = {"LauncherPart", "DirectJava"}; QString method = launchMethod(); if(!validMethods.contains(method)) { @@ -402,8 +846,21 @@ std::shared_ptr MinecraftInstance::createLaunchTask(AuthSessionPtr s { // actually launch the game - auto step = createMainLaunchStep(pptr, session); - process->appendStep(step); + auto method = launchMethod(); + if(method == "LauncherPart") + { + auto step = std::make_shared(pptr); + step->setWorkingDirectory(minecraftRoot()); + step->setAuthSession(session); + process->appendStep(step); + } + else if (method == "DirectJava") + { + auto step = std::make_shared(pptr); + step->setWorkingDirectory(minecraftRoot()); + step->setAuthSession(session); + process->appendStep(step); + } } // run post-exit command if that's needed @@ -432,5 +889,114 @@ JavaVersion MinecraftInstance::getJavaVersion() const return JavaVersion(settings()->get("JavaVersion").toString()); } +bool MinecraftInstance::setComponentVersion(const QString& uid, const QString& version) +{ + if(uid == "net.minecraft") + { + settings()->set("IntendedVersion", version); + } + else if (uid == "org.lwjgl") + { + settings()->set("LWJGLVersion", version); + } + else if (uid == "net.minecraftforge") + { + settings()->set("ForgeVersion", version); + } + else if (uid == "com.mumfrey.liteloader") + { + settings()->set("LiteloaderVersion", version); + } + if(getMinecraftProfile()) + { + clearProfile(); + } + emit propertiesChanged(this); + return true; +} + +QString MinecraftInstance::getComponentVersion(const QString& uid) const +{ + if(uid == "net.minecraft") + { + return settings()->get("IntendedVersion").toString(); + } + else if(uid == "org.lwjgl") + { + return settings()->get("LWJGLVersion").toString(); + } + else if(uid == "net.minecraftforge") + { + return settings()->get("ForgeVersion").toString(); + } + else if(uid == "com.mumfrey.liteloader") + { + return settings()->get("LiteloaderVersion").toString(); + } + return QString(); +} + +std::shared_ptr MinecraftInstance::loaderModList() const +{ + if (!m_loader_mod_list) + { + m_loader_mod_list.reset(new ModList(loaderModsDir())); + } + m_loader_mod_list->update(); + return m_loader_mod_list; +} + +std::shared_ptr MinecraftInstance::coreModList() const +{ + if (!m_core_mod_list) + { + m_core_mod_list.reset(new ModList(coreModsDir())); + } + m_core_mod_list->update(); + return m_core_mod_list; +} + +std::shared_ptr MinecraftInstance::resourcePackList() const +{ + if (!m_resource_pack_list) + { + m_resource_pack_list.reset(new ModList(resourcePacksDir())); + } + m_resource_pack_list->update(); + return m_resource_pack_list; +} + +std::shared_ptr MinecraftInstance::texturePackList() const +{ + if (!m_texture_pack_list) + { + m_texture_pack_list.reset(new ModList(texturePacksDir())); + } + m_texture_pack_list->update(); + return m_texture_pack_list; +} + +std::shared_ptr MinecraftInstance::worldList() const +{ + if (!m_world_list) + { + m_world_list.reset(new WorldList(worldDir())); + } + return m_world_list; +} + +QList< Mod > MinecraftInstance::getJarMods() const +{ + QList mods; + for (auto jarmod : m_profile->getJarMods()) + { + QStringList jar, temp1, temp2, temp3; + jarmod->getApplicableFiles(currentSystem, jar, temp1, temp2, temp3, jarmodsPath().absolutePath()); + // QString filePath = jarmodsPath().absoluteFilePath(jarmod->filename(currentSystem)); + mods.push_back(Mod(QFileInfo(jar[0]))); + } + return mods; +} + #include "MinecraftInstance.moc" diff --git a/api/logic/minecraft/MinecraftInstance.h b/api/logic/minecraft/MinecraftInstance.h index 7f967ce0..ebb22eb1 100644 --- a/api/logic/minecraft/MinecraftInstance.h +++ b/api/logic/minecraft/MinecraftInstance.h @@ -3,88 +3,117 @@ #include #include "minecraft/Mod.h" #include - +#include #include "multimc_logic_export.h" class ModList; class WorldList; class LaunchStep; +class MinecraftProfile; class MULTIMC_LOGIC_EXPORT MinecraftInstance: public BaseInstance { + Q_OBJECT public: MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir); virtual ~MinecraftInstance() {}; + virtual void init() override; - /// Path to the instance's minecraft directory. - QString minecraftRoot() const; - - /// Path to the instance's minecraft/bin directory. - QString binRoot() const; - - ////// Mod Lists ////// - virtual std::shared_ptr resourcePackList() const - { - return nullptr; - } - virtual std::shared_ptr texturePackList() const - { - return nullptr; - } - virtual std::shared_ptr worldList() const + QString typeName() const override; + QSet traits() override; + bool canExport() const override { - return nullptr; - } - /// get all jar mods applicable to this instance's jar - virtual QList getJarMods() const - { - return QList(); + return true; } - virtual std::shared_ptr createLaunchTask(AuthSessionPtr account) override; - virtual QString createLaunchScript(AuthSessionPtr session) = 0; + ////// Directories and files ////// + QString jarModsDir() const; + QString resourcePacksDir() const; + QString texturePacksDir() const; + QString loaderModsDir() const; + QString coreModsDir() const; + QString libDir() const; + QString worldDir() const; + QDir jarmodsPath() const; + QDir librariesPath() const; + QDir versionsPath() const; + QString instanceConfigFolder() const override; + QString minecraftRoot() const; // Path to the instance's minecraft directory. + QString binRoot() const; // Path to the instance's minecraft bin directory. + QString getNativePath() const; // where to put the natives during/before launch + QString getLocalLibraryPath() const; // where the instance-local libraries should be + + + ////// Profile management ////// + void createProfile(); + std::shared_ptr getMinecraftProfile() const; + void reloadProfile(); + void clearProfile(); + bool reload() override; - //FIXME: nuke? - virtual std::shared_ptr versionList() const override; + ////// Mod Lists ////// + std::shared_ptr loaderModList() const; + std::shared_ptr coreModList() const; + std::shared_ptr resourcePackList() const; + std::shared_ptr texturePackList() const; + std::shared_ptr worldList() const; + + + ////// Launch stuff ////// + shared_qobject_ptr createUpdateTask() override; + std::shared_ptr createLaunchTask(AuthSessionPtr account) override; + QStringList extraArguments() const override; + QStringList verboseDescription(AuthSessionPtr session) override; + QList getJarMods() const; + QString createLaunchScript(AuthSessionPtr session); /// get arguments passed to java QStringList javaArguments() const; /// get variables for launch command variable substitution/environment - virtual QMap getVariables() const override; + QMap getVariables() const override; /// create an environment for launching processes - virtual QProcessEnvironment createEnvironment() override; + QProcessEnvironment createEnvironment() override; /// guess log level from a line of minecraft log - virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level) override; - - virtual IPathMatcher::Ptr getLogFileMatcher() override; + MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level) override; - virtual QString getLogFileRoot() override; + IPathMatcher::Ptr getLogFileMatcher() override; - virtual QString getStatusbarDescription() override; + QString getLogFileRoot() override; - virtual QStringList getClassPath() const = 0; - virtual QStringList getNativeJars() const = 0; + QString getStatusbarDescription() override; - virtual QString getMainClass() const = 0; + virtual QStringList getClassPath() const; + virtual QStringList getNativeJars() const; + virtual QString getMainClass() const; - virtual QString getNativePath() const = 0; + virtual QStringList processMinecraftArgs(AuthSessionPtr account) const; - virtual QString getLocalLibraryPath() const = 0; + virtual JavaVersion getJavaVersion() const; - virtual QStringList processMinecraftArgs(AuthSessionPtr account) const = 0; + QString getComponentVersion(const QString &uid) const; + bool setComponentVersion(const QString &uid, const QString &version); - virtual JavaVersion getJavaVersion() const; +signals: + void versionReloaded(); protected: QMap createCensorFilterFromSession(AuthSessionPtr session); - virtual QStringList validLaunchMethods() = 0; - virtual QString launchMethod(); - virtual std::shared_ptr createMainLaunchStep(LaunchTask *parent, AuthSessionPtr session) = 0; + QStringList validLaunchMethods(); + QString launchMethod(); + private: QString prettifyTimeDuration(int64_t duration); + +protected: // data + std::shared_ptr m_profile; + mutable std::shared_ptr m_loader_mod_list; + mutable std::shared_ptr m_core_mod_list; + mutable std::shared_ptr m_resource_pack_list; + mutable std::shared_ptr m_texture_pack_list; + mutable std::shared_ptr m_world_list; }; typedef std::shared_ptr MinecraftInstancePtr; diff --git a/api/logic/minecraft/MinecraftProfile.cpp b/api/logic/minecraft/MinecraftProfile.cpp index f6e39f45..c883e8e9 100644 --- a/api/logic/minecraft/MinecraftProfile.cpp +++ b/api/logic/minecraft/MinecraftProfile.cpp @@ -22,47 +22,30 @@ #include #include "minecraft/MinecraftProfile.h" -#include "ProfileUtils.h" -#include "ProfileStrategy.h" #include "Exception.h" - -MinecraftProfile::MinecraftProfile(ProfileStrategy *strategy) +#include +#include +#include +#include +#include +#include +#include + +MinecraftProfile::MinecraftProfile(MinecraftInstance * instance) : QAbstractListModel() { - setStrategy(strategy); + m_instance = instance; clear(); } MinecraftProfile::~MinecraftProfile() { - if(m_strategy) - { - delete m_strategy; - } -} - -void MinecraftProfile::setStrategy(ProfileStrategy* strategy) -{ - Q_ASSERT(strategy != nullptr); - - if(m_strategy != nullptr) - { - delete m_strategy; - m_strategy = nullptr; - } - m_strategy = strategy; - m_strategy->profile = this; -} - -ProfileStrategy* MinecraftProfile::strategy() -{ - return m_strategy; } void MinecraftProfile::reload() { beginResetModel(); - m_strategy->load(); + load_internal(); reapplyPatches(); endResetModel(); } @@ -107,7 +90,7 @@ bool MinecraftProfile::remove(const int index) return false; } - if(!m_strategy->removePatch(patch)) + if(!removePatch_internal(patch)) { qCritical() << "Patch" << patch->getID() << "could not be removed"; return false; @@ -143,7 +126,7 @@ bool MinecraftProfile::customize(int index) qDebug() << "Patch" << patch->getID() << "is not customizable"; return false; } - if(!m_strategy->customizePatch(patch)) + if(!customizePatch_internal(patch)) { qCritical() << "Patch" << patch->getID() << "could not be customized"; return false; @@ -163,7 +146,7 @@ bool MinecraftProfile::revertToBase(int index) qDebug() << "Patch" << patch->getID() << "is not revertible"; return false; } - if(!m_strategy->revertPatch(patch)) + if(!revertPatch_internal(patch)) { qCritical() << "Patch" << patch->getID() << "could not be reverted"; return false; @@ -334,7 +317,7 @@ void MinecraftProfile::saveCurrentOrder() const continue; order.append(item->getID()); } - m_strategy->saveOrder(order); + saveOrder_internal(order); } void MinecraftProfile::move(const int index, const MoveDirection direction) @@ -374,7 +357,7 @@ void MinecraftProfile::move(const int index, const MoveDirection direction) } void MinecraftProfile::resetOrder() { - m_strategy->resetOrder(); + resetOrder_internal(); reload(); } @@ -661,12 +644,12 @@ void MinecraftProfile::getLibraryFiles(const QString& architecture, QStringList& void MinecraftProfile::installJarMods(QStringList selectedFiles) { - m_strategy->installJarMods(selectedFiles); + installJarMods_internal(selectedFiles); } void MinecraftProfile::installCustomJar(QString selectedFile) { - m_strategy->installCustomJar(selectedFile); + installCustomJar_internal(selectedFile); } @@ -685,3 +668,389 @@ int MinecraftProfile::getFreeOrderNumber() } return largest + 1; } + +void MinecraftProfile::loadDefaultBuiltinPatches_internal() +{ + auto addBuiltinPatch = [&](const QString &uid, const QString intendedVersion, int order) + { + auto jsonFilePath = FS::PathCombine(m_instance->instanceRoot(), "patches" , uid + ".json"); + // load up the base minecraft patch + ProfilePatchPtr profilePatch; + if(QFile::exists(jsonFilePath)) + { + auto file = ProfileUtils::parseJsonFile(QFileInfo(jsonFilePath), false); + if(file->version.isEmpty()) + { + file->version = intendedVersion; + } + profilePatch = std::make_shared(file, jsonFilePath); + profilePatch->setVanilla(false); + profilePatch->setRevertible(true); + } + else + { + auto metaVersion = ENV.metadataIndex()->get(uid, intendedVersion); + profilePatch = std::make_shared(metaVersion); + profilePatch->setVanilla(true); + } + profilePatch->setOrder(order); + appendPatch(profilePatch); + }; + addBuiltinPatch("net.minecraft", m_instance->getComponentVersion("net.minecraft"), -2); + addBuiltinPatch("org.lwjgl", m_instance->getComponentVersion("org.lwjgl"), -1); +} + +void MinecraftProfile::loadUserPatches_internal() +{ + // first, collect all patches (that are not builtins of OneSix) and load them + QMap loadedPatches; + QDir patchesDir(FS::PathCombine(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); + // ignore builtins + if (file->uid == "net.minecraft") + continue; + if (file->uid == "org.lwjgl") + continue; + auto patch = std::make_shared(file, info.filePath()); + patch->setRemovable(true); + patch->setMovable(true); + if(ENV.metadataIndex()->hasUid(file->uid)) + { + // FIXME: requesting a uid/list creates it in the index... this allows reverting to possibly invalid versions... + patch->setRevertible(true); + } + loadedPatches[file->uid] = patch; + } + // these are 'special'... if not already loaded from instance files, grab them from the metadata repo. + auto loadSpecial = [&](const QString & uid, int order) + { + auto patchVersion = m_instance->getComponentVersion(uid); + if(!patchVersion.isEmpty() && !loadedPatches.contains(uid)) + { + auto patch = std::make_shared(ENV.metadataIndex()->get(uid, patchVersion)); + patch->setOrder(order); + patch->setVanilla(true); + patch->setRemovable(true); + patch->setMovable(true); + loadedPatches[uid] = patch; + } + }; + loadSpecial("net.minecraftforge", 5); + loadSpecial("com.mumfrey.liteloader", 10); + + // now add all the patches by user sort order + ProfileUtils::PatchOrder userOrder; + ProfileUtils::readOverrideOrders(FS::PathCombine(m_instance->instanceRoot(), "order.json"), userOrder); + for (auto uid : userOrder) + { + // ignore builtins + if (uid == "net.minecraft") + continue; + if (uid == "org.lwjgl") + continue; + // ordering has a patch that is gone? + if(!loadedPatches.contains(uid)) + { + continue; + } + appendPatch(loadedPatches.take(uid)); + } + + // is there anything left to sort? + if(loadedPatches.isEmpty()) + { + // TODO: save the order here? + return; + } + + // inserting into multimap by order number as key sorts the patches and detects duplicates + QMultiMap files; + auto iter = loadedPatches.begin(); + while(iter != loadedPatches.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 + appendPatch(value); + } + } + // TODO: save the order here? +} + + +void MinecraftProfile::load_internal() +{ + clearPatches(); + loadDefaultBuiltinPatches_internal(); + loadUserPatches_internal(); +} + +bool MinecraftProfile::saveOrder_internal(ProfileUtils::PatchOrder order) const +{ + return ProfileUtils::writeOverrideOrders(FS::PathCombine(m_instance->instanceRoot(), "order.json"), order); +} + +bool MinecraftProfile::resetOrder_internal() +{ + return QDir(m_instance->instanceRoot()).remove("order.json"); +} + +bool MinecraftProfile::removePatch_internal(ProfilePatchPtr patch) +{ + bool ok = true; + // first, remove the patch file. this ensures it's not used anymore + auto fileName = patch->getFilename(); + if(fileName.size()) + { + QFile patchFile(fileName); + if(patchFile.exists() && !patchFile.remove()) + { + qCritical() << "File" << fileName << "could not be removed because:" << patchFile.errorString(); + return false; + } + } + if(!m_instance->getComponentVersion(patch->getID()).isEmpty()) + { + m_instance->setComponentVersion(patch->getID(), QString()); + } + + // FIXME: we need a generic way of removing local resources, not just jar mods... + auto preRemoveJarMod = [&](LibraryPtr jarMod) -> bool + { + if (!jarMod->isLocal()) + { + return true; + } + QStringList jar, temp1, temp2, temp3; + jarMod->getApplicableFiles(currentSystem, jar, temp1, temp2, temp3, m_instance->jarmodsPath().absolutePath()); + QFileInfo finfo (jar[0]); + if(finfo.exists()) + { + QFile jarModFile(jar[0]); + if(!jarModFile.remove()) + { + qCritical() << "File" << jar[0] << "could not be removed because:" << jarModFile.errorString(); + return false; + } + return true; + } + return true; + }; + + auto &jarMods = patch->getVersionFile()->jarMods; + for(auto &jarmod: jarMods) + { + ok &= preRemoveJarMod(jarmod); + } + return ok; +} + +bool MinecraftProfile::customizePatch_internal(ProfilePatchPtr patch) +{ + if(patch->isCustom()) + { + return false; + } + + auto filename = FS::PathCombine(m_instance->instanceRoot(), "patches" , patch->getID() + ".json"); + if(!FS::ensureFilePathExists(filename)) + { + return false; + } + // FIXME: get rid of this try-catch. + try + { + QSaveFile jsonFile(filename); + if(!jsonFile.open(QIODevice::WriteOnly)) + { + return false; + } + auto vfile = patch->getVersionFile(); + if(!vfile) + { + return false; + } + auto document = OneSixVersionFormat::versionFileToJson(vfile, true); + jsonFile.write(document.toJson()); + if(!jsonFile.commit()) + { + return false; + } + load_internal(); + } + catch (Exception &error) + { + qWarning() << "Version could not be loaded:" << error.cause(); + } + return true; +} + +bool MinecraftProfile::revertPatch_internal(ProfilePatchPtr patch) +{ + if(!patch->isCustom()) + { + // already not custom + return true; + } + auto filename = patch->getFilename(); + if(!QFile::exists(filename)) + { + // already gone / not custom + return true; + } + // just kill the file and reload + bool result = QFile::remove(filename); + // FIXME: get rid of this try-catch. + try + { + load_internal(); + } + catch (Exception &error) + { + qWarning() << "Version could not be loaded:" << error.cause(); + } + return result; +} + +bool MinecraftProfile::installJarMods_internal(QStringList filepaths) +{ + QString patchDir = FS::PathCombine(m_instance->instanceRoot(), "patches"); + if(!FS::ensureFolderPathExists(patchDir)) + { + return false; + } + + if (!FS::ensureFolderPathExists(m_instance->jarModsDir())) + { + return false; + } + + for(auto filepath:filepaths) + { + QFileInfo sourceInfo(filepath); + auto uuid = QUuid::createUuid(); + QString id = uuid.toString().remove('{').remove('}'); + QString target_filename = id + ".jar"; + QString target_id = "org.multimc.jarmod." + id; + QString target_name = sourceInfo.completeBaseName() + " (jar mod)"; + QString finalPath = FS::PathCombine(m_instance->jarModsDir(), target_filename); + + QFileInfo targetInfo(finalPath); + if(targetInfo.exists()) + { + return false; + } + + if (!QFile::copy(sourceInfo.absoluteFilePath(),QFileInfo(finalPath).absoluteFilePath())) + { + return false; + } + + auto f = std::make_shared(); + auto jarMod = std::make_shared(); + jarMod->setRawName(GradleSpecifier("org.multimc.jarmods:" + id + ":1")); + jarMod->setFilename(target_filename); + jarMod->setDisplayName(sourceInfo.completeBaseName()); + jarMod->setHint("local"); + f->jarMods.append(jarMod); + f->name = target_name; + f->uid = target_id; + f->order = getFreeOrderNumber(); + QString patchFileName = FS::PathCombine(patchDir, target_id + ".json"); + + QFile file(patchFileName); + if (!file.open(QFile::WriteOnly)) + { + qCritical() << "Error opening" << file.fileName() + << "for reading:" << file.errorString(); + return false; + } + file.write(OneSixVersionFormat::versionFileToJson(f, true).toJson()); + file.close(); + + auto patch = std::make_shared(f, patchFileName); + patch->setMovable(true); + patch->setRemovable(true); + appendPatch(patch); + } + saveCurrentOrder(); + reapplyPatches(); + return true; +} + +bool MinecraftProfile::installCustomJar_internal(QString filepath) +{ + QString patchDir = FS::PathCombine(m_instance->instanceRoot(), "patches"); + if(!FS::ensureFolderPathExists(patchDir)) + { + return false; + } + + QString libDir = m_instance->getLocalLibraryPath(); + if (!FS::ensureFolderPathExists(libDir)) + { + return false; + } + + auto specifier = GradleSpecifier("org.multimc:customjar:1"); + QFileInfo sourceInfo(filepath); + QString target_filename = specifier.getFileName(); + QString target_id = specifier.artifactId(); + QString target_name = sourceInfo.completeBaseName() + " (custom jar)"; + QString finalPath = FS::PathCombine(libDir, target_filename); + + QFileInfo jarInfo(finalPath); + if (jarInfo.exists()) + { + if(!QFile::remove(finalPath)) + { + return false; + } + } + if (!QFile::copy(filepath, finalPath)) + { + return false; + } + + auto f = std::make_shared(); + auto jarMod = std::make_shared(); + jarMod->setRawName(specifier); + jarMod->setDisplayName(sourceInfo.completeBaseName()); + jarMod->setHint("local"); + f->mainJar = jarMod; + f->name = target_name; + f->uid = target_id; + f->order = getFreeOrderNumber(); + QString patchFileName = FS::PathCombine(patchDir, target_id + ".json"); + + QFile file(patchFileName); + if (!file.open(QFile::WriteOnly)) + { + qCritical() << "Error opening" << file.fileName() + << "for reading:" << file.errorString(); + return false; + } + file.write(OneSixVersionFormat::versionFileToJson(f, true).toJson()); + file.close(); + + auto patch = std::make_shared(f, patchFileName); + patch->setMovable(true); + patch->setRemovable(true); + appendPatch(patch); + + saveCurrentOrder(); + reapplyPatches(); + return true; +} \ No newline at end of file diff --git a/api/logic/minecraft/MinecraftProfile.h b/api/logic/minecraft/MinecraftProfile.h index e81ba499..da8dcbbc 100644 --- a/api/logic/minecraft/MinecraftProfile.h +++ b/api/logic/minecraft/MinecraftProfile.h @@ -23,13 +23,13 @@ #include "Library.h" #include "ProfilePatch.h" +#include "ProfileUtils.h" #include "BaseVersion.h" #include "MojangDownloadInfo.h" #include "multimc_logic_export.h" -class ProfileStrategy; -class OneSixInstance; +class MinecraftInstance; class MULTIMC_LOGIC_EXPORT MinecraftProfile : public QAbstractListModel @@ -37,12 +37,9 @@ class MULTIMC_LOGIC_EXPORT MinecraftProfile : public QAbstractListModel Q_OBJECT public: - explicit MinecraftProfile(ProfileStrategy *strategy); + explicit MinecraftProfile(MinecraftInstance * instance); virtual ~MinecraftProfile(); - void setStrategy(ProfileStrategy * strategy); - ProfileStrategy *strategy(); - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; @@ -138,6 +135,19 @@ public: /// Add the patch object to the internal list of patches void appendPatch(ProfilePatchPtr patch); +private: + void load_internal(); + bool resetOrder_internal(); + bool saveOrder_internal(ProfileUtils::PatchOrder order) const; + bool installJarMods_internal(QStringList filepaths); + bool installCustomJar_internal(QString filepath); + bool removePatch_internal(ProfilePatchPtr patch); + bool customizePatch_internal(ProfilePatchPtr patch); + bool revertPatch_internal(ProfilePatchPtr patch); + void loadDefaultBuiltinPatches_internal(); + void loadUserPatches_internal(); + void upgradeDeprecatedFiles_internal(); + private: /* data */ /// the version of Minecraft - jar to use QString m_minecraftVersion; @@ -185,30 +195,9 @@ private: /* data */ ProblemSeverity m_problemSeverity = ProblemSeverity::None; - /* - FIXME: add support for those rules here? Looks like a pile of quick hacks to me though. - - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx", - "version": "^10\\.5\\.\\d$" - } - } - ], - "incompatibilityReason": "There is a bug in LWJGL which makes it incompatible with OSX - 10.5.8. Please go to New Profile and use 1.5.2 for now. Sorry!" - } - */ - // QList rules; - /// list of attached profile patches QList m_patches; - /// strategy used for profile operations - ProfileStrategy *m_strategy = nullptr; + // the instance this belongs to + MinecraftInstance *m_instance; }; diff --git a/api/logic/minecraft/MinecraftUpdate.cpp b/api/logic/minecraft/MinecraftUpdate.cpp new file mode 100644 index 00000000..61dce779 --- /dev/null +++ b/api/logic/minecraft/MinecraftUpdate.cpp @@ -0,0 +1,203 @@ +/* Copyright 2013-2017 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 "Env.h" +#include +#include "MinecraftUpdate.h" +#include "MinecraftInstance.h" + +#include +#include +#include +#include + +#include "BaseInstance.h" +#include "minecraft/MinecraftProfile.h" +#include "minecraft/Library.h" +#include "net/URLConstants.h" +#include + +#include "update/FoldersTask.h" +#include "update/LibrariesTask.h" +#include "update/FMLLibrariesTask.h" +#include "update/AssetUpdateTask.h" + +#include +#include + +OneSixUpdate::OneSixUpdate(MinecraftInstance *inst, QObject *parent) : Task(parent), m_inst(inst) +{ + // create folders + { + m_tasks.append(std::make_shared(m_inst)); + } + + // add metadata update tasks, if necessary + { + /* + * FIXME: there are some corner cases here that remain unhandled: + * what if local load succeeds but remote fails? The version is still usable... + * We should not rely on the remote to be there... and prefer local files if it does not respond. + */ + qDebug() << "Updating patches..."; + auto profile = m_inst->getMinecraftProfile(); + m_inst->reloadProfile(); + for(int i = 0; i < profile->rowCount(); i++) + { + auto patch = profile->versionPatch(i); + auto id = patch->getID(); + auto metadata = patch->getMeta(); + if(metadata) + { + metadata->load(); + auto task = metadata->getCurrentTask(); + if(task) + { + qDebug() << "Loading remote meta patch" << id; + m_tasks.append(task.unwrap()); + } + } + else + { + qDebug() << "Ignoring local patch" << id; + } + } + } + + // libraries download + { + m_tasks.append(std::make_shared(m_inst)); + } + + // FML libraries download and copy into the instance + { + m_tasks.append(std::make_shared(m_inst)); + } + + // assets update + { + m_tasks.append(std::make_shared(m_inst)); + } +} + +void OneSixUpdate::executeTask() +{ + if(!m_preFailure.isEmpty()) + { + emitFailed(m_preFailure); + return; + } + next(); +} + +void OneSixUpdate::next() +{ + if(m_abort) + { + emitFailed(tr("Aborted by user.")); + return; + } + if(m_failed_out_of_order) + { + emitFailed(m_fail_reason); + return; + } + m_currentTask ++; + if(m_currentTask > 0) + { + auto task = m_tasks[m_currentTask - 1]; + disconnect(task.get(), &Task::succeeded, this, &OneSixUpdate::subtaskSucceeded); + disconnect(task.get(), &Task::failed, this, &OneSixUpdate::subtaskFailed); + disconnect(task.get(), &Task::progress, this, &OneSixUpdate::progress); + disconnect(task.get(), &Task::status, this, &OneSixUpdate::setStatus); + } + if(m_currentTask == m_tasks.size()) + { + emitSucceeded(); + return; + } + auto task = m_tasks[m_currentTask]; + // if the task is already finished by the time we look at it, skip it + if(task->isFinished()) + { + qCritical() << "OneSixUpdate: Skipping finished subtask" << m_currentTask << ":" << task.get(); + next(); + } + connect(task.get(), &Task::succeeded, this, &OneSixUpdate::subtaskSucceeded); + connect(task.get(), &Task::failed, this, &OneSixUpdate::subtaskFailed); + connect(task.get(), &Task::progress, this, &OneSixUpdate::progress); + connect(task.get(), &Task::status, this, &OneSixUpdate::setStatus); + // if the task is already running, do not start it again + if(!task->isRunning()) + { + task->start(); + } +} + +void OneSixUpdate::subtaskSucceeded() +{ + if(isFinished()) + { + qCritical() << "OneSixUpdate: Subtask" << sender() << "succeeded, but work was already done!"; + return; + } + auto senderTask = QObject::sender(); + auto currentTask = m_tasks[m_currentTask].get(); + if(senderTask != currentTask) + { + qDebug() << "OneSixUpdate: Subtask" << sender() << "succeeded out of order."; + return; + } + next(); +} + +void OneSixUpdate::subtaskFailed(QString error) +{ + if(isFinished()) + { + qCritical() << "OneSixUpdate: Subtask" << sender() << "failed, but work was already done!"; + return; + } + auto senderTask = QObject::sender(); + auto currentTask = m_tasks[m_currentTask].get(); + if(senderTask != currentTask) + { + qDebug() << "OneSixUpdate: Subtask" << sender() << "failed out of order."; + m_failed_out_of_order = true; + m_fail_reason = error; + return; + } + emitFailed(error); +} + + +bool OneSixUpdate::abort() +{ + if(!m_abort) + { + m_abort = true; + auto task = m_tasks[m_currentTask]; + if(task->canAbort()) + { + return task->abort(); + } + } + return true; +} + +bool OneSixUpdate::canAbort() const +{ + return true; +} diff --git a/api/logic/minecraft/MinecraftUpdate.h b/api/logic/minecraft/MinecraftUpdate.h new file mode 100644 index 00000000..23c7da89 --- /dev/null +++ b/api/logic/minecraft/MinecraftUpdate.h @@ -0,0 +1,55 @@ +/* Copyright 2013-2017 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 +#include +#include + +#include "net/NetJob.h" +#include "tasks/Task.h" +#include "minecraft/VersionFilterData.h" +#include + +class MinecraftVersion; +class MinecraftInstance; + +class OneSixUpdate : public Task +{ + Q_OBJECT +public: + explicit OneSixUpdate(MinecraftInstance *inst, QObject *parent = 0); + void executeTask() override; + bool canAbort() const override; + +private +slots: + bool abort() override; + void subtaskSucceeded(); + void subtaskFailed(QString error); + +private: + void next(); + +private: + MinecraftInstance *m_inst = nullptr; + QList> m_tasks; + QString m_preFailure; + int m_currentTask = -1; + bool m_abort = false; + bool m_failed_out_of_order = false; + QString m_fail_reason; +}; diff --git a/api/logic/minecraft/MojangVersionFormat.cpp b/api/logic/minecraft/MojangVersionFormat.cpp index f73c8e49..a0aa2894 100644 --- a/api/logic/minecraft/MojangVersionFormat.cpp +++ b/api/logic/minecraft/MojangVersionFormat.cpp @@ -1,5 +1,5 @@ #include "MojangVersionFormat.h" -#include "onesix/OneSixVersionFormat.h" +#include "OneSixVersionFormat.h" #include "MojangDownloadInfo.h" #include "Json.h" diff --git a/api/logic/minecraft/OneSixVersionFormat.cpp b/api/logic/minecraft/OneSixVersionFormat.cpp new file mode 100644 index 00000000..7ebf514f --- /dev/null +++ b/api/logic/minecraft/OneSixVersionFormat.cpp @@ -0,0 +1,332 @@ +#include "OneSixVersionFormat.h" +#include +#include "minecraft/ParseUtils.h" +#include + +using namespace Json; + +static void readString(const QJsonObject &root, const QString &key, QString &variable) +{ + if (root.contains(key)) + { + variable = requireString(root.value(key)); + } +} + +LibraryPtr OneSixVersionFormat::libraryFromJson(const QJsonObject &libObj, const QString &filename) +{ + LibraryPtr out = MojangVersionFormat::libraryFromJson(libObj, filename); + readString(libObj, "MMC-hint", out->m_hint); + readString(libObj, "MMC-absulute_url", out->m_absoluteURL); + readString(libObj, "MMC-absoluteUrl", out->m_absoluteURL); + readString(libObj, "MMC-filename", out->m_filename); + readString(libObj, "MMC-displayname", out->m_displayname); + return out; +} + +QJsonObject OneSixVersionFormat::libraryToJson(Library *library) +{ + QJsonObject libRoot = MojangVersionFormat::libraryToJson(library); + if (library->m_absoluteURL.size()) + libRoot.insert("MMC-absoluteUrl", library->m_absoluteURL); + if (library->m_hint.size()) + libRoot.insert("MMC-hint", library->m_hint); + if (library->m_filename.size()) + libRoot.insert("MMC-filename", library->m_filename); + if (library->m_displayname.size()) + libRoot.insert("MMC-displayname", library->m_displayname); + return libRoot; +} + +VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc, const QString &filename, const bool requireOrder) +{ + VersionFilePtr out(new VersionFile()); + if (doc.isEmpty() || doc.isNull()) + { + throw JSONValidationError(filename + " is empty or null"); + } + if (!doc.isObject()) + { + throw JSONValidationError(filename + " is not an object"); + } + + QJsonObject root = doc.object(); + + if (requireOrder) + { + if (root.contains("order")) + { + out->order = requireInteger(root.value("order")); + } + else + { + // FIXME: evaluate if we don't want to throw exceptions here instead + qCritical() << filename << "doesn't contain an order field"; + } + } + + out->name = root.value("name").toString(); + + if(root.contains("uid")) + { + out->uid = root.value("uid").toString(); + } + else + { + out->uid = root.value("fileId").toString(); + } + + out->version = root.value("version").toString(); + out->dependsOnMinecraftVersion = root.value("mcVersion").toString(); + // out->filename = filename; + + MojangVersionFormat::readVersionProperties(root, out.get()); + + // added for legacy Minecraft window embedding, TODO: remove + readString(root, "appletClass", out->appletClass); + + if (root.contains("+tweakers")) + { + for (auto tweakerVal : requireArray(root.value("+tweakers"))) + { + out->addTweakers.append(requireString(tweakerVal)); + } + } + + if (root.contains("+traits")) + { + for (auto tweakerVal : requireArray(root.value("+traits"))) + { + out->traits.insert(requireString(tweakerVal)); + } + } + + + if (root.contains("jarMods")) + { + for (auto libVal : requireArray(root.value("jarMods"))) + { + QJsonObject libObj = requireObject(libVal); + // parse the jarmod + auto lib = OneSixVersionFormat::jarModFromJson(libObj, filename); + // and add to jar mods + out->jarMods.append(lib); + } + } + else if (root.contains("+jarMods")) // DEPRECATED: old style '+jarMods' are only here for backwards compatibility + { + for (auto libVal : requireArray(root.value("+jarMods"))) + { + QJsonObject libObj = requireObject(libVal); + // parse the jarmod + auto lib = OneSixVersionFormat::plusJarModFromJson(libObj, filename, out->name); + // and add to jar mods + out->jarMods.append(lib); + } + } + + if (root.contains("mods")) + { + for (auto libVal : requireArray(root.value("mods"))) + { + QJsonObject libObj = requireObject(libVal); + // parse the jarmod + auto lib = OneSixVersionFormat::modFromJson(libObj, filename); + // and add to jar mods + out->mods.append(lib); + } + } + + auto readLibs = [&](const char * which) + { + for (auto libVal : requireArray(root.value(which))) + { + QJsonObject libObj = requireObject(libVal); + // parse the library + auto lib = libraryFromJson(libObj, filename); + out->libraries.append(lib); + } + }; + bool hasPlusLibs = root.contains("+libraries"); + bool hasLibs = root.contains("libraries"); + if (hasPlusLibs && hasLibs) + { + out->addProblem(ProblemSeverity::Warning, + QObject::tr("Version file has both '+libraries' and 'libraries'. This is no longer supported.")); + readLibs("libraries"); + readLibs("+libraries"); + } + else if (hasLibs) + { + readLibs("libraries"); + } + else if(hasPlusLibs) + { + readLibs("+libraries"); + } + + // if we have mainJar, just use it + if(root.contains("mainJar")) + { + QJsonObject libObj = requireObject(root, "mainJar"); + out->mainJar = libraryFromJson(libObj, filename); + } + // else reconstruct it from downloads and id ... if that's available + else if(!out->minecraftVersion.isEmpty()) + { + auto lib = std::make_shared(); + lib->setRawName(GradleSpecifier(QString("com.mojang:minecraft:%1:client").arg(out->minecraftVersion))); + // we have a reliable client download, use it. + if(out->mojangDownloads.contains("client")) + { + auto LibDLInfo = std::make_shared(); + LibDLInfo->artifact = out->mojangDownloads["client"]; + lib->setMojangDownloadInfo(LibDLInfo); + } + // we got nothing... guess based on ancient hardcoded Mojang behaviour + // FIXME: this will eventually break... + else + { + lib->setAbsoluteUrl(URLConstants::getLegacyJarUrl(out->minecraftVersion)); + } + out->mainJar = lib; + } + + /* removed features that shouldn't be used */ + if (root.contains("tweakers")) + { + out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element 'tweakers'")); + } + if (root.contains("-libraries")) + { + out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-libraries'")); + } + if (root.contains("-tweakers")) + { + out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-tweakers'")); + } + if (root.contains("-minecraftArguments")) + { + out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-minecraftArguments'")); + } + if (root.contains("+minecraftArguments")) + { + out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '+minecraftArguments'")); + } + return out; +} + +QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch, bool saveOrder) +{ + QJsonObject root; + if (saveOrder) + { + root.insert("order", patch->order); + } + writeString(root, "name", patch->name); + + writeString(root, "uid", patch->uid); + + writeString(root, "version", patch->version); + writeString(root, "mcVersion", patch->dependsOnMinecraftVersion); + + MojangVersionFormat::writeVersionProperties(patch.get(), root); + + if(patch->mainJar) + { + root.insert("mainJar", libraryToJson(patch->mainJar.get())); + } + writeString(root, "appletClass", patch->appletClass); + writeStringList(root, "+tweakers", patch->addTweakers); + writeStringList(root, "+traits", patch->traits.toList()); + if (!patch->libraries.isEmpty()) + { + QJsonArray array; + for (auto value: patch->libraries) + { + array.append(OneSixVersionFormat::libraryToJson(value.get())); + } + root.insert("libraries", array); + } + if (!patch->jarMods.isEmpty()) + { + QJsonArray array; + for (auto value: patch->jarMods) + { + array.append(OneSixVersionFormat::jarModtoJson(value.get())); + } + root.insert("jarMods", array); + } + if (!patch->mods.isEmpty()) + { + QJsonArray array; + for (auto value: patch->jarMods) + { + array.append(OneSixVersionFormat::modtoJson(value.get())); + } + root.insert("mods", array); + } + // write the contents to a json document. + { + QJsonDocument out; + out.setObject(root); + return out; + } +} + +LibraryPtr OneSixVersionFormat::plusJarModFromJson(const QJsonObject &libObj, const QString &filename, const QString &originalName) +{ + LibraryPtr out(new Library()); + if (!libObj.contains("name")) + { + throw JSONValidationError(filename + + "contains a jarmod that doesn't have a 'name' field"); + } + + // just make up something unique on the spot for the library name. + auto uuid = QUuid::createUuid(); + QString id = uuid.toString().remove('{').remove('}'); + out->setRawName(GradleSpecifier("org.multimc.jarmods:" + id + ":1")); + + // filename override is the old name + out->setFilename(libObj.value("name").toString()); + + // it needs to be local, it is stored in the instance jarmods folder + out->setHint("local"); + + // read the original name if present - some versions did not set it + // it is the original jar mod filename before it got renamed at the point of addition + auto displayName = libObj.value("originalName").toString(); + if(displayName.isEmpty()) + { + auto fixed = originalName; + fixed.remove(" (jar mod)"); + out->setDisplayName(fixed); + } + else + { + out->setDisplayName(displayName); + } + return out; +} + +LibraryPtr OneSixVersionFormat::jarModFromJson(const QJsonObject& libObj, const QString& filename) +{ + return libraryFromJson(libObj, filename); +} + + +QJsonObject OneSixVersionFormat::jarModtoJson(Library *jarmod) +{ + return libraryToJson(jarmod); +} + +LibraryPtr OneSixVersionFormat::modFromJson(const QJsonObject& libObj, const QString& filename) +{ + return libraryFromJson(libObj, filename); +} + +QJsonObject OneSixVersionFormat::modtoJson(Library *jarmod) +{ + return libraryToJson(jarmod); +} diff --git a/api/logic/minecraft/OneSixVersionFormat.h b/api/logic/minecraft/OneSixVersionFormat.h new file mode 100644 index 00000000..64f18da8 --- /dev/null +++ b/api/logic/minecraft/OneSixVersionFormat.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include + +class OneSixVersionFormat +{ +public: + // version files / profile patches + static VersionFilePtr versionFileFromJson(const QJsonDocument &doc, const QString &filename, const bool requireOrder); + static QJsonDocument versionFileToJson(const VersionFilePtr &patch, bool saveOrder); + + // libraries + static LibraryPtr libraryFromJson(const QJsonObject &libObj, const QString &filename); + static QJsonObject libraryToJson(Library *library); + + // DEPRECATED: old 'plus' jar mods generated by the application + static LibraryPtr plusJarModFromJson(const QJsonObject &libObj, const QString &filename, const QString &originalName); + + // new jar mods derived from libraries + static LibraryPtr jarModFromJson(const QJsonObject &libObj, const QString &filename); + static QJsonObject jarModtoJson(Library * jarmod); + + // mods, also derived from libraries + static LibraryPtr modFromJson(const QJsonObject &libObj, const QString &filename); + static QJsonObject modtoJson(Library * jarmod); +}; diff --git a/api/logic/minecraft/ProfileStrategy.h b/api/logic/minecraft/ProfileStrategy.h deleted file mode 100644 index 09a09f24..00000000 --- a/api/logic/minecraft/ProfileStrategy.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include "ProfileUtils.h" -#include "ProfilePatch.h" - -class MinecraftProfile; - -class ProfileStrategy -{ - friend class MinecraftProfile; -public: - virtual ~ProfileStrategy(){}; - - /// load the patch files into the profile - virtual void load() = 0; - - /// reset the order of patches - virtual bool resetOrder() = 0; - - /// save the order of patches, given the order - virtual bool saveOrder(ProfileUtils::PatchOrder order) = 0; - - /// install a list of jar mods into the instance - virtual bool installJarMods(QStringList filepaths) = 0; - - /// install a custom jar (replaces the one from the Minecraft component) - virtual bool installCustomJar(QString filepath) = 0; - - /// remove any files or records that constitute the version patch - virtual bool removePatch(ProfilePatchPtr jarMod) = 0; - - /// make the patch custom, if possible - virtual bool customizePatch(ProfilePatchPtr patch) = 0; - - /// revert the custom patch to 'vanilla', if possible - virtual bool revertPatch(ProfilePatchPtr patch) = 0; -protected: - MinecraftProfile *profile; -}; diff --git a/api/logic/minecraft/ProfileUtils.cpp b/api/logic/minecraft/ProfileUtils.cpp index 8c5bc052..c8722f7e 100644 --- a/api/logic/minecraft/ProfileUtils.cpp +++ b/api/logic/minecraft/ProfileUtils.cpp @@ -1,6 +1,6 @@ #include "ProfileUtils.h" #include "minecraft/VersionFilterData.h" -#include "minecraft/onesix/OneSixVersionFormat.h" +#include "minecraft/OneSixVersionFormat.h" #include "Json.h" #include diff --git a/api/logic/minecraft/launch/ModMinecraftJar.cpp b/api/logic/minecraft/launch/ModMinecraftJar.cpp index b56f6fca..497a9d2d 100644 --- a/api/logic/minecraft/launch/ModMinecraftJar.cpp +++ b/api/logic/minecraft/launch/ModMinecraftJar.cpp @@ -14,31 +14,47 @@ */ #include "ModMinecraftJar.h" -#include -#include +#include "launch/LaunchTask.h" +#include "MMCZip.h" +#include "minecraft/OpSys.h" +#include "FileSystem.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/MinecraftProfile.h" void ModMinecraftJar::executeTask() { - m_jarModTask = m_parent->instance()->createJarModdingTask(); - if(m_jarModTask) + auto m_inst = std::dynamic_pointer_cast(m_parent->instance()); + + // nuke obsolete stripped jar(s) if needed + if(!FS::ensureFolderPathExists(m_inst->binRoot())) { - connect(m_jarModTask.get(), SIGNAL(finished()), this, SLOT(jarModdingFinished())); - m_jarModTask->start(); - return; + emitFailed(tr("Couldn't create the bin folder for Minecraft.jar")); } - emitSucceeded(); -} - -void ModMinecraftJar::jarModdingFinished() -{ - if(m_jarModTask->wasSuccessful()) + auto finalJarPath = QDir(m_inst->binRoot()).absoluteFilePath("minecraft.jar"); + QFile finalJar(finalJarPath); + if(finalJar.exists()) { - emitSucceeded(); + if(!finalJar.remove()) + { + emitFailed(tr("Couldn't remove stale jar file: %1").arg(finalJarPath)); + return; + } } - else + + // create temporary modded jar, if needed + auto profile = m_inst->getMinecraftProfile(); + auto jarMods = m_inst->getJarMods(); + if(jarMods.size()) { - QString reason = tr("jar modding failed because: %1.\n\n").arg(m_jarModTask->failReason()); - emit logLine(reason, MessageLevel::Fatal); - emitFailed(reason); + auto mainJar = profile->getMainJar(); + QStringList jars, temp1, temp2, temp3, temp4; + mainJar->getApplicableFiles(currentSystem, jars, temp1, temp2, temp3, m_inst->getLocalLibraryPath()); + auto sourceJarPath = jars[0]; + if(!MMCZip::createModdedJar(sourceJarPath, finalJarPath, jarMods)) + { + emitFailed(tr("Failed to create the custom Minecraft jar file.")); + return; + } } + emitSucceeded(); } diff --git a/api/logic/minecraft/launch/ModMinecraftJar.h b/api/logic/minecraft/launch/ModMinecraftJar.h index 1196c487..ab8d7dca 100644 --- a/api/logic/minecraft/launch/ModMinecraftJar.h +++ b/api/logic/minecraft/launch/ModMinecraftJar.h @@ -18,7 +18,6 @@ #include #include -// FIXME: temporary wrapper for existing task. class ModMinecraftJar: public LaunchStep { Q_OBJECT @@ -31,9 +30,4 @@ public: { return false; } -private slots: - void jarModdingFinished(); - -private: - std::shared_ptr m_jarModTask; }; diff --git a/api/logic/minecraft/onesix/OneSixInstance.cpp b/api/logic/minecraft/onesix/OneSixInstance.cpp deleted file mode 100644 index 34253f02..00000000 --- a/api/logic/minecraft/onesix/OneSixInstance.cpp +++ /dev/null @@ -1,706 +0,0 @@ -/* Copyright 2013-2017 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 -#include -#include -#include - -#include "OneSixInstance.h" -#include "OneSixUpdate.h" -#include "OneSixProfileStrategy.h" - -#include "minecraft/MinecraftProfile.h" -#include "minecraft/launch/ModMinecraftJar.h" -#include "MMCZip.h" - -#include "minecraft/AssetsUtils.h" -#include "minecraft/WorldList.h" -#include - -OneSixInstance::OneSixInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) - : MinecraftInstance(globalSettings, settings, rootDir) -{ - // set explicitly during instance creation - m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, ""); - - // defaults to the version we've been using for years (2.9.1) - m_settings->registerSetting("LWJGLVersion", "2.9.1"); - - // optionals - m_settings->registerSetting("ForgeVersion", ""); - m_settings->registerSetting("LiteloaderVersion", ""); -} - -void OneSixInstance::init() -{ - createProfile(); -} - -void OneSixInstance::createProfile() -{ - m_profile.reset(new MinecraftProfile(new OneSixProfileStrategy(this))); -} - -QSet OneSixInstance::traits() -{ - auto version = getMinecraftProfile(); - if (!version) - { - return {"version-incomplete"}; - } - else - { - return version->getTraits(); - } -} - -shared_qobject_ptr OneSixInstance::createUpdateTask() -{ - return shared_qobject_ptr(new OneSixUpdate(this)); -} - -QString replaceTokensIn(QString text, QMap with) -{ - QString result; - QRegExp token_regexp("\\$\\{(.+)\\}"); - token_regexp.setMinimal(true); - QStringList list; - int tail = 0; - int head = 0; - while ((head = token_regexp.indexIn(text, head)) != -1) - { - result.append(text.mid(tail, head - tail)); - QString key = token_regexp.cap(1); - auto iter = with.find(key); - if (iter != with.end()) - { - result.append(*iter); - } - head += token_regexp.matchedLength(); - tail = head; - } - result.append(text.mid(tail)); - return result; -} - -QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session) const -{ - QString args_pattern = m_profile->getMinecraftArguments(); - for (auto tweaker : m_profile->getTweakers()) - { - args_pattern += " --tweakClass " + tweaker; - } - - QMap token_mapping; - // yggdrasil! - if(session) - { - token_mapping["auth_username"] = session->username; - token_mapping["auth_session"] = session->session; - token_mapping["auth_access_token"] = session->access_token; - token_mapping["auth_player_name"] = session->player_name; - token_mapping["auth_uuid"] = session->uuid; - token_mapping["user_properties"] = session->serializeUserProperties(); - token_mapping["user_type"] = session->user_type; - } - - // blatant self-promotion. - token_mapping["profile_name"] = token_mapping["version_name"] = "MultiMC5"; - if(m_profile->isVanilla()) - { - token_mapping["version_type"] = m_profile->getMinecraftVersionType(); - } - else - { - token_mapping["version_type"] = "custom"; - } - - QString absRootDir = QDir(minecraftRoot()).absolutePath(); - token_mapping["game_directory"] = absRootDir; - QString absAssetsDir = QDir("assets/").absolutePath(); - auto assets = m_profile->getMinecraftAssets(); - token_mapping["game_assets"] = AssetsUtils::reconstructAssets(assets->id).absolutePath(); - - // 1.7.3+ assets tokens - token_mapping["assets_root"] = absAssetsDir; - token_mapping["assets_index_name"] = assets->id; - - QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts); - for (int i = 0; i < parts.length(); i++) - { - parts[i] = replaceTokensIn(parts[i], token_mapping); - } - return parts; -} - -QString OneSixInstance::getNativePath() const -{ - QDir natives_dir(FS::PathCombine(instanceRoot(), "natives/")); - return natives_dir.absolutePath(); -} - -QString OneSixInstance::getLocalLibraryPath() const -{ - QDir libraries_dir(FS::PathCombine(instanceRoot(), "libraries/")); - return libraries_dir.absolutePath(); -} - -QString OneSixInstance::createLaunchScript(AuthSessionPtr session) -{ - QString launchScript; - - if (!m_profile) - return nullptr; - - auto mainClass = getMainClass(); - if (!mainClass.isEmpty()) - { - launchScript += "mainClass " + mainClass + "\n"; - } - auto appletClass = m_profile->getAppletClass(); - if (!appletClass.isEmpty()) - { - launchScript += "appletClass " + appletClass + "\n"; - } - - // generic minecraft params - for (auto param : processMinecraftArgs(session)) - { - launchScript += "param " + param + "\n"; - } - - // window size, title and state, legacy - { - QString windowParams; - if (settings()->get("LaunchMaximized").toBool()) - windowParams = "max"; - else - windowParams = QString("%1x%2") - .arg(settings()->get("MinecraftWinWidth").toInt()) - .arg(settings()->get("MinecraftWinHeight").toInt()); - launchScript += "windowTitle " + windowTitle() + "\n"; - launchScript += "windowParams " + windowParams + "\n"; - } - - // legacy auth - if(session) - { - launchScript += "userName " + session->player_name + "\n"; - launchScript += "sessionId " + session->session + "\n"; - } - - // libraries and class path. - { - QStringList jars, nativeJars; - auto javaArchitecture = settings()->get("JavaArchitecture").toString(); - m_profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot()); - for(auto file: jars) - { - launchScript += "cp " + file + "\n"; - } - for(auto file: nativeJars) - { - launchScript += "ext " + file + "\n"; - } - launchScript += "natives " + getNativePath() + "\n"; - } - - for (auto trait : m_profile->getTraits()) - { - launchScript += "traits " + trait + "\n"; - } - launchScript += "launcher onesix\n"; - return launchScript; -} - -QStringList OneSixInstance::verboseDescription(AuthSessionPtr session) -{ - QStringList out; - out << "Main Class:" << " " + getMainClass() << ""; - out << "Native path:" << " " + getNativePath() << ""; - - - auto alltraits = traits(); - if(alltraits.size()) - { - out << "Traits:"; - for (auto trait : alltraits) - { - out << "traits " + trait; - } - out << ""; - } - - // libraries and class path. - { - out << "Libraries:"; - QStringList jars, nativeJars; - auto javaArchitecture = settings()->get("JavaArchitecture").toString(); - m_profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot()); - auto printLibFile = [&](const QString & path) - { - QFileInfo info(path); - if(info.exists()) - { - out << " " + path; - } - else - { - out << " " + path + " (missing)"; - } - }; - for(auto file: jars) - { - printLibFile(file); - } - out << ""; - out << "Native libraries:"; - for(auto file: nativeJars) - { - printLibFile(file); - } - out << ""; - } - - if(loaderModList()->size()) - { - out << "Mods:"; - for(auto & mod: loaderModList()->allMods()) - { - if(!mod.enabled()) - continue; - if(mod.type() == Mod::MOD_FOLDER) - continue; - // TODO: proper implementation would need to descend into folders. - - out << " " + mod.filename().completeBaseName(); - } - out << ""; - } - - if(coreModList()->size()) - { - out << "Core Mods:"; - for(auto & coremod: coreModList()->allMods()) - { - if(!coremod.enabled()) - continue; - if(coremod.type() == Mod::MOD_FOLDER) - continue; - // TODO: proper implementation would need to descend into folders. - - out << " " + coremod.filename().completeBaseName(); - } - out << ""; - } - - auto & jarMods = m_profile->getJarMods(); - if(jarMods.size()) - { - out << "Jar Mods:"; - for(auto & jarmod: jarMods) - { - auto displayname = jarmod->displayName(currentSystem); - auto realname = jarmod->filename(currentSystem); - if(displayname != realname) - { - out << " " + displayname + " (" + realname + ")"; - } - else - { - out << " " + realname; - } - } - out << ""; - } - - auto params = processMinecraftArgs(nullptr); - out << "Params:"; - out << " " + params.join(' '); - 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; -} - - -std::shared_ptr OneSixInstance::createMainLaunchStep(LaunchTask * parent, AuthSessionPtr session) -{ - auto method = launchMethod(); - if(method == "LauncherPart") - { - auto step = std::make_shared(parent); - step->setAuthSession(session); - step->setWorkingDirectory(minecraftRoot()); - return step; - } - else if (method == "DirectJava") - { - auto step = std::make_shared(parent); - step->setWorkingDirectory(minecraftRoot()); - step->setAuthSession(session); - return step; - } - return nullptr; -} - -class JarModTask : public Task -{ - Q_OBJECT -public: - explicit JarModTask(std::shared_ptr inst) : Task(nullptr), m_inst(inst) - { - } - virtual void executeTask() - { - auto profile = m_inst->getMinecraftProfile(); - // nuke obsolete stripped jar(s) if needed - QString version_id = profile->getMinecraftVersion(); - if(!FS::ensureFolderPathExists(m_inst->binRoot())) - { - emitFailed(tr("Couldn't create the bin folder for Minecraft.jar")); - } - auto finalJarPath = QDir(m_inst->binRoot()).absoluteFilePath("minecraft.jar"); - QFile finalJar(finalJarPath); - if(finalJar.exists()) - { - if(!finalJar.remove()) - { - emitFailed(tr("Couldn't remove stale jar file: %1").arg(finalJarPath)); - return; - } - } - - // create temporary modded jar, if needed - auto jarMods = m_inst->getJarMods(); - if(jarMods.size()) - { - auto mainJar = profile->getMainJar(); - QStringList jars, temp1, temp2, temp3, temp4; - mainJar->getApplicableFiles(currentSystem, jars, temp1, temp2, temp3, m_inst->getLocalLibraryPath()); - auto sourceJarPath = jars[0]; - if(!MMCZip::createModdedJar(sourceJarPath, finalJarPath, jarMods)) - { - emitFailed(tr("Failed to create the custom Minecraft jar file.")); - return; - } - } - emitSucceeded(); - } - std::shared_ptr m_inst; -}; - -std::shared_ptr OneSixInstance::createJarModdingTask() -{ - return std::make_shared(std::dynamic_pointer_cast(shared_from_this())); -} - -std::shared_ptr OneSixInstance::loaderModList() const -{ - if (!m_loader_mod_list) - { - m_loader_mod_list.reset(new ModList(loaderModsDir())); - } - m_loader_mod_list->update(); - return m_loader_mod_list; -} - -std::shared_ptr OneSixInstance::coreModList() const -{ - if (!m_core_mod_list) - { - m_core_mod_list.reset(new ModList(coreModsDir())); - } - m_core_mod_list->update(); - return m_core_mod_list; -} - -std::shared_ptr OneSixInstance::resourcePackList() const -{ - if (!m_resource_pack_list) - { - m_resource_pack_list.reset(new ModList(resourcePacksDir())); - } - m_resource_pack_list->update(); - return m_resource_pack_list; -} - -std::shared_ptr OneSixInstance::texturePackList() const -{ - if (!m_texture_pack_list) - { - m_texture_pack_list.reset(new ModList(texturePacksDir())); - } - m_texture_pack_list->update(); - return m_texture_pack_list; -} - -std::shared_ptr OneSixInstance::worldList() const -{ - if (!m_world_list) - { - m_world_list.reset(new WorldList(worldDir())); - } - return m_world_list; -} - -bool OneSixInstance::setIntendedVersionId(QString version) -{ - return setComponentVersion("net.minecraft", version); -} - -QString OneSixInstance::intendedVersionId() const -{ - return getComponentVersion("net.minecraft"); -} - -bool OneSixInstance::setComponentVersion(const QString& uid, const QString& version) -{ - if(uid == "net.minecraft") - { - settings()->set("IntendedVersion", version); - } - else if (uid == "org.lwjgl") - { - settings()->set("LWJGLVersion", version); - } - else if (uid == "net.minecraftforge") - { - settings()->set("ForgeVersion", version); - } - else if (uid == "com.mumfrey.liteloader") - { - settings()->set("LiteloaderVersion", version); - } - if(getMinecraftProfile()) - { - clearProfile(); - } - emit propertiesChanged(this); - return true; -} - -QString OneSixInstance::getComponentVersion(const QString& uid) const -{ - if(uid == "net.minecraft") - { - return settings()->get("IntendedVersion").toString(); - } - else if(uid == "org.lwjgl") - { - return settings()->get("LWJGLVersion").toString(); - } - else if(uid == "net.minecraftforge") - { - return settings()->get("ForgeVersion").toString(); - } - else if(uid == "com.mumfrey.liteloader") - { - return settings()->get("LiteloaderVersion").toString(); - } - return QString(); -} - -QList< Mod > OneSixInstance::getJarMods() const -{ - QList mods; - for (auto jarmod : m_profile->getJarMods()) - { - QStringList jar, temp1, temp2, temp3; - jarmod->getApplicableFiles(currentSystem, jar, temp1, temp2, temp3, jarmodsPath().absolutePath()); - // QString filePath = jarmodsPath().absoluteFilePath(jarmod->filename(currentSystem)); - mods.push_back(Mod(QFileInfo(jar[0]))); - } - return mods; -} - -void OneSixInstance::setShouldUpdate(bool) -{ -} - -bool OneSixInstance::shouldUpdate() const -{ - return true; -} - -QString OneSixInstance::currentVersionId() const -{ - return intendedVersionId(); -} - -void OneSixInstance::reloadProfile() -{ - m_profile->reload(); - setVersionBroken(m_profile->getProblemSeverity() == ProblemSeverity::Error); - emit versionReloaded(); -} - -void OneSixInstance::clearProfile() -{ - m_profile->clear(); - emit versionReloaded(); -} - -std::shared_ptr OneSixInstance::getMinecraftProfile() const -{ - return m_profile; -} - -QDir OneSixInstance::librariesPath() const -{ - return QDir::current().absoluteFilePath("libraries"); -} - -QDir OneSixInstance::jarmodsPath() const -{ - return QDir(jarModsDir()); -} - -QDir OneSixInstance::versionsPath() const -{ - return QDir::current().absoluteFilePath("versions"); -} - -bool OneSixInstance::providesVersionFile() const -{ - return false; -} - -bool OneSixInstance::reload() -{ - if (BaseInstance::reload()) - { - try - { - reloadProfile(); - return true; - } - catch (...) - { - return false; - } - } - return false; -} - -QString OneSixInstance::loaderModsDir() const -{ - return FS::PathCombine(minecraftRoot(), "mods"); -} - -QString OneSixInstance::coreModsDir() const -{ - return FS::PathCombine(minecraftRoot(), "coremods"); -} - -QString OneSixInstance::resourcePacksDir() const -{ - return FS::PathCombine(minecraftRoot(), "resourcepacks"); -} - -QString OneSixInstance::texturePacksDir() const -{ - return FS::PathCombine(minecraftRoot(), "texturepacks"); -} - -QString OneSixInstance::instanceConfigFolder() const -{ - return FS::PathCombine(minecraftRoot(), "config"); -} - -QString OneSixInstance::jarModsDir() const -{ - return FS::PathCombine(instanceRoot(), "jarmods"); -} - -QString OneSixInstance::FMLlibDir() const -{ - return FS::PathCombine(minecraftRoot(), "lib"); -} - -QString OneSixInstance::customLibrariesDir() const -{ - return FS::PathCombine(instanceRoot(), "libraries"); -} - -QString OneSixInstance::worldDir() const -{ - return FS::PathCombine(minecraftRoot(), "saves"); -} - -QStringList OneSixInstance::extraArguments() const -{ - auto list = BaseInstance::extraArguments(); - auto version = getMinecraftProfile(); - if (!version) - return list; - auto jarMods = getJarMods(); - if (!jarMods.isEmpty()) - { - list.append({"-Dfml.ignoreInvalidMinecraftCertificates=true", - "-Dfml.ignorePatchDiscrepancies=true"}); - } - return list; -} - -std::shared_ptr OneSixInstance::getSharedPtr() -{ - return std::dynamic_pointer_cast(BaseInstance::getSharedPtr()); -} - -QString OneSixInstance::typeName() const -{ - return tr("OneSix"); -} - -QStringList OneSixInstance::validLaunchMethods() -{ - return {"LauncherPart", "DirectJava"}; -} - -QStringList OneSixInstance::getClassPath() const -{ - QStringList jars, nativeJars; - auto javaArchitecture = settings()->get("JavaArchitecture").toString(); - m_profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot()); - return jars; -} - -QString OneSixInstance::getMainClass() const -{ - return m_profile->getMainClass(); -} - -QStringList OneSixInstance::getNativeJars() const -{ - QStringList jars, nativeJars; - auto javaArchitecture = settings()->get("JavaArchitecture").toString(); - m_profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot()); - return nativeJars; -} - -#include "OneSixInstance.moc" diff --git a/api/logic/minecraft/onesix/OneSixInstance.h b/api/logic/minecraft/onesix/OneSixInstance.h deleted file mode 100644 index 0cb4340b..00000000 --- a/api/logic/minecraft/onesix/OneSixInstance.h +++ /dev/null @@ -1,128 +0,0 @@ -/* Copyright 2013-2017 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 "minecraft/MinecraftInstance.h" - -#include "minecraft/MinecraftProfile.h" -#include "minecraft/ModList.h" - -#include "multimc_logic_export.h" - -class MULTIMC_LOGIC_EXPORT OneSixInstance : public MinecraftInstance -{ - Q_OBJECT -public: - explicit OneSixInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir); - virtual ~OneSixInstance(){}; - - virtual void init() override; - - ////// Mod Lists ////// - std::shared_ptr loaderModList() const; - std::shared_ptr coreModList() const; - std::shared_ptr resourcePackList() const override; - std::shared_ptr texturePackList() const override; - std::shared_ptr worldList() const override; - virtual QList getJarMods() const override; - virtual void createProfile(); - - virtual QSet traits() override; - - ////// Directories and files ////// - QString jarModsDir() const; - QString resourcePacksDir() const; - QString texturePacksDir() const; - QString loaderModsDir() const; - QString coreModsDir() const; - QString FMLlibDir() const; - QString customLibrariesDir() const; - QString worldDir() const; - virtual QString instanceConfigFolder() const override; - - virtual shared_qobject_ptr createUpdateTask() override; - virtual std::shared_ptr createJarModdingTask() override; - virtual QString createLaunchScript(AuthSessionPtr session) override; - QStringList verboseDescription(AuthSessionPtr session) override; - - virtual QString intendedVersionId() const override; - virtual bool setIntendedVersionId(QString version) override; - virtual QString currentVersionId() const override; - - QString getComponentVersion(const QString &uid) const; - bool setComponentVersion(const QString &uid, const QString &version); - - virtual bool shouldUpdate() const override; - virtual void setShouldUpdate(bool val) override; - - /** - * reload the profile, including version json files. - * - * throws various exceptions :3 - */ - void reloadProfile(); - - /// clears all version information in preparation for an update - void clearProfile(); - - /// get the current full version info - std::shared_ptr getMinecraftProfile() const; - - virtual QDir jarmodsPath() const; - virtual QDir librariesPath() const; - virtual QDir versionsPath() const; - virtual bool providesVersionFile() const; - - bool reload() override; - - virtual QStringList extraArguments() const override; - - std::shared_ptr getSharedPtr(); - - virtual QString typeName() const override; - - bool canExport() const override - { - return true; - } - - QStringList getClassPath() const override; - QString getMainClass() const override; - - QStringList getNativeJars() const override; - QString getNativePath() const override; - - QString getLocalLibraryPath() const override; - - QStringList processMinecraftArgs(AuthSessionPtr account) const override; - -protected: - std::shared_ptr createMainLaunchStep(LaunchTask *parent, AuthSessionPtr session) override; - QStringList validLaunchMethods() override; - -signals: - void versionReloaded(); - -protected: - std::shared_ptr m_profile; - mutable std::shared_ptr m_loader_mod_list; - mutable std::shared_ptr m_core_mod_list; - mutable std::shared_ptr m_resource_pack_list; - mutable std::shared_ptr m_texture_pack_list; - mutable std::shared_ptr m_world_list; -}; - -Q_DECLARE_METATYPE(std::shared_ptr) diff --git a/api/logic/minecraft/onesix/OneSixProfileStrategy.cpp b/api/logic/minecraft/onesix/OneSixProfileStrategy.cpp deleted file mode 100644 index 39dd8343..00000000 --- a/api/logic/minecraft/onesix/OneSixProfileStrategy.cpp +++ /dev/null @@ -1,471 +0,0 @@ -#include "OneSixProfileStrategy.h" -#include "OneSixInstance.h" -#include "OneSixVersionFormat.h" - -#include "Env.h" -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -OneSixProfileStrategy::OneSixProfileStrategy(OneSixInstance* instance) -{ - m_instance = instance; -} - -void OneSixProfileStrategy::upgradeDeprecatedFiles() -{ - auto versionJsonPath = FS::PathCombine(m_instance->instanceRoot(), "version.json"); - auto customJsonPath = FS::PathCombine(m_instance->instanceRoot(), "custom.json"); - auto mcJson = FS::PathCombine(m_instance->instanceRoot(), "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" << m_instance->name(); - return; - } - if(!renameFile.isEmpty() && QFile::exists(renameFile)) - { - if(!QFile::rename(renameFile, renameFile + ".old")) - { - qWarning() << "Couldn't rename" << renameFile << "to" << renameFile + ".old" << "in" << m_instance->name(); - return; - } - } - auto file = ProfileUtils::parseJsonFile(QFileInfo(sourceFile), false); - ProfileUtils::removeLwjglFromPatch(file); - file->uid = "net.minecraft"; - file->version = file->minecraftVersion; - file->name = "Minecraft"; - auto data = OneSixVersionFormat::versionFileToJson(file, false).toJson(); - QSaveFile newPatchFile(mcJson); - if(!newPatchFile.open(QIODevice::WriteOnly)) - { - newPatchFile.cancelWriting(); - qWarning() << "Couldn't open main patch for writing in" << m_instance->name(); - return; - } - newPatchFile.write(data); - if(!newPatchFile.commit()) - { - qWarning() << "Couldn't save main patch in" << m_instance->name(); - return; - } - if(!QFile::rename(sourceFile, sourceFile + ".old")) - { - qWarning() << "Couldn't rename" << sourceFile << "to" << sourceFile + ".old" << "in" << m_instance->name(); - return; - } - } -} - -void OneSixProfileStrategy::loadDefaultBuiltinPatches() -{ - auto addBuiltinPatch = [&](const QString &uid, const QString intendedVersion, int order) - { - auto jsonFilePath = FS::PathCombine(m_instance->instanceRoot(), "patches" , uid + ".json"); - // load up the base minecraft patch - ProfilePatchPtr profilePatch; - if(QFile::exists(jsonFilePath)) - { - auto file = ProfileUtils::parseJsonFile(QFileInfo(jsonFilePath), false); - if(file->version.isEmpty()) - { - file->version = intendedVersion; - } - profilePatch = std::make_shared(file, jsonFilePath); - profilePatch->setVanilla(false); - profilePatch->setRevertible(true); - } - else - { - auto metaVersion = ENV.metadataIndex()->get(uid, intendedVersion); - profilePatch = std::make_shared(metaVersion); - profilePatch->setVanilla(true); - } - profilePatch->setOrder(order); - profile->appendPatch(profilePatch); - }; - addBuiltinPatch("net.minecraft", m_instance->getComponentVersion("net.minecraft"), -2); - addBuiltinPatch("org.lwjgl", m_instance->getComponentVersion("org.lwjgl"), -1); -} - -void OneSixProfileStrategy::loadUserPatches() -{ - // first, collect all patches (that are not builtins of OneSix) and load them - QMap loadedPatches; - QDir patchesDir(FS::PathCombine(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); - // ignore builtins - if (file->uid == "net.minecraft") - continue; - if (file->uid == "org.lwjgl") - continue; - auto patch = std::make_shared(file, info.filePath()); - patch->setRemovable(true); - patch->setMovable(true); - if(ENV.metadataIndex()->hasUid(file->uid)) - { - // FIXME: requesting a uid/list creates it in the index... this allows reverting to possibly invalid versions... - patch->setRevertible(true); - } - loadedPatches[file->uid] = patch; - } - // these are 'special'... if not already loaded from instance files, grab them from the metadata repo. - auto loadSpecial = [&](const QString & uid, int order) - { - auto patchVersion = m_instance->getComponentVersion(uid); - if(!patchVersion.isEmpty() && !loadedPatches.contains(uid)) - { - auto patch = std::make_shared(ENV.metadataIndex()->get(uid, patchVersion)); - patch->setOrder(order); - patch->setVanilla(true); - patch->setRemovable(true); - patch->setMovable(true); - loadedPatches[uid] = patch; - } - }; - loadSpecial("net.minecraftforge", 5); - loadSpecial("com.mumfrey.liteloader", 10); - - // now add all the patches by user sort order - ProfileUtils::PatchOrder userOrder; - ProfileUtils::readOverrideOrders(FS::PathCombine(m_instance->instanceRoot(), "order.json"), userOrder); - for (auto uid : userOrder) - { - // ignore builtins - if (uid == "net.minecraft") - continue; - if (uid == "org.lwjgl") - continue; - // ordering has a patch that is gone? - if(!loadedPatches.contains(uid)) - { - continue; - } - profile->appendPatch(loadedPatches.take(uid)); - } - - // is there anything left to sort? - if(loadedPatches.isEmpty()) - { - // TODO: save the order here? - return; - } - - // inserting into multimap by order number as key sorts the patches and detects duplicates - QMultiMap files; - auto iter = loadedPatches.begin(); - while(iter != loadedPatches.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 - profile->appendPatch(value); - } - } - // TODO: save the order here? -} - - -void OneSixProfileStrategy::load() -{ - profile->clearPatches(); - - upgradeDeprecatedFiles(); - loadDefaultBuiltinPatches(); - loadUserPatches(); -} - -bool OneSixProfileStrategy::saveOrder(ProfileUtils::PatchOrder order) -{ - return ProfileUtils::writeOverrideOrders(FS::PathCombine(m_instance->instanceRoot(), "order.json"), order); -} - -bool OneSixProfileStrategy::resetOrder() -{ - return QDir(m_instance->instanceRoot()).remove("order.json"); -} - -bool OneSixProfileStrategy::removePatch(ProfilePatchPtr patch) -{ - bool ok = true; - // first, remove the patch file. this ensures it's not used anymore - auto fileName = patch->getFilename(); - if(fileName.size()) - { - QFile patchFile(fileName); - if(patchFile.exists() && !patchFile.remove()) - { - qCritical() << "File" << fileName << "could not be removed because:" << patchFile.errorString(); - return false; - } - } - if(!m_instance->getComponentVersion(patch->getID()).isEmpty()) - { - m_instance->setComponentVersion(patch->getID(), QString()); - } - - // FIXME: we need a generic way of removing local resources, not just jar mods... - auto preRemoveJarMod = [&](LibraryPtr jarMod) -> bool - { - if (!jarMod->isLocal()) - { - return true; - } - QStringList jar, temp1, temp2, temp3; - jarMod->getApplicableFiles(currentSystem, jar, temp1, temp2, temp3, m_instance->jarmodsPath().absolutePath()); - QFileInfo finfo (jar[0]); - if(finfo.exists()) - { - QFile jarModFile(jar[0]); - if(!jarModFile.remove()) - { - qCritical() << "File" << jar[0] << "could not be removed because:" << jarModFile.errorString(); - return false; - } - return true; - } - return true; - }; - - auto &jarMods = patch->getVersionFile()->jarMods; - for(auto &jarmod: jarMods) - { - ok &= preRemoveJarMod(jarmod); - } - return ok; -} - -bool OneSixProfileStrategy::customizePatch(ProfilePatchPtr patch) -{ - if(patch->isCustom()) - { - return false; - } - - auto filename = FS::PathCombine(m_instance->instanceRoot(), "patches" , patch->getID() + ".json"); - if(!FS::ensureFilePathExists(filename)) - { - return false; - } - // FIXME: get rid of this try-catch. - try - { - QSaveFile jsonFile(filename); - if(!jsonFile.open(QIODevice::WriteOnly)) - { - return false; - } - auto vfile = patch->getVersionFile(); - if(!vfile) - { - return false; - } - auto document = OneSixVersionFormat::versionFileToJson(vfile, true); - jsonFile.write(document.toJson()); - if(!jsonFile.commit()) - { - return false; - } - load(); - } - catch (Exception &error) - { - qWarning() << "Version could not be loaded:" << error.cause(); - } - return true; -} - -bool OneSixProfileStrategy::revertPatch(ProfilePatchPtr patch) -{ - if(!patch->isCustom()) - { - // already not custom - return true; - } - auto filename = patch->getFilename(); - if(!QFile::exists(filename)) - { - // already gone / not custom - return true; - } - // just kill the file and reload - bool result = QFile::remove(filename); - // FIXME: get rid of this try-catch. - try - { - load(); - } - catch (Exception &error) - { - qWarning() << "Version could not be loaded:" << error.cause(); - } - return result; -} - -bool OneSixProfileStrategy::installJarMods(QStringList filepaths) -{ - QString patchDir = FS::PathCombine(m_instance->instanceRoot(), "patches"); - if(!FS::ensureFolderPathExists(patchDir)) - { - return false; - } - - if (!FS::ensureFolderPathExists(m_instance->jarModsDir())) - { - return false; - } - - for(auto filepath:filepaths) - { - QFileInfo sourceInfo(filepath); - auto uuid = QUuid::createUuid(); - QString id = uuid.toString().remove('{').remove('}'); - QString target_filename = id + ".jar"; - QString target_id = "org.multimc.jarmod." + id; - QString target_name = sourceInfo.completeBaseName() + " (jar mod)"; - QString finalPath = FS::PathCombine(m_instance->jarModsDir(), target_filename); - - QFileInfo targetInfo(finalPath); - if(targetInfo.exists()) - { - return false; - } - - if (!QFile::copy(sourceInfo.absoluteFilePath(),QFileInfo(finalPath).absoluteFilePath())) - { - return false; - } - - auto f = std::make_shared(); - auto jarMod = std::make_shared(); - jarMod->setRawName(GradleSpecifier("org.multimc.jarmods:" + id + ":1")); - jarMod->setFilename(target_filename); - jarMod->setDisplayName(sourceInfo.completeBaseName()); - jarMod->setHint("local"); - f->jarMods.append(jarMod); - f->name = target_name; - f->uid = target_id; - f->order = profile->getFreeOrderNumber(); - QString patchFileName = FS::PathCombine(patchDir, target_id + ".json"); - - QFile file(patchFileName); - if (!file.open(QFile::WriteOnly)) - { - qCritical() << "Error opening" << file.fileName() - << "for reading:" << file.errorString(); - return false; - } - file.write(OneSixVersionFormat::versionFileToJson(f, true).toJson()); - file.close(); - - auto patch = std::make_shared(f, patchFileName); - patch->setMovable(true); - patch->setRemovable(true); - profile->appendPatch(patch); - } - profile->saveCurrentOrder(); - profile->reapplyPatches(); - return true; -} - -bool OneSixProfileStrategy::installCustomJar(QString filepath) -{ - QString patchDir = FS::PathCombine(m_instance->instanceRoot(), "patches"); - if(!FS::ensureFolderPathExists(patchDir)) - { - return false; - } - - QString libDir = m_instance->customLibrariesDir(); - if (!FS::ensureFolderPathExists(libDir)) - { - return false; - } - - auto specifier = GradleSpecifier("org.multimc:customjar:1"); - QFileInfo sourceInfo(filepath); - QString target_filename = specifier.getFileName(); - QString target_id = specifier.artifactId(); - QString target_name = sourceInfo.completeBaseName() + " (custom jar)"; - QString finalPath = FS::PathCombine(libDir, target_filename); - - QFileInfo jarInfo(finalPath); - if (jarInfo.exists()) - { - if(!QFile::remove(finalPath)) - { - return false; - } - } - if (!QFile::copy(filepath, finalPath)) - { - return false; - } - - auto f = std::make_shared(); - auto jarMod = std::make_shared(); - jarMod->setRawName(specifier); - jarMod->setDisplayName(sourceInfo.completeBaseName()); - jarMod->setHint("local"); - f->mainJar = jarMod; - f->name = target_name; - f->uid = target_id; - f->order = profile->getFreeOrderNumber(); - QString patchFileName = FS::PathCombine(patchDir, target_id + ".json"); - - QFile file(patchFileName); - if (!file.open(QFile::WriteOnly)) - { - qCritical() << "Error opening" << file.fileName() - << "for reading:" << file.errorString(); - return false; - } - file.write(OneSixVersionFormat::versionFileToJson(f, true).toJson()); - file.close(); - - auto patch = std::make_shared(f, patchFileName); - patch->setMovable(true); - patch->setRemovable(true); - profile->appendPatch(patch); - - profile->saveCurrentOrder(); - profile->reapplyPatches(); - return true; -} diff --git a/api/logic/minecraft/onesix/OneSixProfileStrategy.h b/api/logic/minecraft/onesix/OneSixProfileStrategy.h deleted file mode 100644 index e4eee4b2..00000000 --- a/api/logic/minecraft/onesix/OneSixProfileStrategy.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include "minecraft/ProfileStrategy.h" - -class OneSixInstance; - -class OneSixProfileStrategy : public ProfileStrategy -{ -public: - OneSixProfileStrategy(OneSixInstance * instance); - virtual ~OneSixProfileStrategy() {}; - void load() override; - bool resetOrder() override; - bool saveOrder(ProfileUtils::PatchOrder order) override; - bool installJarMods(QStringList filepaths) override; - bool installCustomJar(QString filepath) override; - bool removePatch(ProfilePatchPtr patch) override; - bool customizePatch(ProfilePatchPtr patch) override; - bool revertPatch(ProfilePatchPtr patch) override; - -protected: - virtual void loadDefaultBuiltinPatches(); - virtual void loadUserPatches(); - void upgradeDeprecatedFiles(); - -protected: - OneSixInstance *m_instance; -}; diff --git a/api/logic/minecraft/onesix/OneSixUpdate.cpp b/api/logic/minecraft/onesix/OneSixUpdate.cpp deleted file mode 100644 index 230877ef..00000000 --- a/api/logic/minecraft/onesix/OneSixUpdate.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/* Copyright 2013-2017 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 "Env.h" -#include -#include "OneSixUpdate.h" -#include "OneSixInstance.h" - -#include -#include -#include -#include - -#include "BaseInstance.h" -#include "minecraft/MinecraftProfile.h" -#include "minecraft/Library.h" -#include "net/URLConstants.h" -#include - -#include "update/FoldersTask.h" -#include "update/LibrariesTask.h" -#include "update/FMLLibrariesTask.h" -#include "update/AssetUpdateTask.h" - -#include -#include - -OneSixUpdate::OneSixUpdate(OneSixInstance *inst, QObject *parent) : Task(parent), m_inst(inst) -{ - // create folders - { - m_tasks.append(std::make_shared(m_inst)); - } - - // add metadata update tasks, if necessary - { - /* - * FIXME: there are some corner cases here that remain unhandled: - * what if local load succeeds but remote fails? The version is still usable... - * We should not rely on the remote to be there... and prefer local files if it does not respond. - */ - qDebug() << "Updating patches..."; - auto profile = m_inst->getMinecraftProfile(); - m_inst->reloadProfile(); - for(int i = 0; i < profile->rowCount(); i++) - { - auto patch = profile->versionPatch(i); - auto id = patch->getID(); - auto metadata = patch->getMeta(); - if(metadata) - { - metadata->load(); - auto task = metadata->getCurrentTask(); - if(task) - { - qDebug() << "Loading remote meta patch" << id; - m_tasks.append(task.unwrap()); - } - } - else - { - qDebug() << "Ignoring local patch" << id; - } - } - } - - // libraries download - { - m_tasks.append(std::make_shared(m_inst)); - } - - // FML libraries download and copy into the instance - { - m_tasks.append(std::make_shared(m_inst)); - } - - // assets update - { - m_tasks.append(std::make_shared(m_inst)); - } -} - -void OneSixUpdate::executeTask() -{ - if(!m_preFailure.isEmpty()) - { - emitFailed(m_preFailure); - return; - } - next(); -} - -void OneSixUpdate::next() -{ - if(m_abort) - { - emitFailed(tr("Aborted by user.")); - return; - } - if(m_failed_out_of_order) - { - emitFailed(m_fail_reason); - return; - } - m_currentTask ++; - if(m_currentTask > 0) - { - auto task = m_tasks[m_currentTask - 1]; - disconnect(task.get(), &Task::succeeded, this, &OneSixUpdate::subtaskSucceeded); - disconnect(task.get(), &Task::failed, this, &OneSixUpdate::subtaskFailed); - disconnect(task.get(), &Task::progress, this, &OneSixUpdate::progress); - disconnect(task.get(), &Task::status, this, &OneSixUpdate::setStatus); - } - if(m_currentTask == m_tasks.size()) - { - emitSucceeded(); - return; - } - auto task = m_tasks[m_currentTask]; - // if the task is already finished by the time we look at it, skip it - if(task->isFinished()) - { - qCritical() << "OneSixUpdate: Skipping finished subtask" << m_currentTask << ":" << task.get(); - next(); - } - connect(task.get(), &Task::succeeded, this, &OneSixUpdate::subtaskSucceeded); - connect(task.get(), &Task::failed, this, &OneSixUpdate::subtaskFailed); - connect(task.get(), &Task::progress, this, &OneSixUpdate::progress); - connect(task.get(), &Task::status, this, &OneSixUpdate::setStatus); - // if the task is already running, do not start it again - if(!task->isRunning()) - { - task->start(); - } -} - -void OneSixUpdate::subtaskSucceeded() -{ - if(isFinished()) - { - qCritical() << "OneSixUpdate: Subtask" << sender() << "succeeded, but work was already done!"; - return; - } - auto senderTask = QObject::sender(); - auto currentTask = m_tasks[m_currentTask].get(); - if(senderTask != currentTask) - { - qDebug() << "OneSixUpdate: Subtask" << sender() << "succeeded out of order."; - return; - } - next(); -} - -void OneSixUpdate::subtaskFailed(QString error) -{ - if(isFinished()) - { - qCritical() << "OneSixUpdate: Subtask" << sender() << "failed, but work was already done!"; - return; - } - auto senderTask = QObject::sender(); - auto currentTask = m_tasks[m_currentTask].get(); - if(senderTask != currentTask) - { - qDebug() << "OneSixUpdate: Subtask" << sender() << "failed out of order."; - m_failed_out_of_order = true; - m_fail_reason = error; - return; - } - emitFailed(error); -} - - -bool OneSixUpdate::abort() -{ - if(!m_abort) - { - m_abort = true; - auto task = m_tasks[m_currentTask]; - if(task->canAbort()) - { - return task->abort(); - } - } - return true; -} - -bool OneSixUpdate::canAbort() const -{ - return true; -} diff --git a/api/logic/minecraft/onesix/OneSixUpdate.h b/api/logic/minecraft/onesix/OneSixUpdate.h deleted file mode 100644 index f879f19e..00000000 --- a/api/logic/minecraft/onesix/OneSixUpdate.h +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright 2013-2017 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 -#include -#include - -#include "net/NetJob.h" -#include "tasks/Task.h" -#include "minecraft/VersionFilterData.h" -#include - -class MinecraftVersion; -class OneSixInstance; - -class OneSixUpdate : public Task -{ - Q_OBJECT -public: - explicit OneSixUpdate(OneSixInstance *inst, QObject *parent = 0); - void executeTask() override; - bool canAbort() const override; - -private -slots: - bool abort() override; - void subtaskSucceeded(); - void subtaskFailed(QString error); - -private: - void next(); - -private: - OneSixInstance *m_inst = nullptr; - QList> m_tasks; - QString m_preFailure; - int m_currentTask = -1; - bool m_abort = false; - bool m_failed_out_of_order = false; - QString m_fail_reason; -}; diff --git a/api/logic/minecraft/onesix/OneSixVersionFormat.cpp b/api/logic/minecraft/onesix/OneSixVersionFormat.cpp deleted file mode 100644 index 7ebf514f..00000000 --- a/api/logic/minecraft/onesix/OneSixVersionFormat.cpp +++ /dev/null @@ -1,332 +0,0 @@ -#include "OneSixVersionFormat.h" -#include -#include "minecraft/ParseUtils.h" -#include - -using namespace Json; - -static void readString(const QJsonObject &root, const QString &key, QString &variable) -{ - if (root.contains(key)) - { - variable = requireString(root.value(key)); - } -} - -LibraryPtr OneSixVersionFormat::libraryFromJson(const QJsonObject &libObj, const QString &filename) -{ - LibraryPtr out = MojangVersionFormat::libraryFromJson(libObj, filename); - readString(libObj, "MMC-hint", out->m_hint); - readString(libObj, "MMC-absulute_url", out->m_absoluteURL); - readString(libObj, "MMC-absoluteUrl", out->m_absoluteURL); - readString(libObj, "MMC-filename", out->m_filename); - readString(libObj, "MMC-displayname", out->m_displayname); - return out; -} - -QJsonObject OneSixVersionFormat::libraryToJson(Library *library) -{ - QJsonObject libRoot = MojangVersionFormat::libraryToJson(library); - if (library->m_absoluteURL.size()) - libRoot.insert("MMC-absoluteUrl", library->m_absoluteURL); - if (library->m_hint.size()) - libRoot.insert("MMC-hint", library->m_hint); - if (library->m_filename.size()) - libRoot.insert("MMC-filename", library->m_filename); - if (library->m_displayname.size()) - libRoot.insert("MMC-displayname", library->m_displayname); - return libRoot; -} - -VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc, const QString &filename, const bool requireOrder) -{ - VersionFilePtr out(new VersionFile()); - if (doc.isEmpty() || doc.isNull()) - { - throw JSONValidationError(filename + " is empty or null"); - } - if (!doc.isObject()) - { - throw JSONValidationError(filename + " is not an object"); - } - - QJsonObject root = doc.object(); - - if (requireOrder) - { - if (root.contains("order")) - { - out->order = requireInteger(root.value("order")); - } - else - { - // FIXME: evaluate if we don't want to throw exceptions here instead - qCritical() << filename << "doesn't contain an order field"; - } - } - - out->name = root.value("name").toString(); - - if(root.contains("uid")) - { - out->uid = root.value("uid").toString(); - } - else - { - out->uid = root.value("fileId").toString(); - } - - out->version = root.value("version").toString(); - out->dependsOnMinecraftVersion = root.value("mcVersion").toString(); - // out->filename = filename; - - MojangVersionFormat::readVersionProperties(root, out.get()); - - // added for legacy Minecraft window embedding, TODO: remove - readString(root, "appletClass", out->appletClass); - - if (root.contains("+tweakers")) - { - for (auto tweakerVal : requireArray(root.value("+tweakers"))) - { - out->addTweakers.append(requireString(tweakerVal)); - } - } - - if (root.contains("+traits")) - { - for (auto tweakerVal : requireArray(root.value("+traits"))) - { - out->traits.insert(requireString(tweakerVal)); - } - } - - - if (root.contains("jarMods")) - { - for (auto libVal : requireArray(root.value("jarMods"))) - { - QJsonObject libObj = requireObject(libVal); - // parse the jarmod - auto lib = OneSixVersionFormat::jarModFromJson(libObj, filename); - // and add to jar mods - out->jarMods.append(lib); - } - } - else if (root.contains("+jarMods")) // DEPRECATED: old style '+jarMods' are only here for backwards compatibility - { - for (auto libVal : requireArray(root.value("+jarMods"))) - { - QJsonObject libObj = requireObject(libVal); - // parse the jarmod - auto lib = OneSixVersionFormat::plusJarModFromJson(libObj, filename, out->name); - // and add to jar mods - out->jarMods.append(lib); - } - } - - if (root.contains("mods")) - { - for (auto libVal : requireArray(root.value("mods"))) - { - QJsonObject libObj = requireObject(libVal); - // parse the jarmod - auto lib = OneSixVersionFormat::modFromJson(libObj, filename); - // and add to jar mods - out->mods.append(lib); - } - } - - auto readLibs = [&](const char * which) - { - for (auto libVal : requireArray(root.value(which))) - { - QJsonObject libObj = requireObject(libVal); - // parse the library - auto lib = libraryFromJson(libObj, filename); - out->libraries.append(lib); - } - }; - bool hasPlusLibs = root.contains("+libraries"); - bool hasLibs = root.contains("libraries"); - if (hasPlusLibs && hasLibs) - { - out->addProblem(ProblemSeverity::Warning, - QObject::tr("Version file has both '+libraries' and 'libraries'. This is no longer supported.")); - readLibs("libraries"); - readLibs("+libraries"); - } - else if (hasLibs) - { - readLibs("libraries"); - } - else if(hasPlusLibs) - { - readLibs("+libraries"); - } - - // if we have mainJar, just use it - if(root.contains("mainJar")) - { - QJsonObject libObj = requireObject(root, "mainJar"); - out->mainJar = libraryFromJson(libObj, filename); - } - // else reconstruct it from downloads and id ... if that's available - else if(!out->minecraftVersion.isEmpty()) - { - auto lib = std::make_shared(); - lib->setRawName(GradleSpecifier(QString("com.mojang:minecraft:%1:client").arg(out->minecraftVersion))); - // we have a reliable client download, use it. - if(out->mojangDownloads.contains("client")) - { - auto LibDLInfo = std::make_shared(); - LibDLInfo->artifact = out->mojangDownloads["client"]; - lib->setMojangDownloadInfo(LibDLInfo); - } - // we got nothing... guess based on ancient hardcoded Mojang behaviour - // FIXME: this will eventually break... - else - { - lib->setAbsoluteUrl(URLConstants::getLegacyJarUrl(out->minecraftVersion)); - } - out->mainJar = lib; - } - - /* removed features that shouldn't be used */ - if (root.contains("tweakers")) - { - out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element 'tweakers'")); - } - if (root.contains("-libraries")) - { - out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-libraries'")); - } - if (root.contains("-tweakers")) - { - out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-tweakers'")); - } - if (root.contains("-minecraftArguments")) - { - out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-minecraftArguments'")); - } - if (root.contains("+minecraftArguments")) - { - out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '+minecraftArguments'")); - } - return out; -} - -QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch, bool saveOrder) -{ - QJsonObject root; - if (saveOrder) - { - root.insert("order", patch->order); - } - writeString(root, "name", patch->name); - - writeString(root, "uid", patch->uid); - - writeString(root, "version", patch->version); - writeString(root, "mcVersion", patch->dependsOnMinecraftVersion); - - MojangVersionFormat::writeVersionProperties(patch.get(), root); - - if(patch->mainJar) - { - root.insert("mainJar", libraryToJson(patch->mainJar.get())); - } - writeString(root, "appletClass", patch->appletClass); - writeStringList(root, "+tweakers", patch->addTweakers); - writeStringList(root, "+traits", patch->traits.toList()); - if (!patch->libraries.isEmpty()) - { - QJsonArray array; - for (auto value: patch->libraries) - { - array.append(OneSixVersionFormat::libraryToJson(value.get())); - } - root.insert("libraries", array); - } - if (!patch->jarMods.isEmpty()) - { - QJsonArray array; - for (auto value: patch->jarMods) - { - array.append(OneSixVersionFormat::jarModtoJson(value.get())); - } - root.insert("jarMods", array); - } - if (!patch->mods.isEmpty()) - { - QJsonArray array; - for (auto value: patch->jarMods) - { - array.append(OneSixVersionFormat::modtoJson(value.get())); - } - root.insert("mods", array); - } - // write the contents to a json document. - { - QJsonDocument out; - out.setObject(root); - return out; - } -} - -LibraryPtr OneSixVersionFormat::plusJarModFromJson(const QJsonObject &libObj, const QString &filename, const QString &originalName) -{ - LibraryPtr out(new Library()); - if (!libObj.contains("name")) - { - throw JSONValidationError(filename + - "contains a jarmod that doesn't have a 'name' field"); - } - - // just make up something unique on the spot for the library name. - auto uuid = QUuid::createUuid(); - QString id = uuid.toString().remove('{').remove('}'); - out->setRawName(GradleSpecifier("org.multimc.jarmods:" + id + ":1")); - - // filename override is the old name - out->setFilename(libObj.value("name").toString()); - - // it needs to be local, it is stored in the instance jarmods folder - out->setHint("local"); - - // read the original name if present - some versions did not set it - // it is the original jar mod filename before it got renamed at the point of addition - auto displayName = libObj.value("originalName").toString(); - if(displayName.isEmpty()) - { - auto fixed = originalName; - fixed.remove(" (jar mod)"); - out->setDisplayName(fixed); - } - else - { - out->setDisplayName(displayName); - } - return out; -} - -LibraryPtr OneSixVersionFormat::jarModFromJson(const QJsonObject& libObj, const QString& filename) -{ - return libraryFromJson(libObj, filename); -} - - -QJsonObject OneSixVersionFormat::jarModtoJson(Library *jarmod) -{ - return libraryToJson(jarmod); -} - -LibraryPtr OneSixVersionFormat::modFromJson(const QJsonObject& libObj, const QString& filename) -{ - return libraryFromJson(libObj, filename); -} - -QJsonObject OneSixVersionFormat::modtoJson(Library *jarmod) -{ - return libraryToJson(jarmod); -} diff --git a/api/logic/minecraft/onesix/OneSixVersionFormat.h b/api/logic/minecraft/onesix/OneSixVersionFormat.h deleted file mode 100644 index 64f18da8..00000000 --- a/api/logic/minecraft/onesix/OneSixVersionFormat.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -class OneSixVersionFormat -{ -public: - // version files / profile patches - static VersionFilePtr versionFileFromJson(const QJsonDocument &doc, const QString &filename, const bool requireOrder); - static QJsonDocument versionFileToJson(const VersionFilePtr &patch, bool saveOrder); - - // libraries - static LibraryPtr libraryFromJson(const QJsonObject &libObj, const QString &filename); - static QJsonObject libraryToJson(Library *library); - - // DEPRECATED: old 'plus' jar mods generated by the application - static LibraryPtr plusJarModFromJson(const QJsonObject &libObj, const QString &filename, const QString &originalName); - - // new jar mods derived from libraries - static LibraryPtr jarModFromJson(const QJsonObject &libObj, const QString &filename); - static QJsonObject jarModtoJson(Library * jarmod); - - // mods, also derived from libraries - static LibraryPtr modFromJson(const QJsonObject &libObj, const QString &filename); - static QJsonObject modtoJson(Library * jarmod); -}; diff --git a/api/logic/minecraft/onesix/update/AssetUpdateTask.cpp b/api/logic/minecraft/onesix/update/AssetUpdateTask.cpp deleted file mode 100644 index 21600ff0..00000000 --- a/api/logic/minecraft/onesix/update/AssetUpdateTask.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include "Env.h" -#include "AssetUpdateTask.h" -#include "minecraft/onesix/OneSixInstance.h" -#include "net/ChecksumValidator.h" -#include "minecraft/AssetsUtils.h" - -AssetUpdateTask::AssetUpdateTask(OneSixInstance * inst) -{ - m_inst = inst; -} -void AssetUpdateTask::executeTask() -{ - setStatus(tr("Updating assets index...")); - auto profile = m_inst->getMinecraftProfile(); - auto assets = profile->getMinecraftAssets(); - QUrl indexUrl = assets->url; - QString localPath = assets->id + ".json"; - auto job = new NetJob(tr("Asset index for %1").arg(m_inst->name())); - - auto metacache = ENV.metacache(); - auto entry = metacache->resolveEntry("asset_indexes", localPath); - entry->setStale(true); - auto hexSha1 = assets->sha1.toLatin1(); - qDebug() << "Asset index SHA1:" << hexSha1; - auto dl = Net::Download::makeCached(indexUrl, entry); - auto rawSha1 = QByteArray::fromHex(assets->sha1.toLatin1()); - dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); - job->addNetAction(dl); - - downloadJob.reset(job); - - connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::assetIndexFinished); - connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetIndexFailed); - connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); - - qDebug() << m_inst->name() << ": Starting asset index download"; - downloadJob->start(); -} - -bool AssetUpdateTask::canAbort() const -{ - return true; -} - -void AssetUpdateTask::assetIndexFinished() -{ - AssetsIndex index; - qDebug() << m_inst->name() << ": Finished asset index download"; - - auto profile = m_inst->getMinecraftProfile(); - auto assets = profile->getMinecraftAssets(); - - QString asset_fname = "assets/indexes/" + assets->id + ".json"; - // FIXME: this looks like a job for a generic validator based on json schema? - if (!AssetsUtils::loadAssetsIndexJson(assets->id, asset_fname, &index)) - { - auto metacache = ENV.metacache(); - auto entry = metacache->resolveEntry("asset_indexes", assets->id + ".json"); - metacache->evictEntry(entry); - emitFailed(tr("Failed to read the assets index!")); - } - - auto job = index.getDownloadJob(); - if(job) - { - setStatus(tr("Getting the assets files from Mojang...")); - downloadJob = job; - connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::emitSucceeded); - connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed); - connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); - downloadJob->start(); - return; - } - emitSucceeded(); -} - -void AssetUpdateTask::assetIndexFailed(QString reason) -{ - qDebug() << m_inst->name() << ": Failed asset index download"; - emitFailed(tr("Failed to download the assets index:\n%1").arg(reason)); -} - -void AssetUpdateTask::assetsFailed(QString reason) -{ - emitFailed(tr("Failed to download assets:\n%1").arg(reason)); -} - -bool AssetUpdateTask::abort() -{ - if(downloadJob) - { - return downloadJob->abort(); - } - else - { - qWarning() << "Prematurely aborted FMLLibrariesTask"; - } - return true; -} diff --git a/api/logic/minecraft/onesix/update/AssetUpdateTask.h b/api/logic/minecraft/onesix/update/AssetUpdateTask.h deleted file mode 100644 index 3b500189..00000000 --- a/api/logic/minecraft/onesix/update/AssetUpdateTask.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include "tasks/Task.h" -#include "net/NetJob.h" -class OneSixInstance; - -class AssetUpdateTask : public Task -{ - Q_OBJECT -public: - AssetUpdateTask(OneSixInstance * inst); - void executeTask() override; - - bool canAbort() const override; - -private slots: - void assetIndexFinished(); - void assetIndexFailed(QString reason); - void assetsFailed(QString reason); - -public slots: - bool abort() override; - -private: - OneSixInstance *m_inst; - NetJobPtr downloadJob; -}; diff --git a/api/logic/minecraft/onesix/update/FMLLibrariesTask.cpp b/api/logic/minecraft/onesix/update/FMLLibrariesTask.cpp deleted file mode 100644 index 13310e92..00000000 --- a/api/logic/minecraft/onesix/update/FMLLibrariesTask.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include "Env.h" -#include -#include -#include "FMLLibrariesTask.h" -#include "minecraft/onesix/OneSixInstance.h" - - -FMLLibrariesTask::FMLLibrariesTask(OneSixInstance * inst) -{ - m_inst = inst; -} -void FMLLibrariesTask::executeTask() -{ - // Get the mod list - OneSixInstance *inst = (OneSixInstance *)m_inst; - std::shared_ptr profile = inst->getMinecraftProfile(); - bool forge_present = false; - - if (!profile->hasTrait("legacyFML")) - { - emitSucceeded(); - return; - } - - QString version = inst->intendedVersionId(); - auto &fmlLibsMapping = g_VersionFilterData.fmlLibsMapping; - if (!fmlLibsMapping.contains(version)) - { - emitSucceeded(); - return; - } - - auto &libList = fmlLibsMapping[version]; - - // determine if we need some libs for FML or forge - setStatus(tr("Checking for FML libraries...")); - forge_present = (profile->versionPatch("net.minecraftforge") != nullptr); - // we don't... - if (!forge_present) - { - emitSucceeded(); - return; - } - - // now check the lib folder inside the instance for files. - for (auto &lib : libList) - { - QFileInfo libInfo(FS::PathCombine(inst->FMLlibDir(), lib.filename)); - if (libInfo.exists()) - continue; - fmlLibsToProcess.append(lib); - } - - // if everything is in place, there's nothing to do here... - if (fmlLibsToProcess.isEmpty()) - { - emitSucceeded(); - return; - } - - // download missing libs to our place - setStatus(tr("Dowloading FML libraries...")); - auto dljob = new NetJob("FML libraries"); - auto metacache = ENV.metacache(); - for (auto &lib : fmlLibsToProcess) - { - auto entry = metacache->resolveEntry("fmllibs", lib.filename); - QString urlString = lib.ours ? URLConstants::FMLLIBS_OUR_BASE_URL + lib.filename - : URLConstants::FMLLIBS_FORGE_BASE_URL + lib.filename; - dljob->addNetAction(Net::Download::makeCached(QUrl(urlString), entry)); - } - - connect(dljob, &NetJob::succeeded, this, &FMLLibrariesTask::fmllibsFinished); - connect(dljob, &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed); - connect(dljob, &NetJob::progress, this, &FMLLibrariesTask::progress); - downloadJob.reset(dljob); - downloadJob->start(); -} - -bool FMLLibrariesTask::canAbort() const -{ - return true; -} - -void FMLLibrariesTask::fmllibsFinished() -{ - downloadJob.reset(); - if (!fmlLibsToProcess.isEmpty()) - { - setStatus(tr("Copying FML libraries into the instance...")); - OneSixInstance *inst = (OneSixInstance *)m_inst; - auto metacache = ENV.metacache(); - int index = 0; - for (auto &lib : fmlLibsToProcess) - { - progress(index, fmlLibsToProcess.size()); - auto entry = metacache->resolveEntry("fmllibs", lib.filename); - auto path = FS::PathCombine(inst->FMLlibDir(), lib.filename); - if (!FS::ensureFilePathExists(path)) - { - emitFailed(tr("Failed creating FML library folder inside the instance.")); - return; - } - if (!QFile::copy(entry->getFullPath(), FS::PathCombine(inst->FMLlibDir(), lib.filename))) - { - emitFailed(tr("Failed copying Forge/FML library: %1.").arg(lib.filename)); - return; - } - index++; - } - progress(index, fmlLibsToProcess.size()); - } - emitSucceeded(); -} -void FMLLibrariesTask::fmllibsFailed(QString reason) -{ - QStringList failed = downloadJob->getFailedFiles(); - QString failed_all = failed.join("\n"); - emitFailed(tr("Failed to download the following files:\n%1\n\nReason:%2\nPlease try again.").arg(failed_all, reason)); -} - -bool FMLLibrariesTask::abort() -{ - if(downloadJob) - { - return downloadJob->abort(); - } - else - { - qWarning() << "Prematurely aborted FMLLibrariesTask"; - } - return true; -} diff --git a/api/logic/minecraft/onesix/update/FMLLibrariesTask.h b/api/logic/minecraft/onesix/update/FMLLibrariesTask.h deleted file mode 100644 index 616053d7..00000000 --- a/api/logic/minecraft/onesix/update/FMLLibrariesTask.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include "tasks/Task.h" -#include "net/NetJob.h" -class OneSixInstance; - -class FMLLibrariesTask : public Task -{ - Q_OBJECT -public: - FMLLibrariesTask(OneSixInstance * inst); - - void executeTask() override; - - bool canAbort() const override; - -private slots: - void fmllibsFinished(); - void fmllibsFailed(QString reason); - -public slots: - bool abort() override; - -private: - OneSixInstance *m_inst; - NetJobPtr downloadJob; - QList fmlLibsToProcess; -}; - diff --git a/api/logic/minecraft/onesix/update/FoldersTask.cpp b/api/logic/minecraft/onesix/update/FoldersTask.cpp deleted file mode 100644 index d478560c..00000000 --- a/api/logic/minecraft/onesix/update/FoldersTask.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "FoldersTask.h" -#include "minecraft/onesix/OneSixInstance.h" -#include - -FoldersTask::FoldersTask(OneSixInstance * inst) - :Task() -{ - m_inst = inst; -} - -void FoldersTask::executeTask() -{ - // Make directories - QDir mcDir(m_inst->minecraftRoot()); - if (!mcDir.exists() && !mcDir.mkpath(".")) - { - emitFailed(tr("Failed to create folder for minecraft binaries.")); - return; - } - emitSucceeded(); -} diff --git a/api/logic/minecraft/onesix/update/FoldersTask.h b/api/logic/minecraft/onesix/update/FoldersTask.h deleted file mode 100644 index 7cdc5a93..00000000 --- a/api/logic/minecraft/onesix/update/FoldersTask.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "tasks/Task.h" - -class OneSixInstance; -class FoldersTask : public Task -{ - Q_OBJECT -public: - FoldersTask(OneSixInstance * inst); - void executeTask() override; -private: - OneSixInstance *m_inst; -}; - diff --git a/api/logic/minecraft/onesix/update/LibrariesTask.cpp b/api/logic/minecraft/onesix/update/LibrariesTask.cpp deleted file mode 100644 index 2cd41ded..00000000 --- a/api/logic/minecraft/onesix/update/LibrariesTask.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "Env.h" -#include "LibrariesTask.h" -#include "minecraft/onesix/OneSixInstance.h" - -LibrariesTask::LibrariesTask(OneSixInstance * inst) -{ - m_inst = inst; -} - -void LibrariesTask::executeTask() -{ - setStatus(tr("Getting the library files from Mojang...")); - qDebug() << m_inst->name() << ": downloading libraries"; - OneSixInstance *inst = (OneSixInstance *)m_inst; - inst->reloadProfile(); - if(inst->hasVersionBroken()) - { - emitFailed(tr("Failed to load the version description files - check the instance for errors.")); - return; - } - - // Build a list of URLs that will need to be downloaded. - std::shared_ptr profile = inst->getMinecraftProfile(); - - auto job = new NetJob(tr("Libraries for instance %1").arg(inst->name())); - downloadJob.reset(job); - - auto metacache = ENV.metacache(); - QList brokenLocalLibs; - QStringList failedFiles; - auto createJob = [&](const LibraryPtr & lib) - { - if(!lib) - { - emitFailed(tr("Null jar is specified in the metadata, aborting.")); - return; - } - auto dls = lib->getDownloads(currentSystem, metacache.get(), failedFiles, inst->getLocalLibraryPath()); - for(auto dl : dls) - { - downloadJob->addNetAction(dl); - } - }; - auto createJobs = [&](const QList & libs) - { - for (auto lib : libs) - { - createJob(lib); - } - }; - createJobs(profile->getLibraries()); - createJobs(profile->getNativeLibraries()); - createJobs(profile->getJarMods()); - createJob(profile->getMainJar()); - - // FIXME: this is never filled!!!! - if (!brokenLocalLibs.empty()) - { - downloadJob.reset(); - QString failed_all = failedFiles.join("\n"); - emitFailed(tr("Some libraries marked as 'local' are missing their jar " - "files:\n%1\n\nYou'll have to correct this problem manually. If this is " - "an externally tracked instance, make sure to run it at least once " - "outside of MultiMC.").arg(failed_all)); - return; - } - connect(downloadJob.get(), &NetJob::succeeded, this, &LibrariesTask::emitSucceeded); - connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed); - connect(downloadJob.get(), &NetJob::progress, this, &LibrariesTask::progress); - downloadJob->start(); -} - -bool LibrariesTask::canAbort() const -{ - return true; -} - -void LibrariesTask::jarlibFailed(QString reason) -{ - emitFailed(tr("Game update failed: it was impossible to fetch the required libraries.\nReason:\n%1").arg(reason)); -} - -bool LibrariesTask::abort() -{ - if(downloadJob) - { - return downloadJob->abort(); - } - else - { - qWarning() << "Prematurely aborted LibrariesTask"; - } - return true; -} diff --git a/api/logic/minecraft/onesix/update/LibrariesTask.h b/api/logic/minecraft/onesix/update/LibrariesTask.h deleted file mode 100644 index a84975e5..00000000 --- a/api/logic/minecraft/onesix/update/LibrariesTask.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "tasks/Task.h" -#include "net/NetJob.h" -class OneSixInstance; - -class LibrariesTask : public Task -{ - Q_OBJECT -public: - LibrariesTask(OneSixInstance * inst); - - void executeTask() override; - - bool canAbort() const override; - -private slots: - void jarlibFailed(QString reason); - -public slots: - bool abort() override; - -private: - OneSixInstance *m_inst; - NetJobPtr downloadJob; -}; diff --git a/api/logic/minecraft/update/AssetUpdateTask.cpp b/api/logic/minecraft/update/AssetUpdateTask.cpp new file mode 100644 index 00000000..b67e630e --- /dev/null +++ b/api/logic/minecraft/update/AssetUpdateTask.cpp @@ -0,0 +1,100 @@ +#include "Env.h" +#include "AssetUpdateTask.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/MinecraftProfile.h" +#include "net/ChecksumValidator.h" +#include "minecraft/AssetsUtils.h" + +AssetUpdateTask::AssetUpdateTask(MinecraftInstance * inst) +{ + m_inst = inst; +} +void AssetUpdateTask::executeTask() +{ + setStatus(tr("Updating assets index...")); + auto profile = m_inst->getMinecraftProfile(); + auto assets = profile->getMinecraftAssets(); + QUrl indexUrl = assets->url; + QString localPath = assets->id + ".json"; + auto job = new NetJob(tr("Asset index for %1").arg(m_inst->name())); + + auto metacache = ENV.metacache(); + auto entry = metacache->resolveEntry("asset_indexes", localPath); + entry->setStale(true); + auto hexSha1 = assets->sha1.toLatin1(); + qDebug() << "Asset index SHA1:" << hexSha1; + auto dl = Net::Download::makeCached(indexUrl, entry); + auto rawSha1 = QByteArray::fromHex(assets->sha1.toLatin1()); + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); + job->addNetAction(dl); + + downloadJob.reset(job); + + connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::assetIndexFinished); + connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetIndexFailed); + connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); + + qDebug() << m_inst->name() << ": Starting asset index download"; + downloadJob->start(); +} + +bool AssetUpdateTask::canAbort() const +{ + return true; +} + +void AssetUpdateTask::assetIndexFinished() +{ + AssetsIndex index; + qDebug() << m_inst->name() << ": Finished asset index download"; + + auto profile = m_inst->getMinecraftProfile(); + auto assets = profile->getMinecraftAssets(); + + QString asset_fname = "assets/indexes/" + assets->id + ".json"; + // FIXME: this looks like a job for a generic validator based on json schema? + if (!AssetsUtils::loadAssetsIndexJson(assets->id, asset_fname, &index)) + { + auto metacache = ENV.metacache(); + auto entry = metacache->resolveEntry("asset_indexes", assets->id + ".json"); + metacache->evictEntry(entry); + emitFailed(tr("Failed to read the assets index!")); + } + + auto job = index.getDownloadJob(); + if(job) + { + setStatus(tr("Getting the assets files from Mojang...")); + downloadJob = job; + connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::emitSucceeded); + connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed); + connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); + downloadJob->start(); + return; + } + emitSucceeded(); +} + +void AssetUpdateTask::assetIndexFailed(QString reason) +{ + qDebug() << m_inst->name() << ": Failed asset index download"; + emitFailed(tr("Failed to download the assets index:\n%1").arg(reason)); +} + +void AssetUpdateTask::assetsFailed(QString reason) +{ + emitFailed(tr("Failed to download assets:\n%1").arg(reason)); +} + +bool AssetUpdateTask::abort() +{ + if(downloadJob) + { + return downloadJob->abort(); + } + else + { + qWarning() << "Prematurely aborted FMLLibrariesTask"; + } + return true; +} diff --git a/api/logic/minecraft/update/AssetUpdateTask.h b/api/logic/minecraft/update/AssetUpdateTask.h new file mode 100644 index 00000000..c666faa6 --- /dev/null +++ b/api/logic/minecraft/update/AssetUpdateTask.h @@ -0,0 +1,26 @@ +#pragma once +#include "tasks/Task.h" +#include "net/NetJob.h" +class MinecraftInstance; + +class AssetUpdateTask : public Task +{ + Q_OBJECT +public: + AssetUpdateTask(MinecraftInstance * inst); + void executeTask() override; + + bool canAbort() const override; + +private slots: + void assetIndexFinished(); + void assetIndexFailed(QString reason); + void assetsFailed(QString reason); + +public slots: + bool abort() override; + +private: + MinecraftInstance *m_inst; + NetJobPtr downloadJob; +}; diff --git a/api/logic/minecraft/update/FMLLibrariesTask.cpp b/api/logic/minecraft/update/FMLLibrariesTask.cpp new file mode 100644 index 00000000..63428a48 --- /dev/null +++ b/api/logic/minecraft/update/FMLLibrariesTask.cpp @@ -0,0 +1,133 @@ +#include "Env.h" +#include +#include +#include "FMLLibrariesTask.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/MinecraftProfile.h" + +FMLLibrariesTask::FMLLibrariesTask(MinecraftInstance * inst) +{ + m_inst = inst; +} +void FMLLibrariesTask::executeTask() +{ + // Get the mod list + MinecraftInstance *inst = (MinecraftInstance *)m_inst; + std::shared_ptr profile = inst->getMinecraftProfile(); + bool forge_present = false; + + if (!profile->hasTrait("legacyFML")) + { + emitSucceeded(); + return; + } + + QString version = inst->getComponentVersion("net.minecraft"); + auto &fmlLibsMapping = g_VersionFilterData.fmlLibsMapping; + if (!fmlLibsMapping.contains(version)) + { + emitSucceeded(); + return; + } + + auto &libList = fmlLibsMapping[version]; + + // determine if we need some libs for FML or forge + setStatus(tr("Checking for FML libraries...")); + forge_present = (profile->versionPatch("net.minecraftforge") != nullptr); + // we don't... + if (!forge_present) + { + emitSucceeded(); + return; + } + + // now check the lib folder inside the instance for files. + for (auto &lib : libList) + { + QFileInfo libInfo(FS::PathCombine(inst->libDir(), lib.filename)); + if (libInfo.exists()) + continue; + fmlLibsToProcess.append(lib); + } + + // if everything is in place, there's nothing to do here... + if (fmlLibsToProcess.isEmpty()) + { + emitSucceeded(); + return; + } + + // download missing libs to our place + setStatus(tr("Dowloading FML libraries...")); + auto dljob = new NetJob("FML libraries"); + auto metacache = ENV.metacache(); + for (auto &lib : fmlLibsToProcess) + { + auto entry = metacache->resolveEntry("fmllibs", lib.filename); + QString urlString = lib.ours ? URLConstants::FMLLIBS_OUR_BASE_URL + lib.filename + : URLConstants::FMLLIBS_FORGE_BASE_URL + lib.filename; + dljob->addNetAction(Net::Download::makeCached(QUrl(urlString), entry)); + } + + connect(dljob, &NetJob::succeeded, this, &FMLLibrariesTask::fmllibsFinished); + connect(dljob, &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed); + connect(dljob, &NetJob::progress, this, &FMLLibrariesTask::progress); + downloadJob.reset(dljob); + downloadJob->start(); +} + +bool FMLLibrariesTask::canAbort() const +{ + return true; +} + +void FMLLibrariesTask::fmllibsFinished() +{ + downloadJob.reset(); + if (!fmlLibsToProcess.isEmpty()) + { + setStatus(tr("Copying FML libraries into the instance...")); + MinecraftInstance *inst = (MinecraftInstance *)m_inst; + auto metacache = ENV.metacache(); + int index = 0; + for (auto &lib : fmlLibsToProcess) + { + progress(index, fmlLibsToProcess.size()); + auto entry = metacache->resolveEntry("fmllibs", lib.filename); + auto path = FS::PathCombine(inst->libDir(), lib.filename); + if (!FS::ensureFilePathExists(path)) + { + emitFailed(tr("Failed creating FML library folder inside the instance.")); + return; + } + if (!QFile::copy(entry->getFullPath(), FS::PathCombine(inst->libDir(), lib.filename))) + { + emitFailed(tr("Failed copying Forge/FML library: %1.").arg(lib.filename)); + return; + } + index++; + } + progress(index, fmlLibsToProcess.size()); + } + emitSucceeded(); +} +void FMLLibrariesTask::fmllibsFailed(QString reason) +{ + QStringList failed = downloadJob->getFailedFiles(); + QString failed_all = failed.join("\n"); + emitFailed(tr("Failed to download the following files:\n%1\n\nReason:%2\nPlease try again.").arg(failed_all, reason)); +} + +bool FMLLibrariesTask::abort() +{ + if(downloadJob) + { + return downloadJob->abort(); + } + else + { + qWarning() << "Prematurely aborted FMLLibrariesTask"; + } + return true; +} diff --git a/api/logic/minecraft/update/FMLLibrariesTask.h b/api/logic/minecraft/update/FMLLibrariesTask.h new file mode 100644 index 00000000..10f48f5b --- /dev/null +++ b/api/logic/minecraft/update/FMLLibrariesTask.h @@ -0,0 +1,28 @@ +#pragma once +#include "tasks/Task.h" +#include "net/NetJob.h" +class MinecraftInstance; + +class FMLLibrariesTask : public Task +{ + Q_OBJECT +public: + FMLLibrariesTask(MinecraftInstance * inst); + + void executeTask() override; + + bool canAbort() const override; + +private slots: + void fmllibsFinished(); + void fmllibsFailed(QString reason); + +public slots: + bool abort() override; + +private: + MinecraftInstance *m_inst; + NetJobPtr downloadJob; + QList fmlLibsToProcess; +}; + diff --git a/api/logic/minecraft/update/FoldersTask.cpp b/api/logic/minecraft/update/FoldersTask.cpp new file mode 100644 index 00000000..34e2292a --- /dev/null +++ b/api/logic/minecraft/update/FoldersTask.cpp @@ -0,0 +1,21 @@ +#include "FoldersTask.h" +#include "minecraft/MinecraftInstance.h" +#include + +FoldersTask::FoldersTask(MinecraftInstance * inst) + :Task() +{ + m_inst = inst; +} + +void FoldersTask::executeTask() +{ + // Make directories + QDir mcDir(m_inst->minecraftRoot()); + if (!mcDir.exists() && !mcDir.mkpath(".")) + { + emitFailed(tr("Failed to create folder for minecraft binaries.")); + return; + } + emitSucceeded(); +} diff --git a/api/logic/minecraft/update/FoldersTask.h b/api/logic/minecraft/update/FoldersTask.h new file mode 100644 index 00000000..6e669b1e --- /dev/null +++ b/api/logic/minecraft/update/FoldersTask.h @@ -0,0 +1,15 @@ +#pragma once + +#include "tasks/Task.h" + +class MinecraftInstance; +class FoldersTask : public Task +{ + Q_OBJECT +public: + FoldersTask(MinecraftInstance * inst); + void executeTask() override; +private: + MinecraftInstance *m_inst; +}; + diff --git a/api/logic/minecraft/update/LibrariesTask.cpp b/api/logic/minecraft/update/LibrariesTask.cpp new file mode 100644 index 00000000..c408f432 --- /dev/null +++ b/api/logic/minecraft/update/LibrariesTask.cpp @@ -0,0 +1,95 @@ +#include "Env.h" +#include "LibrariesTask.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/MinecraftProfile.h" + +LibrariesTask::LibrariesTask(MinecraftInstance * inst) +{ + m_inst = inst; +} + +void LibrariesTask::executeTask() +{ + setStatus(tr("Getting the library files from Mojang...")); + qDebug() << m_inst->name() << ": downloading libraries"; + MinecraftInstance *inst = (MinecraftInstance *)m_inst; + inst->reloadProfile(); + if(inst->hasVersionBroken()) + { + emitFailed(tr("Failed to load the version description files - check the instance for errors.")); + return; + } + + // Build a list of URLs that will need to be downloaded. + std::shared_ptr profile = inst->getMinecraftProfile(); + + auto job = new NetJob(tr("Libraries for instance %1").arg(inst->name())); + downloadJob.reset(job); + + auto metacache = ENV.metacache(); + QList brokenLocalLibs; + QStringList failedFiles; + auto createJob = [&](const LibraryPtr & lib) + { + if(!lib) + { + emitFailed(tr("Null jar is specified in the metadata, aborting.")); + return; + } + auto dls = lib->getDownloads(currentSystem, metacache.get(), failedFiles, inst->getLocalLibraryPath()); + for(auto dl : dls) + { + downloadJob->addNetAction(dl); + } + }; + auto createJobs = [&](const QList & libs) + { + for (auto lib : libs) + { + createJob(lib); + } + }; + createJobs(profile->getLibraries()); + createJobs(profile->getNativeLibraries()); + createJobs(profile->getJarMods()); + createJob(profile->getMainJar()); + + // FIXME: this is never filled!!!! + if (!brokenLocalLibs.empty()) + { + downloadJob.reset(); + QString failed_all = failedFiles.join("\n"); + emitFailed(tr("Some libraries marked as 'local' are missing their jar " + "files:\n%1\n\nYou'll have to correct this problem manually. If this is " + "an externally tracked instance, make sure to run it at least once " + "outside of MultiMC.").arg(failed_all)); + return; + } + connect(downloadJob.get(), &NetJob::succeeded, this, &LibrariesTask::emitSucceeded); + connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed); + connect(downloadJob.get(), &NetJob::progress, this, &LibrariesTask::progress); + downloadJob->start(); +} + +bool LibrariesTask::canAbort() const +{ + return true; +} + +void LibrariesTask::jarlibFailed(QString reason) +{ + emitFailed(tr("Game update failed: it was impossible to fetch the required libraries.\nReason:\n%1").arg(reason)); +} + +bool LibrariesTask::abort() +{ + if(downloadJob) + { + return downloadJob->abort(); + } + else + { + qWarning() << "Prematurely aborted LibrariesTask"; + } + return true; +} diff --git a/api/logic/minecraft/update/LibrariesTask.h b/api/logic/minecraft/update/LibrariesTask.h new file mode 100644 index 00000000..d06a5037 --- /dev/null +++ b/api/logic/minecraft/update/LibrariesTask.h @@ -0,0 +1,25 @@ +#pragma once +#include "tasks/Task.h" +#include "net/NetJob.h" +class MinecraftInstance; + +class LibrariesTask : public Task +{ + Q_OBJECT +public: + LibrariesTask(MinecraftInstance * inst); + + void executeTask() override; + + bool canAbort() const override; + +private slots: + void jarlibFailed(QString reason); + +public slots: + bool abort() override; + +private: + MinecraftInstance *m_inst; + NetJobPtr downloadJob; +}; -- cgit v1.2.3