diff options
author | Petr Mrázek <peterix@gmail.com> | 2016-04-10 15:53:05 +0200 |
---|---|---|
committer | Petr Mrázek <peterix@gmail.com> | 2016-05-01 00:00:14 +0200 |
commit | b6d455a02bd338e9dc0faa09d4d8177ecd8d569a (patch) | |
tree | 41982bca1ede50049f2f8c7109dd18edeefde6d0 /libraries/logic/minecraft | |
parent | 47e37635f50c09b4f9a9ee7699e3120bab3e4088 (diff) | |
download | MultiMC-b6d455a02bd338e9dc0faa09d4d8177ecd8d569a.tar MultiMC-b6d455a02bd338e9dc0faa09d4d8177ecd8d569a.tar.gz MultiMC-b6d455a02bd338e9dc0faa09d4d8177ecd8d569a.tar.lz MultiMC-b6d455a02bd338e9dc0faa09d4d8177ecd8d569a.tar.xz MultiMC-b6d455a02bd338e9dc0faa09d4d8177ecd8d569a.zip |
NOISSUE reorganize and document libraries
Diffstat (limited to 'libraries/logic/minecraft')
91 files changed, 0 insertions, 15240 deletions
diff --git a/libraries/logic/minecraft/AssetsUtils.cpp b/libraries/logic/minecraft/AssetsUtils.cpp deleted file mode 100644 index 7a525abe..00000000 --- a/libraries/logic/minecraft/AssetsUtils.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* Copyright 2013-2015 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <QFileInfo> -#include <QDir> -#include <QDirIterator> -#include <QCryptographicHash> -#include <QJsonParseError> -#include <QJsonDocument> -#include <QJsonObject> -#include <QVariant> -#include <QDebug> - -#include "AssetsUtils.h" -#include "FileSystem.h" -#include "net/MD5EtagDownload.h" - -namespace AssetsUtils -{ - -/* - * Returns true on success, with index populated - * index is undefined otherwise - */ -bool loadAssetsIndexJson(QString assetsId, QString path, AssetsIndex *index) -{ - /* - { - "objects": { - "icons/icon_16x16.png": { - "hash": "bdf48ef6b5d0d23bbb02e17d04865216179f510a", - "size": 3665 - }, - ... - } - } - } - */ - - QFile file(path); - - // Try to open the file and fail if we can't. - // TODO: We should probably report this error to the user. - if (!file.open(QIODevice::ReadOnly)) - { - qCritical() << "Failed to read assets index file" << path; - return false; - } - index->id = assetsId; - - // Read the file and close it. - QByteArray jsonData = file.readAll(); - file.close(); - - QJsonParseError parseError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError); - - // Fail if the JSON is invalid. - if (parseError.error != QJsonParseError::NoError) - { - qCritical() << "Failed to parse assets index file:" << parseError.errorString() - << "at offset " << QString::number(parseError.offset); - return false; - } - - // Make sure the root is an object. - if (!jsonDoc.isObject()) - { - qCritical() << "Invalid assets index JSON: Root should be an array."; - return false; - } - - QJsonObject root = jsonDoc.object(); - - QJsonValue isVirtual = root.value("virtual"); - if (!isVirtual.isUndefined()) - { - index->isVirtual = isVirtual.toBool(false); - } - - QJsonValue objects = root.value("objects"); - QVariantMap map = objects.toVariant().toMap(); - - for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) - { - // qDebug() << iter.key(); - - QVariant variant = iter.value(); - QVariantMap nested_objects = variant.toMap(); - - AssetObject object; - - for (QVariantMap::const_iterator nested_iter = nested_objects.begin(); - nested_iter != nested_objects.end(); ++nested_iter) - { - // qDebug() << nested_iter.key() << nested_iter.value().toString(); - QString key = nested_iter.key(); - QVariant value = nested_iter.value(); - - if (key == "hash") - { - object.hash = value.toString(); - } - else if (key == "size") - { - object.size = value.toDouble(); - } - } - - index->objects.insert(iter.key(), object); - } - - return true; -} - -QDir reconstructAssets(QString assetsId) -{ - QDir assetsDir = QDir("assets/"); - QDir indexDir = QDir(FS::PathCombine(assetsDir.path(), "indexes")); - QDir objectDir = QDir(FS::PathCombine(assetsDir.path(), "objects")); - QDir virtualDir = QDir(FS::PathCombine(assetsDir.path(), "virtual")); - - QString indexPath = FS::PathCombine(indexDir.path(), assetsId + ".json"); - QFile indexFile(indexPath); - QDir virtualRoot(FS::PathCombine(virtualDir.path(), assetsId)); - - if (!indexFile.exists()) - { - qCritical() << "No assets index file" << indexPath << "; can't reconstruct assets"; - return virtualRoot; - } - - qDebug() << "reconstructAssets" << assetsDir.path() << indexDir.path() - << objectDir.path() << virtualDir.path() << virtualRoot.path(); - - AssetsIndex index; - bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, &index); - - if (loadAssetsIndex && index.isVirtual) - { - qDebug() << "Reconstructing virtual assets folder at" << virtualRoot.path(); - - for (QString map : index.objects.keys()) - { - AssetObject asset_object = index.objects.value(map); - QString target_path = FS::PathCombine(virtualRoot.path(), map); - QFile target(target_path); - - QString tlk = asset_object.hash.left(2); - - QString original_path = FS::PathCombine(objectDir.path(), tlk, asset_object.hash); - QFile original(original_path); - if (!original.exists()) - continue; - if (!target.exists()) - { - QFileInfo info(target_path); - QDir target_dir = info.dir(); - // qDebug() << target_dir; - if (!target_dir.exists()) - QDir("").mkpath(target_dir.path()); - - bool couldCopy = original.copy(target_path); - qDebug() << " Copying" << original_path << "to" << target_path - << QString::number(couldCopy); // << original.errorString(); - } - } - - // TODO: Write last used time to virtualRoot/.lastused - } - - return virtualRoot; -} - -} - -NetActionPtr AssetObject::getDownloadAction() -{ - QFileInfo objectFile(getLocalPath()); - if ((!objectFile.isFile()) || (objectFile.size() != size)) - { - auto objectDL = MD5EtagDownload::make(getUrl(), objectFile.filePath()); - objectDL->m_total_progress = size; - return objectDL; - } - return nullptr; -} - -QString AssetObject::getLocalPath() -{ - return "assets/objects/" + getRelPath(); -} - -QUrl AssetObject::getUrl() -{ - return QUrl("http://resources.download.minecraft.net/" + getRelPath()); -} - -QString AssetObject::getRelPath() -{ - return hash.left(2) + "/" + hash; -} - -NetJobPtr AssetsIndex::getDownloadJob() -{ - auto job = new NetJob(QObject::tr("Assets for %1").arg(id)); - for (auto &object : objects.values()) - { - auto dl = object.getDownloadAction(); - if(dl) - { - job->addNetAction(dl); - } - } - if(job->size()) - return job; - return nullptr; -} diff --git a/libraries/logic/minecraft/AssetsUtils.h b/libraries/logic/minecraft/AssetsUtils.h deleted file mode 100644 index 90251c2d..00000000 --- a/libraries/logic/minecraft/AssetsUtils.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright 2013-2015 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 <QString> -#include <QMap> -#include "net/NetAction.h" -#include "net/NetJob.h" - -struct AssetObject -{ - QString getRelPath(); - QUrl getUrl(); - QString getLocalPath(); - NetActionPtr getDownloadAction(); - - QString hash; - qint64 size; -}; - -struct AssetsIndex -{ - NetJobPtr getDownloadJob(); - - QString id; - QMap<QString, AssetObject> objects; - bool isVirtual = false; -}; - -namespace AssetsUtils -{ -bool loadAssetsIndexJson(QString id, QString file, AssetsIndex* index); -/// Reconstruct a virtual assets folder for the given assets ID and return the folder -QDir reconstructAssets(QString assetsId); -} diff --git a/libraries/logic/minecraft/GradleSpecifier.h b/libraries/logic/minecraft/GradleSpecifier.h deleted file mode 100644 index 18308537..00000000 --- a/libraries/logic/minecraft/GradleSpecifier.h +++ /dev/null @@ -1,129 +0,0 @@ -#pragma once - -#include <QString> -#include <QStringList> -#include "DefaultVariable.h" - -struct GradleSpecifier -{ - GradleSpecifier() - { - m_valid = false; - } - GradleSpecifier(QString value) - { - operator=(value); - } - GradleSpecifier & operator =(const QString & value) - { - /* - org.gradle.test.classifiers : service : 1.0 : jdk15 @ jar - DEBUG 0 "org.gradle.test.classifiers:service:1.0:jdk15@jar" - DEBUG 1 "org.gradle.test.classifiers" - DEBUG 2 "service" - DEBUG 3 "1.0" - DEBUG 4 ":jdk15" - DEBUG 5 "jdk15" - DEBUG 6 "@jar" - DEBUG 7 "jar" - */ - QRegExp matcher("([^:@]+):([^:@]+):([^:@]+)" "(:([^:@]+))?" "(@([^:@]+))?"); - m_valid = matcher.exactMatch(value); - auto elements = matcher.capturedTexts(); - m_groupId = elements[1]; - m_artifactId = elements[2]; - m_version = elements[3]; - m_classifier = elements[5]; - if(!elements[7].isEmpty()) - { - m_extension = elements[7]; - } - return *this; - } - operator QString() const - { - if(!m_valid) - return "INVALID"; - QString retval = m_groupId + ":" + m_artifactId + ":" + m_version; - if(!m_classifier.isEmpty()) - { - retval += ":" + m_classifier; - } - if(m_extension.isExplicit()) - { - retval += "@" + m_extension; - } - return retval; - } - QString toPath() const - { - if(!m_valid) - return "INVALID"; - QString path = m_groupId; - path.replace('.', '/'); - path += '/' + m_artifactId + '/' + m_version + '/' + m_artifactId + '-' + m_version; - if(!m_classifier.isEmpty()) - { - path += "-" + m_classifier; - } - path += "." + m_extension; - return path; - } - inline bool valid() const - { - return m_valid; - } - inline QString version() const - { - return m_version; - } - inline QString groupId() const - { - return m_groupId; - } - inline QString artifactId() const - { - return m_artifactId; - } - inline void setClassifier(const QString & classifier) - { - m_classifier = classifier; - } - inline QString classifier() const - { - return m_classifier; - } - inline QString extension() const - { - return m_extension; - } - inline QString artifactPrefix() const - { - return m_groupId + ":" + m_artifactId; - } - bool matchName(const GradleSpecifier & other) const - { - return other.artifactId() == artifactId() && other.groupId() == groupId(); - } - bool operator==(const GradleSpecifier & other) const - { - if(m_groupId != other.m_groupId) - return false; - if(m_artifactId != other.m_artifactId) - return false; - if(m_version != other.m_version) - return false; - if(m_classifier != other.m_classifier) - return false; - if(m_extension != other.m_extension) - return false; - return true; - } -private: - QString m_groupId; - QString m_artifactId; - QString m_version; - QString m_classifier; - DefaultVariable<QString> m_extension = DefaultVariable<QString>("jar"); - bool m_valid = false; -}; diff --git a/libraries/logic/minecraft/JarMod.h b/libraries/logic/minecraft/JarMod.h deleted file mode 100644 index 42d05da9..00000000 --- a/libraries/logic/minecraft/JarMod.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include <QString> -#include <QJsonObject> -#include <memory> -class Jarmod; -typedef std::shared_ptr<Jarmod> JarmodPtr; -class Jarmod -{ -public: /* data */ - QString name; - QString originalName; -}; diff --git a/libraries/logic/minecraft/Library.cpp b/libraries/logic/minecraft/Library.cpp deleted file mode 100644 index 922db84e..00000000 --- a/libraries/logic/minecraft/Library.cpp +++ /dev/null @@ -1,239 +0,0 @@ -#include "Library.h" -#include <net/CacheDownload.h> -#include <minecraft/forge/ForgeXzDownload.h> -#include <Env.h> -#include <FileSystem.h> - -void Library::getApplicableFiles(OpSys system, QStringList& jar, QStringList& native, QStringList& native32, QStringList& native64) const -{ - auto actualPath = [&](QString relPath) - { - QFileInfo out(FS::PathCombine(storagePrefix(), relPath)); - return out.absoluteFilePath(); - }; - if(m_mojangDownloads) - { - if(m_mojangDownloads->artifact) - { - auto artifact = m_mojangDownloads->artifact; - jar += actualPath(artifact->path); - } - if(!isNative()) - return; - if(m_nativeClassifiers.contains(system)) - { - auto nativeClassifier = m_nativeClassifiers[system]; - if(nativeClassifier.contains("${arch}")) - { - auto nat32Classifier = nativeClassifier; - nat32Classifier.replace("${arch}", "32"); - auto nat64Classifier = nativeClassifier; - nat64Classifier.replace("${arch}", "64"); - auto nat32info = m_mojangDownloads->getDownloadInfo(nat32Classifier); - if(nat32info) - native32 += actualPath(nat32info->path); - auto nat64info = m_mojangDownloads->getDownloadInfo(nat64Classifier); - if(nat64info) - native64 += actualPath(nat64info->path); - } - else - { - native += actualPath(m_mojangDownloads->getDownloadInfo(nativeClassifier)->path); - } - } - } - else - { - QString raw_storage = storageSuffix(system); - if(isNative()) - { - if (raw_storage.contains("${arch}")) - { - auto nat32Storage = raw_storage; - nat32Storage.replace("${arch}", "32"); - auto nat64Storage = raw_storage; - nat64Storage.replace("${arch}", "64"); - native32 += actualPath(nat32Storage); - native64 += actualPath(nat64Storage); - } - else - { - native += actualPath(raw_storage); - } - } - else - { - jar += actualPath(raw_storage); - } - } -} - -QList<NetActionPtr> Library::getDownloads(OpSys system, HttpMetaCache * cache, QStringList &failedFiles) const -{ - QList<NetActionPtr> out; - bool isLocal = (hint() == "local"); - bool isForge = (hint() == "forge-pack-xz"); - - auto add_download = [&](QString storage, QString dl) - { - auto entry = cache->resolveEntry("libraries", storage); - if (!entry->isStale()) - return true; - if(isLocal) - { - QFileInfo fileinfo(entry->getFullPath()); - if(!fileinfo.exists()) - { - failedFiles.append(entry->getFullPath()); - return false; - } - return true; - } - if (isForge) - { - out.append(ForgeXzDownload::make(storage, entry)); - } - else - { - out.append(CacheDownload::make(dl, entry)); - } - return true; - }; - - if(m_mojangDownloads) - { - if(m_mojangDownloads->artifact) - { - auto artifact = m_mojangDownloads->artifact; - add_download(artifact->path, artifact->url); - } - if(m_nativeClassifiers.contains(system)) - { - auto nativeClassifier = m_nativeClassifiers[system]; - if(nativeClassifier.contains("${arch}")) - { - auto nat32Classifier = nativeClassifier; - nat32Classifier.replace("${arch}", "32"); - auto nat64Classifier = nativeClassifier; - nat64Classifier.replace("${arch}", "64"); - auto nat32info = m_mojangDownloads->getDownloadInfo(nat32Classifier); - if(nat32info) - add_download(nat32info->path, nat32info->url); - auto nat64info = m_mojangDownloads->getDownloadInfo(nat64Classifier); - if(nat64info) - add_download(nat64info->path, nat64info->url); - } - else - { - auto info = m_mojangDownloads->getDownloadInfo(nativeClassifier); - if(info) - { - add_download(info->path, info->url); - } - } - } - } - else - { - QString raw_storage = storageSuffix(system); - auto raw_dl = [&](){ - if (!m_absoluteURL.isEmpty()) - { - return m_absoluteURL; - } - - if (m_repositoryURL.isEmpty()) - { - return QString("https://" + URLConstants::LIBRARY_BASE) + raw_storage; - } - - if(m_repositoryURL.endsWith('/')) - { - return m_repositoryURL + raw_storage; - } - else - { - return m_repositoryURL + QChar('/') + raw_storage; - } - }(); - if (raw_storage.contains("${arch}")) - { - QString cooked_storage = raw_storage; - QString cooked_dl = raw_dl; - add_download(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32")); - cooked_storage = raw_storage; - cooked_dl = raw_dl; - add_download(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64")); - } - else - { - add_download(raw_storage, raw_dl); - } - } - return out; -} - -bool Library::isActive() const -{ - bool result = true; - if (m_rules.empty()) - { - result = true; - } - else - { - RuleAction ruleResult = Disallow; - for (auto rule : m_rules) - { - RuleAction temp = rule->apply(this); - if (temp != Defer) - ruleResult = temp; - } - result = result && (ruleResult == Allow); - } - if (isNative()) - { - result = result && m_nativeClassifiers.contains(currentSystem); - } - return result; -} - -void Library::setStoragePrefix(QString prefix) -{ - m_storagePrefix = prefix; -} - -QString Library::defaultStoragePrefix() -{ - return "libraries/"; -} - -QString Library::storagePrefix() const -{ - if(m_storagePrefix.isEmpty()) - { - return defaultStoragePrefix(); - } - return m_storagePrefix; -} - -QString Library::storageSuffix(OpSys system) const -{ - // non-native? use only the gradle specifier - if (!isNative()) - { - return m_name.toPath(); - } - - // otherwise native, override classifiers. Mojang HACK! - GradleSpecifier nativeSpec = m_name; - if (m_nativeClassifiers.contains(system)) - { - nativeSpec.setClassifier(m_nativeClassifiers[system]); - } - else - { - nativeSpec.setClassifier("INVALID"); - } - return nativeSpec.toPath(); -} diff --git a/libraries/logic/minecraft/Library.h b/libraries/logic/minecraft/Library.h deleted file mode 100644 index fdce93f3..00000000 --- a/libraries/logic/minecraft/Library.h +++ /dev/null @@ -1,184 +0,0 @@ -#pragma once -#include <QString> -#include <net/NetAction.h> -#include <QPair> -#include <QList> -#include <QStringList> -#include <QMap> -#include <QDir> -#include <QUrl> -#include <memory> - -#include "Rule.h" -#include "minecraft/OpSys.h" -#include "GradleSpecifier.h" -#include "net/URLConstants.h" -#include "MojangDownloadInfo.h" - -#include "multimc_logic_export.h" - -class Library; - -typedef std::shared_ptr<Library> LibraryPtr; - -class MULTIMC_LOGIC_EXPORT Library -{ - friend class OneSixVersionFormat; - friend class MojangVersionFormat; - friend class LibraryTest; -public: - Library() - { - } - Library(const QString &name) - { - m_name = name; - } - /// limited copy without some data. TODO: why? - static LibraryPtr limitedCopy(LibraryPtr base) - { - auto newlib = std::make_shared<Library>(); - newlib->m_name = base->m_name; - newlib->m_repositoryURL = base->m_repositoryURL; - newlib->m_hint = base->m_hint; - newlib->m_absoluteURL = base->m_absoluteURL; - newlib->m_extractExcludes = base->m_extractExcludes; - newlib->m_nativeClassifiers = base->m_nativeClassifiers; - newlib->m_rules = base->m_rules; - newlib->m_storagePrefix = base->m_storagePrefix; - newlib->m_mojangDownloads = base->m_mojangDownloads; - return newlib; - } - -public: /* methods */ - /// Returns the raw name field - const GradleSpecifier & rawName() const - { - return m_name; - } - - void setRawName(const GradleSpecifier & spec) - { - m_name = spec; - } - - void setClassifier(const QString & spec) - { - m_name.setClassifier(spec); - } - - /// returns the full group and artifact prefix - QString artifactPrefix() const - { - return m_name.artifactPrefix(); - } - - /// get the artifact ID - QString artifactId() const - { - return m_name.artifactId(); - } - - /// get the artifact version - QString version() const - { - return m_name.version(); - } - - /// Returns true if the library is native - bool isNative() const - { - return m_nativeClassifiers.size() != 0; - } - - void setStoragePrefix(QString prefix = QString()); - - /// Set the url base for downloads - void setRepositoryURL(const QString &base_url) - { - m_repositoryURL = base_url; - } - - void getApplicableFiles(OpSys system, QStringList & jar, QStringList & native, QStringList & native32, QStringList & native64) const; - - void setAbsoluteUrl(const QString &absolute_url) - { - m_absoluteURL = absolute_url; - } - - void setMojangDownloadInfo(MojangLibraryDownloadInfo::Ptr info) - { - m_mojangDownloads = info; - } - - void setHint(const QString &hint) - { - m_hint = hint; - } - - /// Set the load rules - void setRules(QList<std::shared_ptr<Rule>> rules) - { - m_rules = rules; - } - - /// Returns true if the library should be loaded (or extracted, in case of natives) - bool isActive() const; - - // Get a list of downloads for this library - QList<NetActionPtr> getDownloads(OpSys system, class HttpMetaCache * cache, QStringList &failedFiles) const; - -private: /* methods */ - /// the default storage prefix used by MultiMC - static QString defaultStoragePrefix(); - - /// Get the prefix - root of the storage to be used - QString storagePrefix() const; - - /// Get the relative path where the library should be saved - QString storageSuffix(OpSys system) const; - - QString hint() const - { - return m_hint; - } - -protected: /* data */ - /// the basic gradle dependency specifier. - GradleSpecifier m_name; - - /// DEPRECATED URL prefix of the maven repo where the file can be downloaded - QString m_repositoryURL; - - /// DEPRECATED: MultiMC-specific absolute URL. takes precedence over the implicit maven repo URL, if defined - QString m_absoluteURL; - - /** - * MultiMC-specific type hint - modifies how the library is treated - */ - QString m_hint; - - /** - * storage - by default the local libraries folder in multimc, but could be elsewhere - * MultiMC specific, because of FTB. - */ - QString m_storagePrefix; - - /// true if the library had an extract/excludes section (even empty) - bool m_hasExcludes = false; - - /// a list of files that shouldn't be extracted from the library - QStringList m_extractExcludes; - - /// native suffixes per OS - QMap<OpSys, QString> m_nativeClassifiers; - - /// true if the library had a rules section (even empty) - bool applyRules = false; - - /// rules associated with the library - QList<std::shared_ptr<Rule>> m_rules; - - /// MOJANG: container with Mojang style download info - MojangLibraryDownloadInfo::Ptr m_mojangDownloads; -}; diff --git a/libraries/logic/minecraft/MinecraftInstance.cpp b/libraries/logic/minecraft/MinecraftInstance.cpp deleted file mode 100644 index 405ccd26..00000000 --- a/libraries/logic/minecraft/MinecraftInstance.cpp +++ /dev/null @@ -1,369 +0,0 @@ -#include "MinecraftInstance.h" -#include <settings/Setting.h> -#include "settings/SettingsObject.h" -#include "Env.h" -#include "minecraft/MinecraftVersionList.h" -#include <MMCStrings.h> -#include <pathmatcher/RegexpMatcher.h> -#include <pathmatcher/MultiMatcher.h> -#include <FileSystem.h> -#include <java/JavaVersion.h> - -#define IBUS "@im=ibus" - -// all of this because keeping things compatible with deprecated old settings -// if either of the settings {a, b} is true, this also resolves to true -class OrSetting : public Setting -{ - Q_OBJECT -public: - OrSetting(QString id, std::shared_ptr<Setting> a, std::shared_ptr<Setting> b) - :Setting({id}, false), m_a(a), m_b(b) - { - } - virtual QVariant get() const - { - bool a = m_a->get().toBool(); - bool b = m_b->get().toBool(); - return a || b; - } - virtual void reset() {} - virtual void set(QVariant value) {} -private: - std::shared_ptr<Setting> m_a; - std::shared_ptr<Setting> m_b; -}; - -MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) - : BaseInstance(globalSettings, settings, rootDir) -{ - // Java Settings - auto javaOverride = m_settings->registerSetting("OverrideJava", false); - auto locationOverride = m_settings->registerSetting("OverrideJavaLocation", false); - auto argsOverride = m_settings->registerSetting("OverrideJavaArgs", false); - - // combinations - auto javaOrLocation = std::make_shared<OrSetting>("JavaOrLocationOverride", javaOverride, locationOverride); - auto javaOrArgs = std::make_shared<OrSetting>("JavaOrArgsOverride", javaOverride, argsOverride); - - m_settings->registerOverride(globalSettings->getSetting("JavaPath"), javaOrLocation); - m_settings->registerOverride(globalSettings->getSetting("JvmArgs"), javaOrArgs); - - // special! - m_settings->registerPassthrough(globalSettings->getSetting("JavaTimestamp"), javaOrLocation); - m_settings->registerPassthrough(globalSettings->getSetting("JavaVersion"), javaOrLocation); - - // Window Size - auto windowSetting = m_settings->registerSetting("OverrideWindow", false); - m_settings->registerOverride(globalSettings->getSetting("LaunchMaximized"), windowSetting); - m_settings->registerOverride(globalSettings->getSetting("MinecraftWinWidth"), windowSetting); - m_settings->registerOverride(globalSettings->getSetting("MinecraftWinHeight"), windowSetting); - - // Memory - auto memorySetting = m_settings->registerSetting("OverrideMemory", false); - m_settings->registerOverride(globalSettings->getSetting("MinMemAlloc"), memorySetting); - m_settings->registerOverride(globalSettings->getSetting("MaxMemAlloc"), memorySetting); - m_settings->registerOverride(globalSettings->getSetting("PermGen"), memorySetting); -} - -QString MinecraftInstance::minecraftRoot() const -{ - QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft")); - QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft")); - - if (dotMCDir.exists() && !mcDir.exists()) - return dotMCDir.filePath(); - else - return mcDir.filePath(); -} - -std::shared_ptr< BaseVersionList > MinecraftInstance::versionList() const -{ - return ENV.getVersionList("net.minecraft"); -} - -QStringList MinecraftInstance::javaArguments() const -{ - QStringList args; - - // custom args go first. we want to override them if we have our own here. - args.append(extraArguments()); - - // OSX dock icon and name -#ifdef Q_OS_MAC - args << "-Xdock:icon=icon.png"; - args << QString("-Xdock:name=\"%1\"").arg(windowTitle()); -#endif - - // HACK: Stupid hack for Intel drivers. See: https://mojang.atlassian.net/browse/MCL-767 -#ifdef Q_OS_WIN32 - args << QString("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_" - "minecraft.exe.heapdump"); -#endif - - args << QString("-Xms%1m").arg(settings()->get("MinMemAlloc").toInt()); - args << QString("-Xmx%1m").arg(settings()->get("MaxMemAlloc").toInt()); - - // No PermGen in newer java. - JavaVersion javaVersion(settings()->get("JavaVersion").toString()); - if(javaVersion.requiresPermGen()) - { - auto permgen = settings()->get("PermGen").toInt(); - if (permgen != 64) - { - args << QString("-XX:PermSize=%1m").arg(permgen); - } - } - - args << "-Duser.language=en"; - args << "-jar" << FS::PathCombine(QCoreApplication::applicationDirPath(), "jars", "NewLaunch.jar"); - - return args; -} - -QMap<QString, QString> MinecraftInstance::getVariables() const -{ - QMap<QString, QString> out; - out.insert("INST_NAME", name()); - out.insert("INST_ID", id()); - out.insert("INST_DIR", QDir(instanceRoot()).absolutePath()); - out.insert("INST_MC_DIR", QDir(minecraftRoot()).absolutePath()); - out.insert("INST_JAVA", settings()->get("JavaPath").toString()); - out.insert("INST_JAVA_ARGS", javaArguments().join(' ')); - return out; -} - -static QString processLD_LIBRARY_PATH(const QString & LD_LIBRARY_PATH) -{ - QDir mmcBin(QCoreApplication::applicationDirPath()); - auto items = LD_LIBRARY_PATH.split(':'); - QStringList final; - for(auto & item: items) - { - QDir test(item); - if(test == mmcBin) - { - qDebug() << "Env:LD_LIBRARY_PATH ignoring path" << item; - continue; - } - final.append(item); - } - return final.join(':'); -} - -QProcessEnvironment MinecraftInstance::createEnvironment() -{ - // prepare the process environment - QProcessEnvironment rawenv = QProcessEnvironment::systemEnvironment(); - QProcessEnvironment env; - - QStringList ignored = - { - "JAVA_ARGS", - "CLASSPATH", - "CONFIGPATH", - "JAVA_HOME", - "JRE_HOME", - "_JAVA_OPTIONS", - "JAVA_OPTIONS", - "JAVA_TOOL_OPTIONS" - }; - for(auto key: rawenv.keys()) - { - auto value = rawenv.value(key); - // filter out dangerous java crap - if(ignored.contains(key)) - { - qDebug() << "Env: ignoring" << key << value; - continue; - } - // filter MultiMC-related things - if(key.startsWith("QT_")) - { - qDebug() << "Env: ignoring" << key << value; - continue; - } -#ifdef Q_OS_LINUX - // Do not pass LD_* variables to java. They were intended for MultiMC - if(key.startsWith("LD_")) - { - qDebug() << "Env: ignoring" << key << value; - continue; - } - // Strip IBus - // IBus is a Linux IME framework. For some reason, it breaks MC? - if (key == "XMODIFIERS" && value.contains(IBUS)) - { - QString save = value; - value.replace(IBUS, ""); - qDebug() << "Env: stripped" << IBUS << "from" << save << ":" << value; - } - if(key == "GAME_PRELOAD") - { - env.insert("LD_PRELOAD", value); - continue; - } - if(key == "GAME_LIBRARY_PATH") - { - env.insert("LD_LIBRARY_PATH", processLD_LIBRARY_PATH(value)); - continue; - } -#endif - qDebug() << "Env: " << key << value; - env.insert(key, value); - } -#ifdef Q_OS_LINUX - // HACK: Workaround for QTBUG42500 - if(!env.contains("LD_LIBRARY_PATH")) - { - env.insert("LD_LIBRARY_PATH", ""); - } -#endif - - // export some infos - auto variables = getVariables(); - for (auto it = variables.begin(); it != variables.end(); ++it) - { - env.insert(it.key(), it.value()); - } - return env; -} - -QMap<QString, QString> MinecraftInstance::createCensorFilterFromSession(AuthSessionPtr session) -{ - if(!session) - { - return QMap<QString, QString>(); - } - auto & sessionRef = *session.get(); - QMap<QString, QString> filter; - auto addToFilter = [&filter](QString key, QString value) - { - if(key.trimmed().size()) - { - filter[key] = value; - } - }; - if (sessionRef.session != "-") - { - addToFilter(sessionRef.session, tr("<SESSION ID>")); - } - addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>")); - addToFilter(sessionRef.client_token, tr("<CLIENT TOKEN>")); - addToFilter(sessionRef.uuid, tr("<PROFILE ID>")); - addToFilter(sessionRef.player_name, tr("<PROFILE NAME>")); - - auto i = sessionRef.u.properties.begin(); - while (i != sessionRef.u.properties.end()) - { - addToFilter(i.value(), "<" + i.key().toUpper() + ">"); - ++i; - } - return filter; -} - -MessageLevel::Enum MinecraftInstance::guessLevel(const QString &line, MessageLevel::Enum level) -{ - QRegularExpression re("\\[(?<timestamp>[0-9:]+)\\] \\[[^/]+/(?<level>[^\\]]+)\\]"); - auto match = re.match(line); - if(match.hasMatch()) - { - // New style logs from log4j - QString timestamp = match.captured("timestamp"); - QString levelStr = match.captured("level"); - if(levelStr == "INFO") - level = MessageLevel::Message; - if(levelStr == "WARN") - level = MessageLevel::Warning; - if(levelStr == "ERROR") - level = MessageLevel::Error; - if(levelStr == "FATAL") - level = MessageLevel::Fatal; - if(levelStr == "TRACE" || levelStr == "DEBUG") - level = MessageLevel::Debug; - } - else - { - // Old style forge logs - if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || - line.contains("[FINER]") || line.contains("[FINEST]")) - level = MessageLevel::Message; - if (line.contains("[SEVERE]") || line.contains("[STDERR]")) - level = MessageLevel::Error; - if (line.contains("[WARNING]")) - level = MessageLevel::Warning; - if (line.contains("[DEBUG]")) - level = MessageLevel::Debug; - } - if (line.contains("overwriting existing")) - return MessageLevel::Fatal; - //NOTE: this diverges from the real regexp. no unicode, the first section is + instead of * - static const QString javaSymbol = "([a-zA-Z_$][a-zA-Z\\d_$]*\\.)+[a-zA-Z_$][a-zA-Z\\d_$]*"; - if (line.contains("Exception in thread") - || line.contains(QRegularExpression("\\s+at " + javaSymbol)) - || line.contains(QRegularExpression("Caused by: " + javaSymbol)) - || line.contains(QRegularExpression("([a-zA-Z_$][a-zA-Z\\d_$]*\\.)+[a-zA-Z_$]?[a-zA-Z\\d_$]*(Exception|Error|Throwable)")) - || line.contains(QRegularExpression("... \\d+ more$")) - ) - return MessageLevel::Error; - return level; -} - -IPathMatcher::Ptr MinecraftInstance::getLogFileMatcher() -{ - auto combined = std::make_shared<MultiMatcher>(); - combined->add(std::make_shared<RegexpMatcher>(".*\\.log(\\.[0-9]*)?(\\.gz)?$")); - combined->add(std::make_shared<RegexpMatcher>("crash-.*\\.txt")); - combined->add(std::make_shared<RegexpMatcher>("IDMap dump.*\\.txt$")); - combined->add(std::make_shared<RegexpMatcher>("ModLoader\\.txt(\\..*)?$")); - return combined; -} - -QString MinecraftInstance::getLogFileRoot() -{ - return minecraftRoot(); -} - -QString MinecraftInstance::prettifyTimeDuration(int64_t duration) -{ - int seconds = (int) (duration % 60); - duration /= 60; - int minutes = (int) (duration % 60); - duration /= 60; - int hours = (int) (duration % 24); - int days = (int) (duration / 24); - if((hours == 0)&&(days == 0)) - { - return tr("%1m %2s").arg(minutes).arg(seconds); - } - if (days == 0) - { - return tr("%1h %2m").arg(hours).arg(minutes); - } - return tr("%1d %2h %3m").arg(days).arg(hours).arg(minutes); -} - -QString MinecraftInstance::getStatusbarDescription() -{ - QStringList traits; - if (flags() & VersionBrokenFlag) - { - traits.append(tr("broken")); - } - - QString description; - description.append(tr("Minecraft %1 (%2)").arg(intendedVersionId()).arg(typeName())); - if(totalTimePlayed() > 0) - { - description.append(tr(", played for %1").arg(prettifyTimeDuration(totalTimePlayed()))); - } - /* - if(traits.size()) - { - description.append(QString(" (%1)").arg(traits.join(", "))); - } - */ - return description; -} - -#include "MinecraftInstance.moc" diff --git a/libraries/logic/minecraft/MinecraftInstance.h b/libraries/logic/minecraft/MinecraftInstance.h deleted file mode 100644 index cd3a8d90..00000000 --- a/libraries/logic/minecraft/MinecraftInstance.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once -#include "BaseInstance.h" -#include "minecraft/Mod.h" -#include <QProcess> - -#include "multimc_logic_export.h" - -class ModList; -class WorldList; - -class MULTIMC_LOGIC_EXPORT MinecraftInstance: public BaseInstance -{ -public: - MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir); - virtual ~MinecraftInstance() {}; - - /// Path to the instance's minecraft directory. - QString minecraftRoot() const; - - ////// Mod Lists ////// - virtual std::shared_ptr<ModList> resourcePackList() const - { - return nullptr; - } - virtual std::shared_ptr<ModList> texturePackList() const - { - return nullptr; - } - virtual std::shared_ptr<WorldList> worldList() const - { - return nullptr; - } - /// get all jar mods applicable to this instance's jar - virtual QList<Mod> getJarMods() const - { - return QList<Mod>(); - } - - /// get the launch script to be used with this - virtual QString createLaunchScript(AuthSessionPtr session) = 0; - - //FIXME: nuke? - virtual std::shared_ptr<BaseVersionList> versionList() const override; - - /// get arguments passed to java - QStringList javaArguments() const; - - /// get variables for launch command variable substitution/environment - virtual QMap<QString, QString> getVariables() const override; - - /// create an environment for launching processes - virtual 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; - - virtual QString getLogFileRoot() override; - - virtual QString getStatusbarDescription() override; - -protected: - QMap<QString, QString> createCensorFilterFromSession(AuthSessionPtr session); -private: - QString prettifyTimeDuration(int64_t duration); -}; - -typedef std::shared_ptr<MinecraftInstance> MinecraftInstancePtr; diff --git a/libraries/logic/minecraft/MinecraftProfile.cpp b/libraries/logic/minecraft/MinecraftProfile.cpp deleted file mode 100644 index 70d0cee4..00000000 --- a/libraries/logic/minecraft/MinecraftProfile.cpp +++ /dev/null @@ -1,610 +0,0 @@ -/* Copyright 2013-2015 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 <QFile> -#include <QCryptographicHash> -#include <Version.h> -#include <QDir> -#include <QJsonDocument> -#include <QJsonArray> -#include <QDebug> - -#include "minecraft/MinecraftProfile.h" -#include "ProfileUtils.h" -#include "ProfileStrategy.h" -#include "Exception.h" - -MinecraftProfile::MinecraftProfile(ProfileStrategy *strategy) - : QAbstractListModel() -{ - setStrategy(strategy); - clear(); -} - -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(); - reapplyPatches(); - endResetModel(); -} - -void MinecraftProfile::clear() -{ - m_minecraftVersion.clear(); - m_minecraftVersionType.clear(); - m_minecraftAssets.reset(); - m_minecraftArguments.clear(); - m_tweakers.clear(); - m_mainClass.clear(); - m_appletClass.clear(); - m_libraries.clear(); - m_traits.clear(); - m_jarMods.clear(); - mojangDownloads.clear(); - m_problemSeverity = ProblemSeverity::PROBLEM_NONE; -} - -void MinecraftProfile::clearPatches() -{ - beginResetModel(); - m_patches.clear(); - endResetModel(); -} - -void MinecraftProfile::appendPatch(ProfilePatchPtr patch) -{ - int index = m_patches.size(); - beginInsertRows(QModelIndex(), index, index); - m_patches.append(patch); - endInsertRows(); -} - -bool MinecraftProfile::remove(const int index) -{ - auto patch = versionPatch(index); - if (!patch->isRemovable()) - { - qDebug() << "Patch" << patch->getID() << "is non-removable"; - return false; - } - - if(!m_strategy->removePatch(patch)) - { - qCritical() << "Patch" << patch->getID() << "could not be removed"; - return false; - } - - beginRemoveRows(QModelIndex(), index, index); - m_patches.removeAt(index); - endRemoveRows(); - reapplyPatches(); - saveCurrentOrder(); - return true; -} - -bool MinecraftProfile::remove(const QString id) -{ - int i = 0; - for (auto patch : m_patches) - { - if (patch->getID() == id) - { - return remove(i); - } - i++; - } - return false; -} - -bool MinecraftProfile::customize(int index) -{ - auto patch = versionPatch(index); - if (!patch->isCustomizable()) - { - qDebug() << "Patch" << patch->getID() << "is not customizable"; - return false; - } - if(!m_strategy->customizePatch(patch)) - { - qCritical() << "Patch" << patch->getID() << "could not be customized"; - return false; - } - reapplyPatches(); - saveCurrentOrder(); - // FIXME: maybe later in unstable - // emit dataChanged(createIndex(index, 0), createIndex(index, columnCount(QModelIndex()) - 1)); - return true; -} - -bool MinecraftProfile::revertToBase(int index) -{ - auto patch = versionPatch(index); - if (!patch->isRevertible()) - { - qDebug() << "Patch" << patch->getID() << "is not revertible"; - return false; - } - if(!m_strategy->revertPatch(patch)) - { - qCritical() << "Patch" << patch->getID() << "could not be reverted"; - return false; - } - reapplyPatches(); - saveCurrentOrder(); - // FIXME: maybe later in unstable - // emit dataChanged(createIndex(index, 0), createIndex(index, columnCount(QModelIndex()) - 1)); - return true; -} - -ProfilePatchPtr MinecraftProfile::versionPatch(const QString &id) -{ - for (auto file : m_patches) - { - if (file->getID() == id) - { - return file; - } - } - return nullptr; -} - -ProfilePatchPtr MinecraftProfile::versionPatch(int index) -{ - if(index < 0 || index >= m_patches.size()) - return nullptr; - return m_patches[index]; -} - -bool MinecraftProfile::isVanilla() -{ - for(auto patchptr: m_patches) - { - if(patchptr->isCustom()) - return false; - } - return true; -} - -bool MinecraftProfile::revertToVanilla() -{ - // remove patches, if present - auto VersionPatchesCopy = m_patches; - for(auto & it: VersionPatchesCopy) - { - if (!it->isCustom()) - { - continue; - } - if(it->isRevertible() || it->isRemovable()) - { - if(!remove(it->getID())) - { - qWarning() << "Couldn't remove" << it->getID() << "from profile!"; - reapplyPatches(); - saveCurrentOrder(); - return false; - } - } - } - reapplyPatches(); - saveCurrentOrder(); - return true; -} - -QVariant MinecraftProfile::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - int row = index.row(); - int column = index.column(); - - if (row < 0 || row >= m_patches.size()) - return QVariant(); - - auto patch = m_patches.at(row); - - if (role == Qt::DisplayRole) - { - switch (column) - { - case 0: - return m_patches.at(row)->getName(); - case 1: - { - if(patch->isCustom()) - { - return QString("%1 (Custom)").arg(patch->getVersion()); - } - else - { - return patch->getVersion(); - } - } - default: - return QVariant(); - } - } - if(role == Qt::DecorationRole) - { - switch(column) - { - case 0: - { - auto severity = patch->getProblemSeverity(); - switch (severity) - { - case PROBLEM_WARNING: - return "warning"; - case PROBLEM_ERROR: - return "error"; - default: - return QVariant(); - } - } - default: - { - return QVariant(); - } - } - } - return QVariant(); -} -QVariant MinecraftProfile::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Horizontal) - { - if (role == Qt::DisplayRole) - { - switch (section) - { - case 0: - return tr("Name"); - case 1: - return tr("Version"); - default: - return QVariant(); - } - } - } - return QVariant(); -} -Qt::ItemFlags MinecraftProfile::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return Qt::NoItemFlags; - return Qt::ItemIsSelectable | Qt::ItemIsEnabled; -} - -int MinecraftProfile::rowCount(const QModelIndex &parent) const -{ - return m_patches.size(); -} - -int MinecraftProfile::columnCount(const QModelIndex &parent) const -{ - return 2; -} - -void MinecraftProfile::saveCurrentOrder() const -{ - ProfileUtils::PatchOrder order; - for(auto item: m_patches) - { - if(!item->isMoveable()) - continue; - order.append(item->getID()); - } - m_strategy->saveOrder(order); -} - -void MinecraftProfile::move(const int index, const MoveDirection direction) -{ - int theirIndex; - if (direction == MoveUp) - { - theirIndex = index - 1; - } - else - { - theirIndex = index + 1; - } - - if (index < 0 || index >= m_patches.size()) - return; - if (theirIndex >= rowCount()) - theirIndex = rowCount() - 1; - if (theirIndex == -1) - theirIndex = rowCount() - 1; - if (index == theirIndex) - return; - int togap = theirIndex > index ? theirIndex + 1 : theirIndex; - - auto from = versionPatch(index); - auto to = versionPatch(theirIndex); - - if (!from || !to || !to->isMoveable() || !from->isMoveable()) - { - return; - } - beginMoveRows(QModelIndex(), index, index, QModelIndex(), togap); - m_patches.swap(index, theirIndex); - endMoveRows(); - reapplyPatches(); - saveCurrentOrder(); -} -void MinecraftProfile::resetOrder() -{ - m_strategy->resetOrder(); - reload(); -} - -bool MinecraftProfile::reapplyPatches() -{ - try - { - clear(); - for(auto file: m_patches) - { - file->applyTo(this); - } - } - catch (Exception & error) - { - clear(); - qWarning() << "Couldn't apply profile patches because: " << error.cause(); - return false; - } - return true; -} - -static void applyString(const QString & from, QString & to) -{ - if(from.isEmpty()) - return; - to = from; -} - -void MinecraftProfile::applyMinecraftVersion(const QString& id) -{ - applyString(id, this->m_minecraftVersion); -} - -void MinecraftProfile::applyAppletClass(const QString& appletClass) -{ - applyString(appletClass, this->m_appletClass); -} - -void MinecraftProfile::applyMainClass(const QString& mainClass) -{ - applyString(mainClass, this->m_mainClass); -} - -void MinecraftProfile::applyMinecraftArguments(const QString& minecraftArguments) -{ - applyString(minecraftArguments, this->m_minecraftArguments); -} - -void MinecraftProfile::applyMinecraftVersionType(const QString& type) -{ - applyString(type, this->m_minecraftVersionType); -} - -void MinecraftProfile::applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets) -{ - if(assets) - { - m_minecraftAssets = assets; - } -} - -void MinecraftProfile::applyMojangDownload(const QString &key, MojangDownloadInfo::Ptr download) -{ - if(download) - { - mojangDownloads[key] = download; - } - else - { - mojangDownloads.remove(key); - } -} - -void MinecraftProfile::applyTraits(const QSet<QString>& traits) -{ - this->m_traits.unite(traits); -} - -void MinecraftProfile::applyTweakers(const QStringList& tweakers) -{ - // FIXME: check for dupes? - // FIXME: does order matter? - for (auto tweaker : tweakers) - { - this->m_tweakers += tweaker; - } -} - -void MinecraftProfile::applyJarMods(const QList<JarmodPtr>& jarMods) -{ - this->m_jarMods.append(jarMods); -} - -static int findLibraryByName(QList<LibraryPtr> haystack, const GradleSpecifier &needle) -{ - int retval = -1; - for (int i = 0; i < haystack.size(); ++i) - { - if (haystack.at(i)->rawName().matchName(needle)) - { - // only one is allowed. - if (retval != -1) - return -1; - retval = i; - } - } - return retval; -} - -void MinecraftProfile::applyLibrary(LibraryPtr library) -{ - if(!library->isActive()) - { - return; - } - // find the library by name. - const int index = findLibraryByName(m_libraries, library->rawName()); - // library not found? just add it. - if (index < 0) - { - m_libraries.append(Library::limitedCopy(library)); - return; - } - auto existingLibrary = m_libraries.at(index); - // if we are higher it means we should update - if (Version(library->version()) > Version(existingLibrary->version())) - { - auto libraryCopy = Library::limitedCopy(library); - m_libraries.replace(index, libraryCopy); - } -} - -void MinecraftProfile::applyProblemSeverity(ProblemSeverity severity) -{ - if (m_problemSeverity < severity) - { - m_problemSeverity = severity; - } -} - - -QString MinecraftProfile::getMinecraftVersion() const -{ - return m_minecraftVersion; -} - -QString MinecraftProfile::getAppletClass() const -{ - return m_appletClass; -} - -QString MinecraftProfile::getMainClass() const -{ - return m_mainClass; -} - -const QSet<QString> &MinecraftProfile::getTraits() const -{ - return m_traits; -} - -const QStringList & MinecraftProfile::getTweakers() const -{ - return m_tweakers; -} - -bool MinecraftProfile::hasTrait(const QString& trait) const -{ - return m_traits.contains(trait); -} - -ProblemSeverity MinecraftProfile::getProblemSeverity() const -{ - return m_problemSeverity; -} - -QString MinecraftProfile::getMinecraftVersionType() const -{ - return m_minecraftVersionType; -} - -std::shared_ptr<MojangAssetIndexInfo> MinecraftProfile::getMinecraftAssets() const -{ - if(!m_minecraftAssets) - { - return std::make_shared<MojangAssetIndexInfo>("legacy"); - } - return m_minecraftAssets; -} - -QString MinecraftProfile::getMinecraftArguments() const -{ - return m_minecraftArguments; -} - -const QList<JarmodPtr> & MinecraftProfile::getJarMods() const -{ - return m_jarMods; -} - -const QList<LibraryPtr> & MinecraftProfile::getLibraries() const -{ - return m_libraries; -} - -QString MinecraftProfile::getMainJarUrl() const -{ - auto iter = mojangDownloads.find("client"); - if(iter != mojangDownloads.end()) - { - // current - return iter.value()->url; - } - else - { - // legacy fallback - return URLConstants::getLegacyJarUrl(getMinecraftVersion()); - } -} - -void MinecraftProfile::installJarMods(QStringList selectedFiles) -{ - m_strategy->installJarMods(selectedFiles); -} - -/* - * TODO: get rid of this. Get rid of all order numbers. - */ -int MinecraftProfile::getFreeOrderNumber() -{ - int largest = 100; - // yes, I do realize this is dumb. The order thing itself is dumb. and to be removed next. - for(auto thing: m_patches) - { - int order = thing->getOrder(); - if(order > largest) - largest = order; - } - return largest + 1; -} diff --git a/libraries/logic/minecraft/MinecraftProfile.h b/libraries/logic/minecraft/MinecraftProfile.h deleted file mode 100644 index ca9288ad..00000000 --- a/libraries/logic/minecraft/MinecraftProfile.h +++ /dev/null @@ -1,200 +0,0 @@ -/* Copyright 2013-2015 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 <QAbstractListModel> - -#include <QString> -#include <QList> -#include <memory> - -#include "Library.h" -#include "VersionFile.h" -#include "JarMod.h" -#include "MojangDownloadInfo.h" - -#include "multimc_logic_export.h" - -class ProfileStrategy; -class OneSixInstance; - - -class MULTIMC_LOGIC_EXPORT MinecraftProfile : public QAbstractListModel -{ - Q_OBJECT - -public: - explicit MinecraftProfile(ProfileStrategy *strategy); - - 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; - virtual int columnCount(const QModelIndex &parent) const override; - virtual Qt::ItemFlags flags(const QModelIndex &index) const override; - - /// is this version unchanged by the user? - bool isVanilla(); - - /// remove any customizations on top of whatever 'vanilla' means - bool revertToVanilla(); - - /// install more jar mods - void installJarMods(QStringList selectedFiles); - - /// DEPRECATED, remove ASAP - int getFreeOrderNumber(); - - enum MoveDirection { MoveUp, MoveDown }; - /// move patch file # up or down the list - void move(const int index, const MoveDirection direction); - - /// remove patch file # - including files/records - bool remove(const int index); - - /// remove patch file by id - including files/records - bool remove(const QString id); - - bool customize(int index); - - bool revertToBase(int index); - - void resetOrder(); - - /// reload all profile patches from storage, clear the profile and apply the patches - void reload(); - - /// clear the profile - void clear(); - - /// apply the patches. Catches all the errors and returns true/false for success/failure - bool reapplyPatches(); - -public: /* application of profile variables from patches */ - void applyMinecraftVersion(const QString& id); - void applyMainClass(const QString& mainClass); - void applyAppletClass(const QString& appletClass); - void applyMinecraftArguments(const QString& minecraftArguments); - void applyMinecraftVersionType(const QString& type); - void applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets); - void applyTraits(const QSet<QString> &traits); - void applyTweakers(const QStringList &tweakers); - void applyJarMods(const QList<JarmodPtr> &jarMods); - void applyLibrary(LibraryPtr library); - void applyProblemSeverity(ProblemSeverity severity); - void applyMojangDownload(const QString & key, MojangDownloadInfo::Ptr download); - -public: /* getters for profile variables */ - QString getMinecraftVersion() const; - QString getMainClass() const; - QString getAppletClass() const; - QString getMinecraftVersionType() const; - MojangAssetIndexInfo::Ptr getMinecraftAssets() const; - QString getMinecraftArguments() const; - const QSet<QString> & getTraits() const; - const QStringList & getTweakers() const; - const QList<JarmodPtr> & getJarMods() const; - const QList<LibraryPtr> & getLibraries() const; - QString getMainJarUrl() const; - bool hasTrait(const QString & trait) const; - ProblemSeverity getProblemSeverity() const; - -public: - /// get the profile patch by id - ProfilePatchPtr versionPatch(const QString &id); - - /// get the profile patch by index - ProfilePatchPtr versionPatch(int index); - - /// save the current patch order - void saveCurrentOrder() const; - - /// Remove all the patches - void clearPatches(); - - /// Add the patch object to the internal list of patches - void appendPatch(ProfilePatchPtr patch); - -private: /* data */ - /// the version of Minecraft - jar to use - QString m_minecraftVersion; - - /// Release type - "release" or "snapshot" - QString m_minecraftVersionType; - - /// Assets type - "legacy" or a version ID - MojangAssetIndexInfo::Ptr m_minecraftAssets; - - // Mojang: list of 'downloads' - client jar, server jar, windows server exe, maybe more. - QMap <QString, std::shared_ptr<MojangDownloadInfo>> mojangDownloads; - - /** - * arguments that should be used for launching minecraft - * - * ex: "--username ${auth_player_name} --session ${auth_session} - * --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}" - */ - QString m_minecraftArguments; - - /// A list of all tweaker classes - QStringList m_tweakers; - - /// The main class to load first - QString m_mainClass; - - /// The applet class, for some very old minecraft releases - QString m_appletClass; - - /// the list of libraries - QList<LibraryPtr> m_libraries; - - /// traits, collected from all the version files (version files can only add) - QSet<QString> m_traits; - - /// A list of jar mods. version files can add those. - QList<JarmodPtr> m_jarMods; - - ProblemSeverity m_problemSeverity = PROBLEM_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<Rule> rules; - - /// list of attached profile patches - QList<ProfilePatchPtr> m_patches; - - /// strategy used for profile operations - ProfileStrategy *m_strategy = nullptr; -}; diff --git a/libraries/logic/minecraft/MinecraftVersion.cpp b/libraries/logic/minecraft/MinecraftVersion.cpp deleted file mode 100644 index 1e1d273c..00000000 --- a/libraries/logic/minecraft/MinecraftVersion.cpp +++ /dev/null @@ -1,215 +0,0 @@ -#include "MinecraftVersion.h" -#include "MinecraftProfile.h" -#include "VersionBuildError.h" -#include "ProfileUtils.h" -#include "settings/SettingsObject.h" -#include "minecraft/VersionFilterData.h" - -bool MinecraftVersion::usesLegacyLauncher() -{ - return getReleaseDateTime() < g_VersionFilterData.legacyCutoffDate; -} - - -QString MinecraftVersion::descriptor() -{ - return m_version; -} - -QString MinecraftVersion::name() -{ - return m_version; -} - -QString MinecraftVersion::typeString() const -{ - if(m_type == "snapshot") - { - return QObject::tr("Snapshot"); - } - else if (m_type == "release") - { - return QObject::tr("Regular release"); - } - else if (m_type == "old_alpha") - { - return QObject::tr("Alpha"); - } - else if (m_type == "old_beta") - { - return QObject::tr("Beta"); - } - else - { - return QString(); - } -} - -VersionSource MinecraftVersion::getVersionSource() -{ - return m_versionSource; -} - -bool MinecraftVersion::hasJarMods() -{ - return false; -} - -bool MinecraftVersion::isMinecraftVersion() -{ - return true; -} - -void MinecraftVersion::applyFileTo(MinecraftProfile *profile) -{ - if(m_versionSource == Local && getVersionFile()) - { - getVersionFile()->applyTo(profile); - } - else - { - throw VersionIncomplete(QObject::tr("Can't apply incomplete/builtin Minecraft version %1").arg(m_version)); - } -} - -QString MinecraftVersion::getUrl() const -{ - // legacy fallback - if(m_versionFileURL.isEmpty()) - { - return QString("http://") + URLConstants::AWS_DOWNLOAD_VERSIONS + m_version + "/" + m_version + ".json"; - } - // current - return m_versionFileURL; -} - -VersionFilePtr MinecraftVersion::getVersionFile() -{ - QFileInfo versionFile(QString("versions/%1/%1.dat").arg(m_version)); - m_problems.clear(); - if(!versionFile.exists()) - { - if(m_loadedVersionFile) - { - m_loadedVersionFile.reset(); - } - addProblem(PROBLEM_WARNING, QObject::tr("The patch file doesn't exist locally. It's possible it just needs to be downloaded.")); - } - else - { - try - { - if(versionFile.lastModified() != m_loadedVersionFileTimestamp) - { - auto loadedVersionFile = ProfileUtils::parseBinaryJsonFile(versionFile); - loadedVersionFile->name = "Minecraft"; - loadedVersionFile->setCustomizable(true); - m_loadedVersionFileTimestamp = versionFile.lastModified(); - m_loadedVersionFile = loadedVersionFile; - } - } - catch(Exception e) - { - m_loadedVersionFile.reset(); - addProblem(PROBLEM_ERROR, QObject::tr("The patch file couldn't be read:\n%1").arg(e.cause())); - } - } - return m_loadedVersionFile; -} - -bool MinecraftVersion::isCustomizable() -{ - switch(m_versionSource) - { - case Local: - case Remote: - // locally cached file, or a remote file that we can acquire can be customized - return true; - default: - // Everything else is undefined and therefore not customizable. - return false; - } - return false; -} - -const QList<PatchProblem> &MinecraftVersion::getProblems() -{ - if(getVersionFile()) - { - return getVersionFile()->getProblems(); - } - return ProfilePatch::getProblems(); -} - -ProblemSeverity MinecraftVersion::getProblemSeverity() -{ - if(getVersionFile()) - { - return getVersionFile()->getProblemSeverity(); - } - return ProfilePatch::getProblemSeverity(); -} - -void MinecraftVersion::applyTo(MinecraftProfile *profile) -{ - // do we have this one cached? - if (m_versionSource == Local) - { - applyFileTo(profile); - return; - } - throw VersionIncomplete(QObject::tr("Minecraft version %1 could not be applied: version files are missing.").arg(m_version)); -} - -int MinecraftVersion::getOrder() -{ - return order; -} - -void MinecraftVersion::setOrder(int order) -{ - this->order = order; -} - -QList<JarmodPtr> MinecraftVersion::getJarMods() -{ - return QList<JarmodPtr>(); -} - -QString MinecraftVersion::getName() -{ - return "Minecraft"; -} -QString MinecraftVersion::getVersion() -{ - return m_version; -} -QString MinecraftVersion::getID() -{ - return "net.minecraft"; -} -QString MinecraftVersion::getFilename() -{ - return QString(); -} -QDateTime MinecraftVersion::getReleaseDateTime() -{ - return m_releaseTime; -} - - -bool MinecraftVersion::needsUpdate() -{ - return m_versionSource == Remote || hasUpdate(); -} - -bool MinecraftVersion::hasUpdate() -{ - return m_versionSource == Remote || (m_versionSource == Local && upstreamUpdate); -} - -bool MinecraftVersion::isCustom() -{ - // if we add any other source types, this will evaluate to false for them. - return m_versionSource != Local && m_versionSource != Remote; -} diff --git a/libraries/logic/minecraft/MinecraftVersion.h b/libraries/logic/minecraft/MinecraftVersion.h deleted file mode 100644 index b21427d9..00000000 --- a/libraries/logic/minecraft/MinecraftVersion.h +++ /dev/null @@ -1,119 +0,0 @@ -/* Copyright 2013-2015 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 <QStringList> -#include <QSet> -#include <QDateTime> - -#include "BaseVersion.h" -#include "ProfilePatch.h" -#include "VersionFile.h" - -#include "multimc_logic_export.h" - -class MinecraftProfile; -class MinecraftVersion; -typedef std::shared_ptr<MinecraftVersion> MinecraftVersionPtr; - -class MULTIMC_LOGIC_EXPORT MinecraftVersion : public BaseVersion, public ProfilePatch -{ -friend class MinecraftVersionList; - -public: /* methods */ - // FIXME: nuke this. - bool usesLegacyLauncher(); - - virtual QString descriptor() override; - virtual QString name() override; - virtual QString typeString() const override; - virtual bool hasJarMods() override; - virtual bool isMinecraftVersion() override; - virtual void applyTo(MinecraftProfile *profile) override; - virtual int getOrder() override; - virtual void setOrder(int order) override; - virtual QList<JarmodPtr> getJarMods() override; - virtual QString getID() override; - virtual QString getVersion() override; - virtual QString getName() override; - virtual QString getFilename() override; - QDateTime getReleaseDateTime() override; - VersionSource getVersionSource() override; - - bool needsUpdate(); - bool hasUpdate(); - virtual bool isCustom() override; - virtual bool isMoveable() override - { - return false; - } - virtual bool isCustomizable() override; - virtual bool isRemovable() override - { - return false; - } - virtual bool isRevertible() override - { - return false; - } - virtual bool isEditable() override - { - return false; - } - virtual bool isVersionChangeable() override - { - return true; - } - - virtual VersionFilePtr getVersionFile() override; - - // virtual QJsonDocument toJson(bool saveOrder) override; - - QString getUrl() const; - - virtual const QList<PatchProblem> &getProblems() override; - virtual ProblemSeverity getProblemSeverity() override; - -private: /* methods */ - void applyFileTo(MinecraftProfile *profile); - -protected: /* data */ - VersionSource m_versionSource = Remote; - - /// The URL that this version will be downloaded from. - QString m_versionFileURL; - - /// the human readable version name - QString m_version; - - /// The type of this release - QString m_type; - - /// the time this version was actually released by Mojang - QDateTime m_releaseTime; - - /// the time this version was last updated by Mojang - QDateTime m_updateTime; - - /// order of this file... default = -2 - int order = -2; - - /// an update available from Mojang - MinecraftVersionPtr upstreamUpdate; - - QDateTime m_loadedVersionFileTimestamp; - mutable VersionFilePtr m_loadedVersionFile; -}; diff --git a/libraries/logic/minecraft/MinecraftVersionList.cpp b/libraries/logic/minecraft/MinecraftVersionList.cpp deleted file mode 100644 index a5cc3a39..00000000 --- a/libraries/logic/minecraft/MinecraftVersionList.cpp +++ /dev/null @@ -1,591 +0,0 @@ -/* Copyright 2013-2015 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 <QtXml> -#include "Json.h" -#include <QtAlgorithms> -#include <QtNetwork> - -#include "Env.h" -#include "Exception.h" - -#include "MinecraftVersionList.h" -#include "net/URLConstants.h" - -#include "ParseUtils.h" -#include "ProfileUtils.h" -#include "VersionFilterData.h" -#include "onesix/OneSixVersionFormat.h" -#include "MojangVersionFormat.h" -#include <FileSystem.h> - -static const char * localVersionCache = "versions/versions.dat"; - -class MCVListLoadTask : public Task -{ - Q_OBJECT - -public: - explicit MCVListLoadTask(MinecraftVersionList *vlist); - virtual ~MCVListLoadTask() override{}; - - virtual void executeTask() override; - -protected -slots: - void list_downloaded(); - -protected: - QNetworkReply *vlistReply; - MinecraftVersionList *m_list; - MinecraftVersion *m_currentStable; -}; - -class MCVListVersionUpdateTask : public Task -{ - Q_OBJECT - -public: - explicit MCVListVersionUpdateTask(MinecraftVersionList *vlist, std::shared_ptr<MinecraftVersion> updatedVersion); - virtual ~MCVListVersionUpdateTask() override{}; - virtual void executeTask() override; - -protected -slots: - void json_downloaded(); - -protected: - NetJobPtr specificVersionDownloadJob; - std::shared_ptr<MinecraftVersion> updatedVersion; - MinecraftVersionList *m_list; -}; - -class ListLoadError : public Exception -{ -public: - ListLoadError(QString cause) : Exception(cause) {}; - virtual ~ListLoadError() noexcept - { - } -}; - -MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent) -{ - loadCachedList(); -} - -Task *MinecraftVersionList::getLoadTask() -{ - return new MCVListLoadTask(this); -} - -bool MinecraftVersionList::isLoaded() -{ - return m_loaded; -} - -const BaseVersionPtr MinecraftVersionList::at(int i) const -{ - return m_vlist.at(i); -} - -int MinecraftVersionList::count() const -{ - return m_vlist.count(); -} - -static bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second) -{ - auto left = std::dynamic_pointer_cast<MinecraftVersion>(first); - auto right = std::dynamic_pointer_cast<MinecraftVersion>(second); - return left->getReleaseDateTime() > right->getReleaseDateTime(); -} - -void MinecraftVersionList::sortInternal() -{ - qSort(m_vlist.begin(), m_vlist.end(), cmpVersions); -} - -void MinecraftVersionList::loadCachedList() -{ - QFile localIndex(localVersionCache); - if (!localIndex.exists()) - { - return; - } - if (!localIndex.open(QIODevice::ReadOnly)) - { - // FIXME: this is actually a very bad thing! How do we deal with this? - qCritical() << "The minecraft version cache can't be read."; - return; - } - auto data = localIndex.readAll(); - try - { - localIndex.close(); - QJsonDocument jsonDoc = QJsonDocument::fromBinaryData(data); - if (jsonDoc.isNull()) - { - throw ListLoadError(tr("Error reading the version list.")); - } - loadList(jsonDoc, Local); - } - catch (Exception &e) - { - // the cache has gone bad for some reason... flush it. - qCritical() << "The minecraft version cache is corrupted. Flushing cache."; - localIndex.remove(); - return; - } - m_hasLocalIndex = true; -} - -void MinecraftVersionList::loadList(QJsonDocument jsonDoc, VersionSource source) -{ - qDebug() << "Loading" << ((source == Remote) ? "remote" : "local") << "version list."; - - if (!jsonDoc.isObject()) - { - throw ListLoadError(tr("Error parsing version list JSON: jsonDoc is not an object")); - } - - QJsonObject root = jsonDoc.object(); - - try - { - QJsonObject latest = Json::requireObject(root.value("latest")); - m_latestReleaseID = Json::requireString(latest.value("release")); - m_latestSnapshotID = Json::requireString(latest.value("snapshot")); - } - catch (Exception &err) - { - qCritical() - << tr("Error parsing version list JSON: couldn't determine latest versions"); - } - - // Now, get the array of versions. - if (!root.value("versions").isArray()) - { - throw ListLoadError(tr("Error parsing version list JSON: version list object is " - "missing 'versions' array")); - } - QJsonArray versions = root.value("versions").toArray(); - - QList<BaseVersionPtr> tempList; - for (auto version : versions) - { - // Load the version info. - if (!version.isObject()) - { - qCritical() << "Error while parsing version list : invalid JSON structure"; - continue; - } - - QJsonObject versionObj = version.toObject(); - QString versionID = versionObj.value("id").toString(""); - if (versionID.isEmpty()) - { - qCritical() << "Error while parsing version : version ID is missing"; - continue; - } - - if (g_VersionFilterData.legacyBlacklist.contains(versionID)) - { - qWarning() << "Blacklisted legacy version ignored: " << versionID; - continue; - } - - // Now, we construct the version object and add it to the list. - std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion()); - mcVersion->m_version = versionID; - - mcVersion->m_releaseTime = timeFromS3Time(versionObj.value("releaseTime").toString("")); - mcVersion->m_updateTime = timeFromS3Time(versionObj.value("time").toString("")); - - // depends on where we load the version from -- network request or local file? - mcVersion->m_versionSource = source; - mcVersion->m_versionFileURL = versionObj.value("url").toString(""); - QString versionTypeStr = versionObj.value("type").toString(""); - if (versionTypeStr.isEmpty()) - { - qCritical() << "Ignoring" << versionID - << "because it doesn't have the version type set."; - continue; - } - // OneSix or Legacy. use filter to determine type - if (versionTypeStr == "release") - { - } - else if (versionTypeStr == "snapshot") // It's a snapshot... yay - { - } - else if (versionTypeStr == "old_alpha") - { - } - else if (versionTypeStr == "old_beta") - { - } - else - { - qCritical() << "Ignoring" << versionID - << "because it has an invalid version type."; - continue; - } - mcVersion->m_type = versionTypeStr; - qDebug() << "Loaded version" << versionID << "from" - << ((source == Remote) ? "remote" : "local") << "version list."; - tempList.append(mcVersion); - } - updateListData(tempList); - if(source == Remote) - { - m_loaded = true; - } -} - -void MinecraftVersionList::sortVersions() -{ - beginResetModel(); - sortInternal(); - endResetModel(); -} - -QVariant MinecraftVersionList::data(const QModelIndex& index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - if (index.row() > count()) - return QVariant(); - - auto version = std::dynamic_pointer_cast<MinecraftVersion>(m_vlist[index.row()]); - switch (role) - { - case VersionPointerRole: - return qVariantFromValue(m_vlist[index.row()]); - - case VersionRole: - return version->name(); - - case VersionIdRole: - return version->descriptor(); - - case RecommendedRole: - return version->descriptor() == m_latestReleaseID; - - case LatestRole: - { - if(version->descriptor() != m_latestSnapshotID) - return false; - MinecraftVersionPtr latestRelease = std::dynamic_pointer_cast<MinecraftVersion>(getLatestStable()); - /* - if(latestRelease && latestRelease->m_releaseTime > version->m_releaseTime) - { - return false; - } - */ - return true; - } - - case TypeRole: - return version->typeString(); - - default: - return QVariant(); - } -} - -BaseVersionList::RoleList MinecraftVersionList::providesRoles() const -{ - return {VersionPointerRole, VersionRole, VersionIdRole, RecommendedRole, LatestRole, TypeRole}; -} - -BaseVersionPtr MinecraftVersionList::getLatestStable() const -{ - if(m_lookup.contains(m_latestReleaseID)) - return m_lookup[m_latestReleaseID]; - return BaseVersionPtr(); -} - -BaseVersionPtr MinecraftVersionList::getRecommended() const -{ - return getLatestStable(); -} - -void MinecraftVersionList::updateListData(QList<BaseVersionPtr> versions) -{ - beginResetModel(); - for (auto version : versions) - { - auto descr = version->descriptor(); - - if (!m_lookup.contains(descr)) - { - m_lookup[version->descriptor()] = version; - m_vlist.append(version); - continue; - } - auto orig = std::dynamic_pointer_cast<MinecraftVersion>(m_lookup[descr]); - auto added = std::dynamic_pointer_cast<MinecraftVersion>(version); - // updateListData is called after Mojang list loads. those can be local or remote - // remote comes always after local - // any other options are ignored - if (orig->m_versionSource != Local || added->m_versionSource != Remote) - { - continue; - } - // alright, it's an update. put it inside the original, for further processing. - orig->upstreamUpdate = added; - } - sortInternal(); - endResetModel(); -} - -MCVListLoadTask::MCVListLoadTask(MinecraftVersionList *vlist) -{ - m_list = vlist; - m_currentStable = NULL; - vlistReply = nullptr; -} - -void MCVListLoadTask::executeTask() -{ - setStatus(tr("Loading instance version list...")); - auto worker = ENV.qnam(); - vlistReply = worker->get(QNetworkRequest(QUrl("https://launchermeta.mojang.com/mc/game/version_manifest.json"))); - connect(vlistReply, SIGNAL(finished()), this, SLOT(list_downloaded())); -} - -void MCVListLoadTask::list_downloaded() -{ - if (vlistReply->error() != QNetworkReply::NoError) - { - vlistReply->deleteLater(); - emitFailed("Failed to load Minecraft main version list" + vlistReply->errorString()); - return; - } - - auto data = vlistReply->readAll(); - vlistReply->deleteLater(); - try - { - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); - if (jsonError.error != QJsonParseError::NoError) - { - throw ListLoadError( - tr("Error parsing version list JSON: %1").arg(jsonError.errorString())); - } - m_list->loadList(jsonDoc, Remote); - } - catch (Exception &e) - { - emitFailed(e.cause()); - return; - } - - emitSucceeded(); - return; -} - -MCVListVersionUpdateTask::MCVListVersionUpdateTask(MinecraftVersionList *vlist, std::shared_ptr<MinecraftVersion> updatedVersion) - : Task() -{ - m_list = vlist; - this->updatedVersion = updatedVersion; -} - -void MCVListVersionUpdateTask::executeTask() -{ - auto job = new NetJob("Version index"); - job->addNetAction(ByteArrayDownload::make(QUrl(updatedVersion->getUrl()))); - specificVersionDownloadJob.reset(job); - connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(json_downloaded())); - connect(specificVersionDownloadJob.get(), SIGNAL(failed(QString)), SIGNAL(failed(QString))); - connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); - specificVersionDownloadJob->start(); -} - -void MCVListVersionUpdateTask::json_downloaded() -{ - NetActionPtr DlJob = specificVersionDownloadJob->first(); - auto data = std::dynamic_pointer_cast<ByteArrayDownload>(DlJob)->m_data; - specificVersionDownloadJob.reset(); - - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); - - if (jsonError.error != QJsonParseError::NoError) - { - emitFailed(tr("The download version file is not valid.")); - return; - } - VersionFilePtr file; - try - { - file = MojangVersionFormat::versionFileFromJson(jsonDoc, "net.minecraft.json"); - } - catch (Exception &e) - { - emitFailed(tr("Couldn't process version file: %1").arg(e.cause())); - return; - } - - // Strip LWJGL from the version file. We use our own. - ProfileUtils::removeLwjglFromPatch(file); - - file->fileId = "net.minecraft"; - - // now dump the file to disk - auto doc = OneSixVersionFormat::versionFileToJson(file, false); - auto newdata = doc.toBinaryData(); - auto id = updatedVersion->descriptor(); - QString targetPath = "versions/" + id + "/" + id + ".dat"; - FS::ensureFilePathExists(targetPath); - QSaveFile vfile1(targetPath); - if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly)) - { - emitFailed(tr("Can't open %1 for writing.").arg(targetPath)); - return; - } - qint64 actual = 0; - if ((actual = vfile1.write(newdata)) != newdata.size()) - { - emitFailed(tr("Failed to write into %1. Written %2 out of %3.") - .arg(targetPath) - .arg(actual) - .arg(newdata.size())); - return; - } - if (!vfile1.commit()) - { - emitFailed(tr("Can't commit changes to %1").arg(targetPath)); - return; - } - - m_list->finalizeUpdate(id); - emitSucceeded(); -} - -std::shared_ptr<Task> MinecraftVersionList::createUpdateTask(QString version) -{ - auto iter = m_lookup.find(version); - if(iter == m_lookup.end()) - return nullptr; - - auto mcversion = std::dynamic_pointer_cast<MinecraftVersion>(*iter); - if(!mcversion) - { - return nullptr; - } - - return std::shared_ptr<Task>(new MCVListVersionUpdateTask(this, mcversion)); -} - -void MinecraftVersionList::saveCachedList() -{ - // FIXME: throw. - if (!FS::ensureFilePathExists(localVersionCache)) - return; - QSaveFile tfile(localVersionCache); - if (!tfile.open(QIODevice::WriteOnly | QIODevice::Truncate)) - return; - QJsonObject toplevel; - QJsonArray entriesArr; - for (auto version : m_vlist) - { - auto mcversion = std::dynamic_pointer_cast<MinecraftVersion>(version); - // do not save the remote versions. - if (mcversion->m_versionSource != Local) - continue; - QJsonObject entryObj; - - entryObj.insert("id", mcversion->descriptor()); - entryObj.insert("version", mcversion->descriptor()); - entryObj.insert("time", timeToS3Time(mcversion->m_updateTime)); - entryObj.insert("releaseTime", timeToS3Time(mcversion->m_releaseTime)); - entryObj.insert("url", mcversion->m_versionFileURL); - entryObj.insert("type", mcversion->m_type); - entriesArr.append(entryObj); - } - toplevel.insert("versions", entriesArr); - - { - bool someLatest = false; - QJsonObject latestObj; - if(!m_latestReleaseID.isNull()) - { - latestObj.insert("release", m_latestReleaseID); - someLatest = true; - } - if(!m_latestSnapshotID.isNull()) - { - latestObj.insert("snapshot", m_latestSnapshotID); - someLatest = true; - } - if(someLatest) - { - toplevel.insert("latest", latestObj); - } - } - - QJsonDocument doc(toplevel); - QByteArray jsonData = doc.toBinaryData(); - qint64 result = tfile.write(jsonData); - if (result == -1) - return; - if (result != jsonData.size()) - return; - tfile.commit(); -} - -void MinecraftVersionList::finalizeUpdate(QString version) -{ - int idx = -1; - for (int i = 0; i < m_vlist.size(); i++) - { - if (version == m_vlist[i]->descriptor()) - { - idx = i; - break; - } - } - if (idx == -1) - { - return; - } - - auto updatedVersion = std::dynamic_pointer_cast<MinecraftVersion>(m_vlist[idx]); - - // if we have an update for the version, replace it, make the update local - if (updatedVersion->upstreamUpdate) - { - auto updatedWith = updatedVersion->upstreamUpdate; - updatedWith->m_versionSource = Local; - m_vlist[idx] = updatedWith; - m_lookup[version] = updatedWith; - } - else - { - // otherwise, just set the version as local; - updatedVersion->m_versionSource = Local; - } - - dataChanged(index(idx), index(idx)); - - saveCachedList(); -} - -#include "MinecraftVersionList.moc" diff --git a/libraries/logic/minecraft/MinecraftVersionList.h b/libraries/logic/minecraft/MinecraftVersionList.h deleted file mode 100644 index 0fca02a7..00000000 --- a/libraries/logic/minecraft/MinecraftVersionList.h +++ /dev/null @@ -1,72 +0,0 @@ -/* Copyright 2013-2015 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 <QSet> - -#include "BaseVersionList.h" -#include "tasks/Task.h" -#include "minecraft/MinecraftVersion.h" -#include <net/NetJob.h> - -#include "multimc_logic_export.h" - -class MCVListLoadTask; -class MCVListVersionUpdateTask; - -class MULTIMC_LOGIC_EXPORT MinecraftVersionList : public BaseVersionList -{ - Q_OBJECT -private: - void sortInternal(); - void loadList(QJsonDocument jsonDoc, VersionSource source); - void loadCachedList(); - void saveCachedList(); - void finalizeUpdate(QString version); -public: - friend class MCVListLoadTask; - friend class MCVListVersionUpdateTask; - - explicit MinecraftVersionList(QObject *parent = 0); - - std::shared_ptr<Task> createUpdateTask(QString version); - - virtual Task *getLoadTask() override; - virtual bool isLoaded() override; - virtual const BaseVersionPtr at(int i) const override; - virtual int count() const override; - virtual void sortVersions() override; - virtual QVariant data(const QModelIndex & index, int role) const override; - virtual RoleList providesRoles() const override; - - virtual BaseVersionPtr getLatestStable() const override; - virtual BaseVersionPtr getRecommended() const override; - -protected: - QList<BaseVersionPtr> m_vlist; - QMap<QString, BaseVersionPtr> m_lookup; - - bool m_loaded = false; - bool m_hasLocalIndex = false; - QString m_latestReleaseID = "INVALID"; - QString m_latestSnapshotID = "INVALID"; - -protected -slots: - virtual void updateListData(QList<BaseVersionPtr> versions) override; -}; diff --git a/libraries/logic/minecraft/Mod.cpp b/libraries/logic/minecraft/Mod.cpp deleted file mode 100644 index 9b9f76f9..00000000 --- a/libraries/logic/minecraft/Mod.cpp +++ /dev/null @@ -1,377 +0,0 @@ -/* Copyright 2013-2015 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 <QDir> -#include <QString> -#include <QJsonDocument> -#include <QJsonObject> -#include <QJsonArray> -#include <QJsonValue> -#include <quazip.h> -#include <quazipfile.h> - -#include "Mod.h" -#include "settings/INIFile.h" -#include <FileSystem.h> -#include <QDebug> - -Mod::Mod(const QFileInfo &file) -{ - repath(file); -} - -void Mod::repath(const QFileInfo &file) -{ - m_file = file; - QString name_base = file.fileName(); - - m_type = Mod::MOD_UNKNOWN; - - if (m_file.isDir()) - { - m_type = MOD_FOLDER; - m_name = name_base; - m_mmc_id = name_base; - } - else if (m_file.isFile()) - { - if (name_base.endsWith(".disabled")) - { - m_enabled = false; - name_base.chop(9); - } - else - { - m_enabled = true; - } - m_mmc_id = name_base; - if (name_base.endsWith(".zip") || name_base.endsWith(".jar")) - { - m_type = MOD_ZIPFILE; - name_base.chop(4); - } - else if (name_base.endsWith(".litemod")) - { - m_type = MOD_LITEMOD; - name_base.chop(8); - } - else - { - m_type = MOD_SINGLEFILE; - } - m_name = name_base; - } - - if (m_type == MOD_ZIPFILE) - { - QuaZip zip(m_file.filePath()); - if (!zip.open(QuaZip::mdUnzip)) - return; - - QuaZipFile file(&zip); - - if (zip.setCurrentFile("mcmod.info")) - { - if (!file.open(QIODevice::ReadOnly)) - { - zip.close(); - return; - } - - ReadMCModInfo(file.readAll()); - file.close(); - zip.close(); - return; - } - else if (zip.setCurrentFile("forgeversion.properties")) - { - if (!file.open(QIODevice::ReadOnly)) - { - zip.close(); - return; - } - - ReadForgeInfo(file.readAll()); - file.close(); - zip.close(); - return; - } - - zip.close(); - } - else if (m_type == MOD_FOLDER) - { - QFileInfo mcmod_info(FS::PathCombine(m_file.filePath(), "mcmod.info")); - if (mcmod_info.isFile()) - { - QFile mcmod(mcmod_info.filePath()); - if (!mcmod.open(QIODevice::ReadOnly)) - return; - auto data = mcmod.readAll(); - if (data.isEmpty() || data.isNull()) - return; - ReadMCModInfo(data); - } - } - else if (m_type == MOD_LITEMOD) - { - QuaZip zip(m_file.filePath()); - if (!zip.open(QuaZip::mdUnzip)) - return; - - QuaZipFile file(&zip); - - if (zip.setCurrentFile("litemod.json")) - { - if (!file.open(QIODevice::ReadOnly)) - { - zip.close(); - return; - } - - ReadLiteModInfo(file.readAll()); - file.close(); - } - zip.close(); - } -} - -// NEW format -// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/6f62b37cea040daf350dc253eae6326dd9c822c3 - -// OLD format: -// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/5bf6a2d05145ec79387acc0d45c958642fb049fc -void Mod::ReadMCModInfo(QByteArray contents) -{ - auto getInfoFromArray = [&](QJsonArray arr)->void - { - if (!arr.at(0).isObject()) - return; - auto firstObj = arr.at(0).toObject(); - m_mod_id = firstObj.value("modid").toString(); - m_name = firstObj.value("name").toString(); - m_version = firstObj.value("version").toString(); - m_homeurl = firstObj.value("url").toString(); - m_updateurl = firstObj.value("updateUrl").toString(); - m_homeurl = m_homeurl.trimmed(); - if(!m_homeurl.isEmpty()) - { - // fix up url. - if (!m_homeurl.startsWith("http://") && !m_homeurl.startsWith("https://") && - !m_homeurl.startsWith("ftp://")) - { - m_homeurl.prepend("http://"); - } - } - m_description = firstObj.value("description").toString(); - QJsonArray authors = firstObj.value("authorList").toArray(); - if (authors.size() == 0) - authors = firstObj.value("authors").toArray(); - - if (authors.size() == 0) - m_authors = ""; - else if (authors.size() >= 1) - { - m_authors = authors.at(0).toString(); - for (int i = 1; i < authors.size(); i++) - { - m_authors += ", " + authors.at(i).toString(); - } - } - m_credits = firstObj.value("credits").toString(); - return; - } - ; - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError); - // this is the very old format that had just the array - if (jsonDoc.isArray()) - { - getInfoFromArray(jsonDoc.array()); - } - else if (jsonDoc.isObject()) - { - auto val = jsonDoc.object().value("modinfoversion"); - if(val.isUndefined()) - val = jsonDoc.object().value("modListVersion"); - int version = val.toDouble(); - if (version != 2) - { - qCritical() << "BAD stuff happened to mod json:"; - qCritical() << contents; - return; - } - auto arrVal = jsonDoc.object().value("modlist"); - if(arrVal.isUndefined()) - arrVal = jsonDoc.object().value("modList"); - if (arrVal.isArray()) - { - getInfoFromArray(arrVal.toArray()); - } - } -} - -void Mod::ReadForgeInfo(QByteArray contents) -{ - // Read the data - m_name = "Minecraft Forge"; - m_mod_id = "Forge"; - m_homeurl = "http://www.minecraftforge.net/forum/"; - INIFile ini; - if (!ini.loadFile(contents)) - return; - - QString major = ini.get("forge.major.number", "0").toString(); - QString minor = ini.get("forge.minor.number", "0").toString(); - QString revision = ini.get("forge.revision.number", "0").toString(); - QString build = ini.get("forge.build.number", "0").toString(); - - m_version = major + "." + minor + "." + revision + "." + build; -} - -void Mod::ReadLiteModInfo(QByteArray contents) -{ - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError); - auto object = jsonDoc.object(); - if (object.contains("name")) - { - m_mod_id = m_name = object.value("name").toString(); - } - if (object.contains("version")) - { - m_version = object.value("version").toString(""); - } - else - { - m_version = object.value("revision").toString(""); - } - m_mcversion = object.value("mcversion").toString(); - m_authors = object.value("author").toString(); - m_description = object.value("description").toString(); - m_homeurl = object.value("url").toString(); -} - -bool Mod::replace(Mod &with) -{ - if (!destroy()) - return false; - bool success = false; - auto t = with.type(); - - if (t == MOD_ZIPFILE || t == MOD_SINGLEFILE || t == MOD_LITEMOD) - { - qDebug() << "Copy: " << with.m_file.filePath() << " to " << m_file.filePath(); - success = QFile::copy(with.m_file.filePath(), m_file.filePath()); - } - if (t == MOD_FOLDER) - { - success = FS::copy(with.m_file.filePath(), m_file.path())(); - } - if (success) - { - m_name = with.m_name; - m_mmc_id = with.m_mmc_id; - m_mod_id = with.m_mod_id; - m_version = with.m_version; - m_mcversion = with.m_mcversion; - m_description = with.m_description; - m_authors = with.m_authors; - m_credits = with.m_credits; - m_homeurl = with.m_homeurl; - m_type = with.m_type; - m_file.refresh(); - } - return success; -} - -bool Mod::destroy() -{ - if (m_type == MOD_FOLDER) - { - QDir d(m_file.filePath()); - if (d.removeRecursively()) - { - m_type = MOD_UNKNOWN; - return true; - } - return false; - } - else if (m_type == MOD_SINGLEFILE || m_type == MOD_ZIPFILE || m_type == MOD_LITEMOD) - { - QFile f(m_file.filePath()); - if (f.remove()) - { - m_type = MOD_UNKNOWN; - return true; - } - return false; - } - return true; -} - -QString Mod::version() const -{ - switch (type()) - { - case MOD_ZIPFILE: - case MOD_LITEMOD: - return m_version; - case MOD_FOLDER: - return "Folder"; - case MOD_SINGLEFILE: - return "File"; - default: - return "VOID"; - } -} - -bool Mod::enable(bool value) -{ - if (m_type == Mod::MOD_UNKNOWN || m_type == Mod::MOD_FOLDER) - return false; - - if (m_enabled == value) - return false; - - QString path = m_file.absoluteFilePath(); - if (value) - { - QFile foo(path); - if (!path.endsWith(".disabled")) - return false; - path.chop(9); - if (!foo.rename(path)) - return false; - } - else - { - QFile foo(path); - path += ".disabled"; - if (!foo.rename(path)) - return false; - } - m_file = QFileInfo(path); - m_enabled = value; - return true; -} -bool Mod::operator==(const Mod &other) const -{ - return mmc_id() == other.mmc_id(); -} -bool Mod::strongCompare(const Mod &other) const -{ - return mmc_id() == other.mmc_id() && version() == other.version() && type() == other.type(); -} diff --git a/libraries/logic/minecraft/Mod.h b/libraries/logic/minecraft/Mod.h deleted file mode 100644 index 19f4c740..00000000 --- a/libraries/logic/minecraft/Mod.h +++ /dev/null @@ -1,134 +0,0 @@ -/* Copyright 2013-2015 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 <QFileInfo> - -class Mod -{ -public: - enum ModType - { - MOD_UNKNOWN, //!< Indicates an unspecified mod type. - MOD_ZIPFILE, //!< The mod is a zip file containing the mod's class files. - MOD_SINGLEFILE, //!< The mod is a single file (not a zip file). - MOD_FOLDER, //!< The mod is in a folder on the filesystem. - MOD_LITEMOD, //!< The mod is a litemod - }; - - Mod(const QFileInfo &file); - - QFileInfo filename() const - { - return m_file; - } - QString mmc_id() const - { - return m_mmc_id; - } - QString mod_id() const - { - return m_mod_id; - } - ModType type() const - { - return m_type; - } - QString mcversion() const - { - return m_mcversion; - } - ; - bool valid() - { - return m_type != MOD_UNKNOWN; - } - QString name() const - { - if(m_name.trimmed().isEmpty()) - { - return m_mmc_id; - } - return m_name; - } - - QString version() const; - - QString homeurl() const - { - return m_homeurl; - } - - QString description() const - { - return m_description; - } - - QString authors() const - { - return m_authors; - } - - QString credits() const - { - return m_credits; - } - - bool enabled() const - { - return m_enabled; - } - - bool enable(bool value); - - // delete all the files of this mod - bool destroy(); - // replace this mod with a copy of the other - bool replace(Mod &with); - // change the mod's filesystem path (used by mod lists for *MAGIC* purposes) - void repath(const QFileInfo &file); - - // WEAK compare operator - used for replacing mods - bool operator==(const Mod &other) const; - bool strongCompare(const Mod &other) const; - -private: - void ReadMCModInfo(QByteArray contents); - void ReadForgeInfo(QByteArray contents); - void ReadLiteModInfo(QByteArray contents); - -protected: - - // FIXME: what do do with those? HMM... - /* - void ReadModInfoData(QString info); - void ReadForgeInfoData(QString infoFileData); - */ - - QFileInfo m_file; - QString m_mmc_id; - QString m_mod_id; - bool m_enabled = true; - QString m_name; - QString m_version; - QString m_mcversion; - QString m_homeurl; - QString m_updateurl; - QString m_description; - QString m_authors; - QString m_credits; - - ModType m_type; -}; diff --git a/libraries/logic/minecraft/ModList.cpp b/libraries/logic/minecraft/ModList.cpp deleted file mode 100644 index d9ed4886..00000000 --- a/libraries/logic/minecraft/ModList.cpp +++ /dev/null @@ -1,616 +0,0 @@ -/* Copyright 2013-2015 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 "ModList.h" -#include <FileSystem.h> -#include <QMimeData> -#include <QUrl> -#include <QUuid> -#include <QString> -#include <QFileSystemWatcher> -#include <QDebug> - -ModList::ModList(const QString &dir, const QString &list_file) - : QAbstractListModel(), 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 ModList::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 ModList::stopWatching() -{ - is_watching = !m_watcher->removePath(m_dir.absolutePath()); - if (!is_watching) - { - qDebug() << "Stopped watching " << m_dir.absolutePath(); - } - else - { - qDebug() << "Failed to stop watching " << m_dir.absolutePath(); - } -} - -void ModList::internalSort(QList<Mod> &what) -{ - auto predicate = [](const Mod &left, const Mod &right) - { - if (left.name() == right.name()) - { - return left.mmc_id().localeAwareCompare(right.mmc_id()) < 0; - } - return left.name().localeAwareCompare(right.name()) < 0; - }; - std::sort(what.begin(), what.end(), predicate); -} - -bool ModList::update() -{ - if (!isValid()) - return false; - - QList<Mod> orderedMods; - QList<Mod> newMods; - m_dir.refresh(); - auto folderContents = m_dir.entryInfoList(); - bool orderOrStateChanged = false; - - // first, process the ordered items (if any) - OrderList listOrder = readListFile(); - for (auto item : listOrder) - { - QFileInfo infoEnabled(m_dir.filePath(item.id)); - QFileInfo infoDisabled(m_dir.filePath(item.id + ".disabled")); - int idxEnabled = folderContents.indexOf(infoEnabled); - int idxDisabled = folderContents.indexOf(infoDisabled); - bool isEnabled; - // if both enabled and disabled versions are present, it's a special case... - if (idxEnabled >= 0 && idxDisabled >= 0) - { - // we only process the one we actually have in the order file. - // and exactly as we have it. - // THIS IS A CORNER CASE - isEnabled = item.enabled; - } - else - { - // only one is present. - // we pick the one that we found. - // we assume the mod was enabled/disabled by external means - isEnabled = idxEnabled >= 0; - } - int idx = isEnabled ? idxEnabled : idxDisabled; - QFileInfo &info = isEnabled ? infoEnabled : infoDisabled; - // if the file from the index file exists - if (idx != -1) - { - // remove from the actual folder contents list - folderContents.takeAt(idx); - // append the new mod - orderedMods.append(Mod(info)); - if (isEnabled != item.enabled) - orderOrStateChanged = true; - } - else - { - orderOrStateChanged = true; - } - } - // if there are any untracked files... - if (folderContents.size()) - { - // the order surely changed! - for (auto entry : folderContents) - { - newMods.append(Mod(entry)); - } - internalSort(newMods); - orderedMods.append(newMods); - orderOrStateChanged = true; - } - // otherwise, if we were already tracking some mods - else if (mods.size()) - { - // if the number doesn't match, order changed. - if (mods.size() != orderedMods.size()) - orderOrStateChanged = true; - // if it does match, compare the mods themselves - else - for (int i = 0; i < mods.size(); i++) - { - if (!mods[i].strongCompare(orderedMods[i])) - { - orderOrStateChanged = true; - break; - } - } - } - beginResetModel(); - mods.swap(orderedMods); - endResetModel(); - if (orderOrStateChanged && !m_list_file.isEmpty()) - { - qDebug() << "Mod list " << m_list_file << " changed!"; - saveListFile(); - emit changed(); - } - return true; -} - -void ModList::directoryChanged(QString path) -{ - update(); -} - -ModList::OrderList ModList::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 ModList::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 ModList::isValid() -{ - return m_dir.exists() && m_dir.isReadable(); -} - -bool ModList::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 ModList::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 ModList::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 ModList::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 ModList::moveModUp(int from) -{ - if (from > 0) - return moveModTo(from, from - 1); - return false; -} - -bool ModList::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 ModList::moveModDown(int from) -{ - if (from < 0) - return false; - if (from < mods.size() - 1) - return moveModTo(from, from + 1); - return false; -} - -bool ModList::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 ModList::columnCount(const QModelIndex &parent) const -{ - return 3; -} - -QVariant ModList::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 ModList::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 ModList::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 ModList::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 ModList::mimeTypes() const -{ - QStringList types; - types << "text/uri-list"; - types << "text/plain"; - return types; -} - -Qt::DropActions ModList::supportedDropActions() const -{ - // copy from outside, move from within and other mod lists - return Qt::CopyAction | Qt::MoveAction; -} - -Qt::DropActions ModList::supportedDragActions() const -{ - // move to other mod lists or VOID - return Qt::MoveAction; -} - -QMimeData *ModList::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 ModList::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/libraries/logic/minecraft/ModList.h b/libraries/logic/minecraft/ModList.h deleted file mode 100644 index 05ada8ee..00000000 --- a/libraries/logic/minecraft/ModList.h +++ /dev/null @@ -1,160 +0,0 @@ -/* Copyright 2013-2015 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <QList> -#include <QString> -#include <QDir> -#include <QAbstractListModel> - -#include "minecraft/Mod.h" - -#include "multimc_logic_export.h" - -class LegacyInstance; -class BaseInstance; -class QFileSystemWatcher; - -/** - * A legacy mod list. - * Backed by a folder. - */ -class MULTIMC_LOGIC_EXPORT ModList : public QAbstractListModel -{ - Q_OBJECT -public: - enum Columns - { - ActiveColumn = 0, - NameColumn, - VersionColumn - }; - ModList(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]; - } - - /// 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(); - - QDir dir() - { - return m_dir; - } - - const QList<Mod> & allMods() - { - 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/libraries/logic/minecraft/MojangDownloadInfo.h b/libraries/logic/minecraft/MojangDownloadInfo.h deleted file mode 100644 index 1f3306e0..00000000 --- a/libraries/logic/minecraft/MojangDownloadInfo.h +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once -#include <QString> -#include <QMap> -#include <memory> - -struct MojangDownloadInfo -{ - // types - typedef std::shared_ptr<MojangDownloadInfo> Ptr; - - // data - /// Local filesystem path. WARNING: not used, only here so we can pass through mojang files unmolested! - QString path; - /// absolute URL of this file - QString url; - /// sha-1 checksum of the file - QString sha1; - /// size of the file in bytes - int size; -}; - - - -struct MojangLibraryDownloadInfo -{ - MojangLibraryDownloadInfo(MojangDownloadInfo::Ptr artifact): artifact(artifact) {}; - MojangLibraryDownloadInfo() {}; - - // types - typedef std::shared_ptr<MojangLibraryDownloadInfo> Ptr; - - // methods - MojangDownloadInfo *getDownloadInfo(QString classifier) - { - if (classifier.isNull()) - { - return artifact.get(); - } - - return classifiers[classifier].get(); - } - - // data - MojangDownloadInfo::Ptr artifact; - QMap<QString, MojangDownloadInfo::Ptr> classifiers; -}; - - - -struct MojangAssetIndexInfo : public MojangDownloadInfo -{ - // types - typedef std::shared_ptr<MojangAssetIndexInfo> Ptr; - - // methods - MojangAssetIndexInfo() - { - } - - MojangAssetIndexInfo(QString id) - { - this->id = id; - url = "https://s3.amazonaws.com/Minecraft.Download/indexes/" + id + ".json"; - known = false; - } - - // data - int totalSize; - QString id; - bool known = true; -}; diff --git a/libraries/logic/minecraft/MojangVersionFormat.cpp b/libraries/logic/minecraft/MojangVersionFormat.cpp deleted file mode 100644 index 34129c9e..00000000 --- a/libraries/logic/minecraft/MojangVersionFormat.cpp +++ /dev/null @@ -1,381 +0,0 @@ -#include "MojangVersionFormat.h" -#include "onesix/OneSixVersionFormat.h" -#include "MinecraftVersion.h" -#include "VersionBuildError.h" -#include "MojangDownloadInfo.h" - -#include "Json.h" -using namespace Json; -#include "ParseUtils.h" - -static const int CURRENT_MINIMUM_LAUNCHER_VERSION = 18; - -static MojangAssetIndexInfo::Ptr assetIndexFromJson (const QJsonObject &obj); -static MojangDownloadInfo::Ptr downloadInfoFromJson (const QJsonObject &obj); -static MojangLibraryDownloadInfo::Ptr libDownloadInfoFromJson (const QJsonObject &libObj); -static QJsonObject assetIndexToJson (MojangAssetIndexInfo::Ptr assetidxinfo); -static QJsonObject libDownloadInfoToJson (MojangLibraryDownloadInfo::Ptr libinfo); -static QJsonObject downloadInfoToJson (MojangDownloadInfo::Ptr info); - -namespace Bits -{ -static void readString(const QJsonObject &root, const QString &key, QString &variable) -{ - if (root.contains(key)) - { - variable = requireString(root.value(key)); - } -} - -static void readDownloadInfo(MojangDownloadInfo::Ptr out, const QJsonObject &obj) -{ - // optional, not used - readString(obj, "path", out->path); - // required! - out->sha1 = requireString(obj, "sha1"); - out->url = requireString(obj, "url"); - out->size = requireInteger(obj, "size"); -} - -static void readAssetIndex(MojangAssetIndexInfo::Ptr out, const QJsonObject &obj) -{ - out->totalSize = requireInteger(obj, "totalSize"); - out->id = requireString(obj, "id"); - // out->known = true; -} -} - -MojangDownloadInfo::Ptr downloadInfoFromJson(const QJsonObject &obj) -{ - auto out = std::make_shared<MojangDownloadInfo>(); - Bits::readDownloadInfo(out, obj); - return out; -} - -MojangAssetIndexInfo::Ptr assetIndexFromJson(const QJsonObject &obj) -{ - auto out = std::make_shared<MojangAssetIndexInfo>(); - Bits::readDownloadInfo(out, obj); - Bits::readAssetIndex(out, obj); - return out; -} - -QJsonObject downloadInfoToJson(MojangDownloadInfo::Ptr info) -{ - QJsonObject out; - if(!info->path.isNull()) - { - out.insert("path", info->path); - } - out.insert("sha1", info->sha1); - out.insert("size", info->size); - out.insert("url", info->url); - return out; -} - -MojangLibraryDownloadInfo::Ptr libDownloadInfoFromJson(const QJsonObject &libObj) -{ - auto out = std::make_shared<MojangLibraryDownloadInfo>(); - auto dlObj = requireObject(libObj.value("downloads")); - if(dlObj.contains("artifact")) - { - out->artifact = downloadInfoFromJson(requireObject(dlObj, "artifact")); - } - if(dlObj.contains("classifiers")) - { - auto classifiersObj = requireObject(dlObj, "classifiers"); - for(auto iter = classifiersObj.begin(); iter != classifiersObj.end(); iter++) - { - auto classifier = iter.key(); - auto classifierObj = requireObject(iter.value()); - out->classifiers[classifier] = downloadInfoFromJson(classifierObj); - } - } - return out; -} - -QJsonObject libDownloadInfoToJson(MojangLibraryDownloadInfo::Ptr libinfo) -{ - QJsonObject out; - if(libinfo->artifact) - { - out.insert("artifact", downloadInfoToJson(libinfo->artifact)); - } - if(libinfo->classifiers.size()) - { - QJsonObject classifiersOut; - for(auto iter = libinfo->classifiers.begin(); iter != libinfo->classifiers.end(); iter++) - { - classifiersOut.insert(iter.key(), downloadInfoToJson(iter.value())); - } - out.insert("classifiers", classifiersOut); - } - return out; -} - -QJsonObject assetIndexToJson(MojangAssetIndexInfo::Ptr info) -{ - QJsonObject out; - if(!info->path.isNull()) - { - out.insert("path", info->path); - } - out.insert("sha1", info->sha1); - out.insert("size", info->size); - out.insert("url", info->url); - out.insert("totalSize", info->totalSize); - out.insert("id", info->id); - return out; -} - -void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFile *out) -{ - Bits::readString(in, "id", out->minecraftVersion); - Bits::readString(in, "mainClass", out->mainClass); - Bits::readString(in, "minecraftArguments", out->minecraftArguments); - if(out->minecraftArguments.isEmpty()) - { - QString processArguments; - Bits::readString(in, "processArguments", processArguments); - QString toCompare = processArguments.toLower(); - if (toCompare == "legacy") - { - out->minecraftArguments = " ${auth_player_name} ${auth_session}"; - } - else if (toCompare == "username_session") - { - out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}"; - } - else if (toCompare == "username_session_version") - { - out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; - } - else if (!toCompare.isEmpty()) - { - out->addProblem(PROBLEM_ERROR, QObject::tr("processArguments is set to unknown value '%1'").arg(processArguments)); - } - } - Bits::readString(in, "type", out->type); - - Bits::readString(in, "assets", out->assets); - if(in.contains("assetIndex")) - { - out->mojangAssetIndex = assetIndexFromJson(requireObject(in, "assetIndex")); - } - else if (!out->assets.isNull()) - { - out->mojangAssetIndex = std::make_shared<MojangAssetIndexInfo>(out->assets); - } - - out->m_releaseTime = timeFromS3Time(in.value("releaseTime").toString("")); - out->m_updateTime = timeFromS3Time(in.value("time").toString("")); - - if (in.contains("minimumLauncherVersion")) - { - out->minimumLauncherVersion = requireInteger(in.value("minimumLauncherVersion")); - if (out->minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION) - { - out->addProblem( - PROBLEM_WARNING, - QObject::tr("The 'minimumLauncherVersion' value of this version (%1) is higher than supported by MultiMC (%2). It might not work properly!") - .arg(out->minimumLauncherVersion) - .arg(CURRENT_MINIMUM_LAUNCHER_VERSION)); - } - } - if(in.contains("downloads")) - { - auto downloadsObj = requireObject(in, "downloads"); - for(auto iter = downloadsObj.begin(); iter != downloadsObj.end(); iter++) - { - auto classifier = iter.key(); - auto classifierObj = requireObject(iter.value()); - out->mojangDownloads[classifier] = downloadInfoFromJson(classifierObj); - } - } -} - -VersionFilePtr MojangVersionFormat::versionFileFromJson(const QJsonDocument &doc, const QString &filename) -{ - 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(); - - readVersionProperties(root, out.get()); - - out->name = "Minecraft"; - out->fileId = "net.minecraft"; - out->version = out->minecraftVersion; - out->filename = filename; - - - if (root.contains("libraries")) - { - for (auto libVal : requireArray(root.value("libraries"))) - { - auto libObj = requireObject(libVal); - - auto lib = MojangVersionFormat::libraryFromJson(libObj, filename); - out->libraries.append(lib); - } - } - return out; -} - -void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObject& out) -{ - writeString(out, "id", in->minecraftVersion); - writeString(out, "mainClass", in->mainClass); - writeString(out, "minecraftArguments", in->minecraftArguments); - writeString(out, "type", in->type); - if(!in->m_releaseTime.isNull()) - { - writeString(out, "releaseTime", timeToS3Time(in->m_releaseTime)); - } - if(!in->m_updateTime.isNull()) - { - writeString(out, "time", timeToS3Time(in->m_updateTime)); - } - if(in->minimumLauncherVersion != -1) - { - out.insert("minimumLauncherVersion", in->minimumLauncherVersion); - } - writeString(out, "assets", in->assets); - if(in->mojangAssetIndex && in->mojangAssetIndex->known) - { - out.insert("assetIndex", assetIndexToJson(in->mojangAssetIndex)); - } - if(in->mojangDownloads.size()) - { - QJsonObject downloadsOut; - for(auto iter = in->mojangDownloads.begin(); iter != in->mojangDownloads.end(); iter++) - { - downloadsOut.insert(iter.key(), downloadInfoToJson(iter.value())); - } - out.insert("downloads", downloadsOut); - } -} - -QJsonDocument MojangVersionFormat::versionFileToJson(const VersionFilePtr &patch) -{ - QJsonObject root; - writeVersionProperties(patch.get(), root); - if (!patch->libraries.isEmpty()) - { - QJsonArray array; - for (auto value: patch->libraries) - { - array.append(MojangVersionFormat::libraryToJson(value.get())); - } - root.insert("libraries", array); - } - - // write the contents to a json document. - { - QJsonDocument out; - out.setObject(root); - return out; - } -} - -LibraryPtr MojangVersionFormat::libraryFromJson(const QJsonObject &libObj, const QString &filename) -{ - LibraryPtr out(new Library()); - if (!libObj.contains("name")) - { - throw JSONValidationError(filename + "contains a library that doesn't have a 'name' field"); - } - out->m_name = libObj.value("name").toString(); - - Bits::readString(libObj, "url", out->m_repositoryURL); - if (libObj.contains("extract")) - { - out->m_hasExcludes = true; - auto extractObj = requireObject(libObj.value("extract")); - for (auto excludeVal : requireArray(extractObj.value("exclude"))) - { - out->m_extractExcludes.append(requireString(excludeVal)); - } - } - if (libObj.contains("natives")) - { - QJsonObject nativesObj = requireObject(libObj.value("natives")); - for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) - { - if (!it.value().isString()) - { - qWarning() << filename << "contains an invalid native (skipping)"; - } - OpSys opSys = OpSys_fromString(it.key()); - if (opSys != Os_Other) - { - out->m_nativeClassifiers[opSys] = it.value().toString(); - } - } - } - if (libObj.contains("rules")) - { - out->applyRules = true; - out->m_rules = rulesFromJsonV4(libObj); - } - if (libObj.contains("downloads")) - { - out->m_mojangDownloads = libDownloadInfoFromJson(libObj); - } - return out; -} - -QJsonObject MojangVersionFormat::libraryToJson(Library *library) -{ - QJsonObject libRoot; - libRoot.insert("name", (QString)library->m_name); - if (!library->m_repositoryURL.isEmpty()) - { - libRoot.insert("url", library->m_repositoryURL); - } - if (library->isNative()) - { - QJsonObject nativeList; - auto iter = library->m_nativeClassifiers.begin(); - while (iter != library->m_nativeClassifiers.end()) - { - nativeList.insert(OpSys_toString(iter.key()), iter.value()); - iter++; - } - libRoot.insert("natives", nativeList); - if (library->m_extractExcludes.size()) - { - QJsonArray excludes; - QJsonObject extract; - for (auto exclude : library->m_extractExcludes) - { - excludes.append(exclude); - } - extract.insert("exclude", excludes); - libRoot.insert("extract", extract); - } - } - if (library->m_rules.size()) - { - QJsonArray allRules; - for (auto &rule : library->m_rules) - { - QJsonObject ruleObj = rule->toJson(); - allRules.append(ruleObj); - } - libRoot.insert("rules", allRules); - } - if(library->m_mojangDownloads) - { - auto downloadsObj = libDownloadInfoToJson(library->m_mojangDownloads); - libRoot.insert("downloads", downloadsObj); - } - return libRoot; -} diff --git a/libraries/logic/minecraft/MojangVersionFormat.h b/libraries/logic/minecraft/MojangVersionFormat.h deleted file mode 100644 index 4e141088..00000000 --- a/libraries/logic/minecraft/MojangVersionFormat.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include <minecraft/VersionFile.h> -#include <minecraft/Library.h> -#include <QJsonDocument> - -#include "multimc_logic_export.h" - -class MULTIMC_LOGIC_EXPORT MojangVersionFormat -{ -friend class OneSixVersionFormat; -protected: - // does not include libraries - static void readVersionProperties(const QJsonObject& in, VersionFile* out); - // does not include libraries - static void writeVersionProperties(const VersionFile* in, QJsonObject& out); -public: - // version files / profile patches - static VersionFilePtr versionFileFromJson(const QJsonDocument &doc, const QString &filename); - static QJsonDocument versionFileToJson(const VersionFilePtr &patch); - - // libraries - static LibraryPtr libraryFromJson(const QJsonObject &libObj, const QString &filename); - static QJsonObject libraryToJson(Library *library); -}; diff --git a/libraries/logic/minecraft/OpSys.cpp b/libraries/logic/minecraft/OpSys.cpp deleted file mode 100644 index 4c2a236d..00000000 --- a/libraries/logic/minecraft/OpSys.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright 2013-2015 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 "OpSys.h" - -OpSys OpSys_fromString(QString name) -{ - if (name == "linux") - return Os_Linux; - if (name == "windows") - return Os_Windows; - if (name == "osx") - return Os_OSX; - return Os_Other; -} - -QString OpSys_toString(OpSys name) -{ - switch (name) - { - case Os_Linux: - return "linux"; - case Os_OSX: - return "osx"; - case Os_Windows: - return "windows"; - default: - return "other"; - } -}
\ No newline at end of file diff --git a/libraries/logic/minecraft/OpSys.h b/libraries/logic/minecraft/OpSys.h deleted file mode 100644 index 9ebea3de..00000000 --- a/libraries/logic/minecraft/OpSys.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright 2013-2015 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 <QString> -enum OpSys -{ - Os_Windows, - Os_Linux, - Os_OSX, - Os_Other -}; - -OpSys OpSys_fromString(QString); -QString OpSys_toString(OpSys); - -#ifdef Q_OS_WIN32 -#define currentSystem Os_Windows -#else -#ifdef Q_OS_MAC -#define currentSystem Os_OSX -#else -#define currentSystem Os_Linux -#endif -#endif
\ No newline at end of file diff --git a/libraries/logic/minecraft/ParseUtils.cpp b/libraries/logic/minecraft/ParseUtils.cpp deleted file mode 100644 index ca188432..00000000 --- a/libraries/logic/minecraft/ParseUtils.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include <QDateTime> -#include <QString> -#include "ParseUtils.h" -#include <QDebug> -#include <cstdlib> - -QDateTime timeFromS3Time(QString str) -{ - return QDateTime::fromString(str, Qt::ISODate); -} - -QString timeToS3Time(QDateTime time) -{ - // this all because Qt can't format timestamps right. - int offsetRaw = time.offsetFromUtc(); - bool negative = offsetRaw < 0; - int offsetAbs = std::abs(offsetRaw); - - int offsetSeconds = offsetAbs % 60; - offsetAbs -= offsetSeconds; - - int offsetMinutes = offsetAbs % 3600; - offsetAbs -= offsetMinutes; - offsetMinutes /= 60; - - int offsetHours = offsetAbs / 3600; - - QString raw = time.toString("yyyy-MM-ddTHH:mm:ss"); - raw += (negative ? QChar('-') : QChar('+')); - raw += QString("%1").arg(offsetHours, 2, 10, QChar('0')); - raw += ":"; - raw += QString("%1").arg(offsetMinutes, 2, 10, QChar('0')); - return raw; -} diff --git a/libraries/logic/minecraft/ParseUtils.h b/libraries/logic/minecraft/ParseUtils.h deleted file mode 100644 index 2b367a10..00000000 --- a/libraries/logic/minecraft/ParseUtils.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include <QString> -#include <QDateTime> - -#include "multimc_logic_export.h" - -/// take the timestamp used by S3 and turn it into QDateTime -MULTIMC_LOGIC_EXPORT QDateTime timeFromS3Time(QString str); - -/// take a timestamp and convert it into an S3 timestamp -MULTIMC_LOGIC_EXPORT QString timeToS3Time(QDateTime); diff --git a/libraries/logic/minecraft/ProfilePatch.h b/libraries/logic/minecraft/ProfilePatch.h deleted file mode 100644 index f0c65360..00000000 --- a/libraries/logic/minecraft/ProfilePatch.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -#include <memory> -#include <QList> -#include <QJsonDocument> -#include <QDateTime> -#include "JarMod.h" - -class MinecraftProfile; - -enum ProblemSeverity -{ - PROBLEM_NONE, - PROBLEM_WARNING, - PROBLEM_ERROR -}; - -/// where is a version from? -enum VersionSource -{ - Local, //!< version loaded from a file in the cache. - Remote, //!< incomplete version on a remote server. -}; - -class PatchProblem -{ -public: - PatchProblem(ProblemSeverity severity, const QString & description) - { - m_severity = severity; - m_description = description; - } - const QString & getDescription() const - { - return m_description; - } - const ProblemSeverity getSeverity() const - { - return m_severity; - } -private: - ProblemSeverity m_severity; - QString m_description; -}; - -class ProfilePatch : public std::enable_shared_from_this<ProfilePatch> -{ -public: - virtual ~ProfilePatch(){}; - virtual void applyTo(MinecraftProfile *profile) = 0; - - virtual bool isMinecraftVersion() = 0; - virtual bool hasJarMods() = 0; - virtual QList<JarmodPtr> getJarMods() = 0; - - virtual bool isMoveable() = 0; - virtual bool isCustomizable() = 0; - virtual bool isRevertible() = 0; - virtual bool isRemovable() = 0; - virtual bool isCustom() = 0; - virtual bool isEditable() = 0; - virtual bool isVersionChangeable() = 0; - - virtual void setOrder(int order) = 0; - virtual int getOrder() = 0; - - virtual QString getID() = 0; - virtual QString getName() = 0; - virtual QString getVersion() = 0; - virtual QDateTime getReleaseDateTime() = 0; - - virtual QString getFilename() = 0; - - virtual VersionSource getVersionSource() = 0; - - virtual std::shared_ptr<class VersionFile> getVersionFile() = 0; - - virtual const QList<PatchProblem>& getProblems() - { - return m_problems; - } - virtual void addProblem(ProblemSeverity severity, const QString &description) - { - if(severity > m_problemSeverity) - { - m_problemSeverity = severity; - } - m_problems.append(PatchProblem(severity, description)); - } - virtual ProblemSeverity getProblemSeverity() - { - return m_problemSeverity; - } - virtual bool hasFailed() - { - return getProblemSeverity() == PROBLEM_ERROR; - } - -protected: - QList<PatchProblem> m_problems; - ProblemSeverity m_problemSeverity = PROBLEM_NONE; -}; - -typedef std::shared_ptr<ProfilePatch> ProfilePatchPtr; diff --git a/libraries/logic/minecraft/ProfileStrategy.h b/libraries/logic/minecraft/ProfileStrategy.h deleted file mode 100644 index b4dfc4b3..00000000 --- a/libraries/logic/minecraft/ProfileStrategy.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "ProfileUtils.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; - - /// 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/libraries/logic/minecraft/ProfileUtils.cpp b/libraries/logic/minecraft/ProfileUtils.cpp deleted file mode 100644 index ef9b3b28..00000000 --- a/libraries/logic/minecraft/ProfileUtils.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include "ProfileUtils.h" -#include "minecraft/VersionFilterData.h" -#include "minecraft/onesix/OneSixVersionFormat.h" -#include "Json.h" -#include <QDebug> - -#include <QJsonDocument> -#include <QJsonArray> -#include <QRegularExpression> -#include <QSaveFile> - -namespace ProfileUtils -{ - -static const int currentOrderFileVersion = 1; - -bool writeOverrideOrders(QString path, const PatchOrder &order) -{ - QJsonObject obj; - obj.insert("version", currentOrderFileVersion); - QJsonArray orderArray; - for(auto str: order) - { - orderArray.append(str); - } - obj.insert("order", orderArray); - QSaveFile orderFile(path); - if (!orderFile.open(QFile::WriteOnly)) - { - qCritical() << "Couldn't open" << orderFile.fileName() - << "for writing:" << orderFile.errorString(); - return false; - } - auto data = QJsonDocument(obj).toJson(QJsonDocument::Indented); - if(orderFile.write(data) != data.size()) - { - qCritical() << "Couldn't write all the data into" << orderFile.fileName() - << "because:" << orderFile.errorString(); - return false; - } - if(!orderFile.commit()) - { - qCritical() << "Couldn't save" << orderFile.fileName() - << "because:" << orderFile.errorString(); - } - return true; -} - -bool readOverrideOrders(QString path, PatchOrder &order) -{ - QFile orderFile(path); - if (!orderFile.exists()) - { - qWarning() << "Order file doesn't exist. Ignoring."; - return false; - } - if (!orderFile.open(QFile::ReadOnly)) - { - qCritical() << "Couldn't open" << orderFile.fileName() - << " for reading:" << orderFile.errorString(); - qWarning() << "Ignoring overriden order"; - return false; - } - - // and it's valid JSON - QJsonParseError error; - QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll(), &error); - if (error.error != QJsonParseError::NoError) - { - qCritical() << "Couldn't parse" << orderFile.fileName() << ":" << error.errorString(); - qWarning() << "Ignoring overriden order"; - return false; - } - - // and then read it and process it if all above is true. - try - { - auto obj = Json::requireObject(doc); - // check order file version. - auto version = Json::requireInteger(obj.value("version")); - if (version != currentOrderFileVersion) - { - throw JSONValidationError(QObject::tr("Invalid order file version, expected %1") - .arg(currentOrderFileVersion)); - } - auto orderArray = Json::requireArray(obj.value("order")); - for(auto item: orderArray) - { - order.append(Json::requireString(item)); - } - } - catch (JSONValidationError &err) - { - qCritical() << "Couldn't parse" << orderFile.fileName() << ": bad file format"; - qWarning() << "Ignoring overriden order"; - order.clear(); - return false; - } - return true; -} - -static VersionFilePtr createErrorVersionFile(QString fileId, QString filepath, QString error) -{ - auto outError = std::make_shared<VersionFile>(); - outError->fileId = outError->name = fileId; - outError->filename = filepath; - outError->addProblem(PROBLEM_ERROR, error); - return outError; -} - -static VersionFilePtr guardedParseJson(const QJsonDocument & doc,const QString &fileId,const QString &filepath,const bool &requireOrder) -{ - try - { - return OneSixVersionFormat::versionFileFromJson(doc, filepath, requireOrder); - } - catch (Exception & e) - { - return createErrorVersionFile(fileId, filepath, e.cause()); - } -} - -VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder) -{ - QFile file(fileInfo.absoluteFilePath()); - if (!file.open(QFile::ReadOnly)) - { - auto errorStr = QObject::tr("Unable to open the version file %1: %2.").arg(fileInfo.fileName(), file.errorString()); - return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr); - } - QJsonParseError error; - auto data = file.readAll(); - QJsonDocument doc = QJsonDocument::fromJson(data, &error); - file.close(); - if (error.error != QJsonParseError::NoError) - { - int line = 1; - int column = 0; - for(int i = 0; i < error.offset; i++) - { - if(data[i] == '\n') - { - line++; - column = 0; - continue; - } - column++; - } - auto errorStr = QObject::tr("Unable to process the version file %1: %2 at line %3 column %4.") - .arg(fileInfo.fileName(), error.errorString()) - .arg(line).arg(column); - return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr); - } - return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), requireOrder); -} - -VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo) -{ - QFile file(fileInfo.absoluteFilePath()); - if (!file.open(QFile::ReadOnly)) - { - auto errorStr = QObject::tr("Unable to open the version file %1: %2.").arg(fileInfo.fileName(), file.errorString()); - return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr); - } - QJsonDocument doc = QJsonDocument::fromBinaryData(file.readAll()); - file.close(); - if (doc.isNull()) - { - file.remove(); - throw JSONValidationError(QObject::tr("Unable to process the version file %1.").arg(fileInfo.fileName())); - } - return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), false); -} - -void removeLwjglFromPatch(VersionFilePtr patch) -{ - auto filter = [](QList<LibraryPtr>& libs) - { - QList<LibraryPtr> filteredLibs; - for (auto lib : libs) - { - if (!g_VersionFilterData.lwjglWhitelist.contains(lib->artifactPrefix())) - { - filteredLibs.append(lib); - } - } - libs = filteredLibs; - }; - filter(patch->libraries); -} -} diff --git a/libraries/logic/minecraft/ProfileUtils.h b/libraries/logic/minecraft/ProfileUtils.h deleted file mode 100644 index 267fd42b..00000000 --- a/libraries/logic/minecraft/ProfileUtils.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "Library.h" -#include "VersionFile.h" - -namespace ProfileUtils -{ -typedef QStringList PatchOrder; - -/// Read and parse a OneSix format order file -bool readOverrideOrders(QString path, PatchOrder &order); - -/// Write a OneSix format order file -bool writeOverrideOrders(QString path, const PatchOrder &order); - - -/// Parse a version file in JSON format -VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder); - -/// Parse a version file in binary JSON format -VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo); - -/// Remove LWJGL from a patch file. This is applied to all Mojang-like profile files. -void removeLwjglFromPatch(VersionFilePtr patch); - -} diff --git a/libraries/logic/minecraft/Rule.cpp b/libraries/logic/minecraft/Rule.cpp deleted file mode 100644 index c8ba297b..00000000 --- a/libraries/logic/minecraft/Rule.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* Copyright 2013-2015 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 <QJsonObject> -#include <QJsonArray> - -#include "Rule.h" - -RuleAction RuleAction_fromString(QString name) -{ - if (name == "allow") - return Allow; - if (name == "disallow") - return Disallow; - return Defer; -} - -QList<std::shared_ptr<Rule>> rulesFromJsonV4(const QJsonObject &objectWithRules) -{ - QList<std::shared_ptr<Rule>> rules; - auto rulesVal = objectWithRules.value("rules"); - if (!rulesVal.isArray()) - return rules; - - QJsonArray ruleList = rulesVal.toArray(); - for (auto ruleVal : ruleList) - { - std::shared_ptr<Rule> rule; - if (!ruleVal.isObject()) - continue; - auto ruleObj = ruleVal.toObject(); - auto actionVal = ruleObj.value("action"); - if (!actionVal.isString()) - continue; - auto action = RuleAction_fromString(actionVal.toString()); - if (action == Defer) - continue; - - auto osVal = ruleObj.value("os"); - if (!osVal.isObject()) - { - // add a new implicit action rule - rules.append(ImplicitRule::create(action)); - continue; - } - - auto osObj = osVal.toObject(); - auto osNameVal = osObj.value("name"); - if (!osNameVal.isString()) - continue; - OpSys requiredOs = OpSys_fromString(osNameVal.toString()); - QString versionRegex = osObj.value("version").toString(); - // add a new OS rule - rules.append(OsRule::create(action, requiredOs, versionRegex)); - } - return rules; -} - -QJsonObject ImplicitRule::toJson() -{ - QJsonObject ruleObj; - ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow")); - return ruleObj; -} - -QJsonObject OsRule::toJson() -{ - QJsonObject ruleObj; - ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow")); - QJsonObject osObj; - { - osObj.insert("name", OpSys_toString(m_system)); - if(!m_version_regexp.isEmpty()) - { - osObj.insert("version", m_version_regexp); - } - } - ruleObj.insert("os", osObj); - return ruleObj; -} - diff --git a/libraries/logic/minecraft/Rule.h b/libraries/logic/minecraft/Rule.h deleted file mode 100644 index c8bf6eaa..00000000 --- a/libraries/logic/minecraft/Rule.h +++ /dev/null @@ -1,101 +0,0 @@ -/* Copyright 2013-2015 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 <QString> -#include <QList> -#include <QJsonObject> -#include <memory> -#include "OpSys.h" - -class Library; -class Rule; - -enum RuleAction -{ - Allow, - Disallow, - Defer -}; - -QList<std::shared_ptr<Rule>> rulesFromJsonV4(const QJsonObject &objectWithRules); - -class Rule -{ -protected: - RuleAction m_result; - virtual bool applies(const Library *parent) = 0; - -public: - Rule(RuleAction result) : m_result(result) - { - } - virtual ~Rule() {}; - virtual QJsonObject toJson() = 0; - RuleAction apply(const Library *parent) - { - if (applies(parent)) - return m_result; - else - return Defer; - } -}; - -class OsRule : public Rule -{ -private: - // the OS - OpSys m_system; - // the OS version regexp - QString m_version_regexp; - -protected: - virtual bool applies(const Library *) - { - return (m_system == currentSystem); - } - OsRule(RuleAction result, OpSys system, QString version_regexp) - : Rule(result), m_system(system), m_version_regexp(version_regexp) - { - } - -public: - virtual QJsonObject toJson(); - static std::shared_ptr<OsRule> create(RuleAction result, OpSys system, - QString version_regexp) - { - return std::shared_ptr<OsRule>(new OsRule(result, system, version_regexp)); - } -}; - -class ImplicitRule : public Rule -{ -protected: - virtual bool applies(const Library *) - { - return true; - } - ImplicitRule(RuleAction result) : Rule(result) - { - } - -public: - virtual QJsonObject toJson(); - static std::shared_ptr<ImplicitRule> create(RuleAction result) - { - return std::shared_ptr<ImplicitRule>(new ImplicitRule(result)); - } -}; diff --git a/libraries/logic/minecraft/VersionBuildError.h b/libraries/logic/minecraft/VersionBuildError.h deleted file mode 100644 index fda453e5..00000000 --- a/libraries/logic/minecraft/VersionBuildError.h +++ /dev/null @@ -1,58 +0,0 @@ -#include "Exception.h" - -class VersionBuildError : public Exception -{ -public: - explicit VersionBuildError(QString cause) : Exception(cause) {} - virtual ~VersionBuildError() noexcept - { - } -}; - -/** - * the base version file was meant for a newer version of the vanilla launcher than we support - */ -class LauncherVersionError : public VersionBuildError -{ -public: - LauncherVersionError(int actual, int supported) - : VersionBuildError(QObject::tr( - "The base version file of this instance was meant for a newer (%1) " - "version of the vanilla launcher than this version of MultiMC supports (%2).") - .arg(actual) - .arg(supported)) {}; - virtual ~LauncherVersionError() noexcept - { - } -}; - -/** - * some patch was intended for a different version of minecraft - */ -class MinecraftVersionMismatch : public VersionBuildError -{ -public: - MinecraftVersionMismatch(QString fileId, QString mcVersion, QString parentMcVersion) - : VersionBuildError(QObject::tr("The patch %1 is for a different version of Minecraft " - "(%2) than that of the instance (%3).") - .arg(fileId) - .arg(mcVersion) - .arg(parentMcVersion)) {}; - virtual ~MinecraftVersionMismatch() noexcept - { - } -}; - -/** - * files required for the version are not (yet?) present - */ -class VersionIncomplete : public VersionBuildError -{ -public: - VersionIncomplete(QString missingPatch) - : VersionBuildError(QObject::tr("Version is incomplete: missing %1.") - .arg(missingPatch)) {}; - virtual ~VersionIncomplete() noexcept - { - } -}; diff --git a/libraries/logic/minecraft/VersionFile.cpp b/libraries/logic/minecraft/VersionFile.cpp deleted file mode 100644 index 573c4cb4..00000000 --- a/libraries/logic/minecraft/VersionFile.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include <QJsonArray> -#include <QJsonDocument> - -#include <QDebug> - -#include "minecraft/VersionFile.h" -#include "minecraft/Library.h" -#include "minecraft/MinecraftProfile.h" -#include "minecraft/JarMod.h" -#include "ParseUtils.h" - -#include "VersionBuildError.h" -#include <Version.h> - -bool VersionFile::isMinecraftVersion() -{ - return fileId == "net.minecraft"; -} - -bool VersionFile::hasJarMods() -{ - return !jarMods.isEmpty(); -} - -void VersionFile::applyTo(MinecraftProfile *profile) -{ - auto theirVersion = profile->getMinecraftVersion(); - if (!theirVersion.isNull() && !dependsOnMinecraftVersion.isNull()) - { - if (QRegExp(dependsOnMinecraftVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(theirVersion) == -1) - { - throw MinecraftVersionMismatch(fileId, dependsOnMinecraftVersion, theirVersion); - } - } - profile->applyMinecraftVersion(minecraftVersion); - profile->applyMainClass(mainClass); - profile->applyAppletClass(appletClass); - profile->applyMinecraftArguments(minecraftArguments); - if (isMinecraftVersion()) - { - profile->applyMinecraftVersionType(type); - } - profile->applyMinecraftAssets(mojangAssetIndex); - profile->applyTweakers(addTweakers); - - profile->applyJarMods(jarMods); - profile->applyTraits(traits); - - for (auto library : libraries) - { - profile->applyLibrary(library); - } - profile->applyProblemSeverity(getProblemSeverity()); - auto iter = mojangDownloads.begin(); - while(iter != mojangDownloads.end()) - { - profile->applyMojangDownload(iter.key(), iter.value()); - iter++; - } -} diff --git a/libraries/logic/minecraft/VersionFile.h b/libraries/logic/minecraft/VersionFile.h deleted file mode 100644 index 1b692f0f..00000000 --- a/libraries/logic/minecraft/VersionFile.h +++ /dev/null @@ -1,195 +0,0 @@ -#pragma once - -#include <QString> -#include <QStringList> -#include <QDateTime> -#include <QSet> - -#include <memory> -#include "minecraft/OpSys.h" -#include "minecraft/Rule.h" -#include "ProfilePatch.h" -#include "Library.h" -#include "JarMod.h" - -class MinecraftProfile; -class VersionFile; -struct MojangDownloadInfo; -struct MojangAssetIndexInfo; - -typedef std::shared_ptr<VersionFile> VersionFilePtr; -class VersionFile : public ProfilePatch -{ - friend class MojangVersionFormat; - friend class OneSixVersionFormat; -public: /* methods */ - virtual void applyTo(MinecraftProfile *profile) override; - virtual bool isMinecraftVersion() override; - virtual bool hasJarMods() override; - virtual int getOrder() override - { - return order; - } - virtual void setOrder(int order) override - { - this->order = order; - } - virtual QList<JarmodPtr> getJarMods() override - { - return jarMods; - } - virtual QString getID() override - { - return fileId; - } - virtual QString getName() override - { - return name; - } - virtual QString getVersion() override - { - return version; - } - virtual QString getFilename() override - { - return filename; - } - virtual QDateTime getReleaseDateTime() override - { - return m_releaseTime; - } - VersionSource getVersionSource() override - { - return Local; - } - - std::shared_ptr<class VersionFile> getVersionFile() override - { - return std::dynamic_pointer_cast<VersionFile>(shared_from_this()); - } - - virtual bool isCustom() override - { - return !m_isVanilla; - }; - virtual bool isCustomizable() override - { - return m_isCustomizable; - } - virtual bool isRemovable() override - { - return m_isRemovable; - } - virtual bool isRevertible() override - { - return m_isRevertible; - } - virtual bool isMoveable() override - { - return m_isMovable; - } - virtual bool isEditable() override - { - return isCustom(); - } - virtual bool isVersionChangeable() override - { - return false; - } - - void setVanilla (bool state) - { - m_isVanilla = state; - } - void setRemovable (bool state) - { - m_isRemovable = state; - } - void setRevertible (bool state) - { - m_isRevertible = state; - } - void setCustomizable (bool state) - { - m_isCustomizable = state; - } - void setMovable (bool state) - { - m_isMovable = state; - } - - -public: /* data */ - /// MultiMC: order hint for this version file if no explicit order is set - int order = 0; - - // Flags for UI and version file manipulation in general - bool m_isVanilla = false; - bool m_isRemovable = false; - bool m_isRevertible = false; - bool m_isCustomizable = false; - bool m_isMovable = false; - - /// MultiMC: filename of the file this was loaded from - QString filename; - - /// MultiMC: human readable name of this package - QString name; - - /// MultiMC: package ID of this package - QString fileId; - - /// MultiMC: version of this package - QString version; - - /// MultiMC: dependency on a Minecraft version - QString dependsOnMinecraftVersion; - - /// Mojang: used to version the Mojang version format - int minimumLauncherVersion = -1; - - /// Mojang: version of Minecraft this is - QString minecraftVersion; - - /// Mojang: class to launch Minecraft with - QString mainClass; - - /// MultiMC: DEPRECATED class to launch legacy Minecraft with (ambed in a custom window) - QString appletClass; - - /// Mojang: Minecraft launch arguments (may contain placeholders for variable substitution) - QString minecraftArguments; - - /// Mojang: type of the Minecraft version - QString type; - - /// Mojang: the time this version was actually released by Mojang - QDateTime m_releaseTime; - - /// Mojang: the time this version was last updated by Mojang - QDateTime m_updateTime; - - /// Mojang: DEPRECATED asset group to be used with Minecraft - QString assets; - - /// MultiMC: list of tweaker mod arguments for launchwrapper - QStringList addTweakers; - - /// Mojang: list of libraries to add to the version - QList<LibraryPtr> libraries; - - /// MultiMC: list of attached traits of this version file - used to enable features - QSet<QString> traits; - - /// MultiMC: list of jar mods added to this version - QList<JarmodPtr> jarMods; - -public: - // Mojang: list of 'downloads' - client jar, server jar, windows server exe, maybe more. - QMap <QString, std::shared_ptr<MojangDownloadInfo>> mojangDownloads; - - // Mojang: extended asset index download information - std::shared_ptr<MojangAssetIndexInfo> mojangAssetIndex; -}; - - diff --git a/libraries/logic/minecraft/VersionFilterData.cpp b/libraries/logic/minecraft/VersionFilterData.cpp deleted file mode 100644 index 0c4a6e3d..00000000 --- a/libraries/logic/minecraft/VersionFilterData.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "VersionFilterData.h" -#include "ParseUtils.h" - -VersionFilterData g_VersionFilterData = VersionFilterData(); - -VersionFilterData::VersionFilterData() -{ - // 1.3.* - auto libs13 = - QList<FMLlib>{{"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false}, - {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false}, - {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false}}; - - fmlLibsMapping["1.3.2"] = libs13; - - // 1.4.* - auto libs14 = QList<FMLlib>{ - {"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false}, - {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false}, - {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false}, - {"bcprov-jdk15on-147.jar", "b6f5d9926b0afbde9f4dbe3db88c5247be7794bb", false}}; - - fmlLibsMapping["1.4"] = libs14; - fmlLibsMapping["1.4.1"] = libs14; - fmlLibsMapping["1.4.2"] = libs14; - fmlLibsMapping["1.4.3"] = libs14; - fmlLibsMapping["1.4.4"] = libs14; - fmlLibsMapping["1.4.5"] = libs14; - fmlLibsMapping["1.4.6"] = libs14; - fmlLibsMapping["1.4.7"] = libs14; - - // 1.5 - fmlLibsMapping["1.5"] = QList<FMLlib>{ - {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false}, - {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false}, - {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false}, - {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true}, - {"deobfuscation_data_1.5.zip", "5f7c142d53776f16304c0bbe10542014abad6af8", false}, - {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}}; - - // 1.5.1 - fmlLibsMapping["1.5.1"] = QList<FMLlib>{ - {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false}, - {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false}, - {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false}, - {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true}, - {"deobfuscation_data_1.5.1.zip", "22e221a0d89516c1f721d6cab056a7e37471d0a6", false}, - {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}}; - - // 1.5.2 - fmlLibsMapping["1.5.2"] = QList<FMLlib>{ - {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false}, - {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false}, - {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false}, - {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true}, - {"deobfuscation_data_1.5.2.zip", "446e55cd986582c70fcf12cb27bc00114c5adfd9", false}, - {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}}; - - // don't use installers for those. - forgeInstallerBlacklist = QSet<QString>({"1.5.2"}); - // these won't show up in version lists because they are extremely bad and dangerous - legacyBlacklist = QSet<QString>({"rd-160052"}); - /* - * nothing older than this will be accepted from Mojang servers - * (these versions need to be tested by us first) - */ - legacyCutoffDate = timeFromS3Time("2013-06-25T15:08:56+02:00"); - lwjglWhitelist = - QSet<QString>{"net.java.jinput:jinput", "net.java.jinput:jinput-platform", - "net.java.jutils:jutils", "org.lwjgl.lwjgl:lwjgl", - "org.lwjgl.lwjgl:lwjgl_util", "org.lwjgl.lwjgl:lwjgl-platform"}; - - // Version list magic - recommendedMinecraftVersion = "1.7.10"; -} diff --git a/libraries/logic/minecraft/VersionFilterData.h b/libraries/logic/minecraft/VersionFilterData.h deleted file mode 100644 index f7d4ebe7..00000000 --- a/libraries/logic/minecraft/VersionFilterData.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -#include <QMap> -#include <QString> -#include <QSet> -#include <QDateTime> - -#include "multimc_logic_export.h" - -struct FMLlib -{ - QString filename; - QString checksum; - bool ours; -}; - -struct VersionFilterData -{ - VersionFilterData(); - // mapping between minecraft versions and FML libraries required - QMap<QString, QList<FMLlib>> fmlLibsMapping; - // set of minecraft versions for which using forge installers is blacklisted - QSet<QString> forgeInstallerBlacklist; - // set of 'legacy' versions that will not show up in the version lists. - QSet<QString> legacyBlacklist; - // no new versions below this date will be accepted from Mojang servers - QDateTime legacyCutoffDate; - // Libraries that belong to LWJGL - QSet<QString> lwjglWhitelist; - // Currently recommended minecraft version - QString recommendedMinecraftVersion; -}; -extern VersionFilterData MULTIMC_LOGIC_EXPORT g_VersionFilterData; diff --git a/libraries/logic/minecraft/World.cpp b/libraries/logic/minecraft/World.cpp deleted file mode 100644 index 6081a8ec..00000000 --- a/libraries/logic/minecraft/World.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/* Copyright 2015 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 <QDir> -#include <QString> -#include <QDebug> -#include <QSaveFile> -#include "World.h" - -#include "GZip.h" -#include <MMCZip.h> -#include <FileSystem.h> -#include <sstream> -#include <io/stream_reader.h> -#include <tag_string.h> -#include <tag_primitive.h> -#include <quazip.h> -#include <quazipfile.h> -#include <quazipdir.h> - -std::unique_ptr <nbt::tag_compound> parseLevelDat(QByteArray data) -{ - QByteArray output; - if(!GZip::unzip(data, output)) - { - return nullptr; - } - std::istringstream foo(std::string(output.constData(), output.size())); - auto pair = nbt::io::read_compound(foo); - - if(pair.first != "") - return nullptr; - - if(pair.second == nullptr) - return nullptr; - - return std::move(pair.second); -} - -QByteArray serializeLevelDat(nbt::tag_compound * levelInfo) -{ - std::ostringstream s; - nbt::io::write_tag("", *levelInfo, s); - QByteArray val( s.str().data(), (int) s.str().size() ); - return val; -} - -QString getLevelDatFromFS(const QFileInfo &file) -{ - QDir worldDir(file.filePath()); - if(!file.isDir() || !worldDir.exists("level.dat")) - { - return QString(); - } - return worldDir.absoluteFilePath("level.dat"); -} - -QByteArray getLevelDatDataFromFS(const QFileInfo &file) -{ - auto fullFilePath = getLevelDatFromFS(file); - if(fullFilePath.isNull()) - { - return QByteArray(); - } - QFile f(fullFilePath); - if(!f.open(QIODevice::ReadOnly)) - { - return QByteArray(); - } - return f.readAll(); -} - -bool putLevelDatDataToFS(const QFileInfo &file, QByteArray & data) -{ - auto fullFilePath = getLevelDatFromFS(file); - if(fullFilePath.isNull()) - { - return false; - } - QSaveFile f(fullFilePath); - if(!f.open(QIODevice::WriteOnly)) - { - return false; - } - QByteArray compressed; - if(!GZip::zip(data, compressed)) - { - return false; - } - if(f.write(compressed) != compressed.size()) - { - f.cancelWriting(); - return false; - } - return f.commit(); -} - -World::World(const QFileInfo &file) -{ - repath(file); -} - -void World::repath(const QFileInfo &file) -{ - m_containerFile = file; - m_folderName = file.fileName(); - if(file.isFile() && file.suffix() == "zip") - { - readFromZip(file); - } - else if(file.isDir()) - { - readFromFS(file); - } -} - -void World::readFromFS(const QFileInfo &file) -{ - auto bytes = getLevelDatDataFromFS(file); - if(bytes.isEmpty()) - { - is_valid = false; - return; - } - loadFromLevelDat(bytes); - levelDatTime = file.lastModified(); -} - -void World::readFromZip(const QFileInfo &file) -{ - QuaZip zip(file.absoluteFilePath()); - is_valid = zip.open(QuaZip::mdUnzip); - if (!is_valid) - { - return; - } - auto location = MMCZip::findFileInZip(&zip, "level.dat"); - is_valid = !location.isEmpty(); - if (!is_valid) - { - return; - } - m_containerOffsetPath = location; - QuaZipFile zippedFile(&zip); - // read the install profile - is_valid = zip.setCurrentFile(location + "level.dat"); - if (!is_valid) - { - return; - } - is_valid = zippedFile.open(QIODevice::ReadOnly); - QuaZipFileInfo64 levelDatInfo; - zippedFile.getFileInfo(&levelDatInfo); - auto modTime = levelDatInfo.getNTFSmTime(); - if(!modTime.isValid()) - { - modTime = levelDatInfo.dateTime; - } - levelDatTime = modTime; - if (!is_valid) - { - return; - } - loadFromLevelDat(zippedFile.readAll()); - zippedFile.close(); -} - -bool World::install(const QString &to, const QString &name) -{ - auto finalPath = FS::PathCombine(to, FS::DirNameFromString(m_actualName, to)); - if(!FS::ensureFolderPathExists(finalPath)) - { - return false; - } - bool ok = false; - if(m_containerFile.isFile()) - { - QuaZip zip(m_containerFile.absoluteFilePath()); - if (!zip.open(QuaZip::mdUnzip)) - { - return false; - } - ok = !MMCZip::extractSubDir(&zip, m_containerOffsetPath, finalPath).isEmpty(); - } - else if(m_containerFile.isDir()) - { - QString from = m_containerFile.filePath(); - ok = FS::copy(from, finalPath)(); - } - - if(ok && !name.isEmpty() && m_actualName != name) - { - World newWorld(finalPath); - if(newWorld.isValid()) - { - newWorld.rename(name); - } - } - return ok; -} - -bool World::rename(const QString &newName) -{ - if(m_containerFile.isFile()) - { - return false; - } - - auto data = getLevelDatDataFromFS(m_containerFile); - if(data.isEmpty()) - { - return false; - } - - auto worldData = parseLevelDat(data); - if(!worldData) - { - return false; - } - auto &val = worldData->at("Data"); - if(val.get_type() != nbt::tag_type::Compound) - { - return false; - } - auto &dataCompound = val.as<nbt::tag_compound>(); - dataCompound.put("LevelName", nbt::value_initializer(newName.toUtf8().data())); - data = serializeLevelDat(worldData.get()); - - putLevelDatDataToFS(m_containerFile, data); - - m_actualName = newName; - - QDir parentDir(m_containerFile.absoluteFilePath()); - parentDir.cdUp(); - QFile container(m_containerFile.absoluteFilePath()); - auto dirName = FS::DirNameFromString(m_actualName, parentDir.absolutePath()); - container.rename(parentDir.absoluteFilePath(dirName)); - - return true; -} - -static QString read_string (nbt::value& parent, const char * name, const QString & fallback = QString()) -{ - try - { - auto &namedValue = parent.at(name); - if(namedValue.get_type() != nbt::tag_type::String) - { - return fallback; - } - auto & tag_str = namedValue.as<nbt::tag_string>(); - return QString::fromStdString(tag_str.get()); - } - catch(std::out_of_range e) - { - // fallback for old world formats - qWarning() << "String NBT tag" << name << "could not be found. Defaulting to" << fallback; - return fallback; - } - catch(std::bad_cast e) - { - // type mismatch - qWarning() << "NBT tag" << name << "could not be converted to string. Defaulting to" << fallback; - return fallback; - } -}; - -static int64_t read_long (nbt::value& parent, const char * name, const int64_t & fallback = 0) -{ - try - { - auto &namedValue = parent.at(name); - if(namedValue.get_type() != nbt::tag_type::Long) - { - return fallback; - } - auto & tag_str = namedValue.as<nbt::tag_long>(); - return tag_str.get(); - } - catch(std::out_of_range e) - { - // fallback for old world formats - qWarning() << "Long NBT tag" << name << "could not be found. Defaulting to" << fallback; - return fallback; - } - catch(std::bad_cast e) - { - // type mismatch - qWarning() << "NBT tag" << name << "could not be converted to long. Defaulting to" << fallback; - return fallback; - } -}; - -void World::loadFromLevelDat(QByteArray data) -{ - try - { - auto levelData = parseLevelDat(data); - if(!levelData) - { - is_valid = false; - return; - } - - auto &val = levelData->at("Data"); - is_valid = val.get_type() == nbt::tag_type::Compound; - if(!is_valid) - return; - - m_actualName = read_string(val, "LevelName", m_folderName); - - - int64_t temp = read_long(val, "LastPlayed", 0); - if(temp == 0) - { - m_lastPlayed = levelDatTime; - } - else - { - m_lastPlayed = QDateTime::fromMSecsSinceEpoch(temp); - } - - m_randomSeed = read_long(val, "RandomSeed", 0); - - qDebug() << "World Name:" << m_actualName; - qDebug() << "Last Played:" << m_lastPlayed.toString(); - qDebug() << "Seed:" << m_randomSeed; - } - catch (nbt::io::input_error e) - { - qWarning() << "Unable to load" << m_folderName << ":" << e.what(); - is_valid = false; - return; - } -} - -bool World::replace(World &with) -{ - if (!destroy()) - return false; - bool success = FS::copy(with.m_containerFile.filePath(), m_containerFile.path())(); - if (success) - { - m_folderName = with.m_folderName; - m_containerFile.refresh(); - } - return success; -} - -bool World::destroy() -{ - if(!is_valid) return false; - if (m_containerFile.isDir()) - { - QDir d(m_containerFile.filePath()); - return d.removeRecursively(); - } - else if(m_containerFile.isFile()) - { - QFile file(m_containerFile.absoluteFilePath()); - return file.remove(); - } - return true; -} - -bool World::operator==(const World &other) const -{ - return is_valid == other.is_valid && folderName() == other.folderName(); -} -bool World::strongCompare(const World &other) const -{ - return is_valid == other.is_valid && folderName() == other.folderName(); -} diff --git a/libraries/logic/minecraft/World.h b/libraries/logic/minecraft/World.h deleted file mode 100644 index 3cde5ea4..00000000 --- a/libraries/logic/minecraft/World.h +++ /dev/null @@ -1,83 +0,0 @@ -/* Copyright 2015 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 <QFileInfo> -#include <QDateTime> - -#include "multimc_logic_export.h" - -class MULTIMC_LOGIC_EXPORT World -{ -public: - World(const QFileInfo &file); - QString folderName() const - { - return m_folderName; - } - QString name() const - { - return m_actualName; - } - QDateTime lastPlayed() const - { - return m_lastPlayed; - } - int64_t seed() const - { - return m_randomSeed; - } - bool isValid() const - { - return is_valid; - } - bool isOnFS() const - { - return m_containerFile.isDir(); - } - QFileInfo container() const - { - return m_containerFile; - } - // delete all the files of this world - bool destroy(); - // replace this world with a copy of the other - bool replace(World &with); - // change the world's filesystem path (used by world lists for *MAGIC* purposes) - void repath(const QFileInfo &file); - - bool rename(const QString &to); - bool install(const QString &to, const QString &name= QString()); - - // WEAK compare operator - used for replacing worlds - bool operator==(const World &other) const; - bool strongCompare(const World &other) const; - -private: - void readFromZip(const QFileInfo &file); - void readFromFS(const QFileInfo &file); - void loadFromLevelDat(QByteArray data); - -protected: - - QFileInfo m_containerFile; - QString m_containerOffsetPath; - QString m_folderName; - QString m_actualName; - QDateTime levelDatTime; - QDateTime m_lastPlayed; - int64_t m_randomSeed = 0; - bool is_valid = false; -}; diff --git a/libraries/logic/minecraft/WorldList.cpp b/libraries/logic/minecraft/WorldList.cpp deleted file mode 100644 index 42c8a3e6..00000000 --- a/libraries/logic/minecraft/WorldList.cpp +++ /dev/null @@ -1,355 +0,0 @@ -/* Copyright 2015 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 "WorldList.h" -#include <FileSystem.h> -#include <QMimeData> -#include <QUrl> -#include <QUuid> -#include <QString> -#include <QFileSystemWatcher> -#include <QDebug> - -WorldList::WorldList(const QString &dir) - : QAbstractListModel(), m_dir(dir) -{ - 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_watcher = new QFileSystemWatcher(this); - is_watching = false; - connect(m_watcher, SIGNAL(directoryChanged(QString)), this, - SLOT(directoryChanged(QString))); -} - -void WorldList::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 WorldList::stopWatching() -{ - is_watching = !m_watcher->removePath(m_dir.absolutePath()); - if (!is_watching) - { - qDebug() << "Stopped watching " << m_dir.absolutePath(); - } - else - { - qDebug() << "Failed to stop watching " << m_dir.absolutePath(); - } -} - -bool WorldList::update() -{ - if (!isValid()) - return false; - - QList<World> newWorlds; - m_dir.refresh(); - auto folderContents = m_dir.entryInfoList(); - // if there are any untracked files... - for (QFileInfo entry : folderContents) - { - if(!entry.isDir()) - continue; - - World w(entry); - if(w.isValid()) - { - newWorlds.append(w); - } - } - beginResetModel(); - worlds.swap(newWorlds); - endResetModel(); - return true; -} - -void WorldList::directoryChanged(QString path) -{ - update(); -} - -bool WorldList::isValid() -{ - return m_dir.exists() && m_dir.isReadable(); -} - -bool WorldList::deleteWorld(int index) -{ - if (index >= worlds.size() || index < 0) - return false; - World &m = worlds[index]; - if (m.destroy()) - { - beginRemoveRows(QModelIndex(), index, index); - worlds.removeAt(index); - endRemoveRows(); - emit changed(); - return true; - } - return false; -} - -bool WorldList::deleteWorlds(int first, int last) -{ - for (int i = first; i <= last; i++) - { - World &m = worlds[i]; - m.destroy(); - } - beginRemoveRows(QModelIndex(), first, last); - worlds.erase(worlds.begin() + first, worlds.begin() + last + 1); - endRemoveRows(); - emit changed(); - return true; -} - -int WorldList::columnCount(const QModelIndex &parent) const -{ - return 2; -} - -QVariant WorldList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - int row = index.row(); - int column = index.column(); - - if (row < 0 || row >= worlds.size()) - return QVariant(); - - auto & world = worlds[row]; - switch (role) - { - case Qt::DisplayRole: - switch (column) - { - case NameColumn: - return world.name(); - - case LastPlayedColumn: - return world.lastPlayed(); - - default: - return QVariant(); - } - - case Qt::ToolTipRole: - { - return world.folderName(); - } - case ObjectRole: - { - return QVariant::fromValue<void *>((void *)&world); - } - case FolderRole: - { - return QDir::toNativeSeparators(dir().absoluteFilePath(world.folderName())); - } - case SeedRole: - { - return qVariantFromValue<qlonglong>(world.seed()); - } - case NameRole: - { - return world.name(); - } - case LastPlayedRole: - { - return world.lastPlayed(); - } - default: - return QVariant(); - } -} - -QVariant WorldList::headerData(int section, Qt::Orientation orientation, int role) const -{ - switch (role) - { - case Qt::DisplayRole: - switch (section) - { - case NameColumn: - return tr("Name"); - case LastPlayedColumn: - return tr("Last Played"); - default: - return QVariant(); - } - - case Qt::ToolTipRole: - switch (section) - { - case NameColumn: - return tr("The name of the world."); - case LastPlayedColumn: - return tr("Date and time the world was last played."); - default: - return QVariant(); - } - default: - return QVariant(); - } - return QVariant(); -} - -QStringList WorldList::mimeTypes() const -{ - QStringList types; - types << "text/uri-list"; - return types; -} - -class WorldMimeData : public QMimeData -{ -Q_OBJECT - -public: - WorldMimeData(QList<World> worlds) - { - m_worlds = worlds; - - } - QStringList formats() const - { - return QMimeData::formats() << "text/uri-list"; - } - -protected: - QVariant retrieveData(const QString &mimetype, QVariant::Type type) const - { - QList<QUrl> urls; - for(auto &world: m_worlds) - { - if(!world.isValid() || !world.isOnFS()) - continue; - QString worldPath = world.container().absoluteFilePath(); - qDebug() << worldPath; - urls.append(QUrl::fromLocalFile(worldPath)); - } - const_cast<WorldMimeData*>(this)->setUrls(urls); - return QMimeData::retrieveData(mimetype, type); - } -private: - QList<World> m_worlds; -}; - -QMimeData *WorldList::mimeData(const QModelIndexList &indexes) const -{ - if (indexes.size() == 0) - return new QMimeData(); - - QList<World> worlds; - for(auto idx : indexes) - { - if(idx.column() != 0) - continue; - int row = idx.row(); - if (row < 0 || row >= this->worlds.size()) - continue; - worlds.append(this->worlds[row]); - } - if(!worlds.size()) - { - return new QMimeData(); - } - return new WorldMimeData(worlds); -} - -Qt::ItemFlags WorldList::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; -} - -Qt::DropActions WorldList::supportedDragActions() const -{ - // move to other mod lists or VOID - return Qt::MoveAction; -} - -Qt::DropActions WorldList::supportedDropActions() const -{ - // copy from outside, move from within and other mod lists - return Qt::CopyAction | Qt::MoveAction; -} - -void WorldList::installWorld(QFileInfo filename) -{ - qDebug() << "installing: " << filename.absoluteFilePath(); - World w(filename); - if(!w.isValid()) - { - return; - } - w.install(m_dir.absolutePath()); -} - -bool WorldList::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; - // 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(); - - QFileInfo worldInfo(filename); - - if(!m_dir.entryInfoList().contains(worldInfo)) - { - installWorld(worldInfo); - } - } - if (was_watching) - startWatching(); - return true; - } - return false; -} - -#include "WorldList.moc" diff --git a/libraries/logic/minecraft/WorldList.h b/libraries/logic/minecraft/WorldList.h deleted file mode 100644 index 34b30e9c..00000000 --- a/libraries/logic/minecraft/WorldList.h +++ /dev/null @@ -1,125 +0,0 @@ -/* Copyright 2015 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <QList> -#include <QString> -#include <QDir> -#include <QAbstractListModel> -#include <QMimeData> -#include "minecraft/World.h" - -#include "multimc_logic_export.h" - -class QFileSystemWatcher; - -class MULTIMC_LOGIC_EXPORT WorldList : public QAbstractListModel -{ - Q_OBJECT -public: - enum Columns - { - NameColumn, - LastPlayedColumn - }; - - enum Roles - { - ObjectRole = Qt::UserRole + 1, - FolderRole, - SeedRole, - NameRole, - LastPlayedRole - }; - - WorldList(const QString &dir); - - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - - 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 worlds.size(); - }; - bool empty() const - { - return size() == 0; - } - World &operator[](size_t index) - { - return worlds[index]; - } - - /// Reloads the mod list and returns true if the list changed. - virtual bool update(); - - /// Install a world from location - void installWorld(QFileInfo filename); - - /// Deletes the mod at the given index. - virtual bool deleteWorld(int index); - - /// Deletes all the selected mods - virtual bool deleteWorlds(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(); - - QDir dir() const - { - return m_dir; - } - - const QList<World> &allWorlds() const - { - return worlds; - } - -private slots: - void directoryChanged(QString path); - -signals: - void changed(); - -protected: - QFileSystemWatcher *m_watcher; - bool is_watching; - QDir m_dir; - QList<World> worlds; -}; diff --git a/libraries/logic/minecraft/auth/AuthSession.cpp b/libraries/logic/minecraft/auth/AuthSession.cpp deleted file mode 100644 index 8758bfbd..00000000 --- a/libraries/logic/minecraft/auth/AuthSession.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "AuthSession.h" -#include <QJsonObject> -#include <QJsonArray> -#include <QJsonDocument> -#include <QStringList> - -QString AuthSession::serializeUserProperties() -{ - QJsonObject userAttrs; - for (auto key : u.properties.keys()) - { - auto array = QJsonArray::fromStringList(u.properties.values(key)); - userAttrs.insert(key, array); - } - QJsonDocument value(userAttrs); - return value.toJson(QJsonDocument::Compact); - -} - -bool AuthSession::MakeOffline(QString offline_playername) -{ - if (status != PlayableOffline && status != PlayableOnline) - { - return false; - } - session = "-"; - player_name = offline_playername; - status = PlayableOffline; - return true; -} diff --git a/libraries/logic/minecraft/auth/AuthSession.h b/libraries/logic/minecraft/auth/AuthSession.h deleted file mode 100644 index dede90a9..00000000 --- a/libraries/logic/minecraft/auth/AuthSession.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include <QString> -#include <QMultiMap> -#include <memory> - -#include "multimc_logic_export.h" - -struct User -{ - QString id; - QMultiMap<QString, QString> properties; -}; - -struct MULTIMC_LOGIC_EXPORT AuthSession -{ - bool MakeOffline(QString offline_playername); - - QString serializeUserProperties(); - - enum Status - { - Undetermined, - RequiresPassword, - PlayableOffline, - PlayableOnline - } status = Undetermined; - - User u; - - // client token - QString client_token; - // account user name - QString username; - // combined session ID - QString session; - // volatile auth token - QString access_token; - // profile name - QString player_name; - // profile ID - QString uuid; - // 'legacy' or 'mojang', depending on account type - QString user_type; - // Did the auth server reply? - bool auth_server_online = false; - // Did the user request online mode? - bool wants_online = true; -}; - -typedef std::shared_ptr<AuthSession> AuthSessionPtr; diff --git a/libraries/logic/minecraft/auth/MojangAccount.cpp b/libraries/logic/minecraft/auth/MojangAccount.cpp deleted file mode 100644 index 69a24c09..00000000 --- a/libraries/logic/minecraft/auth/MojangAccount.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/* Copyright 2013-2015 MultiMC Contributors - * - * Authors: Orochimarufan <orochimarufan.x3@gmail.com> - * - * 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 "MojangAccount.h" -#include "flows/RefreshTask.h" -#include "flows/AuthenticateTask.h" - -#include <QUuid> -#include <QJsonObject> -#include <QJsonArray> -#include <QRegExp> -#include <QStringList> -#include <QJsonDocument> - -#include <QDebug> - -MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object) -{ - // The JSON object must at least have a username for it to be valid. - if (!object.value("username").isString()) - { - qCritical() << "Can't load Mojang account info from JSON object. Username field is " - "missing or of the wrong type."; - return nullptr; - } - - QString username = object.value("username").toString(""); - QString clientToken = object.value("clientToken").toString(""); - QString accessToken = object.value("accessToken").toString(""); - - QJsonArray profileArray = object.value("profiles").toArray(); - if (profileArray.size() < 1) - { - qCritical() << "Can't load Mojang account with username \"" << username - << "\". No profiles found."; - return nullptr; - } - - QList<AccountProfile> profiles; - for (QJsonValue profileVal : profileArray) - { - QJsonObject profileObject = profileVal.toObject(); - QString id = profileObject.value("id").toString(""); - QString name = profileObject.value("name").toString(""); - bool legacy = profileObject.value("legacy").toBool(false); - if (id.isEmpty() || name.isEmpty()) - { - qWarning() << "Unable to load a profile because it was missing an ID or a name."; - continue; - } - profiles.append({id, name, legacy}); - } - - MojangAccountPtr account(new MojangAccount()); - if (object.value("user").isObject()) - { - User u; - QJsonObject userStructure = object.value("user").toObject(); - u.id = userStructure.value("id").toString(); - /* - QJsonObject propMap = userStructure.value("properties").toObject(); - for(auto key: propMap.keys()) - { - auto values = propMap.operator[](key).toArray(); - for(auto value: values) - u.properties.insert(key, value.toString()); - } - */ - account->m_user = u; - } - account->m_username = username; - account->m_clientToken = clientToken; - account->m_accessToken = accessToken; - account->m_profiles = profiles; - - // Get the currently selected profile. - QString currentProfile = object.value("activeProfile").toString(""); - if (!currentProfile.isEmpty()) - account->setCurrentProfile(currentProfile); - - return account; -} - -MojangAccountPtr MojangAccount::createFromUsername(const QString &username) -{ - MojangAccountPtr account(new MojangAccount()); - account->m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]")); - account->m_username = username; - return account; -} - -QJsonObject MojangAccount::saveToJson() const -{ - QJsonObject json; - json.insert("username", m_username); - json.insert("clientToken", m_clientToken); - json.insert("accessToken", m_accessToken); - - QJsonArray profileArray; - for (AccountProfile profile : m_profiles) - { - QJsonObject profileObj; - profileObj.insert("id", profile.id); - profileObj.insert("name", profile.name); - profileObj.insert("legacy", profile.legacy); - profileArray.append(profileObj); - } - json.insert("profiles", profileArray); - - QJsonObject userStructure; - { - userStructure.insert("id", m_user.id); - /* - QJsonObject userAttrs; - for(auto key: m_user.properties.keys()) - { - auto array = QJsonArray::fromStringList(m_user.properties.values(key)); - userAttrs.insert(key, array); - } - userStructure.insert("properties", userAttrs); - */ - } - json.insert("user", userStructure); - - if (m_currentProfile != -1) - json.insert("activeProfile", currentProfile()->id); - - return json; -} - -bool MojangAccount::setCurrentProfile(const QString &profileId) -{ - for (int i = 0; i < m_profiles.length(); i++) - { - if (m_profiles[i].id == profileId) - { - m_currentProfile = i; - return true; - } - } - return false; -} - -const AccountProfile *MojangAccount::currentProfile() const -{ - if (m_currentProfile == -1) - return nullptr; - return &m_profiles[m_currentProfile]; -} - -AccountStatus MojangAccount::accountStatus() const -{ - if (m_accessToken.isEmpty()) - return NotVerified; - else - return Verified; -} - -std::shared_ptr<YggdrasilTask> MojangAccount::login(AuthSessionPtr session, - QString password) -{ - Q_ASSERT(m_currentTask.get() == nullptr); - - // take care of the true offline status - if (accountStatus() == NotVerified && password.isEmpty()) - { - if (session) - { - session->status = AuthSession::RequiresPassword; - fillSession(session); - } - return nullptr; - } - - if (password.isEmpty()) - { - m_currentTask.reset(new RefreshTask(this)); - } - else - { - m_currentTask.reset(new AuthenticateTask(this, password)); - } - m_currentTask->assignSession(session); - - connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); - connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); - return m_currentTask; -} - -void MojangAccount::authSucceeded() -{ - auto session = m_currentTask->getAssignedSession(); - if (session) - { - session->status = - session->wants_online ? AuthSession::PlayableOnline : AuthSession::PlayableOffline; - fillSession(session); - session->auth_server_online = true; - } - m_currentTask.reset(); - emit changed(); -} - -void MojangAccount::authFailed(QString reason) -{ - auto session = m_currentTask->getAssignedSession(); - // This is emitted when the yggdrasil tasks time out or are cancelled. - // -> we treat the error as no-op - if (m_currentTask->state() == YggdrasilTask::STATE_FAILED_SOFT) - { - if (session) - { - session->status = accountStatus() == Verified ? AuthSession::PlayableOffline - : AuthSession::RequiresPassword; - session->auth_server_online = false; - fillSession(session); - } - } - else - { - m_accessToken = QString(); - emit changed(); - if (session) - { - session->status = AuthSession::RequiresPassword; - session->auth_server_online = true; - fillSession(session); - } - } - m_currentTask.reset(); -} - -void MojangAccount::fillSession(AuthSessionPtr session) -{ - // the user name. you have to have an user name - session->username = m_username; - // volatile auth token - session->access_token = m_accessToken; - // the semi-permanent client token - session->client_token = m_clientToken; - if (currentProfile()) - { - // profile name - session->player_name = currentProfile()->name; - // profile ID - session->uuid = currentProfile()->id; - // 'legacy' or 'mojang', depending on account type - session->user_type = currentProfile()->legacy ? "legacy" : "mojang"; - if (!session->access_token.isEmpty()) - { - session->session = "token:" + m_accessToken + ":" + m_profiles[m_currentProfile].id; - } - else - { - session->session = "-"; - } - } - else - { - session->player_name = "Player"; - session->session = "-"; - } - session->u = user(); -} diff --git a/libraries/logic/minecraft/auth/MojangAccount.h b/libraries/logic/minecraft/auth/MojangAccount.h deleted file mode 100644 index 2de0c19c..00000000 --- a/libraries/logic/minecraft/auth/MojangAccount.h +++ /dev/null @@ -1,173 +0,0 @@ -/* Copyright 2013-2015 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 <QString> -#include <QList> -#include <QJsonObject> -#include <QPair> -#include <QMap> - -#include <memory> -#include "AuthSession.h" - -#include "multimc_logic_export.h" - -class Task; -class YggdrasilTask; -class MojangAccount; - -typedef std::shared_ptr<MojangAccount> MojangAccountPtr; -Q_DECLARE_METATYPE(MojangAccountPtr) - -/** - * A profile within someone's Mojang account. - * - * Currently, the profile system has not been implemented by Mojang yet, - * but we might as well add some things for it in MultiMC right now so - * we don't have to rip the code to pieces to add it later. - */ -struct AccountProfile -{ - QString id; - QString name; - bool legacy; -}; - -enum AccountStatus -{ - NotVerified, - Verified -}; - -/** - * Object that stores information about a certain Mojang account. - * - * Said information may include things such as that account's username, client token, and access - * token if the user chose to stay logged in. - */ -class MULTIMC_LOGIC_EXPORT MojangAccount : public QObject -{ - Q_OBJECT -public: /* construction */ - //! Do not copy accounts. ever. - explicit MojangAccount(const MojangAccount &other, QObject *parent) = delete; - - //! Default constructor - explicit MojangAccount(QObject *parent = 0) : QObject(parent) {}; - - //! Creates an empty account for the specified user name. - static MojangAccountPtr createFromUsername(const QString &username); - - //! Loads a MojangAccount from the given JSON object. - static MojangAccountPtr loadFromJson(const QJsonObject &json); - - //! Saves a MojangAccount to a JSON object and returns it. - QJsonObject saveToJson() const; - -public: /* manipulation */ - /** - * Sets the currently selected profile to the profile with the given ID string. - * If profileId is not in the list of available profiles, the function will simply return - * false. - */ - bool setCurrentProfile(const QString &profileId); - - /** - * Attempt to login. Empty password means we use the token. - * If the attempt fails because we already are performing some task, it returns false. - */ - std::shared_ptr<YggdrasilTask> login(AuthSessionPtr session, - QString password = QString()); - -public: /* queries */ - const QString &username() const - { - return m_username; - } - - const QString &clientToken() const - { - return m_clientToken; - } - - const QString &accessToken() const - { - return m_accessToken; - } - - const QList<AccountProfile> &profiles() const - { - return m_profiles; - } - - const User &user() - { - return m_user; - } - - //! Returns the currently selected profile (if none, returns nullptr) - const AccountProfile *currentProfile() const; - - //! Returns whether the account is NotVerified, Verified or Online - AccountStatus accountStatus() const; - -signals: - /** - * This signal is emitted when the account changes - */ - void changed(); - - // TODO: better signalling for the various possible state changes - especially errors - -protected: /* variables */ - QString m_username; - - // Used to identify the client - the user can have multiple clients for the same account - // Think: different launchers, all connecting to the same account/profile - QString m_clientToken; - - // Blank if not logged in. - QString m_accessToken; - - // Index of the selected profile within the list of available - // profiles. -1 if nothing is selected. - int m_currentProfile = -1; - - // List of available profiles. - QList<AccountProfile> m_profiles; - - // the user structure, whatever it is. - User m_user; - - // current task we are executing here - std::shared_ptr<YggdrasilTask> m_currentTask; - -private -slots: - void authSucceeded(); - void authFailed(QString reason); - -private: - void fillSession(AuthSessionPtr session); - -public: - friend class YggdrasilTask; - friend class AuthenticateTask; - friend class ValidateTask; - friend class RefreshTask; -}; diff --git a/libraries/logic/minecraft/auth/MojangAccountList.cpp b/libraries/logic/minecraft/auth/MojangAccountList.cpp deleted file mode 100644 index 26cbc81a..00000000 --- a/libraries/logic/minecraft/auth/MojangAccountList.cpp +++ /dev/null @@ -1,427 +0,0 @@ -/* Copyright 2013-2015 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 "MojangAccountList.h" -#include "MojangAccount.h" - -#include <QIODevice> -#include <QFile> -#include <QTextStream> -#include <QJsonDocument> -#include <QJsonArray> -#include <QJsonObject> -#include <QJsonParseError> -#include <QDir> - -#include <QDebug> - -#include <FileSystem.h> - -#define ACCOUNT_LIST_FORMAT_VERSION 2 - -MojangAccountList::MojangAccountList(QObject *parent) : QAbstractListModel(parent) -{ -} - -MojangAccountPtr MojangAccountList::findAccount(const QString &username) const -{ - for (int i = 0; i < count(); i++) - { - MojangAccountPtr account = at(i); - if (account->username() == username) - return account; - } - return nullptr; -} - -const MojangAccountPtr MojangAccountList::at(int i) const -{ - return MojangAccountPtr(m_accounts.at(i)); -} - -void MojangAccountList::addAccount(const MojangAccountPtr account) -{ - beginResetModel(); - connect(account.get(), SIGNAL(changed()), SLOT(accountChanged())); - m_accounts.append(account); - endResetModel(); - onListChanged(); -} - -void MojangAccountList::removeAccount(const QString &username) -{ - beginResetModel(); - for (auto account : m_accounts) - { - if (account->username() == username) - { - m_accounts.removeOne(account); - return; - } - } - endResetModel(); - onListChanged(); -} - -void MojangAccountList::removeAccount(QModelIndex index) -{ - beginResetModel(); - m_accounts.removeAt(index.row()); - endResetModel(); - onListChanged(); -} - -MojangAccountPtr MojangAccountList::activeAccount() const -{ - return m_activeAccount; -} - -void MojangAccountList::setActiveAccount(const QString &username) -{ - beginResetModel(); - if (username.isEmpty()) - { - m_activeAccount = nullptr; - } - else - { - for (MojangAccountPtr account : m_accounts) - { - if (account->username() == username) - m_activeAccount = account; - } - } - endResetModel(); - onActiveChanged(); -} - -void MojangAccountList::accountChanged() -{ - // the list changed. there is no doubt. - onListChanged(); -} - -void MojangAccountList::onListChanged() -{ - if (m_autosave) - // TODO: Alert the user if this fails. - saveList(); - - emit listChanged(); -} - -void MojangAccountList::onActiveChanged() -{ - if (m_autosave) - saveList(); - - emit activeAccountChanged(); -} - -int MojangAccountList::count() const -{ - return m_accounts.count(); -} - -QVariant MojangAccountList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - if (index.row() > count()) - return QVariant(); - - MojangAccountPtr account = at(index.row()); - - switch (role) - { - case Qt::DisplayRole: - switch (index.column()) - { - case NameColumn: - return account->username(); - - default: - return QVariant(); - } - - case Qt::ToolTipRole: - return account->username(); - - case PointerRole: - return qVariantFromValue(account); - - case Qt::CheckStateRole: - switch (index.column()) - { - case ActiveColumn: - return account == m_activeAccount; - } - - default: - return QVariant(); - } -} - -QVariant MojangAccountList::headerData(int section, Qt::Orientation orientation, int role) const -{ - switch (role) - { - case Qt::DisplayRole: - switch (section) - { - case ActiveColumn: - return tr("Active?"); - - case NameColumn: - return tr("Name"); - - default: - return QVariant(); - } - - case Qt::ToolTipRole: - switch (section) - { - case NameColumn: - return tr("The name of the version."); - - default: - return QVariant(); - } - - default: - return QVariant(); - } -} - -int MojangAccountList::rowCount(const QModelIndex &parent) const -{ - // Return count - return count(); -} - -int MojangAccountList::columnCount(const QModelIndex &parent) const -{ - return 2; -} - -Qt::ItemFlags MojangAccountList::flags(const QModelIndex &index) const -{ - if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid()) - { - return Qt::NoItemFlags; - } - - return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; -} - -bool MojangAccountList::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) - { - if(value == Qt::Checked) - { - MojangAccountPtr account = this->at(index.row()); - this->setActiveAccount(account->username()); - } - } - - emit dataChanged(index, index); - return true; -} - -void MojangAccountList::updateListData(QList<MojangAccountPtr> versions) -{ - beginResetModel(); - m_accounts = versions; - endResetModel(); -} - -bool MojangAccountList::loadList(const QString &filePath) -{ - QString path = filePath; - if (path.isEmpty()) - path = m_listFilePath; - if (path.isEmpty()) - { - qCritical() << "Can't load Mojang account list. No file path given and no default set."; - return false; - } - - QFile file(path); - - // Try to open the file and fail if we can't. - // TODO: We should probably report this error to the user. - if (!file.open(QIODevice::ReadOnly)) - { - qCritical() << QString("Failed to read the account list file (%1).").arg(path).toUtf8(); - return false; - } - - // Read the file and close it. - QByteArray jsonData = file.readAll(); - file.close(); - - QJsonParseError parseError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError); - - // Fail if the JSON is invalid. - if (parseError.error != QJsonParseError::NoError) - { - qCritical() << QString("Failed to parse account list file: %1 at offset %2") - .arg(parseError.errorString(), QString::number(parseError.offset)) - .toUtf8(); - return false; - } - - // Make sure the root is an object. - if (!jsonDoc.isObject()) - { - qCritical() << "Invalid account list JSON: Root should be an array."; - return false; - } - - QJsonObject root = jsonDoc.object(); - - // Make sure the format version matches. - if (root.value("formatVersion").toVariant().toInt() != ACCOUNT_LIST_FORMAT_VERSION) - { - QString newName = "accounts-old.json"; - qWarning() << "Format version mismatch when loading account list. Existing one will be renamed to" - << newName; - - // Attempt to rename the old version. - file.rename(newName); - return false; - } - - // Now, load the accounts array. - beginResetModel(); - QJsonArray accounts = root.value("accounts").toArray(); - for (QJsonValue accountVal : accounts) - { - QJsonObject accountObj = accountVal.toObject(); - MojangAccountPtr account = MojangAccount::loadFromJson(accountObj); - if (account.get() != nullptr) - { - connect(account.get(), SIGNAL(changed()), SLOT(accountChanged())); - m_accounts.append(account); - } - else - { - qWarning() << "Failed to load an account."; - } - } - // Load the active account. - m_activeAccount = findAccount(root.value("activeAccount").toString("")); - endResetModel(); - return true; -} - -bool MojangAccountList::saveList(const QString &filePath) -{ - QString path(filePath); - if (path.isEmpty()) - path = m_listFilePath; - if (path.isEmpty()) - { - qCritical() << "Can't save Mojang account list. No file path given and no default set."; - return false; - } - - // make sure the parent folder exists - if(!FS::ensureFilePathExists(path)) - return false; - - // make sure the file wasn't overwritten with a folder before (fixes a bug) - QFileInfo finfo(path); - if(finfo.isDir()) - { - QDir badDir(path); - badDir.removeRecursively(); - } - - qDebug() << "Writing account list to" << path; - - qDebug() << "Building JSON data structure."; - // Build the JSON document to write to the list file. - QJsonObject root; - - root.insert("formatVersion", ACCOUNT_LIST_FORMAT_VERSION); - - // Build a list of accounts. - qDebug() << "Building account array."; - QJsonArray accounts; - for (MojangAccountPtr account : m_accounts) - { - QJsonObject accountObj = account->saveToJson(); - accounts.append(accountObj); - } - - // Insert the account list into the root object. - root.insert("accounts", accounts); - - if(m_activeAccount) - { - // Save the active account. - root.insert("activeAccount", m_activeAccount->username()); - } - - // Create a JSON document object to convert our JSON to bytes. - QJsonDocument doc(root); - - // Now that we're done building the JSON object, we can write it to the file. - qDebug() << "Writing account list to file."; - QFile file(path); - - // Try to open the file and fail if we can't. - // TODO: We should probably report this error to the user. - if (!file.open(QIODevice::WriteOnly)) - { - qCritical() << QString("Failed to read the account list file (%1).").arg(path).toUtf8(); - return false; - } - - // Write the JSON to the file. - file.write(doc.toJson()); - file.setPermissions(QFile::ReadOwner|QFile::WriteOwner|QFile::ReadUser|QFile::WriteUser); - file.close(); - - qDebug() << "Saved account list to" << path; - - return true; -} - -void MojangAccountList::setListFilePath(QString path, bool autosave) -{ - m_listFilePath = path; - m_autosave = autosave; -} - -bool MojangAccountList::anyAccountIsValid() -{ - for(auto account:m_accounts) - { - if(account->accountStatus() != NotVerified) - return true; - } - return false; -} diff --git a/libraries/logic/minecraft/auth/MojangAccountList.h b/libraries/logic/minecraft/auth/MojangAccountList.h deleted file mode 100644 index c40fa6a3..00000000 --- a/libraries/logic/minecraft/auth/MojangAccountList.h +++ /dev/null @@ -1,201 +0,0 @@ -/* Copyright 2013-2015 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 "MojangAccount.h" - -#include <QObject> -#include <QVariant> -#include <QAbstractListModel> -#include <QSharedPointer> - -#include "multimc_logic_export.h" - -/*! - * \brief List of available Mojang accounts. - * This should be loaded in the background by MultiMC on startup. - * - * This class also inherits from QAbstractListModel. Methods from that - * class determine how this list shows up in a list view. Said methods - * all have a default implementation, but they can be overridden by subclasses to - * change the behavior of the list. - */ -class MULTIMC_LOGIC_EXPORT MojangAccountList : public QAbstractListModel -{ - Q_OBJECT -public: - enum ModelRoles - { - PointerRole = 0x34B1CB48 - }; - - enum VListColumns - { - // TODO: Add icon column. - - // First column - Active? - ActiveColumn = 0, - - // Second column - Name - NameColumn, - }; - - explicit MojangAccountList(QObject *parent = 0); - - //! Gets the account at the given index. - virtual const MojangAccountPtr at(int i) const; - - //! Returns the number of accounts in the list. - virtual int count() const; - - //////// List Model Functions //////// - virtual QVariant data(const QModelIndex &index, int role) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; - virtual int rowCount(const QModelIndex &parent) const; - virtual int columnCount(const QModelIndex &parent) const; - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - virtual bool setData(const QModelIndex &index, const QVariant &value, int role); - - /*! - * Adds a the given Mojang account to the account list. - */ - virtual void addAccount(const MojangAccountPtr account); - - /*! - * Removes the mojang account with the given username from the account list. - */ - virtual void removeAccount(const QString &username); - - /*! - * Removes the account at the given QModelIndex. - */ - virtual void removeAccount(QModelIndex index); - - /*! - * \brief Finds an account by its username. - * \param The username of the account to find. - * \return A const pointer to the account with the given username. NULL if - * one doesn't exist. - */ - virtual MojangAccountPtr findAccount(const QString &username) const; - - /*! - * Sets the default path to save the list file to. - * If autosave is true, this list will automatically save to the given path whenever it changes. - * THIS FUNCTION DOES NOT LOAD THE LIST. If you set autosave, be sure to call loadList() immediately - * after calling this function to ensure an autosaved change doesn't overwrite the list you intended - * to load. - */ - virtual void setListFilePath(QString path, bool autosave = false); - - /*! - * \brief Loads the account list from the given file path. - * If the given file is an empty string (default), will load from the default account list file. - * \return True if successful, otherwise false. - */ - virtual bool loadList(const QString &file = ""); - - /*! - * \brief Saves the account list to the given file. - * If the given file is an empty string (default), will save from the default account list file. - * \return True if successful, otherwise false. - */ - virtual bool saveList(const QString &file = ""); - - /*! - * \brief Gets a pointer to the account that the user has selected as their "active" account. - * Which account is active can be overridden on a per-instance basis, but this will return the one that - * is set as active globally. - * \return The currently active MojangAccount. If there isn't an active account, returns a null pointer. - */ - virtual MojangAccountPtr activeAccount() const; - - /*! - * Sets the given account as the current active account. - * If the username given is an empty string, sets the active account to nothing. - */ - virtual void setActiveAccount(const QString &username); - - /*! - * Returns true if any of the account is at least Validated - */ - bool anyAccountIsValid(); - -signals: - /*! - * Signal emitted to indicate that the account list has changed. - * This will also fire if the value of an element in the list changes (will be implemented - * later). - */ - void listChanged(); - - /*! - * Signal emitted to indicate that the active account has changed. - */ - void activeAccountChanged(); - -public -slots: - /** - * This is called when one of the accounts changes and the list needs to be updated - */ - void accountChanged(); - -protected: - /*! - * Called whenever the list changes. - * This emits the listChanged() signal and autosaves the list (if autosave is enabled). - */ - void onListChanged(); - - /*! - * Called whenever the active account changes. - * Emits the activeAccountChanged() signal and autosaves the list if enabled. - */ - void onActiveChanged(); - - QList<MojangAccountPtr> m_accounts; - - /*! - * Account that is currently active. - */ - MojangAccountPtr m_activeAccount; - - //! Path to the account list file. Empty string if there isn't one. - QString m_listFilePath; - - /*! - * If true, the account list will automatically save to the account list path when it changes. - * Ignored if m_listFilePath is blank. - */ - bool m_autosave = false; - -protected -slots: - /*! - * Updates this list with the given list of accounts. - * This is done by copying each account in the given list and inserting it - * into this one. - * We need to do this so that we can set the parents of the accounts are set to this - * account list. This can't be done in the load task, because the accounts the load - * task creates are on the load task's thread and Qt won't allow their parents - * to be set to something created on another thread. - * To get around that problem, we invoke this method on the GUI thread, which - * then copies the accounts and sets their parents correctly. - * \param accounts List of accounts whose parents should be set. - */ - virtual void updateListData(QList<MojangAccountPtr> versions); -}; diff --git a/libraries/logic/minecraft/auth/YggdrasilTask.cpp b/libraries/logic/minecraft/auth/YggdrasilTask.cpp deleted file mode 100644 index c6971c9f..00000000 --- a/libraries/logic/minecraft/auth/YggdrasilTask.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/* Copyright 2013-2015 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 "YggdrasilTask.h" -#include "MojangAccount.h" - -#include <QObject> -#include <QString> -#include <QJsonObject> -#include <QJsonDocument> -#include <QNetworkReply> -#include <QByteArray> - -#include <Env.h> - -#include <net/URLConstants.h> - -#include <QDebug> - -YggdrasilTask::YggdrasilTask(MojangAccount *account, QObject *parent) - : Task(parent), m_account(account) -{ - changeState(STATE_CREATED); -} - -void YggdrasilTask::executeTask() -{ - changeState(STATE_SENDING_REQUEST); - - // Get the content of the request we're going to send to the server. - QJsonDocument doc(getRequestContent()); - - auto worker = ENV.qnam(); - QUrl reqUrl("https://" + URLConstants::AUTH_BASE + getEndpoint()); - QNetworkRequest netRequest(reqUrl); - netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - - QByteArray requestData = doc.toJson(); - m_netReply = worker->post(netRequest, requestData); - connect(m_netReply, &QNetworkReply::finished, this, &YggdrasilTask::processReply); - connect(m_netReply, &QNetworkReply::uploadProgress, this, &YggdrasilTask::refreshTimers); - connect(m_netReply, &QNetworkReply::downloadProgress, this, &YggdrasilTask::refreshTimers); - connect(m_netReply, &QNetworkReply::sslErrors, this, &YggdrasilTask::sslErrors); - timeout_keeper.setSingleShot(true); - timeout_keeper.start(timeout_max); - counter.setSingleShot(false); - counter.start(time_step); - progress(0, timeout_max); - connect(&timeout_keeper, &QTimer::timeout, this, &YggdrasilTask::abortByTimeout); - connect(&counter, &QTimer::timeout, this, &YggdrasilTask::heartbeat); -} - -void YggdrasilTask::refreshTimers(qint64, qint64) -{ - timeout_keeper.stop(); - timeout_keeper.start(timeout_max); - progress(count = 0, timeout_max); -} -void YggdrasilTask::heartbeat() -{ - count += time_step; - progress(count, timeout_max); -} - -bool YggdrasilTask::abort() -{ - progress(timeout_max, timeout_max); - // TODO: actually use this in a meaningful way - m_aborted = YggdrasilTask::BY_USER; - m_netReply->abort(); - return true; -} - -void YggdrasilTask::abortByTimeout() -{ - progress(timeout_max, timeout_max); - // TODO: actually use this in a meaningful way - m_aborted = YggdrasilTask::BY_TIMEOUT; - m_netReply->abort(); -} - -void YggdrasilTask::sslErrors(QList<QSslError> errors) -{ - int i = 1; - for (auto error : errors) - { - qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString(); - auto cert = error.certificate(); - qCritical() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -void YggdrasilTask::processReply() -{ - changeState(STATE_PROCESSING_RESPONSE); - - switch (m_netReply->error()) - { - case QNetworkReply::NoError: - break; - case QNetworkReply::TimeoutError: - changeState(STATE_FAILED_SOFT, tr("Authentication operation timed out.")); - return; - case QNetworkReply::OperationCanceledError: - changeState(STATE_FAILED_SOFT, tr("Authentication operation cancelled.")); - return; - case QNetworkReply::SslHandshakeFailedError: - changeState( - STATE_FAILED_SOFT, - tr("<b>SSL Handshake failed.</b><br/>There might be a few causes for it:<br/>" - "<ul>" - "<li>You use Windows XP and need to <a " - "href=\"http://www.microsoft.com/en-us/download/details.aspx?id=38918\">update " - "your root certificates</a></li>" - "<li>Some device on your network is interfering with SSL traffic. In that case, " - "you have bigger worries than Minecraft not starting.</li>" - "<li>Possibly something else. Check the MultiMC log file for details</li>" - "</ul>")); - return; - // used for invalid credentials and similar errors. Fall through. - case QNetworkReply::ContentOperationNotPermittedError: - break; - default: - changeState(STATE_FAILED_SOFT, - tr("Authentication operation failed due to a network error: %1 (%2)") - .arg(m_netReply->errorString()).arg(m_netReply->error())); - return; - } - - // Try to parse the response regardless of the response code. - // Sometimes the auth server will give more information and an error code. - QJsonParseError jsonError; - QByteArray replyData = m_netReply->readAll(); - QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError); - // Check the response code. - int responseCode = m_netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (responseCode == 200) - { - // If the response code was 200, then there shouldn't be an error. Make sure - // anyways. - // Also, sometimes an empty reply indicates success. If there was no data received, - // pass an empty json object to the processResponse function. - if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0) - { - processResponse(replyData.size() > 0 ? doc.object() : QJsonObject()); - return; - } - else - { - changeState(STATE_FAILED_SOFT, tr("Failed to parse authentication server response " - "JSON response: %1 at offset %2.") - .arg(jsonError.errorString()) - .arg(jsonError.offset)); - qCritical() << replyData; - } - return; - } - - // If the response code was not 200, then Yggdrasil may have given us information - // about the error. - // If we can parse the response, then get information from it. Otherwise just say - // there was an unknown error. - if (jsonError.error == QJsonParseError::NoError) - { - // We were able to parse the server's response. Woo! - // Call processError. If a subclass has overridden it then they'll handle their - // stuff there. - qDebug() << "The request failed, but the server gave us an error message. " - "Processing error."; - processError(doc.object()); - } - else - { - // The server didn't say anything regarding the error. Give the user an unknown - // error. - qDebug() - << "The request failed and the server gave no error message. Unknown error."; - changeState(STATE_FAILED_SOFT, - tr("An unknown error occurred when trying to communicate with the " - "authentication server: %1").arg(m_netReply->errorString())); - } -} - -void YggdrasilTask::processError(QJsonObject responseData) -{ - QJsonValue errorVal = responseData.value("error"); - QJsonValue errorMessageValue = responseData.value("errorMessage"); - QJsonValue causeVal = responseData.value("cause"); - - if (errorVal.isString() && errorMessageValue.isString()) - { - m_error = std::shared_ptr<Error>(new Error{ - errorVal.toString(""), errorMessageValue.toString(""), causeVal.toString("")}); - changeState(STATE_FAILED_HARD, m_error->m_errorMessageVerbose); - } - else - { - // Error is not in standard format. Don't set m_error and return unknown error. - changeState(STATE_FAILED_HARD, tr("An unknown Yggdrasil error occurred.")); - } -} - -QString YggdrasilTask::getStateMessage() const -{ - switch (m_state) - { - case STATE_CREATED: - return "Waiting..."; - case STATE_SENDING_REQUEST: - return tr("Sending request to auth servers..."); - case STATE_PROCESSING_RESPONSE: - return tr("Processing response from servers..."); - case STATE_SUCCEEDED: - return tr("Authentication task succeeded."); - case STATE_FAILED_SOFT: - return tr("Failed to contact the authentication server."); - case STATE_FAILED_HARD: - return tr("Failed to authenticate."); - default: - return tr("..."); - } -} - -void YggdrasilTask::changeState(YggdrasilTask::State newState, QString reason) -{ - m_state = newState; - setStatus(getStateMessage()); - if (newState == STATE_SUCCEEDED) - { - emitSucceeded(); - } - else if (newState == STATE_FAILED_HARD || newState == STATE_FAILED_SOFT) - { - emitFailed(reason); - } -} - -YggdrasilTask::State YggdrasilTask::state() -{ - return m_state; -} diff --git a/libraries/logic/minecraft/auth/YggdrasilTask.h b/libraries/logic/minecraft/auth/YggdrasilTask.h deleted file mode 100644 index c84cfc06..00000000 --- a/libraries/logic/minecraft/auth/YggdrasilTask.h +++ /dev/null @@ -1,150 +0,0 @@ -/* Copyright 2013-2015 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 <tasks/Task.h> - -#include <QString> -#include <QJsonObject> -#include <QTimer> -#include <qsslerror.h> - -#include "MojangAccount.h" - -class QNetworkReply; - -/** - * A Yggdrasil task is a task that performs an operation on a given mojang account. - */ -class YggdrasilTask : public Task -{ - Q_OBJECT -public: - explicit YggdrasilTask(MojangAccount * account, QObject *parent = 0); - - /** - * assign a session to this task. the session will be filled with required infomration - * upon completion - */ - void assignSession(AuthSessionPtr session) - { - m_session = session; - } - - /// get the assigned session for filling with information. - AuthSessionPtr getAssignedSession() - { - return m_session; - } - - /** - * Class describing a Yggdrasil error response. - */ - struct Error - { - QString m_errorMessageShort; - QString m_errorMessageVerbose; - QString m_cause; - }; - - enum AbortedBy - { - BY_NOTHING, - BY_USER, - BY_TIMEOUT - } m_aborted = BY_NOTHING; - - /** - * Enum for describing the state of the current task. - * Used by the getStateMessage function to determine what the status message should be. - */ - enum State - { - STATE_CREATED, - STATE_SENDING_REQUEST, - STATE_PROCESSING_RESPONSE, - STATE_FAILED_SOFT, //!< soft failure. this generally means the user auth details haven't been invalidated - STATE_FAILED_HARD, //!< hard failure. auth is invalid - STATE_SUCCEEDED - } m_state = STATE_CREATED; - -protected: - - virtual void executeTask() override; - - /** - * Gets the JSON object that will be sent to the authentication server. - * Should be overridden by subclasses. - */ - virtual QJsonObject getRequestContent() const = 0; - - /** - * Gets the endpoint to POST to. - * No leading slash. - */ - virtual QString getEndpoint() const = 0; - - /** - * Processes the response received from the server. - * If an error occurred, this should emit a failed signal and return false. - * If Yggdrasil gave an error response, it should call setError() first, and then return false. - * Otherwise, it should return true. - * Note: If the response from the server was blank, and the HTTP code was 200, this function is called with - * an empty QJsonObject. - */ - virtual void processResponse(QJsonObject responseData) = 0; - - /** - * Processes an error response received from the server. - * The default implementation will read data from Yggdrasil's standard error response format and set it as this task's Error. - * \returns a QString error message that will be passed to emitFailed. - */ - virtual void processError(QJsonObject responseData); - - /** - * Returns the state message for the given state. - * Used to set the status message for the task. - * Should be overridden by subclasses that want to change messages for a given state. - */ - virtual QString getStateMessage() const; - -protected -slots: - void processReply(); - void refreshTimers(qint64, qint64); - void heartbeat(); - void sslErrors(QList<QSslError>); - - void changeState(State newState, QString reason=QString()); -public -slots: - virtual bool abort() override; - void abortByTimeout(); - State state(); -protected: - // FIXME: segfault disaster waiting to happen - MojangAccount *m_account = nullptr; - QNetworkReply *m_netReply = nullptr; - std::shared_ptr<Error> m_error; - QTimer timeout_keeper; - QTimer counter; - int count = 0; // num msec since time reset - - const int timeout_max = 30000; - const int time_step = 50; - - AuthSessionPtr m_session; -}; diff --git a/libraries/logic/minecraft/auth/flows/AuthenticateTask.cpp b/libraries/logic/minecraft/auth/flows/AuthenticateTask.cpp deleted file mode 100644 index 8d136f0b..00000000 --- a/libraries/logic/minecraft/auth/flows/AuthenticateTask.cpp +++ /dev/null @@ -1,202 +0,0 @@ - -/* Copyright 2013-2015 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 "AuthenticateTask.h" -#include "../MojangAccount.h" - -#include <QJsonDocument> -#include <QJsonObject> -#include <QJsonArray> -#include <QVariant> - -#include <QDebug> -#include <QUuid> - -AuthenticateTask::AuthenticateTask(MojangAccount * account, const QString &password, - QObject *parent) - : YggdrasilTask(account, parent), m_password(password) -{ -} - -QJsonObject AuthenticateTask::getRequestContent() const -{ - /* - * { - * "agent": { // optional - * "name": "Minecraft", // So far this is the only encountered value - * "version": 1 // This number might be increased - * // by the vanilla client in the future - * }, - * "username": "mojang account name", // Can be an email address or player name for - // unmigrated accounts - * "password": "mojang account password", - * "clientToken": "client identifier" // optional - * "requestUser": true/false // request the user structure - * } - */ - QJsonObject req; - - { - QJsonObject agent; - // C++ makes string literals void* for some stupid reason, so we have to tell it - // QString... Thanks Obama. - agent.insert("name", QString("Minecraft")); - agent.insert("version", 1); - req.insert("agent", agent); - } - - req.insert("username", m_account->username()); - req.insert("password", m_password); - req.insert("requestUser", true); - - // If we already have a client token, give it to the server. - // Otherwise, let the server give us one. - - if(m_account->m_clientToken.isEmpty()) - { - auto uuid = QUuid::createUuid(); - auto uuidString = uuid.toString().remove('{').remove('-').remove('}'); - m_account->m_clientToken = uuidString; - } - req.insert("clientToken", m_account->m_clientToken); - - return req; -} - -void AuthenticateTask::processResponse(QJsonObject responseData) -{ - // Read the response data. We need to get the client token, access token, and the selected - // profile. - qDebug() << "Processing authentication response."; - // qDebug() << responseData; - // If we already have a client token, make sure the one the server gave us matches our - // existing one. - qDebug() << "Getting client token."; - QString clientToken = responseData.value("clientToken").toString(""); - if (clientToken.isEmpty()) - { - // Fail if the server gave us an empty client token - changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token.")); - return; - } - if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken) - { - changeState(STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported.")); - return; - } - // Set the client token. - m_account->m_clientToken = clientToken; - - // Now, we set the access token. - qDebug() << "Getting access token."; - QString accessToken = responseData.value("accessToken").toString(""); - if (accessToken.isEmpty()) - { - // Fail if the server didn't give us an access token. - changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token.")); - return; - } - // Set the access token. - m_account->m_accessToken = accessToken; - - // Now we load the list of available profiles. - // Mojang hasn't yet implemented the profile system, - // but we might as well support what's there so we - // don't have trouble implementing it later. - qDebug() << "Loading profile list."; - QJsonArray availableProfiles = responseData.value("availableProfiles").toArray(); - QList<AccountProfile> loadedProfiles; - for (auto iter : availableProfiles) - { - QJsonObject profile = iter.toObject(); - // Profiles are easy, we just need their ID and name. - QString id = profile.value("id").toString(""); - QString name = profile.value("name").toString(""); - bool legacy = profile.value("legacy").toBool(false); - - if (id.isEmpty() || name.isEmpty()) - { - // This should never happen, but we might as well - // warn about it if it does so we can debug it easily. - // You never know when Mojang might do something truly derpy. - qWarning() << "Found entry in available profiles list with missing ID or name " - "field. Ignoring it."; - } - - // Now, add a new AccountProfile entry to the list. - loadedProfiles.append({id, name, legacy}); - } - // Put the list of profiles we loaded into the MojangAccount object. - m_account->m_profiles = loadedProfiles; - - // Finally, we set the current profile to the correct value. This is pretty simple. - // We do need to make sure that the current profile that the server gave us - // is actually in the available profiles list. - // If it isn't, we'll just fail horribly (*shouldn't* ever happen, but you never know). - qDebug() << "Setting current profile."; - QJsonObject currentProfile = responseData.value("selectedProfile").toObject(); - QString currentProfileId = currentProfile.value("id").toString(""); - if (currentProfileId.isEmpty()) - { - changeState(STATE_FAILED_HARD, tr("Authentication server didn't specify a currently selected profile. The account exists, but likely isn't premium.")); - return; - } - if (!m_account->setCurrentProfile(currentProfileId)) - { - changeState(STATE_FAILED_HARD, tr("Authentication server specified a selected profile that wasn't in the available profiles list.")); - return; - } - - // this is what the vanilla launcher passes to the userProperties launch param - if (responseData.contains("user")) - { - User u; - auto obj = responseData.value("user").toObject(); - u.id = obj.value("id").toString(); - auto propArray = obj.value("properties").toArray(); - for (auto prop : propArray) - { - auto propTuple = prop.toObject(); - auto name = propTuple.value("name").toString(); - auto value = propTuple.value("value").toString(); - u.properties.insert(name, value); - } - m_account->m_user = u; - } - - // We've made it through the minefield of possible errors. Return true to indicate that - // we've succeeded. - qDebug() << "Finished reading authentication response."; - changeState(STATE_SUCCEEDED); -} - -QString AuthenticateTask::getEndpoint() const -{ - return "authenticate"; -} - -QString AuthenticateTask::getStateMessage() const -{ - switch (m_state) - { - case STATE_SENDING_REQUEST: - return tr("Authenticating: Sending request..."); - case STATE_PROCESSING_RESPONSE: - return tr("Authenticating: Processing response..."); - default: - return YggdrasilTask::getStateMessage(); - } -} diff --git a/libraries/logic/minecraft/auth/flows/AuthenticateTask.h b/libraries/logic/minecraft/auth/flows/AuthenticateTask.h deleted file mode 100644 index 398fab98..00000000 --- a/libraries/logic/minecraft/auth/flows/AuthenticateTask.h +++ /dev/null @@ -1,46 +0,0 @@ -/* Copyright 2013-2015 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 "../YggdrasilTask.h" - -#include <QObject> -#include <QString> -#include <QJsonObject> - -/** - * The authenticate task takes a MojangAccount with no access token and password and attempts to - * authenticate with Mojang's servers. - * If successful, it will set the MojangAccount's access token. - */ -class AuthenticateTask : public YggdrasilTask -{ - Q_OBJECT -public: - AuthenticateTask(MojangAccount *account, const QString &password, QObject *parent = 0); - -protected: - virtual QJsonObject getRequestContent() const override; - - virtual QString getEndpoint() const override; - - virtual void processResponse(QJsonObject responseData) override; - - virtual QString getStateMessage() const override; - -private: - QString m_password; -}; diff --git a/libraries/logic/minecraft/auth/flows/RefreshTask.cpp b/libraries/logic/minecraft/auth/flows/RefreshTask.cpp deleted file mode 100644 index a0fb2e48..00000000 --- a/libraries/logic/minecraft/auth/flows/RefreshTask.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* Copyright 2013-2015 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 "RefreshTask.h" -#include "../MojangAccount.h" - -#include <QJsonDocument> -#include <QJsonObject> -#include <QJsonArray> -#include <QVariant> - -#include <QDebug> - -RefreshTask::RefreshTask(MojangAccount *account) : YggdrasilTask(account) -{ -} - -QJsonObject RefreshTask::getRequestContent() const -{ - /* - * { - * "clientToken": "client identifier" - * "accessToken": "current access token to be refreshed" - * "selectedProfile": // specifying this causes errors - * { - * "id": "profile ID" - * "name": "profile name" - * } - * "requestUser": true/false // request the user structure - * } - */ - QJsonObject req; - req.insert("clientToken", m_account->m_clientToken); - req.insert("accessToken", m_account->m_accessToken); - /* - { - auto currentProfile = m_account->currentProfile(); - QJsonObject profile; - profile.insert("id", currentProfile->id()); - profile.insert("name", currentProfile->name()); - req.insert("selectedProfile", profile); - } - */ - req.insert("requestUser", true); - - return req; -} - -void RefreshTask::processResponse(QJsonObject responseData) -{ - // Read the response data. We need to get the client token, access token, and the selected - // profile. - qDebug() << "Processing authentication response."; - - // qDebug() << responseData; - // If we already have a client token, make sure the one the server gave us matches our - // existing one. - QString clientToken = responseData.value("clientToken").toString(""); - if (clientToken.isEmpty()) - { - // Fail if the server gave us an empty client token - changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token.")); - return; - } - if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken) - { - changeState(STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported.")); - return; - } - - // Now, we set the access token. - qDebug() << "Getting new access token."; - QString accessToken = responseData.value("accessToken").toString(""); - if (accessToken.isEmpty()) - { - // Fail if the server didn't give us an access token. - changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token.")); - return; - } - - // we validate that the server responded right. (our current profile = returned current - // profile) - QJsonObject currentProfile = responseData.value("selectedProfile").toObject(); - QString currentProfileId = currentProfile.value("id").toString(""); - if (m_account->currentProfile()->id != currentProfileId) - { - changeState(STATE_FAILED_HARD, tr("Authentication server didn't specify the same prefile as expected.")); - return; - } - - // this is what the vanilla launcher passes to the userProperties launch param - if (responseData.contains("user")) - { - User u; - auto obj = responseData.value("user").toObject(); - u.id = obj.value("id").toString(); - auto propArray = obj.value("properties").toArray(); - for (auto prop : propArray) - { - auto propTuple = prop.toObject(); - auto name = propTuple.value("name").toString(); - auto value = propTuple.value("value").toString(); - u.properties.insert(name, value); - } - m_account->m_user = u; - } - - // We've made it through the minefield of possible errors. Return true to indicate that - // we've succeeded. - qDebug() << "Finished reading refresh response."; - // Reset the access token. - m_account->m_accessToken = accessToken; - changeState(STATE_SUCCEEDED); -} - -QString RefreshTask::getEndpoint() const -{ - return "refresh"; -} - -QString RefreshTask::getStateMessage() const -{ - switch (m_state) - { - case STATE_SENDING_REQUEST: - return tr("Refreshing login token..."); - case STATE_PROCESSING_RESPONSE: - return tr("Refreshing login token: Processing response..."); - default: - return YggdrasilTask::getStateMessage(); - } -} diff --git a/libraries/logic/minecraft/auth/flows/RefreshTask.h b/libraries/logic/minecraft/auth/flows/RefreshTask.h deleted file mode 100644 index 17714b4f..00000000 --- a/libraries/logic/minecraft/auth/flows/RefreshTask.h +++ /dev/null @@ -1,44 +0,0 @@ -/* Copyright 2013-2015 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 "../YggdrasilTask.h" - -#include <QObject> -#include <QString> -#include <QJsonObject> - -/** - * The authenticate task takes a MojangAccount with a possibly timed-out access token - * and attempts to authenticate with Mojang's servers. - * If successful, it will set the new access token. The token is considered validated. - */ -class RefreshTask : public YggdrasilTask -{ - Q_OBJECT -public: - RefreshTask(MojangAccount * account); - -protected: - virtual QJsonObject getRequestContent() const override; - - virtual QString getEndpoint() const override; - - virtual void processResponse(QJsonObject responseData) override; - - virtual QString getStateMessage() const override; -}; - diff --git a/libraries/logic/minecraft/auth/flows/ValidateTask.cpp b/libraries/logic/minecraft/auth/flows/ValidateTask.cpp deleted file mode 100644 index 4deceb6a..00000000 --- a/libraries/logic/minecraft/auth/flows/ValidateTask.cpp +++ /dev/null @@ -1,61 +0,0 @@ - -/* Copyright 2013-2015 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 "ValidateTask.h" -#include "../MojangAccount.h" - -#include <QJsonDocument> -#include <QJsonObject> -#include <QJsonArray> -#include <QVariant> - -#include <QDebug> - -ValidateTask::ValidateTask(MojangAccount * account, QObject *parent) - : YggdrasilTask(account, parent) -{ -} - -QJsonObject ValidateTask::getRequestContent() const -{ - QJsonObject req; - req.insert("accessToken", m_account->m_accessToken); - return req; -} - -void ValidateTask::processResponse(QJsonObject responseData) -{ - // Assume that if processError wasn't called, then the request was successful. - changeState(YggdrasilTask::STATE_SUCCEEDED); -} - -QString ValidateTask::getEndpoint() const -{ - return "validate"; -} - -QString ValidateTask::getStateMessage() const -{ - switch (m_state) - { - case YggdrasilTask::STATE_SENDING_REQUEST: - return tr("Validating access token: Sending request..."); - case YggdrasilTask::STATE_PROCESSING_RESPONSE: - return tr("Validating access token: Processing response..."); - default: - return YggdrasilTask::getStateMessage(); - } -} diff --git a/libraries/logic/minecraft/auth/flows/ValidateTask.h b/libraries/logic/minecraft/auth/flows/ValidateTask.h deleted file mode 100644 index 77d628a0..00000000 --- a/libraries/logic/minecraft/auth/flows/ValidateTask.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -/* - * :FIXME: DEAD CODE, DEAD CODE, DEAD CODE! :FIXME: - */ - -#pragma once - -#include "../YggdrasilTask.h" - -#include <QObject> -#include <QString> -#include <QJsonObject> - -/** - * The validate task takes a MojangAccount and checks to make sure its access token is valid. - */ -class ValidateTask : public YggdrasilTask -{ - Q_OBJECT -public: - ValidateTask(MojangAccount *account, QObject *parent = 0); - -protected: - virtual QJsonObject getRequestContent() const override; - - virtual QString getEndpoint() const override; - - virtual void processResponse(QJsonObject responseData) override; - - virtual QString getStateMessage() const override; - -private: -}; diff --git a/libraries/logic/minecraft/forge/ForgeInstaller.cpp b/libraries/logic/minecraft/forge/ForgeInstaller.cpp deleted file mode 100644 index 353328ab..00000000 --- a/libraries/logic/minecraft/forge/ForgeInstaller.cpp +++ /dev/null @@ -1,458 +0,0 @@ -/* Copyright 2013-2015 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 "ForgeInstaller.h" -#include "ForgeVersionList.h" - -#include "minecraft/MinecraftProfile.h" -#include "minecraft/GradleSpecifier.h" -#include "net/HttpMetaCache.h" -#include "tasks/Task.h" -#include "minecraft/onesix/OneSixInstance.h" -#include <minecraft/onesix/OneSixVersionFormat.h> -#include "minecraft/VersionFilterData.h" -#include "minecraft/MinecraftVersion.h" -#include "Env.h" -#include "Exception.h" -#include <FileSystem.h> - -#include <quazip.h> -#include <quazipfile.h> -#include <QStringList> -#include <QRegularExpression> -#include <QRegularExpressionMatch> - -#include <QJsonDocument> -#include <QJsonArray> -#include <QSaveFile> -#include <QCryptographicHash> - -ForgeInstaller::ForgeInstaller() : BaseInstaller() -{ -} - -void ForgeInstaller::prepare(const QString &filename, const QString &universalUrl) -{ - VersionFilePtr newVersion; - m_universal_url = universalUrl; - - QuaZip zip(filename); - if (!zip.open(QuaZip::mdUnzip)) - return; - - QuaZipFile file(&zip); - - // read the install profile - if (!zip.setCurrentFile("install_profile.json")) - return; - - QJsonParseError jsonError; - if (!file.open(QIODevice::ReadOnly)) - return; - QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll(), &jsonError); - file.close(); - if (jsonError.error != QJsonParseError::NoError) - return; - - if (!jsonDoc.isObject()) - return; - - QJsonObject root = jsonDoc.object(); - - auto installVal = root.value("install"); - auto versionInfoVal = root.value("versionInfo"); - if (!installVal.isObject() || !versionInfoVal.isObject()) - return; - - try - { - newVersion = OneSixVersionFormat::versionFileFromJson(QJsonDocument(versionInfoVal.toObject()), QString(), false); - } - catch(Exception &err) - { - qWarning() << "Forge: Fatal error while parsing version file:" << err.what(); - return; - } - - for(auto problem: newVersion->getProblems()) - { - qWarning() << "Forge: Problem found: " << problem.getDescription(); - } - if(newVersion->getProblemSeverity() == ProblemSeverity::PROBLEM_ERROR) - { - qWarning() << "Forge: Errors found while parsing version file"; - return; - } - - QJsonObject installObj = installVal.toObject(); - QString libraryName = installObj.value("path").toString(); - internalPath = installObj.value("filePath").toString(); - m_forgeVersionString = installObj.value("version").toString().remove("Forge", Qt::CaseInsensitive).trimmed(); - - // where do we put the library? decode the mojang path - GradleSpecifier lib(libraryName); - - auto cacheentry = ENV.metacache()->resolveEntry("libraries", lib.toPath()); - finalPath = "libraries/" + lib.toPath(); - if (!FS::ensureFilePathExists(finalPath)) - return; - - if (!zip.setCurrentFile(internalPath)) - return; - if (!file.open(QIODevice::ReadOnly)) - return; - { - QByteArray data = file.readAll(); - // extract file - QSaveFile extraction(finalPath); - if (!extraction.open(QIODevice::WriteOnly)) - return; - if (extraction.write(data) != data.size()) - return; - if (!extraction.commit()) - return; - QCryptographicHash md5sum(QCryptographicHash::Md5); - md5sum.addData(data); - - cacheentry->setStale(false); - cacheentry->setMD5Sum(md5sum.result().toHex().constData()); - ENV.metacache()->updateEntry(cacheentry); - } - file.close(); - - m_forge_json = newVersion; -} - -bool ForgeInstaller::add(OneSixInstance *to) -{ - if (!BaseInstaller::add(to)) - { - return false; - } - - if (!m_forge_json) - { - return false; - } - - // A blacklist - QSet<QString> blacklist{"authlib", "realms"}; - QList<QString> xzlist{"org.scala-lang", "com.typesafe"}; - - // get the minecraft version from the instance - VersionFilePtr minecraft; - auto minecraftPatch = to->getMinecraftProfile()->versionPatch("net.minecraft"); - if(minecraftPatch) - { - minecraft = std::dynamic_pointer_cast<VersionFile>(minecraftPatch); - if(!minecraft) - { - auto mcWrap = std::dynamic_pointer_cast<MinecraftVersion>(minecraftPatch); - if(mcWrap) - { - minecraft = mcWrap->getVersionFile(); - } - } - } - - // for each library in the version we are adding (except for the blacklisted) - QMutableListIterator<LibraryPtr> iter(m_forge_json->libraries); - while (iter.hasNext()) - { - auto library = iter.next(); - QString libName = library->artifactId(); - QString libVersion = library->version(); - QString rawName = library->rawName(); - - // ignore lwjgl libraries. - if (g_VersionFilterData.lwjglWhitelist.contains(library->artifactPrefix())) - { - iter.remove(); - continue; - } - // ignore other blacklisted (realms, authlib) - if (blacklist.contains(libName)) - { - iter.remove(); - continue; - } - // if minecraft version was found, ignore everything that is already in the minecraft version - if(minecraft) - { - bool found = false; - for (auto & lib: minecraft->libraries) - { - if(library->artifactPrefix() == lib->artifactPrefix() && library->version() == lib->version()) - { - found = true; - break; - } - } - if (found) - continue; - } - - // if this is the actual forge lib, set an absolute url for the download - if (m_forge_version->type == ForgeVersion::Gradle) - { - if (libName == "forge") - { - library->setClassifier("universal"); - } - else if (libName == "minecraftforge") - { - QString forgeCoord("net.minecraftforge:forge:%1:universal"); - // using insane form of the MC version... - QString longVersion = m_forge_version->mcver + "-" + m_forge_version->jobbuildver; - GradleSpecifier spec(forgeCoord.arg(longVersion)); - library->setRawName(spec); - } - } - else - { - if (libName.contains("minecraftforge")) - { - library->setAbsoluteUrl(m_universal_url); - } - } - - // mark bad libraries based on the xzlist above - for (auto entry : xzlist) - { - qDebug() << "Testing " << rawName << " : " << entry; - if (rawName.startsWith(entry)) - { - library->setHint("forge-pack-xz"); - break; - } - } - } - QString &args = m_forge_json->minecraftArguments; - QStringList tweakers; - { - QRegularExpression expression("--tweakClass ([a-zA-Z0-9\\.]*)"); - QRegularExpressionMatch match = expression.match(args); - while (match.hasMatch()) - { - tweakers.append(match.captured(1)); - args.remove(match.capturedStart(), match.capturedLength()); - match = expression.match(args); - } - if(tweakers.size()) - { - args.operator=(args.trimmed()); - m_forge_json->addTweakers = tweakers; - } - } - if(minecraft && args == minecraft->minecraftArguments) - { - args.clear(); - } - - m_forge_json->name = "Forge"; - m_forge_json->fileId = id(); - m_forge_json->version = m_forgeVersionString; - m_forge_json->dependsOnMinecraftVersion = to->intendedVersionId(); - m_forge_json->order = 5; - - // reset some things we do not want to be passed along. - m_forge_json->m_releaseTime = QDateTime(); - m_forge_json->m_updateTime = QDateTime(); - m_forge_json->minimumLauncherVersion = -1; - m_forge_json->type.clear(); - m_forge_json->minecraftArguments.clear(); - m_forge_json->minecraftVersion.clear(); - - QSaveFile file(filename(to->instanceRoot())); - if (!file.open(QFile::WriteOnly)) - { - qCritical() << "Error opening" << file.fileName() - << "for reading:" << file.errorString(); - return false; - } - file.write(OneSixVersionFormat::versionFileToJson(m_forge_json, true).toJson()); - file.commit(); - - return true; -} - -bool ForgeInstaller::addLegacy(OneSixInstance *to) -{ - if (!BaseInstaller::add(to)) - { - return false; - } - auto entry = ENV.metacache()->resolveEntry("minecraftforge", m_forge_version->filename()); - finalPath = FS::PathCombine(to->jarModsDir(), m_forge_version->filename()); - if (!FS::ensureFilePathExists(finalPath)) - { - return false; - } - if (!QFile::copy(entry->getFullPath(), finalPath)) - { - return false; - } - QJsonObject obj; - obj.insert("order", 5); - { - QJsonArray jarmodsPlus; - { - QJsonObject libObj; - libObj.insert("name", m_forge_version->universal_filename); - jarmodsPlus.append(libObj); - } - obj.insert("+jarMods", jarmodsPlus); - } - - obj.insert("name", QString("Forge")); - obj.insert("fileId", id()); - obj.insert("version", m_forge_version->jobbuildver); - obj.insert("mcVersion", to->intendedVersionId()); - if (g_VersionFilterData.fmlLibsMapping.contains(m_forge_version->mcver)) - { - QJsonArray traitsPlus; - traitsPlus.append(QString("legacyFML")); - obj.insert("+traits", traitsPlus); - } - auto fullversion = to->getMinecraftProfile(); - fullversion->remove("net.minecraftforge"); - - QFile file(filename(to->instanceRoot())); - if (!file.open(QFile::WriteOnly)) - { - qCritical() << "Error opening" << file.fileName() - << "for reading:" << file.errorString(); - return false; - } - file.write(QJsonDocument(obj).toJson()); - file.close(); - return true; -} - -class ForgeInstallTask : public Task -{ - Q_OBJECT -public: - ForgeInstallTask(ForgeInstaller *installer, OneSixInstance *instance, - BaseVersionPtr version, QObject *parent = 0) - : Task(parent), m_installer(installer), m_instance(instance), m_version(version) - { - } - -protected: - void executeTask() override - { - setStatus(tr("Installing Forge...")); - ForgeVersionPtr forgeVersion = std::dynamic_pointer_cast<ForgeVersion>(m_version); - if (!forgeVersion) - { - emitFailed(tr("Unknown error occured")); - return; - } - prepare(forgeVersion); - } - void prepare(ForgeVersionPtr forgeVersion) - { - auto entry = ENV.metacache()->resolveEntry("minecraftforge", forgeVersion->filename()); - auto installFunction = [this, entry, forgeVersion]() - { - if (!install(entry, forgeVersion)) - { - qCritical() << "Failure installing Forge"; - emitFailed(tr("Failure to install Forge")); - } - else - { - reload(); - } - }; - - /* - * HACK IF the local non-stale file is too small, mark is as stale - * - * This fixes some problems with bad files acquired because of unhandled HTTP redirects - * in old versions of MultiMC. - */ - if (!entry->isStale()) - { - QFileInfo localFile(entry->getFullPath()); - if (localFile.size() <= 0x4000) - { - entry->setStale(true); - } - } - - if (entry->isStale()) - { - NetJob *fjob = new NetJob("Forge download"); - fjob->addNetAction(CacheDownload::make(forgeVersion->url(), entry)); - connect(fjob, &NetJob::progress, this, &Task::setProgress); - connect(fjob, &NetJob::status, this, &Task::setStatus); - connect(fjob, &NetJob::failed, [this](QString reason) - { emitFailed(tr("Failure to download Forge:\n%1").arg(reason)); }); - connect(fjob, &NetJob::succeeded, installFunction); - fjob->start(); - } - else - { - installFunction(); - } - } - bool install(const std::shared_ptr<MetaEntry> &entry, const ForgeVersionPtr &forgeVersion) - { - if (forgeVersion->usesInstaller()) - { - QString forgePath = entry->getFullPath(); - m_installer->prepare(forgePath, forgeVersion->universal_url); - return m_installer->add(m_instance); - } - else - return m_installer->addLegacy(m_instance); - } - void reload() - { - try - { - m_instance->reloadProfile(); - emitSucceeded(); - } - catch (Exception &e) - { - emitFailed(e.cause()); - } - catch (...) - { - emitFailed(tr("Failed to load the version description file for reasons unknown.")); - } - } - -private: - ForgeInstaller *m_installer; - OneSixInstance *m_instance; - BaseVersionPtr m_version; -}; - -Task *ForgeInstaller::createInstallTask(OneSixInstance *instance, - BaseVersionPtr version, QObject *parent) -{ - if (!version) - { - return nullptr; - } - m_forge_version = std::dynamic_pointer_cast<ForgeVersion>(version); - return new ForgeInstallTask(this, instance, version, parent); -} - -#include "ForgeInstaller.moc" diff --git a/libraries/logic/minecraft/forge/ForgeInstaller.h b/libraries/logic/minecraft/forge/ForgeInstaller.h deleted file mode 100644 index 499a6fb3..00000000 --- a/libraries/logic/minecraft/forge/ForgeInstaller.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright 2013-2015 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 "BaseInstaller.h" - -#include <QString> -#include <memory> - -#include "multimc_logic_export.h" - -class VersionFile; -class ForgeInstallTask; -struct ForgeVersion; - -class MULTIMC_LOGIC_EXPORT ForgeInstaller : public BaseInstaller -{ - friend class ForgeInstallTask; -public: - ForgeInstaller(); - virtual ~ForgeInstaller(){} - virtual Task *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) override; - virtual QString id() const override { return "net.minecraftforge"; } - -protected: - void prepare(const QString &filename, const QString &universalUrl); - bool add(OneSixInstance *to) override; - bool addLegacy(OneSixInstance *to); - -private: - // the parsed version json, read from the installer - std::shared_ptr<VersionFile> m_forge_json; - // the actual forge version - std::shared_ptr<ForgeVersion> m_forge_version; - QString internalPath; - QString finalPath; - QString m_forgeVersionString; - QString m_universal_url; -}; diff --git a/libraries/logic/minecraft/forge/ForgeVersion.cpp b/libraries/logic/minecraft/forge/ForgeVersion.cpp deleted file mode 100644 index b859a28c..00000000 --- a/libraries/logic/minecraft/forge/ForgeVersion.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "ForgeVersion.h" -#include "minecraft/VersionFilterData.h" -#include <QObject> - -QString ForgeVersion::name() -{ - return "Forge " + jobbuildver; -} - -QString ForgeVersion::descriptor() -{ - return universal_filename; -} - -QString ForgeVersion::typeString() const -{ - if (is_recommended) - return QObject::tr("Recommended"); - return QString(); -} - -bool ForgeVersion::operator<(BaseVersion &a) -{ - ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a); - if (!pa) - return true; - return m_buildnr < pa->m_buildnr; -} - -bool ForgeVersion::operator>(BaseVersion &a) -{ - ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a); - if (!pa) - return false; - return m_buildnr > pa->m_buildnr; -} - -bool ForgeVersion::usesInstaller() -{ - if(installer_url.isEmpty()) - return false; - if(g_VersionFilterData.forgeInstallerBlacklist.contains(mcver)) - return false; - return true; -} - -QString ForgeVersion::filename() -{ - return usesInstaller() ? installer_filename : universal_filename; -} - -QString ForgeVersion::url() -{ - return usesInstaller() ? installer_url : universal_url; -} diff --git a/libraries/logic/minecraft/forge/ForgeVersion.h b/libraries/logic/minecraft/forge/ForgeVersion.h deleted file mode 100644 index e77d32f1..00000000 --- a/libraries/logic/minecraft/forge/ForgeVersion.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include <QString> -#include <memory> -#include "BaseVersion.h" - -struct ForgeVersion; -typedef std::shared_ptr<ForgeVersion> ForgeVersionPtr; - -struct ForgeVersion : public BaseVersion -{ - virtual QString descriptor() override; - virtual QString name() override; - virtual QString typeString() const override; - virtual bool operator<(BaseVersion &a) override; - virtual bool operator>(BaseVersion &a) override; - - QString filename(); - QString url(); - - enum - { - Invalid, - Legacy, - Gradle - } type = Invalid; - - bool usesInstaller(); - - int m_buildnr = 0; - QString branch; - QString universal_url; - QString changelog_url; - QString installer_url; - QString jobbuildver; - QString mcver; - QString mcver_sane; - QString universal_filename; - QString installer_filename; - bool is_recommended = false; -}; - -Q_DECLARE_METATYPE(ForgeVersionPtr) diff --git a/libraries/logic/minecraft/forge/ForgeVersionList.cpp b/libraries/logic/minecraft/forge/ForgeVersionList.cpp deleted file mode 100644 index de185e5f..00000000 --- a/libraries/logic/minecraft/forge/ForgeVersionList.cpp +++ /dev/null @@ -1,450 +0,0 @@ -/* Copyright 2013-2015 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 "ForgeVersionList.h" -#include "ForgeVersion.h" - -#include "net/NetJob.h" -#include "net/URLConstants.h" -#include "Env.h" - -#include <QtNetwork> -#include <QtXml> -#include <QRegExp> - -#include <QDebug> - -ForgeVersionList::ForgeVersionList(QObject *parent) : BaseVersionList(parent) -{ -} - -Task *ForgeVersionList::getLoadTask() -{ - return new ForgeListLoadTask(this); -} - -bool ForgeVersionList::isLoaded() -{ - return m_loaded; -} - -const BaseVersionPtr ForgeVersionList::at(int i) const -{ - return m_vlist.at(i); -} - -int ForgeVersionList::count() const -{ - return m_vlist.count(); -} - -int ForgeVersionList::columnCount(const QModelIndex &parent) const -{ - return 1; -} - -QVariant ForgeVersionList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - if (index.row() > count()) - return QVariant(); - - auto version = std::dynamic_pointer_cast<ForgeVersion>(m_vlist[index.row()]); - switch (role) - { - case VersionPointerRole: - return qVariantFromValue(m_vlist[index.row()]); - - case VersionRole: - return version->name(); - - case VersionIdRole: - return version->descriptor(); - - case ParentGameVersionRole: - return version->mcver_sane; - - case RecommendedRole: - return version->is_recommended; - - case BranchRole: - return version->branch; - - default: - return QVariant(); - } -} - -BaseVersionList::RoleList ForgeVersionList::providesRoles() const -{ - return {VersionPointerRole, VersionRole, VersionIdRole, ParentGameVersionRole, RecommendedRole, BranchRole}; -} - -BaseVersionPtr ForgeVersionList::getLatestStable() const -{ - return BaseVersionPtr(); -} - -void ForgeVersionList::updateListData(QList<BaseVersionPtr> versions) -{ - beginResetModel(); - m_vlist = versions; - m_loaded = true; - endResetModel(); - // NOW SORT!! - // sort(); -} - -void ForgeVersionList::sortVersions() -{ - // NO-OP for now -} - -ForgeListLoadTask::ForgeListLoadTask(ForgeVersionList *vlist) : Task() -{ - m_list = vlist; -} - -void ForgeListLoadTask::executeTask() -{ - setStatus(tr("Fetching Forge version lists...")); - auto job = new NetJob("Version index"); - // we do not care if the version is stale or not. - auto forgeListEntry = ENV.metacache()->resolveEntry("minecraftforge", "list.json"); - auto gradleForgeListEntry = ENV.metacache()->resolveEntry("minecraftforge", "json"); - - // verify by poking the server. - forgeListEntry->setStale(true); - gradleForgeListEntry->setStale(true); - - job->addNetAction(listDownload = CacheDownload::make(QUrl(URLConstants::FORGE_LEGACY_URL), - forgeListEntry)); - job->addNetAction(gradleListDownload = CacheDownload::make( - QUrl(URLConstants::FORGE_GRADLE_URL), gradleForgeListEntry)); - - connect(listDownload.get(), SIGNAL(failed(int)), SLOT(listFailed())); - connect(gradleListDownload.get(), SIGNAL(failed(int)), SLOT(gradleListFailed())); - - listJob.reset(job); - connect(listJob.get(), SIGNAL(succeeded()), SLOT(listDownloaded())); - connect(listJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); - listJob->start(); -} - -bool ForgeListLoadTask::abort() -{ - return listJob->abort(); -} - -bool ForgeListLoadTask::parseForgeList(QList<BaseVersionPtr> &out) -{ - QByteArray data; - { - auto dlJob = listDownload; - auto filename = std::dynamic_pointer_cast<CacheDownload>(dlJob)->getTargetFilepath(); - QFile listFile(filename); - if (!listFile.open(QIODevice::ReadOnly)) - { - return false; - } - data = listFile.readAll(); - dlJob.reset(); - } - - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); - - if (jsonError.error != QJsonParseError::NoError) - { - emitFailed("Error parsing version list JSON:" + jsonError.errorString()); - return false; - } - - if (!jsonDoc.isObject()) - { - emitFailed("Error parsing version list JSON: JSON root is not an object"); - return false; - } - - QJsonObject root = jsonDoc.object(); - - // Now, get the array of versions. - if (!root.value("builds").isArray()) - { - emitFailed( - "Error parsing version list JSON: version list object is missing 'builds' array"); - return false; - } - QJsonArray builds = root.value("builds").toArray(); - - for (int i = 0; i < builds.count(); i++) - { - // Load the version info. - if (!builds[i].isObject()) - { - // FIXME: log this somewhere - continue; - } - QJsonObject obj = builds[i].toObject(); - int build_nr = obj.value("build").toDouble(0); - if (!build_nr) - continue; - QJsonArray files = obj.value("files").toArray(); - QString url, jobbuildver, mcver, buildtype, universal_filename; - QString changelog_url, installer_url; - QString installer_filename; - bool valid = false; - for (int j = 0; j < files.count(); j++) - { - if (!files[j].isObject()) - { - continue; - } - QJsonObject file = files[j].toObject(); - buildtype = file.value("buildtype").toString(); - if ((buildtype == "client" || buildtype == "universal") && !valid) - { - mcver = file.value("mcver").toString(); - url = file.value("url").toString(); - jobbuildver = file.value("jobbuildver").toString(); - int lastSlash = url.lastIndexOf('/'); - universal_filename = url.mid(lastSlash + 1); - valid = true; - } - else if (buildtype == "changelog") - { - QString ext = file.value("ext").toString(); - if (ext.isEmpty()) - { - continue; - } - changelog_url = file.value("url").toString(); - } - else if (buildtype == "installer") - { - installer_url = file.value("url").toString(); - int lastSlash = installer_url.lastIndexOf('/'); - installer_filename = installer_url.mid(lastSlash + 1); - } - } - if (valid) - { - // Now, we construct the version object and add it to the list. - std::shared_ptr<ForgeVersion> fVersion(new ForgeVersion()); - fVersion->universal_url = url; - fVersion->changelog_url = changelog_url; - fVersion->installer_url = installer_url; - fVersion->jobbuildver = jobbuildver; - fVersion->mcver = fVersion->mcver_sane = mcver; - fVersion->installer_filename = installer_filename; - fVersion->universal_filename = universal_filename; - fVersion->m_buildnr = build_nr; - fVersion->type = ForgeVersion::Legacy; - out.append(fVersion); - } - } - - return true; -} - -bool ForgeListLoadTask::parseForgeGradleList(QList<BaseVersionPtr> &out) -{ - QMap<int, std::shared_ptr<ForgeVersion>> lookup; - QByteArray data; - { - auto dlJob = gradleListDownload; - auto filename = std::dynamic_pointer_cast<CacheDownload>(dlJob)->getTargetFilepath(); - QFile listFile(filename); - if (!listFile.open(QIODevice::ReadOnly)) - { - return false; - } - data = listFile.readAll(); - dlJob.reset(); - } - - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); - - if (jsonError.error != QJsonParseError::NoError) - { - emitFailed("Error parsing gradle version list JSON:" + jsonError.errorString()); - return false; - } - - if (!jsonDoc.isObject()) - { - emitFailed("Error parsing gradle version list JSON: JSON root is not an object"); - return false; - } - - QJsonObject root = jsonDoc.object(); - - // we probably could hard code these, but it might still be worth doing it this way - const QString webpath = root.value("webpath").toString(); - const QString artifact = root.value("artifact").toString(); - - QJsonObject numbers = root.value("number").toObject(); - for (auto it = numbers.begin(); it != numbers.end(); ++it) - { - QJsonObject number = it.value().toObject(); - std::shared_ptr<ForgeVersion> fVersion(new ForgeVersion()); - fVersion->m_buildnr = number.value("build").toDouble(); - if(fVersion->m_buildnr >= 953 && fVersion->m_buildnr <= 965) - { - qDebug() << fVersion->m_buildnr; - } - fVersion->jobbuildver = number.value("version").toString(); - fVersion->branch = number.value("branch").toString(""); - fVersion->mcver = number.value("mcversion").toString(); - fVersion->universal_filename = ""; - fVersion->installer_filename = ""; - // HACK: here, we fix the minecraft version used by forge. - // HACK: this will inevitably break (later) - // FIXME: replace with a dictionary - fVersion->mcver_sane = fVersion->mcver; - fVersion->mcver_sane.replace("_pre", "-pre"); - - QString universal_filename, installer_filename; - QJsonArray files = number.value("files").toArray(); - for (auto fIt = files.begin(); fIt != files.end(); ++fIt) - { - // TODO with gradle we also get checksums, use them - QJsonArray file = (*fIt).toArray(); - if (file.size() < 3) - { - continue; - } - - QString extension = file.at(0).toString(); - QString part = file.at(1).toString(); - QString checksum = file.at(2).toString(); - - // insane form of mcver is used here - QString longVersion = fVersion->mcver + "-" + fVersion->jobbuildver; - if (!fVersion->branch.isEmpty()) - { - longVersion = longVersion + "-" + fVersion->branch; - } - QString filename = artifact + "-" + longVersion + "-" + part + "." + extension; - - QString url = QString("%1/%2/%3") - .arg(webpath) - .arg(longVersion) - .arg(filename); - - if (part == "installer") - { - fVersion->installer_url = url; - installer_filename = filename; - } - else if (part == "universal") - { - fVersion->universal_url = url; - universal_filename = filename; - } - else if (part == "changelog") - { - fVersion->changelog_url = url; - } - } - if (fVersion->installer_url.isEmpty() && fVersion->universal_url.isEmpty()) - { - continue; - } - fVersion->universal_filename = universal_filename; - fVersion->installer_filename = installer_filename; - fVersion->type = ForgeVersion::Gradle; - out.append(fVersion); - lookup[fVersion->m_buildnr] = fVersion; - } - QJsonObject promos = root.value("promos").toObject(); - for (auto it = promos.begin(); it != promos.end(); ++it) - { - QString key = it.key(); - int build = it.value().toInt(); - QRegularExpression regexp("^(?<mcversion>[0-9]+(.[0-9]+)*)-(?<label>[a-z]+)$"); - auto match = regexp.match(key); - if(!match.hasMatch()) - { - qDebug() << key << "doesn't match." << "build" << build; - continue; - } - - QString label = match.captured("label"); - if(label != "recommended") - { - continue; - } - QString mcversion = match.captured("mcversion"); - qDebug() << "Forge build" << build << "is the" << label << "for Minecraft" << mcversion << QString("<%1>").arg(key); - lookup[build]->is_recommended = true; - } - return true; -} - -void ForgeListLoadTask::listDownloaded() -{ - QList<BaseVersionPtr> list; - bool ret = true; - if (!parseForgeList(list)) - { - ret = false; - } - if (!parseForgeGradleList(list)) - { - ret = false; - } - - if (!ret) - { - return; - } - std::sort(list.begin(), list.end(), [](const BaseVersionPtr & l, const BaseVersionPtr & r) - { return (*l > *r); }); - - m_list->updateListData(list); - - emitSucceeded(); - return; -} - -void ForgeListLoadTask::listFailed() -{ - auto &reply = listDownload->m_reply; - if (reply) - { - qCritical() << "Getting forge version list failed: " << reply->errorString(); - } - else - { - qCritical() << "Getting forge version list failed for reasons unknown."; - } -} - -void ForgeListLoadTask::gradleListFailed() -{ - auto &reply = gradleListDownload->m_reply; - if (reply) - { - qCritical() << "Getting forge version list failed: " << reply->errorString(); - } - else - { - qCritical() << "Getting forge version list failed for reasons unknown."; - } -} diff --git a/libraries/logic/minecraft/forge/ForgeVersionList.h b/libraries/logic/minecraft/forge/ForgeVersionList.h deleted file mode 100644 index 62c08b2a..00000000 --- a/libraries/logic/minecraft/forge/ForgeVersionList.h +++ /dev/null @@ -1,90 +0,0 @@ -/* Copyright 2013-2015 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 "ForgeVersion.h" - -#include <QObject> -#include <QAbstractListModel> -#include <QUrl> -#include <QNetworkReply> - -#include "BaseVersionList.h" -#include "tasks/Task.h" -#include "net/NetJob.h" - -#include "multimc_logic_export.h" - -class MULTIMC_LOGIC_EXPORT ForgeVersionList : public BaseVersionList -{ - Q_OBJECT -public: - friend class ForgeListLoadTask; - - explicit ForgeVersionList(QObject *parent = 0); - - virtual Task *getLoadTask() override; - virtual bool isLoaded() override; - virtual const BaseVersionPtr at(int i) const override; - virtual int count() const override; - virtual void sortVersions() override; - - virtual BaseVersionPtr getLatestStable() const override; - - ForgeVersionPtr findVersionByVersionNr(QString version); - - virtual QVariant data(const QModelIndex &index, int role) const override; - virtual RoleList providesRoles() const override; - - virtual int columnCount(const QModelIndex &parent) const override; - -protected: - QList<BaseVersionPtr> m_vlist; - - bool m_loaded = false; - -protected -slots: - virtual void updateListData(QList<BaseVersionPtr> versions) override; -}; - -class ForgeListLoadTask : public Task -{ - Q_OBJECT - -public: - explicit ForgeListLoadTask(ForgeVersionList *vlist); - - virtual void executeTask(); - virtual bool abort(); - -protected -slots: - void listDownloaded(); - void listFailed(); - void gradleListFailed(); - -protected: - NetJobPtr listJob; - ForgeVersionList *m_list; - - CacheDownloadPtr listDownload; - CacheDownloadPtr gradleListDownload; - -private: - bool parseForgeList(QList<BaseVersionPtr> &out); - bool parseForgeGradleList(QList<BaseVersionPtr> &out); -}; diff --git a/libraries/logic/minecraft/forge/ForgeXzDownload.cpp b/libraries/logic/minecraft/forge/ForgeXzDownload.cpp deleted file mode 100644 index adf96552..00000000 --- a/libraries/logic/minecraft/forge/ForgeXzDownload.cpp +++ /dev/null @@ -1,358 +0,0 @@ -/* Copyright 2013-2015 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 "ForgeXzDownload.h" -#include <FileSystem.h> - -#include <QCryptographicHash> -#include <QFileInfo> -#include <QDateTime> -#include <QDir> -#include <QDebug> - -ForgeXzDownload::ForgeXzDownload(QString relative_path, MetaEntryPtr entry) : NetAction() -{ - m_entry = entry; - m_target_path = entry->getFullPath(); - m_pack200_xz_file.setFileTemplate("./dl_temp.XXXXXX"); - m_status = Job_NotStarted; - m_url_path = relative_path; - m_url = "http://files.minecraftforge.net/maven/" + m_url_path + ".pack.xz"; -} - -void ForgeXzDownload::start() -{ - m_status = Job_InProgress; - if (!m_entry->isStale()) - { - m_status = Job_Finished; - emit succeeded(m_index_within_job); - return; - } - // can we actually create the real, final file? - if (!FS::ensureFilePathExists(m_target_path)) - { - m_status = Job_Failed; - emit failed(m_index_within_job); - return; - } - - qDebug() << "Downloading " << m_url.toString(); - QNetworkRequest request(m_url); - request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->getETag().toLatin1()); - request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)"); - - auto worker = ENV.qnam(); - QNetworkReply *rep = worker->get(request); - - m_reply.reset(rep); - connect(rep, SIGNAL(downloadProgress(qint64, qint64)), - SLOT(downloadProgress(qint64, qint64))); - connect(rep, SIGNAL(finished()), SLOT(downloadFinished())); - connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), - SLOT(downloadError(QNetworkReply::NetworkError))); - connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead())); -} - -void ForgeXzDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) -{ - m_total_progress = bytesTotal; - m_progress = bytesReceived; - emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal); -} - -void ForgeXzDownload::downloadError(QNetworkReply::NetworkError error) -{ - // error happened during download. - // TODO: log the reason why - m_status = Job_Failed; -} - -void ForgeXzDownload::failAndTryNextMirror() -{ - m_status = Job_Failed; - emit failed(m_index_within_job); -} - -void ForgeXzDownload::downloadFinished() -{ - // if the download succeeded - if (m_status != Job_Failed) - { - // nothing went wrong... - m_status = Job_Finished; - if (m_pack200_xz_file.isOpen()) - { - // we actually downloaded something! process and isntall it - decompressAndInstall(); - return; - } - else - { - // something bad happened -- on the local machine! - m_status = Job_Failed; - m_pack200_xz_file.remove(); - m_reply.reset(); - emit failed(m_index_within_job); - return; - } - } - // else the download failed - else - { - m_status = Job_Failed; - m_pack200_xz_file.close(); - m_pack200_xz_file.remove(); - m_reply.reset(); - failAndTryNextMirror(); - return; - } -} - -void ForgeXzDownload::downloadReadyRead() -{ - - if (!m_pack200_xz_file.isOpen()) - { - if (!m_pack200_xz_file.open()) - { - /* - * Can't open the file... the job failed - */ - m_reply->abort(); - emit failed(m_index_within_job); - return; - } - } - m_pack200_xz_file.write(m_reply->readAll()); -} - -#include "xz.h" -#include "unpack200.h" -#include <stdexcept> -#include <unistd.h> - -const size_t buffer_size = 8196; - -void ForgeXzDownload::decompressAndInstall() -{ - // rewind the downloaded temp file - m_pack200_xz_file.seek(0); - // de-xz'd file - QTemporaryFile pack200_file("./dl_temp.XXXXXX"); - pack200_file.open(); - - bool xz_success = false; - // first, de-xz - { - uint8_t in[buffer_size]; - uint8_t out[buffer_size]; - struct xz_buf b; - struct xz_dec *s; - enum xz_ret ret; - xz_crc32_init(); - xz_crc64_init(); - s = xz_dec_init(XZ_DYNALLOC, 1 << 26); - if (s == nullptr) - { - xz_dec_end(s); - failAndTryNextMirror(); - return; - } - b.in = in; - b.in_pos = 0; - b.in_size = 0; - b.out = out; - b.out_pos = 0; - b.out_size = buffer_size; - while (!xz_success) - { - if (b.in_pos == b.in_size) - { - b.in_size = m_pack200_xz_file.read((char *)in, sizeof(in)); - b.in_pos = 0; - } - - ret = xz_dec_run(s, &b); - - if (b.out_pos == sizeof(out)) - { - if (pack200_file.write((char *)out, b.out_pos) != b.out_pos) - { - // msg = "Write error\n"; - xz_dec_end(s); - failAndTryNextMirror(); - return; - } - - b.out_pos = 0; - } - - if (ret == XZ_OK) - continue; - - if (ret == XZ_UNSUPPORTED_CHECK) - { - // unsupported check. this is OK, but we should log this - continue; - } - - if (pack200_file.write((char *)out, b.out_pos) != b.out_pos) - { - // write error - pack200_file.close(); - xz_dec_end(s); - return; - } - - switch (ret) - { - case XZ_STREAM_END: - xz_dec_end(s); - xz_success = true; - break; - - case XZ_MEM_ERROR: - qCritical() << "Memory allocation failed\n"; - xz_dec_end(s); - failAndTryNextMirror(); - return; - - case XZ_MEMLIMIT_ERROR: - qCritical() << "Memory usage limit reached\n"; - xz_dec_end(s); - failAndTryNextMirror(); - return; - - case XZ_FORMAT_ERROR: - qCritical() << "Not a .xz file\n"; - xz_dec_end(s); - failAndTryNextMirror(); - return; - - case XZ_OPTIONS_ERROR: - qCritical() << "Unsupported options in the .xz headers\n"; - xz_dec_end(s); - failAndTryNextMirror(); - return; - - case XZ_DATA_ERROR: - case XZ_BUF_ERROR: - qCritical() << "File is corrupt\n"; - xz_dec_end(s); - failAndTryNextMirror(); - return; - - default: - qCritical() << "Bug!\n"; - xz_dec_end(s); - failAndTryNextMirror(); - return; - } - } - } - m_pack200_xz_file.remove(); - - // revert pack200 - pack200_file.seek(0); - int handle_in = pack200_file.handle(); - // FIXME: dispose of file handles, pointers and the like. Ideally wrap in objects. - if(handle_in == -1) - { - qCritical() << "Error reopening " << pack200_file.fileName(); - failAndTryNextMirror(); - return; - } - int handle_in_dup = dup (handle_in); - if(handle_in_dup == -1) - { - qCritical() << "Error reopening " << pack200_file.fileName(); - failAndTryNextMirror(); - return; - } - FILE *file_in = fdopen (handle_in_dup, "rb"); - if(!file_in) - { - qCritical() << "Error reopening " << pack200_file.fileName(); - failAndTryNextMirror(); - return; - } - QFile qfile_out(m_target_path); - if(!qfile_out.open(QIODevice::WriteOnly)) - { - qCritical() << "Error opening " << qfile_out.fileName(); - failAndTryNextMirror(); - return; - } - int handle_out = qfile_out.handle(); - if(handle_out == -1) - { - qCritical() << "Error opening " << qfile_out.fileName(); - failAndTryNextMirror(); - return; - } - int handle_out_dup = dup (handle_out); - if(handle_out_dup == -1) - { - qCritical() << "Error reopening " << qfile_out.fileName(); - failAndTryNextMirror(); - return; - } - FILE *file_out = fdopen (handle_out_dup, "wb"); - if(!file_out) - { - qCritical() << "Error opening " << qfile_out.fileName(); - failAndTryNextMirror(); - return; - } - try - { - // NOTE: this takes ownership of both FILE pointers. That's why we duplicate them above. - unpack_200(file_in, file_out); - } - catch (std::runtime_error &err) - { - m_status = Job_Failed; - qCritical() << "Error unpacking " << pack200_file.fileName() << " : " << err.what(); - QFile f(m_target_path); - if (f.exists()) - f.remove(); - failAndTryNextMirror(); - return; - } - pack200_file.remove(); - - QFile jar_file(m_target_path); - - if (!jar_file.open(QIODevice::ReadOnly)) - { - jar_file.remove(); - failAndTryNextMirror(); - return; - } - auto hash = QCryptographicHash::hash(jar_file.readAll(), QCryptographicHash::Md5); - m_entry->setMD5Sum(hash.toHex().constData()); - jar_file.close(); - - QFileInfo output_file_info(m_target_path); - m_entry->setETag(m_reply->rawHeader("ETag").constData()); - m_entry->setLocalChangedTimestamp(output_file_info.lastModified().toUTC().toMSecsSinceEpoch()); - m_entry->setStale(false); - ENV.metacache()->updateEntry(m_entry); - - m_reply.reset(); - emit succeeded(m_index_within_job); -} diff --git a/libraries/logic/minecraft/forge/ForgeXzDownload.h b/libraries/logic/minecraft/forge/ForgeXzDownload.h deleted file mode 100644 index 67524405..00000000 --- a/libraries/logic/minecraft/forge/ForgeXzDownload.h +++ /dev/null @@ -1,59 +0,0 @@ -/* Copyright 2013-2015 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 "net/NetAction.h" -#include "net/HttpMetaCache.h" -#include <QFile> -#include <QTemporaryFile> - -typedef std::shared_ptr<class ForgeXzDownload> ForgeXzDownloadPtr; - -class ForgeXzDownload : public NetAction -{ - Q_OBJECT -public: - MetaEntryPtr m_entry; - /// if saving to file, use the one specified in this string - QString m_target_path; - /// this is the output file, if any - QTemporaryFile m_pack200_xz_file; - /// path relative to the mirror base - QString m_url_path; - -public: - explicit ForgeXzDownload(QString relative_path, MetaEntryPtr entry); - static ForgeXzDownloadPtr make(QString relative_path, MetaEntryPtr entry) - { - return ForgeXzDownloadPtr(new ForgeXzDownload(relative_path, entry)); - } - virtual ~ForgeXzDownload(){}; - -protected -slots: - virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); - virtual void downloadError(QNetworkReply::NetworkError error); - virtual void downloadFinished(); - virtual void downloadReadyRead(); - -public -slots: - virtual void start(); - -private: - void decompressAndInstall(); - void failAndTryNextMirror(); -}; diff --git a/libraries/logic/minecraft/forge/LegacyForge.cpp b/libraries/logic/minecraft/forge/LegacyForge.cpp deleted file mode 100644 index aa2c8063..00000000 --- a/libraries/logic/minecraft/forge/LegacyForge.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* Copyright 2013-2015 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 "LegacyForge.h" - -MinecraftForge::MinecraftForge(const QString &file) : Mod(file) -{ -} - -bool MinecraftForge::FixVersionIfNeeded(QString newVersion) -{/* - wxString reportedVersion = GetModVersion(); - if(reportedVersion == "..." || reportedVersion.empty()) - { - std::auto_ptr<wxFFileInputStream> in(new wxFFileInputStream("forge.zip")); - wxTempFileOutputStream out("forge.zip"); - wxTextOutputStream textout(out); - wxZipInputStream inzip(*in); - wxZipOutputStream outzip(out); - std::auto_ptr<wxZipEntry> entry; - // preserve metadata - outzip.CopyArchiveMetaData(inzip); - // copy all entries - while (entry.reset(inzip.GetNextEntry()), entry.get() != NULL) - if (!outzip.CopyEntry(entry.release(), inzip)) - return false; - // release last entry - in.reset(); - outzip.PutNextEntry("forgeversion.properties"); - - wxStringTokenizer tokenizer(newVersion,"."); - wxString verFile; - verFile << wxString("forge.major.number=") << tokenizer.GetNextToken() << "\n"; - verFile << wxString("forge.minor.number=") << tokenizer.GetNextToken() << "\n"; - verFile << wxString("forge.revision.number=") << tokenizer.GetNextToken() << "\n"; - verFile << wxString("forge.build.number=") << tokenizer.GetNextToken() << "\n"; - auto buf = verFile.ToUTF8(); - outzip.Write(buf.data(), buf.length()); - // check if we succeeded - return inzip.Eof() && outzip.Close() && out.Commit(); - } - */ - return true; -} diff --git a/libraries/logic/minecraft/forge/LegacyForge.h b/libraries/logic/minecraft/forge/LegacyForge.h deleted file mode 100644 index f51d5e85..00000000 --- a/libraries/logic/minecraft/forge/LegacyForge.h +++ /dev/null @@ -1,25 +0,0 @@ -/* Copyright 2013-2015 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/Mod.h" - -class MinecraftForge : public Mod -{ -public: - MinecraftForge(const QString &file); - bool FixVersionIfNeeded(QString newVersion); -}; diff --git a/libraries/logic/minecraft/ftb/FTBPlugin.cpp b/libraries/logic/minecraft/ftb/FTBPlugin.cpp deleted file mode 100644 index a142c106..00000000 --- a/libraries/logic/minecraft/ftb/FTBPlugin.cpp +++ /dev/null @@ -1,395 +0,0 @@ -#include "FTBPlugin.h" -#include <Env.h> -#include "FTBVersion.h" -#include "LegacyFTBInstance.h" -#include "OneSixFTBInstance.h" -#include <BaseInstance.h> -#include <InstanceList.h> -#include <minecraft/MinecraftVersionList.h> -#include <settings/INISettingsObject.h> -#include <FileSystem.h> -#include "QDebug" -#include <QXmlStreamReader> -#include <QRegularExpression> - -struct FTBRecord -{ - QString dirName; - QString name; - QString logo; - QString iconKey; - QString mcVersion; - QString description; - QString instanceDir; - QString templateDir; - bool operator==(const FTBRecord other) const - { - return instanceDir == other.instanceDir; - } -}; - -inline uint qHash(FTBRecord record) -{ - return qHash(record.instanceDir); -} - -QSet<FTBRecord> discoverFTBInstances(SettingsObjectPtr globalSettings) -{ - QSet<FTBRecord> records; - QDir dir = QDir(globalSettings->get("FTBLauncherLocal").toString()); - QDir dataDir = QDir(globalSettings->get("FTBRoot").toString()); - if (!dataDir.exists()) - { - qDebug() << "The FTB directory specified does not exist. Please check your settings"; - return records; - } - else if (!dir.exists()) - { - qDebug() << "The FTB launcher data directory specified does not exist. Please check " - "your settings"; - return records; - } - dir.cd("ModPacks"); - auto allFiles = dir.entryList(QDir::Readable | QDir::Files, QDir::Name); - for (auto filename : allFiles) - { - if (!filename.endsWith(".xml")) - continue; - auto fpath = dir.absoluteFilePath(filename); - QFile f(fpath); - qDebug() << "Discovering FTB instances -- " << fpath; - if (!f.open(QFile::ReadOnly)) - continue; - - // read the FTB packs XML. - QXmlStreamReader reader(&f); - while (!reader.atEnd()) - { - switch (reader.readNext()) - { - case QXmlStreamReader::StartElement: - { - if (reader.name() == "modpack") - { - QXmlStreamAttributes attrs = reader.attributes(); - FTBRecord record; - record.dirName = attrs.value("dir").toString(); - record.instanceDir = dataDir.absoluteFilePath(record.dirName); - record.templateDir = dir.absoluteFilePath(record.dirName); - QDir test(record.instanceDir); - qDebug() << dataDir.absolutePath() << record.instanceDir << record.dirName; - if (!test.exists()) - continue; - record.name = attrs.value("name").toString(); - record.logo = attrs.value("logo").toString(); - QString logo = record.logo; - record.iconKey = logo.remove(QRegularExpression("\\..*")); - auto customVersions = attrs.value("customMCVersions"); - if (!customVersions.isNull()) - { - QMap<QString, QString> versionMatcher; - QString customVersionsStr = customVersions.toString(); - QStringList list = customVersionsStr.split(';'); - for (auto item : list) - { - auto segment = item.split('^'); - if (segment.size() != 2) - { - qCritical() << "FTB: Segment of size < 2 in " - << customVersionsStr; - continue; - } - versionMatcher[segment[0]] = segment[1]; - } - auto actualVersion = attrs.value("version").toString(); - if (versionMatcher.contains(actualVersion)) - { - record.mcVersion = versionMatcher[actualVersion]; - } - else - { - record.mcVersion = attrs.value("mcVersion").toString(); - } - } - else - { - record.mcVersion = attrs.value("mcVersion").toString(); - } - record.description = attrs.value("description").toString(); - records.insert(record); - } - break; - } - case QXmlStreamReader::EndElement: - break; - case QXmlStreamReader::Characters: - break; - default: - break; - } - } - f.close(); - } - return records; -} - -InstancePtr loadInstance(SettingsObjectPtr globalSettings, QMap<QString, QString> &groupMap, const FTBRecord & record) -{ - InstancePtr inst; - - auto m_settings = std::make_shared<INISettingsObject>(FS::PathCombine(record.instanceDir, "instance.cfg")); - m_settings->registerSetting("InstanceType", "Legacy"); - - qDebug() << "Loading existing " << record.name; - - QString inst_type = m_settings->get("InstanceType").toString(); - if (inst_type == "LegacyFTB") - { - inst.reset(new LegacyFTBInstance(globalSettings, m_settings, record.instanceDir)); - } - else if (inst_type == "OneSixFTB") - { - inst.reset(new OneSixFTBInstance(globalSettings, m_settings, record.instanceDir)); - } - else - { - return nullptr; - } - qDebug() << "Construction " << record.instanceDir; - - SettingsObject::Lock lock(inst->settings()); - inst->init(); - qDebug() << "Init " << record.instanceDir; - inst->setGroupInitial("FTB"); - /** - * FIXME: this does not respect the user's preferences. BUT, it would work nicely with the planned pack support - * -> instead of changing the user values, change pack values (defaults you can look at and revert to) - */ - /* - inst->setName(record.name); - inst->setIconKey(record.iconKey); - inst->setNotes(record.description); - */ - if (inst->intendedVersionId() != record.mcVersion) - { - inst->setIntendedVersionId(record.mcVersion); - } - qDebug() << "Post-Process " << record.instanceDir; - if (!InstanceList::continueProcessInstance(inst, InstanceList::NoCreateError, record.instanceDir, groupMap)) - { - return nullptr; - } - qDebug() << "Final " << record.instanceDir; - return inst; -} - -InstancePtr createInstance(SettingsObjectPtr globalSettings, QMap<QString, QString> &groupMap, const FTBRecord & record) -{ - QDir rootDir(record.instanceDir); - - InstancePtr inst; - - qDebug() << "Converting " << record.name << " as new."; - - auto mcVersion = std::dynamic_pointer_cast<MinecraftVersion>(ENV.getVersion("net.minecraft", record.mcVersion)); - if (!mcVersion) - { - qCritical() << "Can't load instance " << record.instanceDir - << " because minecraft version " << record.mcVersion - << " can't be resolved."; - return nullptr; - } - - if (!rootDir.exists() && !rootDir.mkpath(".")) - { - qCritical() << "Can't create instance folder" << record.instanceDir; - return nullptr; - } - - auto m_settings = std::make_shared<INISettingsObject>(FS::PathCombine(record.instanceDir, "instance.cfg")); - m_settings->registerSetting("InstanceType", "Legacy"); - - if (mcVersion->usesLegacyLauncher()) - { - m_settings->set("InstanceType", "LegacyFTB"); - inst.reset(new LegacyFTBInstance(globalSettings, m_settings, record.instanceDir)); - } - else - { - m_settings->set("InstanceType", "OneSixFTB"); - inst.reset(new OneSixFTBInstance(globalSettings, m_settings, record.instanceDir)); - } - // initialize - { - SettingsObject::Lock lock(inst->settings()); - inst->setIntendedVersionId(mcVersion->descriptor()); - inst->init(); - inst->setGroupInitial("FTB"); - inst->setName(record.name); - inst->setIconKey(record.iconKey); - inst->setNotes(record.description); - qDebug() << "Post-Process " << record.instanceDir; - if (!InstanceList::continueProcessInstance(inst, InstanceList::NoCreateError, record.instanceDir, groupMap)) - { - return nullptr; - } - } - return inst; -} - -void FTBPlugin::loadInstances(SettingsObjectPtr globalSettings, QMap<QString, QString> &groupMap, QList<InstancePtr> &tempList) -{ - // nothing to load when we don't have - if (globalSettings->get("TrackFTBInstances").toBool() != true) - { - return; - } - - auto records = discoverFTBInstances(globalSettings); - if (!records.size()) - { - qDebug() << "No FTB instances to load."; - return; - } - qDebug() << "Loading FTB instances! -- got " << records.size(); - // process the records we acquired. - for (auto record : records) - { - qDebug() << "Loading FTB instance from " << record.instanceDir; - QString iconKey = record.iconKey; - // MMC->icons()->addIcon(iconKey, iconKey, FS::PathCombine(record.templateDir, record.logo), MMCIcon::Transient); - auto settingsFilePath = FS::PathCombine(record.instanceDir, "instance.cfg"); - qDebug() << "ICON get!"; - - if (QFileInfo(settingsFilePath).exists()) - { - auto instPtr = loadInstance(globalSettings, groupMap, record); - if (!instPtr) - { - qWarning() << "Couldn't load instance config:" << settingsFilePath; - if(!QFile::remove(settingsFilePath)) - { - qWarning() << "Couldn't remove broken instance config!"; - continue; - } - // failed to load, but removed the poisonous file - } - else - { - tempList.append(InstancePtr(instPtr)); - continue; - } - } - auto instPtr = createInstance(globalSettings, groupMap, record); - if (!instPtr) - { - qWarning() << "Couldn't create FTB instance!"; - continue; - } - tempList.append(InstancePtr(instPtr)); - } -} - -#ifdef Q_OS_WIN32 -#include <windows.h> -static const int APPDATA_BUFFER_SIZE = 1024; -#endif - -static QString getLocalCacheStorageLocation() -{ - QString ftbDefault; -#ifdef Q_OS_WIN32 - wchar_t buf[APPDATA_BUFFER_SIZE]; - if (GetEnvironmentVariableW(L"LOCALAPPDATA", buf, APPDATA_BUFFER_SIZE)) // local - { - ftbDefault = QDir(QString::fromWCharArray(buf)).absoluteFilePath("ftblauncher"); - } - else if (GetEnvironmentVariableW(L"APPDATA", buf, APPDATA_BUFFER_SIZE)) // roaming - { - ftbDefault = QDir(QString::fromWCharArray(buf)).absoluteFilePath("ftblauncher"); - } - else - { - qCritical() << "Your LOCALAPPDATA and APPDATA folders are missing!" - " If you are on windows, this means your system is broken."; - } -#elif defined(Q_OS_MAC) - ftbDefault = FS::PathCombine(QDir::homePath(), "Library/Application Support/ftblauncher"); -#else - ftbDefault = QDir::home().absoluteFilePath(".ftblauncher"); -#endif - return ftbDefault; -} - - -static QString getRoamingStorageLocation() -{ - QString ftbDefault; -#ifdef Q_OS_WIN32 - wchar_t buf[APPDATA_BUFFER_SIZE]; - QString cacheStorage; - if (GetEnvironmentVariableW(L"APPDATA", buf, APPDATA_BUFFER_SIZE)) - { - ftbDefault = QDir(QString::fromWCharArray(buf)).absoluteFilePath("ftblauncher"); - } - else - { - qCritical() << "Your APPDATA folder is missing! If you are on windows, this means your system is broken."; - } -#elif defined(Q_OS_MAC) - ftbDefault = FS::PathCombine(QDir::homePath(), "Library/Application Support/ftblauncher"); -#else - ftbDefault = QDir::home().absoluteFilePath(".ftblauncher"); -#endif - return ftbDefault; -} - -void FTBPlugin::initialize(SettingsObjectPtr globalSettings) -{ - // FTB - globalSettings->registerSetting("TrackFTBInstances", false); - QString ftbRoaming = getRoamingStorageLocation(); - QString ftbLocal = getLocalCacheStorageLocation(); - - globalSettings->registerSetting("FTBLauncherRoaming", ftbRoaming); - globalSettings->registerSetting("FTBLauncherLocal", ftbLocal); - qDebug() << "FTB Launcher paths:" << globalSettings->get("FTBLauncherRoaming").toString() - << "and" << globalSettings->get("FTBLauncherLocal").toString(); - - globalSettings->registerSetting("FTBRoot"); - if (globalSettings->get("FTBRoot").isNull()) - { - QString ftbRoot; - QFile f(QDir(globalSettings->get("FTBLauncherRoaming").toString()).absoluteFilePath("ftblaunch.cfg")); - qDebug() << "Attempting to read" << f.fileName(); - if (f.open(QFile::ReadOnly)) - { - const QString data = QString::fromLatin1(f.readAll()); - QRegularExpression exp("installPath=(.*)"); - ftbRoot = QDir::cleanPath(exp.match(data).captured(1)); -#ifdef Q_OS_WIN32 - if (!ftbRoot.isEmpty()) - { - if (ftbRoot.at(0).isLetter() && ftbRoot.size() > 1 && ftbRoot.at(1) == '/') - { - ftbRoot.remove(1, 1); - } - } -#endif - if (ftbRoot.isEmpty()) - { - qDebug() << "Failed to get FTB root path"; - } - else - { - qDebug() << "FTB is installed at" << ftbRoot; - globalSettings->set("FTBRoot", ftbRoot); - } - } - else - { - qWarning() << "Couldn't open" << f.fileName() << ":" << f.errorString(); - qWarning() << "This is perfectly normal if you don't have FTB installed"; - } - } -} diff --git a/libraries/logic/minecraft/ftb/FTBPlugin.h b/libraries/logic/minecraft/ftb/FTBPlugin.h deleted file mode 100644 index 6851d8a5..00000000 --- a/libraries/logic/minecraft/ftb/FTBPlugin.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include <BaseInstance.h> - -#include "multimc_logic_export.h" - -// Pseudo-plugin for FTB related things. Super derpy! -class MULTIMC_LOGIC_EXPORT FTBPlugin -{ -public: - static void initialize(SettingsObjectPtr globalSettings); - static void loadInstances(SettingsObjectPtr globalSettings, QMap<QString, QString> &groupMap, QList<InstancePtr> &tempList); -}; diff --git a/libraries/logic/minecraft/ftb/FTBProfileStrategy.cpp b/libraries/logic/minecraft/ftb/FTBProfileStrategy.cpp deleted file mode 100644 index f5faacae..00000000 --- a/libraries/logic/minecraft/ftb/FTBProfileStrategy.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "FTBProfileStrategy.h" -#include "OneSixFTBInstance.h" - -#include "minecraft/VersionBuildError.h" -#include "minecraft/MinecraftVersionList.h" -#include <FileSystem.h> - -#include <QDir> -#include <QUuid> -#include <QJsonDocument> -#include <QJsonArray> - -FTBProfileStrategy::FTBProfileStrategy(OneSixFTBInstance* instance) : OneSixProfileStrategy(instance) -{ -} - -void FTBProfileStrategy::loadDefaultBuiltinPatches() -{ - // FIXME: this should be here, but it needs us to be able to deal with multiple libraries paths - // OneSixProfileStrategy::loadDefaultBuiltinPatches(); - auto mcVersion = m_instance->intendedVersionId(); - auto nativeInstance = dynamic_cast<OneSixFTBInstance *>(m_instance); - - ProfilePatchPtr minecraftPatch; - { - auto mcJson = m_instance->versionsPath().absoluteFilePath(mcVersion + "/" + mcVersion + ".json"); - // load up the base minecraft patch - if(QFile::exists(mcJson)) - { - auto file = ProfileUtils::parseJsonFile(QFileInfo(mcJson), false); - file->fileId = "net.minecraft"; - file->name = QObject::tr("Minecraft (tracked)"); - file->setVanilla(true); - if(file->version.isEmpty()) - { - file->version = mcVersion; - } - for(auto addLib: file->libraries) - { - addLib->setHint("local"); - addLib->setStoragePrefix(nativeInstance->librariesPath().absolutePath()); - } - minecraftPatch = std::dynamic_pointer_cast<ProfilePatch>(file); - } - else - { - throw VersionIncomplete("net.minecraft"); - } - minecraftPatch->setOrder(-2); - } - profile->appendPatch(minecraftPatch); - - ProfilePatchPtr packPatch; - { - auto mcJson = m_instance->minecraftRoot() + "/pack.json"; - // load up the base minecraft patch - if(QFile::exists(mcJson)) - { - auto file = ProfileUtils::parseJsonFile(QFileInfo(mcJson), false); - - // adapt the loaded file - the FTB patch file format is different than ours. - file->minecraftVersion.clear(); - for(auto addLib: file->libraries) - { - addLib->setHint("local"); - addLib->setStoragePrefix(nativeInstance->librariesPath().absolutePath()); - } - file->fileId = "org.multimc.ftb.pack"; - file->setVanilla(true); - file->name = QObject::tr("%1 (FTB pack)").arg(m_instance->name()); - if(file->version.isEmpty()) - { - file->version = QObject::tr("Unknown"); - QFile versionFile (FS::PathCombine(m_instance->instanceRoot(), "version")); - if(versionFile.exists()) - { - if(versionFile.open(QIODevice::ReadOnly)) - { - // FIXME: just guessing the encoding/charset here. - auto version = QString::fromUtf8(versionFile.readAll()); - file->version = version; - } - } - } - packPatch = std::dynamic_pointer_cast<ProfilePatch>(file); - } - else - { - throw VersionIncomplete("org.multimc.ftb.pack"); - } - packPatch->setOrder(1); - } - profile->appendPatch(packPatch); - -} - -void FTBProfileStrategy::load() -{ - profile->clearPatches(); - - loadDefaultBuiltinPatches(); - loadUserPatches(); -} - -bool FTBProfileStrategy::saveOrder(ProfileUtils::PatchOrder order) -{ - return false; -} - -bool FTBProfileStrategy::resetOrder() -{ - return false; -} - -bool FTBProfileStrategy::installJarMods(QStringList filepaths) -{ - return false; -} - -bool FTBProfileStrategy::customizePatch(ProfilePatchPtr patch) -{ - return false; -} - -bool FTBProfileStrategy::revertPatch(ProfilePatchPtr patch) -{ - return false; -} diff --git a/libraries/logic/minecraft/ftb/FTBProfileStrategy.h b/libraries/logic/minecraft/ftb/FTBProfileStrategy.h deleted file mode 100644 index 522af098..00000000 --- a/libraries/logic/minecraft/ftb/FTBProfileStrategy.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include "minecraft/ProfileStrategy.h" -#include "minecraft/onesix/OneSixProfileStrategy.h" - -class OneSixFTBInstance; - -class FTBProfileStrategy : public OneSixProfileStrategy -{ -public: - FTBProfileStrategy(OneSixFTBInstance * instance); - virtual ~FTBProfileStrategy() {}; - virtual void load() override; - virtual bool resetOrder() override; - virtual bool saveOrder(ProfileUtils::PatchOrder order) override; - virtual bool installJarMods(QStringList filepaths) override; - virtual bool customizePatch (ProfilePatchPtr patch) override; - virtual bool revertPatch (ProfilePatchPtr patch) override; - -protected: - virtual void loadDefaultBuiltinPatches() override; -}; diff --git a/libraries/logic/minecraft/ftb/FTBVersion.h b/libraries/logic/minecraft/ftb/FTBVersion.h deleted file mode 100644 index 805319b4..00000000 --- a/libraries/logic/minecraft/ftb/FTBVersion.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -#include <minecraft/MinecraftVersion.h> - -class FTBVersion : public BaseVersion -{ -public: - FTBVersion(MinecraftVersionPtr parent) : m_version(parent){}; - -public: - virtual QString descriptor() override - { - return m_version->descriptor(); - } - - virtual QString name() override - { - return m_version->name(); - } - - virtual QString typeString() const override - { - return m_version->typeString(); - } - - MinecraftVersionPtr getMinecraftVersion() - { - return m_version; - } - -private: - MinecraftVersionPtr m_version; -}; diff --git a/libraries/logic/minecraft/ftb/LegacyFTBInstance.cpp b/libraries/logic/minecraft/ftb/LegacyFTBInstance.cpp deleted file mode 100644 index a7091f1d..00000000 --- a/libraries/logic/minecraft/ftb/LegacyFTBInstance.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "LegacyFTBInstance.h" -#include <settings/INISettingsObject.h> -#include <QDir> - -LegacyFTBInstance::LegacyFTBInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) : - LegacyInstance(globalSettings, settings, rootDir) -{ -} - -QString LegacyFTBInstance::id() const -{ - return "FTB/" + BaseInstance::id(); -} - -void LegacyFTBInstance::copy(const QDir &newDir) -{ - // set the target instance to be plain Legacy - INISettingsObject settings_obj(newDir.absoluteFilePath("instance.cfg")); - settings_obj.registerSetting("InstanceType", "Legacy"); - QString inst_type = settings_obj.get("InstanceType").toString(); - settings_obj.set("InstanceType", "Legacy"); -} - -QString LegacyFTBInstance::typeName() const -{ - return tr("Legacy FTB"); -} diff --git a/libraries/logic/minecraft/ftb/LegacyFTBInstance.h b/libraries/logic/minecraft/ftb/LegacyFTBInstance.h deleted file mode 100644 index 7178bca4..00000000 --- a/libraries/logic/minecraft/ftb/LegacyFTBInstance.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "minecraft/legacy/LegacyInstance.h" - -class LegacyFTBInstance : public LegacyInstance -{ - Q_OBJECT -public: - explicit LegacyFTBInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir); - virtual QString id() const; - virtual void copy(const QDir &newDir); - virtual QString typeName() const; - bool canExport() const override - { - return false; - } -}; diff --git a/libraries/logic/minecraft/ftb/OneSixFTBInstance.cpp b/libraries/logic/minecraft/ftb/OneSixFTBInstance.cpp deleted file mode 100644 index 81e939a1..00000000 --- a/libraries/logic/minecraft/ftb/OneSixFTBInstance.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include "OneSixFTBInstance.h" -#include "FTBProfileStrategy.h" - -#include "minecraft/MinecraftProfile.h" -#include "minecraft/GradleSpecifier.h" -#include "tasks/SequentialTask.h" -#include <settings/INISettingsObject.h> -#include <FileSystem.h> - -#include <QJsonArray> - -OneSixFTBInstance::OneSixFTBInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) : - OneSixInstance(globalSettings, settings, rootDir) -{ - m_globalSettings = globalSettings; -} - -void OneSixFTBInstance::copy(const QDir &newDir) -{ - QStringList libraryNames; - // create patch file - { - qDebug()<< "Creating patch file for FTB instance..."; - QFile f(minecraftRoot() + "/pack.json"); - if (!f.open(QFile::ReadOnly)) - { - qCritical() << "Couldn't open" << f.fileName() << ":" << f.errorString(); - return; - } - QJsonObject root = QJsonDocument::fromJson(f.readAll()).object(); - QJsonArray libs = root.value("libraries").toArray(); - QJsonArray outLibs; - for (auto lib : libs) - { - QJsonObject libObj = lib.toObject(); - libObj.insert("MMC-hint", QString("local")); - libObj.insert("insert", QString("prepend")); - libraryNames.append(libObj.value("name").toString()); - outLibs.append(libObj); - } - root.remove("libraries"); - root.remove("id"); - - // HACK HACK HACK HACK - // A workaround for a problem in MultiMC, triggered by a historical problem in FTB, - // triggered by Mojang getting their library versions wrong in 1.7.10 - if(intendedVersionId() == "1.7.10") - { - auto insert = [&outLibs, &libraryNames](QString name) - { - QJsonObject libObj; - libObj.insert("insert", QString("replace")); - libObj.insert("name", name); - libraryNames.push_back(name); - outLibs.prepend(libObj); - }; - insert("com.google.guava:guava:16.0"); - insert("org.apache.commons:commons-lang3:3.2.1"); - } - root.insert("+libraries", outLibs); - root.insert("order", 1); - root.insert("fileId", QString("org.multimc.ftb.pack.json")); - root.insert("name", name()); - root.insert("mcVersion", intendedVersionId()); - root.insert("version", intendedVersionId()); - FS::ensureFilePathExists(newDir.absoluteFilePath("patches/ftb.json")); - QFile out(newDir.absoluteFilePath("patches/ftb.json")); - if (!out.open(QFile::WriteOnly | QFile::Truncate)) - { - qCritical() << "Couldn't open" << out.fileName() << ":" << out.errorString(); - return; - } - out.write(QJsonDocument(root).toJson()); - } - // copy libraries - { - qDebug() << "Copying FTB libraries"; - for (auto library : libraryNames) - { - GradleSpecifier lib(library); - const QString out = QDir::current().absoluteFilePath("libraries/" + lib.toPath()); - if (QFile::exists(out)) - { - continue; - } - if (!FS::ensureFilePathExists(out)) - { - qCritical() << "Couldn't create folder structure for" << out; - } - if (!QFile::copy(librariesPath().absoluteFilePath(lib.toPath()), out)) - { - qCritical() << "Couldn't copy" << QString(lib); - } - } - } - // now set the target instance to be plain OneSix - INISettingsObject settings_obj(newDir.absoluteFilePath("instance.cfg")); - settings_obj.registerSetting("InstanceType", "Legacy"); - QString inst_type = settings_obj.get("InstanceType").toString(); - settings_obj.set("InstanceType", "OneSix"); -} - -QString OneSixFTBInstance::id() const -{ - return "FTB/" + BaseInstance::id(); -} - -QDir OneSixFTBInstance::librariesPath() const -{ - return QDir(m_globalSettings->get("FTBRoot").toString() + "/libraries"); -} - -QDir OneSixFTBInstance::versionsPath() const -{ - return QDir(m_globalSettings->get("FTBRoot").toString() + "/versions"); -} - -bool OneSixFTBInstance::providesVersionFile() const -{ - return true; -} - -void OneSixFTBInstance::createProfile() -{ - m_profile.reset(new MinecraftProfile(new FTBProfileStrategy(this))); -} - -std::shared_ptr<Task> OneSixFTBInstance::createUpdateTask() -{ - return OneSixInstance::createUpdateTask(); -} - -QString OneSixFTBInstance::typeName() const -{ - return tr("OneSix FTB"); -} - -#include "OneSixFTBInstance.moc" diff --git a/libraries/logic/minecraft/ftb/OneSixFTBInstance.h b/libraries/logic/minecraft/ftb/OneSixFTBInstance.h deleted file mode 100644 index e7f8f485..00000000 --- a/libraries/logic/minecraft/ftb/OneSixFTBInstance.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "minecraft/onesix/OneSixInstance.h" - -class OneSixFTBInstance : public OneSixInstance -{ - Q_OBJECT -public: - explicit OneSixFTBInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir); - virtual ~OneSixFTBInstance(){}; - - void copy(const QDir &newDir) override; - - virtual void createProfile() override; - - virtual std::shared_ptr<Task> createUpdateTask() override; - - virtual QString id() const override; - - QDir librariesPath() const override; - QDir versionsPath() const override; - bool providesVersionFile() const override; - virtual QString typeName() const override; - bool canExport() const override - { - return false; - } -private: - SettingsObjectPtr m_globalSettings; -}; diff --git a/libraries/logic/minecraft/legacy/LegacyInstance.cpp b/libraries/logic/minecraft/legacy/LegacyInstance.cpp deleted file mode 100644 index f8264f20..00000000 --- a/libraries/logic/minecraft/legacy/LegacyInstance.cpp +++ /dev/null @@ -1,453 +0,0 @@ -/* Copyright 2013-2015 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <QFileInfo> -#include <QDir> -#include <settings/Setting.h> - -#include "LegacyInstance.h" - -#include "minecraft/legacy/LegacyUpdate.h" -#include "launch/LaunchTask.h" -#include <launch/steps/LaunchMinecraft.h> -#include <launch/steps/PostLaunchCommand.h> -#include <launch/steps/ModMinecraftJar.h> -#include <launch/steps/Update.h> -#include <launch/steps/PreLaunchCommand.h> -#include <launch/steps/TextPrint.h> -#include <launch/steps/CheckJava.h> -#include "minecraft/ModList.h" -#include "minecraft/WorldList.h" -#include <MMCZip.h> -#include <FileSystem.h> - -LegacyInstance::LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) - : MinecraftInstance(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", ""); - /* - * custom base jar has no default. it is determined in code... see the accessor methods for - *it - * - * for instances that DO NOT have the CustomBaseJar setting (legacy instances), - * [.]minecraft/bin/mcbackup.jar is the default base jar - */ - settings->registerSetting("UseCustomBaseJar", true); - settings->registerSetting("CustomBaseJar", ""); -} - -QString LegacyInstance::baseJar() const -{ - bool customJar = m_settings->get("UseCustomBaseJar").toBool(); - if (customJar) - { - return customBaseJar(); - } - else - return defaultBaseJar(); -} - -QString LegacyInstance::customBaseJar() const -{ - QString value = m_settings->get("CustomBaseJar").toString(); - if (value.isNull() || value.isEmpty()) - { - return defaultCustomBaseJar(); - } - return value; -} - -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(); -} - - -std::shared_ptr<Task> LegacyInstance::createUpdateTask() -{ - // make sure the jar mods list is initialized by asking for it. - auto list = jarModList(); - // create an update task - return std::shared_ptr<Task>(new LegacyUpdate(this, this)); -} - -std::shared_ptr<LaunchTask> LegacyInstance::createLaunchTask(AuthSessionPtr session) -{ - auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(getSharedPtr())); - auto pptr = process.get(); - - // print a header - { - process->appendStep(std::make_shared<TextPrint>(pptr, "Minecraft folder is:\n" + minecraftRoot() + "\n\n", MessageLevel::MultiMC)); - } - { - auto step = std::make_shared<CheckJava>(pptr); - process->appendStep(step); - } - // run pre-launch command if that's needed - if(getPreLaunchCommand().size()) - { - auto step = std::make_shared<PreLaunchCommand>(pptr); - step->setWorkingDirectory(minecraftRoot()); - process->appendStep(step); - } - // if we aren't in offline mode,. - if(session->status != AuthSession::PlayableOffline) - { - process->appendStep(std::make_shared<Update>(pptr)); - } - // if there are any jar mods - if(getJarMods().size()) - { - auto step = std::make_shared<ModMinecraftJar>(pptr); - process->appendStep(step); - } - // actually launch the game - { - auto step = std::make_shared<LaunchMinecraft>(pptr); - step->setWorkingDirectory(minecraftRoot()); - step->setAuthSession(session); - process->appendStep(step); - } - // run post-exit command if that's needed - if(getPostExitCommand().size()) - { - auto step = std::make_shared<PostLaunchCommand>(pptr); - step->setWorkingDirectory(minecraftRoot()); - process->appendStep(step); - } - if (session) - { - process->setCensorFilter(createCensorFilterFromSession(session)); - } - return process; -} - -std::shared_ptr<Task> LegacyInstance::createJarModdingTask() -{ - class JarModTask : public Task - { - public: - explicit JarModTask(std::shared_ptr<LegacyInstance> inst) : Task(nullptr), m_inst(inst) - { - } - virtual void executeTask() - { - if (!m_inst->shouldRebuild()) - { - emitSucceeded(); - return; - } - - // 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(); - - // 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()) - { - emitFailed("The base jar " + baseJar.filePath() + " does not exist"); - return; - } - - if (runnableJar.exists() && !QFile::remove(runnableJar.filePath())) - { - emitFailed("Failed to delete old minecraft.jar"); - 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(); - 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 += "lwjgl " + lwjgl + "\n"; - launchScript += "launcher legacy\n"; - return launchScript; -} - -void LegacyInstance::cleanupAfterRun() -{ - // FIXME: delete the launcher and icons and whatnot. -} - -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<ModList> LegacyInstance::jarModList() const -{ - if (!jar_mod_list) - { - auto list = new ModList(jarModsDir(), modListFile()); - connect(list, SIGNAL(changed()), SLOT(jarModsChanged())); - jar_mod_list.reset(list); - } - jar_mod_list->update(); - return jar_mod_list; -} - -QList<Mod> LegacyInstance::getJarMods() const -{ - return jarModList()->allMods(); -} - -void LegacyInstance::jarModsChanged() -{ - qDebug() << "Jar mods of instance " << name() << " have changed. Jar will be rebuilt."; - setShouldRebuild(true); -} - -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; -} - -std::shared_ptr<ModList> LegacyInstance::texturePackList() 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; -} - -QString LegacyInstance::jarModsDir() const -{ - return FS::PathCombine(instanceRoot(), "instMods"); -} - -QString LegacyInstance::binDir() const -{ - return FS::PathCombine(minecraftRoot(), "bin"); -} - -QString LegacyInstance::libDir() const -{ - return FS::PathCombine(minecraftRoot(), "lib"); -} - -QString LegacyInstance::savesDir() const -{ - return FS::PathCombine(minecraftRoot(), "saves"); -} - -QString LegacyInstance::loaderModsDir() const -{ - return FS::PathCombine(minecraftRoot(), "mods"); -} - -QString LegacyInstance::coreModsDir() const -{ - return FS::PathCombine(minecraftRoot(), "coremods"); -} - -QString LegacyInstance::resourceDir() const -{ - return FS::PathCombine(minecraftRoot(), "resources"); -} -QString LegacyInstance::texturePacksDir() const -{ - return FS::PathCombine(minecraftRoot(), "texturepacks"); -} - -QString LegacyInstance::runnableJar() const -{ - return FS::PathCombine(binDir(), "minecraft.jar"); -} - -QString LegacyInstance::modListFile() const -{ - return FS::PathCombine(instanceRoot(), "modlist"); -} - -QString LegacyInstance::instanceConfigFolder() const -{ - return FS::PathCombine(minecraftRoot(), "config"); -} - -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"); - if (!var.isValid() || var.toBool() == false) - { - return intendedVersionId() != currentVersionId(); - } - return true; -} - -void LegacyInstance::setShouldUpdate(bool val) -{ - settings()->set("ShouldUpdate", val); -} - -QString LegacyInstance::defaultBaseJar() const -{ - return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar"; -} - -QString LegacyInstance::defaultCustomBaseJar() const -{ - return FS::PathCombine(binDir(), "mcbackup.jar"); -} - -QString LegacyInstance::lwjglFolder() const -{ - return m_lwjglFolderSetting->get().toString(); -} - -QString LegacyInstance::typeName() const -{ - return tr("Legacy"); -} diff --git a/libraries/logic/minecraft/legacy/LegacyInstance.h b/libraries/logic/minecraft/legacy/LegacyInstance.h deleted file mode 100644 index 3bef240d..00000000 --- a/libraries/logic/minecraft/legacy/LegacyInstance.h +++ /dev/null @@ -1,142 +0,0 @@ -/* Copyright 2013-2015 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 "multimc_logic_export.h" - -class ModList; -class Task; - -class MULTIMC_LOGIC_EXPORT LegacyInstance : public MinecraftInstance -{ - Q_OBJECT -public: - - explicit LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir); - - virtual void init() override {}; - - /// Path to the instance's minecraft.jar - QString runnableJar() const; - - //! 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<ModList> 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; - QString texturePacksDir() const; - QString jarModsDir() const; - QString binDir() const; - QString loaderModsDir() const; - QString coreModsDir() const; - QString resourceDir() const; - virtual QString instanceConfigFolder() const override; - - /// Get the curent base jar of this instance. By default, it's the - /// versions/$version/$version.jar - QString baseJar() const; - - /// the default base jar of this instance - QString defaultBaseJar() const; - /// the default custom base jar of this instance - QString defaultCustomBaseJar() const; - - /*! - * 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); - - /*! - * Whether or not the instance's minecraft.jar needs to be rebuilt. - * If this is true, when the instance launches, its jar mods will be - * re-added to a fresh minecraft.jar file. - */ - bool shouldRebuild() const; - 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; - - /// 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 - { - return {"legacy-instance", "texturepacks"}; - }; - - virtual bool shouldUpdate() const override; - virtual void setShouldUpdate(bool val) override; - virtual std::shared_ptr<Task> createUpdateTask() override; - - virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override; - - virtual std::shared_ptr<Task> createJarModdingTask() override; - - virtual QString createLaunchScript(AuthSessionPtr session) override; - - virtual void cleanupAfterRun() override; - - virtual QString typeName() const override; - - bool canExport() const override - { - return true; - } - -protected: - mutable std::shared_ptr<ModList> 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/libraries/logic/minecraft/legacy/LegacyUpdate.cpp b/libraries/logic/minecraft/legacy/LegacyUpdate.cpp deleted file mode 100644 index 2d7e8dd2..00000000 --- a/libraries/logic/minecraft/legacy/LegacyUpdate.cpp +++ /dev/null @@ -1,393 +0,0 @@ -/* Copyright 2013-2015 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 "LwjglVersionList.h" -#include "minecraft/MinecraftVersionList.h" -#include "minecraft/ModList.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(CacheDownload::make(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; - - 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; - } - - auto list = std::dynamic_pointer_cast<LWJGLVersionList>(ENV.getVersionList("org.lwjgl.legacy")); - if (!list->isLoaded()) - { - emitFailed("Too soon! Let the LWJGL list load :)"); - return; - } - - setStatus(tr("Downloading new LWJGL...")); - auto version = std::dynamic_pointer_cast<LWJGLVersion>(list->findVersion(lwjglVersion)); - if (!version) - { - emitFailed("Game update failed: the selected LWJGL version is invalid."); - 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.get(), &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->binDir()); - 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(CacheDownload::make(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/libraries/logic/minecraft/legacy/LegacyUpdate.h b/libraries/logic/minecraft/legacy/LegacyUpdate.h deleted file mode 100644 index c52bf934..00000000 --- a/libraries/logic/minecraft/legacy/LegacyUpdate.h +++ /dev/null @@ -1,70 +0,0 @@ -/* Copyright 2013-2015 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/libraries/logic/minecraft/legacy/LwjglVersionList.cpp b/libraries/logic/minecraft/legacy/LwjglVersionList.cpp deleted file mode 100644 index bb017368..00000000 --- a/libraries/logic/minecraft/legacy/LwjglVersionList.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/* Copyright 2013-2015 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 "http://sourceforge.net/projects/java-game-lib/rss" - -LWJGLVersionList::LWJGLVersionList(QObject *parent) : BaseVersionList(parent) -{ - setLoading(false); -} - -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; -} - -bool LWJGLVersionList::isLoading() const -{ - return m_loading; -} - -void LWJGLVersionList::loadList() -{ - Q_ASSERT_X(!m_loading, "loadList", "list is already loading (m_loading is true)"); - - setLoading(true); - auto worker = ENV.qnam(); - QNetworkRequest req(QUrl(RSS_URL)); - req.setRawHeader("Accept", "application/rss+xml, text/xml, */*"); - req.setRawHeader("User-Agent", "MultiMC/5.0 (Uncached)"); - reply = worker->get(req); - connect(reply, SIGNAL(finished()), SLOT(netRequestComplete())); -} - -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::netRequestComplete() -{ - if (reply->error() == QNetworkReply::NoError) - { - QRegExp lwjglRegex("lwjgl-(([0-9]\\.?)+)\\.zip"); - Q_ASSERT_X(lwjglRegex.isValid(), "load LWJGL list", "LWJGL regex is invalid"); - - QDomDocument doc; - - QString xmlErrorMsg; - int errorLine; - auto rawData = reply->readAll(); - if (!doc.setContent(rawData, false, &xmlErrorMsg, &errorLine)) - { - failed("Failed to load LWJGL list. XML error: " + xmlErrorMsg + " at line " + - QString::number(errorLine)); - setLoading(false); - return; - } - - 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."; - finished(); - } - else - { - failed("Failed to load LWJGL list. Network error: " + reply->errorString()); - } - - setLoading(false); - reply->deleteLater(); -} - -void LWJGLVersionList::failed(QString msg) -{ - qCritical() << msg; - emit loadListFailed(msg); -} - -void LWJGLVersionList::finished() -{ - emit loadListFinished(); -} - -void LWJGLVersionList::setLoading(bool loading) -{ - m_loading = loading; - emit loadingStateUpdated(m_loading); -} diff --git a/libraries/logic/minecraft/legacy/LwjglVersionList.h b/libraries/logic/minecraft/legacy/LwjglVersionList.h deleted file mode 100644 index f043f6e2..00000000 --- a/libraries/logic/minecraft/legacy/LwjglVersionList.h +++ /dev/null @@ -1,156 +0,0 @@ -/* Copyright 2013-2015 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" - -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 Task* getLoadTask() override - { - return nullptr; - } - - 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; - - virtual bool isLoading() const; - virtual bool errored() const - { - return m_errored; - } - - virtual QString lastErrorMsg() const - { - return m_lastErrorMsg; - } - -public -slots: - /*! - * Loads the version list. - * This is done asynchronously. On success, the loadListFinished() signal will - * be emitted. The list model will be reset as well, resulting in the modelReset() - * signal being emitted. Note that the model will be reset before loadListFinished() is - * emitted. - * If loading the list failed, the loadListFailed(QString msg), - * signal will be emitted. - */ - virtual void loadList(); - -signals: - /*! - * Emitted when the list either starts or finishes loading. - * \param loading Whether or not the list is loading. - */ - void loadingStateUpdated(bool loading); - - void loadListFinished(); - - void loadListFailed(QString msg); - -private: - QList<PtrLWJGLVersion> m_vlist; - - QNetworkReply *m_netReply; - QNetworkReply *reply; - - bool m_loading; - bool m_errored; - QString m_lastErrorMsg; - - void failed(QString msg); - - void finished(); - - void setLoading(bool loading); - -private -slots: - virtual void netRequestComplete(); -}; diff --git a/libraries/logic/minecraft/liteloader/LiteLoaderInstaller.cpp b/libraries/logic/minecraft/liteloader/LiteLoaderInstaller.cpp deleted file mode 100644 index 25297fa4..00000000 --- a/libraries/logic/minecraft/liteloader/LiteLoaderInstaller.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* Copyright 2013-2015 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 "LiteLoaderInstaller.h" - -#include <QJsonArray> -#include <QJsonDocument> - -#include <QDebug> - -#include "minecraft/MinecraftProfile.h" -#include "minecraft/Library.h" -#include "minecraft/onesix/OneSixInstance.h" -#include <minecraft/onesix/OneSixVersionFormat.h> -#include "minecraft/liteloader/LiteLoaderVersionList.h" -#include "Exception.h" - -LiteLoaderInstaller::LiteLoaderInstaller() : BaseInstaller() -{ -} - -void LiteLoaderInstaller::prepare(LiteLoaderVersionPtr version) -{ - m_version = version; -} -bool LiteLoaderInstaller::add(OneSixInstance *to) -{ - if (!BaseInstaller::add(to)) - { - return false; - } - - QJsonObject obj; - - obj.insert("mainClass", QString("net.minecraft.launchwrapper.Launch")); - obj.insert("+tweakers", QJsonArray::fromStringList(QStringList() << m_version->tweakClass)); - obj.insert("order", 10); - - QJsonArray libraries; - - for (auto Library : m_version->libraries) - { - libraries.append(OneSixVersionFormat::libraryToJson(Library.get())); - } - - // liteloader - { - Library liteloaderLib("com.mumfrey:liteloader:" + m_version->version); - liteloaderLib.setAbsoluteUrl(QString("http://dl.liteloader.com/versions/com/mumfrey/liteloader/%1/%2").arg(m_version->mcVersion, m_version->file)); - QJsonObject llLibObj = OneSixVersionFormat::libraryToJson(&liteloaderLib); - libraries.append(llLibObj); - } - - obj.insert("+libraries", libraries); - obj.insert("name", QString("LiteLoader")); - obj.insert("fileId", id()); - obj.insert("version", m_version->version); - obj.insert("mcVersion", to->intendedVersionId()); - - QFile file(filename(to->instanceRoot())); - if (!file.open(QFile::WriteOnly)) - { - qCritical() << "Error opening" << file.fileName() - << "for reading:" << file.errorString(); - return false; - } - file.write(QJsonDocument(obj).toJson()); - file.close(); - - return true; -} - -class LiteLoaderInstallTask : public Task -{ - Q_OBJECT -public: - LiteLoaderInstallTask(LiteLoaderInstaller *installer, OneSixInstance *instance, - BaseVersionPtr version, QObject *parent) - : Task(parent), m_installer(installer), m_instance(instance), m_version(version) - { - } - -protected: - void executeTask() override - { - LiteLoaderVersionPtr liteloaderVersion = - std::dynamic_pointer_cast<LiteLoaderVersion>(m_version); - if (!liteloaderVersion) - { - return; - } - m_installer->prepare(liteloaderVersion); - if (!m_installer->add(m_instance)) - { - emitFailed(tr("For reasons unknown, the LiteLoader installation failed. Check your " - "MultiMC log files for details.")); - } - else - { - try - { - m_instance->reloadProfile(); - emitSucceeded(); - } - catch (Exception &e) - { - emitFailed(e.cause()); - } - catch (...) - { - emitFailed( - tr("Failed to load the version description file for reasons unknown.")); - } - } - } - -private: - LiteLoaderInstaller *m_installer; - OneSixInstance *m_instance; - BaseVersionPtr m_version; -}; - -Task *LiteLoaderInstaller::createInstallTask(OneSixInstance *instance, - BaseVersionPtr version, - QObject *parent) -{ - return new LiteLoaderInstallTask(this, instance, version, parent); -} - -#include "LiteLoaderInstaller.moc" diff --git a/libraries/logic/minecraft/liteloader/LiteLoaderInstaller.h b/libraries/logic/minecraft/liteloader/LiteLoaderInstaller.h deleted file mode 100644 index fe0aee3d..00000000 --- a/libraries/logic/minecraft/liteloader/LiteLoaderInstaller.h +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright 2013-2015 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 <QString> -#include <QMap> - -#include "BaseInstaller.h" -#include "LiteLoaderVersionList.h" - -#include "multimc_logic_export.h" - -class MULTIMC_LOGIC_EXPORT LiteLoaderInstaller : public BaseInstaller -{ -public: - LiteLoaderInstaller(); - - void prepare(LiteLoaderVersionPtr version); - bool add(OneSixInstance *to) override; - virtual QString id() const override { return "com.mumfrey.liteloader"; } - - Task *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) override; - -private: - LiteLoaderVersionPtr m_version; -}; diff --git a/libraries/logic/minecraft/liteloader/LiteLoaderVersionList.cpp b/libraries/logic/minecraft/liteloader/LiteLoaderVersionList.cpp deleted file mode 100644 index b0c9736a..00000000 --- a/libraries/logic/minecraft/liteloader/LiteLoaderVersionList.cpp +++ /dev/null @@ -1,276 +0,0 @@ -/* Copyright 2013-2015 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 "LiteLoaderVersionList.h" -#include <minecraft/onesix/OneSixVersionFormat.h> -#include "Env.h" -#include "net/URLConstants.h" -#include "Exception.h" - -#include <QtXml> - -#include <QJsonDocument> -#include <QJsonObject> -#include <QJsonArray> -#include <QJsonValue> -#include <QJsonParseError> - -#include <QtAlgorithms> - -#include <QtNetwork> - -LiteLoaderVersionList::LiteLoaderVersionList(QObject *parent) : BaseVersionList(parent) -{ -} - -Task *LiteLoaderVersionList::getLoadTask() -{ - return new LLListLoadTask(this); -} - -bool LiteLoaderVersionList::isLoaded() -{ - return m_loaded; -} - -const BaseVersionPtr LiteLoaderVersionList::at(int i) const -{ - return m_vlist.at(i); -} - -int LiteLoaderVersionList::count() const -{ - return m_vlist.count(); -} - -static bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second) -{ - auto left = std::dynamic_pointer_cast<LiteLoaderVersion>(first); - auto right = std::dynamic_pointer_cast<LiteLoaderVersion>(second); - return left->timestamp > right->timestamp; -} - -void LiteLoaderVersionList::sortVersions() -{ - beginResetModel(); - std::sort(m_vlist.begin(), m_vlist.end(), cmpVersions); - endResetModel(); -} - -QVariant LiteLoaderVersionList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - if (index.row() > count()) - return QVariant(); - - auto version = std::dynamic_pointer_cast<LiteLoaderVersion>(m_vlist[index.row()]); - switch (role) - { - case VersionPointerRole: - return qVariantFromValue(m_vlist[index.row()]); - - case VersionRole: - return version->name(); - - case VersionIdRole: - return version->descriptor(); - - case ParentGameVersionRole: - return version->mcVersion; - - case RecommendedRole: - return version->isLatest; - - default: - return QVariant(); - } -} - -QList<BaseVersionList::ModelRoles> LiteLoaderVersionList::providesRoles() -{ - return {VersionPointerRole, VersionRole, VersionIdRole, ParentGameVersionRole, RecommendedRole}; -} - -BaseVersionPtr LiteLoaderVersionList::getLatestStable() const -{ - for (int i = 0; i < m_vlist.length(); i++) - { - auto ver = std::dynamic_pointer_cast<LiteLoaderVersion>(m_vlist.at(i)); - if (ver->isLatest) - { - return m_vlist.at(i); - } - } - return BaseVersionPtr(); -} - -void LiteLoaderVersionList::updateListData(QList<BaseVersionPtr> versions) -{ - beginResetModel(); - m_vlist = versions; - m_loaded = true; - std::sort(m_vlist.begin(), m_vlist.end(), cmpVersions); - endResetModel(); -} - -LLListLoadTask::LLListLoadTask(LiteLoaderVersionList *vlist) -{ - m_list = vlist; -} - -LLListLoadTask::~LLListLoadTask() -{ -} - -void LLListLoadTask::executeTask() -{ - setStatus(tr("Loading LiteLoader version list...")); - auto job = new NetJob("Version index"); - // we do not care if the version is stale or not. - auto liteloaderEntry = ENV.metacache()->resolveEntry("liteloader", "versions.json"); - - // verify by poking the server. - liteloaderEntry->setStale(true); - - job->addNetAction(listDownload = CacheDownload::make(QUrl(URLConstants::LITELOADER_URL), - liteloaderEntry)); - - connect(listDownload.get(), SIGNAL(failed(int)), SLOT(listFailed())); - - listJob.reset(job); - connect(listJob.get(), SIGNAL(succeeded()), SLOT(listDownloaded())); - connect(listJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); - listJob->start(); -} - -void LLListLoadTask::listFailed() -{ - emitFailed("Failed to load LiteLoader version list."); - return; -} - -void LLListLoadTask::listDownloaded() -{ - QByteArray data; - { - auto dlJob = listDownload; - auto filename = std::dynamic_pointer_cast<CacheDownload>(dlJob)->getTargetFilepath(); - QFile listFile(filename); - if (!listFile.open(QIODevice::ReadOnly)) - { - emitFailed("Failed to open the LiteLoader version list."); - return; - } - data = listFile.readAll(); - listFile.close(); - dlJob.reset(); - } - - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); - - if (jsonError.error != QJsonParseError::NoError) - { - emitFailed("Error parsing version list JSON:" + jsonError.errorString()); - return; - } - - if (!jsonDoc.isObject()) - { - emitFailed("Error parsing version list JSON: jsonDoc is not an object"); - return; - } - - const QJsonObject root = jsonDoc.object(); - - // Now, get the array of versions. - if (!root.value("versions").isObject()) - { - emitFailed("Error parsing version list JSON: missing 'versions' object"); - return; - } - - auto meta = root.value("meta").toObject(); - QString description = meta.value("description").toString(tr("This is a lightweight loader for mods that don't change game mechanics.")); - QString defaultUrl = meta.value("url").toString("http://dl.liteloader.com"); - QString authors = meta.value("authors").toString("Mumfrey"); - auto versions = root.value("versions").toObject(); - - QList<BaseVersionPtr> tempList; - for (auto vIt = versions.begin(); vIt != versions.end(); ++vIt) - { - const QString mcVersion = vIt.key(); - QString latest; - const QJsonObject artefacts = vIt.value() - .toObject() - .value("artefacts") - .toObject() - .value("com.mumfrey:liteloader") - .toObject(); - QList<BaseVersionPtr> perMcVersionList; - for (auto aIt = artefacts.begin(); aIt != artefacts.end(); ++aIt) - { - const QString identifier = aIt.key(); - const QJsonObject artefact = aIt.value().toObject(); - if (identifier == "latest") - { - latest = artefact.value("version").toString(); - continue; - } - LiteLoaderVersionPtr version(new LiteLoaderVersion()); - version->version = artefact.value("version").toString(); - version->file = artefact.value("file").toString(); - version->mcVersion = mcVersion; - version->md5 = artefact.value("md5").toString(); - version->timestamp = artefact.value("timestamp").toString().toInt(); - version->tweakClass = artefact.value("tweakClass").toString(); - version->authors = authors; - version->description = description; - version->defaultUrl = defaultUrl; - const QJsonArray libs = artefact.value("libraries").toArray(); - for (auto lIt = libs.begin(); lIt != libs.end(); ++lIt) - { - auto libobject = (*lIt).toObject(); - try - { - auto lib = OneSixVersionFormat::libraryFromJson(libobject, "versions.json"); - // hack to make liteloader 1.7.10_00 work - if(lib->rawName() == GradleSpecifier("org.ow2.asm:asm-all:5.0.3")) - { - lib->setRepositoryURL("http://repo.maven.apache.org/maven2/"); - } - version->libraries.append(lib); - } - catch (Exception &e) - { - qCritical() << "Couldn't read JSON object:"; - continue; - } - } - perMcVersionList.append(version); - } - for (auto version : perMcVersionList) - { - auto v = std::dynamic_pointer_cast<LiteLoaderVersion>(version); - v->isLatest = v->version == latest; - } - tempList.append(perMcVersionList); - } - m_list->updateListData(tempList); - - emitSucceeded(); -} diff --git a/libraries/logic/minecraft/liteloader/LiteLoaderVersionList.h b/libraries/logic/minecraft/liteloader/LiteLoaderVersionList.h deleted file mode 100644 index 1dba4b6a..00000000 --- a/libraries/logic/minecraft/liteloader/LiteLoaderVersionList.h +++ /dev/null @@ -1,119 +0,0 @@ -/* Copyright 2013-2015 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 <QString> -#include <QStringList> -#include "BaseVersion.h" -#include "BaseVersionList.h" -#include "tasks/Task.h" -#include "net/NetJob.h" -#include <minecraft/Library.h> - -#include "multimc_logic_export.h" - -class LLListLoadTask; -class QNetworkReply; - -class LiteLoaderVersion : public BaseVersion -{ -public: - QString descriptor() override - { - if (isLatest) - { - return QObject::tr("Latest"); - } - return QString(); - } - QString typeString() const override - { - return mcVersion; - } - QString name() override - { - return version; - } - - // important info - QString version; - QString file; - QString mcVersion; - QString md5; - int timestamp; - bool isLatest; - QString tweakClass; - QList<LibraryPtr> libraries; - - // meta - QString defaultUrl; - QString description; - QString authors; -}; -typedef std::shared_ptr<LiteLoaderVersion> LiteLoaderVersionPtr; - -class MULTIMC_LOGIC_EXPORT LiteLoaderVersionList : public BaseVersionList -{ - Q_OBJECT -public: - friend class LLListLoadTask; - - explicit LiteLoaderVersionList(QObject *parent = 0); - - virtual Task *getLoadTask(); - virtual bool isLoaded(); - virtual const BaseVersionPtr at(int i) const; - virtual int count() const; - virtual void sortVersions(); - virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; - virtual QList< ModelRoles > providesRoles(); - - virtual BaseVersionPtr getLatestStable() const; - -protected: - QList<BaseVersionPtr> m_vlist; - - bool m_loaded = false; - -protected -slots: - virtual void updateListData(QList<BaseVersionPtr> versions); -}; - -class LLListLoadTask : public Task -{ - Q_OBJECT - -public: - explicit LLListLoadTask(LiteLoaderVersionList *vlist); - ~LLListLoadTask(); - - virtual void executeTask(); - -protected -slots: - void listDownloaded(); - void listFailed(); - -protected: - NetJobPtr listJob; - CacheDownloadPtr listDownload; - LiteLoaderVersionList *m_list; -}; - -Q_DECLARE_METATYPE(LiteLoaderVersionPtr) diff --git a/libraries/logic/minecraft/onesix/OneSixInstance.cpp b/libraries/logic/minecraft/onesix/OneSixInstance.cpp deleted file mode 100644 index 258e26c5..00000000 --- a/libraries/logic/minecraft/onesix/OneSixInstance.cpp +++ /dev/null @@ -1,597 +0,0 @@ -/* Copyright 2013-2015 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 <QDebug> -#include <Env.h> - -#include "OneSixInstance.h" -#include "OneSixUpdate.h" -#include "OneSixProfileStrategy.h" - -#include "minecraft/MinecraftProfile.h" -#include "minecraft/VersionBuildError.h" -#include "launch/LaunchTask.h" -#include "launch/steps/PreLaunchCommand.h" -#include "launch/steps/Update.h" -#include "launch/steps/LaunchMinecraft.h" -#include "launch/steps/PostLaunchCommand.h" -#include "launch/steps/TextPrint.h" -#include "launch/steps/ModMinecraftJar.h" -#include "launch/steps/CheckJava.h" -#include "MMCZip.h" - -#include "minecraft/AssetsUtils.h" -#include "minecraft/WorldList.h" -#include <FileSystem.h> - -OneSixInstance::OneSixInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) - : MinecraftInstance(globalSettings, settings, rootDir) -{ - m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, ""); -} - -void OneSixInstance::init() -{ - createProfile(); -} - -void OneSixInstance::createProfile() -{ - m_profile.reset(new MinecraftProfile(new OneSixProfileStrategy(this))); -} - -QSet<QString> OneSixInstance::traits() -{ - auto version = getMinecraftProfile(); - if (!version) - { - return {"version-incomplete"}; - } - else - { - return version->getTraits(); - } -} - -std::shared_ptr<Task> OneSixInstance::createUpdateTask() -{ - return std::shared_ptr<Task>(new OneSixUpdate(this)); -} - -QString replaceTokensIn(QString text, QMap<QString, QString> 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) -{ - QString args_pattern = m_profile->getMinecraftArguments(); - for (auto tweaker : m_profile->getTweakers()) - { - args_pattern += " --tweakClass " + tweaker; - } - - QMap<QString, QString> token_mapping; - // yggdrasil! - 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; - - // 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(); - - token_mapping["user_properties"] = session->serializeUserProperties(); - token_mapping["user_type"] = session->user_type; - - // 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::createLaunchScript(AuthSessionPtr session) -{ - QString launchScript; - - if (!m_profile) - return nullptr; - - 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. - - launchScript += "mod " + mod.filename().completeBaseName() + "\n";; - } - - 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. - - launchScript += "coremod " + coremod.filename().completeBaseName() + "\n";; - } - - for(auto & jarmod: m_profile->getJarMods()) - { - launchScript += "jarmod " + jarmod->originalName + " (" + jarmod->name + ")\n"; - } - - auto mainClass = m_profile->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 - { - launchScript += "userName " + session->player_name + "\n"; - launchScript += "sessionId " + session->session + "\n"; - } - - // libraries and class path. - { - auto libs = m_profile->getLibraries(); - - QStringList jar, native, native32, native64; - for (auto lib : libs) - { - lib->getApplicableFiles(currentSystem, jar, native, native32, native64); - } - for(auto file: jar) - { - launchScript += "cp " + file + "\n"; - } - for(auto file: native) - { - launchScript += "ext " + file + "\n"; - } - for(auto file: native32) - { - launchScript += "ext32 " + file + "\n"; - } - for(auto file: native64) - { - launchScript += "ext64 " + file + "\n"; - } - QDir natives_dir(FS::PathCombine(instanceRoot(), "natives/")); - launchScript += "natives " + natives_dir.absolutePath() + "\n"; - auto jarMods = getJarMods(); - if (!jarMods.isEmpty()) - { - launchScript += "cp " + QDir(instanceRoot()).absoluteFilePath("minecraft.jar") + "\n"; - } - else - { - QString relpath = m_profile->getMinecraftVersion() + "/" + m_profile->getMinecraftVersion() + ".jar"; - launchScript += "cp " + versionsPath().absoluteFilePath(relpath) + "\n"; - } - } - - // traits. including legacyLaunch and others ;) - for (auto trait : m_profile->getTraits()) - { - launchScript += "traits " + trait + "\n"; - } - launchScript += "launcher onesix\n"; - return launchScript; -} - -std::shared_ptr<LaunchTask> OneSixInstance::createLaunchTask(AuthSessionPtr session) -{ - auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(getSharedPtr())); - auto pptr = process.get(); - - // print a header - { - process->appendStep(std::make_shared<TextPrint>(pptr, "Minecraft folder is:\n" + minecraftRoot() + "\n\n", MessageLevel::MultiMC)); - } - { - auto step = std::make_shared<CheckJava>(pptr); - process->appendStep(step); - } - // run pre-launch command if that's needed - if(getPreLaunchCommand().size()) - { - auto step = std::make_shared<PreLaunchCommand>(pptr); - step->setWorkingDirectory(minecraftRoot()); - process->appendStep(step); - } - // if we aren't in offline mode,. - if(session->status != AuthSession::PlayableOffline) - { - process->appendStep(std::make_shared<Update>(pptr)); - } - // if there are any jar mods - if(getJarMods().size()) - { - auto step = std::make_shared<ModMinecraftJar>(pptr); - process->appendStep(step); - } - // actually launch the game - { - auto step = std::make_shared<LaunchMinecraft>(pptr); - step->setWorkingDirectory(minecraftRoot()); - step->setAuthSession(session); - process->appendStep(step); - } - // run post-exit command if that's needed - if(getPostExitCommand().size()) - { - auto step = std::make_shared<PostLaunchCommand>(pptr); - step->setWorkingDirectory(minecraftRoot()); - process->appendStep(step); - } - if (session) - { - process->setCensorFilter(createCensorFilterFromSession(session)); - } - return process; -} - -std::shared_ptr<Task> OneSixInstance::createJarModdingTask() -{ - class JarModTask : public Task - { - public: - explicit JarModTask(std::shared_ptr<OneSixInstance> 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(); - QString strippedPath = version_id + "/" + version_id + "-stripped.jar"; - QFile strippedJar(strippedPath); - if(strippedJar.exists()) - { - strippedJar.remove(); - } - auto tempJarPath = QDir(m_inst->instanceRoot()).absoluteFilePath("temp.jar"); - QFile tempJar(tempJarPath); - if(tempJar.exists()) - { - tempJar.remove(); - } - auto finalJarPath = QDir(m_inst->instanceRoot()).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 sourceJarPath = m_inst->versionsPath().absoluteFilePath(version_id + "/" + version_id + ".jar"); - QString localPath = version_id + "/" + version_id + ".jar"; - auto metacache = ENV.metacache(); - auto entry = metacache->resolveEntry("versions", localPath); - QString fullJarPath = entry->getFullPath(); - if(!MMCZip::createModdedJar(sourceJarPath, finalJarPath, jarMods)) - { - emitFailed(tr("Failed to create the custom Minecraft jar file.")); - return; - } - } - emitSucceeded(); - } - std::shared_ptr<OneSixInstance> m_inst; - }; - return std::make_shared<JarModTask>(std::dynamic_pointer_cast<OneSixInstance>(shared_from_this())); -} - -void OneSixInstance::cleanupAfterRun() -{ - QString target_dir = FS::PathCombine(instanceRoot(), "natives/"); - QDir dir(target_dir); - dir.removeRecursively(); -} - -std::shared_ptr<ModList> 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<ModList> 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<ModList> 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<ModList> 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<WorldList> OneSixInstance::worldList() const -{ - if (!m_world_list) - { - m_world_list.reset(new WorldList(worldDir())); - } - return m_world_list; -} - -bool OneSixInstance::setIntendedVersionId(QString version) -{ - settings()->set("IntendedVersion", version); - if(getMinecraftProfile()) - { - clearProfile(); - } - emit propertiesChanged(this); - return true; -} - -QList< Mod > OneSixInstance::getJarMods() const -{ - QList<Mod> mods; - for (auto jarmod : m_profile->getJarMods()) - { - QString filePath = jarmodsPath().absoluteFilePath(jarmod->name); - mods.push_back(Mod(QFileInfo(filePath))); - } - return mods; -} - - -QString OneSixInstance::intendedVersionId() const -{ - return settings()->get("IntendedVersion").toString(); -} - -void OneSixInstance::setShouldUpdate(bool) -{ -} - -bool OneSixInstance::shouldUpdate() const -{ - return true; -} - -QString OneSixInstance::currentVersionId() const -{ - return intendedVersionId(); -} - -void OneSixInstance::reloadProfile() -{ - m_profile->reload(); - auto severity = m_profile->getProblemSeverity(); - if(severity == ProblemSeverity::PROBLEM_ERROR) - { - setFlag(VersionBrokenFlag); - } - else - { - unsetFlag(VersionBrokenFlag); - } - emit versionReloaded(); -} - -void OneSixInstance::clearProfile() -{ - m_profile->clear(); - emit versionReloaded(); -} - -std::shared_ptr<MinecraftProfile> 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::libDir() const -{ - return FS::PathCombine(minecraftRoot(), "lib"); -} - -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> OneSixInstance::getSharedPtr() -{ - return std::dynamic_pointer_cast<OneSixInstance>(BaseInstance::getSharedPtr()); -} - -QString OneSixInstance::typeName() const -{ - return tr("OneSix"); -} diff --git a/libraries/logic/minecraft/onesix/OneSixInstance.h b/libraries/logic/minecraft/onesix/OneSixInstance.h deleted file mode 100644 index 2dfab48c..00000000 --- a/libraries/logic/minecraft/onesix/OneSixInstance.h +++ /dev/null @@ -1,117 +0,0 @@ -/* Copyright 2013-2015 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<ModList> loaderModList() const; - std::shared_ptr<ModList> coreModList() const; - std::shared_ptr<ModList> resourcePackList() const override; - std::shared_ptr<ModList> texturePackList() const override; - std::shared_ptr<WorldList> worldList() const override; - virtual QList<Mod> getJarMods() const override; - virtual void createProfile(); - - virtual QSet<QString> traits() override; - - ////// Directories and files ////// - QString jarModsDir() const; - QString resourcePacksDir() const; - QString texturePacksDir() const; - QString loaderModsDir() const; - QString coreModsDir() const; - QString libDir() const; - QString worldDir() const; - virtual QString instanceConfigFolder() const override; - - virtual std::shared_ptr<Task> createUpdateTask() override; - virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override; - virtual std::shared_ptr<Task> createJarModdingTask() override; - - virtual QString createLaunchScript(AuthSessionPtr session) override; - - virtual void cleanupAfterRun() override; - - virtual QString intendedVersionId() const override; - virtual bool setIntendedVersionId(QString version) override; - - virtual QString currentVersionId() const override; - - 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<MinecraftProfile> 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<OneSixInstance> getSharedPtr(); - - virtual QString typeName() const override; - - bool canExport() const override - { - return true; - } - -signals: - void versionReloaded(); - -private: - QStringList processMinecraftArgs(AuthSessionPtr account); - -protected: - std::shared_ptr<MinecraftProfile> m_profile; - mutable std::shared_ptr<ModList> m_loader_mod_list; - mutable std::shared_ptr<ModList> m_core_mod_list; - mutable std::shared_ptr<ModList> m_resource_pack_list; - mutable std::shared_ptr<ModList> m_texture_pack_list; - mutable std::shared_ptr<WorldList> m_world_list; -}; - -Q_DECLARE_METATYPE(std::shared_ptr<OneSixInstance>) diff --git a/libraries/logic/minecraft/onesix/OneSixProfileStrategy.cpp b/libraries/logic/minecraft/onesix/OneSixProfileStrategy.cpp deleted file mode 100644 index af42286d..00000000 --- a/libraries/logic/minecraft/onesix/OneSixProfileStrategy.cpp +++ /dev/null @@ -1,418 +0,0 @@ -#include "OneSixProfileStrategy.h" -#include "OneSixInstance.h" -#include "OneSixVersionFormat.h" - -#include "minecraft/VersionBuildError.h" -#include "minecraft/MinecraftVersionList.h" -#include "Env.h" -#include <FileSystem.h> - -#include <QDir> -#include <QUuid> -#include <QJsonDocument> -#include <QJsonArray> - -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->fileId = "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 mcJson = FS::PathCombine(m_instance->instanceRoot(), "patches" , "net.minecraft.json"); - // load up the base minecraft patch - ProfilePatchPtr minecraftPatch; - if(QFile::exists(mcJson)) - { - auto file = ProfileUtils::parseJsonFile(QFileInfo(mcJson), false); - if(file->version.isEmpty()) - { - file->version = m_instance->intendedVersionId(); - } - file->setVanilla(false); - file->setRevertible(true); - minecraftPatch = std::dynamic_pointer_cast<ProfilePatch>(file); - } - else - { - auto mcversion = ENV.getVersion("net.minecraft", m_instance->intendedVersionId()); - minecraftPatch = std::dynamic_pointer_cast<ProfilePatch>(mcversion); - } - if (!minecraftPatch) - { - throw VersionIncomplete("net.minecraft"); - } - minecraftPatch->setOrder(-2); - profile->appendPatch(minecraftPatch); - } - - { - auto lwjglJson = FS::PathCombine(m_instance->instanceRoot(), "patches" , "org.lwjgl.json"); - ProfilePatchPtr lwjglPatch; - if(QFile::exists(lwjglJson)) - { - auto file = ProfileUtils::parseJsonFile(QFileInfo(lwjglJson), false); - file->setVanilla(false); - file->setRevertible(true); - lwjglPatch = std::dynamic_pointer_cast<ProfilePatch>(file); - } - else - { - // NOTE: this is obviously fake, is fixed in unstable. - QResource LWJGL(":/versions/LWJGL/2.9.1.json"); - auto lwjgl = ProfileUtils::parseJsonFile(LWJGL.absoluteFilePath(), false); - lwjgl->setVanilla(true); - lwjgl->setCustomizable(true); - lwjglPatch = std::dynamic_pointer_cast<ProfilePatch>(lwjgl); - } - if (!lwjglPatch) - { - throw VersionIncomplete("org.lwjgl"); - } - lwjglPatch->setOrder(-1); - profile->appendPatch(lwjglPatch); - } -} - -void OneSixProfileStrategy::loadUserPatches() -{ - // load all patches, put into map for ordering, apply in the right order - ProfileUtils::PatchOrder userOrder; - ProfileUtils::readOverrideOrders(FS::PathCombine(m_instance->instanceRoot(), "order.json"), userOrder); - QDir patches(FS::PathCombine(m_instance->instanceRoot(),"patches")); - QSet<QString> seen_extra; - - // first, load things by sort order. - for (auto id : userOrder) - { - // ignore builtins - if (id == "net.minecraft") - continue; - if (id == "org.lwjgl") - continue; - // parse the file - QString filename = patches.absoluteFilePath(id + ".json"); - QFileInfo finfo(filename); - if(!finfo.exists()) - { - qDebug() << "Patch file " << filename << " was deleted by external means..."; - continue; - } - qDebug() << "Reading" << filename << "by user order"; - VersionFilePtr file = ProfileUtils::parseJsonFile(finfo, false); - // sanity check. prevent tampering with files. - if (file->fileId != id) - { - file->addProblem(PROBLEM_WARNING, QObject::tr("load id %1 does not match internal id %2").arg(id, file->fileId)); - seen_extra.insert(file->fileId); - } - file->setRemovable(true); - file->setMovable(true); - profile->appendPatch(file); - } - // now load the rest by internal preference. - QMultiMap<int, VersionFilePtr> files; - for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) - { - // parse the file - qDebug() << "Reading" << info.fileName(); - auto file = ProfileUtils::parseJsonFile(info, true); - // ignore builtins - if (file->fileId == "net.minecraft") - continue; - if (file->fileId == "org.lwjgl") - continue; - // do not load versions with broken IDs twice - if(seen_extra.contains(file->fileId)) - continue; - // do not load what we already loaded in the first pass - if (userOrder.contains(file->fileId)) - continue; - file->setRemovable(true); - file->setMovable(true); - files.insert(file->order, file); - } - QSet<int> seen; - for (auto order : files.keys()) - { - if(seen.contains(order)) - continue; - seen.insert(order); - const auto &values = files.values(order); - if(values.size() == 1) - { - profile->appendPatch(values[0]); - continue; - } - for(auto &file: values) - { - QStringList list; - for(auto &file2: values) - { - if(file != file2) - list.append(file2->name); - } - file->addProblem(PROBLEM_WARNING, QObject::tr("%1 has the same order as the following components:\n%2").arg(file->name, list.join(", "))); - profile->appendPatch(file); - } - } -} - - -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; - } - } - - - auto preRemoveJarMod = [&](JarmodPtr jarMod) -> bool - { - QString fullpath = FS::PathCombine(m_instance->jarModsDir(), jarMod->name); - QFileInfo finfo (fullpath); - if(finfo.exists()) - { - QFile jarModFile(fullpath); - if(!jarModFile.remove()) - { - qCritical() << "File" << fullpath << "could not be removed because:" << jarModFile.errorString(); - return false; - } - return true; - } - return true; - }; - - for(auto &jarmod: patch->getJarMods()) - { - 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; - } - 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 (VersionIncomplete &error) - { - qDebug() << "Version was incomplete:" << error.cause(); - } - 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); - try - { - load(); - } - catch (VersionIncomplete &error) - { - qDebug() << "Version was incomplete:" << error.cause(); - } - 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<VersionFile>(); - auto jarMod = std::make_shared<Jarmod>(); - jarMod->name = target_filename; - jarMod->originalName = sourceInfo.completeBaseName(); - f->jarMods.append(jarMod); - f->name = target_name; - f->fileId = target_id; - f->order = profile->getFreeOrderNumber(); - QString patchFileName = FS::PathCombine(patchDir, target_id + ".json"); - f->filename = patchFileName; - f->setMovable(true); - f->setRemovable(true); - - 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(); - profile->appendPatch(f); - } - profile->saveCurrentOrder(); - profile->reapplyPatches(); - return true; -} - diff --git a/libraries/logic/minecraft/onesix/OneSixProfileStrategy.h b/libraries/logic/minecraft/onesix/OneSixProfileStrategy.h deleted file mode 100644 index 96c1ba7b..00000000 --- a/libraries/logic/minecraft/onesix/OneSixProfileStrategy.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include "minecraft/ProfileStrategy.h" - -class OneSixInstance; - -class OneSixProfileStrategy : public ProfileStrategy -{ -public: - OneSixProfileStrategy(OneSixInstance * instance); - virtual ~OneSixProfileStrategy() {}; - virtual void load() override; - virtual bool resetOrder() override; - virtual bool saveOrder(ProfileUtils::PatchOrder order) override; - virtual bool installJarMods(QStringList filepaths) override; - virtual bool removePatch(ProfilePatchPtr patch) override; - virtual bool customizePatch(ProfilePatchPtr patch) override; - virtual bool revertPatch(ProfilePatchPtr patch) override; - -protected: - virtual void loadDefaultBuiltinPatches(); - virtual void loadUserPatches(); - void upgradeDeprecatedFiles(); - -protected: - OneSixInstance *m_instance; -}; diff --git a/libraries/logic/minecraft/onesix/OneSixUpdate.cpp b/libraries/logic/minecraft/onesix/OneSixUpdate.cpp deleted file mode 100644 index 1c2cd196..00000000 --- a/libraries/logic/minecraft/onesix/OneSixUpdate.cpp +++ /dev/null @@ -1,342 +0,0 @@ -/* Copyright 2013-2015 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 <minecraft/forge/ForgeXzDownload.h> -#include "OneSixUpdate.h" -#include "OneSixInstance.h" - -#include <QtNetwork> - -#include <QFile> -#include <QFileInfo> -#include <QTextStream> -#include <QDataStream> -#include <JlCompress.h> - -#include "BaseInstance.h" -#include "minecraft/MinecraftVersionList.h" -#include "minecraft/MinecraftProfile.h" -#include "minecraft/Library.h" -#include "net/URLConstants.h" -#include "minecraft/AssetsUtils.h" -#include "Exception.h" -#include "MMCZip.h" -#include <FileSystem.h> - -OneSixUpdate::OneSixUpdate(OneSixInstance *inst, QObject *parent) : Task(parent), m_inst(inst) -{ -} - -void OneSixUpdate::executeTask() -{ - // Make directories - QDir mcDir(m_inst->minecraftRoot()); - if (!mcDir.exists() && !mcDir.mkpath(".")) - { - emitFailed(tr("Failed to create folder for minecraft binaries.")); - return; - } - - // Get a pointer to the version object that corresponds to the instance's version. - targetVersion = std::dynamic_pointer_cast<MinecraftVersion>(ENV.getVersion("net.minecraft", m_inst->intendedVersionId())); - if (targetVersion == nullptr) - { - // don't do anything if it was invalid - emitFailed(tr("The specified Minecraft version is invalid. Choose a different one.")); - return; - } - if (m_inst->providesVersionFile() || !targetVersion->needsUpdate()) - { - qDebug() << "Instance either provides a version file or doesn't need an update."; - jarlibStart(); - return; - } - versionUpdateTask = std::dynamic_pointer_cast<MinecraftVersionList>(ENV.getVersionList("net.minecraft"))->createUpdateTask(m_inst->intendedVersionId()); - if (!versionUpdateTask) - { - qDebug() << "Didn't spawn an update task."; - jarlibStart(); - return; - } - connect(versionUpdateTask.get(), SIGNAL(succeeded()), SLOT(jarlibStart())); - connect(versionUpdateTask.get(), &NetJob::failed, this, &OneSixUpdate::versionUpdateFailed); - connect(versionUpdateTask.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); - setStatus(tr("Getting the version files from Mojang...")); - versionUpdateTask->start(); -} - -void OneSixUpdate::versionUpdateFailed(QString reason) -{ - emitFailed(reason); -} - -void OneSixUpdate::assetIndexStart() -{ - setStatus(tr("Updating assets index...")); - OneSixInstance *inst = (OneSixInstance *)m_inst; - auto profile = 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(inst->name())); - - auto metacache = ENV.metacache(); - auto entry = metacache->resolveEntry("asset_indexes", localPath); - entry->setStale(true); - job->addNetAction(CacheDownload::make(indexUrl, entry)); - jarlibDownloadJob.reset(job); - - connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(assetIndexFinished())); - connect(jarlibDownloadJob.get(), &NetJob::failed, this, &OneSixUpdate::assetIndexFailed); - connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); - - qDebug() << m_inst->name() << ": Starting asset index download"; - jarlibDownloadJob->start(); -} - -void OneSixUpdate::assetIndexFinished() -{ - AssetsIndex index; - qDebug() << m_inst->name() << ": Finished asset index download"; - - OneSixInstance *inst = (OneSixInstance *)m_inst; - auto profile = 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...")); - jarlibDownloadJob = job; - connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(assetsFinished())); - connect(jarlibDownloadJob.get(), &NetJob::failed, this, &OneSixUpdate::assetsFailed); - connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); - jarlibDownloadJob->start(); - return; - } - assetsFinished(); -} - -void OneSixUpdate::assetIndexFailed(QString reason) -{ - qDebug() << m_inst->name() << ": Failed asset index download"; - emitFailed(tr("Failed to download the assets index:\n%1").arg(reason)); -} - -void OneSixUpdate::assetsFinished() -{ - emitSucceeded(); -} - -void OneSixUpdate::assetsFailed(QString reason) -{ - emitFailed(tr("Failed to download assets:\n%1").arg(reason)); -} - -void OneSixUpdate::jarlibStart() -{ - setStatus(tr("Getting the library files from Mojang...")); - qDebug() << m_inst->name() << ": downloading libraries"; - OneSixInstance *inst = (OneSixInstance *)m_inst; - inst->reloadProfile(); - if(inst->flags() & BaseInstance::VersionBrokenFlag) - { - 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<MinecraftProfile> profile = inst->getMinecraftProfile(); - // minecraft.jar for this version - { - QString version_id = profile->getMinecraftVersion(); - QString localPath = version_id + "/" + version_id + ".jar"; - QString urlstr = profile->getMainJarUrl(); - - auto job = new NetJob(tr("Libraries for instance %1").arg(inst->name())); - - auto metacache = ENV.metacache(); - auto entry = metacache->resolveEntry("versions", localPath); - job->addNetAction(CacheDownload::make(QUrl(urlstr), entry)); - jarlibDownloadJob.reset(job); - } - - auto libs = profile->getLibraries(); - - auto metacache = ENV.metacache(); - QList<LibraryPtr> brokenLocalLibs; - - QStringList failedFiles; - for (auto lib : libs) - { - auto dls = lib->getDownloads(currentSystem, metacache.get(), failedFiles); - for(auto dl : dls) - { - jarlibDownloadJob->addNetAction(dl); - } - } - if (!brokenLocalLibs.empty()) - { - jarlibDownloadJob.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(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(jarlibFinished())); - connect(jarlibDownloadJob.get(), &NetJob::failed, this, &OneSixUpdate::jarlibFailed); - connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), - SIGNAL(progress(qint64, qint64))); - - jarlibDownloadJob->start(); -} - -void OneSixUpdate::jarlibFinished() -{ - OneSixInstance *inst = (OneSixInstance *)m_inst; - std::shared_ptr<MinecraftProfile> profile = inst->getMinecraftProfile(); - - if (profile->hasTrait("legacyFML")) - { - fmllibsStart(); - } - else - { - assetIndexStart(); - } -} - -void OneSixUpdate::jarlibFailed(QString reason) -{ - QStringList failed = jarlibDownloadJob->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)); -} - -void OneSixUpdate::fmllibsStart() -{ - // Get the mod list - OneSixInstance *inst = (OneSixInstance *)m_inst; - std::shared_ptr<MinecraftProfile> profile = inst->getMinecraftProfile(); - bool forge_present = false; - - QString version = inst->intendedVersionId(); - auto &fmlLibsMapping = g_VersionFilterData.fmlLibsMapping; - if (!fmlLibsMapping.contains(version)) - { - assetIndexStart(); - 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) - { - assetIndexStart(); - 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()) - { - assetIndexStart(); - 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(CacheDownload::make(QUrl(urlString), entry)); - } - - connect(dljob, SIGNAL(succeeded()), SLOT(fmllibsFinished())); - connect(dljob, &NetJob::failed, this, &OneSixUpdate::fmllibsFailed); - connect(dljob, SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); - legacyDownloadJob.reset(dljob); - legacyDownloadJob->start(); -} - -void OneSixUpdate::fmllibsFinished() -{ - legacyDownloadJob.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->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()); - } - assetIndexStart(); -} - -void OneSixUpdate::fmllibsFailed(QString reason) -{ - emitFailed(tr("Game update failed: it was impossible to fetch the required FML libraries.\nReason:\n%1").arg(reason)); - return; -} - diff --git a/libraries/logic/minecraft/onesix/OneSixUpdate.h b/libraries/logic/minecraft/onesix/OneSixUpdate.h deleted file mode 100644 index b5195364..00000000 --- a/libraries/logic/minecraft/onesix/OneSixUpdate.h +++ /dev/null @@ -1,67 +0,0 @@ -/* Copyright 2013-2015 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" -#include <quazip.h> - -class MinecraftVersion; -class OneSixInstance; - -class OneSixUpdate : public Task -{ - Q_OBJECT -public: - explicit OneSixUpdate(OneSixInstance *inst, QObject *parent = 0); - virtual void executeTask(); - -private -slots: - void versionUpdateFailed(QString reason); - - void jarlibStart(); - void jarlibFinished(); - void jarlibFailed(QString reason); - - void fmllibsStart(); - void fmllibsFinished(); - void fmllibsFailed(QString reason); - - void assetIndexStart(); - void assetIndexFinished(); - void assetIndexFailed(QString reason); - - void assetsFinished(); - void assetsFailed(QString reason); - -private: - NetJobPtr jarlibDownloadJob; - NetJobPtr legacyDownloadJob; - - /// target version, determined during this task - std::shared_ptr<MinecraftVersion> targetVersion; - /// the task that is spawned for version updates - std::shared_ptr<Task> versionUpdateTask; - - OneSixInstance *m_inst = nullptr; - QList<FMLlib> fmlLibsToProcess; -}; diff --git a/libraries/logic/minecraft/onesix/OneSixVersionFormat.cpp b/libraries/logic/minecraft/onesix/OneSixVersionFormat.cpp deleted file mode 100644 index 541fb109..00000000 --- a/libraries/logic/minecraft/onesix/OneSixVersionFormat.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include "OneSixVersionFormat.h" -#include <Json.h> -#include "minecraft/ParseUtils.h" -#include <minecraft/MinecraftVersion.h> -#include <minecraft/VersionBuildError.h> -#include <minecraft/MojangVersionFormat.h> - -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); - 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); - 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(); - out->fileId = 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, out->name); - if(lib->originalName.isEmpty()) - { - auto fixed = out->name; - fixed.remove(" (jar mod)"); - lib->originalName = out->name; - } - // and add to jar mods - out->jarMods.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(PROBLEM_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"); - } - - /* removed features that shouldn't be used */ - if (root.contains("tweakers")) - { - out->addProblem(PROBLEM_ERROR, QObject::tr("Version file contains unsupported element 'tweakers'")); - } - if (root.contains("-libraries")) - { - out->addProblem(PROBLEM_ERROR, QObject::tr("Version file contains unsupported element '-libraries'")); - } - if (root.contains("-tweakers")) - { - out->addProblem(PROBLEM_ERROR, QObject::tr("Version file contains unsupported element '-tweakers'")); - } - if (root.contains("-minecraftArguments")) - { - out->addProblem(PROBLEM_ERROR, QObject::tr("Version file contains unsupported element '-minecraftArguments'")); - } - if (root.contains("+minecraftArguments")) - { - out->addProblem(PROBLEM_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, "fileId", patch->fileId); - writeString(root, "version", patch->version); - writeString(root, "mcVersion", patch->dependsOnMinecraftVersion); - - MojangVersionFormat::writeVersionProperties(patch.get(), root); - - 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); - } - // write the contents to a json document. - { - QJsonDocument out; - out.setObject(root); - return out; - } -} - -JarmodPtr OneSixVersionFormat::jarModFromJson(const QJsonObject &libObj, const QString &filename, const QString &originalName) -{ - JarmodPtr out(new Jarmod()); - if (!libObj.contains("name")) - { - throw JSONValidationError(filename + - "contains a jarmod that doesn't have a 'name' field"); - } - out->name = libObj.value("name").toString(); - out->originalName = libObj.value("originalName").toString(); - return out; -} - -QJsonObject OneSixVersionFormat::jarModtoJson(Jarmod *jarmod) -{ - QJsonObject out; - writeString(out, "name", jarmod->name); - if(!jarmod->originalName.isEmpty()) - { - writeString(out, "originalName", jarmod->originalName); - } - return out; -} diff --git a/libraries/logic/minecraft/onesix/OneSixVersionFormat.h b/libraries/logic/minecraft/onesix/OneSixVersionFormat.h deleted file mode 100644 index 5696e79e..00000000 --- a/libraries/logic/minecraft/onesix/OneSixVersionFormat.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include <minecraft/VersionFile.h> -#include <minecraft/MinecraftProfile.h> -#include <minecraft/Library.h> -#include <QJsonDocument> - -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); - - // jar mods - static JarmodPtr jarModFromJson(const QJsonObject &libObj, const QString &filename, const QString &originalName); - static QJsonObject jarModtoJson(Jarmod * jarmod); -}; |