diff options
Diffstat (limited to 'api/logic/minecraft/legacy')
-rw-r--r-- | api/logic/minecraft/legacy/LegacyInstance.cpp | 359 | ||||
-rw-r--r-- | api/logic/minecraft/legacy/LegacyInstance.h | 115 | ||||
-rw-r--r-- | api/logic/minecraft/legacy/LegacyModList.cpp | 533 | ||||
-rw-r--r-- | api/logic/minecraft/legacy/LegacyModList.h | 112 | ||||
-rw-r--r-- | api/logic/minecraft/legacy/LegacyUpdate.cpp | 399 | ||||
-rw-r--r-- | api/logic/minecraft/legacy/LegacyUpdate.h | 70 | ||||
-rw-r--r-- | api/logic/minecraft/legacy/LegacyUpgradeTask.cpp | 142 | ||||
-rw-r--r-- | api/logic/minecraft/legacy/LegacyUpgradeTask.h | 38 | ||||
-rw-r--r-- | api/logic/minecraft/legacy/LwjglVersionList.cpp | 169 | ||||
-rw-r--r-- | api/logic/minecraft/legacy/LwjglVersionList.h | 116 |
10 files changed, 360 insertions, 1693 deletions
diff --git a/api/logic/minecraft/legacy/LegacyInstance.cpp b/api/logic/minecraft/legacy/LegacyInstance.cpp index 0987d56f..6e318458 100644 --- a/api/logic/minecraft/legacy/LegacyInstance.cpp +++ b/api/logic/minecraft/legacy/LegacyInstance.cpp @@ -1,4 +1,4 @@ -/* Copyright 2013-2017 MultiMC Contributors +/* Copyright 2013-2018 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,6 @@ #include "LegacyInstance.h" -#include "minecraft/legacy/LegacyUpdate.h" #include "minecraft/legacy/LegacyModList.h" #include "minecraft/ModList.h" #include "minecraft/WorldList.h" @@ -28,14 +27,12 @@ #include <FileSystem.h> LegacyInstance::LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) - : MinecraftInstance(globalSettings, settings, rootDir) + : BaseInstance(globalSettings, settings, rootDir) { - m_lwjglFolderSetting = globalSettings->getSetting("LWJGLDir"); settings->registerSetting("NeedsRebuild", true); settings->registerSetting("ShouldUpdate", false); - settings->registerSetting("JarVersion", "Unknown"); - settings->registerSetting("LwjglVersion", "2.9.0"); - settings->registerSetting("IntendedJarVersion", ""); + settings->registerSetting("JarVersion", QString()); + settings->registerSetting("IntendedJarVersion", QString()); /* * custom base jar has no default. it is determined in code... see the accessor methods for *it @@ -68,178 +65,102 @@ QString LegacyInstance::customBaseJar() const return value; } -void LegacyInstance::setCustomBaseJar(QString val) -{ - if (val.isNull() || val.isEmpty() || val == defaultCustomBaseJar()) - m_settings->reset("CustomBaseJar"); - else - m_settings->set("CustomBaseJar", val); -} - -void LegacyInstance::setShouldUseCustomBaseJar(bool val) -{ - m_settings->set("UseCustomBaseJar", val); -} - bool LegacyInstance::shouldUseCustomBaseJar() const { return m_settings->get("UseCustomBaseJar").toBool(); } -shared_qobject_ptr<Task> LegacyInstance::createUpdateTask() +shared_qobject_ptr<Task> LegacyInstance::createUpdateTask(Net::Mode) { - // make sure the jar mods list is initialized by asking for it. - auto list = jarModList(); - // create an update task - return shared_qobject_ptr<Task>(new LegacyUpdate(this, this)); + return nullptr; } -std::shared_ptr<Task> LegacyInstance::createJarModdingTask() +/* +class LegacyJarModTask : public Task { - class JarModTask : public Task + //Q_OBJECT +public: + explicit LegacyJarModTask(std::shared_ptr<LegacyInstance> inst) : Task(nullptr), m_inst(inst) + { + } + virtual void executeTask() { - public: - explicit JarModTask(std::shared_ptr<LegacyInstance> inst) : Task(nullptr), m_inst(inst) + if (!m_inst->shouldRebuild()) { + emitSucceeded(); + return; } - virtual void executeTask() - { - if (!m_inst->shouldRebuild()) - { - emitSucceeded(); - return; - } - // Get the mod list - auto modList = m_inst->getJarMods(); + // Get the mod list + auto modList = m_inst->getJarMods(); - QFileInfo runnableJar(m_inst->runnableJar()); - QFileInfo baseJar(m_inst->baseJar()); - bool base_is_custom = m_inst->shouldUseCustomBaseJar(); + QFileInfo runnableJar(m_inst->runnableJar()); + QFileInfo baseJar(m_inst->baseJar()); + bool base_is_custom = m_inst->shouldUseCustomBaseJar(); - // Nothing to do if there are no jar mods to install, no backup and just the mc jar - if (base_is_custom) - { - // yes, this can happen if the instance only has the runnable jar and not the base jar - // it *could* be assumed that such an instance is vanilla, but that wouldn't be safe - // because that's not something mmc4 guarantees - if (runnableJar.isFile() && !baseJar.exists() && modList.empty()) - { - m_inst->setShouldRebuild(false); - emitSucceeded(); - return; - } - - setStatus(tr("Installing mods: Backing up minecraft.jar ...")); - if (!baseJar.exists() && !QFile::copy(runnableJar.filePath(), baseJar.filePath())) - { - emitFailed("It seems both the active and base jar are gone. A fresh base jar will " - "be used on next run."); - m_inst->setShouldRebuild(true); - m_inst->setShouldUpdate(true); - m_inst->setShouldUseCustomBaseJar(false); - return; - } - } - - if (!baseJar.exists()) + // Nothing to do if there are no jar mods to install, no backup and just the mc jar + if (base_is_custom) + { + // yes, this can happen if the instance only has the runnable jar and not the base jar + // it *could* be assumed that such an instance is vanilla, but that wouldn't be safe + // because that's not something mmc4 guarantees + if (runnableJar.isFile() && !baseJar.exists() && modList.empty()) { - emitFailed("The base jar " + baseJar.filePath() + " does not exist"); + m_inst->setShouldRebuild(false); + emitSucceeded(); return; } - if (runnableJar.exists() && !QFile::remove(runnableJar.filePath())) + setStatus(tr("Installing mods: Backing up minecraft.jar ...")); + if (!baseJar.exists() && !QFile::copy(runnableJar.filePath(), baseJar.filePath())) { - emitFailed("Failed to delete old minecraft.jar"); + emitFailed("It seems both the active and base jar are gone. A fresh base jar will " + "be used on next run."); + m_inst->setShouldRebuild(true); + m_inst->setShouldUpdate(true); + m_inst->setShouldUseCustomBaseJar(false); return; } + } - setStatus(tr("Installing mods: Opening minecraft.jar ...")); - - QString outputJarPath = runnableJar.filePath(); - QString inputJarPath = baseJar.filePath(); - - if(!MMCZip::createModdedJar(inputJarPath, outputJarPath, modList)) - { - emitFailed(tr("Failed to create the custom Minecraft jar file.")); - return; - } - m_inst->setShouldRebuild(false); - // inst->UpdateVersion(true); - emitSucceeded(); + if (!baseJar.exists()) + { + emitFailed("The base jar " + baseJar.filePath() + " does not exist"); return; - } - std::shared_ptr<LegacyInstance> m_inst; - }; - return std::make_shared<JarModTask>(std::dynamic_pointer_cast<LegacyInstance>(shared_from_this())); -} -QString LegacyInstance::createLaunchScript(AuthSessionPtr session) -{ - QString launchScript; - - // window size - QString windowParams; - if (settings()->get("LaunchMaximized").toBool()) - { - windowParams = "max"; - } - else - { - windowParams = QString("%1x%2").arg(settings()->get("MinecraftWinWidth").toInt()).arg(settings()->get("MinecraftWinHeight").toInt()); - } - - QString lwjgl = QDir(m_lwjglFolderSetting->get().toString() + "/" + lwjglVersion()).absolutePath(); - launchScript += "userName " + session->player_name + "\n"; - launchScript += "sessionId " + session->session + "\n"; - launchScript += "windowTitle " + windowTitle() + "\n"; - launchScript += "windowParams " + windowParams + "\n"; - launchScript += "cp bin/minecraft.jar\n"; - launchScript += "cp " + lwjgl + "/lwjgl.jar\n"; - launchScript += "cp " + lwjgl + "/lwjgl_util.jar\n"; - launchScript += "cp " + lwjgl + "/jinput.jar\n"; - launchScript += "natives " + lwjgl + "/natives\n"; - launchScript += "traits legacyLaunch\n"; - launchScript += "launcher onesix\n"; - return launchScript; -} + if (runnableJar.exists() && !QFile::remove(runnableJar.filePath())) + { + emitFailed("Failed to delete old minecraft.jar"); + return; + } -std::shared_ptr<LaunchStep> LegacyInstance::createMainLaunchStep(LaunchTask * parent, AuthSessionPtr session) -{ - auto step = std::make_shared<LauncherPartLaunch>(parent); - step->setWorkingDirectory(minecraftRoot()); - step->setAuthSession(session); - return step; -} + setStatus(tr("Installing mods: Opening minecraft.jar ...")); -QString LegacyInstance::launchMethod() -{ - return "Legacy"; -} + QString outputJarPath = runnableJar.filePath(); + QString inputJarPath = baseJar.filePath(); -QStringList LegacyInstance::validLaunchMethods() -{ - return {"Legacy"}; -} + if(!MMCZip::createModdedJar(inputJarPath, outputJarPath, modList)) + { + emitFailed(tr("Failed to create the custom Minecraft jar file.")); + return; + } + m_inst->setShouldRebuild(false); + // inst->UpdateVersion(true); + emitSucceeded(); + return; -std::shared_ptr<ModList> LegacyInstance::coreModList() const -{ - if (!core_mod_list) - { - core_mod_list.reset(new ModList(coreModsDir())); } - core_mod_list->update(); - return core_mod_list; -} + std::shared_ptr<LegacyInstance> m_inst; +}; +*/ std::shared_ptr<LegacyModList> LegacyInstance::jarModList() const { if (!jar_mod_list) { auto list = new LegacyModList(jarModsDir(), modListFile()); - connect(list, SIGNAL(changed()), SLOT(jarModsChanged())); jar_mod_list.reset(list); } jar_mod_list->update(); @@ -251,39 +172,20 @@ QList<Mod> LegacyInstance::getJarMods() const return jarModList()->allMods(); } -void LegacyInstance::jarModsChanged() +QString LegacyInstance::minecraftRoot() const { - qDebug() << "Jar mods of instance " << name() << " have changed. Jar will be rebuilt."; - setShouldRebuild(true); -} + QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft")); + QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft")); -std::shared_ptr<ModList> LegacyInstance::loaderModList() const -{ - if (!loader_mod_list) - { - loader_mod_list.reset(new ModList(loaderModsDir())); - } - loader_mod_list->update(); - return loader_mod_list; + if (mcDir.exists() && !dotMCDir.exists()) + return mcDir.filePath(); + else + return dotMCDir.filePath(); } -std::shared_ptr<ModList> LegacyInstance::texturePackList() const +QString LegacyInstance::binRoot() const { - if (!texture_pack_list) - { - texture_pack_list.reset(new ModList(texturePacksDir())); - } - texture_pack_list->update(); - return texture_pack_list; -} - -std::shared_ptr<WorldList> LegacyInstance::worldList() const -{ - if (!m_world_list) - { - m_world_list.reset(new WorldList(savesDir())); - } - return m_world_list; + return FS::PathCombine(minecraftRoot(), "bin"); } QString LegacyInstance::jarModsDir() const @@ -340,38 +242,16 @@ bool LegacyInstance::shouldRebuild() const return m_settings->get("NeedsRebuild").toBool(); } -void LegacyInstance::setShouldRebuild(bool val) -{ - m_settings->set("NeedsRebuild", val); -} - QString LegacyInstance::currentVersionId() const { return m_settings->get("JarVersion").toString(); } -QString LegacyInstance::lwjglVersion() const -{ - return m_settings->get("LwjglVersion").toString(); -} - -void LegacyInstance::setLWJGLVersion(QString val) -{ - m_settings->set("LwjglVersion", val); -} - QString LegacyInstance::intendedVersionId() const { return m_settings->get("IntendedJarVersion").toString(); } -bool LegacyInstance::setIntendedVersionId(QString version) -{ - settings()->set("IntendedJarVersion", version); - setShouldUpdate(true); - return true; -} - bool LegacyInstance::shouldUpdate() const { QVariant var = settings()->get("ShouldUpdate"); @@ -382,11 +262,6 @@ bool LegacyInstance::shouldUpdate() const return true; } -void LegacyInstance::setShouldUpdate(bool val) -{ - settings()->set("ShouldUpdate", val); -} - QString LegacyInstance::defaultBaseJar() const { return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar"; @@ -397,9 +272,13 @@ QString LegacyInstance::defaultCustomBaseJar() const return FS::PathCombine(binRoot(), "mcbackup.jar"); } -QString LegacyInstance::lwjglFolder() const +std::shared_ptr<WorldList> LegacyInstance::worldList() const { - return m_lwjglFolderSetting->get().toString(); + if (!m_world_list) + { + m_world_list.reset(new WorldList(savesDir())); + } + return m_world_list; } QString LegacyInstance::typeName() const @@ -407,6 +286,11 @@ QString LegacyInstance::typeName() const return tr("Legacy"); } +QString LegacyInstance::getStatusbarDescription() +{ + return tr("Instance from previous versions."); +} + QStringList LegacyInstance::verboseDescription(AuthSessionPtr session) { QStringList out; @@ -422,48 +306,6 @@ QStringList LegacyInstance::verboseDescription(AuthSessionPtr session) 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 << ""; - } - - if(jarModList()->size()) - { - out << "Jar Mods:"; - for(auto & jarmod: jarModList()->allMods()) - { - out << " " + jarmod.name() + " (" + jarmod.filename().filePath() + ")"; - } - out << ""; - } - QString windowParams; if (settings()->get("LaunchMaximized").toBool()) { @@ -478,40 +320,3 @@ QStringList LegacyInstance::verboseDescription(AuthSessionPtr session) out << ""; return out; } - -QStringList LegacyInstance::getClassPath() const -{ - QString launchScript; - QString lwjgl = getNativePath(); - QStringList out = - { - "bin/minecraft.jar", - lwjgl + "/lwjgl.jar", - lwjgl + "/lwjgl_util.jar", - lwjgl + "/jinput.jar" - }; - return out; -} - -QString LegacyInstance::getMainClass() const -{ - return "net.minecraft.client.Minecraft"; -} - -QString LegacyInstance::getNativePath() const -{ - return QDir(m_lwjglFolderSetting->get().toString() + "/" + lwjglVersion()).absolutePath(); -} - -QStringList LegacyInstance::getNativeJars() const -{ - return {}; -} - -QStringList LegacyInstance::processMinecraftArgs(AuthSessionPtr account) const -{ - QStringList out; - out.append(account->player_name); - out.append(account->session); - return out; -} diff --git a/api/logic/minecraft/legacy/LegacyInstance.h b/api/logic/minecraft/legacy/LegacyInstance.h index 15d1383f..4a8bc436 100644 --- a/api/logic/minecraft/legacy/LegacyInstance.h +++ b/api/logic/minecraft/legacy/LegacyInstance.h @@ -1,4 +1,4 @@ -/* Copyright 2013-2017 MultiMC Contributors +/* Copyright 2013-2018 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,22 +15,27 @@ #pragma once -#include "minecraft/MinecraftInstance.h" +#include "BaseInstance.h" +#include "minecraft/Mod.h" #include "multimc_logic_export.h" class ModList; class LegacyModList; +class WorldList; class Task; - -class MULTIMC_LOGIC_EXPORT LegacyInstance : public MinecraftInstance +/* + * WHY: Legacy instances - from MultiMC 3 and 4 - are here only to provide a way to upgrade them to the current format. + */ +class MULTIMC_LOGIC_EXPORT LegacyInstance : public BaseInstance { Q_OBJECT public: explicit LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir); - virtual void init() override {}; + virtual void init() override {} + virtual void saveNow() override {} /// Path to the instance's minecraft.jar QString runnableJar() const; @@ -38,20 +43,6 @@ public: //! Path to the instance's modlist file. QString modListFile() const; - /* - ////// Edit Instance Dialog stuff ////// - virtual QList<BasePage *> getPages(); - virtual QString dialogTitle(); - */ - - ////// Mod Lists ////// - std::shared_ptr<LegacyModList> jarModList() const ; - virtual QList< Mod > getJarMods() const override; - std::shared_ptr<ModList> coreModList() const; - std::shared_ptr<ModList> loaderModList() const; - std::shared_ptr<ModList> texturePackList() const override; - std::shared_ptr<WorldList> worldList() const override; - ////// Directories ////// QString libDir() const; QString savesDir() const; @@ -61,8 +52,10 @@ public: QString coreModsDir() const; QString resourceDir() const; virtual 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. - /// Get the curent base jar of this instance. By default, it's the + /// Get the curent base jar of this instance. By default, it's the /// versions/$version/$version.jar QString baseJar() const; @@ -75,13 +68,15 @@ public: * Whether or not custom base jar is used */ bool shouldUseCustomBaseJar() const; - void setShouldUseCustomBaseJar(bool val); /*! * The value of the custom base jar */ QString customBaseJar() const; - void setCustomBaseJar(QString val); + + std::shared_ptr<LegacyModList> jarModList() const; + QList<Mod> getJarMods() const; + std::shared_ptr<WorldList> worldList() const; /*! * Whether or not the instance's minecraft.jar needs to be rebuilt. @@ -89,67 +84,57 @@ public: * re-added to a fresh minecraft.jar file. */ bool shouldRebuild() const; - void setShouldRebuild(bool val); - - virtual QString currentVersionId() const override; - - //! The version of LWJGL that this instance uses. - QString lwjglVersion() const; - //! Where the lwjgl versions foor this instance can be found... HACK HACK HACK - QString lwjglFolder() const; + QString currentVersionId() const; + QString intendedVersionId() const; - /// st the version of LWJGL libs this instance will use - void setLWJGLVersion(QString val); - - virtual QString intendedVersionId() const override; - virtual bool setIntendedVersionId(QString version) override; - - virtual QSet<QString> traits() override + QSet<QString> traits() const override { return {"legacy-instance", "texturepacks"}; }; - virtual bool shouldUpdate() const override; - virtual void setShouldUpdate(bool val) override; - virtual shared_qobject_ptr<Task> createUpdateTask() override; - virtual std::shared_ptr<Task> createJarModdingTask() override; - virtual QString createLaunchScript(AuthSessionPtr session) override; + virtual bool shouldUpdate() const; + virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override; virtual QString typeName() const override; - bool canExport() const override + bool canLaunch() const override + { + return false; + } + bool canEdit() const override { return true; } - - QStringList getClassPath() const override; - QString getMainClass() const override; - - QStringList getNativeJars() const override; - QString getNativePath() const override; - - QString getLocalLibraryPath() const override + bool canExport() const override + { + return false; + } + std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override + { + return nullptr; + } + IPathMatcher::Ptr getLogFileMatcher() override + { + return nullptr; + } + QString getLogFileRoot() override { - return QString(); + return minecraftRoot(); } - QStringList processMinecraftArgs(AuthSessionPtr account) const override; + QString getStatusbarDescription() override; QStringList verboseDescription(AuthSessionPtr session) override; -protected: - std::shared_ptr<LaunchStep> createMainLaunchStep(LaunchTask *parent, AuthSessionPtr session) override; - QStringList validLaunchMethods() override; - QString launchMethod() override; - + QProcessEnvironment createEnvironment() override + { + return QProcessEnvironment(); + } + QMap<QString, QString> getVariables() const override + { + return {}; + } protected: mutable std::shared_ptr<LegacyModList> jar_mod_list; - mutable std::shared_ptr<ModList> core_mod_list; - mutable std::shared_ptr<ModList> loader_mod_list; - mutable std::shared_ptr<ModList> texture_pack_list; mutable std::shared_ptr<WorldList> m_world_list; - std::shared_ptr<Setting> m_lwjglFolderSetting; -protected -slots: - virtual void jarModsChanged(); }; diff --git a/api/logic/minecraft/legacy/LegacyModList.cpp b/api/logic/minecraft/legacy/LegacyModList.cpp index 052f75fd..638b2e21 100644 --- a/api/logic/minecraft/legacy/LegacyModList.cpp +++ b/api/logic/minecraft/legacy/LegacyModList.cpp @@ -1,4 +1,4 @@ -/* Copyright 2013-2017 MultiMC Contributors +/* Copyright 2013-2018 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,55 +15,26 @@ #include "LegacyModList.h" #include <FileSystem.h> -#include <QMimeData> -#include <QUrl> -#include <QUuid> #include <QString> -#include <QFileSystemWatcher> #include <QDebug> LegacyModList::LegacyModList(const QString &dir, const QString &list_file) - : QAbstractListModel(), m_dir(dir), m_list_file(list_file) + : m_dir(dir), m_list_file(list_file) { FS::ensureFolderPathExists(m_dir.absolutePath()); m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs | QDir::NoSymLinks); m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); - m_list_id = QUuid::createUuid().toString(); - m_watcher = new QFileSystemWatcher(this); - is_watching = false; - connect(m_watcher, SIGNAL(directoryChanged(QString)), this, - SLOT(directoryChanged(QString))); } -void LegacyModList::startWatching() -{ - update(); - is_watching = m_watcher->addPath(m_dir.absolutePath()); - if (is_watching) - { - qDebug() << "Started watching " << m_dir.absolutePath(); - } - else - { - qDebug() << "Failed to start watching " << m_dir.absolutePath(); - } -} - -void LegacyModList::stopWatching() -{ - is_watching = !m_watcher->removePath(m_dir.absolutePath()); - if (!is_watching) + struct OrderItem { - qDebug() << "Stopped watching " << m_dir.absolutePath(); - } - else - { - qDebug() << "Failed to stop watching " << m_dir.absolutePath(); - } -} + QString id; + bool enabled = false; + }; + typedef QList<OrderItem> OrderList; -void LegacyModList::internalSort(QList<Mod> &what) +static void internalSort(QList<Mod> &what) { auto predicate = [](const Mod &left, const Mod &right) { @@ -76,9 +47,43 @@ void LegacyModList::internalSort(QList<Mod> &what) std::sort(what.begin(), what.end(), predicate); } +static OrderList readListFile(const QString &m_list_file) +{ + OrderList itemList; + if (m_list_file.isNull() || m_list_file.isEmpty()) + return itemList; + + QFile textFile(m_list_file); + if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text)) + return OrderList(); + + QTextStream textStream; + textStream.setAutoDetectUnicode(true); + textStream.setDevice(&textFile); + while (true) + { + QString line = textStream.readLine(); + if (line.isNull() || line.isEmpty()) + break; + else + { + OrderItem it; + it.enabled = !line.endsWith(".disabled"); + if (!it.enabled) + { + line.chop(9); + } + it.id = line; + itemList.append(it); + } + } + textFile.close(); + return itemList; +} + bool LegacyModList::update() { - if (!isValid()) + if (!m_dir.exists() || !m_dir.isReadable()) return false; QList<Mod> orderedMods; @@ -88,7 +93,7 @@ bool LegacyModList::update() bool orderOrStateChanged = false; // first, process the ordered items (if any) - OrderList listOrder = readListFile(); + OrderList listOrder = readListFile(m_list_file); for (auto item : listOrder) { QFileInfo infoEnabled(m_dir.filePath(item.id)); @@ -157,460 +162,10 @@ bool LegacyModList::update() } } } - beginResetModel(); mods.swap(orderedMods); - endResetModel(); if (orderOrStateChanged && !m_list_file.isEmpty()) { qDebug() << "Mod list " << m_list_file << " changed!"; - saveListFile(); - emit changed(); - } - return true; -} - -void LegacyModList::directoryChanged(QString path) -{ - update(); -} - -LegacyModList::OrderList LegacyModList::readListFile() -{ - OrderList itemList; - if (m_list_file.isNull() || m_list_file.isEmpty()) - return itemList; - - QFile textFile(m_list_file); - if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text)) - return OrderList(); - - QTextStream textStream; - textStream.setAutoDetectUnicode(true); - textStream.setDevice(&textFile); - while (true) - { - QString line = textStream.readLine(); - if (line.isNull() || line.isEmpty()) - break; - else - { - OrderItem it; - it.enabled = !line.endsWith(".disabled"); - if (!it.enabled) - { - line.chop(9); - } - it.id = line; - itemList.append(it); - } - } - textFile.close(); - return itemList; -} - -bool LegacyModList::saveListFile() -{ - if (m_list_file.isNull() || m_list_file.isEmpty()) - return false; - QFile textFile(m_list_file); - if (!textFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) - return false; - QTextStream textStream; - textStream.setGenerateByteOrderMark(true); - textStream.setCodec("UTF-8"); - textStream.setDevice(&textFile); - for (auto mod : mods) - { - textStream << mod.mmc_id(); - if (!mod.enabled()) - textStream << ".disabled"; - textStream << endl; - } - textFile.close(); - return false; -} - -bool LegacyModList::isValid() -{ - return m_dir.exists() && m_dir.isReadable(); -} - -bool LegacyModList::installMod(const QString &filename, int index) -{ - // NOTE: fix for GH-1178: remove trailing slash to avoid issues with using the empty result of QFileInfo::fileName - QFileInfo fileinfo(FS::NormalizePath(filename)); - - qDebug() << "installing: " << fileinfo.absoluteFilePath(); - - if (!fileinfo.exists() || !fileinfo.isReadable() || index < 0) - { - return false; - } - Mod m(fileinfo); - if (!m.valid()) - return false; - - // if it's already there, replace the original mod (in place) - int idx = mods.indexOf(m); - if (idx != -1) - { - int idx2 = mods.indexOf(m, idx + 1); - if (idx2 != -1) - return false; - if (mods[idx].replace(m)) - { - - auto left = this->index(index); - auto right = this->index(index, columnCount(QModelIndex()) - 1); - emit dataChanged(left, right); - saveListFile(); - update(); - return true; - } - return false; - } - - auto type = m.type(); - if (type == Mod::MOD_UNKNOWN) - return false; - if (type == Mod::MOD_SINGLEFILE || type == Mod::MOD_ZIPFILE || type == Mod::MOD_LITEMOD) - { - QString newpath = FS::PathCombine(m_dir.path(), fileinfo.fileName()); - if (!QFile::copy(fileinfo.filePath(), newpath)) - return false; - m.repath(newpath); - beginInsertRows(QModelIndex(), index, index); - mods.insert(index, m); - endInsertRows(); - saveListFile(); - update(); - return true; - } - else if (type == Mod::MOD_FOLDER) - { - - QString from = fileinfo.filePath(); - QString to = FS::PathCombine(m_dir.path(), fileinfo.fileName()); - if (!FS::copy(from, to)()) - return false; - m.repath(to); - beginInsertRows(QModelIndex(), index, index); - mods.insert(index, m); - endInsertRows(); - saveListFile(); - update(); - return true; } - return false; -} - -bool LegacyModList::deleteMod(int index) -{ - if (index >= mods.size() || index < 0) - return false; - Mod &m = mods[index]; - if (m.destroy()) - { - beginRemoveRows(QModelIndex(), index, index); - mods.removeAt(index); - endRemoveRows(); - saveListFile(); - emit changed(); - return true; - } - return false; -} - -bool LegacyModList::deleteMods(int first, int last) -{ - for (int i = first; i <= last; i++) - { - Mod &m = mods[i]; - m.destroy(); - } - beginRemoveRows(QModelIndex(), first, last); - mods.erase(mods.begin() + first, mods.begin() + last + 1); - endRemoveRows(); - saveListFile(); - emit changed(); - return true; -} - -bool LegacyModList::moveModTo(int from, int to) -{ - if (from < 0 || from >= mods.size()) - return false; - if (to >= rowCount()) - to = rowCount() - 1; - if (to == -1) - to = rowCount() - 1; - if (from == to) - return false; - int togap = to > from ? to + 1 : to; - beginMoveRows(QModelIndex(), from, from, QModelIndex(), togap); - mods.move(from, to); - endMoveRows(); - saveListFile(); - emit changed(); - return true; -} - -bool LegacyModList::moveModUp(int from) -{ - if (from > 0) - return moveModTo(from, from - 1); - return false; -} - -bool LegacyModList::moveModsUp(int first, int last) -{ - if (first == 0) - return false; - - beginMoveRows(QModelIndex(), first, last, QModelIndex(), first - 1); - mods.move(first - 1, last); - endMoveRows(); - saveListFile(); - emit changed(); return true; } - -bool LegacyModList::moveModDown(int from) -{ - if (from < 0) - return false; - if (from < mods.size() - 1) - return moveModTo(from, from + 1); - return false; -} - -bool LegacyModList::moveModsDown(int first, int last) -{ - if (last == mods.size() - 1) - return false; - - beginMoveRows(QModelIndex(), first, last, QModelIndex(), last + 2); - mods.move(last + 1, first); - endMoveRows(); - saveListFile(); - emit changed(); - return true; -} - -int LegacyModList::columnCount(const QModelIndex &parent) const -{ - return 3; -} - -QVariant LegacyModList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - int row = index.row(); - int column = index.column(); - - if (row < 0 || row >= mods.size()) - return QVariant(); - - switch (role) - { - case Qt::DisplayRole: - switch (column) - { - case NameColumn: - return mods[row].name(); - case VersionColumn: - return mods[row].version(); - - default: - return QVariant(); - } - - case Qt::ToolTipRole: - return mods[row].mmc_id(); - - case Qt::CheckStateRole: - switch (column) - { - case ActiveColumn: - return mods[row].enabled() ? Qt::Checked : Qt::Unchecked; - default: - return QVariant(); - } - default: - return QVariant(); - } -} - -bool LegacyModList::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid()) - { - return false; - } - - if (role == Qt::CheckStateRole) - { - auto &mod = mods[index.row()]; - if (mod.enable(!mod.enabled())) - { - emit dataChanged(index, index); - return true; - } - } - return false; -} - -QVariant LegacyModList::headerData(int section, Qt::Orientation orientation, int role) const -{ - switch (role) - { - case Qt::DisplayRole: - switch (section) - { - case ActiveColumn: - return QString(); - case NameColumn: - return tr("Name"); - case VersionColumn: - return tr("Version"); - default: - return QVariant(); - } - - case Qt::ToolTipRole: - switch (section) - { - case ActiveColumn: - return tr("Is the mod enabled?"); - case NameColumn: - return tr("The name of the mod."); - case VersionColumn: - return tr("The version of the mod."); - default: - return QVariant(); - } - default: - return QVariant(); - } - return QVariant(); -} - -Qt::ItemFlags LegacyModList::flags(const QModelIndex &index) const -{ - Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); - if (index.isValid()) - return Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | - defaultFlags; - else - return Qt::ItemIsDropEnabled | defaultFlags; -} - -QStringList LegacyModList::mimeTypes() const -{ - QStringList types; - types << "text/uri-list"; - types << "text/plain"; - return types; -} - -Qt::DropActions LegacyModList::supportedDropActions() const -{ - // copy from outside, move from within and other mod lists - return Qt::CopyAction | Qt::MoveAction; -} - -Qt::DropActions LegacyModList::supportedDragActions() const -{ - // move to other mod lists or VOID - return Qt::MoveAction; -} - -QMimeData *LegacyModList::mimeData(const QModelIndexList &indexes) const -{ - QMimeData *data = new QMimeData(); - - if (indexes.size() == 0) - return data; - - auto idx = indexes[0]; - int row = idx.row(); - if (row < 0 || row >= mods.size()) - return data; - - QStringList params; - params << m_list_id << QString::number(row); - data->setText(params.join('|')); - return data; -} - -bool LegacyModList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, - const QModelIndex &parent) -{ - if (action == Qt::IgnoreAction) - return true; - // check if the action is supported - if (!data || !(action & supportedDropActions())) - return false; - if (parent.isValid()) - { - row = parent.row(); - column = parent.column(); - } - - if (row > rowCount()) - row = rowCount(); - if (row == -1) - row = rowCount(); - if (column == -1) - column = 0; - qDebug() << "Drop row: " << row << " column: " << column; - - // files dropped from outside? - if (data->hasUrls()) - { - bool was_watching = is_watching; - if (was_watching) - stopWatching(); - auto urls = data->urls(); - for (auto url : urls) - { - // only local files may be dropped... - if (!url.isLocalFile()) - continue; - QString filename = url.toLocalFile(); - installMod(filename, row); - // if there is no ordering, re-sort the list - if (m_list_file.isEmpty()) - { - beginResetModel(); - internalSort(mods); - endResetModel(); - } - } - if (was_watching) - startWatching(); - return true; - } - else if (data->hasText()) - { - QString sourcestr = data->text(); - auto list = sourcestr.split('|'); - if (list.size() != 2) - return false; - QString remoteId = list[0]; - int remoteIndex = list[1].toInt(); - qDebug() << "move: " << sourcestr; - // no moving of things between two lists - if (remoteId != m_list_id) - return false; - // no point moving to the same place... - if (row == remoteIndex) - return false; - // otherwise, move the mod :D - moveModTo(remoteIndex, row); - return true; - } - return false; -} diff --git a/api/logic/minecraft/legacy/LegacyModList.h b/api/logic/minecraft/legacy/LegacyModList.h index d1bd0e8b..19b191a7 100644 --- a/api/logic/minecraft/legacy/LegacyModList.h +++ b/api/logic/minecraft/legacy/LegacyModList.h @@ -1,4 +1,4 @@ -/* Copyright 2013-2017 MultiMC Contributors +/* Copyright 2013-2018 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ #include <QList> #include <QString> #include <QDir> -#include <QAbstractListModel> #include "minecraft/Mod.h" @@ -26,102 +25,19 @@ class LegacyInstance; class BaseInstance; -class QFileSystemWatcher; /** * A legacy mod list. * Backed by a folder. */ -class MULTIMC_LOGIC_EXPORT LegacyModList : public QAbstractListModel +class MULTIMC_LOGIC_EXPORT LegacyModList { - Q_OBJECT public: - enum Columns - { - ActiveColumn = 0, - NameColumn, - VersionColumn - }; - LegacyModList(const QString &dir, const QString &list_file = QString()); - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - virtual bool setData(const QModelIndex &index, const QVariant &value, - int role = Qt::EditRole); - - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const - { - return size(); - } - ; - virtual QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; - virtual int columnCount(const QModelIndex &parent) const; - - size_t size() const - { - return mods.size(); - } - ; - bool empty() const - { - return size() == 0; - } - Mod &operator[](size_t index) - { - return mods[index]; - } + LegacyModList(const QString &dir, const QString &list_file = QString()); /// Reloads the mod list and returns true if the list changed. - virtual bool update(); - - /** - * Adds the given mod to the list at the given index - if the list supports custom ordering - */ - virtual bool installMod(const QString & filename, int index = 0); - - /// Deletes the mod at the given index. - virtual bool deleteMod(int index); - - /// Deletes all the selected mods - virtual bool deleteMods(int first, int last); - - /** - * move the mod at index to the position N - * 0 is the beginning of the list, length() is the end of the list. - */ - virtual bool moveModTo(int from, int to); - - /** - * move the mod at index one position upwards - */ - virtual bool moveModUp(int from); - virtual bool moveModsUp(int first, int last); - - /** - * move the mod at index one position downwards - */ - virtual bool moveModDown(int from); - virtual bool moveModsDown(int first, int last); - - /// flags, mostly to support drag&drop - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - /// get data for drag action - virtual QMimeData *mimeData(const QModelIndexList &indexes) const; - /// get the supported mime types - virtual QStringList mimeTypes() const; - /// process data from drop action - virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, - const QModelIndex &parent); - /// what drag actions do we support? - virtual Qt::DropActions supportedDragActions() const; - - /// what drop actions do we support? - virtual Qt::DropActions supportedDropActions() const; - - void startWatching(); - void stopWatching(); - - virtual bool isValid(); + bool update(); QDir dir() { @@ -133,28 +49,8 @@ public: return mods; } -private: - void internalSort(QList<Mod> & what); - struct OrderItem - { - QString id; - bool enabled = false; - }; - typedef QList<OrderItem> OrderList; - OrderList readListFile(); - bool saveListFile(); -private -slots: - void directoryChanged(QString path); - -signals: - void changed(); - protected: - QFileSystemWatcher *m_watcher; - bool is_watching; QDir m_dir; QString m_list_file; - QString m_list_id; QList<Mod> mods; }; diff --git a/api/logic/minecraft/legacy/LegacyUpdate.cpp b/api/logic/minecraft/legacy/LegacyUpdate.cpp deleted file mode 100644 index e263d0de..00000000 --- a/api/logic/minecraft/legacy/LegacyUpdate.cpp +++ /dev/null @@ -1,399 +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 <QStringList> -#include <quazip.h> -#include <quazipfile.h> -#include <QDebug> - -#include "Env.h" -#include "BaseInstance.h" -#include "net/URLConstants.h" -#include "MMCZip.h" - -#include "LegacyUpdate.h" -#include "LegacyModList.h" - -#include "LwjglVersionList.h" -#include "LegacyInstance.h" -#include <FileSystem.h> - -LegacyUpdate::LegacyUpdate(BaseInstance *inst, QObject *parent) : Task(parent), m_inst(inst) -{ -} - -void LegacyUpdate::executeTask() -{ - fmllibsStart(); -} - -void LegacyUpdate::fmllibsStart() -{ - // Get the mod list - LegacyInstance *inst = (LegacyInstance *)m_inst; - auto modList = inst->jarModList(); - - bool forge_present = false; - - QString version = inst->intendedVersionId(); - auto & fmlLibsMapping = g_VersionFilterData.fmlLibsMapping; - if (!fmlLibsMapping.contains(version)) - { - lwjglStart(); - return; - } - - auto &libList = fmlLibsMapping[version]; - - // determine if we need some libs for FML or forge - setStatus(tr("Checking for FML libraries...")); - for (unsigned i = 0; i < modList->size(); i++) - { - auto &mod = modList->operator[](i); - - // do not use disabled mods. - if (!mod.enabled()) - continue; - - if (mod.type() != Mod::MOD_ZIPFILE) - continue; - - if (mod.mmc_id().contains("forge", Qt::CaseInsensitive)) - { - forge_present = true; - break; - } - if (mod.mmc_id().contains("fml", Qt::CaseInsensitive)) - { - forge_present = true; - break; - } - } - // we don't... - if (!forge_present) - { - lwjglStart(); - 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()) - { - lwjglStart(); - 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, &LegacyUpdate::fmllibsFinished); - connect(dljob, &NetJob::failed, this, &LegacyUpdate::fmllibsFailed); - connect(dljob, &NetJob::progress, this, &LegacyUpdate::progress); - legacyDownloadJob.reset(dljob); - legacyDownloadJob->start(); -} - -void LegacyUpdate::fmllibsFinished() -{ - legacyDownloadJob.reset(); - if(!fmlLibsToProcess.isEmpty()) - { - setStatus(tr("Copying FML libraries into the instance...")); - LegacyInstance *inst = (LegacyInstance *)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()); - } - lwjglStart(); -} - -void LegacyUpdate::fmllibsFailed(QString reason) -{ - emitFailed(tr("Game update failed: it was impossible to fetch the required FML libraries. Reason: %1").arg(reason)); - return; -} - -void LegacyUpdate::lwjglStart() -{ - LegacyInstance *inst = (LegacyInstance *)m_inst; - - auto list = std::dynamic_pointer_cast<LWJGLVersionList>(ENV.getVersionList("org.lwjgl.legacy")); - if (!list->isLoaded()) - { - setStatus(tr("Checking the LWJGL version list...")); - list->loadList(); - auto task = list->getLoadTask(); - connect(task.get(), &Task::succeeded, this, &LegacyUpdate::lwjglStart); - connect(task.get(), &Task::failed, this, [&](const QString & error) - { - emitFailed(tr("Failed to refresh LWJGL list: %1.").arg(error)); - }); - return; - } - - lwjglVersion = inst->lwjglVersion(); - lwjglTargetPath = FS::PathCombine(inst->lwjglFolder(), lwjglVersion); - lwjglNativesPath = FS::PathCombine(lwjglTargetPath, "natives"); - - // if the 'done' file exists, we don't have to download this again - QFileInfo doneFile(FS::PathCombine(lwjglTargetPath, "done")); - if (doneFile.exists()) - { - jarStart(); - return; - } - - setStatus(tr("Downloading new LWJGL...")); - auto version = std::dynamic_pointer_cast<LWJGLVersion>(list->findVersion(lwjglVersion)); - if (!version) - { - emitFailed(QString("Game update failed: the selected LWJGL version is invalid: %1").arg(lwjglVersion)); - return; - } - - QString url = version->url(); - QUrl realUrl(url); - QString hostname = realUrl.host(); - auto worker = &ENV.qnam(); - QNetworkRequest req(realUrl); - req.setRawHeader("Host", hostname.toLatin1()); - req.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)"); - QNetworkReply *rep = worker->get(req); - - m_reply = std::shared_ptr<QNetworkReply>(rep); - connect(rep, &QNetworkReply::downloadProgress, this, &LegacyUpdate::progress); - connect(worker, &QNetworkAccessManager::finished, this, &LegacyUpdate::lwjglFinished); -} - -void LegacyUpdate::lwjglFinished(QNetworkReply *reply) -{ - if (m_reply.get() != reply) - { - return; - } - if (reply->error() != QNetworkReply::NoError) - { - emitFailed("Failed to download: " + reply->errorString() + - "\nSometimes you have to wait a bit if you download many LWJGL versions in " - "a row. YMMV"); - return; - } - auto worker = &ENV.qnam(); - // Here i check if there is a cookie for me in the reply and extract it - QList<QNetworkCookie> cookies = - qvariant_cast<QList<QNetworkCookie>>(reply->header(QNetworkRequest::SetCookieHeader)); - if (cookies.count() != 0) - { - // you must tell which cookie goes with which url - worker->cookieJar()->setCookiesFromUrl(cookies, QUrl("sourceforge.net")); - } - - // here you can check for the 302 or whatever other header i need - QVariant newLoc = reply->header(QNetworkRequest::LocationHeader); - if (newLoc.isValid()) - { - QString redirectedTo = reply->header(QNetworkRequest::LocationHeader).toString(); - QUrl realUrl(redirectedTo); - QString hostname = realUrl.host(); - QNetworkRequest req(redirectedTo); - req.setRawHeader("Host", hostname.toLatin1()); - req.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)"); - QNetworkReply *rep = worker->get(req); - connect(rep, &QNetworkReply::downloadProgress, this, &LegacyUpdate::progress); - m_reply = std::shared_ptr<QNetworkReply>(rep); - return; - } - QFile saveMe("lwjgl.zip"); - saveMe.open(QIODevice::WriteOnly); - saveMe.write(m_reply->readAll()); - saveMe.close(); - setStatus(tr("Installing new LWJGL...")); - extractLwjgl(); - jarStart(); -} -void LegacyUpdate::extractLwjgl() -{ - // make sure the directories are there - - bool success = FS::ensureFolderPathExists(lwjglNativesPath); - - if (!success) - { - emitFailed("Failed to extract the lwjgl libs - error when creating required folders."); - return; - } - - QuaZip zip("lwjgl.zip"); - if (!zip.open(QuaZip::mdUnzip)) - { - emitFailed("Failed to extract the lwjgl libs - not a valid archive."); - return; - } - - // and now we are going to access files inside it - QuaZipFile file(&zip); - const QString jarNames[] = {"jinput.jar", "lwjgl_util.jar", "lwjgl.jar"}; - for (bool more = zip.goToFirstFile(); more; more = zip.goToNextFile()) - { - if (!file.open(QIODevice::ReadOnly)) - { - zip.close(); - emitFailed("Failed to extract the lwjgl libs - error while reading archive."); - return; - } - QuaZipFileInfo info; - QString name = file.getActualFileName(); - if (name.endsWith('/')) - { - file.close(); - continue; - } - QString destFileName; - // Look for the jars - for (int i = 0; i < 3; i++) - { - if (name.endsWith(jarNames[i])) - { - destFileName = FS::PathCombine(lwjglTargetPath, jarNames[i]); - } - } - // Not found? look for the natives - if (destFileName.isEmpty()) - { -#ifdef Q_OS_WIN32 - QString nativesDir = "windows"; -#else -#ifdef Q_OS_MAC - QString nativesDir = "macosx"; -#else - QString nativesDir = "linux"; -#endif -#endif - if (name.contains(nativesDir)) - { - int lastSlash = name.lastIndexOf('/'); - int lastBackSlash = name.lastIndexOf('\\'); - if (lastSlash != -1) - name = name.mid(lastSlash + 1); - else if (lastBackSlash != -1) - name = name.mid(lastBackSlash + 1); - destFileName = FS::PathCombine(lwjglNativesPath, name); - } - } - // Now if destFileName is still empty, go to the next file. - if (!destFileName.isEmpty()) - { - setStatus(tr("Installing new LWJGL - extracting ") + name + "..."); - QFile output(destFileName); - output.open(QIODevice::WriteOnly); - output.write(file.readAll()); - output.close(); - } - file.close(); // do not forget to close! - } - zip.close(); - m_reply.reset(); - QFile doneFile(FS::PathCombine(lwjglTargetPath, "done")); - doneFile.open(QIODevice::WriteOnly); - doneFile.write("done."); - doneFile.close(); -} - -void LegacyUpdate::lwjglFailed(QString reason) -{ - emitFailed(tr("Bad stuff happened while trying to get the lwjgl libs: %1").arg(reason)); -} - -void LegacyUpdate::jarStart() -{ - LegacyInstance *inst = (LegacyInstance *)m_inst; - if (!inst->shouldUpdate() || inst->shouldUseCustomBaseJar()) - { - emitSucceeded(); - return; - } - - setStatus(tr("Checking for jar updates...")); - // Make directories - QDir binDir(inst->binRoot()); - if (!binDir.exists() && !binDir.mkpath(".")) - { - emitFailed("Failed to create bin folder."); - return; - } - - // Build a list of URLs that will need to be downloaded. - setStatus(tr("Downloading new minecraft.jar ...")); - - QString version_id = inst->intendedVersionId(); - - auto dljob = new NetJob("Minecraft.jar for version " + version_id); - - auto metacache = ENV.metacache(); - auto entry = metacache->resolveEntry("versions", URLConstants::getJarPath(version_id)); - dljob->addNetAction(Net::Download::makeCached(QUrl(URLConstants::getLegacyJarUrl(version_id)), entry)); - connect(dljob, SIGNAL(succeeded()), SLOT(jarFinished())); - connect(dljob, SIGNAL(failed(QString)), SLOT(jarFailed(QString))); - connect(dljob, SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); - legacyDownloadJob.reset(dljob); - legacyDownloadJob->start(); -} - -void LegacyUpdate::jarFinished() -{ - // process the jar - emitSucceeded(); -} - -void LegacyUpdate::jarFailed(QString reason) -{ - // bad, bad - emitFailed(tr("Failed to download the minecraft jar: %1.").arg(reason)); -} diff --git a/api/logic/minecraft/legacy/LegacyUpdate.h b/api/logic/minecraft/legacy/LegacyUpdate.h deleted file mode 100644 index caab978e..00000000 --- a/api/logic/minecraft/legacy/LegacyUpdate.h +++ /dev/null @@ -1,70 +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 <QObject> -#include <QList> -#include <QUrl> - -#include "net/NetJob.h" -#include "tasks/Task.h" -#include "minecraft/VersionFilterData.h" - -class MinecraftVersion; -class BaseInstance; -class QuaZip; -class Mod; - -class LegacyUpdate : public Task -{ - Q_OBJECT -public: - explicit LegacyUpdate(BaseInstance *inst, QObject *parent = 0); - virtual void executeTask(); - -private -slots: - void lwjglStart(); - void lwjglFinished(QNetworkReply *); - void lwjglFailed(QString reason); - - void jarStart(); - void jarFinished(); - void jarFailed(QString reason); - - void fmllibsStart(); - void fmllibsFinished(); - void fmllibsFailed(QString reason); - - void extractLwjgl(); - -private: - - std::shared_ptr<QNetworkReply> m_reply; - - // target version, determined during this task - // MinecraftVersion *targetVersion; - QString lwjglURL; - QString lwjglVersion; - - QString lwjglTargetPath; - QString lwjglNativesPath; - -private: - NetJobPtr legacyDownloadJob; - BaseInstance *m_inst = nullptr; - QList<FMLlib> fmlLibsToProcess; -}; diff --git a/api/logic/minecraft/legacy/LegacyUpgradeTask.cpp b/api/logic/minecraft/legacy/LegacyUpgradeTask.cpp new file mode 100644 index 00000000..6cda3e4d --- /dev/null +++ b/api/logic/minecraft/legacy/LegacyUpgradeTask.cpp @@ -0,0 +1,142 @@ +#include "LegacyUpgradeTask.h" +#include "BaseInstanceProvider.h" +#include "settings/INISettingsObject.h" +#include "FileSystem.h" +#include "NullInstance.h" +#include "pathmatcher/RegexpMatcher.h" +#include <QtConcurrentRun> +#include "LegacyInstance.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/ComponentList.h" +#include "classparser.h" + +LegacyUpgradeTask::LegacyUpgradeTask(SettingsObjectPtr settings, const QString & stagingPath, InstancePtr origInstance, const QString & newName) +{ + m_globalSettings = settings; + m_stagingPath = stagingPath; + m_origInstance = origInstance; + m_newName = newName; +} + +void LegacyUpgradeTask::executeTask() +{ + setStatus(tr("Copying instance %1").arg(m_origInstance->name())); + + FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath); + folderCopy.followSymlinks(true); + + m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy); + connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &LegacyUpgradeTask::copyFinished); + connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &LegacyUpgradeTask::copyAborted); + m_copyFutureWatcher.setFuture(m_copyFuture); +} + +static QString decideVersion(const QString& currentVersion, const QString& intendedVersion) +{ + if(intendedVersion != currentVersion) + { + if(!intendedVersion.isEmpty()) + { + return intendedVersion; + } + else if(!currentVersion.isEmpty()) + { + return currentVersion; + } + } + else + { + if(!intendedVersion.isEmpty()) + { + return intendedVersion; + } + } + return QString(); +} + +void LegacyUpgradeTask::copyFinished() +{ + auto successful = m_copyFuture.result(); + if(!successful) + { + emitFailed(tr("Instance folder copy failed.")); + return; + } + auto legacyInst = std::dynamic_pointer_cast<LegacyInstance>(m_origInstance); + + auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg")); + instanceSettings->registerSetting("InstanceType", "Legacy"); + instanceSettings->set("InstanceType", "OneSix"); + // NOTE: this scope ensures the instance is fully saved before we emitSucceeded + { + MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath); + inst.setName(m_newName); + inst.init(); + + QString preferredVersionNumber = decideVersion(legacyInst->currentVersionId(), legacyInst->intendedVersionId()); + if(preferredVersionNumber.isNull()) + { + // try to decide version based on the jar(s?) + preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->baseJar()); + if(preferredVersionNumber.isNull()) + { + preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->runnableJar()); + if(preferredVersionNumber.isNull()) + { + emitFailed(tr("Could not decide Minecraft version.")); + return; + } + } + } + auto components = inst.getComponentList(); + components->buildingFromScratch(); + components->setComponentVersion("net.minecraft", preferredVersionNumber, true); + + if(legacyInst->shouldUseCustomBaseJar()) + { + QString jarPath = legacyInst->customBaseJar(); + qDebug() << "Base jar is custom! : " << jarPath; + // FIXME: handle case when the jar is unreadable? + // TODO: check the hash, if it's the same as the upstream jar, do not do this + components->installCustomJar(jarPath); + } + + auto jarMods = legacyInst->getJarMods(); + for(auto & jarMod: jarMods) + { + QString modPath = jarMod.filename().absoluteFilePath(); + qDebug() << "jarMod: " << modPath; + components->installJarMods({modPath}); + } + + // remove all the extra garbage we no longer need + auto removeAll = [&](const QString &root, const QStringList &things) + { + for(auto &thing : things) + { + auto removePath = FS::PathCombine(root, thing); + QFileInfo stat(removePath); + if(stat.isDir()) + { + FS::deletePath(removePath); + } + else + { + QFile::remove(removePath); + } + } + }; + QStringList rootRemovables = {"modlist", "version", "instMods"}; + QStringList mcRemovables = {"bin", "MultiMCLauncher.jar", "icon.png"}; + removeAll(inst.instanceRoot(), rootRemovables); + removeAll(inst.minecraftRoot(), mcRemovables); + } + emitSucceeded(); +} + +void LegacyUpgradeTask::copyAborted() +{ + emitFailed(tr("Instance folder copy has been aborted.")); + return; +} + diff --git a/api/logic/minecraft/legacy/LegacyUpgradeTask.h b/api/logic/minecraft/legacy/LegacyUpgradeTask.h new file mode 100644 index 00000000..56896385 --- /dev/null +++ b/api/logic/minecraft/legacy/LegacyUpgradeTask.h @@ -0,0 +1,38 @@ +#pragma once + +#include "tasks/Task.h" +#include "multimc_logic_export.h" +#include "net/NetJob.h" +#include <QUrl> +#include <QFuture> +#include <QFutureWatcher> +#include "settings/SettingsObject.h" +#include "BaseVersion.h" +#include "BaseInstance.h" + + +class BaseInstanceProvider; + +class MULTIMC_LOGIC_EXPORT LegacyUpgradeTask : public Task +{ + Q_OBJECT +public: + explicit LegacyUpgradeTask(SettingsObjectPtr settings, const QString & stagingPath, InstancePtr origInstance, const QString & newName); + +protected: + //! Entry point for tasks. + virtual void executeTask() override; + void copyFinished(); + void copyAborted(); + +private: /* data */ + SettingsObjectPtr m_globalSettings; + InstancePtr m_origInstance; + QString m_stagingPath; + QString m_newName; + QFuture<bool> m_copyFuture; + QFutureWatcher<bool> m_copyFutureWatcher; +}; + + + diff --git a/api/logic/minecraft/legacy/LwjglVersionList.cpp b/api/logic/minecraft/legacy/LwjglVersionList.cpp deleted file mode 100644 index 3d7ad2d4..00000000 --- a/api/logic/minecraft/legacy/LwjglVersionList.cpp +++ /dev/null @@ -1,169 +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 "LwjglVersionList.h" -#include "Env.h" - -#include <QtNetwork> -#include <QtXml> -#include <QRegExp> - -#include <QDebug> - -#define RSS_URL "https://sourceforge.net/projects/java-game-lib/rss" - -LWJGLVersionList::LWJGLVersionList(QObject *parent) : BaseVersionList(parent) -{ -} - -QVariant LWJGLVersionList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - if (index.row() > count()) - return QVariant(); - - const PtrLWJGLVersion version = m_vlist.at(index.row()); - - switch (role) - { - case Qt::DisplayRole: - return version->name(); - - case Qt::ToolTipRole: - return version->url(); - - default: - return QVariant(); - } -} - -QVariant LWJGLVersionList::headerData(int section, Qt::Orientation orientation, int role) const -{ - switch (role) - { - case Qt::DisplayRole: - return tr("Version"); - - case Qt::ToolTipRole: - return tr("LWJGL version name."); - - default: - return QVariant(); - } -} - -int LWJGLVersionList::columnCount(const QModelIndex &parent) const -{ - return 1; -} - -void LWJGLVersionList::loadList() -{ - if(m_loading) - { - return; - } - m_loading = true; - - qDebug() << "Downloading LWJGL RSS..."; - m_rssDLJob.reset(new NetJob("LWJGL RSS")); - m_rssDL = Net::Download::makeByteArray(QUrl(RSS_URL), &m_rssData); - m_rssDLJob->addNetAction(m_rssDL); - connect(m_rssDLJob.get(), &NetJob::failed, this, &LWJGLVersionList::rssFailed); - connect(m_rssDLJob.get(), &NetJob::succeeded, this, &LWJGLVersionList::rssSucceeded); - m_rssDLJob->start(); -} - -inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname) -{ - QDomNodeList elementList = parent.elementsByTagName(tagname); - if (elementList.count()) - return elementList.at(0).toElement(); - else - return QDomElement(); -} - -void LWJGLVersionList::rssFailed(const QString& reason) -{ - m_rssDLJob.reset(); - m_loading = false; - qWarning() << "Failed to load LWJGL list. Network error: " + reason; -} - -void LWJGLVersionList::rssSucceeded() -{ - QRegExp lwjglRegex("lwjgl-(([0-9]\\.?)+)\\.zip"); - Q_ASSERT_X(lwjglRegex.isValid(), "load LWJGL list", "LWJGL regex is invalid"); - - QDomDocument doc; - - QString xmlErrorMsg; - int errorLine; - - if (!doc.setContent(m_rssData, false, &xmlErrorMsg, &errorLine)) - { - qWarning() << "Failed to load LWJGL list. XML error: " + xmlErrorMsg + " at line " + QString::number(errorLine); - m_rssDLJob.reset(); - m_rssData.clear(); - m_loading = false; - return; - } - m_rssData.clear(); - - QDomNodeList items = doc.elementsByTagName("item"); - - QList<PtrLWJGLVersion> tempList; - - for (int i = 0; i < items.length(); i++) - { - Q_ASSERT_X(items.at(i).isElement(), "load LWJGL list", "XML element isn't an element... wat?"); - - QDomElement linkElement = getDomElementByTagName(items.at(i).toElement(), "link"); - if (linkElement.isNull()) - { - qDebug() << "Link element" << i << "in RSS feed doesn't exist! Skipping."; - continue; - } - - QString link = linkElement.text(); - - // Make sure it's a download link. - if (link.endsWith("/download") && link.contains(lwjglRegex)) - { - QString name = link.mid(lwjglRegex.indexIn(link) + 6); - // Subtract 4 here to remove the .zip file extension. - name = name.left(lwjglRegex.matchedLength() - 10); - - QUrl url(link); - if (!url.isValid()) - { - qWarning() << "LWJGL version URL isn't valid:" << link << "Skipping."; - continue; - } - qDebug() << "Discovered LWGL version" << name << "at" << link; - tempList.append(std::make_shared<LWJGLVersion>(name, link)); - } - } - - beginResetModel(); - m_vlist.swap(tempList); - endResetModel(); - - qDebug() << "Loaded LWJGL list."; - m_rssDLJob.reset(); - m_loading = false; -} diff --git a/api/logic/minecraft/legacy/LwjglVersionList.h b/api/logic/minecraft/legacy/LwjglVersionList.h deleted file mode 100644 index f5312e2c..00000000 --- a/api/logic/minecraft/legacy/LwjglVersionList.h +++ /dev/null @@ -1,116 +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 <QObject> -#include <QAbstractListModel> -#include <QUrl> -#include <QNetworkReply> -#include <memory> - -#include "BaseVersion.h" -#include "BaseVersionList.h" - -#include "multimc_logic_export.h" -#include <net/NetJob.h> - -class LWJGLVersion; -typedef std::shared_ptr<LWJGLVersion> PtrLWJGLVersion; - -class MULTIMC_LOGIC_EXPORT LWJGLVersion : public BaseVersion -{ -public: - LWJGLVersion(const QString &name, const QString &url) - : m_name(name), m_url(url) - { - } - - virtual QString descriptor() - { - return m_name; - } - - virtual QString name() - { - return m_name; - } - - virtual QString typeString() const - { - return QObject::tr("Upstream"); - } - - QString url() const - { - return m_url; - } - -protected: - QString m_name; - QString m_url; -}; - -class MULTIMC_LOGIC_EXPORT LWJGLVersionList : public BaseVersionList -{ - Q_OBJECT -public: - explicit LWJGLVersionList(QObject *parent = 0); - - bool isLoaded() override - { - return m_vlist.length() > 0; - } - virtual const BaseVersionPtr at(int i) const override - { - return m_vlist[i]; - } - - virtual shared_qobject_ptr<Task> getLoadTask() override - { - return m_rssDLJob; - } - - virtual void sortVersions() override {}; - - virtual void updateListData(QList< BaseVersionPtr > versions) override {}; - - int count() const override - { - return m_vlist.length(); - } - - virtual QVariant data(const QModelIndex &index, int role) const override; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - virtual int rowCount(const QModelIndex &parent) const override - { - return count(); - } - virtual int columnCount(const QModelIndex &parent) const override; - -public slots: - virtual void loadList(); - -private slots: - void rssFailed(const QString & reason); - void rssSucceeded(); - -private: - QList<PtrLWJGLVersion> m_vlist; - Net::Download::Ptr m_rssDL; - NetJobPtr m_rssDLJob; - QByteArray m_rssData; - bool m_loading = false; -}; |