aboutsummaryrefslogtreecommitdiff
path: root/api/logic/minecraft
diff options
context:
space:
mode:
authorPetr Mrázek <peterix@gmail.com>2021-07-25 19:11:59 +0200
committerPetr Mrázek <peterix@gmail.com>2021-07-25 19:50:44 +0200
commit20b9f2b42a3b58b6081af271774fbcc34025dccb (patch)
tree064fa59facb3357139b47bd4e60bfc8edb35ca11 /api/logic/minecraft
parentdd133680858351e3e07690e286882327a4f42ba5 (diff)
downloadPrismLauncher-20b9f2b42a3b58b6081af271774fbcc34025dccb.tar.gz
PrismLauncher-20b9f2b42a3b58b6081af271774fbcc34025dccb.tar.bz2
PrismLauncher-20b9f2b42a3b58b6081af271774fbcc34025dccb.zip
NOISSUE Flatten gui and logic libraries into MultiMC
Diffstat (limited to 'api/logic/minecraft')
-rw-r--r--api/logic/minecraft/AssetsUtils.cpp333
-rw-r--r--api/logic/minecraft/AssetsUtils.h53
-rw-r--r--api/logic/minecraft/Component.cpp439
-rw-r--r--api/logic/minecraft/Component.h111
-rw-r--r--api/logic/minecraft/ComponentUpdateTask.cpp704
-rw-r--r--api/logic/minecraft/ComponentUpdateTask.h37
-rw-r--r--api/logic/minecraft/ComponentUpdateTask_p.h32
-rw-r--r--api/logic/minecraft/GradleSpecifier.h151
-rw-r--r--api/logic/minecraft/GradleSpecifier_test.cpp78
-rw-r--r--api/logic/minecraft/LaunchProfile.cpp319
-rw-r--r--api/logic/minecraft/LaunchProfile.h104
-rw-r--r--api/logic/minecraft/Library.cpp309
-rw-r--r--api/logic/minecraft/Library.h219
-rw-r--r--api/logic/minecraft/Library_test.cpp272
-rw-r--r--api/logic/minecraft/MinecraftInstance.cpp1054
-rw-r--r--api/logic/minecraft/MinecraftInstance.h133
-rw-r--r--api/logic/minecraft/MinecraftLoadAndCheck.cpp45
-rw-r--r--api/logic/minecraft/MinecraftLoadAndCheck.h48
-rw-r--r--api/logic/minecraft/MinecraftUpdate.cpp182
-rw-r--r--api/logic/minecraft/MinecraftUpdate.h57
-rw-r--r--api/logic/minecraft/MojangDownloadInfo.h82
-rw-r--r--api/logic/minecraft/MojangVersionFormat.cpp383
-rw-r--r--api/logic/minecraft/MojangVersionFormat.h26
-rw-r--r--api/logic/minecraft/MojangVersionFormat_test.cpp55
-rw-r--r--api/logic/minecraft/OneSixVersionFormat.cpp391
-rw-r--r--api/logic/minecraft/OneSixVersionFormat.h30
-rw-r--r--api/logic/minecraft/OpSys.cpp42
-rw-r--r--api/logic/minecraft/OpSys.h37
-rw-r--r--api/logic/minecraft/PackProfile.cpp1225
-rw-r--r--api/logic/minecraft/PackProfile.h152
-rw-r--r--api/logic/minecraft/PackProfile_p.h42
-rw-r--r--api/logic/minecraft/ParseUtils.cpp34
-rw-r--r--api/logic/minecraft/ParseUtils.h11
-rw-r--r--api/logic/minecraft/ParseUtils_test.cpp45
-rw-r--r--api/logic/minecraft/ProfileUtils.cpp178
-rw-r--r--api/logic/minecraft/ProfileUtils.h28
-rw-r--r--api/logic/minecraft/Rule.cpp93
-rw-r--r--api/logic/minecraft/Rule.h101
-rw-r--r--api/logic/minecraft/VersionFile.cpp60
-rw-r--r--api/logic/minecraft/VersionFile.h114
-rw-r--r--api/logic/minecraft/VersionFilterData.cpp71
-rw-r--r--api/logic/minecraft/VersionFilterData.h31
-rw-r--r--api/logic/minecraft/World.cpp520
-rw-r--r--api/logic/minecraft/World.h113
-rw-r--r--api/logic/minecraft/WorldList.cpp387
-rw-r--r--api/logic/minecraft/WorldList.h131
-rw-r--r--api/logic/minecraft/auth-msa/BuildConfig.cpp.in9
-rw-r--r--api/logic/minecraft/auth-msa/BuildConfig.h11
-rw-r--r--api/logic/minecraft/auth-msa/CMakeLists.txt28
-rw-r--r--api/logic/minecraft/auth-msa/context.cpp938
-rw-r--r--api/logic/minecraft/auth-msa/context.h128
-rw-r--r--api/logic/minecraft/auth-msa/main.cpp100
-rw-r--r--api/logic/minecraft/auth-msa/mainwindow.cpp97
-rw-r--r--api/logic/minecraft/auth-msa/mainwindow.h34
-rw-r--r--api/logic/minecraft/auth-msa/mainwindow.ui72
-rw-r--r--api/logic/minecraft/auth/AuthSession.cpp30
-rw-r--r--api/logic/minecraft/auth/AuthSession.h54
-rw-r--r--api/logic/minecraft/auth/MojangAccount.cpp315
-rw-r--r--api/logic/minecraft/auth/MojangAccount.h182
-rw-r--r--api/logic/minecraft/auth/MojangAccountList.cpp468
-rw-r--r--api/logic/minecraft/auth/MojangAccountList.h201
-rw-r--r--api/logic/minecraft/auth/YggdrasilTask.cpp255
-rw-r--r--api/logic/minecraft/auth/YggdrasilTask.h151
-rw-r--r--api/logic/minecraft/auth/flows/AuthenticateTask.cpp202
-rw-r--r--api/logic/minecraft/auth/flows/AuthenticateTask.h46
-rw-r--r--api/logic/minecraft/auth/flows/RefreshTask.cpp144
-rw-r--r--api/logic/minecraft/auth/flows/RefreshTask.h44
-rw-r--r--api/logic/minecraft/auth/flows/ValidateTask.cpp61
-rw-r--r--api/logic/minecraft/auth/flows/ValidateTask.h47
-rw-r--r--api/logic/minecraft/gameoptions/GameOptions.cpp144
-rw-r--r--api/logic/minecraft/gameoptions/GameOptions.h34
-rw-r--r--api/logic/minecraft/launch/ClaimAccount.cpp24
-rw-r--r--api/logic/minecraft/launch/ClaimAccount.h37
-rw-r--r--api/logic/minecraft/launch/CreateGameFolders.cpp28
-rw-r--r--api/logic/minecraft/launch/CreateGameFolders.h37
-rw-r--r--api/logic/minecraft/launch/DirectJavaLaunch.cpp148
-rw-r--r--api/logic/minecraft/launch/DirectJavaLaunch.h58
-rw-r--r--api/logic/minecraft/launch/ExtractNatives.cpp111
-rw-r--r--api/logic/minecraft/launch/ExtractNatives.h38
-rw-r--r--api/logic/minecraft/launch/LauncherPartLaunch.cpp218
-rw-r--r--api/logic/minecraft/launch/LauncherPartLaunch.h60
-rw-r--r--api/logic/minecraft/launch/MinecraftServerTarget.cpp66
-rw-r--r--api/logic/minecraft/launch/MinecraftServerTarget.h30
-rw-r--r--api/logic/minecraft/launch/ModMinecraftJar.cpp82
-rw-r--r--api/logic/minecraft/launch/ModMinecraftJar.h36
-rw-r--r--api/logic/minecraft/launch/PrintInstanceInfo.cpp106
-rw-r--r--api/logic/minecraft/launch/PrintInstanceInfo.h41
-rw-r--r--api/logic/minecraft/launch/ReconstructAssets.cpp36
-rw-r--r--api/logic/minecraft/launch/ReconstructAssets.h33
-rw-r--r--api/logic/minecraft/launch/ScanModFolders.cpp59
-rw-r--r--api/logic/minecraft/launch/ScanModFolders.h42
-rw-r--r--api/logic/minecraft/launch/VerifyJavaInstall.cpp34
-rw-r--r--api/logic/minecraft/launch/VerifyJavaInstall.h17
-rw-r--r--api/logic/minecraft/legacy/LegacyInstance.cpp256
-rw-r--r--api/logic/minecraft/legacy/LegacyInstance.h142
-rw-r--r--api/logic/minecraft/legacy/LegacyModList.cpp136
-rw-r--r--api/logic/minecraft/legacy/LegacyModList.h49
-rw-r--r--api/logic/minecraft/legacy/LegacyUpgradeTask.cpp138
-rw-r--r--api/logic/minecraft/legacy/LegacyUpgradeTask.h30
-rw-r--r--api/logic/minecraft/mod/LocalModParseTask.cpp467
-rw-r--r--api/logic/minecraft/mod/LocalModParseTask.h37
-rw-r--r--api/logic/minecraft/mod/Mod.cpp151
-rw-r--r--api/logic/minecraft/mod/Mod.h117
-rw-r--r--api/logic/minecraft/mod/ModDetails.h17
-rw-r--r--api/logic/minecraft/mod/ModFolderLoadTask.cpp18
-rw-r--r--api/logic/minecraft/mod/ModFolderLoadTask.h29
-rw-r--r--api/logic/minecraft/mod/ModFolderModel.cpp554
-rw-r--r--api/logic/minecraft/mod/ModFolderModel.h149
-rw-r--r--api/logic/minecraft/mod/ModFolderModel_test.cpp53
-rw-r--r--api/logic/minecraft/mod/ResourcePackFolderModel.cpp23
-rw-r--r--api/logic/minecraft/mod/ResourcePackFolderModel.h13
-rw-r--r--api/logic/minecraft/mod/TexturePackFolderModel.cpp23
-rw-r--r--api/logic/minecraft/mod/TexturePackFolderModel.h13
-rw-r--r--api/logic/minecraft/services/SkinDelete.cpp42
-rw-r--r--api/logic/minecraft/services/SkinDelete.h30
-rw-r--r--api/logic/minecraft/services/SkinUpload.cpp66
-rw-r--r--api/logic/minecraft/services/SkinUpload.h39
-rw-r--r--api/logic/minecraft/testdata/1.9-simple.json198
-rw-r--r--api/logic/minecraft/testdata/1.9.json529
-rw-r--r--api/logic/minecraft/testdata/codecwav-20101023.jar1
-rw-r--r--api/logic/minecraft/testdata/lib-native-arch.json46
-rw-r--r--api/logic/minecraft/testdata/lib-native.json52
-rw-r--r--api/logic/minecraft/testdata/lib-simple.json11
-rw-r--r--api/logic/minecraft/testdata/testname-testversion-linux-32.jar1
-rw-r--r--api/logic/minecraft/update/AssetUpdateTask.cpp107
-rw-r--r--api/logic/minecraft/update/AssetUpdateTask.h28
-rw-r--r--api/logic/minecraft/update/FMLLibrariesTask.cpp131
-rw-r--r--api/logic/minecraft/update/FMLLibrariesTask.h31
-rw-r--r--api/logic/minecraft/update/FoldersTask.cpp21
-rw-r--r--api/logic/minecraft/update/FoldersTask.h17
-rw-r--r--api/logic/minecraft/update/LibrariesTask.cpp90
-rw-r--r--api/logic/minecraft/update/LibrariesTask.h26
132 files changed, 0 insertions, 18079 deletions
diff --git a/api/logic/minecraft/AssetsUtils.cpp b/api/logic/minecraft/AssetsUtils.cpp
deleted file mode 100644
index c01733b6..00000000
--- a/api/logic/minecraft/AssetsUtils.cpp
+++ /dev/null
@@ -1,333 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <QFileInfo>
-#include <QDir>
-#include <QDirIterator>
-#include <QCryptographicHash>
-#include <QJsonParseError>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QVariant>
-#include <QDebug>
-
-#include "AssetsUtils.h"
-#include "FileSystem.h"
-#include "net/Download.h"
-#include "net/ChecksumValidator.h"
-#include "BuildConfig.h"
-
-namespace {
-QSet<QString> collectPathsFromDir(QString dirPath)
-{
- QFileInfo dirInfo(dirPath);
-
- if (!dirInfo.exists())
- {
- return {};
- }
-
- QSet<QString> out;
-
- QDirIterator iter(dirPath, QDirIterator::Subdirectories);
- while (iter.hasNext())
- {
- QString value = iter.next();
- QFileInfo info(value);
- if(info.isFile())
- {
- out.insert(value);
- qDebug() << value;
- }
- }
- return out;
-}
-}
-
-
-namespace AssetsUtils
-{
-
-/*
- * Returns true on success, with index populated
- * index is undefined otherwise
- */
-bool loadAssetsIndexJson(const QString &assetsId, const 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 mapToResources = root.value("map_to_resources");
- if (!mapToResources.isUndefined())
- {
- index.mapToResources = mapToResources.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;
-}
-
-// FIXME: ugly code duplication
-QDir getAssetsDir(const QString &assetsId, const QString &resourcesFolder)
-{
- 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 determine assets path!";
- return virtualRoot;
- }
-
- AssetsIndex index;
- if(!AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, index))
- {
- qCritical() << "Failed to load asset index file" << indexPath << "; can't determine assets path!";
- return virtualRoot;
- }
-
- QString targetPath;
- if(index.isVirtual)
- {
- return virtualRoot;
- }
- else if(index.mapToResources)
- {
- return QDir(resourcesFolder);
- }
- return virtualRoot;
-}
-
-// FIXME: ugly code duplication
-bool reconstructAssets(QString assetsId, QString resourcesFolder)
-{
- 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 false;
- }
-
- qDebug() << "reconstructAssets" << assetsDir.path() << indexDir.path() << objectDir.path() << virtualDir.path() << virtualRoot.path();
-
- AssetsIndex index;
- if(!AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, index))
- {
- qCritical() << "Failed to load asset index file" << indexPath << "; can't reconstruct assets!";
- return false;
- }
-
- QString targetPath;
- bool removeLeftovers = false;
- if(index.isVirtual)
- {
- targetPath = virtualRoot.path();
- removeLeftovers = true;
- qDebug() << "Reconstructing virtual assets folder at" << targetPath;
- }
- else if(index.mapToResources)
- {
- targetPath = resourcesFolder;
- qDebug() << "Reconstructing resources folder at" << targetPath;
- }
-
- if (!targetPath.isNull())
- {
- auto presentFiles = collectPathsFromDir(targetPath);
- for (QString map : index.objects.keys())
- {
- AssetObject asset_object = index.objects.value(map);
- QString target_path = FS::PathCombine(targetPath, 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;
-
- presentFiles.remove(target_path);
-
- if (!target.exists())
- {
- QFileInfo info(target_path);
- QDir target_dir = info.dir();
-
- qDebug() << target_dir.path();
- FS::ensureFolderPathExists(target_dir.path());
-
- bool couldCopy = original.copy(target_path);
- qDebug() << " Copying" << original_path << "to" << target_path << QString::number(couldCopy);
- }
- }
-
- // TODO: Write last used time to virtualRoot/.lastused
- if(removeLeftovers)
- {
- for(auto & file: presentFiles)
- {
- qDebug() << "Would remove" << file;
- }
- }
- }
- return true;
-}
-
-}
-
-NetActionPtr AssetObject::getDownloadAction()
-{
- QFileInfo objectFile(getLocalPath());
- if ((!objectFile.isFile()) || (objectFile.size() != size))
- {
- auto objectDL = Net::Download::makeFile(getUrl(), objectFile.filePath());
- if(hash.size())
- {
- auto rawHash = QByteArray::fromHex(hash.toLatin1());
- objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash));
- }
- objectDL->m_total_progress = size;
- return objectDL;
- }
- return nullptr;
-}
-
-QString AssetObject::getLocalPath()
-{
- return "assets/objects/" + getRelPath();
-}
-
-QUrl AssetObject::getUrl()
-{
- return BuildConfig.RESOURCE_BASE + 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/api/logic/minecraft/AssetsUtils.h b/api/logic/minecraft/AssetsUtils.h
deleted file mode 100644
index 32e57060..00000000
--- a/api/logic/minecraft/AssetsUtils.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <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;
- bool mapToResources = false;
-};
-
-/// FIXME: this is absolutely horrendous. REDO!!!!
-namespace AssetsUtils
-{
-bool loadAssetsIndexJson(const QString &id, const QString &file, AssetsIndex& index);
-
-QDir getAssetsDir(const QString &assetsId, const QString &resourcesFolder);
-
-/// Reconstruct a virtual assets folder for the given assets ID and return the folder
-bool reconstructAssets(QString assetsId, QString resourcesFolder);
-}
diff --git a/api/logic/minecraft/Component.cpp b/api/logic/minecraft/Component.cpp
deleted file mode 100644
index 92821065..00000000
--- a/api/logic/minecraft/Component.cpp
+++ /dev/null
@@ -1,439 +0,0 @@
-#include <meta/VersionList.h>
-#include <meta/Index.h>
-#include <Env.h>
-#include "Component.h"
-
-#include "meta/Version.h"
-#include "VersionFile.h"
-#include "minecraft/PackProfile.h"
-#include <FileSystem.h>
-#include <QSaveFile>
-#include "OneSixVersionFormat.h"
-#include <assert.h>
-
-Component::Component(PackProfile * parent, const QString& uid)
-{
- assert(parent);
- m_parent = parent;
-
- m_uid = uid;
-}
-
-Component::Component(PackProfile * parent, std::shared_ptr<Meta::Version> version)
-{
- assert(parent);
- m_parent = parent;
-
- m_metaVersion = version;
- m_uid = version->uid();
- m_version = m_cachedVersion = version->version();
- m_cachedName = version->name();
- m_loaded = version->isLoaded();
-}
-
-Component::Component(PackProfile * parent, const QString& uid, std::shared_ptr<VersionFile> file)
-{
- assert(parent);
- m_parent = parent;
-
- m_file = file;
- m_uid = uid;
- m_cachedVersion = m_file->version;
- m_cachedName = m_file->name;
- m_loaded = true;
-}
-
-std::shared_ptr<Meta::Version> Component::getMeta()
-{
- return m_metaVersion;
-}
-
-void Component::applyTo(LaunchProfile* profile)
-{
- // do not apply disabled components
- if(!isEnabled())
- {
- return;
- }
- auto vfile = getVersionFile();
- if(vfile)
- {
- vfile->applyTo(profile);
- }
- else
- {
- profile->applyProblemSeverity(getProblemSeverity());
- }
-}
-
-std::shared_ptr<class VersionFile> Component::getVersionFile() const
-{
- if(m_metaVersion)
- {
- if(!m_metaVersion->isLoaded())
- {
- m_metaVersion->load(Net::Mode::Online);
- }
- return m_metaVersion->data();
- }
- else
- {
- return m_file;
- }
-}
-
-std::shared_ptr<class Meta::VersionList> Component::getVersionList() const
-{
- // FIXME: what if the metadata index isn't loaded yet?
- if(ENV.metadataIndex()->hasUid(m_uid))
- {
- return ENV.metadataIndex()->get(m_uid);
- }
- return nullptr;
-}
-
-int Component::getOrder()
-{
- if(m_orderOverride)
- return m_order;
-
- auto vfile = getVersionFile();
- if(vfile)
- {
- return vfile->order;
- }
- return 0;
-}
-void Component::setOrder(int order)
-{
- m_orderOverride = true;
- m_order = order;
-}
-QString Component::getID()
-{
- return m_uid;
-}
-QString Component::getName()
-{
- if (!m_cachedName.isEmpty())
- return m_cachedName;
- return m_uid;
-}
-QString Component::getVersion()
-{
- return m_cachedVersion;
-}
-QString Component::getFilename()
-{
- return m_parent->patchFilePathForUid(m_uid);
-}
-QDateTime Component::getReleaseDateTime()
-{
- if(m_metaVersion)
- {
- return m_metaVersion->time();
- }
- auto vfile = getVersionFile();
- if(vfile)
- {
- return vfile->releaseTime;
- }
- // FIXME: fake
- return QDateTime::currentDateTime();
-}
-
-bool Component::isEnabled()
-{
- return !canBeDisabled() || !m_disabled;
-}
-
-bool Component::canBeDisabled()
-{
- return isRemovable() && !m_dependencyOnly;
-}
-
-bool Component::setEnabled(bool state)
-{
- bool intendedDisabled = !state;
- if (!canBeDisabled())
- {
- intendedDisabled = false;
- }
- if(intendedDisabled != m_disabled)
- {
- m_disabled = intendedDisabled;
- emit dataChanged();
- return true;
- }
- return false;
-}
-
-bool Component::isCustom()
-{
- return m_file != nullptr;
-}
-
-bool Component::isCustomizable()
-{
- if(m_metaVersion)
- {
- if(getVersionFile())
- {
- return true;
- }
- }
- return false;
-}
-bool Component::isRemovable()
-{
- return !m_important;
-}
-bool Component::isRevertible()
-{
- if (isCustom())
- {
- if(ENV.metadataIndex()->hasUid(m_uid))
- {
- return true;
- }
- }
- return false;
-}
-bool Component::isMoveable()
-{
- // HACK, FIXME: this was too dumb and wouldn't follow dependency constraints anyway. For now hardcoded to 'true'.
- return true;
-}
-bool Component::isVersionChangeable()
-{
- auto list = getVersionList();
- if(list)
- {
- if(!list->isLoaded())
- {
- list->load(Net::Mode::Online);
- }
- return list->count() != 0;
- }
- return false;
-}
-
-void Component::setImportant(bool state)
-{
- if(m_important != state)
- {
- m_important = state;
- emit dataChanged();
- }
-}
-
-ProblemSeverity Component::getProblemSeverity() const
-{
- auto file = getVersionFile();
- if(file)
- {
- return file->getProblemSeverity();
- }
- return ProblemSeverity::Error;
-}
-
-const QList<PatchProblem> Component::getProblems() const
-{
- auto file = getVersionFile();
- if(file)
- {
- return file->getProblems();
- }
- return {{ProblemSeverity::Error, QObject::tr("Patch is not loaded yet.")}};
-}
-
-void Component::setVersion(const QString& version)
-{
- if(version == m_version)
- {
- return;
- }
- m_version = version;
- if(m_loaded)
- {
- // we are loaded and potentially have state to invalidate
- if(m_file)
- {
- // we have a file... explicit version has been changed and there is nothing else to do.
- }
- else
- {
- // we don't have a file, therefore we are loaded with metadata
- m_cachedVersion = version;
- // see if the meta version is loaded
- auto metaVersion = ENV.metadataIndex()->get(m_uid, version);
- if(metaVersion->isLoaded())
- {
- // if yes, we can continue with that.
- m_metaVersion = metaVersion;
- }
- else
- {
- // if not, we need loading
- m_metaVersion.reset();
- m_loaded = false;
- }
- updateCachedData();
- }
- }
- else
- {
- // not loaded... assume it will be sorted out later by the update task
- }
- emit dataChanged();
-}
-
-bool Component::customize()
-{
- if(isCustom())
- {
- return false;
- }
-
- auto filename = getFilename();
- if(!FS::ensureFilePathExists(filename))
- {
- return false;
- }
- // FIXME: get rid of this try-catch.
- try
- {
- QSaveFile jsonFile(filename);
- if(!jsonFile.open(QIODevice::WriteOnly))
- {
- return false;
- }
- auto vfile = getVersionFile();
- if(!vfile)
- {
- return false;
- }
- auto document = OneSixVersionFormat::versionFileToJson(vfile);
- jsonFile.write(document.toJson());
- if(!jsonFile.commit())
- {
- return false;
- }
- m_file = vfile;
- m_metaVersion.reset();
- emit dataChanged();
- }
- catch (const Exception &error)
- {
- qWarning() << "Version could not be loaded:" << error.cause();
- }
- return true;
-}
-
-bool Component::revert()
-{
- if(!isCustom())
- {
- // already not custom
- return true;
- }
- auto filename = getFilename();
- bool result = true;
- // just kill the file and reload
- if(QFile::exists(filename))
- {
- result = QFile::remove(filename);
- }
- if(result)
- {
- // file gone...
- m_file.reset();
-
- // check local cache for metadata...
- auto version = ENV.metadataIndex()->get(m_uid, m_version);
- if(version->isLoaded())
- {
- m_metaVersion = version;
- }
- else
- {
- m_metaVersion.reset();
- m_loaded = false;
- }
- emit dataChanged();
- }
- return result;
-}
-
-/**
- * deep inspecting compare for requirement sets
- * By default, only uids are compared for set operations.
- * This compares all fields of the Require structs in the sets.
- */
-static bool deepCompare(const std::set<Meta::Require> & a, const std::set<Meta::Require> & b)
-{
- // NOTE: this needs to be rewritten if the type of Meta::RequireSet changes
- if(a.size() != b.size())
- {
- return false;
- }
- for(const auto & reqA :a)
- {
- const auto &iter2 = b.find(reqA);
- if(iter2 == b.cend())
- {
- return false;
- }
- const auto & reqB = *iter2;
- if(!reqA.deepEquals(reqB))
- {
- return false;
- }
- }
- return true;
-}
-
-void Component::updateCachedData()
-{
- auto file = getVersionFile();
- if(file)
- {
- bool changed = false;
- if(m_cachedName != file->name)
- {
- m_cachedName = file->name;
- changed = true;
- }
- if(m_cachedVersion != file->version)
- {
- m_cachedVersion = file->version;
- changed = true;
- }
- if(m_cachedVolatile != file->m_volatile)
- {
- m_cachedVolatile = file->m_volatile;
- changed = true;
- }
- if(!deepCompare(m_cachedRequires, file->requires))
- {
- m_cachedRequires = file->requires;
- changed = true;
- }
- if(!deepCompare(m_cachedConflicts, file->conflicts))
- {
- m_cachedConflicts = file->conflicts;
- changed = true;
- }
- if(changed)
- {
- emit dataChanged();
- }
- }
- else
- {
- // in case we removed all the metadata
- m_cachedRequires.clear();
- m_cachedConflicts.clear();
- emit dataChanged();
- }
-}
diff --git a/api/logic/minecraft/Component.h b/api/logic/minecraft/Component.h
deleted file mode 100644
index cb202f7f..00000000
--- a/api/logic/minecraft/Component.h
+++ /dev/null
@@ -1,111 +0,0 @@
-#pragma once
-
-#include <memory>
-#include <QList>
-#include <QJsonDocument>
-#include <QDateTime>
-#include "meta/JsonFormat.h"
-#include "ProblemProvider.h"
-#include "QObjectPtr.h"
-#include "multimc_logic_export.h"
-
-class PackProfile;
-class LaunchProfile;
-namespace Meta
-{
- class Version;
- class VersionList;
-}
-class VersionFile;
-
-class MULTIMC_LOGIC_EXPORT Component : public QObject, public ProblemProvider
-{
-Q_OBJECT
-public:
- Component(PackProfile * parent, const QString &uid);
-
- // DEPRECATED: remove these constructors?
- Component(PackProfile * parent, std::shared_ptr<Meta::Version> version);
- Component(PackProfile * parent, const QString & uid, std::shared_ptr<VersionFile> file);
-
- virtual ~Component(){};
- void applyTo(LaunchProfile *profile);
-
- bool isEnabled();
- bool setEnabled (bool state);
- bool canBeDisabled();
-
- bool isMoveable();
- bool isCustomizable();
- bool isRevertible();
- bool isRemovable();
- bool isCustom();
- bool isVersionChangeable();
-
- // DEPRECATED: explicit numeric order values, used for loading old non-component config. TODO: refactor and move to migration code
- void setOrder(int order);
- int getOrder();
-
- QString getID();
- QString getName();
- QString getVersion();
- std::shared_ptr<Meta::Version> getMeta();
- QDateTime getReleaseDateTime();
-
- QString getFilename();
-
- std::shared_ptr<class VersionFile> getVersionFile() const;
- std::shared_ptr<class Meta::VersionList> getVersionList() const;
-
- void setImportant (bool state);
-
-
- const QList<PatchProblem> getProblems() const override;
- ProblemSeverity getProblemSeverity() const override;
-
- void setVersion(const QString & version);
- bool customize();
- bool revert();
-
- void updateCachedData();
-
-signals:
- void dataChanged();
-
-public: /* data */
- PackProfile * m_parent;
-
- // BEGIN: persistent component list properties
- /// ID of the component
- QString m_uid;
- /// version of the component - when there's a custom json override, this is also the version the component reverts to
- QString m_version;
- /// if true, this has been added automatically to satisfy dependencies and may be automatically removed
- bool m_dependencyOnly = false;
- /// if true, the component is either the main component of the instance, or otherwise important and cannot be removed.
- bool m_important = false;
- /// if true, the component is disabled
- bool m_disabled = false;
-
- /// cached name for display purposes, taken from the version file (meta or local override)
- QString m_cachedName;
- /// cached version for display AND other purposes, taken from the version file (meta or local override)
- QString m_cachedVersion;
- /// cached set of requirements, taken from the version file (meta or local override)
- Meta::RequireSet m_cachedRequires;
- Meta::RequireSet m_cachedConflicts;
- /// if true, the component is volatile and may be automatically removed when no longer needed
- bool m_cachedVolatile = false;
- // END: persistent component list properties
-
- // DEPRECATED: explicit numeric order values, used for loading old non-component config. TODO: refactor and move to migration code
- bool m_orderOverride = false;
- int m_order = 0;
-
- // load state
- std::shared_ptr<Meta::Version> m_metaVersion;
- std::shared_ptr<VersionFile> m_file;
- bool m_loaded = false;
-};
-
-typedef shared_qobject_ptr<Component> ComponentPtr;
diff --git a/api/logic/minecraft/ComponentUpdateTask.cpp b/api/logic/minecraft/ComponentUpdateTask.cpp
deleted file mode 100644
index 241d9a49..00000000
--- a/api/logic/minecraft/ComponentUpdateTask.cpp
+++ /dev/null
@@ -1,704 +0,0 @@
-#include "ComponentUpdateTask.h"
-
-#include "PackProfile_p.h"
-#include "PackProfile.h"
-#include "Component.h"
-#include <Env.h>
-#include <meta/Index.h>
-#include <meta/VersionList.h>
-#include <meta/Version.h>
-#include "ComponentUpdateTask_p.h"
-#include <cassert>
-#include <Version.h>
-#include "net/Mode.h"
-#include "OneSixVersionFormat.h"
-
-/*
- * This is responsible for loading the components of a component list AND resolving dependency issues between them
- */
-
-/*
- * FIXME: the 'one shot async task' nature of this does not fit the intended usage
- * Really, it should be a reactor/state machine that receives input from the application
- * and dynamically adapts to changing requirements...
- *
- * The reactor should be the only entry into manipulating the PackProfile.
- * See: https://en.wikipedia.org/wiki/Reactor_pattern
- */
-
-/*
- * Or make this operate on a snapshot of the PackProfile state, then merge results in as long as the snapshot and PackProfile didn't change?
- * If the component list changes, start over.
- */
-
-ComponentUpdateTask::ComponentUpdateTask(Mode mode, Net::Mode netmode, PackProfile* list, QObject* parent)
- : Task(parent)
-{
- d.reset(new ComponentUpdateTaskData);
- d->m_list = list;
- d->mode = mode;
- d->netmode = netmode;
-}
-
-ComponentUpdateTask::~ComponentUpdateTask()
-{
-}
-
-void ComponentUpdateTask::executeTask()
-{
- qDebug() << "Loading components";
- loadComponents();
-}
-
-namespace
-{
-enum class LoadResult
-{
- LoadedLocal,
- RequiresRemote,
- Failed
-};
-
-LoadResult composeLoadResult(LoadResult a, LoadResult b)
-{
- if (a < b)
- {
- return b;
- }
- return a;
-}
-
-static LoadResult loadComponent(ComponentPtr component, shared_qobject_ptr<Task>& loadTask, Net::Mode netmode)
-{
- if(component->m_loaded)
- {
- qDebug() << component->getName() << "is already loaded";
- return LoadResult::LoadedLocal;
- }
-
- LoadResult result = LoadResult::Failed;
- auto customPatchFilename = component->getFilename();
- if(QFile::exists(customPatchFilename))
- {
- // if local file exists...
-
- // check for uid problems inside...
- bool fileChanged = false;
- auto file = ProfileUtils::parseJsonFile(QFileInfo(customPatchFilename), false);
- if(file->uid != component->m_uid)
- {
- file->uid = component->m_uid;
- fileChanged = true;
- }
- if(fileChanged)
- {
- // FIXME: @QUALITY do not ignore return value
- ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), customPatchFilename);
- }
-
- component->m_file = file;
- component->m_loaded = true;
- result = LoadResult::LoadedLocal;
- }
- else
- {
- auto metaVersion = ENV.metadataIndex()->get(component->m_uid, component->m_version);
- component->m_metaVersion = metaVersion;
- if(metaVersion->isLoaded())
- {
- component->m_loaded = true;
- result = LoadResult::LoadedLocal;
- }
- else
- {
- metaVersion->load(netmode);
- loadTask = metaVersion->getCurrentTask();
- if(loadTask)
- result = LoadResult::RequiresRemote;
- else if (metaVersion->isLoaded())
- result = LoadResult::LoadedLocal;
- else
- result = LoadResult::Failed;
- }
- }
- return result;
-}
-
-// FIXME: dead code. determine if this can still be useful?
-/*
-static LoadResult loadPackProfile(ComponentPtr component, shared_qobject_ptr<Task>& loadTask, Net::Mode netmode)
-{
- if(component->m_loaded)
- {
- qDebug() << component->getName() << "is already loaded";
- return LoadResult::LoadedLocal;
- }
-
- LoadResult result = LoadResult::Failed;
- auto metaList = ENV.metadataIndex()->get(component->m_uid);
- if(metaList->isLoaded())
- {
- component->m_loaded = true;
- result = LoadResult::LoadedLocal;
- }
- else
- {
- metaList->load(netmode);
- loadTask = metaList->getCurrentTask();
- result = LoadResult::RequiresRemote;
- }
- return result;
-}
-*/
-
-static LoadResult loadIndex(shared_qobject_ptr<Task>& loadTask, Net::Mode netmode)
-{
- // FIXME: DECIDE. do we want to run the update task anyway?
- if(ENV.metadataIndex()->isLoaded())
- {
- qDebug() << "Index is already loaded";
- return LoadResult::LoadedLocal;
- }
- ENV.metadataIndex()->load(netmode);
- loadTask = ENV.metadataIndex()->getCurrentTask();
- if(loadTask)
- {
- return LoadResult::RequiresRemote;
- }
- // FIXME: this is assuming the load succeeded... did it really?
- return LoadResult::LoadedLocal;
-}
-}
-
-void ComponentUpdateTask::loadComponents()
-{
- LoadResult result = LoadResult::LoadedLocal;
- size_t taskIndex = 0;
- size_t componentIndex = 0;
- d->remoteLoadSuccessful = true;
- // load the main index (it is needed to determine if components can revert)
- {
- // FIXME: tear out as a method? or lambda?
- shared_qobject_ptr<Task> indexLoadTask;
- auto singleResult = loadIndex(indexLoadTask, d->netmode);
- result = composeLoadResult(result, singleResult);
- if(indexLoadTask)
- {
- qDebug() << "Remote loading is being run for metadata index";
- RemoteLoadStatus status;
- status.type = RemoteLoadStatus::Type::Index;
- d->remoteLoadStatusList.append(status);
- connect(indexLoadTask.get(), &Task::succeeded, [=]()
- {
- remoteLoadSucceeded(taskIndex);
- });
- connect(indexLoadTask.get(), &Task::failed, [=](const QString & error)
- {
- remoteLoadFailed(taskIndex, error);
- });
- taskIndex++;
- }
- }
- // load all the components OR their lists...
- for (auto component: d->m_list->d->components)
- {
- shared_qobject_ptr<Task> loadTask;
- LoadResult singleResult;
- RemoteLoadStatus::Type loadType;
- // FIXME: to do this right, we need to load the lists and decide on which versions to use during dependency resolution. For now, ignore all that...
-#if 0
- switch(d->mode)
- {
- case Mode::Launch:
- {
- singleResult = loadComponent(component, loadTask, d->netmode);
- loadType = RemoteLoadStatus::Type::Version;
- break;
- }
- case Mode::Resolution:
- {
- singleResult = loadPackProfile(component, loadTask, d->netmode);
- loadType = RemoteLoadStatus::Type::List;
- break;
- }
- }
-#else
- singleResult = loadComponent(component, loadTask, d->netmode);
- loadType = RemoteLoadStatus::Type::Version;
-#endif
- if(singleResult == LoadResult::LoadedLocal)
- {
- component->updateCachedData();
- }
- result = composeLoadResult(result, singleResult);
- if (loadTask)
- {
- qDebug() << "Remote loading is being run for" << component->getName();
- connect(loadTask.get(), &Task::succeeded, [=]()
- {
- remoteLoadSucceeded(taskIndex);
- });
- connect(loadTask.get(), &Task::failed, [=](const QString & error)
- {
- remoteLoadFailed(taskIndex, error);
- });
- RemoteLoadStatus status;
- status.type = loadType;
- status.PackProfileIndex = componentIndex;
- d->remoteLoadStatusList.append(status);
- taskIndex++;
- }
- componentIndex++;
- }
- d->remoteTasksInProgress = taskIndex;
- switch(result)
- {
- case LoadResult::LoadedLocal:
- {
- // Everything got loaded. Advance to dependency resolution.
- resolveDependencies(d->mode == Mode::Launch || d->netmode == Net::Mode::Offline);
- break;
- }
- case LoadResult::RequiresRemote:
- {
- // we wait for signals.
- break;
- }
- case LoadResult::Failed:
- {
- emitFailed(tr("Some component metadata load tasks failed."));
- break;
- }
- }
-}
-
-namespace
-{
- struct RequireEx : public Meta::Require
- {
- size_t indexOfFirstDependee = 0;
- };
- struct RequireCompositionResult
- {
- bool ok;
- RequireEx outcome;
- };
- using RequireExSet = std::set<RequireEx>;
-}
-
-static RequireCompositionResult composeRequirement(const RequireEx & a, const RequireEx & b)
-{
- assert(a.uid == b.uid);
- RequireEx out;
- out.uid = a.uid;
- out.indexOfFirstDependee = std::min(a.indexOfFirstDependee, b.indexOfFirstDependee);
- if(a.equalsVersion.isEmpty())
- {
- out.equalsVersion = b.equalsVersion;
- }
- else if (b.equalsVersion.isEmpty())
- {
- out.equalsVersion = a.equalsVersion;
- }
- else if (a.equalsVersion == b.equalsVersion)
- {
- out.equalsVersion = a.equalsVersion;
- }
- else
- {
- // FIXME: mark error as explicit version conflict
- return {false, out};
- }
-
- if(a.suggests.isEmpty())
- {
- out.suggests = b.suggests;
- }
- else if (b.suggests.isEmpty())
- {
- out.suggests = a.suggests;
- }
- else
- {
- Version aVer(a.suggests);
- Version bVer(b.suggests);
- out.suggests = (aVer < bVer ? b.suggests : a.suggests);
- }
- return {true, out};
-}
-
-// gather the requirements from all components, finding any obvious conflicts
-static bool gatherRequirementsFromComponents(const ComponentContainer & input, RequireExSet & output)
-{
- bool succeeded = true;
- size_t componentNum = 0;
- for(auto component: input)
- {
- auto &componentRequires = component->m_cachedRequires;
- for(const auto & componentRequire: componentRequires)
- {
- auto found = std::find_if(output.cbegin(), output.cend(), [componentRequire](const Meta::Require & req){
- return req.uid == componentRequire.uid;
- });
-
- RequireEx componenRequireEx;
- componenRequireEx.uid = componentRequire.uid;
- componenRequireEx.suggests = componentRequire.suggests;
- componenRequireEx.equalsVersion = componentRequire.equalsVersion;
- componenRequireEx.indexOfFirstDependee = componentNum;
-
- if(found != output.cend())
- {
- // found... process it further
- auto result = composeRequirement(componenRequireEx, *found);
- if(result.ok)
- {
- output.erase(componenRequireEx);
- output.insert(result.outcome);
- }
- else
- {
- qCritical()
- << "Conflicting requirements:"
- << componentRequire.uid
- << "versions:"
- << componentRequire.equalsVersion
- << ";"
- << (*found).equalsVersion;
- }
- succeeded &= result.ok;
- }
- else
- {
- // not found, accumulate
- output.insert(componenRequireEx);
- }
- }
- componentNum++;
- }
- return succeeded;
-}
-
-/// Get list of uids that can be trivially removed because nothing is depending on them anymore (and they are installed as deps)
-static void getTrivialRemovals(const ComponentContainer & components, const RequireExSet & reqs, QStringList &toRemove)
-{
- for(const auto & component: components)
- {
- if(!component->m_dependencyOnly)
- continue;
- if(!component->m_cachedVolatile)
- continue;
- RequireEx reqNeedle;
- reqNeedle.uid = component->m_uid;
- const auto iter = reqs.find(reqNeedle);
- if(iter == reqs.cend())
- {
- toRemove.append(component->m_uid);
- }
- }
-}
-
-/**
- * handles:
- * - trivial addition (there is an unmet requirement and it can be trivially met by adding something)
- * - trivial version conflict of dependencies == explicit version required and installed is different
- *
- * toAdd - set of requirements than mean adding a new component
- * toChange - set of requirements that mean changing version of an existing component
- */
-static bool getTrivialComponentChanges(const ComponentIndex & index, const RequireExSet & input, RequireExSet & toAdd, RequireExSet & toChange)
-{
- enum class Decision
- {
- Undetermined,
- Met,
- Missing,
- VersionNotSame,
- LockedVersionNotSame
- } decision = Decision::Undetermined;
-
- QString reqStr;
- bool succeeded = true;
- // list the composed requirements and say if they are met or unmet
- for(auto & req: input)
- {
- do
- {
- if(req.equalsVersion.isEmpty())
- {
- reqStr = QString("Req: %1").arg(req.uid);
- if(index.contains(req.uid))
- {
- decision = Decision::Met;
- }
- else
- {
- toAdd.insert(req);
- decision = Decision::Missing;
- }
- break;
- }
- else
- {
- reqStr = QString("Req: %1 == %2").arg(req.uid, req.equalsVersion);
- const auto & compIter = index.find(req.uid);
- if(compIter == index.cend())
- {
- toAdd.insert(req);
- decision = Decision::Missing;
- break;
- }
- auto & comp = (*compIter);
- if(comp->getVersion() != req.equalsVersion)
- {
- if(comp->isCustom()) {
- decision = Decision::LockedVersionNotSame;
- } else {
- if(comp->m_dependencyOnly)
- {
- decision = Decision::VersionNotSame;
- }
- else
- {
- decision = Decision::LockedVersionNotSame;
- }
- }
- break;
- }
- decision = Decision::Met;
- }
- } while(false);
- switch(decision)
- {
- case Decision::Undetermined:
- qCritical() << "No decision for" << reqStr;
- succeeded = false;
- break;
- case Decision::Met:
- qDebug() << reqStr << "Is met.";
- break;
- case Decision::Missing:
- qDebug() << reqStr << "Is missing and should be added at" << req.indexOfFirstDependee;
- toAdd.insert(req);
- break;
- case Decision::VersionNotSame:
- qDebug() << reqStr << "already has different version that can be changed.";
- toChange.insert(req);
- break;
- case Decision::LockedVersionNotSame:
- qDebug() << reqStr << "already has different version that cannot be changed.";
- succeeded = false;
- break;
- }
- }
- return succeeded;
-}
-
-// FIXME, TODO: decouple dependency resolution from loading
-// FIXME: This works directly with the PackProfile internals. It shouldn't! It needs richer data types than PackProfile uses.
-// FIXME: throw all this away and use a graph
-void ComponentUpdateTask::resolveDependencies(bool checkOnly)
-{
- qDebug() << "Resolving dependencies";
- /*
- * this is a naive dependency resolving algorithm. all it does is check for following conditions and react in simple ways:
- * 1. There are conflicting dependencies on the same uid with different exact version numbers
- * -> hard error
- * 2. A dependency has non-matching exact version number
- * -> hard error
- * 3. A dependency is entirely missing and needs to be injected before the dependee(s)
- * -> requirements are injected
- *
- * NOTE: this is a placeholder and should eventually be replaced with something 'serious'
- */
- auto & components = d->m_list->d->components;
- auto & componentIndex = d->m_list->d->componentIndex;
-
- RequireExSet allRequires;
- QStringList toRemove;
- do
- {
- allRequires.clear();
- toRemove.clear();
- if(!gatherRequirementsFromComponents(components, allRequires))
- {
- emitFailed(tr("Conflicting requirements detected during dependency checking!"));
- return;
- }
- getTrivialRemovals(components, allRequires, toRemove);
- if(!toRemove.isEmpty())
- {
- qDebug() << "Removing obsolete components...";
- for(auto & remove : toRemove)
- {
- qDebug() << "Removing" << remove;
- d->m_list->remove(remove);
- }
- }
- } while (!toRemove.isEmpty());
- RequireExSet toAdd;
- RequireExSet toChange;
- bool succeeded = getTrivialComponentChanges(componentIndex, allRequires, toAdd, toChange);
- if(!succeeded)
- {
- emitFailed(tr("Instance has conflicting dependencies."));
- return;
- }
- if(checkOnly)
- {
- if(toAdd.size() || toChange.size())
- {
- emitFailed(tr("Instance has unresolved dependencies while loading/checking for launch."));
- }
- else
- {
- emitSucceeded();
- }
- return;
- }
-
- bool recursionNeeded = false;
- if(toAdd.size())
- {
- // add stuff...
- for(auto &add: toAdd)
- {
- ComponentPtr component = new Component(d->m_list, add.uid);
- if(!add.equalsVersion.isEmpty())
- {
- // exact version
- qDebug() << "Adding" << add.uid << "version" << add.equalsVersion << "at position" << add.indexOfFirstDependee;
- component->m_version = add.equalsVersion;
- }
- else
- {
- // version needs to be decided
- qDebug() << "Adding" << add.uid << "at position" << add.indexOfFirstDependee;
-// ############################################################################################################
-// HACK HACK HACK HACK FIXME: this is a placeholder for deciding what version to use. For now, it is hardcoded.
- if(!add.suggests.isEmpty())
- {
- component->m_version = add.suggests;
- }
- else
- {
- if(add.uid == "org.lwjgl")
- {
- component->m_version = "2.9.1";
- }
- else if (add.uid == "org.lwjgl3")
- {
- component->m_version = "3.1.2";
- }
- else if (add.uid == "net.fabricmc.intermediary")
- {
- auto minecraft = std::find_if(components.begin(), components.end(), [](ComponentPtr & cmp){
- return cmp->getID() == "net.minecraft";
- });
- if(minecraft != components.end()) {
- component->m_version = (*minecraft)->getVersion();
- }
- }
- }
-// HACK HACK HACK HACK FIXME: this is a placeholder for deciding what version to use. For now, it is hardcoded.
-// ############################################################################################################
- }
- component->m_dependencyOnly = true;
- // FIXME: this should not work directly with the component list
- d->m_list->insertComponent(add.indexOfFirstDependee, component);
- componentIndex[add.uid] = component;
- }
- recursionNeeded = true;
- }
- if(toChange.size())
- {
- // change a version of something that exists
- for(auto &change: toChange)
- {
- // FIXME: this should not work directly with the component list
- qDebug() << "Setting version of " << change.uid << "to" << change.equalsVersion;
- auto component = componentIndex[change.uid];
- component->setVersion(change.equalsVersion);
- }
- recursionNeeded = true;
- }
-
- if(recursionNeeded)
- {
- loadComponents();
- }
- else
- {
- emitSucceeded();
- }
-}
-
-void ComponentUpdateTask::remoteLoadSucceeded(size_t taskIndex)
-{
- auto &taskSlot = d->remoteLoadStatusList[taskIndex];
- if(taskSlot.finished)
- {
- qWarning() << "Got multiple results from remote load task" << taskIndex;
- return;
- }
- qDebug() << "Remote task" << taskIndex << "succeeded";
- taskSlot.succeeded = false;
- taskSlot.finished = true;
- d->remoteTasksInProgress --;
- // update the cached data of the component from the downloaded version file.
- if (taskSlot.type == RemoteLoadStatus::Type::Version)
- {
- auto component = d->m_list->getComponent(taskSlot.PackProfileIndex);
- component->m_loaded = true;
- component->updateCachedData();
- }
- checkIfAllFinished();
-}
-
-
-void ComponentUpdateTask::remoteLoadFailed(size_t taskIndex, const QString& msg)
-{
- auto &taskSlot = d->remoteLoadStatusList[taskIndex];
- if(taskSlot.finished)
- {
- qWarning() << "Got multiple results from remote load task" << taskIndex;
- return;
- }
- qDebug() << "Remote task" << taskIndex << "failed: " << msg;
- d->remoteLoadSuccessful = false;
- taskSlot.succeeded = false;
- taskSlot.finished = true;
- taskSlot.error = msg;
- d->remoteTasksInProgress --;
- checkIfAllFinished();
-}
-
-void ComponentUpdateTask::checkIfAllFinished()
-{
- if(d->remoteTasksInProgress)
- {
- // not yet...
- return;
- }
- if(d->remoteLoadSuccessful)
- {
- // nothing bad happened... clear the temp load status and proceed with looking at dependencies
- d->remoteLoadStatusList.clear();
- resolveDependencies(d->mode == Mode::Launch);
- }
- else
- {
- // remote load failed... report error and bail
- QStringList allErrorsList;
- for(auto & item: d->remoteLoadStatusList)
- {
- if(!item.succeeded)
- {
- allErrorsList.append(item.error);
- }
- }
- auto allErrors = allErrorsList.join("\n");
- emitFailed(tr("Component metadata update task failed while downloading from remote server:\n%1").arg(allErrors));
- d->remoteLoadStatusList.clear();
- }
-}
diff --git a/api/logic/minecraft/ComponentUpdateTask.h b/api/logic/minecraft/ComponentUpdateTask.h
deleted file mode 100644
index 4274cabb..00000000
--- a/api/logic/minecraft/ComponentUpdateTask.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#pragma once
-
-#include "tasks/Task.h"
-#include "net/Mode.h"
-
-#include <memory>
-class PackProfile;
-struct ComponentUpdateTaskData;
-
-class ComponentUpdateTask : public Task
-{
- Q_OBJECT
-public:
- enum class Mode
- {
- Launch,
- Resolution
- };
-
-public:
- explicit ComponentUpdateTask(Mode mode, Net::Mode netmode, PackProfile * list, QObject *parent = 0);
- virtual ~ComponentUpdateTask();
-
-protected:
- void executeTask();
-
-private:
- void loadComponents();
- void resolveDependencies(bool checkOnly);
-
- void remoteLoadSucceeded(size_t index);
- void remoteLoadFailed(size_t index, const QString &msg);
- void checkIfAllFinished();
-
-private:
- std::unique_ptr<ComponentUpdateTaskData> d;
-};
diff --git a/api/logic/minecraft/ComponentUpdateTask_p.h b/api/logic/minecraft/ComponentUpdateTask_p.h
deleted file mode 100644
index 5b02431b..00000000
--- a/api/logic/minecraft/ComponentUpdateTask_p.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#pragma once
-
-#include <cstddef>
-#include <QString>
-#include <QList>
-#include "net/Mode.h"
-
-class PackProfile;
-
-struct RemoteLoadStatus
-{
- enum class Type
- {
- Index,
- List,
- Version
- } type = Type::Version;
- size_t PackProfileIndex = 0;
- bool finished = false;
- bool succeeded = false;
- QString error;
-};
-
-struct ComponentUpdateTaskData
-{
- PackProfile * m_list = nullptr;
- QList<RemoteLoadStatus> remoteLoadStatusList;
- bool remoteLoadSuccessful = true;
- size_t remoteTasksInProgress = 0;
- ComponentUpdateTask::Mode mode;
- Net::Mode netmode;
-};
diff --git a/api/logic/minecraft/GradleSpecifier.h b/api/logic/minecraft/GradleSpecifier.h
deleted file mode 100644
index 60e0a726..00000000
--- a/api/logic/minecraft/GradleSpecifier.h
+++ /dev/null
@@ -1,151 +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
- 0 "org.gradle.test.classifiers:service:1.0:jdk15@jar"
- 1 "org.gradle.test.classifiers"
- 2 "service"
- 3 "1.0"
- 4 "jdk15"
- 5 "jar"
- */
- QRegExp matcher("([^:@]+):([^:@]+):([^:@]+)" "(?::([^:@]+))?" "(?:@([^:@]+))?");
- m_valid = matcher.exactMatch(value);
- if(!m_valid) {
- m_invalidValue = value;
- return *this;
- }
- auto elements = matcher.capturedTexts();
- m_groupId = elements[1];
- m_artifactId = elements[2];
- m_version = elements[3];
- m_classifier = elements[4];
- if(!elements[5].isEmpty())
- {
- m_extension = elements[5];
- }
- return *this;
- }
- QString serialize() const
- {
- if(!m_valid) {
- return m_invalidValue;
- }
- 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 getFileName() const
- {
- if(!m_valid) {
- return QString();
- }
- QString filename = m_artifactId + '-' + m_version;
- if(!m_classifier.isEmpty())
- {
- filename += "-" + m_classifier;
- }
- filename += "." + m_extension;
- return filename;
- }
- QString toPath(const QString & filenameOverride = QString()) const
- {
- if(!m_valid) {
- return QString();
- }
- QString filename;
- if(filenameOverride.isEmpty())
- {
- filename = getFileName();
- }
- else
- {
- filename = filenameOverride;
- }
- QString path = m_groupId;
- path.replace('.', '/');
- path += '/' + m_artifactId + '/' + m_version + '/' + filename;
- 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_invalidValue;
- 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/api/logic/minecraft/GradleSpecifier_test.cpp b/api/logic/minecraft/GradleSpecifier_test.cpp
deleted file mode 100644
index 0900c9d8..00000000
--- a/api/logic/minecraft/GradleSpecifier_test.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-#include <QTest>
-#include "TestUtil.h"
-
-#include "minecraft/GradleSpecifier.h"
-
-class GradleSpecifierTest : public QObject
-{
- Q_OBJECT
-private
-slots:
- void initTestCase()
- {
-
- }
- void cleanupTestCase()
- {
-
- }
-
- void test_Positive_data()
- {
- QTest::addColumn<QString>("through");
-
- QTest::newRow("3 parter") << "org.gradle.test.classifiers:service:1.0";
- QTest::newRow("classifier") << "org.gradle.test.classifiers:service:1.0:jdk15";
- QTest::newRow("jarextension") << "org.gradle.test.classifiers:service:1.0@jar";
- QTest::newRow("jarboth") << "org.gradle.test.classifiers:service:1.0:jdk15@jar";
- QTest::newRow("packxz") << "org.gradle.test.classifiers:service:1.0:jdk15@jar.pack.xz";
- }
- void test_Positive()
- {
- QFETCH(QString, through);
-
- QString converted = GradleSpecifier(through).serialize();
-
- QCOMPARE(converted, through);
- }
-
- void test_Path_data()
- {
- QTest::addColumn<QString>("spec");
- QTest::addColumn<QString>("expected");
-
- QTest::newRow("3 parter") << "group.id:artifact:1.0" << "group/id/artifact/1.0/artifact-1.0.jar";
- QTest::newRow("doom") << "id.software:doom:1.666:demons@wad" << "id/software/doom/1.666/doom-1.666-demons.wad";
- }
- void test_Path()
- {
- QFETCH(QString, spec);
- QFETCH(QString, expected);
-
- QString converted = GradleSpecifier(spec).toPath();
-
- QCOMPARE(converted, expected);
- }
- void test_Negative_data()
- {
- QTest::addColumn<QString>("input");
-
- QTest::newRow("too many :") << "org:gradle.test:class:::ifiers:service:1.0::";
- QTest::newRow("nonsense") << "I like turtles";
- QTest::newRow("empty string") << "";
- QTest::newRow("missing version") << "herp.derp:artifact";
- }
- void test_Negative()
- {
- QFETCH(QString, input);
-
- GradleSpecifier spec(input);
- QVERIFY(!spec.valid());
- QCOMPARE(spec.serialize(), input);
- QCOMPARE(spec.toPath(), QString());
- }
-};
-
-QTEST_GUILESS_MAIN(GradleSpecifierTest)
-
-#include "GradleSpecifier_test.moc"
diff --git a/api/logic/minecraft/LaunchProfile.cpp b/api/logic/minecraft/LaunchProfile.cpp
deleted file mode 100644
index 41705187..00000000
--- a/api/logic/minecraft/LaunchProfile.cpp
+++ /dev/null
@@ -1,319 +0,0 @@
-#include "LaunchProfile.h"
-#include <Version.h>
-
-void LaunchProfile::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_mavenFiles.clear();
- m_traits.clear();
- m_jarMods.clear();
- m_mainJar.reset();
- m_problemSeverity = ProblemSeverity::None;
-}
-
-static void applyString(const QString & from, QString & to)
-{
- if(from.isEmpty())
- return;
- to = from;
-}
-
-void LaunchProfile::applyMinecraftVersion(const QString& id)
-{
- applyString(id, this->m_minecraftVersion);
-}
-
-void LaunchProfile::applyAppletClass(const QString& appletClass)
-{
- applyString(appletClass, this->m_appletClass);
-}
-
-void LaunchProfile::applyMainClass(const QString& mainClass)
-{
- applyString(mainClass, this->m_mainClass);
-}
-
-void LaunchProfile::applyMinecraftArguments(const QString& minecraftArguments)
-{
- applyString(minecraftArguments, this->m_minecraftArguments);
-}
-
-void LaunchProfile::applyMinecraftVersionType(const QString& type)
-{
- applyString(type, this->m_minecraftVersionType);
-}
-
-void LaunchProfile::applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets)
-{
- if(assets)
- {
- m_minecraftAssets = assets;
- }
-}
-
-void LaunchProfile::applyTraits(const QSet<QString>& traits)
-{
- this->m_traits.unite(traits);
-}
-
-void LaunchProfile::applyTweakers(const QStringList& tweakers)
-{
- // if the applied tweakers override an existing one, skip it. this effectively moves it later in the sequence
- QStringList newTweakers;
- for(auto & tweaker: m_tweakers)
- {
- if (tweakers.contains(tweaker))
- {
- continue;
- }
- newTweakers.append(tweaker);
- }
- // then just append the new tweakers (or moved original ones)
- newTweakers += tweakers;
- m_tweakers = newTweakers;
-}
-
-void LaunchProfile::applyJarMods(const QList<LibraryPtr>& 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 LaunchProfile::applyMods(const QList<LibraryPtr>& mods)
-{
- QList<LibraryPtr> * list = &m_mods;
- for(auto & mod: mods)
- {
- auto modCopy = Library::limitedCopy(mod);
-
- // find the mod by name.
- const int index = findLibraryByName(list, mod->rawName());
- // mod not found? just add it.
- if (index < 0)
- {
- list->append(modCopy);
- return;
- }
-
- auto existingLibrary = list->at(index);
- // if we are higher it means we should update
- if (Version(mod->version()) > Version(existingLibrary->version()))
- {
- list->replace(index, modCopy);
- }
- }
-}
-
-void LaunchProfile::applyLibrary(LibraryPtr library)
-{
- if(!library->isActive())
- {
- return;
- }
-
- QList<LibraryPtr> * list = &m_libraries;
- if(library->isNative())
- {
- list = &m_nativeLibraries;
- }
-
- auto libraryCopy = Library::limitedCopy(library);
-
- // find the library by name.
- const int index = findLibraryByName(list, library->rawName());
- // library not found? just add it.
- if (index < 0)
- {
- list->append(libraryCopy);
- return;
- }
-
- auto existingLibrary = list->at(index);
- // if we are higher it means we should update
- if (Version(library->version()) > Version(existingLibrary->version()))
- {
- list->replace(index, libraryCopy);
- }
-}
-
-void LaunchProfile::applyMavenFile(LibraryPtr mavenFile)
-{
- if(!mavenFile->isActive())
- {
- return;
- }
-
- if(mavenFile->isNative())
- {
- return;
- }
-
- // unlike libraries, we do not keep only one version or try to dedupe them
- m_mavenFiles.append(Library::limitedCopy(mavenFile));
-}
-
-const LibraryPtr LaunchProfile::getMainJar() const
-{
- return m_mainJar;
-}
-
-void LaunchProfile::applyMainJar(LibraryPtr jar)
-{
- if(jar)
- {
- m_mainJar = jar;
- }
-}
-
-void LaunchProfile::applyProblemSeverity(ProblemSeverity severity)
-{
- if (m_problemSeverity < severity)
- {
- m_problemSeverity = severity;
- }
-}
-
-const QList<PatchProblem> LaunchProfile::getProblems() const
-{
- // FIXME: implement something that actually makes sense here
- return {};
-}
-
-QString LaunchProfile::getMinecraftVersion() const
-{
- return m_minecraftVersion;
-}
-
-QString LaunchProfile::getAppletClass() const
-{
- return m_appletClass;
-}
-
-QString LaunchProfile::getMainClass() const
-{
- return m_mainClass;
-}
-
-const QSet<QString> &LaunchProfile::getTraits() const
-{
- return m_traits;
-}
-
-const QStringList & LaunchProfile::getTweakers() const
-{
- return m_tweakers;
-}
-
-bool LaunchProfile::hasTrait(const QString& trait) const
-{
- return m_traits.contains(trait);
-}
-
-ProblemSeverity LaunchProfile::getProblemSeverity() const
-{
- return m_problemSeverity;
-}
-
-QString LaunchProfile::getMinecraftVersionType() const
-{
- return m_minecraftVersionType;
-}
-
-std::shared_ptr<MojangAssetIndexInfo> LaunchProfile::getMinecraftAssets() const
-{
- if(!m_minecraftAssets)
- {
- return std::make_shared<MojangAssetIndexInfo>("legacy");
- }
- return m_minecraftAssets;
-}
-
-QString LaunchProfile::getMinecraftArguments() const
-{
- return m_minecraftArguments;
-}
-
-const QList<LibraryPtr> & LaunchProfile::getJarMods() const
-{
- return m_jarMods;
-}
-
-const QList<LibraryPtr> & LaunchProfile::getLibraries() const
-{
- return m_libraries;
-}
-
-const QList<LibraryPtr> & LaunchProfile::getNativeLibraries() const
-{
- return m_nativeLibraries;
-}
-
-const QList<LibraryPtr> & LaunchProfile::getMavenFiles() const
-{
- return m_mavenFiles;
-}
-
-void LaunchProfile::getLibraryFiles(
- const QString& architecture,
- QStringList& jars,
- QStringList& nativeJars,
- const QString& overridePath,
- const QString& tempPath
-) const
-{
- QStringList native32, native64;
- jars.clear();
- nativeJars.clear();
- for (auto lib : getLibraries())
- {
- lib->getApplicableFiles(currentSystem, jars, nativeJars, native32, native64, overridePath);
- }
- // NOTE: order is important here, add main jar last to the lists
- if(m_mainJar)
- {
- // FIXME: HACK!! jar modding is weird and unsystematic!
- if(m_jarMods.size())
- {
- QDir tempDir(tempPath);
- jars.append(tempDir.absoluteFilePath("minecraft.jar"));
- }
- else
- {
- m_mainJar->getApplicableFiles(currentSystem, jars, nativeJars, native32, native64, overridePath);
- }
- }
- for (auto lib : getNativeLibraries())
- {
- lib->getApplicableFiles(currentSystem, jars, nativeJars, native32, native64, overridePath);
- }
- if(architecture == "32")
- {
- nativeJars.append(native32);
- }
- else if(architecture == "64")
- {
- nativeJars.append(native64);
- }
-}
diff --git a/api/logic/minecraft/LaunchProfile.h b/api/logic/minecraft/LaunchProfile.h
deleted file mode 100644
index c1752531..00000000
--- a/api/logic/minecraft/LaunchProfile.h
+++ /dev/null
@@ -1,104 +0,0 @@
-#pragma once
-#include <QString>
-#include "Library.h"
-#include <ProblemProvider.h>
-
-class LaunchProfile: public ProblemProvider
-{
-public:
- virtual ~LaunchProfile() {};
-
-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<LibraryPtr> &jarMods);
- void applyMods(const QList<LibraryPtr> &jarMods);
- void applyLibrary(LibraryPtr library);
- void applyMavenFile(LibraryPtr library);
- void applyMainJar(LibraryPtr jar);
- void applyProblemSeverity(ProblemSeverity severity);
- /// clear the profile
- void clear();
-
-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<LibraryPtr> & getJarMods() const;
- const QList<LibraryPtr> & getLibraries() const;
- const QList<LibraryPtr> & getNativeLibraries() const;
- const QList<LibraryPtr> & getMavenFiles() const;
- const LibraryPtr getMainJar() const;
- void getLibraryFiles(
- const QString & architecture,
- QStringList & jars,
- QStringList & nativeJars,
- const QString & overridePath,
- const QString & tempPath
- ) const;
- bool hasTrait(const QString & trait) const;
- ProblemSeverity getProblemSeverity() const override;
- const QList<PatchProblem> getProblems() const override;
-
-private:
- /// 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;
-
- /**
- * 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;
-
- /// the list of maven files to be placed in the libraries folder, but not acted upon
- QList<LibraryPtr> m_mavenFiles;
-
- /// the main jar
- LibraryPtr m_mainJar;
-
- /// the list of native libraries
- QList<LibraryPtr> m_nativeLibraries;
-
- /// 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<LibraryPtr> m_jarMods;
-
- /// the list of mods
- QList<LibraryPtr> m_mods;
-
- ProblemSeverity m_problemSeverity = ProblemSeverity::None;
-
-};
diff --git a/api/logic/minecraft/Library.cpp b/api/logic/minecraft/Library.cpp
deleted file mode 100644
index f2293679..00000000
--- a/api/logic/minecraft/Library.cpp
+++ /dev/null
@@ -1,309 +0,0 @@
-#include "Library.h"
-#include "MinecraftInstance.h"
-
-#include <net/Download.h>
-#include <net/ChecksumValidator.h>
-#include <Env.h>
-#include <FileSystem.h>
-#include <BuildConfig.h>
-
-
-void Library::getApplicableFiles(OpSys system, QStringList& jar, QStringList& native, QStringList& native32,
- QStringList& native64, const QString &overridePath) const
-{
- bool local = isLocal();
- auto actualPath = [&](QString relPath)
- {
- QFileInfo out(FS::PathCombine(storagePrefix(), relPath));
- if(local && !overridePath.isEmpty())
- {
- QString fileName = out.fileName();
- return QFileInfo(FS::PathCombine(overridePath, fileName)).absoluteFilePath();
- }
- return out.absoluteFilePath();
- };
- 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< std::shared_ptr< NetAction > > Library::getDownloads(
- OpSys system,
- class HttpMetaCache* cache,
- QStringList& failedLocalFiles,
- const QString & overridePath
-) const
-{
- QList<NetActionPtr> out;
- bool stale = isAlwaysStale();
- bool local = isLocal();
-
- auto check_local_file = [&](QString storage)
- {
- QFileInfo fileinfo(storage);
- QString fileName = fileinfo.fileName();
- auto fullPath = FS::PathCombine(overridePath, fileName);
- QFileInfo localFileInfo(fullPath);
- if(!localFileInfo.exists())
- {
- failedLocalFiles.append(localFileInfo.filePath());
- return false;
- }
- return true;
- };
-
- auto add_download = [&](QString storage, QString url, QString sha1)
- {
- if(local)
- {
- return check_local_file(storage);
- }
- auto entry = cache->resolveEntry("libraries", storage);
- if(stale)
- {
- entry->setStale(true);
- }
- if (!entry->isStale())
- return true;
- Net::Download::Options options;
- if(stale)
- {
- options |= Net::Download::Option::AcceptLocalFiles;
- }
-
- if(sha1.size())
- {
- auto rawSha1 = QByteArray::fromHex(sha1.toLatin1());
- auto dl = Net::Download::makeCached(url, entry, options);
- dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
- qDebug() << "Checksummed Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url;
- out.append(dl);
- }
- else
- {
- out.append(Net::Download::makeCached(url, entry, options));
- qDebug() << "Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url;
- }
- return true;
- };
-
- QString raw_storage = storageSuffix(system);
- if(m_mojangDownloads)
- {
- if(isNative())
- {
- 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)
- {
- auto cooked_storage = raw_storage;
- cooked_storage.replace("${arch}", "32");
- add_download(cooked_storage, nat32info->url, nat32info->sha1);
- }
- auto nat64info = m_mojangDownloads->getDownloadInfo(nat64Classifier);
- if(nat64info)
- {
- auto cooked_storage = raw_storage;
- cooked_storage.replace("${arch}", "64");
- add_download(cooked_storage, nat64info->url, nat64info->sha1);
- }
- }
- else
- {
- auto info = m_mojangDownloads->getDownloadInfo(nativeClassifier);
- if(info)
- {
- add_download(raw_storage, info->url, info->sha1);
- }
- }
- }
- else
- {
- qDebug() << "Ignoring native library" << m_name.serialize() << "because it has no classifier for current OS";
- }
- }
- else
- {
- if(m_mojangDownloads->artifact)
- {
- auto artifact = m_mojangDownloads->artifact;
- add_download(raw_storage, artifact->url, artifact->sha1);
- }
- else
- {
- qDebug() << "Ignoring java library" << m_name.serialize() << "because it has no artifact";
- }
- }
- }
- else
- {
- auto raw_dl = [&]()
- {
- if (!m_absoluteURL.isEmpty())
- {
- return m_absoluteURL;
- }
-
- if (m_repositoryURL.isEmpty())
- {
- return BuildConfig.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"), QString());
- cooked_storage = raw_storage;
- cooked_dl = raw_dl;
- add_download(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64"), QString());
- }
- else
- {
- add_download(raw_storage, raw_dl, QString());
- }
- }
- 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;
-}
-
-bool Library::isLocal() const
-{
- return m_hint == "local";
-}
-
-bool Library::isAlwaysStale() const
-{
- return m_hint == "always-stale";
-}
-
-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::filename(OpSys system) const
-{
- if(!m_filename.isEmpty())
- {
- return m_filename;
- }
- // non-native? use only the gradle specifier
- if (!isNative())
- {
- return m_name.getFileName();
- }
-
- // 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.getFileName();
-}
-
-QString Library::displayName(OpSys system) const
-{
- if(!m_displayname.isEmpty())
- return m_displayname;
- return filename(system);
-}
-
-QString Library::storageSuffix(OpSys system) const
-{
- // non-native? use only the gradle specifier
- if (!isNative())
- {
- return m_name.toPath(m_filename);
- }
-
- // 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(m_filename);
-}
diff --git a/api/logic/minecraft/Library.h b/api/logic/minecraft/Library.h
deleted file mode 100644
index acdd6c9c..00000000
--- a/api/logic/minecraft/Library.h
+++ /dev/null
@@ -1,219 +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 "MojangDownloadInfo.h"
-
-#include "multimc_logic_export.h"
-
-class Library;
-class MinecraftInstance;
-
-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;
- newlib->m_filename = base->m_filename;
- 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 QString & overridePath) const;
-
- void setAbsoluteUrl(const QString &absolute_url)
- {
- m_absoluteURL = absolute_url;
- }
-
- void setFilename(const QString &filename)
- {
- m_filename = filename;
- }
-
- /// Get the file name of the library
- QString filename(OpSys system) const;
-
- // DEPRECATED: set a display name, used by jar mods only
- void setDisplayName(const QString & displayName)
- {
- m_displayname = displayName;
- }
-
- /// Get the file name of the library
- QString displayName(OpSys system) const;
-
- 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;
-
- /// Returns true if the library is contained in an instance and false if it is shared
- bool isLocal() const;
-
- /// Returns true if the library is to always be checked for updates
- bool isAlwaysStale() const;
-
- /// Return true if the library requires forge XZ hacks
- bool isForge() const;
-
- // Get a list of downloads for this library
- QList<NetActionPtr> getDownloads(OpSys system, class HttpMetaCache * cache,
- QStringList & failedLocalFiles, const QString & overridePath) 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 file 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 extension - filename override
- QString m_filename;
-
- /// DEPRECATED MultiMC extension - display name
- QString m_displayname;
-
- /**
- * 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/api/logic/minecraft/Library_test.cpp b/api/logic/minecraft/Library_test.cpp
deleted file mode 100644
index 75bb4db1..00000000
--- a/api/logic/minecraft/Library_test.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-#include <QTest>
-#include "TestUtil.h"
-
-#include "minecraft/MojangVersionFormat.h"
-#include "minecraft/OneSixVersionFormat.h"
-#include "minecraft/Library.h"
-#include "net/HttpMetaCache.h"
-#include "FileSystem.h"
-
-class LibraryTest : public QObject
-{
- Q_OBJECT
-private:
- LibraryPtr readMojangJson(const char *file)
- {
- auto path = QFINDTESTDATA(file);
- QFile jsonFile(path);
- jsonFile.open(QIODevice::ReadOnly);
- auto data = jsonFile.readAll();
- jsonFile.close();
- ProblemContainer problems;
- return MojangVersionFormat::libraryFromJson(problems, QJsonDocument::fromJson(data).object(), file);
- }
- // get absolute path to expected storage, assuming default cache prefix
- QStringList getStorage(QString relative)
- {
- return {FS::PathCombine(cache->getBasePath("libraries"), relative)};
- }
-private
-slots:
- void initTestCase()
- {
- cache.reset(new HttpMetaCache());
- cache->addBase("libraries", QDir("libraries").absolutePath());
- dataDir = QDir("data").absolutePath();
- }
- void test_legacy()
- {
- Library test("test.package:testname:testversion");
- QCOMPARE(test.artifactPrefix(), QString("test.package:testname"));
- QCOMPARE(test.isNative(), false);
-
- QStringList jar, native, native32, native64;
- test.getApplicableFiles(currentSystem, jar, native, native32, native64, QString());
- QCOMPARE(jar, getStorage("test/package/testname/testversion/testname-testversion.jar"));
- QCOMPARE(native, {});
- QCOMPARE(native32, {});
- QCOMPARE(native64, {});
- }
- void test_legacy_url()
- {
- QStringList failedFiles;
- Library test("test.package:testname:testversion");
- test.setRepositoryURL("file://foo/bar");
- auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString());
- QCOMPARE(downloads.size(), 1);
- QCOMPARE(failedFiles, {});
- NetActionPtr dl = downloads[0];
- QCOMPARE(dl->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion.jar"));
- }
- void test_legacy_url_local_broken()
- {
- Library test("test.package:testname:testversion");
- QCOMPARE(test.isNative(), false);
- QStringList failedFiles;
- test.setHint("local");
- auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString());
- QCOMPARE(downloads.size(), 0);
- QCOMPARE(failedFiles, {"testname-testversion.jar"});
- }
- void test_legacy_url_local_override()
- {
- Library test("com.paulscode:codecwav:20101023");
- QCOMPARE(test.isNative(), false);
- QStringList failedFiles;
- test.setHint("local");
- auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString("data"));
- QCOMPARE(downloads.size(), 0);
- qDebug() << failedFiles;
- QCOMPARE(failedFiles.size(), 0);
-
- QStringList jar, native, native32, native64;
- test.getApplicableFiles(currentSystem, jar, native, native32, native64, QString("data"));
- QCOMPARE(jar, {QFileInfo("data/codecwav-20101023.jar").absoluteFilePath()});
- QCOMPARE(native, {});
- QCOMPARE(native32, {});
- QCOMPARE(native64, {});
- }
- void test_legacy_native()
- {
- Library test("test.package:testname:testversion");
- test.m_nativeClassifiers[OpSys::Os_Linux]="linux";
- QCOMPARE(test.isNative(), true);
- test.setRepositoryURL("file://foo/bar");
- {
- QStringList jar, native, native32, native64;
- test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QString());
- QCOMPARE(jar, {});
- QCOMPARE(native, getStorage("test/package/testname/testversion/testname-testversion-linux.jar"));
- QCOMPARE(native32, {});
- QCOMPARE(native64, {});
- QStringList failedFiles;
- auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString());
- QCOMPARE(dls.size(), 1);
- QCOMPARE(failedFiles, {});
- auto dl = dls[0];
- QCOMPARE(dl->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux.jar"));
- }
- }
- void test_legacy_native_arch()
- {
- Library test("test.package:testname:testversion");
- test.m_nativeClassifiers[OpSys::Os_Linux]="linux-${arch}";
- test.m_nativeClassifiers[OpSys::Os_OSX]="osx-${arch}";
- test.m_nativeClassifiers[OpSys::Os_Windows]="windows-${arch}";
- QCOMPARE(test.isNative(), true);
- test.setRepositoryURL("file://foo/bar");
- {
- QStringList jar, native, native32, native64;
- test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QString());
- QCOMPARE(jar, {});
- QCOMPARE(native, {});
- QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-linux-32.jar"));
- QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-linux-64.jar"));
- QStringList failedFiles;
- auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString());
- QCOMPARE(dls.size(), 2);
- QCOMPARE(failedFiles, {});
- QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-32.jar"));
- QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-64.jar"));
- }
- {
- QStringList jar, native, native32, native64;
- test.getApplicableFiles(Os_Windows, jar, native, native32, native64, QString());
- QCOMPARE(jar, {});
- QCOMPARE(native, {});
- QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-windows-32.jar"));
- QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-windows-64.jar"));
- QStringList failedFiles;
- auto dls = test.getDownloads(Os_Windows, cache.get(), failedFiles, QString());
- QCOMPARE(dls.size(), 2);
- QCOMPARE(failedFiles, {});
- QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-32.jar"));
- QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-64.jar"));
- }
- {
- QStringList jar, native, native32, native64;
- test.getApplicableFiles(Os_OSX, jar, native, native32, native64, QString());
- QCOMPARE(jar, {});
- QCOMPARE(native, {});
- QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-osx-32.jar"));
- QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-osx-64.jar"));
- QStringList failedFiles;
- auto dls = test.getDownloads(Os_OSX, cache.get(), failedFiles, QString());
- QCOMPARE(dls.size(), 2);
- QCOMPARE(failedFiles, {});
- QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-32.jar"));
- QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-64.jar"));
- }
- }
- void test_legacy_native_arch_local_override()
- {
- Library test("test.package:testname:testversion");
- test.m_nativeClassifiers[OpSys::Os_Linux]="linux-${arch}";
- test.setHint("local");
- QCOMPARE(test.isNative(), true);
- test.setRepositoryURL("file://foo/bar");
- {
- QStringList jar, native, native32, native64;
- test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QString("data"));
- QCOMPARE(jar, {});
- QCOMPARE(native, {});
- QCOMPARE(native32, {QFileInfo("data/testname-testversion-linux-32.jar").absoluteFilePath()});
- QCOMPARE(native64, {QFileInfo("data/testname-testversion-linux-64.jar").absoluteFilePath()});
- QStringList failedFiles;
- auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString("data"));
- QCOMPARE(dls.size(), 0);
- QCOMPARE(failedFiles, {"data/testname-testversion-linux-64.jar"});
- }
- }
- void test_onenine()
- {
- auto test = readMojangJson("data/lib-simple.json");
- {
- QStringList jar, native, native32, native64;
- test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString());
- QCOMPARE(jar, getStorage("com/paulscode/codecwav/20101023/codecwav-20101023.jar"));
- QCOMPARE(native, {});
- QCOMPARE(native32, {});
- QCOMPARE(native64, {});
- }
- {
- QStringList failedFiles;
- auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString());
- QCOMPARE(dls.size(), 1);
- QCOMPARE(failedFiles, {});
- QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/com/paulscode/codecwav/20101023/codecwav-20101023.jar"));
- }
- test->setHint("local");
- {
- QStringList jar, native, native32, native64;
- test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString("data"));
- QCOMPARE(jar, {QFileInfo("data/codecwav-20101023.jar").absoluteFilePath()});
- QCOMPARE(native, {});
- QCOMPARE(native32, {});
- QCOMPARE(native64, {});
- }
- {
- QStringList failedFiles;
- auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString("data"));
- QCOMPARE(dls.size(), 0);
- QCOMPARE(failedFiles, {});
- }
- }
- void test_onenine_local_override()
- {
- auto test = readMojangJson("data/lib-simple.json");
- test->setHint("local");
- {
- QStringList jar, native, native32, native64;
- test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString("data"));
- QCOMPARE(jar, {QFileInfo("data/codecwav-20101023.jar").absoluteFilePath()});
- QCOMPARE(native, {});
- QCOMPARE(native32, {});
- QCOMPARE(native64, {});
- }
- {
- QStringList failedFiles;
- auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString("data"));
- QCOMPARE(dls.size(), 0);
- QCOMPARE(failedFiles, {});
- }
- }
- void test_onenine_native()
- {
- auto test = readMojangJson("data/lib-native.json");
- QStringList jar, native, native32, native64;
- test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString());
- QCOMPARE(jar, QStringList());
- QCOMPARE(native, getStorage("org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar"));
- QCOMPARE(native32, {});
- QCOMPARE(native64, {});
- QStringList failedFiles;
- auto dls = test->getDownloads(Os_OSX, cache.get(), failedFiles, QString());
- QCOMPARE(dls.size(), 1);
- QCOMPARE(failedFiles, {});
- QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar"));
- }
- void test_onenine_native_arch()
- {
- auto test = readMojangJson("data/lib-native-arch.json");
- QStringList jar, native, native32, native64;
- test->getApplicableFiles(Os_Windows, jar, native, native32, native64, QString());
- QCOMPARE(jar, {});
- QCOMPARE(native, {});
- QCOMPARE(native32, getStorage("tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar"));
- QCOMPARE(native64, getStorage("tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar"));
- QStringList failedFiles;
- auto dls = test->getDownloads(Os_Windows, cache.get(), failedFiles, QString());
- QCOMPARE(dls.size(), 2);
- QCOMPARE(failedFiles, {});
- QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar"));
- QCOMPARE(dls[1]->m_url, QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar"));
- }
-private:
- std::unique_ptr<HttpMetaCache> cache;
- QString dataDir;
-};
-
-QTEST_GUILESS_MAIN(LibraryTest)
-
-#include "Library_test.moc"
diff --git a/api/logic/minecraft/MinecraftInstance.cpp b/api/logic/minecraft/MinecraftInstance.cpp
deleted file mode 100644
index dbf9f816..00000000
--- a/api/logic/minecraft/MinecraftInstance.cpp
+++ /dev/null
@@ -1,1054 +0,0 @@
-#include "MinecraftInstance.h"
-#include <minecraft/launch/CreateGameFolders.h>
-#include <minecraft/launch/ExtractNatives.h>
-#include <minecraft/launch/PrintInstanceInfo.h>
-#include <settings/Setting.h>
-#include "settings/SettingsObject.h"
-#include "Env.h"
-#include <MMCStrings.h>
-#include <pathmatcher/RegexpMatcher.h>
-#include <pathmatcher/MultiMatcher.h>
-#include <FileSystem.h>
-#include <java/JavaVersion.h>
-
-#include "launch/LaunchTask.h"
-#include "launch/steps/LookupServerAddress.h"
-#include "launch/steps/PostLaunchCommand.h"
-#include "launch/steps/Update.h"
-#include "launch/steps/PreLaunchCommand.h"
-#include "launch/steps/TextPrint.h"
-#include "minecraft/launch/LauncherPartLaunch.h"
-#include "minecraft/launch/DirectJavaLaunch.h"
-#include "minecraft/launch/ModMinecraftJar.h"
-#include "minecraft/launch/ClaimAccount.h"
-#include "minecraft/launch/ReconstructAssets.h"
-#include "minecraft/launch/ScanModFolders.h"
-#include "minecraft/launch/VerifyJavaInstall.h"
-#include "java/launch/CheckJava.h"
-#include "java/JavaUtils.h"
-#include "meta/Index.h"
-#include "meta/VersionList.h"
-
-#include "mod/ModFolderModel.h"
-#include "mod/ResourcePackFolderModel.h"
-#include "mod/TexturePackFolderModel.h"
-#include "WorldList.h"
-
-#include "icons/IIconList.h"
-
-#include <QCoreApplication>
-#include "PackProfile.h"
-#include "AssetsUtils.h"
-#include "MinecraftUpdate.h"
-#include "MinecraftLoadAndCheck.h"
-#include <minecraft/gameoptions/GameOptions.h>
-#include <minecraft/update/FoldersTask.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);
- m_settings->registerPassthrough(globalSettings->getSetting("JavaArchitecture"), 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);
-
- // Minecraft launch method
- auto launchMethodOverride = m_settings->registerSetting("OverrideMCLaunchMethod", false);
- m_settings->registerOverride(globalSettings->getSetting("MCLaunchMethod"), launchMethodOverride);
-
- // Native library workarounds
- auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false);
- m_settings->registerOverride(globalSettings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride);
- m_settings->registerOverride(globalSettings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride);
-
- // Game time
- auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false);
- m_settings->registerOverride(globalSettings->getSetting("ShowGameTime"), gameTimeOverride);
- m_settings->registerOverride(globalSettings->getSetting("RecordGameTime"), gameTimeOverride);
-
- // Join server on launch, this does not have a global override
- m_settings->registerSetting("JoinServerOnLaunch", false);
- m_settings->registerSetting("JoinServerOnLaunchAddress", "");
-
- // DEPRECATED: Read what versions the user configuration thinks should be used
- m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, "");
- m_settings->registerSetting("LWJGLVersion", "");
- m_settings->registerSetting("ForgeVersion", "");
- m_settings->registerSetting("LiteloaderVersion", "");
-
- m_components.reset(new PackProfile(this));
- m_components->setOldConfigVersion("net.minecraft", m_settings->get("IntendedVersion").toString());
- auto setting = m_settings->getSetting("LWJGLVersion");
- m_components->setOldConfigVersion("org.lwjgl", m_settings->get("LWJGLVersion").toString());
- m_components->setOldConfigVersion("net.minecraftforge", m_settings->get("ForgeVersion").toString());
- m_components->setOldConfigVersion("com.mumfrey.liteloader", m_settings->get("LiteloaderVersion").toString());
-}
-
-void MinecraftInstance::saveNow()
-{
- m_components->saveNow();
-}
-
-QString MinecraftInstance::typeName() const
-{
- return "Minecraft";
-}
-
-std::shared_ptr<PackProfile> MinecraftInstance::getPackProfile() const
-{
- return m_components;
-}
-
-QSet<QString> MinecraftInstance::traits() const
-{
- auto components = getPackProfile();
- if (!components)
- {
- return {"version-incomplete"};
- }
- auto profile = components->getProfile();
- if (!profile)
- {
- return {"version-incomplete"};
- }
- return profile->getTraits();
-}
-
-QString MinecraftInstance::gameRoot() const
-{
- QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
- QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft"));
-
- if (mcDir.exists() && !dotMCDir.exists())
- return mcDir.filePath();
- else
- return dotMCDir.filePath();
-}
-
-QString MinecraftInstance::binRoot() const
-{
- return FS::PathCombine(gameRoot(), "bin");
-}
-
-QString MinecraftInstance::getNativePath() const
-{
- QDir natives_dir(FS::PathCombine(instanceRoot(), "natives/"));
- return natives_dir.absolutePath();
-}
-
-QString MinecraftInstance::getLocalLibraryPath() const
-{
- QDir libraries_dir(FS::PathCombine(instanceRoot(), "libraries/"));
- return libraries_dir.absolutePath();
-}
-
-QString MinecraftInstance::jarModsDir() const
-{
- QDir jarmods_dir(FS::PathCombine(instanceRoot(), "jarmods/"));
- return jarmods_dir.absolutePath();
-}
-
-QString MinecraftInstance::loaderModsDir() const
-{
- return FS::PathCombine(gameRoot(), "mods");
-}
-
-QString MinecraftInstance::modsCacheLocation() const
-{
- return FS::PathCombine(instanceRoot(), "mods.cache");
-}
-
-QString MinecraftInstance::coreModsDir() const
-{
- return FS::PathCombine(gameRoot(), "coremods");
-}
-
-QString MinecraftInstance::resourcePacksDir() const
-{
- return FS::PathCombine(gameRoot(), "resourcepacks");
-}
-
-QString MinecraftInstance::texturePacksDir() const
-{
- return FS::PathCombine(gameRoot(), "texturepacks");
-}
-
-QString MinecraftInstance::instanceConfigFolder() const
-{
- return FS::PathCombine(gameRoot(), "config");
-}
-
-QString MinecraftInstance::libDir() const
-{
- return FS::PathCombine(gameRoot(), "lib");
-}
-
-QString MinecraftInstance::worldDir() const
-{
- return FS::PathCombine(gameRoot(), "saves");
-}
-
-QString MinecraftInstance::resourcesDir() const
-{
- return FS::PathCombine(gameRoot(), "resources");
-}
-
-QDir MinecraftInstance::librariesPath() const
-{
- return QDir::current().absoluteFilePath("libraries");
-}
-
-QDir MinecraftInstance::jarmodsPath() const
-{
- return QDir(jarModsDir());
-}
-
-QDir MinecraftInstance::versionsPath() const
-{
- return QDir::current().absoluteFilePath("versions");
-}
-
-QStringList MinecraftInstance::getClassPath() const
-{
- QStringList jars, nativeJars;
- auto javaArchitecture = settings()->get("JavaArchitecture").toString();
- auto profile = m_components->getProfile();
- profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot());
- return jars;
-}
-
-QString MinecraftInstance::getMainClass() const
-{
- auto profile = m_components->getProfile();
- return profile->getMainClass();
-}
-
-QStringList MinecraftInstance::getNativeJars() const
-{
- QStringList jars, nativeJars;
- auto javaArchitecture = settings()->get("JavaArchitecture").toString();
- auto profile = m_components->getProfile();
- profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot());
- return nativeJars;
-}
-
-QStringList MinecraftInstance::extraArguments() const
-{
- auto list = BaseInstance::extraArguments();
- auto version = getPackProfile();
- if (!version)
- return list;
- auto jarMods = getJarMods();
- if (!jarMods.isEmpty())
- {
- list.append({"-Dfml.ignoreInvalidMinecraftCertificates=true",
- "-Dfml.ignorePatchDiscrepancies=true"});
- }
- return list;
-}
-
-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
- auto traits_ = traits();
- // HACK: fix issues on macOS with 1.13 snapshots
- // NOTE: Oracle Java option. if there are alternate jvm implementations, this would be the place to customize this for them
-#ifdef Q_OS_MAC
- if(traits_.contains("FirstThreadOnMacOS"))
- {
- args << QString("-XstartOnFirstThread");
- }
-#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
-
- int min = settings()->get("MinMemAlloc").toInt();
- int max = settings()->get("MaxMemAlloc").toInt();
- if(min < max)
- {
- args << QString("-Xms%1m").arg(min);
- args << QString("-Xmx%1m").arg(max);
- }
- else
- {
- args << QString("-Xms%1m").arg(max);
- args << QString("-Xmx%1m").arg(min);
- }
-
- // No PermGen in newer java.
- JavaVersion javaVersion = getJavaVersion();
- if(javaVersion.requiresPermGen())
- {
- auto permgen = settings()->get("PermGen").toInt();
- if (permgen != 64)
- {
- args << QString("-XX:PermSize=%1m").arg(permgen);
- }
- }
-
- args << "-Duser.language=en";
-
- 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(gameRoot()).absolutePath());
- out.insert("INST_JAVA", settings()->get("JavaPath").toString());
- out.insert("INST_JAVA_ARGS", javaArguments().join(' '));
- return out;
-}
-
-QProcessEnvironment MinecraftInstance::createEnvironment()
-{
- // prepare the process environment
- QProcessEnvironment env = CleanEnviroment();
-
- // export some infos
- auto variables = getVariables();
- for (auto it = variables.begin(); it != variables.end(); ++it)
- {
- env.insert(it.key(), it.value());
- }
- return env;
-}
-
-static 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 MinecraftInstance::processMinecraftArgs(
- AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) const
-{
- auto profile = m_components->getProfile();
- QString args_pattern = profile->getMinecraftArguments();
- for (auto tweaker : profile->getTweakers())
- {
- args_pattern += " --tweakClass " + tweaker;
- }
-
- if (serverToJoin && !serverToJoin->address.isEmpty())
- {
- args_pattern += " --server " + serverToJoin->address;
- args_pattern += " --port " + QString::number(serverToJoin->port);
- }
-
- QMap<QString, QString> token_mapping;
- // yggdrasil!
- if(session)
- {
- token_mapping["auth_username"] = session->username;
- token_mapping["auth_session"] = session->session;
- token_mapping["auth_access_token"] = session->access_token;
- token_mapping["auth_player_name"] = session->player_name;
- token_mapping["auth_uuid"] = session->uuid;
- token_mapping["user_properties"] = session->serializeUserProperties();
- token_mapping["user_type"] = session->user_type;
- }
-
- // blatant self-promotion.
- token_mapping["profile_name"] = token_mapping["version_name"] = "MultiMC5";
-
- token_mapping["version_type"] = profile->getMinecraftVersionType();
-
- QString absRootDir = QDir(gameRoot()).absolutePath();
- token_mapping["game_directory"] = absRootDir;
- QString absAssetsDir = QDir("assets/").absolutePath();
- auto assets = profile->getMinecraftAssets();
- token_mapping["game_assets"] = AssetsUtils::getAssetsDir(assets->id, resourcesDir()).absolutePath();
-
- // 1.7.3+ assets tokens
- token_mapping["assets_root"] = absAssetsDir;
- token_mapping["assets_index_name"] = assets->id;
-
- QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts);
- for (int i = 0; i < parts.length(); i++)
- {
- parts[i] = replaceTokensIn(parts[i], token_mapping);
- }
- return parts;
-}
-
-QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
-{
- QString launchScript;
-
- if (!m_components)
- return QString();
- auto profile = m_components->getProfile();
- if(!profile)
- return QString();
-
- auto mainClass = getMainClass();
- if (!mainClass.isEmpty())
- {
- launchScript += "mainClass " + mainClass + "\n";
- }
- auto appletClass = profile->getAppletClass();
- if (!appletClass.isEmpty())
- {
- launchScript += "appletClass " + appletClass + "\n";
- }
-
- if (serverToJoin && !serverToJoin->address.isEmpty())
- {
- launchScript += "serverAddress " + serverToJoin->address + "\n";
- launchScript += "serverPort " + QString::number(serverToJoin->port) + "\n";
- }
-
- // generic minecraft params
- for (auto param : processMinecraftArgs(
- session,
- nullptr /* When using a launch script, the server parameters are handled by it*/
- ))
- {
- launchScript += "param " + param + "\n";
- }
-
- // window size, title and state, legacy
- {
- QString windowParams;
- if (settings()->get("LaunchMaximized").toBool())
- windowParams = "max";
- else
- windowParams = QString("%1x%2")
- .arg(settings()->get("MinecraftWinWidth").toInt())
- .arg(settings()->get("MinecraftWinHeight").toInt());
- launchScript += "windowTitle " + windowTitle() + "\n";
- launchScript += "windowParams " + windowParams + "\n";
- }
-
- // legacy auth
- if(session)
- {
- launchScript += "userName " + session->player_name + "\n";
- launchScript += "sessionId " + session->session + "\n";
- }
-
- // libraries and class path.
- {
- QStringList jars, nativeJars;
- auto javaArchitecture = settings()->get("JavaArchitecture").toString();
- profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot());
- for(auto file: jars)
- {
- launchScript += "cp " + file + "\n";
- }
- for(auto file: nativeJars)
- {
- launchScript += "ext " + file + "\n";
- }
- launchScript += "natives " + getNativePath() + "\n";
- }
-
- for (auto trait : profile->getTraits())
- {
- launchScript += "traits " + trait + "\n";
- }
- launchScript += "launcher onesix\n";
- // qDebug() << "Generated launch script:" << launchScript;
- return launchScript;
-}
-
-QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
-{
- QStringList out;
- out << "Main Class:" << " " + getMainClass() << "";
- out << "Native path:" << " " + getNativePath() << "";
-
- auto profile = m_components->getProfile();
-
- auto alltraits = traits();
- if(alltraits.size())
- {
- out << "Traits:";
- for (auto trait : alltraits)
- {
- out << "traits " + trait;
- }
- out << "";
- }
-
- auto settings = this->settings();
- bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool();
- bool nativeGLFW = settings->get("UseNativeGLFW").toBool();
- if (nativeOpenAL || nativeGLFW)
- {
- if (nativeOpenAL)
- out << "Using system OpenAL.";
- if (nativeGLFW)
- out << "Using system GLFW.";
- out << "";
- }
-
- // libraries and class path.
- {
- out << "Libraries:";
- QStringList jars, nativeJars;
- auto javaArchitecture = settings->get("JavaArchitecture").toString();
- profile->getLibraryFiles(javaArchitecture, jars, nativeJars, getLocalLibraryPath(), binRoot());
- auto printLibFile = [&](const QString & path)
- {
- QFileInfo info(path);
- if(info.exists())
- {
- out << " " + path;
- }
- else
- {
- out << " " + path + " (missing)";
- }
- };
- for(auto file: jars)
- {
- printLibFile(file);
- }
- out << "";
- out << "Native libraries:";
- for(auto file: nativeJars)
- {
- printLibFile(file);
- }
- out << "";
- }
-
- auto printModList = [&](const QString & label, ModFolderModel & model) {
- if(model.size())
- {
- out << QString("%1:").arg(label);
- auto modList = model.allMods();
- std::sort(modList.begin(), modList.end(), [](Mod &a, Mod &b) {
- auto aName = a.filename().completeBaseName();
- auto bName = b.filename().completeBaseName();
- return aName.localeAwareCompare(bName) < 0;
- });
- for(auto & mod: modList)
- {
- if(mod.type() == Mod::MOD_FOLDER)
- {
- out << u8" [📁] " + mod.filename().completeBaseName() + " (folder)";
- continue;
- }
-
- if(mod.enabled()) {
- out << u8" [✔️] " + mod.filename().completeBaseName();
- }
- else {
- out << u8" [❌] " + mod.filename().completeBaseName() + " (disabled)";
- }
-
- }
- out << "";
- }
- };
-
- printModList("Mods", *(loaderModList().get()));
- printModList("Core Mods", *(coreModList().get()));
-
- auto & jarMods = profile->getJarMods();
- if(jarMods.size())
- {
- out << "Jar Mods:";
- for(auto & jarmod: jarMods)
- {
- auto displayname = jarmod->displayName(currentSystem);
- auto realname = jarmod->filename(currentSystem);
- if(displayname != realname)
- {
- out << " " + displayname + " (" + realname + ")";
- }
- else
- {
- out << " " + realname;
- }
- }
- out << "";
- }
-
- auto params = processMinecraftArgs(nullptr, serverToJoin);
- out << "Params:";
- out << " " + params.join(' ');
- out << "";
-
- QString windowParams;
- if (settings->get("LaunchMaximized").toBool())
- {
- out << "Window size: max (if available)";
- }
- else
- {
- auto width = settings->get("MinecraftWinWidth").toInt();
- auto height = settings->get("MinecraftWinHeight").toInt();
- out << "Window size: " + QString::number(width) + " x " + QString::number(height);
- }
- out << "";
- return out;
-}
-
-QMap<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>"));
-
- auto i = sessionRef.u.properties.begin();
- while (i != sessionRef.u.properties.end())
- {
- if(i.value().length() <= 3) {
- ++i;
- continue;
- }
- 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 gameRoot();
-}
-
-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 (hasVersionBroken())
- {
- traits.append(tr("broken"));
- }
-
- QString description;
- description.append(tr("Minecraft %1 (%2)").arg(m_components->getComponentVersion("net.minecraft")).arg(typeName()));
- if(m_settings->get("ShowGameTime").toBool())
- {
- if (lastTimePlayed() > 0) {
- description.append(tr(", last played for %1").arg(prettifyTimeDuration(lastTimePlayed())));
- }
-
- if (totalTimePlayed() > 0) {
- description.append(tr(", total played for %1").arg(prettifyTimeDuration(totalTimePlayed())));
- }
- }
- if(hasCrashed())
- {
- description.append(tr(", has crashed."));
- }
- return description;
-}
-
-shared_qobject_ptr<Task> MinecraftInstance::createUpdateTask(Net::Mode mode)
-{
- switch (mode)
- {
- case Net::Mode::Offline:
- {
- return shared_qobject_ptr<Task>(new MinecraftLoadAndCheck(this));
- }
- case Net::Mode::Online:
- {
- return shared_qobject_ptr<Task>(new MinecraftUpdate(this));
- }
- }
- return nullptr;
-}
-
-shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
-{
- // FIXME: get rid of shared_from_this ...
- auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(shared_from_this()));
- auto pptr = process.get();
-
- ENV.icons()->saveIcon(iconKey(), FS::PathCombine(gameRoot(), "icon.png"), "PNG");
-
- // print a header
- {
- process->appendStep(new TextPrint(pptr, "Minecraft folder is:\n" + gameRoot() + "\n\n", MessageLevel::MultiMC));
- }
-
- // check java
- {
- process->appendStep(new CheckJava(pptr));
- }
-
- // check launch method
- QStringList validMethods = {"LauncherPart", "DirectJava"};
- QString method = launchMethod();
- if(!validMethods.contains(method))
- {
- process->appendStep(new TextPrint(pptr, "Selected launch method \"" + method + "\" is not valid.\n", MessageLevel::Fatal));
- return process;
- }
-
- // create the .minecraft folder and server-resource-packs (workaround for Minecraft bug MCL-3732)
- {
- process->appendStep(new CreateGameFolders(pptr));
- }
-
- if (!serverToJoin && m_settings->get("JoinServerOnLaunch").toBool())
- {
- QString fullAddress = m_settings->get("JoinServerOnLaunchAddress").toString();
- serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(fullAddress)));
- }
-
- if(serverToJoin && serverToJoin->port == 25565)
- {
- // Resolve server address to join on launch
- auto *step = new LookupServerAddress(pptr);
- step->setLookupAddress(serverToJoin->address);
- step->setOutputAddressPtr(serverToJoin);
- process->appendStep(step);
- }
-
- // run pre-launch command if that's needed
- if(getPreLaunchCommand().size())
- {
- auto step = new PreLaunchCommand(pptr);
- step->setWorkingDirectory(gameRoot());
- process->appendStep(step);
- }
-
- // if we aren't in offline mode,.
- if(session->status != AuthSession::PlayableOffline)
- {
- process->appendStep(new ClaimAccount(pptr, session));
- process->appendStep(new Update(pptr, Net::Mode::Online));
- }
- else
- {
- process->appendStep(new Update(pptr, Net::Mode::Offline));
- }
-
- // if there are any jar mods
- {
- process->appendStep(new ModMinecraftJar(pptr));
- }
-
- // Scan mods folders for mods
- {
- process->appendStep(new ScanModFolders(pptr));
- }
-
- // print some instance info here...
- {
- process->appendStep(new PrintInstanceInfo(pptr, session, serverToJoin));
- }
-
- // extract native jars if needed
- {
- process->appendStep(new ExtractNatives(pptr));
- }
-
- // reconstruct assets if needed
- {
- process->appendStep(new ReconstructAssets(pptr));
- }
-
- // verify that minimum Java requirements are met
- {
- process->appendStep(new VerifyJavaInstall(pptr));
- }
-
- {
- // actually launch the game
- auto method = launchMethod();
- if(method == "LauncherPart")
- {
- auto step = new LauncherPartLaunch(pptr);
- step->setWorkingDirectory(gameRoot());
- step->setAuthSession(session);
- step->setServerToJoin(serverToJoin);
- process->appendStep(step);
- }
- else if (method == "DirectJava")
- {
- auto step = new DirectJavaLaunch(pptr);
- step->setWorkingDirectory(gameRoot());
- step->setAuthSession(session);
- step->setServerToJoin(serverToJoin);
- process->appendStep(step);
- }
- }
-
- // run post-exit command if that's needed
- if(getPostExitCommand().size())
- {
- auto step = new PostLaunchCommand(pptr);
- step->setWorkingDirectory(gameRoot());
- process->appendStep(step);
- }
- if (session)
- {
- process->setCensorFilter(createCensorFilterFromSession(session));
- }
- m_launchProcess = process;
- emit launchTaskChanged(m_launchProcess);
- return m_launchProcess;
-}
-
-QString MinecraftInstance::launchMethod()
-{
- return m_settings->get("MCLaunchMethod").toString();
-}
-
-JavaVersion MinecraftInstance::getJavaVersion() const
-{
- return JavaVersion(settings()->get("JavaVersion").toString());
-}
-
-std::shared_ptr<ModFolderModel> MinecraftInstance::loaderModList() const
-{
- if (!m_loader_mod_list)
- {
- m_loader_mod_list.reset(new ModFolderModel(loaderModsDir()));
- m_loader_mod_list->disableInteraction(isRunning());
- connect(this, &BaseInstance::runningStatusChanged, m_loader_mod_list.get(), &ModFolderModel::disableInteraction);
- }
- return m_loader_mod_list;
-}
-
-std::shared_ptr<ModFolderModel> MinecraftInstance::coreModList() const
-{
- if (!m_core_mod_list)
- {
- m_core_mod_list.reset(new ModFolderModel(coreModsDir()));
- m_core_mod_list->disableInteraction(isRunning());
- connect(this, &BaseInstance::runningStatusChanged, m_core_mod_list.get(), &ModFolderModel::disableInteraction);
- }
- return m_core_mod_list;
-}
-
-std::shared_ptr<ModFolderModel> MinecraftInstance::resourcePackList() const
-{
- if (!m_resource_pack_list)
- {
- m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir()));
- m_resource_pack_list->disableInteraction(isRunning());
- connect(this, &BaseInstance::runningStatusChanged, m_resource_pack_list.get(), &ModFolderModel::disableInteraction);
- }
- return m_resource_pack_list;
-}
-
-std::shared_ptr<ModFolderModel> MinecraftInstance::texturePackList() const
-{
- if (!m_texture_pack_list)
- {
- m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir()));
- m_texture_pack_list->disableInteraction(isRunning());
- connect(this, &BaseInstance::runningStatusChanged, m_texture_pack_list.get(), &ModFolderModel::disableInteraction);
- }
- return m_texture_pack_list;
-}
-
-std::shared_ptr<WorldList> MinecraftInstance::worldList() const
-{
- if (!m_world_list)
- {
- m_world_list.reset(new WorldList(worldDir()));
- }
- return m_world_list;
-}
-
-std::shared_ptr<GameOptions> MinecraftInstance::gameOptionsModel() const
-{
- if (!m_game_options)
- {
- m_game_options.reset(new GameOptions(FS::PathCombine(gameRoot(), "options.txt")));
- }
- return m_game_options;
-}
-
-QList< Mod > MinecraftInstance::getJarMods() const
-{
- auto profile = m_components->getProfile();
- QList<Mod> mods;
- for (auto jarmod : profile->getJarMods())
- {
- QStringList jar, temp1, temp2, temp3;
- jarmod->getApplicableFiles(currentSystem, jar, temp1, temp2, temp3, jarmodsPath().absolutePath());
- // QString filePath = jarmodsPath().absoluteFilePath(jarmod->filename(currentSystem));
- mods.push_back(Mod(QFileInfo(jar[0])));
- }
- return mods;
-}
-
-
-#include "MinecraftInstance.moc"
diff --git a/api/logic/minecraft/MinecraftInstance.h b/api/logic/minecraft/MinecraftInstance.h
deleted file mode 100644
index 05600797..00000000
--- a/api/logic/minecraft/MinecraftInstance.h
+++ /dev/null
@@ -1,133 +0,0 @@
-#pragma once
-#include "BaseInstance.h"
-#include <java/JavaVersion.h>
-#include "minecraft/mod/Mod.h"
-#include <QProcess>
-#include <QDir>
-#include "multimc_logic_export.h"
-#include "minecraft/launch/MinecraftServerTarget.h"
-
-class ModFolderModel;
-class WorldList;
-class GameOptions;
-class LaunchStep;
-class PackProfile;
-
-class MULTIMC_LOGIC_EXPORT MinecraftInstance: public BaseInstance
-{
- Q_OBJECT
-public:
- MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
- virtual ~MinecraftInstance() {};
- virtual void saveNow() override;
-
- // FIXME: remove
- QString typeName() const override;
- // FIXME: remove
- QSet<QString> traits() const override;
-
- bool canEdit() const override
- {
- return true;
- }
-
- bool canExport() const override
- {
- return true;
- }
-
- ////// Directories and files //////
- QString jarModsDir() const;
- QString resourcePacksDir() const;
- QString texturePacksDir() const;
- QString loaderModsDir() const;
- QString coreModsDir() const;
- QString modsCacheLocation() const;
- QString libDir() const;
- QString worldDir() const;
- QString resourcesDir() const;
- QDir jarmodsPath() const;
- QDir librariesPath() const;
- QDir versionsPath() const;
- QString instanceConfigFolder() const override;
-
- // Path to the instance's minecraft directory.
- QString gameRoot() const override;
-
- // Path to the instance's minecraft bin directory.
- QString binRoot() const;
-
- // where to put the natives during/before launch
- QString getNativePath() const;
-
- // where the instance-local libraries should be
- QString getLocalLibraryPath() const;
-
-
- ////// Profile management //////
- std::shared_ptr<PackProfile> getPackProfile() const;
-
- ////// Mod Lists //////
- std::shared_ptr<ModFolderModel> loaderModList() const;
- std::shared_ptr<ModFolderModel> coreModList() const;
- std::shared_ptr<ModFolderModel> resourcePackList() const;
- std::shared_ptr<ModFolderModel> texturePackList() const;
- std::shared_ptr<WorldList> worldList() const;
- std::shared_ptr<GameOptions> gameOptionsModel() const;
-
- ////// Launch stuff //////
- shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override;
- shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override;
- QStringList extraArguments() const override;
- QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override;
- QList<Mod> getJarMods() const;
- QString createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin);
- /// get arguments passed to java
- QStringList javaArguments() const;
-
- /// get variables for launch command variable substitution/environment
- QMap<QString, QString> getVariables() const override;
-
- /// create an environment for launching processes
- QProcessEnvironment createEnvironment() override;
-
- /// guess log level from a line of minecraft log
- MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level) override;
-
- IPathMatcher::Ptr getLogFileMatcher() override;
-
- QString getLogFileRoot() override;
-
- QString getStatusbarDescription() override;
-
- // FIXME: remove
- virtual QStringList getClassPath() const;
- // FIXME: remove
- virtual QStringList getNativeJars() const;
- // FIXME: remove
- virtual QString getMainClass() const;
-
- // FIXME: remove
- virtual QStringList processMinecraftArgs(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) const;
-
- virtual JavaVersion getJavaVersion() const;
-
-protected:
- QMap<QString, QString> createCensorFilterFromSession(AuthSessionPtr session);
- QStringList validLaunchMethods();
- QString launchMethod();
-
-private:
- QString prettifyTimeDuration(int64_t duration);
-
-protected: // data
- std::shared_ptr<PackProfile> m_components;
- mutable std::shared_ptr<ModFolderModel> m_loader_mod_list;
- mutable std::shared_ptr<ModFolderModel> m_core_mod_list;
- mutable std::shared_ptr<ModFolderModel> m_resource_pack_list;
- mutable std::shared_ptr<ModFolderModel> m_texture_pack_list;
- mutable std::shared_ptr<WorldList> m_world_list;
- mutable std::shared_ptr<GameOptions> m_game_options;
-};
-
-typedef std::shared_ptr<MinecraftInstance> MinecraftInstancePtr;
diff --git a/api/logic/minecraft/MinecraftLoadAndCheck.cpp b/api/logic/minecraft/MinecraftLoadAndCheck.cpp
deleted file mode 100644
index 79b0c484..00000000
--- a/api/logic/minecraft/MinecraftLoadAndCheck.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-#include "MinecraftLoadAndCheck.h"
-#include "MinecraftInstance.h"
-#include "PackProfile.h"
-
-MinecraftLoadAndCheck::MinecraftLoadAndCheck(MinecraftInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
-{
-}
-
-void MinecraftLoadAndCheck::executeTask()
-{
- // add offline metadata load task
- auto components = m_inst->getPackProfile();
- components->reload(Net::Mode::Offline);
- m_task = components->getCurrentTask();
-
- if(!m_task)
- {
- emitSucceeded();
- return;
- }
- connect(m_task.get(), &Task::succeeded, this, &MinecraftLoadAndCheck::subtaskSucceeded);
- connect(m_task.get(), &Task::failed, this, &MinecraftLoadAndCheck::subtaskFailed);
- connect(m_task.get(), &Task::progress, this, &MinecraftLoadAndCheck::progress);
- connect(m_task.get(), &Task::status, this, &MinecraftLoadAndCheck::setStatus);
-}
-
-void MinecraftLoadAndCheck::subtaskSucceeded()
-{
- if(isFinished())
- {
- qCritical() << "MinecraftUpdate: Subtask" << sender() << "succeeded, but work was already done!";
- return;
- }
- emitSucceeded();
-}
-
-void MinecraftLoadAndCheck::subtaskFailed(QString error)
-{
- if(isFinished())
- {
- qCritical() << "MinecraftUpdate: Subtask" << sender() << "failed, but work was already done!";
- return;
- }
- emitFailed(error);
-}
diff --git a/api/logic/minecraft/MinecraftLoadAndCheck.h b/api/logic/minecraft/MinecraftLoadAndCheck.h
deleted file mode 100644
index 3435b52b..00000000
--- a/api/logic/minecraft/MinecraftLoadAndCheck.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QList>
-#include <QUrl>
-
-#include "tasks/Task.h"
-#include <quazip.h>
-
-#include "QObjectPtr.h"
-
-class MinecraftVersion;
-class MinecraftInstance;
-
-class MinecraftLoadAndCheck : public Task
-{
- Q_OBJECT
-public:
- explicit MinecraftLoadAndCheck(MinecraftInstance *inst, QObject *parent = 0);
- virtual ~MinecraftLoadAndCheck() {};
- void executeTask() override;
-
-private slots:
- void subtaskSucceeded();
- void subtaskFailed(QString error);
-
-private:
- MinecraftInstance *m_inst = nullptr;
- shared_qobject_ptr<Task> m_task;
- QString m_preFailure;
- QString m_fail_reason;
-};
-
diff --git a/api/logic/minecraft/MinecraftUpdate.cpp b/api/logic/minecraft/MinecraftUpdate.cpp
deleted file mode 100644
index 8f1565b0..00000000
--- a/api/logic/minecraft/MinecraftUpdate.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Env.h"
-#include "MinecraftUpdate.h"
-#include "MinecraftInstance.h"
-
-#include <QFile>
-#include <QFileInfo>
-#include <QTextStream>
-#include <QDataStream>
-
-#include "BaseInstance.h"
-#include "minecraft/PackProfile.h"
-#include "minecraft/Library.h"
-#include <FileSystem.h>
-
-#include "update/FoldersTask.h"
-#include "update/LibrariesTask.h"
-#include "update/FMLLibrariesTask.h"
-#include "update/AssetUpdateTask.h"
-
-#include <meta/Index.h>
-#include <meta/Version.h>
-
-MinecraftUpdate::MinecraftUpdate(MinecraftInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
-{
-}
-
-void MinecraftUpdate::executeTask()
-{
- m_tasks.clear();
- // create folders
- {
- m_tasks.append(std::make_shared<FoldersTask>(m_inst));
- }
-
- // add metadata update task if necessary
- {
- auto components = m_inst->getPackProfile();
- components->reload(Net::Mode::Online);
- auto task = components->getCurrentTask();
- if(task)
- {
- m_tasks.append(task.unwrap());
- }
- }
-
- // libraries download
- {
- m_tasks.append(std::make_shared<LibrariesTask>(m_inst));
- }
-
- // FML libraries download and copy into the instance
- {
- m_tasks.append(std::make_shared<FMLLibrariesTask>(m_inst));
- }
-
- // assets update
- {
- m_tasks.append(std::make_shared<AssetUpdateTask>(m_inst));
- }
-
- if(!m_preFailure.isEmpty())
- {
- emitFailed(m_preFailure);
- return;
- }
- next();
-}
-
-void MinecraftUpdate::next()
-{
- if(m_abort)
- {
- emitFailed(tr("Aborted by user."));
- return;
- }
- if(m_failed_out_of_order)
- {
- emitFailed(m_fail_reason);
- return;
- }
- m_currentTask ++;
- if(m_currentTask > 0)
- {
- auto task = m_tasks[m_currentTask - 1];
- disconnect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded);
- disconnect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
- disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
- disconnect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
- }
- if(m_currentTask == m_tasks.size())
- {
- emitSucceeded();
- return;
- }
- auto task = m_tasks[m_currentTask];
- // if the task is already finished by the time we look at it, skip it
- if(task->isFinished())
- {
- qCritical() << "MinecraftUpdate: Skipping finished subtask" << m_currentTask << ":" << task.get();
- next();
- }
- connect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded);
- connect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
- connect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
- connect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
- // if the task is already running, do not start it again
- if(!task->isRunning())
- {
- task->start();
- }
-}
-
-void MinecraftUpdate::subtaskSucceeded()
-{
- if(isFinished())
- {
- qCritical() << "MinecraftUpdate: Subtask" << sender() << "succeeded, but work was already done!";
- return;
- }
- auto senderTask = QObject::sender();
- auto currentTask = m_tasks[m_currentTask].get();
- if(senderTask != currentTask)
- {
- qDebug() << "MinecraftUpdate: Subtask" << sender() << "succeeded out of order.";
- return;
- }
- next();
-}
-
-void MinecraftUpdate::subtaskFailed(QString error)
-{
- if(isFinished())
- {
- qCritical() << "MinecraftUpdate: Subtask" << sender() << "failed, but work was already done!";
- return;
- }
- auto senderTask = QObject::sender();
- auto currentTask = m_tasks[m_currentTask].get();
- if(senderTask != currentTask)
- {
- qDebug() << "MinecraftUpdate: Subtask" << sender() << "failed out of order.";
- m_failed_out_of_order = true;
- m_fail_reason = error;
- return;
- }
- emitFailed(error);
-}
-
-
-bool MinecraftUpdate::abort()
-{
- if(!m_abort)
- {
- m_abort = true;
- auto task = m_tasks[m_currentTask];
- if(task->canAbort())
- {
- return task->abort();
- }
- }
- return true;
-}
-
-bool MinecraftUpdate::canAbort() const
-{
- return true;
-}
diff --git a/api/logic/minecraft/MinecraftUpdate.h b/api/logic/minecraft/MinecraftUpdate.h
deleted file mode 100644
index fadebff9..00000000
--- a/api/logic/minecraft/MinecraftUpdate.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QList>
-#include <QUrl>
-
-#include "net/NetJob.h"
-#include "tasks/Task.h"
-#include "minecraft/VersionFilterData.h"
-#include <quazip.h>
-
-class MinecraftVersion;
-class MinecraftInstance;
-
-class MinecraftUpdate : public Task
-{
- Q_OBJECT
-public:
- explicit MinecraftUpdate(MinecraftInstance *inst, QObject *parent = 0);
- virtual ~MinecraftUpdate() {};
-
- void executeTask() override;
- bool canAbort() const override;
-
-private
-slots:
- bool abort() override;
- void subtaskSucceeded();
- void subtaskFailed(QString error);
-
-private:
- void next();
-
-private:
- MinecraftInstance *m_inst = nullptr;
- QList<std::shared_ptr<Task>> m_tasks;
- QString m_preFailure;
- int m_currentTask = -1;
- bool m_abort = false;
- bool m_failed_out_of_order = false;
- QString m_fail_reason;
-};
diff --git a/api/logic/minecraft/MojangDownloadInfo.h b/api/logic/minecraft/MojangDownloadInfo.h
deleted file mode 100644
index 88f87287..00000000
--- a/api/logic/minecraft/MojangDownloadInfo.h
+++ /dev/null
@@ -1,82 +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;
- // HACK: ignore assets from other version files than Minecraft
- // workaround for stupid assets issue caused by amazon:
- // https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/
- if(id == "legacy")
- {
- url = "https://launchermeta.mojang.com/mc/assets/legacy/c0fd82e8ce9fbc93119e40d96d5a4e62cfa3f729/legacy.json";
- }
- // HACK
- else
- {
- url = "https://s3.amazonaws.com/Minecraft.Download/indexes/" + id + ".json";
- }
- known = false;
- }
-
- // data
- int totalSize;
- QString id;
- bool known = true;
-};
diff --git a/api/logic/minecraft/MojangVersionFormat.cpp b/api/logic/minecraft/MojangVersionFormat.cpp
deleted file mode 100644
index f9cb2228..00000000
--- a/api/logic/minecraft/MojangVersionFormat.cpp
+++ /dev/null
@@ -1,383 +0,0 @@
-#include "MojangVersionFormat.h"
-#include "OneSixVersionFormat.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(ProblemSeverity::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->releaseTime = timeFromS3Time(in.value("releaseTime").toString(""));
- out->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(
- ProblemSeverity::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->uid = "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(*out, 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->releaseTime.isNull())
- {
- writeString(out, "releaseTime", timeToS3Time(in->releaseTime));
- }
- if(!in->updateTime.isNull())
- {
- writeString(out, "time", timeToS3Time(in->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(ProblemContainer & problems, 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");
- }
- auto rawName = libObj.value("name").toString();
- out->m_name = rawName;
- if(!out->m_name.valid()) {
- problems.addProblem(ProblemSeverity::Error, QObject::tr("Library %1 name is broken and cannot be processed.").arg(rawName));
- }
-
- 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", library->m_name.serialize());
- 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/api/logic/minecraft/MojangVersionFormat.h b/api/logic/minecraft/MojangVersionFormat.h
deleted file mode 100644
index 2871dae4..00000000
--- a/api/logic/minecraft/MojangVersionFormat.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-
-#include <minecraft/VersionFile.h>
-#include <minecraft/Library.h>
-#include <QJsonDocument>
-#include <ProblemProvider.h>
-
-#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(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename);
- static QJsonObject libraryToJson(Library *library);
-};
diff --git a/api/logic/minecraft/MojangVersionFormat_test.cpp b/api/logic/minecraft/MojangVersionFormat_test.cpp
deleted file mode 100644
index 9d095340..00000000
--- a/api/logic/minecraft/MojangVersionFormat_test.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-#include <QTest>
-#include <QDebug>
-#include "TestUtil.h"
-
-#include "minecraft/MojangVersionFormat.h"
-
-class MojangVersionFormatTest : public QObject
-{
- Q_OBJECT
-
- static QJsonDocument readJson(const char *file)
- {
- auto path = QFINDTESTDATA(file);
- QFile jsonFile(path);
- jsonFile.open(QIODevice::ReadOnly);
- auto data = jsonFile.readAll();
- jsonFile.close();
- return QJsonDocument::fromJson(data);
- }
- static void writeJson(const char *file, QJsonDocument doc)
- {
- QFile jsonFile(file);
- jsonFile.open(QIODevice::WriteOnly | QIODevice::Text);
- auto data = doc.toJson(QJsonDocument::Indented);
- qDebug() << QString::fromUtf8(data);
- jsonFile.write(data);
- jsonFile.close();
- }
-
-private
-slots:
- void test_Through_Simple()
- {
- QJsonDocument doc = readJson("data/1.9-simple.json");
- auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9-simple.json");
- auto doc2 = MojangVersionFormat::versionFileToJson(vfile);
- writeJson("1.9-simple-passthorugh.json", doc2);
-
- QCOMPARE(doc.toJson(), doc2.toJson());
- }
-
- void test_Through()
- {
- QJsonDocument doc = readJson("data/1.9.json");
- auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9.json");
- auto doc2 = MojangVersionFormat::versionFileToJson(vfile);
- writeJson("1.9-passthorugh.json", doc2);
- QCOMPARE(doc.toJson(), doc2.toJson());
- }
-};
-
-QTEST_GUILESS_MAIN(MojangVersionFormatTest)
-
-#include "MojangVersionFormat_test.moc"
-
diff --git a/api/logic/minecraft/OneSixVersionFormat.cpp b/api/logic/minecraft/OneSixVersionFormat.cpp
deleted file mode 100644
index 0329d70e..00000000
--- a/api/logic/minecraft/OneSixVersionFormat.cpp
+++ /dev/null
@@ -1,391 +0,0 @@
-#include "OneSixVersionFormat.h"
-#include <Json.h>
-#include "minecraft/ParseUtils.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(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename)
-{
- LibraryPtr out = MojangVersionFormat::libraryFromJson(problems, libObj, filename);
- readString(libObj, "MMC-hint", out->m_hint);
- readString(libObj, "MMC-absulute_url", out->m_absoluteURL);
- readString(libObj, "MMC-absoluteUrl", out->m_absoluteURL);
- readString(libObj, "MMC-filename", out->m_filename);
- readString(libObj, "MMC-displayname", out->m_displayname);
- return out;
-}
-
-QJsonObject OneSixVersionFormat::libraryToJson(Library *library)
-{
- QJsonObject libRoot = MojangVersionFormat::libraryToJson(library);
- if (library->m_absoluteURL.size())
- libRoot.insert("MMC-absoluteUrl", library->m_absoluteURL);
- if (library->m_hint.size())
- libRoot.insert("MMC-hint", library->m_hint);
- if (library->m_filename.size())
- libRoot.insert("MMC-filename", library->m_filename);
- if (library->m_displayname.size())
- libRoot.insert("MMC-displayname", library->m_displayname);
- return libRoot;
-}
-
-VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc, const QString &filename, const bool requireOrder)
-{
- VersionFilePtr out(new VersionFile());
- if (doc.isEmpty() || doc.isNull())
- {
- throw JSONValidationError(filename + " is empty or null");
- }
- if (!doc.isObject())
- {
- throw JSONValidationError(filename + " is not an object");
- }
-
- QJsonObject root = doc.object();
-
- Meta::MetadataVersion formatVersion = Meta::parseFormatVersion(root, false);
- switch(formatVersion)
- {
- case Meta::MetadataVersion::InitialRelease:
- break;
- case Meta::MetadataVersion::Invalid:
- throw JSONValidationError(filename + " does not contain a recognizable version of the metadata format.");
- }
-
- if (requireOrder)
- {
- if (root.contains("order"))
- {
- out->order = requireInteger(root.value("order"));
- }
- else
- {
- // FIXME: evaluate if we don't want to throw exceptions here instead
- qCritical() << filename << "doesn't contain an order field";
- }
- }
-
- out->name = root.value("name").toString();
-
- if(root.contains("uid"))
- {
- out->uid = root.value("uid").toString();
- }
- else
- {
- out->uid = root.value("fileId").toString();
- }
-
- out->version = root.value("version").toString();
-
- 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(*out, libObj, filename);
- // and add to jar mods
- out->jarMods.append(lib);
- }
- }
- else if (root.contains("+jarMods")) // DEPRECATED: old style '+jarMods' are only here for backwards compatibility
- {
- for (auto libVal : requireArray(root.value("+jarMods")))
- {
- QJsonObject libObj = requireObject(libVal);
- // parse the jarmod
- auto lib = OneSixVersionFormat::plusJarModFromJson(*out, libObj, filename, out->name);
- // and add to jar mods
- out->jarMods.append(lib);
- }
- }
-
- if (root.contains("mods"))
- {
- for (auto libVal : requireArray(root.value("mods")))
- {
- QJsonObject libObj = requireObject(libVal);
- // parse the jarmod
- auto lib = OneSixVersionFormat::modFromJson(*out, libObj, filename);
- // and add to jar mods
- out->mods.append(lib);
- }
- }
-
- auto readLibs = [&](const char * which, QList<LibraryPtr> & outList)
- {
- for (auto libVal : requireArray(root.value(which)))
- {
- QJsonObject libObj = requireObject(libVal);
- // parse the library
- auto lib = libraryFromJson(*out, libObj, filename);
- outList.append(lib);
- }
- };
- bool hasPlusLibs = root.contains("+libraries");
- bool hasLibs = root.contains("libraries");
- if (hasPlusLibs && hasLibs)
- {
- out->addProblem(ProblemSeverity::Warning,
- QObject::tr("Version file has both '+libraries' and 'libraries'. This is no longer supported."));
- readLibs("libraries", out->libraries);
- readLibs("+libraries", out->libraries);
- }
- else if (hasLibs)
- {
- readLibs("libraries", out->libraries);
- }
- else if(hasPlusLibs)
- {
- readLibs("+libraries", out->libraries);
- }
-
- if(root.contains("mavenFiles")) {
- readLibs("mavenFiles", out->mavenFiles);
- }
-
- // if we have mainJar, just use it
- if(root.contains("mainJar"))
- {
- QJsonObject libObj = requireObject(root, "mainJar");
- out->mainJar = libraryFromJson(*out, libObj, filename);
- }
- // else reconstruct it from downloads and id ... if that's available
- else if(!out->minecraftVersion.isEmpty())
- {
- auto lib = std::make_shared<Library>();
- lib->setRawName(GradleSpecifier(QString("com.mojang:minecraft:%1:client").arg(out->minecraftVersion)));
- // we have a reliable client download, use it.
- if(out->mojangDownloads.contains("client"))
- {
- auto LibDLInfo = std::make_shared<MojangLibraryDownloadInfo>();
- LibDLInfo->artifact = out->mojangDownloads["client"];
- lib->setMojangDownloadInfo(LibDLInfo);
- }
- // we got nothing...
- else
- {
- out->addProblem(
- ProblemSeverity::Error,
- QObject::tr("URL for the main jar could not be determined - Mojang removed the server that we used as fallback.")
- );
- }
- out->mainJar = lib;
- }
-
- if (root.contains("requires"))
- {
- Meta::parseRequires(root, &out->requires);
- }
- QString dependsOnMinecraftVersion = root.value("mcVersion").toString();
- if(!dependsOnMinecraftVersion.isEmpty())
- {
- Meta::Require mcReq;
- mcReq.uid = "net.minecraft";
- mcReq.equalsVersion = dependsOnMinecraftVersion;
- if (out->requires.count(mcReq) == 0)
- {
- out->requires.insert(mcReq);
- }
- }
- if (root.contains("conflicts"))
- {
- Meta::parseRequires(root, &out->conflicts);
- }
- if (root.contains("volatile"))
- {
- out->m_volatile = requireBoolean(root, "volatile");
- }
-
- /* removed features that shouldn't be used */
- if (root.contains("tweakers"))
- {
- out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element 'tweakers'"));
- }
- if (root.contains("-libraries"))
- {
- out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-libraries'"));
- }
- if (root.contains("-tweakers"))
- {
- out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-tweakers'"));
- }
- if (root.contains("-minecraftArguments"))
- {
- out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-minecraftArguments'"));
- }
- if (root.contains("+minecraftArguments"))
- {
- out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '+minecraftArguments'"));
- }
- return out;
-}
-
-QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch)
-{
- QJsonObject root;
- writeString(root, "name", patch->name);
-
- writeString(root, "uid", patch->uid);
-
- writeString(root, "version", patch->version);
-
- Meta::serializeFormatVersion(root, Meta::MetadataVersion::InitialRelease);
-
- MojangVersionFormat::writeVersionProperties(patch.get(), root);
-
- if(patch->mainJar)
- {
- root.insert("mainJar", libraryToJson(patch->mainJar.get()));
- }
- writeString(root, "appletClass", patch->appletClass);
- writeStringList(root, "+tweakers", patch->addTweakers);
- writeStringList(root, "+traits", patch->traits.toList());
- if (!patch->libraries.isEmpty())
- {
- QJsonArray array;
- for (auto value: patch->libraries)
- {
- array.append(OneSixVersionFormat::libraryToJson(value.get()));
- }
- root.insert("libraries", array);
- }
- if (!patch->mavenFiles.isEmpty())
- {
- QJsonArray array;
- for (auto value: patch->mavenFiles)
- {
- array.append(OneSixVersionFormat::libraryToJson(value.get()));
- }
- root.insert("mavenFiles", array);
- }
- if (!patch->jarMods.isEmpty())
- {
- QJsonArray array;
- for (auto value: patch->jarMods)
- {
- array.append(OneSixVersionFormat::jarModtoJson(value.get()));
- }
- root.insert("jarMods", array);
- }
- if (!patch->mods.isEmpty())
- {
- QJsonArray array;
- for (auto value: patch->jarMods)
- {
- array.append(OneSixVersionFormat::modtoJson(value.get()));
- }
- root.insert("mods", array);
- }
- if(!patch->requires.empty())
- {
- Meta::serializeRequires(root, &patch->requires, "requires");
- }
- if(!patch->conflicts.empty())
- {
- Meta::serializeRequires(root, &patch->conflicts, "conflicts");
- }
- if(patch->m_volatile)
- {
- root.insert("volatile", true);
- }
- // write the contents to a json document.
- {
- QJsonDocument out;
- out.setObject(root);
- return out;
- }
-}
-
-LibraryPtr OneSixVersionFormat::plusJarModFromJson(
- ProblemContainer & problems,
- const QJsonObject &libObj,
- const QString &filename,
- const QString &originalName
-) {
- LibraryPtr out(new Library());
- if (!libObj.contains("name"))
- {
- throw JSONValidationError(filename +
- "contains a jarmod that doesn't have a 'name' field");
- }
-
- // just make up something unique on the spot for the library name.
- auto uuid = QUuid::createUuid();
- QString id = uuid.toString().remove('{').remove('}');
- out->setRawName(GradleSpecifier("org.multimc.jarmods:" + id + ":1"));
-
- // filename override is the old name
- out->setFilename(libObj.value("name").toString());
-
- // it needs to be local, it is stored in the instance jarmods folder
- out->setHint("local");
-
- // read the original name if present - some versions did not set it
- // it is the original jar mod filename before it got renamed at the point of addition
- auto displayName = libObj.value("originalName").toString();
- if(displayName.isEmpty())
- {
- auto fixed = originalName;
- fixed.remove(" (jar mod)");
- out->setDisplayName(fixed);
- }
- else
- {
- out->setDisplayName(displayName);
- }
- return out;
-}
-
-LibraryPtr OneSixVersionFormat::jarModFromJson(ProblemContainer & problems, const QJsonObject& libObj, const QString& filename)
-{
- return libraryFromJson(problems, libObj, filename);
-}
-
-
-QJsonObject OneSixVersionFormat::jarModtoJson(Library *jarmod)
-{
- return libraryToJson(jarmod);
-}
-
-LibraryPtr OneSixVersionFormat::modFromJson(ProblemContainer & problems, const QJsonObject& libObj, const QString& filename)
-{
- return libraryFromJson(problems, libObj, filename);
-}
-
-QJsonObject OneSixVersionFormat::modtoJson(Library *jarmod)
-{
- return libraryToJson(jarmod);
-}
diff --git a/api/logic/minecraft/OneSixVersionFormat.h b/api/logic/minecraft/OneSixVersionFormat.h
deleted file mode 100644
index 1a091d88..00000000
--- a/api/logic/minecraft/OneSixVersionFormat.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma once
-
-#include <minecraft/VersionFile.h>
-#include <minecraft/PackProfile.h>
-#include <minecraft/Library.h>
-#include <QJsonDocument>
-#include <ProblemProvider.h>
-
-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);
-
- // libraries
- static LibraryPtr libraryFromJson(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename);
- static QJsonObject libraryToJson(Library *library);
-
- // DEPRECATED: old 'plus' jar mods generated by the application
- static LibraryPtr plusJarModFromJson(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename, const QString &originalName);
-
- // new jar mods derived from libraries
- static LibraryPtr jarModFromJson(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename);
- static QJsonObject jarModtoJson(Library * jarmod);
-
- // mods, also derived from libraries
- static LibraryPtr modFromJson(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename);
- static QJsonObject modtoJson(Library * jarmod);
-};
diff --git a/api/logic/minecraft/OpSys.cpp b/api/logic/minecraft/OpSys.cpp
deleted file mode 100644
index f6a4ed1c..00000000
--- a/api/logic/minecraft/OpSys.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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/api/logic/minecraft/OpSys.h b/api/logic/minecraft/OpSys.h
deleted file mode 100644
index 63c750b1..00000000
--- a/api/logic/minecraft/OpSys.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <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/api/logic/minecraft/PackProfile.cpp b/api/logic/minecraft/PackProfile.cpp
deleted file mode 100644
index f6918116..00000000
--- a/api/logic/minecraft/PackProfile.cpp
+++ /dev/null
@@ -1,1225 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <QFile>
-#include <QCryptographicHash>
-#include <Version.h>
-#include <QDir>
-#include <QJsonDocument>
-#include <QJsonArray>
-#include <QDebug>
-
-#include "Exception.h"
-#include <minecraft/OneSixVersionFormat.h>
-#include <FileSystem.h>
-#include <QSaveFile>
-#include <Env.h>
-#include <meta/Index.h>
-#include <minecraft/MinecraftInstance.h>
-#include <QUuid>
-#include <QTimer>
-#include <Json.h>
-
-#include "PackProfile.h"
-#include "PackProfile_p.h"
-#include "ComponentUpdateTask.h"
-
-PackProfile::PackProfile(MinecraftInstance * instance)
- : QAbstractListModel()
-{
- d.reset(new PackProfileData);
- d->m_instance = instance;
- d->m_saveTimer.setSingleShot(true);
- d->m_saveTimer.setInterval(5000);
- d->interactionDisabled = instance->isRunning();
- connect(d->m_instance, &BaseInstance::runningStatusChanged, this, &PackProfile::disableInteraction);
- connect(&d->m_saveTimer, &QTimer::timeout, this, &PackProfile::save_internal);
-}
-
-PackProfile::~PackProfile()
-{
- saveNow();
-}
-
-// BEGIN: component file format
-
-static const int currentComponentsFileVersion = 1;
-
-static QJsonObject componentToJsonV1(ComponentPtr component)
-{
- QJsonObject obj;
- // critical
- obj.insert("uid", component->m_uid);
- if(!component->m_version.isEmpty())
- {
- obj.insert("version", component->m_version);
- }
- if(component->m_dependencyOnly)
- {
- obj.insert("dependencyOnly", true);
- }
- if(component->m_important)
- {
- obj.insert("important", true);
- }
- if(component->m_disabled)
- {
- obj.insert("disabled", true);
- }
-
- // cached
- if(!component->m_cachedVersion.isEmpty())
- {
- obj.insert("cachedVersion", component->m_cachedVersion);
- }
- if(!component->m_cachedName.isEmpty())
- {
- obj.insert("cachedName", component->m_cachedName);
- }
- Meta::serializeRequires(obj, &component->m_cachedRequires, "cachedRequires");
- Meta::serializeRequires(obj, &component->m_cachedConflicts, "cachedConflicts");
- if(component->m_cachedVolatile)
- {
- obj.insert("cachedVolatile", true);
- }
- return obj;
-}
-
-static ComponentPtr componentFromJsonV1(PackProfile * parent, const QString & componentJsonPattern, const QJsonObject &obj)
-{
- // critical
- auto uid = Json::requireString(obj.value("uid"));
- auto filePath = componentJsonPattern.arg(uid);
- auto component = new Component(parent, uid);
- component->m_version = Json::ensureString(obj.value("version"));
- component->m_dependencyOnly = Json::ensureBoolean(obj.value("dependencyOnly"), false);
- component->m_important = Json::ensureBoolean(obj.value("important"), false);
-
- // cached
- // TODO @RESILIENCE: ignore invalid values/structure here?
- component->m_cachedVersion = Json::ensureString(obj.value("cachedVersion"));
- component->m_cachedName = Json::ensureString(obj.value("cachedName"));
- Meta::parseRequires(obj, &component->m_cachedRequires, "cachedRequires");
- Meta::parseRequires(obj, &component->m_cachedConflicts, "cachedConflicts");
- component->m_cachedVolatile = Json::ensureBoolean(obj.value("volatile"), false);
- bool disabled = Json::ensureBoolean(obj.value("disabled"), false);
- component->setEnabled(!disabled);
- return component;
-}
-
-// Save the given component container data to a file
-static bool savePackProfile(const QString & filename, const ComponentContainer & container)
-{
- QJsonObject obj;
- obj.insert("formatVersion", currentComponentsFileVersion);
- QJsonArray orderArray;
- for(auto component: container)
- {
- orderArray.append(componentToJsonV1(component));
- }
- obj.insert("components", orderArray);
- QSaveFile outFile(filename);
- if (!outFile.open(QFile::WriteOnly))
- {
- qCritical() << "Couldn't open" << outFile.fileName()
- << "for writing:" << outFile.errorString();
- return false;
- }
- auto data = QJsonDocument(obj).toJson(QJsonDocument::Indented);
- if(outFile.write(data) != data.size())
- {
- qCritical() << "Couldn't write all the data into" << outFile.fileName()
- << "because:" << outFile.errorString();
- return false;
- }
- if(!outFile.commit())
- {
- qCritical() << "Couldn't save" << outFile.fileName()
- << "because:" << outFile.errorString();
- }
- return true;
-}
-
-// Read the given file into component containers
-static bool loadPackProfile(PackProfile * parent, const QString & filename, const QString & componentJsonPattern, ComponentContainer & container)
-{
- QFile componentsFile(filename);
- if (!componentsFile.exists())
- {
- qWarning() << "Components file doesn't exist. This should never happen.";
- return false;
- }
- if (!componentsFile.open(QFile::ReadOnly))
- {
- qCritical() << "Couldn't open" << componentsFile.fileName()
- << " for reading:" << componentsFile.errorString();
- qWarning() << "Ignoring overriden order";
- return false;
- }
-
- // and it's valid JSON
- QJsonParseError error;
- QJsonDocument doc = QJsonDocument::fromJson(componentsFile.readAll(), &error);
- if (error.error != QJsonParseError::NoError)
- {
- qCritical() << "Couldn't parse" << componentsFile.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("formatVersion"));
- if (version != currentComponentsFileVersion)
- {
- throw JSONValidationError(QObject::tr("Invalid component file version, expected %1")
- .arg(currentComponentsFileVersion));
- }
- auto orderArray = Json::requireArray(obj.value("components"));
- for(auto item: orderArray)
- {
- auto obj = Json::requireObject(item, "Component must be an object.");
- container.append(componentFromJsonV1(parent, componentJsonPattern, obj));
- }
- }
- catch (const JSONValidationError &err)
- {
- qCritical() << "Couldn't parse" << componentsFile.fileName() << ": bad file format";
- container.clear();
- return false;
- }
- return true;
-}
-
-// END: component file format
-
-// BEGIN: save/load logic
-
-void PackProfile::saveNow()
-{
- if(saveIsScheduled())
- {
- d->m_saveTimer.stop();
- save_internal();
- }
-}
-
-bool PackProfile::saveIsScheduled() const
-{
- return d->dirty;
-}
-
-void PackProfile::buildingFromScratch()
-{
- d->loaded = true;
- d->dirty = true;
-}
-
-void PackProfile::scheduleSave()
-{
- if(!d->loaded)
- {
- qDebug() << "Component list should never save if it didn't successfully load, instance:" << d->m_instance->name();
- return;
- }
- if(!d->dirty)
- {
- d->dirty = true;
- qDebug() << "Component list save is scheduled for" << d->m_instance->name();
- }
- d->m_saveTimer.start();
-}
-
-QString PackProfile::componentsFilePath() const
-{
- return FS::PathCombine(d->m_instance->instanceRoot(), "mmc-pack.json");
-}
-
-QString PackProfile::patchesPattern() const
-{
- return FS::PathCombine(d->m_instance->instanceRoot(), "patches", "%1.json");
-}
-
-QString PackProfile::patchFilePathForUid(const QString& uid) const
-{
- return patchesPattern().arg(uid);
-}
-
-void PackProfile::save_internal()
-{
- qDebug() << "Component list save performed now for" << d->m_instance->name();
- auto filename = componentsFilePath();
- savePackProfile(filename, d->components);
- d->dirty = false;
-}
-
-bool PackProfile::load()
-{
- auto filename = componentsFilePath();
- QFile componentsFile(filename);
-
- // migrate old config to new one, if needed
- if(!componentsFile.exists())
- {
- if(!migratePreComponentConfig())
- {
- // FIXME: the user should be notified...
- qCritical() << "Failed to convert old pre-component config for instance" << d->m_instance->name();
- return false;
- }
- }
-
- // load the new component list and swap it with the current one...
- ComponentContainer newComponents;
- if(!loadPackProfile(this, filename, patchesPattern(), newComponents))
- {
- qCritical() << "Failed to load the component config for instance" << d->m_instance->name();
- return false;
- }
- else
- {
- // FIXME: actually use fine-grained updates, not this...
- beginResetModel();
- // disconnect all the old components
- for(auto component: d->components)
- {
- disconnect(component.get(), &Component::dataChanged, this, &PackProfile::componentDataChanged);
- }
- d->components.clear();
- d->componentIndex.clear();
- for(auto component: newComponents)
- {
- if(d->componentIndex.contains(component->m_uid))
- {
- qWarning() << "Ignoring duplicate component entry" << component->m_uid;
- continue;
- }
- connect(component.get(), &Component::dataChanged, this, &PackProfile::componentDataChanged);
- d->components.append(component);
- d->componentIndex[component->m_uid] = component;
- }
- endResetModel();
- d->loaded = true;
- return true;
- }
-}
-
-void PackProfile::reload(Net::Mode netmode)
-{
- // Do not reload when the update/resolve task is running. It is in control.
- if(d->m_updateTask)
- {
- return;
- }
-
- // flush any scheduled saves to not lose state
- saveNow();
-
- // FIXME: differentiate when a reapply is required by propagating state from components
- invalidateLaunchProfile();
-
- if(load())
- {
- resolve(netmode);
- }
-}
-
-shared_qobject_ptr<Task> PackProfile::getCurrentTask()
-{
- return d->m_updateTask;
-}
-
-void PackProfile::resolve(Net::Mode netmode)
-{
- auto updateTask = new ComponentUpdateTask(ComponentUpdateTask::Mode::Resolution, netmode, this);
- d->m_updateTask.reset(updateTask);
- connect(updateTask, &ComponentUpdateTask::succeeded, this, &PackProfile::updateSucceeded);
- connect(updateTask, &ComponentUpdateTask::failed, this, &PackProfile::updateFailed);
- d->m_updateTask->start();
-}
-
-
-void PackProfile::updateSucceeded()
-{
- qDebug() << "Component list update/resolve task succeeded for" << d->m_instance->name();
- d->m_updateTask.reset();
- invalidateLaunchProfile();
-}
-
-void PackProfile::updateFailed(const QString& error)
-{
- qDebug() << "Component list update/resolve task failed for" << d->m_instance->name() << "Reason:" << error;
- d->m_updateTask.reset();
- invalidateLaunchProfile();
-}
-
-// NOTE this is really old stuff, and only needs to be used when loading the old hardcoded component-unaware format (loadPreComponentConfig).
-static void upgradeDeprecatedFiles(QString root, QString instanceName)
-{
- auto versionJsonPath = FS::PathCombine(root, "version.json");
- auto customJsonPath = FS::PathCombine(root, "custom.json");
- auto mcJson = FS::PathCombine(root, "patches" , "net.minecraft.json");
-
- QString sourceFile;
- QString renameFile;
-
- // convert old crap.
- if(QFile::exists(customJsonPath))
- {
- sourceFile = customJsonPath;
- renameFile = versionJsonPath;
- }
- else if(QFile::exists(versionJsonPath))
- {
- sourceFile = versionJsonPath;
- }
- if(!sourceFile.isEmpty() && !QFile::exists(mcJson))
- {
- if(!FS::ensureFilePathExists(mcJson))
- {
- qWarning() << "Couldn't create patches folder for" << instanceName;
- return;
- }
- if(!renameFile.isEmpty() && QFile::exists(renameFile))
- {
- if(!QFile::rename(renameFile, renameFile + ".old"))
- {
- qWarning() << "Couldn't rename" << renameFile << "to" << renameFile + ".old" << "in" << instanceName;
- return;
- }
- }
- auto file = ProfileUtils::parseJsonFile(QFileInfo(sourceFile), false);
- ProfileUtils::removeLwjglFromPatch(file);
- file->uid = "net.minecraft";
- file->version = file->minecraftVersion;
- file->name = "Minecraft";
-
- Meta::Require needsLwjgl;
- needsLwjgl.uid = "org.lwjgl";
- file->requires.insert(needsLwjgl);
-
- if(!ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), mcJson))
- {
- return;
- }
- if(!QFile::rename(sourceFile, sourceFile + ".old"))
- {
- qWarning() << "Couldn't rename" << sourceFile << "to" << sourceFile + ".old" << "in" << instanceName;
- return;
- }
- }
-}
-
-/*
- * Migrate old layout to the component based one...
- * - Part of the version information is taken from `instance.cfg` (fed to this class from outside).
- * - Part is taken from the old order.json file.
- * - Part is loaded from loose json files in the instance's `patches` directory.
- */
-bool PackProfile::migratePreComponentConfig()
-{
- // upgrade the very old files from the beginnings of MultiMC 5
- upgradeDeprecatedFiles(d->m_instance->instanceRoot(), d->m_instance->name());
-
- QList<ComponentPtr> components;
- QSet<QString> loaded;
-
- auto addBuiltinPatch = [&](const QString &uid, bool asDependency, const QString & emptyVersion, const Meta::Require & req, const Meta::Require & conflict)
- {
- auto jsonFilePath = FS::PathCombine(d->m_instance->instanceRoot(), "patches" , uid + ".json");
- auto intendedVersion = d->getOldConfigVersion(uid);
- // load up the base minecraft patch
- ComponentPtr component;
- if(QFile::exists(jsonFilePath))
- {
- if(intendedVersion.isEmpty())
- {
- intendedVersion = emptyVersion;
- }
- auto file = ProfileUtils::parseJsonFile(QFileInfo(jsonFilePath), false);
- // fix uid
- file->uid = uid;
- // if version is missing, add it from the outside.
- if(file->version.isEmpty())
- {
- file->version = intendedVersion;
- }
- // if this is a dependency (LWJGL), mark it also as volatile
- if(asDependency)
- {
- file->m_volatile = true;
- }
- // insert requirements if needed
- if(!req.uid.isEmpty())
- {
- file->requires.insert(req);
- }
- // insert conflicts if needed
- if(!conflict.uid.isEmpty())
- {
- file->conflicts.insert(conflict);
- }
- // FIXME: @QUALITY do not ignore return value
- ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), jsonFilePath);
- component = new Component(this, uid, file);
- component->m_version = intendedVersion;
- }
- else if(!intendedVersion.isEmpty())
- {
- auto metaVersion = ENV.metadataIndex()->get(uid, intendedVersion);
- component = new Component(this, metaVersion);
- }
- else
- {
- return;
- }
- component->m_dependencyOnly = asDependency;
- component->m_important = !asDependency;
- components.append(component);
- };
- // TODO: insert depends and conflicts here if these are customized files...
- Meta::Require reqLwjgl;
- reqLwjgl.uid = "org.lwjgl";
- reqLwjgl.suggests = "2.9.1";
- Meta::Require conflictLwjgl3;
- conflictLwjgl3.uid = "org.lwjgl3";
- Meta::Require nullReq;
- addBuiltinPatch("org.lwjgl", true, "2.9.1", nullReq, conflictLwjgl3);
- addBuiltinPatch("net.minecraft", false, QString(), reqLwjgl, nullReq);
-
- // first, collect all other file-based patches and load them
- QMap<QString, ComponentPtr> loadedComponents;
- QDir patchesDir(FS::PathCombine(d->m_instance->instanceRoot(),"patches"));
- for (auto info : patchesDir.entryInfoList(QStringList() << "*.json", QDir::Files))
- {
- // parse the file
- qDebug() << "Reading" << info.fileName();
- auto file = ProfileUtils::parseJsonFile(info, true);
-
- // correct missing or wrong uid based on the file name
- QString uid = info.completeBaseName();
-
- // ignore builtins, they've been handled already
- if (uid == "net.minecraft")
- continue;
- if (uid == "org.lwjgl")
- continue;
-
- // handle horrible corner cases
- if(uid.isEmpty())
- {
- // if you have a file named '.json', make it just go away.
- // FIXME: @QUALITY do not ignore return value
- QFile::remove(info.absoluteFilePath());
- continue;
- }
- file->uid = uid;
- // FIXME: @QUALITY do not ignore return value
- ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), info.absoluteFilePath());
-
- auto component = new Component(this, file->uid, file);
- auto version = d->getOldConfigVersion(file->uid);
- if(!version.isEmpty())
- {
- component->m_version = version;
- }
- loadedComponents[file->uid] = component;
- }
- // try to load the other 'hardcoded' patches (forge, liteloader), if they weren't loaded from files
- auto loadSpecial = [&](const QString & uid, int order)
- {
- auto patchVersion = d->getOldConfigVersion(uid);
- if(!patchVersion.isEmpty() && !loadedComponents.contains(uid))
- {
- auto patch = new Component(this, ENV.metadataIndex()->get(uid, patchVersion));
- patch->setOrder(order);
- loadedComponents[uid] = patch;
- }
- };
- loadSpecial("net.minecraftforge", 5);
- loadSpecial("com.mumfrey.liteloader", 10);
-
- // load the old order.json file, if present
- ProfileUtils::PatchOrder userOrder;
- ProfileUtils::readOverrideOrders(FS::PathCombine(d->m_instance->instanceRoot(), "order.json"), userOrder);
-
- // now add all the patches by user sort order
- for (auto uid : userOrder)
- {
- // ignore builtins
- if (uid == "net.minecraft")
- continue;
- if (uid == "org.lwjgl")
- continue;
- // ordering has a patch that is gone?
- if(!loadedComponents.contains(uid))
- {
- continue;
- }
- components.append(loadedComponents.take(uid));
- }
-
- // is there anything left to sort? - this is used when there are leftover components that aren't part of the order.json
- if(!loadedComponents.isEmpty())
- {
- // inserting into multimap by order number as key sorts the patches and detects duplicates
- QMultiMap<int, ComponentPtr> files;
- auto iter = loadedComponents.begin();
- while(iter != loadedComponents.end())
- {
- files.insert((*iter)->getOrder(), *iter);
- iter++;
- }
-
- // then just extract the patches and put them in the list
- for (auto order : files.keys())
- {
- const auto &values = files.values(order);
- for(auto &value: values)
- {
- // TODO: put back the insertion of problem messages here, so the user knows about the id duplication
- components.append(value);
- }
- }
- }
- // new we have a complete list of components...
- return savePackProfile(componentsFilePath(), components);
-}
-
-// END: save/load
-
-void PackProfile::appendComponent(ComponentPtr component)
-{
- insertComponent(d->components.size(), component);
-}
-
-void PackProfile::insertComponent(size_t index, ComponentPtr component)
-{
- auto id = component->getID();
- if(id.isEmpty())
- {
- qWarning() << "Attempt to add a component with empty ID!";
- return;
- }
- if(d->componentIndex.contains(id))
- {
- qWarning() << "Attempt to add a component that is already present!";
- return;
- }
- beginInsertRows(QModelIndex(), index, index);
- d->components.insert(index, component);
- d->componentIndex[id] = component;
- endInsertRows();
- connect(component.get(), &Component::dataChanged, this, &PackProfile::componentDataChanged);
- scheduleSave();
-}
-
-void PackProfile::componentDataChanged()
-{
- auto objPtr = qobject_cast<Component *>(sender());
- if(!objPtr)
- {
- qWarning() << "PackProfile got dataChenged signal from a non-Component!";
- return;
- }
- if(objPtr->getID() == "net.minecraft") {
- emit minecraftChanged();
- }
- // figure out which one is it... in a seriously dumb way.
- int index = 0;
- for (auto component: d->components)
- {
- if(component.get() == objPtr)
- {
- emit dataChanged(createIndex(index, 0), createIndex(index, columnCount(QModelIndex()) - 1));
- scheduleSave();
- return;
- }
- index++;
- }
- qWarning() << "PackProfile got dataChenged signal from a Component which does not belong to it!";
-}
-
-bool PackProfile::remove(const int index)
-{
- auto patch = getComponent(index);
- if (!patch->isRemovable())
- {
- qWarning() << "Patch" << patch->getID() << "is non-removable";
- return false;
- }
-
- if(!removeComponent_internal(patch))
- {
- qCritical() << "Patch" << patch->getID() << "could not be removed";
- return false;
- }
-
- beginRemoveRows(QModelIndex(), index, index);
- d->components.removeAt(index);
- d->componentIndex.remove(patch->getID());
- endRemoveRows();
- invalidateLaunchProfile();
- scheduleSave();
- return true;
-}
-
-bool PackProfile::remove(const QString id)
-{
- int i = 0;
- for (auto patch : d->components)
- {
- if (patch->getID() == id)
- {
- return remove(i);
- }
- i++;
- }
- return false;
-}
-
-bool PackProfile::customize(int index)
-{
- auto patch = getComponent(index);
- if (!patch->isCustomizable())
- {
- qDebug() << "Patch" << patch->getID() << "is not customizable";
- return false;
- }
- if(!patch->customize())
- {
- qCritical() << "Patch" << patch->getID() << "could not be customized";
- return false;
- }
- invalidateLaunchProfile();
- scheduleSave();
- return true;
-}
-
-bool PackProfile::revertToBase(int index)
-{
- auto patch = getComponent(index);
- if (!patch->isRevertible())
- {
- qDebug() << "Patch" << patch->getID() << "is not revertible";
- return false;
- }
- if(!patch->revert())
- {
- qCritical() << "Patch" << patch->getID() << "could not be reverted";
- return false;
- }
- invalidateLaunchProfile();
- scheduleSave();
- return true;
-}
-
-Component * PackProfile::getComponent(const QString &id)
-{
- auto iter = d->componentIndex.find(id);
- if (iter == d->componentIndex.end())
- {
- return nullptr;
- }
- return (*iter).get();
-}
-
-Component * PackProfile::getComponent(int index)
-{
- if(index < 0 || index >= d->components.size())
- {
- return nullptr;
- }
- return d->components[index].get();
-}
-
-QVariant PackProfile::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid())
- return QVariant();
-
- int row = index.row();
- int column = index.column();
-
- if (row < 0 || row >= d->components.size())
- return QVariant();
-
- auto patch = d->components.at(row);
-
- switch (role)
- {
- case Qt::CheckStateRole:
- {
- switch (column)
- {
- case NameColumn: {
- return patch->isEnabled() ? Qt::Checked : Qt::Unchecked;
- }
- default:
- return QVariant();
- }
- }
- case Qt::DisplayRole:
- {
- switch (column)
- {
- case NameColumn:
- return patch->getName();
- case VersionColumn:
- {
- if(patch->isCustom())
- {
- return QString("%1 (Custom)").arg(patch->getVersion());
- }
- else
- {
- return patch->getVersion();
- }
- }
- default:
- return QVariant();
- }
- }
- case Qt::DecorationRole:
- {
- switch(column)
- {
- case NameColumn:
- {
- auto severity = patch->getProblemSeverity();
- switch (severity)
- {
- case ProblemSeverity::Warning:
- return "warning";
- case ProblemSeverity::Error:
- return "error";
- default:
- return QVariant();
- }
- }
- default:
- {
- return QVariant();
- }
- }
- }
- }
- return QVariant();
-}
-
-bool PackProfile::setData(const QModelIndex& index, const QVariant& value, int role)
-{
- if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index))
- {
- return false;
- }
-
- if (role == Qt::CheckStateRole)
- {
- auto component = d->components[index.row()];
- if (component->setEnabled(!component->isEnabled()))
- {
- return true;
- }
- }
- return false;
-}
-
-QVariant PackProfile::headerData(int section, Qt::Orientation orientation, int role) const
-{
- if (orientation == Qt::Horizontal)
- {
- if (role == Qt::DisplayRole)
- {
- switch (section)
- {
- case NameColumn:
- return tr("Name");
- case VersionColumn:
- return tr("Version");
- default:
- return QVariant();
- }
- }
- }
- return QVariant();
-}
-
-// FIXME: zero precision mess
-Qt::ItemFlags PackProfile::flags(const QModelIndex &index) const
-{
- if (!index.isValid()) {
- return Qt::NoItemFlags;
- }
-
- Qt::ItemFlags outFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
-
- int row = index.row();
-
- if (row < 0 || row >= d->components.size()) {
- return Qt::NoItemFlags;
- }
-
- auto patch = d->components.at(row);
- // TODO: this will need fine-tuning later...
- if(patch->canBeDisabled() && !d->interactionDisabled)
- {
- outFlags |= Qt::ItemIsUserCheckable;
- }
- return outFlags;
-}
-
-int PackProfile::rowCount(const QModelIndex &parent) const
-{
- return d->components.size();
-}
-
-int PackProfile::columnCount(const QModelIndex &parent) const
-{
- return NUM_COLUMNS;
-}
-
-void PackProfile::move(const int index, const MoveDirection direction)
-{
- int theirIndex;
- if (direction == MoveUp)
- {
- theirIndex = index - 1;
- }
- else
- {
- theirIndex = index + 1;
- }
-
- if (index < 0 || index >= d->components.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 = getComponent(index);
- auto to = getComponent(theirIndex);
-
- if (!from || !to || !to->isMoveable() || !from->isMoveable())
- {
- return;
- }
- beginMoveRows(QModelIndex(), index, index, QModelIndex(), togap);
- d->components.swap(index, theirIndex);
- endMoveRows();
- invalidateLaunchProfile();
- scheduleSave();
-}
-
-void PackProfile::invalidateLaunchProfile()
-{
- d->m_profile.reset();
-}
-
-void PackProfile::installJarMods(QStringList selectedFiles)
-{
- installJarMods_internal(selectedFiles);
-}
-
-void PackProfile::installCustomJar(QString selectedFile)
-{
- installCustomJar_internal(selectedFile);
-}
-
-bool PackProfile::installEmpty(const QString& uid, const QString& name)
-{
- QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches");
- if(!FS::ensureFolderPathExists(patchDir))
- {
- return false;
- }
- auto f = std::make_shared<VersionFile>();
- f->name = name;
- f->uid = uid;
- f->version = "1";
- QString patchFileName = FS::PathCombine(patchDir, uid + ".json");
- QFile file(patchFileName);
- if (!file.open(QFile::WriteOnly))
- {
- qCritical() << "Error opening" << file.fileName()
- << "for reading:" << file.errorString();
- return false;
- }
- file.write(OneSixVersionFormat::versionFileToJson(f).toJson());
- file.close();
-
- appendComponent(new Component(this, f->uid, f));
- scheduleSave();
- invalidateLaunchProfile();
- return true;
-}
-
-bool PackProfile::removeComponent_internal(ComponentPtr 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;
- }
- }
-
- // FIXME: we need a generic way of removing local resources, not just jar mods...
- auto preRemoveJarMod = [&](LibraryPtr jarMod) -> bool
- {
- if (!jarMod->isLocal())
- {
- return true;
- }
- QStringList jar, temp1, temp2, temp3;
- jarMod->getApplicableFiles(currentSystem, jar, temp1, temp2, temp3, d->m_instance->jarmodsPath().absolutePath());
- QFileInfo finfo (jar[0]);
- if(finfo.exists())
- {
- QFile jarModFile(jar[0]);
- if(!jarModFile.remove())
- {
- qCritical() << "File" << jar[0] << "could not be removed because:" << jarModFile.errorString();
- return false;
- }
- return true;
- }
- return true;
- };
-
- auto vFile = patch->getVersionFile();
- if(vFile)
- {
- auto &jarMods = vFile->jarMods;
- for(auto &jarmod: jarMods)
- {
- ok &= preRemoveJarMod(jarmod);
- }
- }
- return ok;
-}
-
-bool PackProfile::installJarMods_internal(QStringList filepaths)
-{
- QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches");
- if(!FS::ensureFolderPathExists(patchDir))
- {
- return false;
- }
-
- if (!FS::ensureFolderPathExists(d->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(d->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<Library>();
- jarMod->setRawName(GradleSpecifier("org.multimc.jarmods:" + id + ":1"));
- jarMod->setFilename(target_filename);
- jarMod->setDisplayName(sourceInfo.completeBaseName());
- jarMod->setHint("local");
- f->jarMods.append(jarMod);
- f->name = target_name;
- f->uid = target_id;
- QString patchFileName = FS::PathCombine(patchDir, target_id + ".json");
-
- QFile file(patchFileName);
- if (!file.open(QFile::WriteOnly))
- {
- qCritical() << "Error opening" << file.fileName()
- << "for reading:" << file.errorString();
- return false;
- }
- file.write(OneSixVersionFormat::versionFileToJson(f).toJson());
- file.close();
-
- appendComponent(new Component(this, f->uid, f));
- }
- scheduleSave();
- invalidateLaunchProfile();
- return true;
-}
-
-bool PackProfile::installCustomJar_internal(QString filepath)
-{
- QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches");
- if(!FS::ensureFolderPathExists(patchDir))
- {
- return false;
- }
-
- QString libDir = d->m_instance->getLocalLibraryPath();
- if (!FS::ensureFolderPathExists(libDir))
- {
- return false;
- }
-
- auto specifier = GradleSpecifier("org.multimc:customjar:1");
- QFileInfo sourceInfo(filepath);
- QString target_filename = specifier.getFileName();
- QString target_id = specifier.artifactId();
- QString target_name = sourceInfo.completeBaseName() + " (custom jar)";
- QString finalPath = FS::PathCombine(libDir, target_filename);
-
- QFileInfo jarInfo(finalPath);
- if (jarInfo.exists())
- {
- if(!QFile::remove(finalPath))
- {
- return false;
- }
- }
- if (!QFile::copy(filepath, finalPath))
- {
- return false;
- }
-
- auto f = std::make_shared<VersionFile>();
- auto jarMod = std::make_shared<Library>();
- jarMod->setRawName(specifier);
- jarMod->setDisplayName(sourceInfo.completeBaseName());
- jarMod->setHint("local");
- f->mainJar = jarMod;
- f->name = target_name;
- f->uid = target_id;
- QString patchFileName = FS::PathCombine(patchDir, target_id + ".json");
-
- QFile file(patchFileName);
- if (!file.open(QFile::WriteOnly))
- {
- qCritical() << "Error opening" << file.fileName()
- << "for reading:" << file.errorString();
- return false;
- }
- file.write(OneSixVersionFormat::versionFileToJson(f).toJson());
- file.close();
-
- appendComponent(new Component(this, f->uid, f));
-
- scheduleSave();
- invalidateLaunchProfile();
- return true;
-}
-
-std::shared_ptr<LaunchProfile> PackProfile::getProfile() const
-{
- if(!d->m_profile)
- {
- try
- {
- auto profile = std::make_shared<LaunchProfile>();
- for(auto file: d->components)
- {
- qDebug() << "Applying" << file->getID() << (file->getProblemSeverity() == ProblemSeverity::Error ? "ERROR" : "GOOD");
- file->applyTo(profile.get());
- }
- d->m_profile = profile;
- }
- catch (const Exception &error)
- {
- qWarning() << "Couldn't apply profile patches because: " << error.cause();
- }
- }
- return d->m_profile;
-}
-
-void PackProfile::setOldConfigVersion(const QString& uid, const QString& version)
-{
- if(version.isEmpty())
- {
- return;
- }
- d->m_oldConfigVersions[uid] = version;
-}
-
-bool PackProfile::setComponentVersion(const QString& uid, const QString& version, bool important)
-{
- auto iter = d->componentIndex.find(uid);
- if(iter != d->componentIndex.end())
- {
- ComponentPtr component = *iter;
- // set existing
- if(component->revert())
- {
- component->setVersion(version);
- component->setImportant(important);
- return true;
- }
- return false;
- }
- else
- {
- // add new
- auto component = new Component(this, uid);
- component->m_version = version;
- component->m_important = important;
- appendComponent(component);
- return true;
- }
-}
-
-QString PackProfile::getComponentVersion(const QString& uid) const
-{
- const auto iter = d->componentIndex.find(uid);
- if (iter != d->componentIndex.end())
- {
- return (*iter)->getVersion();
- }
- return QString();
-}
-
-void PackProfile::disableInteraction(bool disable)
-{
- if(d->interactionDisabled != disable) {
- d->interactionDisabled = disable;
- auto size = d->components.size();
- if(size) {
- emit dataChanged(index(0), index(size - 1));
- }
- }
-}
diff --git a/api/logic/minecraft/PackProfile.h b/api/logic/minecraft/PackProfile.h
deleted file mode 100644
index e55e6a58..00000000
--- a/api/logic/minecraft/PackProfile.h
+++ /dev/null
@@ -1,152 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QAbstractListModel>
-
-#include <QString>
-#include <QList>
-#include <memory>
-
-#include "Library.h"
-#include "LaunchProfile.h"
-#include "Component.h"
-#include "ProfileUtils.h"
-#include "BaseVersion.h"
-#include "MojangDownloadInfo.h"
-#include "multimc_logic_export.h"
-#include "net/Mode.h"
-
-class MinecraftInstance;
-struct PackProfileData;
-class ComponentUpdateTask;
-
-class MULTIMC_LOGIC_EXPORT PackProfile : public QAbstractListModel
-{
- Q_OBJECT
- friend ComponentUpdateTask;
-public:
- enum Columns
- {
- NameColumn = 0,
- VersionColumn,
- NUM_COLUMNS
- };
-
- explicit PackProfile(MinecraftInstance * instance);
- virtual ~PackProfile();
-
- virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) 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;
-
- /// call this to explicitly mark the component list as loaded - this is used to build a new component list from scratch.
- void buildingFromScratch();
-
- /// install more jar mods
- void installJarMods(QStringList selectedFiles);
-
- /// install a jar/zip as a replacement for the main jar
- void installCustomJar(QString selectedFile);
-
- enum MoveDirection { MoveUp, MoveDown };
- /// move component file # up or down the list
- void move(const int index, const MoveDirection direction);
-
- /// remove component file # - including files/records
- bool remove(const int index);
-
- /// remove component file by id - including files/records
- bool remove(const QString id);
-
- bool customize(int index);
-
- bool revertToBase(int index);
-
- /// reload the list, reload all components, resolve dependencies
- void reload(Net::Mode netmode);
-
- // reload all components, resolve dependencies
- void resolve(Net::Mode netmode);
-
- /// get current running task...
- shared_qobject_ptr<Task> getCurrentTask();
-
- std::shared_ptr<LaunchProfile> getProfile() const;
-
- // NOTE: used ONLY by MinecraftInstance to provide legacy version mappings from instance config
- void setOldConfigVersion(const QString &uid, const QString &version);
-
- QString getComponentVersion(const QString &uid) const;
-
- bool setComponentVersion(const QString &uid, const QString &version, bool important = false);
-
- bool installEmpty(const QString &uid, const QString &name);
-
- QString patchFilePathForUid(const QString &uid) const;
-
- /// if there is a save scheduled, do it now.
- void saveNow();
-
-signals:
- void minecraftChanged();
-
-public:
- /// get the profile component by id
- Component * getComponent(const QString &id);
-
- /// get the profile component by index
- Component * getComponent(int index);
-
- /// Add the component to the internal list of patches
- // todo(merged): is this the best approach
- void appendComponent(ComponentPtr component);
-
-private:
- void scheduleSave();
- bool saveIsScheduled() const;
-
- /// apply the component patches. Catches all the errors and returns true/false for success/failure
- void invalidateLaunchProfile();
-
- /// insert component so that its index is ideally the specified one (returns real index)
- void insertComponent(size_t index, ComponentPtr component);
-
- QString componentsFilePath() const;
- QString patchesPattern() const;
-
-private slots:
- void save_internal();
- void updateSucceeded();
- void updateFailed(const QString & error);
- void componentDataChanged();
- void disableInteraction(bool disable);
-
-private:
- bool load();
- bool installJarMods_internal(QStringList filepaths);
- bool installCustomJar_internal(QString filepath);
- bool removeComponent_internal(ComponentPtr patch);
-
- bool migratePreComponentConfig();
-
-private: /* data */
-
- std::unique_ptr<PackProfileData> d;
-};
diff --git a/api/logic/minecraft/PackProfile_p.h b/api/logic/minecraft/PackProfile_p.h
deleted file mode 100644
index 6cd2a4e5..00000000
--- a/api/logic/minecraft/PackProfile_p.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#pragma once
-
-#include "Component.h"
-#include <map>
-#include <QTimer>
-#include <QList>
-#include <QMap>
-
-class MinecraftInstance;
-using ComponentContainer = QList<ComponentPtr>;
-using ComponentIndex = QMap<QString, ComponentPtr>;
-
-struct PackProfileData
-{
- // the instance this belongs to
- MinecraftInstance *m_instance;
-
- // the launch profile (volatile, temporary thing created on demand)
- std::shared_ptr<LaunchProfile> m_profile;
-
- // version information migrated from instance.cfg file. Single use on migration!
- std::map<QString, QString> m_oldConfigVersions;
- QString getOldConfigVersion(const QString& uid) const
- {
- const auto iter = m_oldConfigVersions.find(uid);
- if(iter != m_oldConfigVersions.cend())
- {
- return (*iter).second;
- }
- return QString();
- }
-
- // persistent list of components and related machinery
- ComponentContainer components;
- ComponentIndex componentIndex;
- bool dirty = false;
- QTimer m_saveTimer;
- shared_qobject_ptr<Task> m_updateTask;
- bool loaded = false;
- bool interactionDisabled = true;
-};
-
diff --git a/api/logic/minecraft/ParseUtils.cpp b/api/logic/minecraft/ParseUtils.cpp
deleted file mode 100644
index c9640e77..00000000
--- a/api/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/api/logic/minecraft/ParseUtils.h b/api/logic/minecraft/ParseUtils.h
deleted file mode 100644
index 2b367a10..00000000
--- a/api/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/api/logic/minecraft/ParseUtils_test.cpp b/api/logic/minecraft/ParseUtils_test.cpp
deleted file mode 100644
index fcc137e5..00000000
--- a/api/logic/minecraft/ParseUtils_test.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-#include <QTest>
-#include "TestUtil.h"
-
-#include "minecraft/ParseUtils.h"
-
-class ParseUtilsTest : public QObject
-{
- Q_OBJECT
-private
-slots:
- void test_Through_data()
- {
- QTest::addColumn<QString>("timestamp");
- const char * timestamps[] =
- {
- "2016-02-29T13:49:54+01:00",
- "2016-02-26T15:21:11+00:01",
- "2016-02-24T15:52:36+01:13",
- "2016-02-18T17:41:00+00:00",
- "2016-02-17T15:23:19+00:00",
- "2016-02-16T15:22:39+09:22",
- "2016-02-10T15:06:41+00:00",
- "2016-02-04T15:28:02-05:33"
- };
- for(unsigned i = 0; i < (sizeof(timestamps) / sizeof(const char *)); i++)
- {
- QTest::newRow(timestamps[i]) << QString(timestamps[i]);
- }
- }
- void test_Through()
- {
- QFETCH(QString, timestamp);
-
- auto time_parsed = timeFromS3Time(timestamp);
- auto time_serialized = timeToS3Time(time_parsed);
-
- QCOMPARE(time_serialized, timestamp);
- }
-
-};
-
-QTEST_GUILESS_MAIN(ParseUtilsTest)
-
-#include "ParseUtils_test.moc"
-
diff --git a/api/logic/minecraft/ProfileUtils.cpp b/api/logic/minecraft/ProfileUtils.cpp
deleted file mode 100644
index 8ca24cc8..00000000
--- a/api/logic/minecraft/ProfileUtils.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-#include "ProfileUtils.h"
-#include "minecraft/VersionFilterData.h"
-#include "minecraft/OneSixVersionFormat.h"
-#include "Json.h"
-#include <QDebug>
-
-#include <QJsonDocument>
-#include <QJsonArray>
-#include <QRegularExpression>
-#include <QSaveFile>
-
-namespace ProfileUtils
-{
-
-static const int currentOrderFileVersion = 1;
-
-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 (const 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->uid = outError->name = fileId;
- // outError->filename = filepath;
- outError->addProblem(ProblemSeverity::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 (const 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);
-}
-
-bool saveJsonFile(const QJsonDocument doc, const QString & filename)
-{
- auto data = doc.toJson();
- QSaveFile jsonFile(filename);
- if(!jsonFile.open(QIODevice::WriteOnly))
- {
- jsonFile.cancelWriting();
- qWarning() << "Couldn't open" << filename << "for writing";
- return false;
- }
- jsonFile.write(data);
- if(!jsonFile.commit())
- {
- qWarning() << "Couldn't save" << filename;
- return false;
- }
- return true;
-}
-
-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/api/logic/minecraft/ProfileUtils.h b/api/logic/minecraft/ProfileUtils.h
deleted file mode 100644
index 351c36cb..00000000
--- a/api/logic/minecraft/ProfileUtils.h
+++ /dev/null
@@ -1,28 +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);
-
-/// Save a JSON file (in any format)
-bool saveJsonFile(const QJsonDocument doc, const QString & filename);
-
-/// 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/api/logic/minecraft/Rule.cpp b/api/logic/minecraft/Rule.cpp
deleted file mode 100644
index af2861e3..00000000
--- a/api/logic/minecraft/Rule.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <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/api/logic/minecraft/Rule.h b/api/logic/minecraft/Rule.h
deleted file mode 100644
index 7aa34d96..00000000
--- a/api/logic/minecraft/Rule.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <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/api/logic/minecraft/VersionFile.cpp b/api/logic/minecraft/VersionFile.cpp
deleted file mode 100644
index d0a1a507..00000000
--- a/api/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/PackProfile.h"
-#include "ParseUtils.h"
-
-#include <Version.h>
-
-static bool isMinecraftVersion(const QString &uid)
-{
- return uid == "net.minecraft";
-}
-
-void VersionFile::applyTo(LaunchProfile *profile)
-{
- // Only real Minecraft can set those. Don't let anything override them.
- if (isMinecraftVersion(uid))
- {
- profile->applyMinecraftVersion(minecraftVersion);
- profile->applyMinecraftVersionType(type);
- // HACK: ignore assets from other version files than Minecraft
- // workaround for stupid assets issue caused by amazon:
- // https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/
- profile->applyMinecraftAssets(mojangAssetIndex);
- }
-
- profile->applyMainJar(mainJar);
- profile->applyMainClass(mainClass);
- profile->applyAppletClass(appletClass);
- profile->applyMinecraftArguments(minecraftArguments);
- profile->applyTweakers(addTweakers);
- profile->applyJarMods(jarMods);
- profile->applyMods(mods);
- profile->applyTraits(traits);
-
- for (auto library : libraries)
- {
- profile->applyLibrary(library);
- }
- for (auto mavenFile : mavenFiles)
- {
- profile->applyMavenFile(mavenFile);
- }
- profile->applyProblemSeverity(getProblemSeverity());
-}
-
-/*
- auto theirVersion = profile->getMinecraftVersion();
- if (!theirVersion.isNull() && !dependsOnMinecraftVersion.isNull())
- {
- if (QRegExp(dependsOnMinecraftVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(theirVersion) == -1)
- {
- throw MinecraftVersionMismatch(uid, dependsOnMinecraftVersion, theirVersion);
- }
- }
-*/
diff --git a/api/logic/minecraft/VersionFile.h b/api/logic/minecraft/VersionFile.h
deleted file mode 100644
index b79fcd4f..00000000
--- a/api/logic/minecraft/VersionFile.h
+++ /dev/null
@@ -1,114 +0,0 @@
-#pragma once
-
-#include <QString>
-#include <QStringList>
-#include <QDateTime>
-#include <QSet>
-
-#include <memory>
-#include "minecraft/OpSys.h"
-#include "minecraft/Rule.h"
-#include "ProblemProvider.h"
-#include "Library.h"
-#include <meta/JsonFormat.h>
-
-class PackProfile;
-class VersionFile;
-class LaunchProfile;
-struct MojangDownloadInfo;
-struct MojangAssetIndexInfo;
-
-using VersionFilePtr = std::shared_ptr<VersionFile>;
-class VersionFile : public ProblemContainer
-{
- friend class MojangVersionFormat;
- friend class OneSixVersionFormat;
-public: /* methods */
- void applyTo(LaunchProfile* profile);
-
-public: /* data */
- /// MultiMC: order hint for this version file if no explicit order is set
- int order = 0;
-
- /// MultiMC: human readable name of this package
- QString name;
-
- /// MultiMC: package ID of this package
- QString uid;
-
- /// MultiMC: version of this package
- QString version;
-
- /// MultiMC: DEPRECATED dependency on a Minecraft version
- QString dependsOnMinecraftVersion;
-
- /// Mojang: DEPRECATED used to version the Mojang version format
- int minimumLauncherVersion = -1;
-
- /// Mojang: DEPRECATED version of Minecraft this is
- QString minecraftVersion;
-
- /// Mojang: class to launch Minecraft with
- QString mainClass;
-
- /// MultiMC: class to launch legacy Minecraft with (embed 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 releaseTime;
-
- /// Mojang: DEPRECATED the time this version was last updated by Mojang
- QDateTime 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 maven files to put in the libraries folder, but not in classpath
- QList<LibraryPtr> mavenFiles;
-
- /// The main jar (Minecraft version library, normally)
- LibraryPtr mainJar;
-
- /// 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<LibraryPtr> jarMods;
-
- /// MultiMC: list of mods added to this version
- QList<LibraryPtr> mods;
-
- /**
- * MultiMC: set of packages this depends on
- * NOTE: this is shared with the meta format!!!
- */
- Meta::RequireSet requires;
-
- /**
- * MultiMC: set of packages this conflicts with
- * NOTE: this is shared with the meta format!!!
- */
- Meta::RequireSet conflicts;
-
- /// is volatile -- may be removed as soon as it is no longer needed by something else
- bool m_volatile = false;
-
-public:
- // Mojang: DEPRECATED 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/api/logic/minecraft/VersionFilterData.cpp b/api/logic/minecraft/VersionFilterData.cpp
deleted file mode 100644
index 38e7b60c..00000000
--- a/api/logic/minecraft/VersionFilterData.cpp
+++ /dev/null
@@ -1,71 +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"},
- {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f"},
- {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82"}};
-
- fmlLibsMapping["1.3.2"] = libs13;
-
- // 1.4.*
- auto libs14 = QList<FMLlib>{
- {"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b"},
- {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f"},
- {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82"},
- {"bcprov-jdk15on-147.jar", "b6f5d9926b0afbde9f4dbe3db88c5247be7794bb"}};
-
- 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"},
- {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a"},
- {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58"},
- {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65"},
- {"deobfuscation_data_1.5.zip", "5f7c142d53776f16304c0bbe10542014abad6af8"},
- {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85"}};
-
- // 1.5.1
- fmlLibsMapping["1.5.1"] = QList<FMLlib>{
- {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51"},
- {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a"},
- {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58"},
- {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65"},
- {"deobfuscation_data_1.5.1.zip", "22e221a0d89516c1f721d6cab056a7e37471d0a6"},
- {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85"}};
-
- // 1.5.2
- fmlLibsMapping["1.5.2"] = QList<FMLlib>{
- {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51"},
- {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a"},
- {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58"},
- {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65"},
- {"deobfuscation_data_1.5.2.zip", "446e55cd986582c70fcf12cb27bc00114c5adfd9"},
- {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85"}};
-
- // don't use installers for those.
- forgeInstallerBlacklist = QSet<QString>({"1.5.2"});
-
- // FIXME: remove, used for deciding when core mods should display
- 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"};
-
- java8BeginsDate = timeFromS3Time("2017-03-30T09:32:19+00:00");
- java16BeginsDate = timeFromS3Time("2021-05-12T11:19:15+00:00");
-}
diff --git a/api/logic/minecraft/VersionFilterData.h b/api/logic/minecraft/VersionFilterData.h
deleted file mode 100644
index d100acc3..00000000
--- a/api/logic/minecraft/VersionFilterData.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-#include <QMap>
-#include <QString>
-#include <QSet>
-#include <QDateTime>
-
-#include "multimc_logic_export.h"
-
-struct FMLlib
-{
- QString filename;
- QString checksum;
-};
-
-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;
- // no new versions below this date will be accepted from Mojang servers
- QDateTime legacyCutoffDate;
- // Libraries that belong to LWJGL
- QSet<QString> lwjglWhitelist;
- // release date of first version to require Java 8 (17w13a)
- QDateTime java8BeginsDate;
- // release data of first version to require Java 16 (21w19a)
- QDateTime java16BeginsDate;
-};
-extern VersionFilterData MULTIMC_LOGIC_EXPORT g_VersionFilterData;
diff --git a/api/logic/minecraft/World.cpp b/api/logic/minecraft/World.cpp
deleted file mode 100644
index a2b4dac7..00000000
--- a/api/logic/minecraft/World.cpp
+++ /dev/null
@@ -1,520 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <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>
-
-#include <QCoreApplication>
-
-#include <nonstd/optional>
-
-using nonstd::optional;
-using nonstd::nullopt;
-
-GameType::GameType(nonstd::optional<int> original):
- original(original)
-{
- if(!original) {
- return;
- }
- switch(*original) {
- case 0:
- type = GameType::Survival;
- break;
- case 1:
- type = GameType::Creative;
- break;
- case 2:
- type = GameType::Adventure;
- break;
- case 3:
- type = GameType::Spectator;
- break;
- default:
- break;
- }
-}
-
-QString GameType::toTranslatedString() const
-{
- switch (type)
- {
- case GameType::Survival:
- return QCoreApplication::translate("GameType", "Survival");
- case GameType::Creative:
- return QCoreApplication::translate("GameType", "Creative");
- case GameType::Adventure:
- return QCoreApplication::translate("GameType", "Adventure");
- case GameType::Spectator:
- return QCoreApplication::translate("GameType", "Spectator");
- default:
- break;
- }
- if(original) {
- return QCoreApplication::translate("GameType", "Unknown (%1)").arg(*original);
- }
- return QCoreApplication::translate("GameType", "Undefined");
-}
-
-QString GameType::toLogString() const
-{
- switch (type)
- {
- case GameType::Survival:
- return "Survival";
- case GameType::Creative:
- return "Creative";
- case GameType::Adventure:
- return "Adventure";
- case GameType::Spectator:
- return "Spectator";
- default:
- break;
- }
- if(original) {
- return QString("Unknown (%1)").arg(*original);
- }
- return "Undefined";
-}
-
-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()));
- try {
- auto pair = nbt::io::read_compound(foo);
-
- if(pair.first != "")
- return nullptr;
-
- if(pair.second == nullptr)
- return nullptr;
-
- return std::move(pair.second);
- }
- catch (const nbt::io::input_error &e)
- {
- qWarning() << "Unable to parse level.dat:" << e.what();
- return nullptr;
- }
-}
-
-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")
- {
- m_iconFile = QString();
- readFromZip(file);
- }
- else if(file.isDir())
- {
- QFileInfo assumedIconPath(file.absoluteFilePath() + "/icon.png");
- if(assumedIconPath.exists()) {
- m_iconFile = assumedIconPath.absoluteFilePath();
- }
- readFromFS(file);
- }
-}
-
-bool World::resetIcon()
-{
- if(m_iconFile.isNull()) {
- return false;
- }
- if(QFile(m_iconFile).remove()) {
- m_iconFile = QString();
- return true;
- }
- return false;
-}
-
-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::findFolderOfFileInZip(&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);
- }
- 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;
-}
-
-namespace {
-
-optional<QString> read_string (nbt::value& parent, const char * name)
-{
- try
- {
- auto &namedValue = parent.at(name);
- if(namedValue.get_type() != nbt::tag_type::String)
- {
- return nullopt;
- }
- auto & tag_str = namedValue.as<nbt::tag_string>();
- return QString::fromStdString(tag_str.get());
- }
- catch (const std::out_of_range &e)
- {
- // fallback for old world formats
- qWarning() << "String NBT tag" << name << "could not be found.";
- return nullopt;
- }
- catch (const std::bad_cast &e)
- {
- // type mismatch
- qWarning() << "NBT tag" << name << "could not be converted to string.";
- return nullopt;
- }
-}
-
-optional<int64_t> read_long (nbt::value& parent, const char * name)
-{
- try
- {
- auto &namedValue = parent.at(name);
- if(namedValue.get_type() != nbt::tag_type::Long)
- {
- return nullopt;
- }
- auto & tag_str = namedValue.as<nbt::tag_long>();
- return tag_str.get();
- }
- catch (const std::out_of_range &e)
- {
- // fallback for old world formats
- qWarning() << "Long NBT tag" << name << "could not be found.";
- return nullopt;
- }
- catch (const std::bad_cast &e)
- {
- // type mismatch
- qWarning() << "NBT tag" << name << "could not be converted to long.";
- return nullopt;
- }
-}
-
-optional<int> read_int (nbt::value& parent, const char * name)
-{
- try
- {
- auto &namedValue = parent.at(name);
- if(namedValue.get_type() != nbt::tag_type::Int)
- {
- return nullopt;
- }
- auto & tag_str = namedValue.as<nbt::tag_int>();
- return tag_str.get();
- }
- catch (const std::out_of_range &e)
- {
- // fallback for old world formats
- qWarning() << "Int NBT tag" << name << "could not be found.";
- return nullopt;
- }
- catch (const std::bad_cast &e)
- {
- // type mismatch
- qWarning() << "NBT tag" << name << "could not be converted to int.";
- return nullopt;
- }
-}
-
-GameType read_gametype(nbt::value& parent, const char * name) {
- return GameType(read_int(parent, name));
-}
-
-}
-
-void World::loadFromLevelDat(QByteArray data)
-{
- auto levelData = parseLevelDat(data);
- if(!levelData)
- {
- is_valid = false;
- return;
- }
-
- nbt::value * valPtr = nullptr;
- try {
- valPtr = &levelData->at("Data");
- }
- catch (const std::out_of_range &e) {
- qWarning() << "Unable to read NBT tags from " << m_folderName << ":" << e.what();
- is_valid = false;
- return;
- }
- nbt::value &val = *valPtr;
-
- is_valid = val.get_type() == nbt::tag_type::Compound;
- if(!is_valid)
- return;
-
- auto name = read_string(val, "LevelName");
- m_actualName = name ? *name : m_folderName;
-
- auto timestamp = read_long(val, "LastPlayed");
- m_lastPlayed = timestamp ? QDateTime::fromMSecsSinceEpoch(*timestamp) : levelDatTime;
-
- m_gameType = read_gametype(val, "GameType");
-
- optional<int64_t> randomSeed;
- try {
- auto &WorldGen_val = val.at("WorldGenSettings");
- randomSeed = read_long(WorldGen_val, "seed");
- }
- catch (const std::out_of_range &) {}
- if(!randomSeed) {
- randomSeed = read_long(val, "RandomSeed");
- }
- m_randomSeed = randomSeed ? *randomSeed : 0;
-
- qDebug() << "World Name:" << m_actualName;
- qDebug() << "Last Played:" << m_lastPlayed.toString();
- if(randomSeed) {
- qDebug() << "Seed:" << *randomSeed;
- }
- qDebug() << "GameType:" << m_gameType.toLogString();
-}
-
-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();
-}
diff --git a/api/logic/minecraft/World.h b/api/logic/minecraft/World.h
deleted file mode 100644
index 1d94d54d..00000000
--- a/api/logic/minecraft/World.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <QFileInfo>
-#include <QDateTime>
-#include <nonstd/optional>
-
-#include "multimc_logic_export.h"
-
-struct MULTIMC_LOGIC_EXPORT GameType {
- GameType() = default;
- GameType (nonstd::optional<int> original);
-
- QString toTranslatedString() const;
- QString toLogString() const;
-
- enum
- {
- Unknown = -1,
- Survival = 0,
- Creative,
- Adventure,
- Spectator
- } type = Unknown;
- nonstd::optional<int> original;
-};
-
-class MULTIMC_LOGIC_EXPORT World
-{
-public:
- World(const QFileInfo &file);
- QString folderName() const
- {
- return m_folderName;
- }
- QString name() const
- {
- return m_actualName;
- }
- QString iconFile() const
- {
- return m_iconFile;
- }
- QDateTime lastPlayed() const
- {
- return m_lastPlayed;
- }
- GameType gameType() const
- {
- return m_gameType;
- }
- 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);
- // remove the icon file, if any
- bool resetIcon();
-
- 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;
-
-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;
- QString m_iconFile;
- QDateTime levelDatTime;
- QDateTime m_lastPlayed;
- int64_t m_randomSeed = 0;
- GameType m_gameType;
- bool is_valid = false;
-};
diff --git a/api/logic/minecraft/WorldList.cpp b/api/logic/minecraft/WorldList.cpp
deleted file mode 100644
index f6309dbd..00000000
--- a/api/logic/minecraft/WorldList.cpp
+++ /dev/null
@@ -1,387 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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()
-{
- if(is_watching)
- {
- return;
- }
- 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()
-{
- if(!is_watching)
- {
- return;
- }
- 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;
-}
-
-bool WorldList::resetIcon(int row)
-{
- if (row >= worlds.size() || row < 0)
- return false;
- World &m = worlds[row];
- if(m.resetIcon()) {
- emit dataChanged(index(row), index(row), {WorldList::IconFileRole});
- return true;
- }
- return false;
-}
-
-
-int WorldList::columnCount(const QModelIndex &parent) const
-{
- return 3;
-}
-
-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 GameModeColumn:
- return world.gameType().toTranslatedString();
-
- 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();
- }
- case IconFileRole:
- {
- return world.iconFile();
- }
- 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 GameModeColumn:
- return tr("Game Mode");
- case LastPlayedColumn:
- return tr("Last Played");
- default:
- return QVariant();
- }
-
- case Qt::ToolTipRole:
- switch (section)
- {
- case NameColumn:
- return tr("The name of the world.");
- case GameModeColumn:
- return tr("Game mode 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/api/logic/minecraft/WorldList.h b/api/logic/minecraft/WorldList.h
deleted file mode 100644
index 740b1461..00000000
--- a/api/logic/minecraft/WorldList.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/* Copyright 2015-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QList>
-#include <QString>
-#include <QDir>
-#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,
- GameModeColumn,
- LastPlayedColumn
- };
-
- enum Roles
- {
- ObjectRole = Qt::UserRole + 1,
- FolderRole,
- SeedRole,
- NameRole,
- GameModeRole,
- LastPlayedRole,
- IconFileRole
- };
-
- 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);
-
- /// Removes the world icon, if any
- virtual bool resetIcon(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/api/logic/minecraft/auth-msa/BuildConfig.cpp.in b/api/logic/minecraft/auth-msa/BuildConfig.cpp.in
deleted file mode 100644
index 8f470e25..00000000
--- a/api/logic/minecraft/auth-msa/BuildConfig.cpp.in
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "BuildConfig.h"
-#include <QObject>
-
-const Config BuildConfig;
-
-Config::Config()
-{
- CLIENT_ID = "@MOJANGDEMO_CLIENT_ID@";
-}
diff --git a/api/logic/minecraft/auth-msa/BuildConfig.h b/api/logic/minecraft/auth-msa/BuildConfig.h
deleted file mode 100644
index 7a01d704..00000000
--- a/api/logic/minecraft/auth-msa/BuildConfig.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-#include <QString>
-
-class Config
-{
-public:
- Config();
- QString CLIENT_ID;
-};
-
-extern const Config BuildConfig;
diff --git a/api/logic/minecraft/auth-msa/CMakeLists.txt b/api/logic/minecraft/auth-msa/CMakeLists.txt
deleted file mode 100644
index 22777d1b..00000000
--- a/api/logic/minecraft/auth-msa/CMakeLists.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-find_package(Qt5 COMPONENTS Core Gui Network Widgets REQUIRED)
-
-set(CMAKE_AUTOMOC ON)
-set(CMAKE_AUTOUIC ON)
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
-
-
-set(MOJANGDEMO_CLIENT_ID "" CACHE STRING "Client ID used for OAuth2 in mojangdemo")
-
-configure_file("${CMAKE_CURRENT_SOURCE_DIR}/BuildConfig.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp")
-
-set(mojang_SRCS
- main.cpp
- context.cpp
- context.h
-
- mainwindow.cpp
- mainwindow.h
- mainwindow.ui
-
- ${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp
- BuildConfig.h
-)
-
-add_executable( mojangdemo ${mojang_SRCS} )
-target_link_libraries( mojangdemo Katabasis Qt5::Gui Qt5::Widgets )
-target_include_directories(mojangdemo PRIVATE logic)
diff --git a/api/logic/minecraft/auth-msa/context.cpp b/api/logic/minecraft/auth-msa/context.cpp
deleted file mode 100644
index d7ecda30..00000000
--- a/api/logic/minecraft/auth-msa/context.cpp
+++ /dev/null
@@ -1,938 +0,0 @@
-#include <QNetworkAccessManager>
-#include <QNetworkRequest>
-#include <QNetworkReply>
-#include <QDesktopServices>
-#include <QMetaEnum>
-#include <QDebug>
-
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
-
-#include <QUrlQuery>
-
-#include <QPixmap>
-#include <QPainter>
-
-#include "context.h"
-#include "katabasis/Globals.h"
-#include "katabasis/StoreQSettings.h"
-#include "katabasis/Requestor.h"
-#include "BuildConfig.h"
-
-using OAuth2 = Katabasis::OAuth2;
-using Requestor = Katabasis::Requestor;
-using Activity = Katabasis::Activity;
-
-Context::Context(QObject *parent) :
- QObject(parent)
-{
- mgr = new QNetworkAccessManager(this);
-
- Katabasis::OAuth2::Options opts;
- opts.scope = "XboxLive.signin offline_access";
- opts.clientIdentifier = BuildConfig.CLIENT_ID;
- opts.authorizationUrl = "https://login.live.com/oauth20_authorize.srf";
- opts.accessTokenUrl = "https://login.live.com/oauth20_token.srf";
- opts.listenerPorts = {28562, 28563, 28564, 28565, 28566};
-
- oauth2 = new OAuth2(opts, m_account.msaToken, this, mgr);
-
- connect(oauth2, &OAuth2::linkingFailed, this, &Context::onLinkingFailed);
- connect(oauth2, &OAuth2::linkingSucceeded, this, &Context::onLinkingSucceeded);
- connect(oauth2, &OAuth2::openBrowser, this, &Context::onOpenBrowser);
- connect(oauth2, &OAuth2::closeBrowser, this, &Context::onCloseBrowser);
- connect(oauth2, &OAuth2::activityChanged, this, &Context::onOAuthActivityChanged);
-}
-
-void Context::beginActivity(Activity activity) {
- if(isBusy()) {
- throw 0;
- }
- activity_ = activity;
- emit activityChanged(activity_);
-}
-
-void Context::finishActivity() {
- if(!isBusy()) {
- throw 0;
- }
- activity_ = Katabasis::Activity::Idle;
- m_account.validity_ = m_account.minecraftProfile.validity;
- emit activityChanged(activity_);
-}
-
-QString Context::gameToken() {
- return m_account.minecraftToken.token;
-}
-
-QString Context::userId() {
- return m_account.minecraftProfile.id;
-}
-
-QString Context::userName() {
- return m_account.minecraftProfile.name;
-}
-
-bool Context::silentSignIn() {
- if(isBusy()) {
- return false;
- }
- beginActivity(Activity::Refreshing);
- if(!oauth2->refresh()) {
- finishActivity();
- return false;
- }
-
- requestsDone = 0;
- xboxProfileSucceeded = false;
- mcAuthSucceeded = false;
-
- return true;
-}
-
-bool Context::signIn() {
- if(isBusy()) {
- return false;
- }
-
- requestsDone = 0;
- xboxProfileSucceeded = false;
- mcAuthSucceeded = false;
-
- beginActivity(Activity::LoggingIn);
- oauth2->unlink();
- m_account = AccountData();
- oauth2->link();
- return true;
-}
-
-bool Context::signOut() {
- if(isBusy()) {
- return false;
- }
- beginActivity(Activity::LoggingOut);
- oauth2->unlink();
- m_account = AccountData();
- finishActivity();
- return true;
-}
-
-
-void Context::onOpenBrowser(const QUrl &url) {
- QDesktopServices::openUrl(url);
-}
-
-void Context::onCloseBrowser() {
-
-}
-
-void Context::onLinkingFailed() {
- finishActivity();
-}
-
-void Context::onLinkingSucceeded() {
- auto *o2t = qobject_cast<OAuth2 *>(sender());
- if (!o2t->linked()) {
- finishActivity();
- return;
- }
- QVariantMap extraTokens = o2t->extraTokens();
- if (!extraTokens.isEmpty()) {
- qDebug() << "Extra tokens in response:";
- foreach (QString key, extraTokens.keys()) {
- qDebug() << "\t" << key << ":" << extraTokens.value(key);
- }
- }
- doUserAuth();
-}
-
-void Context::onOAuthActivityChanged(Katabasis::Activity activity) {
- // respond to activity change here
-}
-
-void Context::doUserAuth() {
- QString xbox_auth_template = R"XXX(
-{
- "Properties": {
- "AuthMethod": "RPS",
- "SiteName": "user.auth.xboxlive.com",
- "RpsTicket": "d=%1"
- },
- "RelyingParty": "http://auth.xboxlive.com",
- "TokenType": "JWT"
-}
-)XXX";
- auto xbox_auth_data = xbox_auth_template.arg(m_account.msaToken.token);
-
- QNetworkRequest request = QNetworkRequest(QUrl("https://user.auth.xboxlive.com/user/authenticate"));
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- request.setRawHeader("Accept", "application/json");
- auto *requestor = new Katabasis::Requestor(mgr, oauth2, this);
- requestor->setAddAccessTokenInQuery(false);
-
- connect(requestor, &Requestor::finished, this, &Context::onUserAuthDone);
- requestor->post(request, xbox_auth_data.toUtf8());
- qDebug() << "First layer of XBox auth ... commencing.";
-}
-
-namespace {
-bool getDateTime(QJsonValue value, QDateTime & out) {
- if(!value.isString()) {
- return false;
- }
- out = QDateTime::fromString(value.toString(), Qt::ISODateWithMs);
- return out.isValid();
-}
-
-bool getString(QJsonValue value, QString & out) {
- if(!value.isString()) {
- return false;
- }
- out = value.toString();
- return true;
-}
-
-bool getNumber(QJsonValue value, double & out) {
- if(!value.isDouble()) {
- return false;
- }
- out = value.toDouble();
- return true;
-}
-
-/*
-{
- "IssueInstant":"2020-12-07T19:52:08.4463796Z",
- "NotAfter":"2020-12-21T19:52:08.4463796Z",
- "Token":"token",
- "DisplayClaims":{
- "xui":[
- {
- "uhs":"userhash"
- }
- ]
- }
- }
-*/
-// TODO: handle error responses ...
-/*
-{
- "Identity":"0",
- "XErr":2148916238,
- "Message":"",
- "Redirect":"https://start.ui.xboxlive.com/AddChildToFamily"
-}
-// 2148916233 = missing XBox account
-// 2148916238 = child account not linked to a family
-*/
-
-bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output) {
- QJsonParseError jsonError;
- QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
- if(jsonError.error) {
- qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString();
- qDebug() << data;
- return false;
- }
-
- auto obj = doc.object();
- if(!getDateTime(obj.value("IssueInstant"), output.issueInstant)) {
- qWarning() << "User IssueInstant is not a timestamp";
- qDebug() << data;
- return false;
- }
- if(!getDateTime(obj.value("NotAfter"), output.notAfter)) {
- qWarning() << "User NotAfter is not a timestamp";
- qDebug() << data;
- return false;
- }
- if(!getString(obj.value("Token"), output.token)) {
- qWarning() << "User Token is not a timestamp";
- qDebug() << data;
- return false;
- }
- auto arrayVal = obj.value("DisplayClaims").toObject().value("xui");
- if(!arrayVal.isArray()) {
- qWarning() << "Missing xui claims array";
- qDebug() << data;
- return false;
- }
- bool foundUHS = false;
- for(auto item: arrayVal.toArray()) {
- if(!item.isObject()) {
- continue;
- }
- auto obj = item.toObject();
- if(obj.contains("uhs")) {
- foundUHS = true;
- } else {
- continue;
- }
- // consume all 'display claims' ... whatever that means
- for(auto iter = obj.begin(); iter != obj.end(); iter++) {
- QString claim;
- if(!getString(obj.value(iter.key()), claim)) {
- qWarning() << "display claim " << iter.key() << " is not a string...";
- qDebug() << data;
- return false;
- }
- output.extra[iter.key()] = claim;
- }
-
- break;
- }
- if(!foundUHS) {
- qWarning() << "Missing uhs";
- qDebug() << data;
- return false;
- }
- output.validity = Katabasis::Validity::Certain;
- qDebug() << data;
- return true;
-}
-
-}
-
-void Context::onUserAuthDone(
- int requestId,
- QNetworkReply::NetworkError error,
- QByteArray replyData,
- QList<QNetworkReply::RawHeaderPair> headers
-) {
- if (error != QNetworkReply::NoError) {
- qWarning() << "Reply error:" << error;
- finishActivity();
- return;
- }
-
- Katabasis::Token temp;
- if(!parseXTokenResponse(replyData, temp)) {
- qWarning() << "Could not parse user authentication response...";
- finishActivity();
- return;
- }
- m_account.userToken = temp;
-
- doSTSAuthMinecraft();
- doSTSAuthGeneric();
-}
-/*
- url = "https://xsts.auth.xboxlive.com/xsts/authorize"
- headers = {"x-xbl-contract-version": "1"}
- data = {
- "RelyingParty": relying_party,
- "TokenType": "JWT",
- "Properties": {
- "UserTokens": [self.user_token.token],
- "SandboxId": "RETAIL",
- },
- }
-*/
-void Context::doSTSAuthMinecraft() {
- QString xbox_auth_template = R"XXX(
-{
- "Properties": {
- "SandboxId": "RETAIL",
- "UserTokens": [
- "%1"
- ]
- },
- "RelyingParty": "rp://api.minecraftservices.com/",
- "TokenType": "JWT"
-}
-)XXX";
- auto xbox_auth_data = xbox_auth_template.arg(m_account.userToken.token);
-
- QNetworkRequest request = QNetworkRequest(QUrl("https://xsts.auth.xboxlive.com/xsts/authorize"));
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- request.setRawHeader("Accept", "application/json");
- Requestor *requestor = new Requestor(mgr, oauth2, this);
- requestor->setAddAccessTokenInQuery(false);
-
- connect(requestor, &Requestor::finished, this, &Context::onSTSAuthMinecraftDone);
- requestor->post(request, xbox_auth_data.toUtf8());
- qDebug() << "Second layer of XBox auth ... commencing.";
-}
-
-void Context::onSTSAuthMinecraftDone(
- int requestId,
- QNetworkReply::NetworkError error,
- QByteArray replyData,
- QList<QNetworkReply::RawHeaderPair> headers
-) {
- if (error != QNetworkReply::NoError) {
- qWarning() << "Reply error:" << error;
- finishActivity();
- return;
- }
-
- Katabasis::Token temp;
- if(!parseXTokenResponse(replyData, temp)) {
- qWarning() << "Could not parse authorization response for access to mojang services...";
- finishActivity();
- return;
- }
-
- if(temp.extra["uhs"] != m_account.userToken.extra["uhs"]) {
- qWarning() << "Server has changed user hash in the reply... something is wrong. ABORTING";
- qDebug() << replyData;
- finishActivity();
- return;
- }
- m_account.mojangservicesToken = temp;
-
- doMinecraftAuth();
-}
-
-void Context::doSTSAuthGeneric() {
- QString xbox_auth_template = R"XXX(
-{
- "Properties": {
- "SandboxId": "RETAIL",
- "UserTokens": [
- "%1"
- ]
- },
- "RelyingParty": "http://xboxlive.com",
- "TokenType": "JWT"
-}
-)XXX";
- auto xbox_auth_data = xbox_auth_template.arg(m_account.userToken.token);
-
- QNetworkRequest request = QNetworkRequest(QUrl("https://xsts.auth.xboxlive.com/xsts/authorize"));
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- request.setRawHeader("Accept", "application/json");
- Requestor *requestor = new Requestor(mgr, oauth2, this);
- requestor->setAddAccessTokenInQuery(false);
-
- connect(requestor, &Requestor::finished, this, &Context::onSTSAuthGenericDone);
- requestor->post(request, xbox_auth_data.toUtf8());
- qDebug() << "Second layer of XBox auth ... commencing.";
-}
-
-void Context::onSTSAuthGenericDone(
- int requestId,
- QNetworkReply::NetworkError error,
- QByteArray replyData,
- QList<QNetworkReply::RawHeaderPair> headers
-) {
- if (error != QNetworkReply::NoError) {
- qWarning() << "Reply error:" << error;
- finishActivity();
- return;
- }
-
- Katabasis::Token temp;
- if(!parseXTokenResponse(replyData, temp)) {
- qWarning() << "Could not parse authorization response for access to xbox API...";
- finishActivity();
- return;
- }
-
- if(temp.extra["uhs"] != m_account.userToken.extra["uhs"]) {
- qWarning() << "Server has changed user hash in the reply... something is wrong. ABORTING";
- qDebug() << replyData;
- finishActivity();
- return;
- }
- m_account.xboxApiToken = temp;
-
- doXBoxProfile();
-}
-
-
-void Context::doMinecraftAuth() {
- QString mc_auth_template = R"XXX(
-{
- "identityToken": "XBL3.0 x=%1;%2"
-}
-)XXX";
- auto data = mc_auth_template.arg(m_account.mojangservicesToken.extra["uhs"].toString(), m_account.mojangservicesToken.token);
-
- QNetworkRequest request = QNetworkRequest(QUrl("https://api.minecraftservices.com/authentication/login_with_xbox"));
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- request.setRawHeader("Accept", "application/json");
- Requestor *requestor = new Requestor(mgr, oauth2, this);
- requestor->setAddAccessTokenInQuery(false);
-
- connect(requestor, &Requestor::finished, this, &Context::onMinecraftAuthDone);
- requestor->post(request, data.toUtf8());
- qDebug() << "Getting Minecraft access token...";
-}
-
-namespace {
-bool parseMojangResponse(QByteArray & data, Katabasis::Token &output) {
- QJsonParseError jsonError;
- QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
- if(jsonError.error) {
- qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString();
- qDebug() << data;
- return false;
- }
-
- auto obj = doc.object();
- double expires_in = 0;
- if(!getNumber(obj.value("expires_in"), expires_in)) {
- qWarning() << "expires_in is not a valid number";
- qDebug() << data;
- return false;
- }
- auto currentTime = QDateTime::currentDateTimeUtc();
- output.issueInstant = currentTime;
- output.notAfter = currentTime.addSecs(expires_in);
-
- QString username;
- if(!getString(obj.value("username"), username)) {
- qWarning() << "username is not valid";
- qDebug() << data;
- return false;
- }
-
- // TODO: it's a JWT... validate it?
- if(!getString(obj.value("access_token"), output.token)) {
- qWarning() << "access_token is not valid";
- qDebug() << data;
- return false;
- }
- output.validity = Katabasis::Validity::Certain;
- qDebug() << data;
- return true;
-}
-}
-
-void Context::onMinecraftAuthDone(
- int requestId,
- QNetworkReply::NetworkError error,
- QByteArray replyData,
- QList<QNetworkReply::RawHeaderPair> headers
-) {
- requestsDone++;
-
- if (error != QNetworkReply::NoError) {
- qWarning() << "Reply error:" << error;
- qDebug() << replyData;
- finishActivity();
- return;
- }
-
- if(!parseMojangResponse(replyData, m_account.minecraftToken)) {
- qWarning() << "Could not parse login_with_xbox response...";
- qDebug() << replyData;
- finishActivity();
- return;
- }
- mcAuthSucceeded = true;
-
- checkResult();
-}
-
-void Context::doXBoxProfile() {
- auto url = QUrl("https://profile.xboxlive.com/users/me/profile/settings");
- QUrlQuery q;
- q.addQueryItem(
- "settings",
- "GameDisplayName,AppDisplayName,AppDisplayPicRaw,GameDisplayPicRaw,"
- "PublicGamerpic,ShowUserAsAvatar,Gamerscore,Gamertag,ModernGamertag,ModernGamertagSuffix,"
- "UniqueModernGamertag,AccountTier,TenureLevel,XboxOneRep,"
- "PreferredColor,Location,Bio,Watermarks,"
- "RealName,RealNameOverride,IsQuarantined"
- );
- url.setQuery(q);
-
- QNetworkRequest request = QNetworkRequest(url);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- request.setRawHeader("Accept", "application/json");
- request.setRawHeader("x-xbl-contract-version", "3");
- request.setRawHeader("Authorization", QString("XBL3.0 x=%1;%2").arg(m_account.userToken.extra["uhs"].toString(), m_account.xboxApiToken.token).toUtf8());
- Requestor *requestor = new Requestor(mgr, oauth2, this);
- requestor->setAddAccessTokenInQuery(false);
-
- connect(requestor, &Requestor::finished, this, &Context::onXBoxProfileDone);
- requestor->get(request);
- qDebug() << "Getting Xbox profile...";
-}
-
-void Context::onXBoxProfileDone(
- int requestId,
- QNetworkReply::NetworkError error,
- QByteArray replyData,
- QList<QNetworkReply::RawHeaderPair> headers
-) {
- requestsDone ++;
-
- if (error != QNetworkReply::NoError) {
- qWarning() << "Reply error:" << error;
- qDebug() << replyData;
- finishActivity();
- return;
- }
-
- qDebug() << "XBox profile: " << replyData;
-
- xboxProfileSucceeded = true;
- checkResult();
-}
-
-void Context::checkResult() {
- if(requestsDone != 2) {
- return;
- }
- if(mcAuthSucceeded && xboxProfileSucceeded) {
- doMinecraftProfile();
- }
- else {
- finishActivity();
- }
-}
-
-namespace {
-bool parseMinecraftProfile(QByteArray & data, MinecraftProfile &output) {
- QJsonParseError jsonError;
- QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
- if(jsonError.error) {
- qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString();
- qDebug() << data;
- return false;
- }
-
- auto obj = doc.object();
- if(!getString(obj.value("id"), output.id)) {
- qWarning() << "minecraft profile id is not a string";
- qDebug() << data;
- return false;
- }
-
- if(!getString(obj.value("name"), output.name)) {
- qWarning() << "minecraft profile name is not a string";
- qDebug() << data;
- return false;
- }
-
- auto skinsArray = obj.value("skins").toArray();
- for(auto skin: skinsArray) {
- auto skinObj = skin.toObject();
- Skin skinOut;
- if(!getString(skinObj.value("id"), skinOut.id)) {
- continue;
- }
- QString state;
- if(!getString(skinObj.value("state"), state)) {
- continue;
- }
- if(state != "ACTIVE") {
- continue;
- }
- if(!getString(skinObj.value("url"), skinOut.url)) {
- continue;
- }
- if(!getString(skinObj.value("variant"), skinOut.variant)) {
- continue;
- }
- // we deal with only the active skin
- output.skin = skinOut;
- break;
- }
- auto capesArray = obj.value("capes").toArray();
- int i = -1;
- int currentCape = -1;
- for(auto cape: capesArray) {
- i++;
- auto capeObj = cape.toObject();
- Cape capeOut;
- if(!getString(capeObj.value("id"), capeOut.id)) {
- continue;
- }
- QString state;
- if(!getString(capeObj.value("state"), state)) {
- continue;
- }
- if(state == "ACTIVE") {
- currentCape = i;
- }
- if(!getString(capeObj.value("url"), capeOut.url)) {
- continue;
- }
- if(!getString(capeObj.value("alias"), capeOut.alias)) {
- continue;
- }
-
- // we deal with only the active skin
- output.capes.push_back(capeOut);
- }
- output.currentCape = currentCape;
- output.validity = Katabasis::Validity::Certain;
- return true;
-}
-}
-
-void Context::doMinecraftProfile() {
- auto url = QUrl("https://api.minecraftservices.com/minecraft/profile");
- QNetworkRequest request = QNetworkRequest(url);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- // request.setRawHeader("Accept", "application/json");
- request.setRawHeader("Authorization", QString("Bearer %1").arg(m_account.minecraftToken.token).toUtf8());
-
- Requestor *requestor = new Requestor(mgr, oauth2, this);
- requestor->setAddAccessTokenInQuery(false);
-
- connect(requestor, &Requestor::finished, this, &Context::onMinecraftProfileDone);
- requestor->get(request);
-}
-
-void Context::onMinecraftProfileDone(int, QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair> headers) {
- qDebug() << data;
- if (error == QNetworkReply::ContentNotFoundError) {
- m_account.minecraftProfile = MinecraftProfile();
- finishActivity();
- return;
- }
- if (error != QNetworkReply::NoError) {
- finishActivity();
- return;
- }
- if(!parseMinecraftProfile(data, m_account.minecraftProfile)) {
- m_account.minecraftProfile = MinecraftProfile();
- finishActivity();
- return;
- }
- doGetSkin();
-}
-
-void Context::doGetSkin() {
- auto url = QUrl(m_account.minecraftProfile.skin.url);
- QNetworkRequest request = QNetworkRequest(url);
- Requestor *requestor = new Requestor(mgr, oauth2, this);
- requestor->setAddAccessTokenInQuery(false);
- connect(requestor, &Requestor::finished, this, &Context::onSkinDone);
- requestor->get(request);
-}
-
-void Context::onSkinDone(int, QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair>) {
- if (error == QNetworkReply::NoError) {
- m_account.minecraftProfile.skin.data = data;
- }
- finishActivity();
-}
-
-namespace {
-void tokenToJSON(QJsonObject &parent, Katabasis::Token t, const char * tokenName) {
- if(t.validity == Katabasis::Validity::None || !t.persistent) {
- return;
- }
- QJsonObject out;
- if(t.issueInstant.isValid()) {
- out["iat"] = QJsonValue(t.issueInstant.toSecsSinceEpoch());
- }
-
- if(t.notAfter.isValid()) {
- out["exp"] = QJsonValue(t.notAfter.toSecsSinceEpoch());
- }
-
- if(!t.token.isEmpty()) {
- out["token"] = QJsonValue(t.token);
- }
- if(!t.refresh_token.isEmpty()) {
- out["refresh_token"] = QJsonValue(t.refresh_token);
- }
- if(t.extra.size()) {
- out["extra"] = QJsonObject::fromVariantMap(t.extra);
- }
- if(out.size()) {
- parent[tokenName] = out;
- }
-}
-
-Katabasis::Token tokenFromJSON(const QJsonObject &parent, const char * tokenName) {
- Katabasis::Token out;
- auto tokenObject = parent.value(tokenName).toObject();
- if(tokenObject.isEmpty()) {
- return out;
- }
- auto issueInstant = tokenObject.value("iat");
- if(issueInstant.isDouble()) {
- out.issueInstant = QDateTime::fromSecsSinceEpoch((int64_t) issueInstant.toDouble());
- }
-
- auto notAfter = tokenObject.value("exp");
- if(notAfter.isDouble()) {
- out.notAfter = QDateTime::fromSecsSinceEpoch((int64_t) notAfter.toDouble());
- }
-
- auto token = tokenObject.value("token");
- if(token.isString()) {
- out.token = token.toString();
- out.validity = Katabasis::Validity::Assumed;
- }
-
- auto refresh_token = tokenObject.value("refresh_token");
- if(refresh_token.isString()) {
- out.refresh_token = refresh_token.toString();
- }
-
- auto extra = tokenObject.value("extra");
- if(extra.isObject()) {
- out.extra = extra.toObject().toVariantMap();
- }
- return out;
-}
-
-void profileToJSON(QJsonObject &parent, MinecraftProfile p, const char * tokenName) {
- if(p.id.isEmpty()) {
- return;
- }
- QJsonObject out;
- out["id"] = QJsonValue(p.id);
- out["name"] = QJsonValue(p.name);
- if(p.currentCape != -1) {
- out["cape"] = p.capes[p.currentCape].id;
- }
-
- {
- QJsonObject skinObj;
- skinObj["id"] = p.skin.id;
- skinObj["url"] = p.skin.url;
- skinObj["variant"] = p.skin.variant;
- if(p.skin.data.size()) {
- skinObj["data"] = QString::fromLatin1(p.skin.data.toBase64());
- }
- out["skin"] = skinObj;
- }
-
- QJsonArray capesArray;
- for(auto & cape: p.capes) {
- QJsonObject capeObj;
- capeObj["id"] = cape.id;
- capeObj["url"] = cape.url;
- capeObj["alias"] = cape.alias;
- if(cape.data.size()) {
- capeObj["data"] = QString::fromLatin1(cape.data.toBase64());
- }
- capesArray.push_back(capeObj);
- }
- out["capes"] = capesArray;
- parent[tokenName] = out;
-}
-
-MinecraftProfile profileFromJSON(const QJsonObject &parent, const char * tokenName) {
- MinecraftProfile out;
- auto tokenObject = parent.value(tokenName).toObject();
- if(tokenObject.isEmpty()) {
- return out;
- }
- {
- auto idV = tokenObject.value("id");
- auto nameV = tokenObject.value("name");
- if(!idV.isString() || !nameV.isString()) {
- qWarning() << "mandatory profile attributes are missing or of unexpected type";
- return MinecraftProfile();
- }
- out.name = nameV.toString();
- out.id = idV.toString();
- }
-
- {
- auto skinV = tokenObject.value("skin");
- if(!skinV.isObject()) {
- qWarning() << "skin is missing";
- return MinecraftProfile();
- }
- auto skinObj = skinV.toObject();
- auto idV = skinObj.value("id");
- auto urlV = skinObj.value("url");
- auto variantV = skinObj.value("variant");
- if(!idV.isString() || !urlV.isString() || !variantV.isString()) {
- qWarning() << "mandatory skin attributes are missing or of unexpected type";
- return MinecraftProfile();
- }
- out.skin.id = idV.toString();
- out.skin.url = urlV.toString();
- out.skin.variant = variantV.toString();
-
- // data for skin is optional
- auto dataV = skinObj.value("data");
- if(dataV.isString()) {
- // TODO: validate base64
- out.skin.data = QByteArray::fromBase64(dataV.toString().toLatin1());
- }
- else if (!dataV.isUndefined()) {
- qWarning() << "skin data is something unexpected";
- return MinecraftProfile();
- }
- }
-
- auto capesV = tokenObject.value("capes");
- if(!capesV.isArray()) {
- qWarning() << "capes is not an array!";
- return MinecraftProfile();
- }
- auto capesArray = capesV.toArray();
- for(auto capeV: capesArray) {
- if(!capeV.isObject()) {
- qWarning() << "cape is not an object!";
- return MinecraftProfile();
- }
- auto capeObj = capeV.toObject();
- auto idV = capeObj.value("id");
- auto urlV = capeObj.value("url");
- auto aliasV = capeObj.value("alias");
- if(!idV.isString() || !urlV.isString() || !aliasV.isString()) {
- qWarning() << "mandatory skin attributes are missing or of unexpected type";
- return MinecraftProfile();
- }
- Cape cape;
- cape.id = idV.toString();
- cape.url = urlV.toString();
- cape.alias = aliasV.toString();
-
- // data for cape is optional.
- auto dataV = capeObj.value("data");
- if(dataV.isString()) {
- // TODO: validate base64
- cape.data = QByteArray::fromBase64(dataV.toString().toLatin1());
- }
- else if (!dataV.isUndefined()) {
- qWarning() << "cape data is something unexpected";
- return MinecraftProfile();
- }
- out.capes.push_back(cape);
- }
- out.validity = Katabasis::Validity::Assumed;
- return out;
-}
-
-}
-
-bool Context::resumeFromState(QByteArray data) {
- QJsonParseError error;
- auto doc = QJsonDocument::fromJson(data, &error);
- if(error.error != QJsonParseError::NoError) {
- qWarning() << "Failed to parse account data as JSON.";
- return false;
- }
- auto docObject = doc.object();
- m_account.msaToken = tokenFromJSON(docObject, "msa");
- m_account.userToken = tokenFromJSON(docObject, "utoken");
- m_account.xboxApiToken = tokenFromJSON(docObject, "xrp-main");
- m_account.mojangservicesToken = tokenFromJSON(docObject, "xrp-mc");
- m_account.minecraftToken = tokenFromJSON(docObject, "ygg");
-
- m_account.minecraftProfile = profileFromJSON(docObject, "profile");
-
- m_account.validity_ = m_account.minecraftProfile.validity;
-
- return true;
-}
-
-QByteArray Context::saveState() {
- QJsonDocument doc;
- QJsonObject output;
- tokenToJSON(output, m_account.msaToken, "msa");
- tokenToJSON(output, m_account.userToken, "utoken");
- tokenToJSON(output, m_account.xboxApiToken, "xrp-main");
- tokenToJSON(output, m_account.mojangservicesToken, "xrp-mc");
- tokenToJSON(output, m_account.minecraftToken, "ygg");
- profileToJSON(output, m_account.minecraftProfile, "profile");
- doc.setObject(output);
- return doc.toJson(QJsonDocument::Indented);
-}
diff --git a/api/logic/minecraft/auth-msa/context.h b/api/logic/minecraft/auth-msa/context.h
deleted file mode 100644
index f1ac99b8..00000000
--- a/api/logic/minecraft/auth-msa/context.h
+++ /dev/null
@@ -1,128 +0,0 @@
-#pragma once
-
-#include <QObject>
-#include <QList>
-#include <QVector>
-#include <QNetworkReply>
-#include <QImage>
-
-#include <katabasis/OAuth2.h>
-
-struct Skin {
- QString id;
- QString url;
- QString variant;
-
- QByteArray data;
-};
-
-struct Cape {
- QString id;
- QString url;
- QString alias;
-
- QByteArray data;
-};
-
-struct MinecraftProfile {
- QString id;
- QString name;
- Skin skin;
- int currentCape = -1;
- QVector<Cape> capes;
- Katabasis::Validity validity = Katabasis::Validity::None;
-};
-
-enum class AccountType {
- MSA,
- Mojang
-};
-
-struct AccountData {
- AccountType type = AccountType::MSA;
-
- Katabasis::Token msaToken;
- Katabasis::Token userToken;
- Katabasis::Token xboxApiToken;
- Katabasis::Token mojangservicesToken;
- Katabasis::Token minecraftToken;
-
- MinecraftProfile minecraftProfile;
- Katabasis::Validity validity_ = Katabasis::Validity::None;
-};
-
-class Context : public QObject
-{
- Q_OBJECT
-
-public:
- explicit Context(QObject *parent = 0);
-
- QByteArray saveState();
- bool resumeFromState(QByteArray data);
-
- bool isBusy() {
- return activity_ != Katabasis::Activity::Idle;
- };
- Katabasis::Validity validity() {
- return m_account.validity_;
- };
-
- bool signIn();
- bool silentSignIn();
- bool signOut();
-
- QString userName();
- QString userId();
- QString gameToken();
-signals:
- void succeeded();
- void failed();
- void activityChanged(Katabasis::Activity activity);
-
-private slots:
- void onLinkingSucceeded();
- void onLinkingFailed();
- void onOpenBrowser(const QUrl &url);
- void onCloseBrowser();
- void onOAuthActivityChanged(Katabasis::Activity activity);
-
-private:
- void doUserAuth();
- Q_SLOT void onUserAuthDone(int, QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
-
- void doSTSAuthMinecraft();
- Q_SLOT void onSTSAuthMinecraftDone(int, QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
- void doMinecraftAuth();
- Q_SLOT void onMinecraftAuthDone(int, QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
-
- void doSTSAuthGeneric();
- Q_SLOT void onSTSAuthGenericDone(int, QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
- void doXBoxProfile();
- Q_SLOT void onXBoxProfileDone(int, QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
-
- void doMinecraftProfile();
- Q_SLOT void onMinecraftProfileDone(int, QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
-
- void doGetSkin();
- Q_SLOT void onSkinDone(int, QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
-
- void checkResult();
-
-private:
- void beginActivity(Katabasis::Activity activity);
- void finishActivity();
- void clearTokens();
-
-private:
- Katabasis::OAuth2 *oauth2 = nullptr;
-
- int requestsDone = 0;
- bool xboxProfileSucceeded = false;
- bool mcAuthSucceeded = false;
- Katabasis::Activity activity_ = Katabasis::Activity::Idle;
-
- AccountData m_account;
-
- QNetworkAccessManager *mgr = nullptr;
-};
diff --git a/api/logic/minecraft/auth-msa/main.cpp b/api/logic/minecraft/auth-msa/main.cpp
deleted file mode 100644
index 481e0126..00000000
--- a/api/logic/minecraft/auth-msa/main.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-#include <QApplication>
-#include <QStringList>
-#include <QTimer>
-#include <QDebug>
-#include <QFile>
-#include <QSaveFile>
-
-#include "context.h"
-#include "mainwindow.h"
-
-void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
-{
- QByteArray localMsg = msg.toLocal8Bit();
- const char *file = context.file ? context.file : "";
- const char *function = context.function ? context.function : "";
- switch (type) {
- case QtDebugMsg:
- fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
- break;
- case QtInfoMsg:
- fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
- break;
- case QtWarningMsg:
- fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
- break;
- case QtCriticalMsg:
- fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
- break;
- case QtFatalMsg:
- fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
- break;
- }
-}
-
-class Helper : public QObject {
- Q_OBJECT
-
-public:
- Helper(Context * context) : QObject(), context_(context), msg_(QString()) {
- QFile tokenCache("usercache.dat");
- if(tokenCache.open(QIODevice::ReadOnly)) {
- context_->resumeFromState(tokenCache.readAll());
- }
- }
-
-public slots:
- void run() {
- connect(context_, &Context::activityChanged, this, &Helper::onActivityChanged);
- context_->silentSignIn();
- }
-
- void onFailed() {
- qDebug() << "Login failed";
- }
-
- void onActivityChanged(Katabasis::Activity activity) {
- if(activity == Katabasis::Activity::Idle) {
- switch(context_->validity()) {
- case Katabasis::Validity::None: {
- // account is gone, remove it.
- QFile::remove("usercache.dat");
- }
- break;
- case Katabasis::Validity::Assumed: {
- // this is basically a soft-failed refresh. do nothing.
- }
- break;
- case Katabasis::Validity::Certain: {
- // stuff got refreshed / signed in. Save.
- auto data = context_->saveState();
- QSaveFile tokenCache("usercache.dat");
- if(tokenCache.open(QIODevice::WriteOnly)) {
- tokenCache.write(context_->saveState());
- tokenCache.commit();
- }
- }
- break;
- }
- }
- }
-
-private:
- Context *context_;
- QString msg_;
-};
-
-int main(int argc, char *argv[]) {
- qInstallMessageHandler(myMessageOutput);
- QApplication a(argc, argv);
- QCoreApplication::setOrganizationName("MultiMC");
- QCoreApplication::setApplicationName("MultiMC");
- Context c;
- Helper helper(&c);
- MainWindow window(&c);
- window.show();
- QTimer::singleShot(0, &helper, &Helper::run);
- return a.exec();
-}
-
-#include "main.moc"
diff --git a/api/logic/minecraft/auth-msa/mainwindow.cpp b/api/logic/minecraft/auth-msa/mainwindow.cpp
deleted file mode 100644
index d4e18dc0..00000000
--- a/api/logic/minecraft/auth-msa/mainwindow.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-#include "mainwindow.h"
-#include "ui_mainwindow.h"
-#include <QDebug>
-
-#include <QDesktopServices>
-
-#include "BuildConfig.h"
-
-MainWindow::MainWindow(Context * context, QWidget *parent) :
- QMainWindow(parent),
- m_context(context),
- m_ui(new Ui::MainWindow)
-{
- m_ui->setupUi(this);
- connect(m_ui->signInButton_MSA, &QPushButton::clicked, this, &MainWindow::SignInMSAClicked);
- connect(m_ui->signInButton_Mojang, &QPushButton::clicked, this, &MainWindow::SignInMojangClicked);
- connect(m_ui->signOutButton, &QPushButton::clicked, this, &MainWindow::SignOutClicked);
- connect(m_ui->refreshButton, &QPushButton::clicked, this, &MainWindow::RefreshClicked);
-
- // connect(m_context, &Context::linkingSucceeded, this, &MainWindow::SignInSucceeded);
- // connect(m_context, &Context::linkingFailed, this, &MainWindow::SignInFailed);
- connect(m_context, &Context::activityChanged, this, &MainWindow::ActivityChanged);
- ActivityChanged(Katabasis::Activity::Idle);
-}
-
-MainWindow::~MainWindow() = default;
-
-void MainWindow::ActivityChanged(Katabasis::Activity activity) {
- switch(activity) {
- case Katabasis::Activity::Idle: {
- if(m_context->validity() != Katabasis::Validity::None) {
- m_ui->signInButton_Mojang->setEnabled(false);
- m_ui->signInButton_MSA->setEnabled(false);
- m_ui->signOutButton->setEnabled(true);
- m_ui->refreshButton->setEnabled(true);
- m_ui->statusBar->showMessage(QString("Hello %1!").arg(m_context->userName()));
- }
- else {
- m_ui->signInButton_Mojang->setEnabled(true);
- m_ui->signInButton_MSA->setEnabled(true);
- m_ui->signOutButton->setEnabled(false);
- m_ui->refreshButton->setEnabled(false);
- m_ui->statusBar->showMessage("Press the login button to start.");
- }
- }
- break;
- case Katabasis::Activity::LoggingIn: {
- m_ui->signInButton_Mojang->setEnabled(false);
- m_ui->signInButton_MSA->setEnabled(false);
- m_ui->signOutButton->setEnabled(false);
- m_ui->refreshButton->setEnabled(false);
- m_ui->statusBar->showMessage("Logging in...");
- }
- break;
- case Katabasis::Activity::LoggingOut: {
- m_ui->signInButton_Mojang->setEnabled(false);
- m_ui->signInButton_MSA->setEnabled(false);
- m_ui->signOutButton->setEnabled(false);
- m_ui->refreshButton->setEnabled(false);
- m_ui->statusBar->showMessage("Logging out...");
- }
- break;
- case Katabasis::Activity::Refreshing: {
- m_ui->signInButton_Mojang->setEnabled(false);
- m_ui->signInButton_MSA->setEnabled(false);
- m_ui->signOutButton->setEnabled(false);
- m_ui->refreshButton->setEnabled(false);
- m_ui->statusBar->showMessage("Refreshing login...");
- }
- break;
- }
-}
-
-void MainWindow::SignInMSAClicked() {
- qDebug() << "Sign In MSA";
- // signIn({{"prompt", "select_account"}})
- // FIXME: wrong. very wrong. this should not be operating on the current context
- m_context->signIn();
-}
-
-void MainWindow::SignInMojangClicked() {
- qDebug() << "Sign In Mojang";
- // signIn({{"prompt", "select_account"}})
- // FIXME: wrong. very wrong. this should not be operating on the current context
- m_context->signIn();
-}
-
-
-void MainWindow::SignOutClicked() {
- qDebug() << "Sign Out";
- m_context->signOut();
-}
-
-void MainWindow::RefreshClicked() {
- qDebug() << "Refresh";
- m_context->silentSignIn();
-}
diff --git a/api/logic/minecraft/auth-msa/mainwindow.h b/api/logic/minecraft/auth-msa/mainwindow.h
deleted file mode 100644
index abde52d8..00000000
--- a/api/logic/minecraft/auth-msa/mainwindow.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include <QMainWindow>
-#include <QScopedPointer>
-#include <QtNetwork>
-#include <katabasis/Bits.h>
-
-#include "context.h"
-
-namespace Ui {
-class MainWindow;
-}
-
-class MainWindow : public QMainWindow {
- Q_OBJECT
-
-public:
- explicit MainWindow(Context * context, QWidget *parent = nullptr);
- ~MainWindow() override;
-
-private slots:
- void SignInMojangClicked();
- void SignInMSAClicked();
-
- void SignOutClicked();
- void RefreshClicked();
-
- void ActivityChanged(Katabasis::Activity activity);
-
-private:
- Context* m_context;
- QScopedPointer<Ui::MainWindow> m_ui;
-};
-
diff --git a/api/logic/minecraft/auth-msa/mainwindow.ui b/api/logic/minecraft/auth-msa/mainwindow.ui
deleted file mode 100644
index 32b34128..00000000
--- a/api/logic/minecraft/auth-msa/mainwindow.ui
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MainWindow</class>
- <widget class="QMainWindow" name="MainWindow">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>1037</width>
- <height>511</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>SmartMapsClient</string>
- </property>
- <property name="dockNestingEnabled">
- <bool>true</bool>
- </property>
- <widget class="QWidget" name="centralWidget">
- <layout class="QGridLayout" name="gridLayout">
- <item row="1" column="3">
- <widget class="QPushButton" name="signInButton_Mojang">
- <property name="text">
- <string>SignIn Mojang</string>
- </property>
- </widget>
- </item>
- <item row="3" column="3">
- <widget class="Line" name="line">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- </widget>
- </item>
- <item row="1" column="0" rowspan="7" colspan="3">
- <widget class="QTreeView" name="accountView"/>
- </item>
- <item row="5" column="3">
- <widget class="QPushButton" name="refreshButton">
- <property name="text">
- <string>Refresh</string>
- </property>
- </widget>
- </item>
- <item row="2" column="3">
- <widget class="QPushButton" name="signInButton_MSA">
- <property name="text">
- <string>SignIn MSA</string>
- </property>
- </widget>
- </item>
- <item row="6" column="3">
- <widget class="QPushButton" name="signOutButton">
- <property name="text">
- <string>SignOut</string>
- </property>
- </widget>
- </item>
- <item row="4" column="3">
- <widget class="QPushButton" name="makeActiveButton">
- <property name="text">
- <string>Make Active</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <widget class="QStatusBar" name="statusBar"/>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/api/logic/minecraft/auth/AuthSession.cpp b/api/logic/minecraft/auth/AuthSession.cpp
deleted file mode 100644
index 4e858796..00000000
--- a/api/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/api/logic/minecraft/auth/AuthSession.h b/api/logic/minecraft/auth/AuthSession.h
deleted file mode 100644
index b397d9a1..00000000
--- a/api/logic/minecraft/auth/AuthSession.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#pragma once
-
-#include <QString>
-#include <QMultiMap>
-#include <memory>
-
-#include "multimc_logic_export.h"
-
-class MojangAccount;
-
-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;
- std::shared_ptr<MojangAccount> m_accountPtr;
-};
-
-typedef std::shared_ptr<AuthSession> AuthSessionPtr;
diff --git a/api/logic/minecraft/auth/MojangAccount.cpp b/api/logic/minecraft/auth/MojangAccount.cpp
deleted file mode 100644
index f5853fe3..00000000
--- a/api/logic/minecraft/auth/MojangAccount.cpp
+++ /dev/null
@@ -1,315 +0,0 @@
-/* Copyright 2013-2021 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(accountStatus() == Verified && !session->wants_online)
- {
- session->status = AuthSession::PlayableOffline;
- session->auth_server_online = false;
- fillSession(session);
- return nullptr;
- }
- else
- {
- 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();
- session->m_accountPtr = shared_from_this();
-}
-
-void MojangAccount::decrementUses()
-{
- Usable::decrementUses();
- if(!isInUse())
- {
- emit changed();
- qWarning() << "Account" << m_username << "is no longer in use.";
- }
-}
-
-void MojangAccount::incrementUses()
-{
- bool wasInUse = isInUse();
- Usable::incrementUses();
- if(!wasInUse)
- {
- emit changed();
- qWarning() << "Account" << m_username << "is now in use.";
- }
-}
-
-void MojangAccount::invalidateClientToken()
-{
- m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
- emit changed();
-}
diff --git a/api/logic/minecraft/auth/MojangAccount.h b/api/logic/minecraft/auth/MojangAccount.h
deleted file mode 100644
index 30a5f2ff..00000000
--- a/api/logic/minecraft/auth/MojangAccount.h
+++ /dev/null
@@ -1,182 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QString>
-#include <QList>
-#include <QJsonObject>
-#include <QPair>
-#include <QMap>
-
-#include <memory>
-#include "AuthSession.h"
-#include "Usable.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,
- public Usable,
- public std::enable_shared_from_this<MojangAccount>
-{
- 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());
- void invalidateClientToken();
-
-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;
-
-protected: /* methods */
-
- void incrementUses() override;
- void decrementUses() override;
-
-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/api/logic/minecraft/auth/MojangAccountList.cpp b/api/logic/minecraft/auth/MojangAccountList.cpp
deleted file mode 100644
index e584cb3b..00000000
--- a/api/logic/minecraft/auth/MojangAccountList.cpp
+++ /dev/null
@@ -1,468 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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)
-{
- int row = m_accounts.count();
- beginInsertRows(QModelIndex(), row, row);
- connect(account.get(), SIGNAL(changed()), SLOT(accountChanged()));
- m_accounts.append(account);
- endInsertRows();
- onListChanged();
-}
-
-void MojangAccountList::removeAccount(const QString &username)
-{
- int idx = 0;
- for (auto account : m_accounts)
- {
- if (account->username() == username)
- {
- beginRemoveRows(QModelIndex(), idx, idx);
- m_accounts.removeOne(account);
- endRemoveRows();
- return;
- }
- idx++;
- }
- onListChanged();
-}
-
-void MojangAccountList::removeAccount(QModelIndex index)
-{
- int row = index.row();
- if(index.isValid() && row >= 0 && row < m_accounts.size())
- {
- auto & account = m_accounts[row];
- if(account == m_activeAccount)
- {
- m_activeAccount = nullptr;
- onActiveChanged();
- }
- beginRemoveRows(QModelIndex(), row, row);
- m_accounts.removeAt(index.row());
- endRemoveRows();
- onListChanged();
- }
-}
-
-MojangAccountPtr MojangAccountList::activeAccount() const
-{
- return m_activeAccount;
-}
-
-void MojangAccountList::setActiveAccount(const QString &username)
-{
- if (username.isEmpty() && m_activeAccount)
- {
- int idx = 0;
- auto prevActiveAcc = m_activeAccount;
- m_activeAccount = nullptr;
- for (MojangAccountPtr account : m_accounts)
- {
- if (account == prevActiveAcc)
- {
- emit dataChanged(index(idx), index(idx));
- }
- idx ++;
- }
- onActiveChanged();
- }
- else
- {
- auto currentActiveAccount = m_activeAccount;
- int currentActiveAccountIdx = -1;
- auto newActiveAccount = m_activeAccount;
- int newActiveAccountIdx = -1;
- int idx = 0;
- for (MojangAccountPtr account : m_accounts)
- {
- if (account->username() == username)
- {
- newActiveAccount = account;
- newActiveAccountIdx = idx;
- }
- if(currentActiveAccount == account)
- {
- currentActiveAccountIdx = idx;
- }
- idx++;
- }
- if(currentActiveAccount != newActiveAccount)
- {
- emit dataChanged(index(currentActiveAccountIdx), index(currentActiveAccountIdx));
- emit dataChanged(index(newActiveAccountIdx), index(newActiveAccountIdx));
- m_activeAccount = newActiveAccount;
- 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 ? Qt::Checked : Qt::Unchecked;
- }
-
- 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 &) const
-{
- // Return count
- return count();
-}
-
-int MojangAccountList::columnCount(const QModelIndex &) 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/api/logic/minecraft/auth/MojangAccountList.h b/api/logic/minecraft/auth/MojangAccountList.h
deleted file mode 100644
index cc3a61a2..00000000
--- a/api/logic/minecraft/auth/MojangAccountList.h
+++ /dev/null
@@ -1,201 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "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/api/logic/minecraft/auth/YggdrasilTask.cpp b/api/logic/minecraft/auth/YggdrasilTask.cpp
deleted file mode 100644
index 0857b46b..00000000
--- a/api/logic/minecraft/auth/YggdrasilTask.cpp
+++ /dev/null
@@ -1,255 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "YggdrasilTask.h"
-#include "MojangAccount.h"
-
-#include <QObject>
-#include <QString>
-#include <QJsonObject>
-#include <QJsonDocument>
-#include <QNetworkReply>
-#include <QByteArray>
-
-#include <Env.h>
-
-#include <BuildConfig.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());
-
- QUrl reqUrl(BuildConfig.AUTH_BASE + getEndpoint());
- QNetworkRequest netRequest(reqUrl);
- netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
-
- QByteArray requestData = doc.toJson();
- m_netReply = ENV.qnam().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=\"https://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::ContentAccessDenied:
- 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/api/logic/minecraft/auth/YggdrasilTask.h b/api/logic/minecraft/auth/YggdrasilTask.h
deleted file mode 100644
index 8af2e132..00000000
--- a/api/logic/minecraft/auth/YggdrasilTask.h
+++ /dev/null
@@ -1,151 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <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);
- virtual ~YggdrasilTask() {};
-
- /**
- * 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/api/logic/minecraft/auth/flows/AuthenticateTask.cpp b/api/logic/minecraft/auth/flows/AuthenticateTask.cpp
deleted file mode 100644
index 2e8dc859..00000000
--- a/api/logic/minecraft/auth/flows/AuthenticateTask.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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/api/logic/minecraft/auth/flows/AuthenticateTask.h b/api/logic/minecraft/auth/flows/AuthenticateTask.h
deleted file mode 100644
index 4c14eec7..00000000
--- a/api/logic/minecraft/auth/flows/AuthenticateTask.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "../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/api/logic/minecraft/auth/flows/RefreshTask.cpp b/api/logic/minecraft/auth/flows/RefreshTask.cpp
deleted file mode 100644
index ecba178d..00000000
--- a/api/logic/minecraft/auth/flows/RefreshTask.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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/api/logic/minecraft/auth/flows/RefreshTask.h b/api/logic/minecraft/auth/flows/RefreshTask.h
deleted file mode 100644
index f0840dda..00000000
--- a/api/logic/minecraft/auth/flows/RefreshTask.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "../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/api/logic/minecraft/auth/flows/ValidateTask.cpp b/api/logic/minecraft/auth/flows/ValidateTask.cpp
deleted file mode 100644
index 6b3f0a65..00000000
--- a/api/logic/minecraft/auth/flows/ValidateTask.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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/api/logic/minecraft/auth/flows/ValidateTask.h b/api/logic/minecraft/auth/flows/ValidateTask.h
deleted file mode 100644
index 986c2e9f..00000000
--- a/api/logic/minecraft/auth/flows/ValidateTask.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * :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/api/logic/minecraft/gameoptions/GameOptions.cpp b/api/logic/minecraft/gameoptions/GameOptions.cpp
deleted file mode 100644
index e547b32a..00000000
--- a/api/logic/minecraft/gameoptions/GameOptions.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-#include "GameOptions.h"
-#include "FileSystem.h"
-#include <QDebug>
-#include <QSaveFile>
-
-namespace {
-bool load(const QString& path, std::vector<GameOptionItem> &contents, int & version)
-{
- contents.clear();
- QFile file(path);
- if (!file.open(QFile::ReadOnly))
- {
- qWarning() << "Failed to read options file.";
- return false;
- }
- version = 0;
- while(!file.atEnd())
- {
- auto line = file.readLine();
- if(line.endsWith('\n'))
- {
- line.chop(1);
- }
- auto separatorIndex = line.indexOf(':');
- if(separatorIndex == -1)
- {
- continue;
- }
- auto key = QString::fromUtf8(line.data(), separatorIndex);
- auto value = QString::fromUtf8(line.data() + separatorIndex + 1, line.size() - 1 - separatorIndex);
- qDebug() << "!!" << key << "!!";
- if(key == "version")
- {
- version = value.toInt();
- continue;
- }
- contents.emplace_back(GameOptionItem{key, value});
- }
- qDebug() << "Loaded" << path << "with version:" << version;
- return true;
-}
-bool save(const QString& path, std::vector<GameOptionItem> &mapping, int version)
-{
- QSaveFile out(path);
- if(!out.open(QIODevice::WriteOnly))
- {
- return false;
- }
- if(version != 0)
- {
- QString versionLine = QString("version:%1\n").arg(version);
- out.write(versionLine.toUtf8());
- }
- auto iter = mapping.begin();
- while (iter != mapping.end())
- {
- out.write(iter->key.toUtf8());
- out.write(":");
- out.write(iter->value.toUtf8());
- out.write("\n");
- iter++;
- }
- return out.commit();
-}
-}
-
-GameOptions::GameOptions(const QString& path):
- path(path)
-{
- reload();
-}
-
-QVariant GameOptions::headerData(int section, Qt::Orientation orientation, int role) const
-{
- if(role != Qt::DisplayRole)
- {
- return QAbstractListModel::headerData(section, orientation, role);
- }
- switch(section)
- {
- case 0:
- return tr("Key");
- case 1:
- return tr("Value");
- default:
- return QVariant();
- }
-}
-
-QVariant GameOptions::data(const QModelIndex& index, int role) const
-{
- if (!index.isValid())
- return QVariant();
-
- int row = index.row();
- int column = index.column();
-
- if (row < 0 || row >= int(contents.size()))
- return QVariant();
-
- switch (role)
- {
- case Qt::DisplayRole:
- if(column == 0)
- {
- return contents[row].key;
- }
- else
- {
- return contents[row].value;
- }
- default:
- return QVariant();
- }
- return QVariant();
-}
-
-int GameOptions::rowCount(const QModelIndex&) const
-{
- return contents.size();
-}
-
-int GameOptions::columnCount(const QModelIndex&) const
-{
- return 2;
-}
-
-bool GameOptions::isLoaded() const
-{
- return loaded;
-}
-
-bool GameOptions::reload()
-{
- beginResetModel();
- loaded = load(path, contents, version);
- endResetModel();
- return loaded;
-}
-
-bool GameOptions::save()
-{
- return ::save(path, contents, version);
-}
diff --git a/api/logic/minecraft/gameoptions/GameOptions.h b/api/logic/minecraft/gameoptions/GameOptions.h
deleted file mode 100644
index c6d25492..00000000
--- a/api/logic/minecraft/gameoptions/GameOptions.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include <map>
-#include <QString>
-#include <QAbstractListModel>
-
-struct GameOptionItem
-{
- QString key;
- QString value;
-};
-
-class GameOptions : public QAbstractListModel
-{
- Q_OBJECT
-public:
- explicit GameOptions(const QString& path);
- virtual ~GameOptions() = default;
-
- int rowCount(const QModelIndex &parent = QModelIndex()) const override;
- int columnCount(const QModelIndex & parent) const override;
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
-
- bool isLoaded() const;
- bool reload();
- bool save();
-
-private:
- std::vector<GameOptionItem> contents;
- bool loaded = false;
- QString path;
- int version = 0;
-};
diff --git a/api/logic/minecraft/launch/ClaimAccount.cpp b/api/logic/minecraft/launch/ClaimAccount.cpp
deleted file mode 100644
index a1180f0a..00000000
--- a/api/logic/minecraft/launch/ClaimAccount.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#include "ClaimAccount.h"
-#include <launch/LaunchTask.h>
-
-ClaimAccount::ClaimAccount(LaunchTask* parent, AuthSessionPtr session): LaunchStep(parent)
-{
- if(session->status == AuthSession::Status::PlayableOnline)
- {
- m_account = session->m_accountPtr;
- }
-}
-
-void ClaimAccount::executeTask()
-{
- if(m_account)
- {
- lock.reset(new UseLock(m_account));
- emitSucceeded();
- }
-}
-
-void ClaimAccount::finalize()
-{
- lock.reset();
-}
diff --git a/api/logic/minecraft/launch/ClaimAccount.h b/api/logic/minecraft/launch/ClaimAccount.h
deleted file mode 100644
index c5bd75f3..00000000
--- a/api/logic/minecraft/launch/ClaimAccount.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <minecraft/auth/MojangAccount.h>
-
-class ClaimAccount: public LaunchStep
-{
- Q_OBJECT
-public:
- explicit ClaimAccount(LaunchTask *parent, AuthSessionPtr session);
- virtual ~ClaimAccount() {};
-
- void executeTask() override;
- void finalize() override;
- bool canAbort() const override
- {
- return false;
- }
-private:
- std::unique_ptr<UseLock> lock;
- MojangAccountPtr m_account;
-};
diff --git a/api/logic/minecraft/launch/CreateGameFolders.cpp b/api/logic/minecraft/launch/CreateGameFolders.cpp
deleted file mode 100644
index 4081e72e..00000000
--- a/api/logic/minecraft/launch/CreateGameFolders.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "CreateGameFolders.h"
-#include "minecraft/MinecraftInstance.h"
-#include "launch/LaunchTask.h"
-#include "FileSystem.h"
-
-CreateGameFolders::CreateGameFolders(LaunchTask* parent): LaunchStep(parent)
-{
-}
-
-void CreateGameFolders::executeTask()
-{
- auto instance = m_parent->instance();
- std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
-
- if(!FS::ensureFolderPathExists(minecraftInstance->gameRoot()))
- {
- emit logLine("Couldn't create the main game folder", MessageLevel::Error);
- emitFailed(tr("Couldn't create the main game folder"));
- return;
- }
-
- // HACK: this is a workaround for MCL-3732 - 'server-resource-packs' folder is created.
- if(!FS::ensureFolderPathExists(FS::PathCombine(minecraftInstance->gameRoot(), "server-resource-packs")))
- {
- emit logLine("Couldn't create the 'server-resource-packs' folder", MessageLevel::Error);
- }
- emitSucceeded();
-}
diff --git a/api/logic/minecraft/launch/CreateGameFolders.h b/api/logic/minecraft/launch/CreateGameFolders.h
deleted file mode 100644
index 9c7d3c94..00000000
--- a/api/logic/minecraft/launch/CreateGameFolders.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <LoggedProcess.h>
-#include <minecraft/auth/AuthSession.h>
-
-// Create the main .minecraft for the instance and any other necessary folders
-class CreateGameFolders: public LaunchStep
-{
- Q_OBJECT
-public:
- explicit CreateGameFolders(LaunchTask *parent);
- virtual ~CreateGameFolders() {};
-
- virtual void executeTask();
- virtual bool canAbort() const
- {
- return false;
- }
-};
-
-
diff --git a/api/logic/minecraft/launch/DirectJavaLaunch.cpp b/api/logic/minecraft/launch/DirectJavaLaunch.cpp
deleted file mode 100644
index 2110384f..00000000
--- a/api/logic/minecraft/launch/DirectJavaLaunch.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "DirectJavaLaunch.h"
-#include <launch/LaunchTask.h>
-#include <minecraft/MinecraftInstance.h>
-#include <FileSystem.h>
-#include <Commandline.h>
-#include <QStandardPaths>
-
-DirectJavaLaunch::DirectJavaLaunch(LaunchTask *parent) : LaunchStep(parent)
-{
- connect(&m_process, &LoggedProcess::log, this, &DirectJavaLaunch::logLines);
- connect(&m_process, &LoggedProcess::stateChanged, this, &DirectJavaLaunch::on_state);
-}
-
-void DirectJavaLaunch::executeTask()
-{
- auto instance = m_parent->instance();
- std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
- QStringList args = minecraftInstance->javaArguments();
-
- args.append("-Djava.library.path=" + minecraftInstance->getNativePath());
-
- auto classPathEntries = minecraftInstance->getClassPath();
- args.append("-cp");
- QString classpath;
-#ifdef Q_OS_WIN32
- classpath = classPathEntries.join(';');
-#else
- classpath = classPathEntries.join(':');
-#endif
- args.append(classpath);
- args.append(minecraftInstance->getMainClass());
-
- QString allArgs = args.join(", ");
- emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::MultiMC);
-
- auto javaPath = FS::ResolveExecutable(instance->settings()->get("JavaPath").toString());
-
- m_process.setProcessEnvironment(instance->createEnvironment());
-
- // make detachable - this will keep the process running even if the object is destroyed
- m_process.setDetachable(true);
-
- auto mcArgs = minecraftInstance->processMinecraftArgs(m_session, m_serverToJoin);
- args.append(mcArgs);
-
- QString wrapperCommandStr = instance->getWrapperCommand().trimmed();
- if(!wrapperCommandStr.isEmpty())
- {
- auto wrapperArgs = Commandline::splitArgs(wrapperCommandStr);
- auto wrapperCommand = wrapperArgs.takeFirst();
- auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand);
- if (realWrapperCommand.isEmpty())
- {
- const char *reason = QT_TR_NOOP("The wrapper command \"%1\" couldn't be found.");
- emit logLine(QString(reason).arg(wrapperCommand), MessageLevel::Fatal);
- emitFailed(tr(reason).arg(wrapperCommand));
- return;
- }
- emit logLine("Wrapper command is:\n" + wrapperCommandStr + "\n\n", MessageLevel::MultiMC);
- args.prepend(javaPath);
- m_process.start(wrapperCommand, wrapperArgs + args);
- }
- else
- {
- m_process.start(javaPath, args);
- }
-}
-
-void DirectJavaLaunch::on_state(LoggedProcess::State state)
-{
- switch(state)
- {
- case LoggedProcess::FailedToStart:
- {
- //: Error message displayed if instance can't start
- const char *reason = QT_TR_NOOP("Could not launch minecraft!");
- emit logLine(reason, MessageLevel::Fatal);
- emitFailed(tr(reason));
- return;
- }
- case LoggedProcess::Aborted:
- case LoggedProcess::Crashed:
- {
- m_parent->setPid(-1);
- emitFailed(tr("Game crashed."));
- return;
- }
- case LoggedProcess::Finished:
- {
- m_parent->setPid(-1);
- // if the exit code wasn't 0, report this as a crash
- auto exitCode = m_process.exitCode();
- if(exitCode != 0)
- {
- emitFailed(tr("Game crashed."));
- return;
- }
- //FIXME: make this work again
- // m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(exitCode));
- // run post-exit
- emitSucceeded();
- break;
- }
- case LoggedProcess::Running:
- emit logLine(QString("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::MultiMC);
- m_parent->setPid(m_process.processId());
- m_parent->instance()->setLastLaunch();
- break;
- default:
- break;
- }
-}
-
-void DirectJavaLaunch::setWorkingDirectory(const QString &wd)
-{
- m_process.setWorkingDirectory(wd);
-}
-
-void DirectJavaLaunch::proceed()
-{
- // nil
-}
-
-bool DirectJavaLaunch::abort()
-{
- auto state = m_process.state();
- if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
- {
- m_process.kill();
- }
- return true;
-}
-
diff --git a/api/logic/minecraft/launch/DirectJavaLaunch.h b/api/logic/minecraft/launch/DirectJavaLaunch.h
deleted file mode 100644
index 58b119b8..00000000
--- a/api/logic/minecraft/launch/DirectJavaLaunch.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <LoggedProcess.h>
-#include <minecraft/auth/AuthSession.h>
-
-#include "MinecraftServerTarget.h"
-
-class DirectJavaLaunch: public LaunchStep
-{
- Q_OBJECT
-public:
- explicit DirectJavaLaunch(LaunchTask *parent);
- virtual ~DirectJavaLaunch() {};
-
- virtual void executeTask();
- virtual bool abort();
- virtual void proceed();
- virtual bool canAbort() const
- {
- return true;
- }
- void setWorkingDirectory(const QString &wd);
- void setAuthSession(AuthSessionPtr session)
- {
- m_session = session;
- }
-
- void setServerToJoin(MinecraftServerTargetPtr serverToJoin)
- {
- m_serverToJoin = std::move(serverToJoin);
- }
-
-private slots:
- void on_state(LoggedProcess::State state);
-
-private:
- LoggedProcess m_process;
- QString m_command;
- AuthSessionPtr m_session;
- MinecraftServerTargetPtr m_serverToJoin;
-};
-
diff --git a/api/logic/minecraft/launch/ExtractNatives.cpp b/api/logic/minecraft/launch/ExtractNatives.cpp
deleted file mode 100644
index d57499aa..00000000
--- a/api/logic/minecraft/launch/ExtractNatives.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ExtractNatives.h"
-#include <minecraft/MinecraftInstance.h>
-#include <launch/LaunchTask.h>
-
-#include <quazip.h>
-#include <quazipdir.h>
-#include "MMCZip.h"
-#include "FileSystem.h"
-#include <QDir>
-
-static QString replaceSuffix (QString target, const QString &suffix, const QString &replacement)
-{
- if (!target.endsWith(suffix))
- {
- return target;
- }
- target.resize(target.length() - suffix.length());
- return target + replacement;
-}
-
-static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack, bool nativeOpenAL, bool nativeGLFW)
-{
- QuaZip zip(source);
- if(!zip.open(QuaZip::mdUnzip))
- {
- return false;
- }
- QDir directory(targetFolder);
- if (!zip.goToFirstFile())
- {
- return false;
- }
- do
- {
- QString name = zip.getCurrentFileName();
- auto lowercase = name.toLower();
- if (nativeGLFW && name.contains("glfw")) {
- continue;
- }
- if (nativeOpenAL && name.contains("openal")) {
- continue;
- }
- if(applyJnilibHack)
- {
- name = replaceSuffix(name, ".jnilib", ".dylib");
- }
- QString absFilePath = directory.absoluteFilePath(name);
- if (!JlCompress::extractFile(&zip, "", absFilePath))
- {
- return false;
- }
- } while (zip.goToNextFile());
- zip.close();
- if(zip.getZipError()!=0)
- {
- return false;
- }
- return true;
-}
-
-void ExtractNatives::executeTask()
-{
- auto instance = m_parent->instance();
- std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
- auto toExtract = minecraftInstance->getNativeJars();
- if(toExtract.isEmpty())
- {
- emitSucceeded();
- return;
- }
- auto settings = minecraftInstance->settings();
- bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool();
- bool nativeGLFW = settings->get("UseNativeGLFW").toBool();
-
- auto outputPath = minecraftInstance->getNativePath();
- auto javaVersion = minecraftInstance->getJavaVersion();
- bool jniHackEnabled = javaVersion.major() >= 8;
- for(const auto &source: toExtract)
- {
- if(!unzipNatives(source, outputPath, jniHackEnabled, nativeOpenAL, nativeGLFW))
- {
- const char *reason = QT_TR_NOOP("Couldn't extract native jar '%1' to destination '%2'");
- emit logLine(QString(reason).arg(source, outputPath), MessageLevel::Fatal);
- emitFailed(tr(reason).arg(source, outputPath));
- }
- }
- emitSucceeded();
-}
-
-void ExtractNatives::finalize()
-{
- auto instance = m_parent->instance();
- QString target_dir = FS::PathCombine(instance->instanceRoot(), "natives/");
- QDir dir(target_dir);
- dir.removeRecursively();
-}
diff --git a/api/logic/minecraft/launch/ExtractNatives.h b/api/logic/minecraft/launch/ExtractNatives.h
deleted file mode 100644
index 094fcd6b..00000000
--- a/api/logic/minecraft/launch/ExtractNatives.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <memory>
-#include "minecraft/auth/AuthSession.h"
-
-// FIXME: temporary wrapper for existing task.
-class ExtractNatives: public LaunchStep
-{
- Q_OBJECT
-public:
- explicit ExtractNatives(LaunchTask *parent) : LaunchStep(parent){};
- virtual ~ExtractNatives(){};
-
- void executeTask() override;
- bool canAbort() const override
- {
- return false;
- }
- void finalize() override;
-};
-
-
diff --git a/api/logic/minecraft/launch/LauncherPartLaunch.cpp b/api/logic/minecraft/launch/LauncherPartLaunch.cpp
deleted file mode 100644
index ee469770..00000000
--- a/api/logic/minecraft/launch/LauncherPartLaunch.cpp
+++ /dev/null
@@ -1,218 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "LauncherPartLaunch.h"
-#include <QCoreApplication>
-#include <launch/LaunchTask.h>
-#include <minecraft/MinecraftInstance.h>
-#include <FileSystem.h>
-#include <Commandline.h>
-#include <QStandardPaths>
-#include "Env.h"
-
-LauncherPartLaunch::LauncherPartLaunch(LaunchTask *parent) : LaunchStep(parent)
-{
- connect(&m_process, &LoggedProcess::log, this, &LauncherPartLaunch::logLines);
- connect(&m_process, &LoggedProcess::stateChanged, this, &LauncherPartLaunch::on_state);
-}
-
-#ifdef Q_OS_WIN
-// returns 8.3 file format from long path
-#include <windows.h>
-QString shortPathName(const QString & file)
-{
- auto input = file.toStdWString();
- std::wstring output;
- long length = GetShortPathNameW(input.c_str(), NULL, 0);
- // NOTE: this resizing might seem weird...
- // when GetShortPathNameW fails, it returns length including null character
- // when it succeeds, it returns length excluding null character
- // See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx
- output.resize(length);
- GetShortPathNameW(input.c_str(),(LPWSTR)output.c_str(),length);
- output.resize(length-1);
- QString ret = QString::fromStdWString(output);
- return ret;
-}
-#endif
-
-// if the string survives roundtrip through local 8bit encoding...
-bool fitsInLocal8bit(const QString & string)
-{
- return string == QString::fromLocal8Bit(string.toLocal8Bit());
-}
-
-void LauncherPartLaunch::executeTask()
-{
- auto instance = m_parent->instance();
- std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
-
- m_launchScript = minecraftInstance->createLaunchScript(m_session, m_serverToJoin);
- QStringList args = minecraftInstance->javaArguments();
- QString allArgs = args.join(", ");
- emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::MultiMC);
-
- auto javaPath = FS::ResolveExecutable(instance->settings()->get("JavaPath").toString());
-
- m_process.setProcessEnvironment(instance->createEnvironment());
-
- // make detachable - this will keep the process running even if the object is destroyed
- m_process.setDetachable(true);
-
- auto classPath = minecraftInstance->getClassPath();
- classPath.prepend(FS::PathCombine(ENV.getJarsPath(), "NewLaunch.jar"));
-
- auto natPath = minecraftInstance->getNativePath();
-#ifdef Q_OS_WIN
- if (!fitsInLocal8bit(natPath))
- {
- args << "-Djava.library.path=" + shortPathName(natPath);
- }
- else
- {
- args << "-Djava.library.path=" + natPath;
- }
-#else
- args << "-Djava.library.path=" + natPath;
-#endif
-
- args << "-cp";
-#ifdef Q_OS_WIN
- QStringList processed;
- for(auto & item: classPath)
- {
- if (!fitsInLocal8bit(item))
- {
- processed << shortPathName(item);
- }
- else
- {
- processed << item;
- }
- }
- args << processed.join(';');
-#else
- args << classPath.join(':');
-#endif
- args << "org.multimc.EntryPoint";
-
- qDebug() << args.join(' ');
-
- QString wrapperCommandStr = instance->getWrapperCommand().trimmed();
- if(!wrapperCommandStr.isEmpty())
- {
- auto wrapperArgs = Commandline::splitArgs(wrapperCommandStr);
- auto wrapperCommand = wrapperArgs.takeFirst();
- auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand);
- if (realWrapperCommand.isEmpty())
- {
- const char *reason = QT_TR_NOOP("The wrapper command \"%1\" couldn't be found.");
- emit logLine(QString(reason).arg(wrapperCommand), MessageLevel::Fatal);
- emitFailed(tr(reason).arg(wrapperCommand));
- return;
- }
- emit logLine("Wrapper command is:\n" + wrapperCommandStr + "\n\n", MessageLevel::MultiMC);
- args.prepend(javaPath);
- m_process.start(wrapperCommand, wrapperArgs + args);
- }
- else
- {
- m_process.start(javaPath, args);
- }
-}
-
-void LauncherPartLaunch::on_state(LoggedProcess::State state)
-{
- switch(state)
- {
- case LoggedProcess::FailedToStart:
- {
- //: Error message displayed if instace can't start
- const char *reason = QT_TR_NOOP("Could not launch minecraft!");
- emit logLine(reason, MessageLevel::Fatal);
- emitFailed(tr(reason));
- return;
- }
- case LoggedProcess::Aborted:
- case LoggedProcess::Crashed:
- {
- m_parent->setPid(-1);
- emitFailed(tr("Game crashed."));
- return;
- }
- case LoggedProcess::Finished:
- {
- m_parent->setPid(-1);
- // if the exit code wasn't 0, report this as a crash
- auto exitCode = m_process.exitCode();
- if(exitCode != 0)
- {
- emitFailed(tr("Game crashed."));
- return;
- }
- //FIXME: make this work again
- // m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(exitCode));
- // run post-exit
- emitSucceeded();
- break;
- }
- case LoggedProcess::Running:
- emit logLine(QString("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::MultiMC);
- m_parent->setPid(m_process.processId());
- m_parent->instance()->setLastLaunch();
- // send the launch script to the launcher part
- m_process.write(m_launchScript.toUtf8());
-
- mayProceed = true;
- emit readyForLaunch();
- break;
- default:
- break;
- }
-}
-
-void LauncherPartLaunch::setWorkingDirectory(const QString &wd)
-{
- m_process.setWorkingDirectory(wd);
-}
-
-void LauncherPartLaunch::proceed()
-{
- if(mayProceed)
- {
- QString launchString("launch\n");
- m_process.write(launchString.toUtf8());
- mayProceed = false;
- }
-}
-
-bool LauncherPartLaunch::abort()
-{
- if(mayProceed)
- {
- mayProceed = false;
- QString launchString("abort\n");
- m_process.write(launchString.toUtf8());
- }
- else
- {
- auto state = m_process.state();
- if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
- {
- m_process.kill();
- }
- }
- return true;
-}
diff --git a/api/logic/minecraft/launch/LauncherPartLaunch.h b/api/logic/minecraft/launch/LauncherPartLaunch.h
deleted file mode 100644
index 6a7ee0e5..00000000
--- a/api/logic/minecraft/launch/LauncherPartLaunch.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <LoggedProcess.h>
-#include <minecraft/auth/AuthSession.h>
-
-#include "MinecraftServerTarget.h"
-
-class LauncherPartLaunch: public LaunchStep
-{
- Q_OBJECT
-public:
- explicit LauncherPartLaunch(LaunchTask *parent);
- virtual ~LauncherPartLaunch() {};
-
- virtual void executeTask();
- virtual bool abort();
- virtual void proceed();
- virtual bool canAbort() const
- {
- return true;
- }
- void setWorkingDirectory(const QString &wd);
- void setAuthSession(AuthSessionPtr session)
- {
- m_session = session;
- }
-
- void setServerToJoin(MinecraftServerTargetPtr serverToJoin)
- {
- m_serverToJoin = std::move(serverToJoin);
- }
-
-private slots:
- void on_state(LoggedProcess::State state);
-
-private:
- LoggedProcess m_process;
- QString m_command;
- AuthSessionPtr m_session;
- QString m_launchScript;
- MinecraftServerTargetPtr m_serverToJoin;
-
- bool mayProceed = false;
-};
diff --git a/api/logic/minecraft/launch/MinecraftServerTarget.cpp b/api/logic/minecraft/launch/MinecraftServerTarget.cpp
deleted file mode 100644
index 569273b6..00000000
--- a/api/logic/minecraft/launch/MinecraftServerTarget.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "MinecraftServerTarget.h"
-
-#include <QStringList>
-
-MinecraftServerTarget MinecraftServerTarget::parse(const QString &fullAddress) {
- QStringList split = fullAddress.split(":");
-
- // The logic below replicates the exact logic minecraft uses for parsing server addresses.
- // While the conversion is not lossless and eats errors, it ensures the same behavior
- // within Minecraft and MultiMC when entering server addresses.
- if (fullAddress.startsWith("["))
- {
- int bracket = fullAddress.indexOf("]");
- if (bracket > 0)
- {
- QString ipv6 = fullAddress.mid(1, bracket - 1);
- QString port = fullAddress.mid(bracket + 1).trimmed();
-
- if (port.startsWith(":") && !ipv6.isEmpty())
- {
- port = port.mid(1);
- split = QStringList({ ipv6, port });
- }
- else
- {
- split = QStringList({ipv6});
- }
- }
- }
-
- if (split.size() > 2)
- {
- split = QStringList({fullAddress});
- }
-
- QString realAddress = split[0];
-
- quint16 realPort = 25565;
- if (split.size() > 1)
- {
- bool ok;
- realPort = split[1].toUInt(&ok);
-
- if (!ok)
- {
- realPort = 25565;
- }
- }
-
- return MinecraftServerTarget { realAddress, realPort };
-}
diff --git a/api/logic/minecraft/launch/MinecraftServerTarget.h b/api/logic/minecraft/launch/MinecraftServerTarget.h
deleted file mode 100644
index 3c5786f4..00000000
--- a/api/logic/minecraft/launch/MinecraftServerTarget.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <memory>
-
-#include <QString>
-#include <multimc_logic_export.h>
-
-struct MinecraftServerTarget {
- QString address;
- quint16 port;
-
- static MULTIMC_LOGIC_EXPORT MinecraftServerTarget parse(const QString &fullAddress);
-};
-
-typedef std::shared_ptr<MinecraftServerTarget> MinecraftServerTargetPtr;
diff --git a/api/logic/minecraft/launch/ModMinecraftJar.cpp b/api/logic/minecraft/launch/ModMinecraftJar.cpp
deleted file mode 100644
index 93de9d59..00000000
--- a/api/logic/minecraft/launch/ModMinecraftJar.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ModMinecraftJar.h"
-#include "launch/LaunchTask.h"
-#include "MMCZip.h"
-#include "minecraft/OpSys.h"
-#include "FileSystem.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-
-void ModMinecraftJar::executeTask()
-{
- auto m_inst = std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
-
- if(!m_inst->getJarMods().size())
- {
- emitSucceeded();
- return;
- }
- // nuke obsolete stripped jar(s) if needed
- if(!FS::ensureFolderPathExists(m_inst->binRoot()))
- {
- emitFailed(tr("Couldn't create the bin folder for Minecraft.jar"));
- }
-
- auto finalJarPath = QDir(m_inst->binRoot()).absoluteFilePath("minecraft.jar");
- if(!removeJar())
- {
- emitFailed(tr("Couldn't remove stale jar file: %1").arg(finalJarPath));
- }
-
- // create temporary modded jar, if needed
- auto components = m_inst->getPackProfile();
- auto profile = components->getProfile();
- auto jarMods = m_inst->getJarMods();
- if(jarMods.size())
- {
- auto mainJar = profile->getMainJar();
- QStringList jars, temp1, temp2, temp3, temp4;
- mainJar->getApplicableFiles(currentSystem, jars, temp1, temp2, temp3, m_inst->getLocalLibraryPath());
- auto sourceJarPath = jars[0];
- if(!MMCZip::createModdedJar(sourceJarPath, finalJarPath, jarMods))
- {
- emitFailed(tr("Failed to create the custom Minecraft jar file."));
- return;
- }
- }
- emitSucceeded();
-}
-
-void ModMinecraftJar::finalize()
-{
- removeJar();
-}
-
-bool ModMinecraftJar::removeJar()
-{
- auto m_inst = std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
- auto finalJarPath = QDir(m_inst->binRoot()).absoluteFilePath("minecraft.jar");
- QFile finalJar(finalJarPath);
- if(finalJar.exists())
- {
- if(!finalJar.remove())
- {
- return false;
- }
- }
- return true;
-}
diff --git a/api/logic/minecraft/launch/ModMinecraftJar.h b/api/logic/minecraft/launch/ModMinecraftJar.h
deleted file mode 100644
index 081c6a91..00000000
--- a/api/logic/minecraft/launch/ModMinecraftJar.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <memory>
-
-class ModMinecraftJar: public LaunchStep
-{
- Q_OBJECT
-public:
- explicit ModMinecraftJar(LaunchTask *parent) : LaunchStep(parent) {};
- virtual ~ModMinecraftJar(){};
-
- virtual void executeTask() override;
- virtual bool canAbort() const override
- {
- return false;
- }
- void finalize() override;
-private:
- bool removeJar();
-};
diff --git a/api/logic/minecraft/launch/PrintInstanceInfo.cpp b/api/logic/minecraft/launch/PrintInstanceInfo.cpp
deleted file mode 100644
index 0b9611ad..00000000
--- a/api/logic/minecraft/launch/PrintInstanceInfo.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <fstream>
-#include <string>
-
-#include "PrintInstanceInfo.h"
-#include <launch/LaunchTask.h>
-
-#ifdef Q_OS_LINUX
-namespace {
-void probeProcCpuinfo(QStringList &log)
-{
- std::ifstream cpuin("/proc/cpuinfo");
- for (std::string line; std::getline(cpuin, line);)
- {
- if (strncmp(line.c_str(), "model name", 10) == 0)
- {
- log << QString::fromStdString(line.substr(13, std::string::npos));
- break;
- }
- }
-}
-
-void runLspci(QStringList &log)
-{
- // FIXME: fixed size buffers...
- char buff[512];
- int gpuline = -1;
- int cline = 0;
- FILE * lspci = popen("lspci -k", "r");
-
- if (!lspci)
- return;
-
- while (fgets(buff, 512, lspci) != NULL)
- {
- std::string str(buff);
- if (str.length() < 9)
- continue;
- if (str.substr(8, 3) == "VGA")
- {
- gpuline = cline;
- log << QString::fromStdString(str.substr(35, std::string::npos));
- }
- if (gpuline > -1 && gpuline != cline)
- {
- if (cline - gpuline < 3)
- {
- log << QString::fromStdString(str.substr(1, std::string::npos));
- }
- }
- cline++;
- }
- pclose(lspci);
-}
-
-void runGlxinfo(QStringList & log)
-{
- // FIXME: fixed size buffers...
- char buff[512];
- FILE *glxinfo = popen("glxinfo", "r");
- if (!glxinfo)
- return;
-
- while (fgets(buff, 512, glxinfo) != NULL)
- {
- if (strncmp(buff, "OpenGL version string:", 22) == 0)
- {
- log << QString::fromUtf8(buff);
- break;
- }
- }
- pclose(glxinfo);
-}
-
-}
-#endif
-
-void PrintInstanceInfo::executeTask()
-{
- auto instance = m_parent->instance();
- QStringList log;
-
-#ifdef Q_OS_LINUX
- ::probeProcCpuinfo(log);
- ::runLspci(log);
- ::runGlxinfo(log);
-#endif
-
- logLines(log, MessageLevel::MultiMC);
- logLines(instance->verboseDescription(m_session, m_serverToJoin), MessageLevel::MultiMC);
- emitSucceeded();
-}
diff --git a/api/logic/minecraft/launch/PrintInstanceInfo.h b/api/logic/minecraft/launch/PrintInstanceInfo.h
deleted file mode 100644
index fdc30f31..00000000
--- a/api/logic/minecraft/launch/PrintInstanceInfo.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <memory>
-#include "minecraft/auth/AuthSession.h"
-#include "minecraft/launch/MinecraftServerTarget.h"
-
-// FIXME: temporary wrapper for existing task.
-class PrintInstanceInfo: public LaunchStep
-{
- Q_OBJECT
-public:
- explicit PrintInstanceInfo(LaunchTask *parent, AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) :
- LaunchStep(parent), m_session(session), m_serverToJoin(serverToJoin) {};
- virtual ~PrintInstanceInfo(){};
-
- virtual void executeTask();
- virtual bool canAbort() const
- {
- return false;
- }
-private:
- AuthSessionPtr m_session;
- MinecraftServerTargetPtr m_serverToJoin;
-};
-
diff --git a/api/logic/minecraft/launch/ReconstructAssets.cpp b/api/logic/minecraft/launch/ReconstructAssets.cpp
deleted file mode 100644
index 4d206665..00000000
--- a/api/logic/minecraft/launch/ReconstructAssets.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ReconstructAssets.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-#include "minecraft/AssetsUtils.h"
-#include "launch/LaunchTask.h"
-
-void ReconstructAssets::executeTask()
-{
- auto instance = m_parent->instance();
- std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
- auto components = minecraftInstance->getPackProfile();
- auto profile = components->getProfile();
- auto assets = profile->getMinecraftAssets();
-
- if(!AssetsUtils::reconstructAssets(assets->id, minecraftInstance->resourcesDir()))
- {
- emit logLine("Failed to reconstruct Minecraft assets.", MessageLevel::Error);
- }
-
- emitSucceeded();
-}
diff --git a/api/logic/minecraft/launch/ReconstructAssets.h b/api/logic/minecraft/launch/ReconstructAssets.h
deleted file mode 100644
index 58d7febd..00000000
--- a/api/logic/minecraft/launch/ReconstructAssets.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <memory>
-
-class ReconstructAssets: public LaunchStep
-{
- Q_OBJECT
-public:
- explicit ReconstructAssets(LaunchTask *parent) : LaunchStep(parent){};
- virtual ~ReconstructAssets(){};
-
- void executeTask() override;
- bool canAbort() const override
- {
- return false;
- }
-};
diff --git a/api/logic/minecraft/launch/ScanModFolders.cpp b/api/logic/minecraft/launch/ScanModFolders.cpp
deleted file mode 100644
index 2a0e21b3..00000000
--- a/api/logic/minecraft/launch/ScanModFolders.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ScanModFolders.h"
-#include "launch/LaunchTask.h"
-#include "MMCZip.h"
-#include "minecraft/OpSys.h"
-#include "FileSystem.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/mod/ModFolderModel.h"
-
-void ScanModFolders::executeTask()
-{
- auto m_inst = std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
-
- auto loaders = m_inst->loaderModList();
- connect(loaders.get(), &ModFolderModel::updateFinished, this, &ScanModFolders::modsDone);
- if(!loaders->update()) {
- m_modsDone = true;
- }
-
- auto cores = m_inst->coreModList();
- connect(cores.get(), &ModFolderModel::updateFinished, this, &ScanModFolders::coreModsDone);
- if(!cores->update()) {
- m_coreModsDone = true;
- }
- checkDone();
-}
-
-void ScanModFolders::modsDone()
-{
- m_modsDone = true;
- checkDone();
-}
-
-void ScanModFolders::coreModsDone()
-{
- m_coreModsDone = true;
- checkDone();
-}
-
-void ScanModFolders::checkDone()
-{
- if(m_modsDone && m_coreModsDone) {
- emitSucceeded();
- }
-}
diff --git a/api/logic/minecraft/launch/ScanModFolders.h b/api/logic/minecraft/launch/ScanModFolders.h
deleted file mode 100644
index d5989170..00000000
--- a/api/logic/minecraft/launch/ScanModFolders.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <launch/LaunchStep.h>
-#include <memory>
-
-class ScanModFolders: public LaunchStep
-{
- Q_OBJECT
-public:
- explicit ScanModFolders(LaunchTask *parent) : LaunchStep(parent) {};
- virtual ~ScanModFolders(){};
-
- virtual void executeTask() override;
- virtual bool canAbort() const override
- {
- return false;
- }
-private slots:
- void coreModsDone();
- void modsDone();
-private:
- void checkDone();
-
-private: // DATA
- bool m_modsDone = false;
- bool m_coreModsDone = false;
-};
diff --git a/api/logic/minecraft/launch/VerifyJavaInstall.cpp b/api/logic/minecraft/launch/VerifyJavaInstall.cpp
deleted file mode 100644
index 657669af..00000000
--- a/api/logic/minecraft/launch/VerifyJavaInstall.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#include "VerifyJavaInstall.h"
-
-#include <launch/LaunchTask.h>
-#include <minecraft/MinecraftInstance.h>
-#include <minecraft/PackProfile.h>
-#include <minecraft/VersionFilterData.h>
-
-void VerifyJavaInstall::executeTask() {
- auto m_inst = std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
-
- auto javaVersion = m_inst->getJavaVersion();
- auto minecraftComponent = m_inst->getPackProfile()->getComponent("net.minecraft");
-
- // Java 16 requirement
- if (minecraftComponent->getReleaseDateTime() >= g_VersionFilterData.java16BeginsDate) {
- if (javaVersion.major() < 16) {
- emit logLine("Minecraft 21w19a and above require the use of Java 16",
- MessageLevel::Fatal);
- emitFailed(tr("Minecraft 21w19a and above require the use of Java 16"));
- return;
- }
- }
- // Java 8 requirement
- else if (minecraftComponent->getReleaseDateTime() >= g_VersionFilterData.java8BeginsDate) {
- if (javaVersion.major() < 8) {
- emit logLine("Minecraft 17w13a and above require the use of Java 8",
- MessageLevel::Fatal);
- emitFailed(tr("Minecraft 17w13a and above require the use of Java 8"));
- return;
- }
- }
-
- emitSucceeded();
-}
diff --git a/api/logic/minecraft/launch/VerifyJavaInstall.h b/api/logic/minecraft/launch/VerifyJavaInstall.h
deleted file mode 100644
index a553106d..00000000
--- a/api/logic/minecraft/launch/VerifyJavaInstall.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-#include <launch/LaunchStep.h>
-
-class VerifyJavaInstall : public LaunchStep {
- Q_OBJECT
-
-public:
- explicit VerifyJavaInstall(LaunchTask *parent) : LaunchStep(parent) {
- };
- ~VerifyJavaInstall() override = default;
-
- void executeTask() override;
- bool canAbort() const override {
- return false;
- }
-};
diff --git a/api/logic/minecraft/legacy/LegacyInstance.cpp b/api/logic/minecraft/legacy/LegacyInstance.cpp
deleted file mode 100644
index 9f9bda5a..00000000
--- a/api/logic/minecraft/legacy/LegacyInstance.cpp
+++ /dev/null
@@ -1,256 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <QFileInfo>
-#include <minecraft/launch/LauncherPartLaunch.h>
-#include <QDir>
-#include <settings/Setting.h>
-
-#include "LegacyInstance.h"
-
-#include "minecraft/legacy/LegacyModList.h"
-#include "minecraft/WorldList.h"
-#include <MMCZip.h>
-#include <FileSystem.h>
-
-LegacyInstance::LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
- : BaseInstance(globalSettings, settings, rootDir)
-{
- settings->registerSetting("NeedsRebuild", true);
- settings->registerSetting("ShouldUpdate", false);
- settings->registerSetting("JarVersion", QString());
- settings->registerSetting("IntendedJarVersion", QString());
- /*
- * custom base jar has no default. it is determined in code... see the accessor methods for
- *it
- *
- * for instances that DO NOT have the CustomBaseJar setting (legacy instances),
- * [.]minecraft/bin/mcbackup.jar is the default base jar
- */
- settings->registerSetting("UseCustomBaseJar", true);
- settings->registerSetting("CustomBaseJar", "");
-}
-
-QString LegacyInstance::mainJarToPreserve() const
-{
- bool customJar = m_settings->get("UseCustomBaseJar").toBool();
- if(customJar)
- {
- auto base = baseJar();
- if(QFile::exists(base))
- {
- return base;
- }
- }
- auto runnable = runnableJar();
- if(QFile::exists(runnable))
- {
- return runnable;
- }
- return QString();
-}
-
-
-QString LegacyInstance::baseJar() const
-{
- bool customJar = m_settings->get("UseCustomBaseJar").toBool();
- if (customJar)
- {
- return customBaseJar();
- }
- else
- return defaultBaseJar();
-}
-
-QString LegacyInstance::customBaseJar() const
-{
- QString value = m_settings->get("CustomBaseJar").toString();
- if (value.isNull() || value.isEmpty())
- {
- return defaultCustomBaseJar();
- }
- return value;
-}
-
-bool LegacyInstance::shouldUseCustomBaseJar() const
-{
- return m_settings->get("UseCustomBaseJar").toBool();
-}
-
-
-shared_qobject_ptr<Task> LegacyInstance::createUpdateTask(Net::Mode)
-{
- return nullptr;
-}
-
-std::shared_ptr<LegacyModList> LegacyInstance::jarModList() const
-{
- if (!jar_mod_list)
- {
- auto list = new LegacyModList(jarModsDir(), modListFile());
- jar_mod_list.reset(list);
- }
- jar_mod_list->update();
- return jar_mod_list;
-}
-
-QString LegacyInstance::gameRoot() const
-{
- QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
- QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft"));
-
- if (mcDir.exists() && !dotMCDir.exists())
- return mcDir.filePath();
- else
- return dotMCDir.filePath();
-}
-
-QString LegacyInstance::binRoot() const
-{
- return FS::PathCombine(gameRoot(), "bin");
-}
-
-QString LegacyInstance::jarModsDir() const
-{
- return FS::PathCombine(instanceRoot(), "instMods");
-}
-
-QString LegacyInstance::libDir() const
-{
- return FS::PathCombine(gameRoot(), "lib");
-}
-
-QString LegacyInstance::savesDir() const
-{
- return FS::PathCombine(gameRoot(), "saves");
-}
-
-QString LegacyInstance::loaderModsDir() const
-{
- return FS::PathCombine(gameRoot(), "mods");
-}
-
-QString LegacyInstance::coreModsDir() const
-{
- return FS::PathCombine(gameRoot(), "coremods");
-}
-
-QString LegacyInstance::resourceDir() const
-{
- return FS::PathCombine(gameRoot(), "resources");
-}
-QString LegacyInstance::texturePacksDir() const
-{
- return FS::PathCombine(gameRoot(), "texturepacks");
-}
-
-QString LegacyInstance::runnableJar() const
-{
- return FS::PathCombine(binRoot(), "minecraft.jar");
-}
-
-QString LegacyInstance::modListFile() const
-{
- return FS::PathCombine(instanceRoot(), "modlist");
-}
-
-QString LegacyInstance::instanceConfigFolder() const
-{
- return FS::PathCombine(gameRoot(), "config");
-}
-
-bool LegacyInstance::shouldRebuild() const
-{
- return m_settings->get("NeedsRebuild").toBool();
-}
-
-QString LegacyInstance::currentVersionId() const
-{
- return m_settings->get("JarVersion").toString();
-}
-
-QString LegacyInstance::intendedVersionId() const
-{
- return m_settings->get("IntendedJarVersion").toString();
-}
-
-bool LegacyInstance::shouldUpdate() const
-{
- QVariant var = settings()->get("ShouldUpdate");
- if (!var.isValid() || var.toBool() == false)
- {
- return intendedVersionId() != currentVersionId();
- }
- return true;
-}
-
-QString LegacyInstance::defaultBaseJar() const
-{
- return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
-}
-
-QString LegacyInstance::defaultCustomBaseJar() const
-{
- return FS::PathCombine(binRoot(), "mcbackup.jar");
-}
-
-std::shared_ptr<WorldList> LegacyInstance::worldList() const
-{
- if (!m_world_list)
- {
- m_world_list.reset(new WorldList(savesDir()));
- }
- return m_world_list;
-}
-
-QString LegacyInstance::typeName() const
-{
- return tr("Legacy");
-}
-
-QString LegacyInstance::getStatusbarDescription()
-{
- return tr("Instance from previous versions.");
-}
-
-QStringList LegacyInstance::verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
-{
- QStringList out;
-
- auto alltraits = traits();
- if(alltraits.size())
- {
- out << "Traits:";
- for (auto trait : alltraits)
- {
- out << " " + trait;
- }
- out << "";
- }
-
- QString windowParams;
- if (settings()->get("LaunchMaximized").toBool())
- {
- out << "Window size: max (if available)";
- }
- else
- {
- auto width = settings()->get("MinecraftWinWidth").toInt();
- auto height = settings()->get("MinecraftWinHeight").toInt();
- out << "Window size: " + QString::number(width) + " x " + QString::number(height);
- }
- out << "";
- return out;
-}
diff --git a/api/logic/minecraft/legacy/LegacyInstance.h b/api/logic/minecraft/legacy/LegacyInstance.h
deleted file mode 100644
index 325bac7a..00000000
--- a/api/logic/minecraft/legacy/LegacyInstance.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "BaseInstance.h"
-#include "launch/LaunchTask.h"
-
-#include "multimc_logic_export.h"
-
-class ModFolderModel;
-class LegacyModList;
-class WorldList;
-class Task;
-/*
- * WHY: Legacy instances - from MultiMC 3 and 4 - are here only to provide a way to upgrade them to the current format.
- */
-class MULTIMC_LOGIC_EXPORT LegacyInstance : public BaseInstance
-{
- Q_OBJECT
-public:
-
- explicit LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
-
- virtual void saveNow() override {}
-
- /// Path to the instance's minecraft.jar
- QString runnableJar() const;
-
- //! Path to the instance's modlist file.
- QString modListFile() const;
-
- ////// Directories //////
- QString libDir() const;
- QString savesDir() const;
- QString texturePacksDir() const;
- QString jarModsDir() const;
- QString loaderModsDir() const;
- QString coreModsDir() const;
- QString resourceDir() const;
- virtual QString instanceConfigFolder() const override;
- QString gameRoot() const override; // Path to the instance's minecraft directory.
- QString binRoot() const; // Path to the instance's minecraft bin directory.
-
- /// Get the curent base jar of this instance. By default, it's the
- /// versions/$version/$version.jar
- QString baseJar() const;
-
- /// the default base jar of this instance
- QString defaultBaseJar() const;
- /// the default custom base jar of this instance
- QString defaultCustomBaseJar() const;
-
- // the main jar that we actually want to keep when migrating the instance
- QString mainJarToPreserve() const;
-
- /*!
- * Whether or not custom base jar is used
- */
- bool shouldUseCustomBaseJar() const;
-
- /*!
- * The value of the custom base jar
- */
- QString customBaseJar() const;
-
- std::shared_ptr<LegacyModList> jarModList() const;
- std::shared_ptr<WorldList> worldList() const;
-
- /*!
- * Whether or not the instance's minecraft.jar needs to be rebuilt.
- * If this is true, when the instance launches, its jar mods will be
- * re-added to a fresh minecraft.jar file.
- */
- bool shouldRebuild() const;
-
- QString currentVersionId() const;
- QString intendedVersionId() const;
-
- QSet<QString> traits() const override
- {
- return {"legacy-instance", "texturepacks"};
- };
-
- virtual bool shouldUpdate() const;
- virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override;
-
- virtual QString typeName() const override;
-
- bool canLaunch() const override
- {
- return false;
- }
- bool canEdit() const override
- {
- return true;
- }
- bool canExport() const override
- {
- return false;
- }
- shared_qobject_ptr<LaunchTask> createLaunchTask(
- AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override
- {
- return nullptr;
- }
- IPathMatcher::Ptr getLogFileMatcher() override
- {
- return nullptr;
- }
- QString getLogFileRoot() override
- {
- return gameRoot();
- }
-
- QString getStatusbarDescription() override;
- QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override;
-
- QProcessEnvironment createEnvironment() override
- {
- return QProcessEnvironment();
- }
- QMap<QString, QString> getVariables() const override
- {
- return {};
- }
-protected:
- mutable std::shared_ptr<LegacyModList> jar_mod_list;
- mutable std::shared_ptr<WorldList> m_world_list;
-};
diff --git a/api/logic/minecraft/legacy/LegacyModList.cpp b/api/logic/minecraft/legacy/LegacyModList.cpp
deleted file mode 100644
index 7301eb8c..00000000
--- a/api/logic/minecraft/legacy/LegacyModList.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "LegacyModList.h"
-#include <FileSystem.h>
-#include <QString>
-#include <QDebug>
-
-LegacyModList::LegacyModList(const QString &dir, const QString &list_file)
- : m_dir(dir), m_list_file(list_file)
-{
- FS::ensureFolderPathExists(m_dir.absolutePath());
- m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs | QDir::NoSymLinks);
- m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
-}
-
- struct OrderItem
- {
- QString id;
- bool enabled = false;
- };
- typedef QList<OrderItem> OrderList;
-
-static void internalSort(QList<LegacyModList::Mod> &what)
-{
- auto predicate = [](const LegacyModList::Mod &left, const LegacyModList::Mod &right)
- {
- return left.fileName().localeAwareCompare(right.fileName()) < 0;
- };
- std::sort(what.begin(), what.end(), predicate);
-}
-
-static OrderList readListFile(const QString &m_list_file)
-{
- OrderList itemList;
- if (m_list_file.isNull() || m_list_file.isEmpty())
- return itemList;
-
- QFile textFile(m_list_file);
- if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text))
- return OrderList();
-
- QTextStream textStream;
- textStream.setAutoDetectUnicode(true);
- textStream.setDevice(&textFile);
- while (true)
- {
- QString line = textStream.readLine();
- if (line.isNull() || line.isEmpty())
- break;
- else
- {
- OrderItem it;
- it.enabled = !line.endsWith(".disabled");
- if (!it.enabled)
- {
- line.chop(9);
- }
- it.id = line;
- itemList.append(it);
- }
- }
- textFile.close();
- return itemList;
-}
-
-bool LegacyModList::update()
-{
- if (!m_dir.exists() || !m_dir.isReadable())
- return false;
-
- QList<Mod> orderedMods;
- QList<Mod> newMods;
- m_dir.refresh();
- auto folderContents = m_dir.entryInfoList();
-
- // first, process the ordered items (if any)
- OrderList listOrder = readListFile(m_list_file);
- for (auto item : listOrder)
- {
- QFileInfo infoEnabled(m_dir.filePath(item.id));
- QFileInfo infoDisabled(m_dir.filePath(item.id + ".disabled"));
- int idxEnabled = folderContents.indexOf(infoEnabled);
- int idxDisabled = folderContents.indexOf(infoDisabled);
- bool isEnabled;
- // if both enabled and disabled versions are present, it's a special case...
- if (idxEnabled >= 0 && idxDisabled >= 0)
- {
- // we only process the one we actually have in the order file.
- // and exactly as we have it.
- // THIS IS A CORNER CASE
- isEnabled = item.enabled;
- }
- else
- {
- // only one is present.
- // we pick the one that we found.
- // we assume the mod was enabled/disabled by external means
- isEnabled = idxEnabled >= 0;
- }
- int idx = isEnabled ? idxEnabled : idxDisabled;
- QFileInfo &info = isEnabled ? infoEnabled : infoDisabled;
- // if the file from the index file exists
- if (idx != -1)
- {
- // remove from the actual folder contents list
- folderContents.takeAt(idx);
- // append the new mod
- orderedMods.append(info);
- }
- }
- // if there are any untracked files... append them sorted at the end
- if (folderContents.size())
- {
- for (auto entry : folderContents)
- {
- newMods.append(entry);
- }
- internalSort(newMods);
- orderedMods.append(newMods);
- }
- mods.swap(orderedMods);
- return true;
-}
diff --git a/api/logic/minecraft/legacy/LegacyModList.h b/api/logic/minecraft/legacy/LegacyModList.h
deleted file mode 100644
index 8881d471..00000000
--- a/api/logic/minecraft/legacy/LegacyModList.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QList>
-#include <QString>
-#include <QDir>
-
-#include "multimc_logic_export.h"
-
-class MULTIMC_LOGIC_EXPORT LegacyModList
-{
-public:
-
- using Mod = QFileInfo;
-
- LegacyModList(const QString &dir, const QString &list_file = QString());
-
- /// Reloads the mod list and returns true if the list changed.
- bool update();
-
- QDir dir()
- {
- return m_dir;
- }
-
- const QList<Mod> & allMods()
- {
- return mods;
- }
-
-protected:
- QDir m_dir;
- QString m_list_file;
- QList<Mod> mods;
-};
diff --git a/api/logic/minecraft/legacy/LegacyUpgradeTask.cpp b/api/logic/minecraft/legacy/LegacyUpgradeTask.cpp
deleted file mode 100644
index a4ea60cd..00000000
--- a/api/logic/minecraft/legacy/LegacyUpgradeTask.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-#include "LegacyUpgradeTask.h"
-#include "settings/INISettingsObject.h"
-#include "FileSystem.h"
-#include "NullInstance.h"
-#include "pathmatcher/RegexpMatcher.h"
-#include <QtConcurrentRun>
-#include "LegacyInstance.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-#include "LegacyModList.h"
-#include "classparser.h"
-
-LegacyUpgradeTask::LegacyUpgradeTask(InstancePtr origInstance)
-{
- m_origInstance = origInstance;
-}
-
-void LegacyUpgradeTask::executeTask()
-{
- setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
-
- FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
- folderCopy.followSymlinks(true);
-
- m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy);
- connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &LegacyUpgradeTask::copyFinished);
- connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &LegacyUpgradeTask::copyAborted);
- m_copyFutureWatcher.setFuture(m_copyFuture);
-}
-
-static QString decideVersion(const QString& currentVersion, const QString& intendedVersion)
-{
- if(intendedVersion != currentVersion)
- {
- if(!intendedVersion.isEmpty())
- {
- return intendedVersion;
- }
- else if(!currentVersion.isEmpty())
- {
- return currentVersion;
- }
- }
- else
- {
- if(!intendedVersion.isEmpty())
- {
- return intendedVersion;
- }
- }
- return QString();
-}
-
-void LegacyUpgradeTask::copyFinished()
-{
- auto successful = m_copyFuture.result();
- if(!successful)
- {
- emitFailed(tr("Instance folder copy failed."));
- return;
- }
- auto legacyInst = std::dynamic_pointer_cast<LegacyInstance>(m_origInstance);
-
- auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
- instanceSettings->registerSetting("InstanceType", "Legacy");
- instanceSettings->set("InstanceType", "OneSix");
- // NOTE: this scope ensures the instance is fully saved before we emitSucceeded
- {
- MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
- inst.setName(m_instName);
-
- QString preferredVersionNumber = decideVersion(legacyInst->currentVersionId(), legacyInst->intendedVersionId());
- if(preferredVersionNumber.isNull())
- {
- // try to decide version based on the jar(s?)
- preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->baseJar());
- if(preferredVersionNumber.isNull())
- {
- preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->runnableJar());
- if(preferredVersionNumber.isNull())
- {
- emitFailed(tr("Could not decide Minecraft version."));
- return;
- }
- }
- }
- auto components = inst.getPackProfile();
- components->buildingFromScratch();
- components->setComponentVersion("net.minecraft", preferredVersionNumber, true);
-
- QString jarPath = legacyInst->mainJarToPreserve();
- if(!jarPath.isNull())
- {
- qDebug() << "Preserving base jar! : " << jarPath;
- // FIXME: handle case when the jar is unreadable?
- // TODO: check the hash, if it's the same as the upstream jar, do not do this
- components->installCustomJar(jarPath);
- }
-
- auto jarMods = legacyInst->jarModList()->allMods();
- for(auto & jarMod: jarMods)
- {
- QString modPath = jarMod.absoluteFilePath();
- qDebug() << "jarMod: " << modPath;
- components->installJarMods({modPath});
- }
-
- // remove all the extra garbage we no longer need
- auto removeAll = [&](const QString &root, const QStringList &things)
- {
- for(auto &thing : things)
- {
- auto removePath = FS::PathCombine(root, thing);
- QFileInfo stat(removePath);
- if(stat.isDir())
- {
- FS::deletePath(removePath);
- }
- else
- {
- QFile::remove(removePath);
- }
- }
- };
- QStringList rootRemovables = {"modlist", "version", "instMods"};
- QStringList mcRemovables = {"bin", "MultiMCLauncher.jar", "icon.png"};
- removeAll(inst.instanceRoot(), rootRemovables);
- removeAll(inst.gameRoot(), mcRemovables);
- }
- emitSucceeded();
-}
-
-void LegacyUpgradeTask::copyAborted()
-{
- emitFailed(tr("Instance folder copy has been aborted."));
- return;
-}
-
diff --git a/api/logic/minecraft/legacy/LegacyUpgradeTask.h b/api/logic/minecraft/legacy/LegacyUpgradeTask.h
deleted file mode 100644
index e35e43b7..00000000
--- a/api/logic/minecraft/legacy/LegacyUpgradeTask.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma once
-
-#include "InstanceTask.h"
-#include "multimc_logic_export.h"
-#include "net/NetJob.h"
-#include <QUrl>
-#include <QFuture>
-#include <QFutureWatcher>
-#include "settings/SettingsObject.h"
-#include "BaseVersion.h"
-#include "BaseInstance.h"
-
-
-class MULTIMC_LOGIC_EXPORT LegacyUpgradeTask : public InstanceTask
-{
- Q_OBJECT
-public:
- explicit LegacyUpgradeTask(InstancePtr origInstance);
-
-protected:
- //! Entry point for tasks.
- virtual void executeTask() override;
- void copyFinished();
- void copyAborted();
-
-private: /* data */
- InstancePtr m_origInstance;
- QFuture<bool> m_copyFuture;
- QFutureWatcher<bool> m_copyFutureWatcher;
-};
diff --git a/api/logic/minecraft/mod/LocalModParseTask.cpp b/api/logic/minecraft/mod/LocalModParseTask.cpp
deleted file mode 100644
index 0d6972fb..00000000
--- a/api/logic/minecraft/mod/LocalModParseTask.cpp
+++ /dev/null
@@ -1,467 +0,0 @@
-#include "LocalModParseTask.h"
-
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QJsonValue>
-#include <quazip.h>
-#include <quazipfile.h>
-#include <toml.h>
-
-#include "settings/INIFile.h"
-#include "FileSystem.h"
-
-namespace {
-
-// 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
-std::shared_ptr<ModDetails> ReadMCModInfo(QByteArray contents)
-{
- auto getInfoFromArray = [&](QJsonArray arr)->std::shared_ptr<ModDetails>
- {
- if (!arr.at(0).isObject()) {
- return nullptr;
- }
- std::shared_ptr<ModDetails> details = std::make_shared<ModDetails>();
- auto firstObj = arr.at(0).toObject();
- details->mod_id = firstObj.value("modid").toString();
- auto name = firstObj.value("name").toString();
- // NOTE: ignore stupid example mods copies where the author didn't even bother to change the name
- if(name != "Example Mod") {
- details->name = name;
- }
- details->version = firstObj.value("version").toString();
- details->updateurl = firstObj.value("updateUrl").toString();
- auto homeurl = firstObj.value("url").toString().trimmed();
- if(!homeurl.isEmpty())
- {
- // fix up url.
- if (!homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://"))
- {
- homeurl.prepend("http://");
- }
- }
- details->homeurl = homeurl;
- details->description = firstObj.value("description").toString();
- QJsonArray authors = firstObj.value("authorList").toArray();
- if (authors.size() == 0) {
- // FIXME: what is the format of this? is there any?
- authors = firstObj.value("authors").toArray();
- }
-
- for (auto author: authors)
- {
- details->authors.append(author.toString());
- }
- details->credits = firstObj.value("credits").toString();
- return details;
- };
- QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
- // this is the very old format that had just the array
- if (jsonDoc.isArray())
- {
- return 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 nullptr;
- }
- auto arrVal = jsonDoc.object().value("modlist");
- if(arrVal.isUndefined()) {
- arrVal = jsonDoc.object().value("modList");
- }
- if (arrVal.isArray())
- {
- return getInfoFromArray(arrVal.toArray());
- }
- }
- return nullptr;
-}
-
-// https://github.com/MinecraftForge/Documentation/blob/5ab4ba6cf9abc0ac4c0abd96ad187461aefd72af/docs/gettingstarted/structuring.md
-std::shared_ptr<ModDetails> ReadMCModTOML(QByteArray contents)
-{
- std::shared_ptr<ModDetails> details = std::make_shared<ModDetails>();
-
- char errbuf[200];
- // top-level table
- toml_table_t* tomlData = toml_parse(contents.data(), errbuf, sizeof(errbuf));
-
- if(!tomlData)
- {
- return nullptr;
- }
-
- // array defined by [[mods]]
- toml_array_t* tomlModsArr = toml_array_in(tomlData, "mods");
- // we only really care about the first element, since multiple mods in one file is not supported by us at the moment
- toml_table_t* tomlModsTable0 = toml_table_at(tomlModsArr, 0);
-
- // mandatory properties - always in [[mods]]
- toml_datum_t modIdDatum = toml_string_in(tomlModsTable0, "modId");
- if(modIdDatum.ok)
- {
- details->mod_id = modIdDatum.u.s;
- // library says this is required for strings
- free(modIdDatum.u.s);
- }
- toml_datum_t versionDatum = toml_string_in(tomlModsTable0, "version");
- if(versionDatum.ok)
- {
- details->version = versionDatum.u.s;
- free(versionDatum.u.s);
- }
- toml_datum_t displayNameDatum = toml_string_in(tomlModsTable0, "displayName");
- if(displayNameDatum.ok)
- {
- details->name = displayNameDatum.u.s;
- free(displayNameDatum.u.s);
- }
- toml_datum_t descriptionDatum = toml_string_in(tomlModsTable0, "description");
- if(descriptionDatum.ok)
- {
- details->description = descriptionDatum.u.s;
- free(descriptionDatum.u.s);
- }
-
- // optional properties - can be in the root table or [[mods]]
- toml_datum_t authorsDatum = toml_string_in(tomlData, "authors");
- QString authors = "";
- if(authorsDatum.ok)
- {
- authors = authorsDatum.u.s;
- free(authorsDatum.u.s);
- }
- else
- {
- authorsDatum = toml_string_in(tomlModsTable0, "authors");
- if(authorsDatum.ok)
- {
- authors = authorsDatum.u.s;
- free(authorsDatum.u.s);
- }
- }
- if(!authors.isEmpty())
- {
- // author information is stored as a string now, not a list
- details->authors.append(authors);
- }
- // is credits even used anywhere? including this for completion/parity with old data version
- toml_datum_t creditsDatum = toml_string_in(tomlData, "credits");
- QString credits = "";
- if(creditsDatum.ok)
- {
- authors = creditsDatum.u.s;
- free(creditsDatum.u.s);
- }
- else
- {
- creditsDatum = toml_string_in(tomlModsTable0, "credits");
- if(creditsDatum.ok)
- {
- credits = creditsDatum.u.s;
- free(creditsDatum.u.s);
- }
- }
- details->credits = credits;
- toml_datum_t homeurlDatum = toml_string_in(tomlData, "displayURL");
- QString homeurl = "";
- if(homeurlDatum.ok)
- {
- homeurl = homeurlDatum.u.s;
- free(homeurlDatum.u.s);
- }
- else
- {
- homeurlDatum = toml_string_in(tomlModsTable0, "displayURL");
- if(homeurlDatum.ok)
- {
- homeurl = homeurlDatum.u.s;
- free(homeurlDatum.u.s);
- }
- }
- if(!homeurl.isEmpty())
- {
- // fix up url.
- if (!homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://"))
- {
- homeurl.prepend("http://");
- }
- }
- details->homeurl = homeurl;
-
- // this seems to be recursive, so it should free everything
- toml_free(tomlData);
-
- return details;
-}
-
-// https://fabricmc.net/wiki/documentation:fabric_mod_json
-std::shared_ptr<ModDetails> ReadFabricModInfo(QByteArray contents)
-{
- QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
- auto object = jsonDoc.object();
- auto schemaVersion = object.contains("schemaVersion") ? object.value("schemaVersion").toInt(0) : 0;
-
- std::shared_ptr<ModDetails> details = std::make_shared<ModDetails>();
-
- details->mod_id = object.value("id").toString();
- details->version = object.value("version").toString();
-
- details->name = object.contains("name") ? object.value("name").toString() : details->mod_id;
- details->description = object.value("description").toString();
-
- if (schemaVersion >= 1)
- {
- QJsonArray authors = object.value("authors").toArray();
- for (auto author: authors)
- {
- if(author.isObject()) {
- details->authors.append(author.toObject().value("name").toString());
- }
- else {
- details->authors.append(author.toString());
- }
- }
-
- if (object.contains("contact"))
- {
- QJsonObject contact = object.value("contact").toObject();
-
- if (contact.contains("homepage"))
- {
- details->homeurl = contact.value("homepage").toString();
- }
- }
- }
- return details;
-}
-
-std::shared_ptr<ModDetails> ReadForgeInfo(QByteArray contents)
-{
- std::shared_ptr<ModDetails> details = std::make_shared<ModDetails>();
- // Read the data
- details->name = "Minecraft Forge";
- details->mod_id = "Forge";
- details->homeurl = "http://www.minecraftforge.net/forum/";
- INIFile ini;
- if (!ini.loadFile(contents))
- return details;
-
- 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();
-
- details->version = major + "." + minor + "." + revision + "." + build;
- return details;
-}
-
-std::shared_ptr<ModDetails> ReadLiteModInfo(QByteArray contents)
-{
- std::shared_ptr<ModDetails> details = std::make_shared<ModDetails>();
- QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
- auto object = jsonDoc.object();
- if (object.contains("name"))
- {
- details->mod_id = details->name = object.value("name").toString();
- }
- if (object.contains("version"))
- {
- details->version = object.value("version").toString("");
- }
- else
- {
- details->version = object.value("revision").toString("");
- }
- details->mcversion = object.value("mcversion").toString();
- auto author = object.value("author").toString();
- if(!author.isEmpty()) {
- details->authors.append(author);
- }
- details->description = object.value("description").toString();
- details->homeurl = object.value("url").toString();
- return details;
-}
-
-}
-
-LocalModParseTask::LocalModParseTask(int token, Mod::ModType type, const QFileInfo& modFile):
- m_token(token),
- m_type(type),
- m_modFile(modFile),
- m_result(new Result())
-{
-}
-
-void LocalModParseTask::processAsZip()
-{
- QuaZip zip(m_modFile.filePath());
- if (!zip.open(QuaZip::mdUnzip))
- return;
-
- QuaZipFile file(&zip);
-
- if (zip.setCurrentFile("META-INF/mods.toml"))
- {
- if (!file.open(QIODevice::ReadOnly))
- {
- zip.close();
- return;
- }
-
- m_result->details = ReadMCModTOML(file.readAll());
- file.close();
-
- // to replace ${file.jarVersion} with the actual version, as needed
- if (m_result->details && m_result->details->version == "${file.jarVersion}")
- {
- if (zip.setCurrentFile("META-INF/MANIFEST.MF"))
- {
- if (!file.open(QIODevice::ReadOnly))
- {
- zip.close();
- return;
- }
-
- // quick and dirty line-by-line parser
- auto manifestLines = file.readAll().split('\n');
- QString manifestVersion = "";
- for (auto &line : manifestLines)
- {
- if (QString(line).startsWith("Implementation-Version: "))
- {
- manifestVersion = QString(line).remove("Implementation-Version: ");
- break;
- }
- }
-
- // some mods use ${projectversion} in their build.gradle, causing this mess to show up in MANIFEST.MF
- // also keep with forge's behavior of setting the version to "NONE" if none is found
- if (manifestVersion.contains("task ':jar' property 'archiveVersion'") || manifestVersion == "")
- {
- manifestVersion = "NONE";
- }
-
- m_result->details->version = manifestVersion;
-
- file.close();
- }
- }
-
- zip.close();
- return;
- }
- else if (zip.setCurrentFile("mcmod.info"))
- {
- if (!file.open(QIODevice::ReadOnly))
- {
- zip.close();
- return;
- }
-
- m_result->details = ReadMCModInfo(file.readAll());
- file.close();
- zip.close();
- return;
- }
- else if (zip.setCurrentFile("fabric.mod.json"))
- {
- if (!file.open(QIODevice::ReadOnly))
- {
- zip.close();
- return;
- }
-
- m_result->details = ReadFabricModInfo(file.readAll());
- file.close();
- zip.close();
- return;
- }
- else if (zip.setCurrentFile("forgeversion.properties"))
- {
- if (!file.open(QIODevice::ReadOnly))
- {
- zip.close();
- return;
- }
-
- m_result->details = ReadForgeInfo(file.readAll());
- file.close();
- zip.close();
- return;
- }
-
- zip.close();
-}
-
-void LocalModParseTask::processAsFolder()
-{
- QFileInfo mcmod_info(FS::PathCombine(m_modFile.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;
- m_result->details = ReadMCModInfo(data);
- }
-}
-
-void LocalModParseTask::processAsLitemod()
-{
- QuaZip zip(m_modFile.filePath());
- if (!zip.open(QuaZip::mdUnzip))
- return;
-
- QuaZipFile file(&zip);
-
- if (zip.setCurrentFile("litemod.json"))
- {
- if (!file.open(QIODevice::ReadOnly))
- {
- zip.close();
- return;
- }
-
- m_result->details = ReadLiteModInfo(file.readAll());
- file.close();
- }
- zip.close();
-}
-
-void LocalModParseTask::run()
-{
- switch(m_type)
- {
- case Mod::MOD_ZIPFILE:
- processAsZip();
- break;
- case Mod::MOD_FOLDER:
- processAsFolder();
- break;
- case Mod::MOD_LITEMOD:
- processAsLitemod();
- break;
- default:
- break;
- }
- emit finished(m_token);
-}
diff --git a/api/logic/minecraft/mod/LocalModParseTask.h b/api/logic/minecraft/mod/LocalModParseTask.h
deleted file mode 100644
index 0f119ba6..00000000
--- a/api/logic/minecraft/mod/LocalModParseTask.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#pragma once
-#include <QRunnable>
-#include <QDebug>
-#include <QObject>
-#include "Mod.h"
-#include "ModDetails.h"
-
-class LocalModParseTask : public QObject, public QRunnable
-{
- Q_OBJECT
-public:
- struct Result {
- QString id;
- std::shared_ptr<ModDetails> details;
- };
- using ResultPtr = std::shared_ptr<Result>;
- ResultPtr result() const {
- return m_result;
- }
-
- LocalModParseTask(int token, Mod::ModType type, const QFileInfo & modFile);
- void run();
-
-signals:
- void finished(int token);
-
-private:
- void processAsZip();
- void processAsFolder();
- void processAsLitemod();
-
-private:
- int m_token;
- Mod::ModType m_type;
- QFileInfo m_modFile;
- ResultPtr m_result;
-};
diff --git a/api/logic/minecraft/mod/Mod.cpp b/api/logic/minecraft/mod/Mod.cpp
deleted file mode 100644
index b6bff29b..00000000
--- a/api/logic/minecraft/mod/Mod.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <QDir>
-#include <QString>
-
-#include "Mod.h"
-#include <QDebug>
-#include <FileSystem.h>
-
-namespace {
-
-ModDetails invalidDetails;
-
-}
-
-
-Mod::Mod(const QFileInfo &file)
-{
- repath(file);
- m_changedDateTime = file.lastModified();
-}
-
-void Mod::repath(const QFileInfo &file)
-{
- m_file = file;
- QString name_base = file.fileName();
-
- m_type = Mod::MOD_UNKNOWN;
-
- m_mmc_id = name_base;
-
- if (m_file.isDir())
- {
- m_type = MOD_FOLDER;
- m_name = name_base;
- }
- else if (m_file.isFile())
- {
- if (name_base.endsWith(".disabled"))
- {
- m_enabled = false;
- name_base.chop(9);
- }
- else
- {
- m_enabled = true;
- }
- 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;
- }
-}
-
-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;
- }
- repath(QFileInfo(path));
- m_enabled = value;
- return true;
-}
-
-bool Mod::destroy()
-{
- m_type = MOD_UNKNOWN;
- return FS::deletePath(m_file.filePath());
-}
-
-
-const ModDetails & Mod::details() const
-{
- if(!m_localDetails)
- return invalidDetails;
- return *m_localDetails;
-}
-
-
-QString Mod::version() const
-{
- return details().version;
-}
-
-QString Mod::name() const
-{
- auto & d = details();
- if(!d.name.isEmpty()) {
- return d.name;
- }
- return m_name;
-}
-
-QString Mod::homeurl() const
-{
- return details().homeurl;
-}
-
-QString Mod::description() const
-{
- return details().description;
-}
-
-QStringList Mod::authors() const
-{
- return details().authors;
-}
diff --git a/api/logic/minecraft/mod/Mod.h b/api/logic/minecraft/mod/Mod.h
deleted file mode 100644
index f77ffd41..00000000
--- a/api/logic/minecraft/mod/Mod.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <QFileInfo>
-#include <QDateTime>
-#include <QList>
-#include <memory>
-
-#include "multimc_logic_export.h"
-
-#include "ModDetails.h"
-
-
-
-class MULTIMC_LOGIC_EXPORT 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() = default;
- Mod(const QFileInfo &file);
-
- QFileInfo filename() const
- {
- return m_file;
- }
- QString mmc_id() const
- {
- return m_mmc_id;
- }
- ModType type() const
- {
- return m_type;
- }
- bool valid()
- {
- return m_type != MOD_UNKNOWN;
- }
-
- QDateTime dateTimeChanged() const
- {
- return m_changedDateTime;
- }
-
- bool enabled() const
- {
- return m_enabled;
- }
-
- const ModDetails &details() const;
-
- QString name() const;
- QString version() const;
- QString homeurl() const;
- QString description() const;
- QStringList authors() const;
-
- bool enable(bool value);
-
- // delete all the files of this mod
- bool destroy();
-
- // change the mod's filesystem path (used by mod lists for *MAGIC* purposes)
- void repath(const QFileInfo &file);
-
- bool shouldResolve() {
- return !m_resolving && !m_resolved;
- }
- bool isResolving() {
- return m_resolving;
- }
- int resolutionTicket()
- {
- return m_resolutionTicket;
- }
- void setResolving(bool resolving, int resolutionTicket) {
- m_resolving = resolving;
- m_resolutionTicket = resolutionTicket;
- }
- void finishResolvingWithDetails(std::shared_ptr<ModDetails> details){
- m_resolving = false;
- m_resolved = true;
- m_localDetails = details;
- }
-
-protected:
- QFileInfo m_file;
- QDateTime m_changedDateTime;
- QString m_mmc_id;
- QString m_name;
- bool m_enabled = true;
- bool m_resolving = false;
- bool m_resolved = false;
- int m_resolutionTicket = 0;
- ModType m_type = MOD_UNKNOWN;
- std::shared_ptr<ModDetails> m_localDetails;
-};
diff --git a/api/logic/minecraft/mod/ModDetails.h b/api/logic/minecraft/mod/ModDetails.h
deleted file mode 100644
index 6ab4aee7..00000000
--- a/api/logic/minecraft/mod/ModDetails.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-#include <QString>
-#include <QStringList>
-
-struct ModDetails
-{
- QString mod_id;
- QString name;
- QString version;
- QString mcversion;
- QString homeurl;
- QString updateurl;
- QString description;
- QStringList authors;
- QString credits;
-};
diff --git a/api/logic/minecraft/mod/ModFolderLoadTask.cpp b/api/logic/minecraft/mod/ModFolderLoadTask.cpp
deleted file mode 100644
index 88349877..00000000
--- a/api/logic/minecraft/mod/ModFolderLoadTask.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "ModFolderLoadTask.h"
-#include <QDebug>
-
-ModFolderLoadTask::ModFolderLoadTask(QDir dir) :
- m_dir(dir), m_result(new Result())
-{
-}
-
-void ModFolderLoadTask::run()
-{
- m_dir.refresh();
- for (auto entry : m_dir.entryInfoList())
- {
- Mod m(entry);
- m_result->mods[m.mmc_id()] = m;
- }
- emit succeeded();
-}
diff --git a/api/logic/minecraft/mod/ModFolderLoadTask.h b/api/logic/minecraft/mod/ModFolderLoadTask.h
deleted file mode 100644
index 8d720e65..00000000
--- a/api/logic/minecraft/mod/ModFolderLoadTask.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#pragma once
-#include <QRunnable>
-#include <QObject>
-#include <QDir>
-#include <QMap>
-#include "Mod.h"
-#include <memory>
-
-class ModFolderLoadTask : public QObject, public QRunnable
-{
- Q_OBJECT
-public:
- struct Result {
- QMap<QString, Mod> mods;
- };
- using ResultPtr = std::shared_ptr<Result>;
- ResultPtr result() const {
- return m_result;
- }
-
-public:
- ModFolderLoadTask(QDir dir);
- void run();
-signals:
- void succeeded();
-private:
- QDir m_dir;
- ResultPtr m_result;
-};
diff --git a/api/logic/minecraft/mod/ModFolderModel.cpp b/api/logic/minecraft/mod/ModFolderModel.cpp
deleted file mode 100644
index 031eebe5..00000000
--- a/api/logic/minecraft/mod/ModFolderModel.cpp
+++ /dev/null
@@ -1,554 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ModFolderModel.h"
-#include <FileSystem.h>
-#include <QMimeData>
-#include <QUrl>
-#include <QUuid>
-#include <QString>
-#include <QFileSystemWatcher>
-#include <QDebug>
-#include "ModFolderLoadTask.h"
-#include <QThreadPool>
-#include <algorithm>
-#include "LocalModParseTask.h"
-
-ModFolderModel::ModFolderModel(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);
- connect(m_watcher, SIGNAL(directoryChanged(QString)), this, SLOT(directoryChanged(QString)));
-}
-
-void ModFolderModel::startWatching()
-{
- if(is_watching)
- return;
-
- 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 ModFolderModel::stopWatching()
-{
- if(!is_watching)
- return;
-
- 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 ModFolderModel::update()
-{
- if (!isValid()) {
- return false;
- }
- if(m_update) {
- scheduled_update = true;
- return true;
- }
-
- auto task = new ModFolderLoadTask(m_dir);
- m_update = task->result();
- QThreadPool *threadPool = QThreadPool::globalInstance();
- connect(task, &ModFolderLoadTask::succeeded, this, &ModFolderModel::finishUpdate);
- threadPool->start(task);
- return true;
-}
-
-void ModFolderModel::finishUpdate()
-{
- QSet<QString> currentSet = modsIndex.keys().toSet();
- auto & newMods = m_update->mods;
- QSet<QString> newSet = newMods.keys().toSet();
-
- // see if the kept mods changed in some way
- {
- QSet<QString> kept = currentSet;
- kept.intersect(newSet);
- for(auto & keptMod: kept) {
- auto & newMod = newMods[keptMod];
- auto row = modsIndex[keptMod];
- auto & currentMod = mods[row];
- if(newMod.dateTimeChanged() == currentMod.dateTimeChanged()) {
- // no significant change, ignore...
- continue;
- }
- auto & oldMod = mods[row];
- if(oldMod.isResolving()) {
- activeTickets.remove(oldMod.resolutionTicket());
- }
- oldMod = newMod;
- resolveMod(mods[row]);
- emit dataChanged(index(row, 0), index(row, columnCount(QModelIndex()) - 1));
- }
- }
-
- // remove mods no longer present
- {
- QSet<QString> removed = currentSet;
- QList<int> removedRows;
- removed.subtract(newSet);
- for(auto & removedMod: removed) {
- removedRows.append(modsIndex[removedMod]);
- }
- std::sort(removedRows.begin(), removedRows.end(), std::greater<int>());
- for(auto iter = removedRows.begin(); iter != removedRows.end(); iter++) {
- int removedIndex = *iter;
- beginRemoveRows(QModelIndex(), removedIndex, removedIndex);
- auto removedIter = mods.begin() + removedIndex;
- if(removedIter->isResolving()) {
- activeTickets.remove(removedIter->resolutionTicket());
- }
- mods.erase(removedIter);
- endRemoveRows();
- }
- }
-
- // add new mods to the end
- {
- QSet<QString> added = newSet;
- added.subtract(currentSet);
- beginInsertRows(QModelIndex(), mods.size(), mods.size() + added.size() - 1);
- for(auto & addedMod: added) {
- mods.append(newMods[addedMod]);
- resolveMod(mods.last());
- }
- endInsertRows();
- }
-
- // update index
- {
- modsIndex.clear();
- int idx = 0;
- for(auto & mod: mods) {
- modsIndex[mod.mmc_id()] = idx;
- idx++;
- }
- }
-
- m_update.reset();
-
- emit updateFinished();
-
- if(scheduled_update) {
- scheduled_update = false;
- update();
- }
-}
-
-void ModFolderModel::resolveMod(Mod& m)
-{
- if(!m.shouldResolve()) {
- return;
- }
-
- auto task = new LocalModParseTask(nextResolutionTicket, m.type(), m.filename());
- auto result = task->result();
- result->id = m.mmc_id();
- activeTickets.insert(nextResolutionTicket, result);
- m.setResolving(true, nextResolutionTicket);
- nextResolutionTicket++;
- QThreadPool *threadPool = QThreadPool::globalInstance();
- connect(task, &LocalModParseTask::finished, this, &ModFolderModel::finishModParse);
- threadPool->start(task);
-}
-
-void ModFolderModel::finishModParse(int token)
-{
- auto iter = activeTickets.find(token);
- if(iter == activeTickets.end()) {
- return;
- }
- auto result = *iter;
- activeTickets.remove(token);
- int row = modsIndex[result->id];
- auto & mod = mods[row];
- mod.finishResolvingWithDetails(result->details);
- emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1));
-}
-
-void ModFolderModel::disableInteraction(bool disabled)
-{
- if (interaction_disabled == disabled) {
- return;
- }
- interaction_disabled = disabled;
- if(size()) {
- emit dataChanged(index(0), index(size() - 1));
- }
-}
-
-void ModFolderModel::directoryChanged(QString path)
-{
- update();
-}
-
-bool ModFolderModel::isValid()
-{
- return m_dir.exists() && m_dir.isReadable();
-}
-
-// FIXME: this does not take disabled mod (with extra .disable extension) into account...
-bool ModFolderModel::installMod(const QString &filename)
-{
- if(interaction_disabled) {
- return false;
- }
-
- // NOTE: fix for GH-1178: remove trailing slash to avoid issues with using the empty result of QFileInfo::fileName
- auto originalPath = FS::NormalizePath(filename);
- QFileInfo fileinfo(originalPath);
-
- if (!fileinfo.exists() || !fileinfo.isReadable())
- {
- qWarning() << "Caught attempt to install non-existing file or file-like object:" << originalPath;
- return false;
- }
- qDebug() << "installing: " << fileinfo.absoluteFilePath();
-
- Mod installedMod(fileinfo);
- if (!installedMod.valid())
- {
- qDebug() << originalPath << "is not a valid mod. Ignoring it.";
- return false;
- }
-
- auto type = installedMod.type();
- if (type == Mod::MOD_UNKNOWN)
- {
- qDebug() << "Cannot recognize mod type of" << originalPath << ", ignoring it.";
- return false;
- }
-
- auto newpath = FS::NormalizePath(FS::PathCombine(m_dir.path(), fileinfo.fileName()));
- if(originalPath == newpath)
- {
- qDebug() << "Overwriting the mod (" << originalPath << ") with itself makes no sense...";
- return false;
- }
-
- if (type == Mod::MOD_SINGLEFILE || type == Mod::MOD_ZIPFILE || type == Mod::MOD_LITEMOD)
- {
- if(QFile::exists(newpath) || QFile::exists(newpath + QString(".disabled")))
- {
- if(!QFile::remove(newpath))
- {
- // FIXME: report error in a user-visible way
- qWarning() << "Copy from" << originalPath << "to" << newpath << "has failed.";
- return false;
- }
- qDebug() << newpath << "has been deleted.";
- }
- if (!QFile::copy(fileinfo.filePath(), newpath))
- {
- qWarning() << "Copy from" << originalPath << "to" << newpath << "has failed.";
- // FIXME: report error in a user-visible way
- return false;
- }
- FS::updateTimestamp(newpath);
- installedMod.repath(newpath);
- update();
- return true;
- }
- else if (type == Mod::MOD_FOLDER)
- {
- QString from = fileinfo.filePath();
- if(QFile::exists(newpath))
- {
- qDebug() << "Ignoring folder " << from << ", it would merge with " << newpath;
- return false;
- }
-
- if (!FS::copy(from, newpath)())
- {
- qWarning() << "Copy of folder from" << originalPath << "to" << newpath << "has (potentially partially) failed.";
- return false;
- }
- installedMod.repath(newpath);
- update();
- return true;
- }
- return false;
-}
-
-bool ModFolderModel::setModStatus(const QModelIndexList& indexes, ModStatusAction enable)
-{
- if(interaction_disabled) {
- return false;
- }
-
- if(indexes.isEmpty())
- return true;
-
- for (auto index: indexes)
- {
- if(index.column() != 0) {
- continue;
- }
- setModStatus(index.row(), enable);
- }
- return true;
-}
-
-bool ModFolderModel::deleteMods(const QModelIndexList& indexes)
-{
- if(interaction_disabled) {
- return false;
- }
-
- if(indexes.isEmpty())
- return true;
-
- for (auto i: indexes)
- {
- Mod &m = mods[i.row()];
- m.destroy();
- }
- return true;
-}
-
-int ModFolderModel::columnCount(const QModelIndex &parent) const
-{
- return NUM_COLUMNS;
-}
-
-QVariant ModFolderModel::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: {
- switch(mods[row].type()) {
- case Mod::MOD_FOLDER:
- return tr("Folder");
- case Mod::MOD_SINGLEFILE:
- return tr("File");
- default:
- break;
- }
- return mods[row].version();
- }
- case DateColumn:
- return mods[row].dateTimeChanged();
-
- 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 ModFolderModel::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)
- {
- return setModStatus(index.row(), Toggle);
- }
- return false;
-}
-
-bool ModFolderModel::setModStatus(int row, ModFolderModel::ModStatusAction action)
-{
- if(row < 0 || row >= mods.size()) {
- return false;
- }
-
- auto &mod = mods[row];
- bool desiredStatus;
- switch(action) {
- case Enable:
- desiredStatus = true;
- break;
- case Disable:
- desiredStatus = false;
- break;
- case Toggle:
- default:
- desiredStatus = !mod.enabled();
- break;
- }
-
- if(desiredStatus == mod.enabled()) {
- return true;
- }
-
- // preserve the row, but change its ID
- auto oldId = mod.mmc_id();
- if(!mod.enable(!mod.enabled())) {
- return false;
- }
- auto newId = mod.mmc_id();
- if(modsIndex.contains(newId)) {
- // NOTE: this could handle a corner case, where we are overwriting a file, because the same 'mod' exists both enabled and disabled
- // But is it necessary?
- }
- modsIndex.remove(oldId);
- modsIndex[newId] = row;
- emit dataChanged(index(row, 0), index(row, columnCount(QModelIndex()) - 1));
- return true;
-}
-
-QVariant ModFolderModel::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");
- case DateColumn:
- return tr("Last changed");
- 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.");
- case DateColumn:
- return tr("The date and time this mod was last changed (or added).");
- default:
- return QVariant();
- }
- default:
- return QVariant();
- }
- return QVariant();
-}
-
-Qt::ItemFlags ModFolderModel::flags(const QModelIndex &index) const
-{
- Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
- auto flags = defaultFlags;
- if(interaction_disabled) {
- flags &= ~Qt::ItemIsDropEnabled;
- }
- else
- {
- flags |= Qt::ItemIsDropEnabled;
- if(index.isValid()) {
- flags |= Qt::ItemIsUserCheckable;
- }
- }
- return flags;
-}
-
-Qt::DropActions ModFolderModel::supportedDropActions() const
-{
- // copy from outside, move from within and other mod lists
- return Qt::CopyAction | Qt::MoveAction;
-}
-
-QStringList ModFolderModel::mimeTypes() const
-{
- QStringList types;
- types << "text/uri-list";
- return types;
-}
-
-bool ModFolderModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int, int, const QModelIndex&)
-{
- 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())
- {
- auto urls = data->urls();
- for (auto url : urls)
- {
- // only local files may be dropped...
- if (!url.isLocalFile())
- {
- continue;
- }
- // TODO: implement not only copy, but also move
- // FIXME: handle errors here
- installMod(url.toLocalFile());
- }
- return true;
- }
- return false;
-}
diff --git a/api/logic/minecraft/mod/ModFolderModel.h b/api/logic/minecraft/mod/ModFolderModel.h
deleted file mode 100644
index b0a76121..00000000
--- a/api/logic/minecraft/mod/ModFolderModel.h
+++ /dev/null
@@ -1,149 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QList>
-#include <QMap>
-#include <QSet>
-#include <QString>
-#include <QDir>
-#include <QAbstractListModel>
-
-#include "Mod.h"
-
-#include "multimc_logic_export.h"
-#include "ModFolderLoadTask.h"
-#include "LocalModParseTask.h"
-
-class LegacyInstance;
-class BaseInstance;
-class QFileSystemWatcher;
-
-/**
- * A legacy mod list.
- * Backed by a folder.
- */
-class MULTIMC_LOGIC_EXPORT ModFolderModel : public QAbstractListModel
-{
- Q_OBJECT
-public:
- enum Columns
- {
- ActiveColumn = 0,
- NameColumn,
- VersionColumn,
- DateColumn,
- NUM_COLUMNS
- };
- enum ModStatusAction {
- Disable,
- Enable,
- Toggle
- };
- ModFolderModel(const QString &dir);
-
- virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
- Qt::DropActions supportedDropActions() const override;
-
- /// flags, mostly to support drag&drop
- virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
- QStringList mimeTypes() const override;
- bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) override;
-
- virtual int rowCount(const QModelIndex &) const override
- {
- return size();
- }
-
- virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
- virtual int columnCount(const QModelIndex &parent) const override;
-
- size_t size() const
- {
- return mods.size();
- }
- ;
- bool empty() const
- {
- return size() == 0;
- }
- Mod &operator[](size_t index)
- {
- return mods[index];
- }
- const Mod &at(size_t index) const
- {
- return mods.at(index);
- }
-
- /// Reloads the mod list and returns true if the list changed.
- bool update();
-
- /**
- * Adds the given mod to the list at the given index - if the list supports custom ordering
- */
- bool installMod(const QString& filename);
-
- /// Deletes all the selected mods
- bool deleteMods(const QModelIndexList &indexes);
-
- /// Enable or disable listed mods
- bool setModStatus(const QModelIndexList &indexes, ModStatusAction action);
-
- void startWatching();
- void stopWatching();
-
- bool isValid();
-
- QDir dir()
- {
- return m_dir;
- }
-
- const QList<Mod> & allMods()
- {
- return mods;
- }
-
-public slots:
- void disableInteraction(bool disabled);
-
-private
-slots:
- void directoryChanged(QString path);
- void finishUpdate();
- void finishModParse(int token);
-
-signals:
- void updateFinished();
-
-private:
- void resolveMod(Mod& m);
- bool setModStatus(int index, ModStatusAction action);
-
-protected:
- QFileSystemWatcher *m_watcher;
- bool is_watching = false;
- ModFolderLoadTask::ResultPtr m_update;
- bool scheduled_update = false;
- bool interaction_disabled = false;
- QDir m_dir;
- QMap<QString, int> modsIndex;
- QMap<int, LocalModParseTask::ResultPtr> activeTickets;
- int nextResolutionTicket = 0;
- QList<Mod> mods;
-};
diff --git a/api/logic/minecraft/mod/ModFolderModel_test.cpp b/api/logic/minecraft/mod/ModFolderModel_test.cpp
deleted file mode 100644
index 76f16ed5..00000000
--- a/api/logic/minecraft/mod/ModFolderModel_test.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-
-#include <QTest>
-#include <QTemporaryDir>
-#include "TestUtil.h"
-
-#include "FileSystem.h"
-#include "minecraft/mod/ModFolderModel.h"
-
-class ModFolderModelTest : public QObject
-{
- Q_OBJECT
-
-private
-slots:
- // test for GH-1178 - install a folder with files to a mod list
- void test_1178()
- {
- // source
- QString source = QFINDTESTDATA("data/test_folder");
-
- // sanity check
- QVERIFY(!source.endsWith('/'));
-
- auto verify = [](QString path)
- {
- QDir target_dir(FS::PathCombine(path, "test_folder"));
- QVERIFY(target_dir.entryList().contains("pack.mcmeta"));
- QVERIFY(target_dir.entryList().contains("assets"));
- };
-
- // 1. test with no trailing /
- {
- QString folder = source;
- QTemporaryDir tempDir;
- ModFolderModel m(tempDir.path());
- m.installMod(folder);
- verify(tempDir.path());
- }
-
- // 2. test with trailing /
- {
- QString folder = source + '/';
- QTemporaryDir tempDir;
- ModFolderModel m(tempDir.path());
- m.installMod(folder);
- verify(tempDir.path());
- }
- }
-};
-
-QTEST_GUILESS_MAIN(ModFolderModelTest)
-
-#include "ModFolderModel_test.moc"
diff --git a/api/logic/minecraft/mod/ResourcePackFolderModel.cpp b/api/logic/minecraft/mod/ResourcePackFolderModel.cpp
deleted file mode 100644
index f3d7f566..00000000
--- a/api/logic/minecraft/mod/ResourcePackFolderModel.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "ResourcePackFolderModel.h"
-
-ResourcePackFolderModel::ResourcePackFolderModel(const QString &dir) : ModFolderModel(dir) {
-}
-
-QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orientation, int role) const {
- if (role == Qt::ToolTipRole) {
- switch (section) {
- case ActiveColumn:
- return tr("Is the resource pack enabled?");
- case NameColumn:
- return tr("The name of the resource pack.");
- case VersionColumn:
- return tr("The version of the resource pack.");
- case DateColumn:
- return tr("The date and time this resource pack was last changed (or added).");
- default:
- return QVariant();
- }
- }
-
- return ModFolderModel::headerData(section, orientation, role);
-}
diff --git a/api/logic/minecraft/mod/ResourcePackFolderModel.h b/api/logic/minecraft/mod/ResourcePackFolderModel.h
deleted file mode 100644
index 47eb4bb2..00000000
--- a/api/logic/minecraft/mod/ResourcePackFolderModel.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include "ModFolderModel.h"
-
-class MULTIMC_LOGIC_EXPORT ResourcePackFolderModel : public ModFolderModel
-{
- Q_OBJECT
-
-public:
- explicit ResourcePackFolderModel(const QString &dir);
-
- QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
-};
diff --git a/api/logic/minecraft/mod/TexturePackFolderModel.cpp b/api/logic/minecraft/mod/TexturePackFolderModel.cpp
deleted file mode 100644
index d5956da1..00000000
--- a/api/logic/minecraft/mod/TexturePackFolderModel.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "TexturePackFolderModel.h"
-
-TexturePackFolderModel::TexturePackFolderModel(const QString &dir) : ModFolderModel(dir) {
-}
-
-QVariant TexturePackFolderModel::headerData(int section, Qt::Orientation orientation, int role) const {
- if (role == Qt::ToolTipRole) {
- switch (section) {
- case ActiveColumn:
- return tr("Is the texture pack enabled?");
- case NameColumn:
- return tr("The name of the texture pack.");
- case VersionColumn:
- return tr("The version of the texture pack.");
- case DateColumn:
- return tr("The date and time this texture pack was last changed (or added).");
- default:
- return QVariant();
- }
- }
-
- return ModFolderModel::headerData(section, orientation, role);
-}
diff --git a/api/logic/minecraft/mod/TexturePackFolderModel.h b/api/logic/minecraft/mod/TexturePackFolderModel.h
deleted file mode 100644
index d773b17b..00000000
--- a/api/logic/minecraft/mod/TexturePackFolderModel.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include "ModFolderModel.h"
-
-class MULTIMC_LOGIC_EXPORT TexturePackFolderModel : public ModFolderModel
-{
- Q_OBJECT
-
-public:
- explicit TexturePackFolderModel(const QString &dir);
-
- QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
-};
diff --git a/api/logic/minecraft/services/SkinDelete.cpp b/api/logic/minecraft/services/SkinDelete.cpp
deleted file mode 100644
index 34977257..00000000
--- a/api/logic/minecraft/services/SkinDelete.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#include "SkinDelete.h"
-#include <QNetworkRequest>
-#include <QHttpMultiPart>
-#include <Env.h>
-
-SkinDelete::SkinDelete(QObject *parent, AuthSessionPtr session)
- : Task(parent), m_session(session)
-{
-}
-
-void SkinDelete::executeTask()
-{
- QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/skins/active"));
- request.setRawHeader("Authorization", QString("Bearer %1").arg(m_session->access_token).toLocal8Bit());
- QNetworkReply *rep = ENV.qnam().deleteResource(request);
- m_reply = std::shared_ptr<QNetworkReply>(rep);
-
- setStatus(tr("Deleting skin"));
- connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress);
- connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
- connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished()));
-}
-
-void SkinDelete::downloadError(QNetworkReply::NetworkError error)
-{
- // error happened during download.
- qCritical() << "Network error: " << error;
- emitFailed(m_reply->errorString());
-}
-
-void SkinDelete::downloadFinished()
-{
- // if the download failed
- if (m_reply->error() != QNetworkReply::NetworkError::NoError)
- {
- emitFailed(QString("Network error: %1").arg(m_reply->errorString()));
- m_reply.reset();
- return;
- }
- emitSucceeded();
-}
-
diff --git a/api/logic/minecraft/services/SkinDelete.h b/api/logic/minecraft/services/SkinDelete.h
deleted file mode 100644
index 705ce8ef..00000000
--- a/api/logic/minecraft/services/SkinDelete.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma once
-
-#include <QFile>
-#include <QtNetwork/QtNetwork>
-#include <memory>
-#include <minecraft/auth/AuthSession.h>
-#include "tasks/Task.h"
-#include "multimc_logic_export.h"
-
-typedef std::shared_ptr<class SkinDelete> SkinDeletePtr;
-
-class MULTIMC_LOGIC_EXPORT SkinDelete : public Task
-{
- Q_OBJECT
-public:
- SkinDelete(QObject *parent, AuthSessionPtr session);
- virtual ~SkinDelete() = default;
-
-private:
- AuthSessionPtr m_session;
- std::shared_ptr<QNetworkReply> m_reply;
-
-protected:
- virtual void executeTask();
-
-public slots:
- void downloadError(QNetworkReply::NetworkError);
- void downloadFinished();
-};
-
diff --git a/api/logic/minecraft/services/SkinUpload.cpp b/api/logic/minecraft/services/SkinUpload.cpp
deleted file mode 100644
index 4e5a1698..00000000
--- a/api/logic/minecraft/services/SkinUpload.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-#include "SkinUpload.h"
-#include <QNetworkRequest>
-#include <QHttpMultiPart>
-#include <Env.h>
-
-QByteArray getVariant(SkinUpload::Model model) {
- switch (model) {
- default:
- qDebug() << "Unknown skin type!";
- case SkinUpload::STEVE:
- return "CLASSIC";
- case SkinUpload::ALEX:
- return "SLIM";
- }
-}
-
-SkinUpload::SkinUpload(QObject *parent, AuthSessionPtr session, QByteArray skin, SkinUpload::Model model)
- : Task(parent), m_model(model), m_skin(skin), m_session(session)
-{
-}
-
-void SkinUpload::executeTask()
-{
- QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/skins"));
- request.setRawHeader("Authorization", QString("Bearer %1").arg(m_session->access_token).toLocal8Bit());
- QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
-
- QHttpPart skin;
- skin.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png"));
- skin.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\"; filename=\"skin.png\""));
- skin.setBody(m_skin);
-
- QHttpPart model;
- model.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"variant\""));
- model.setBody(getVariant(m_model));
-
- multiPart->append(skin);
- multiPart->append(model);
-
- QNetworkReply *rep = ENV.qnam().post(request, multiPart);
- m_reply = std::shared_ptr<QNetworkReply>(rep);
-
- setStatus(tr("Uploading skin"));
- connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress);
- connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
- connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished()));
-}
-
-void SkinUpload::downloadError(QNetworkReply::NetworkError error)
-{
- // error happened during download.
- qCritical() << "Network error: " << error;
- emitFailed(m_reply->errorString());
-}
-
-void SkinUpload::downloadFinished()
-{
- // if the download failed
- if (m_reply->error() != QNetworkReply::NetworkError::NoError)
- {
- emitFailed(QString("Network error: %1").arg(m_reply->errorString()));
- m_reply.reset();
- return;
- }
- emitSucceeded();
-}
diff --git a/api/logic/minecraft/services/SkinUpload.h b/api/logic/minecraft/services/SkinUpload.h
deleted file mode 100644
index c77abb03..00000000
--- a/api/logic/minecraft/services/SkinUpload.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#pragma once
-
-#include <QFile>
-#include <QtNetwork/QtNetwork>
-#include <memory>
-#include <minecraft/auth/AuthSession.h>
-#include "tasks/Task.h"
-#include "multimc_logic_export.h"
-
-typedef std::shared_ptr<class SkinUpload> SkinUploadPtr;
-
-class MULTIMC_LOGIC_EXPORT SkinUpload : public Task
-{
- Q_OBJECT
-public:
- enum Model
- {
- STEVE,
- ALEX
- };
-
- // Note this class takes ownership of the file.
- SkinUpload(QObject *parent, AuthSessionPtr session, QByteArray skin, Model model = STEVE);
- virtual ~SkinUpload() {}
-
-private:
- Model m_model;
- QByteArray m_skin;
- AuthSessionPtr m_session;
- std::shared_ptr<QNetworkReply> m_reply;
-protected:
- virtual void executeTask();
-
-public slots:
-
- void downloadError(QNetworkReply::NetworkError);
-
- void downloadFinished();
-};
diff --git a/api/logic/minecraft/testdata/1.9-simple.json b/api/logic/minecraft/testdata/1.9-simple.json
deleted file mode 100644
index 574c5b06..00000000
--- a/api/logic/minecraft/testdata/1.9-simple.json
+++ /dev/null
@@ -1,198 +0,0 @@
-{
- "assets": "1.9",
- "id": "1.9",
- "libraries": [
- {
- "name": "oshi-project:oshi-core:1.1"
- },
- {
- "name": "net.java.dev.jna:jna:3.4.0"
- },
- {
- "name": "net.java.dev.jna:platform:3.4.0"
- },
- {
- "name": "com.ibm.icu:icu4j-core-mojang:51.2"
- },
- {
- "name": "net.sf.jopt-simple:jopt-simple:4.6"
- },
- {
- "name": "com.paulscode:codecjorbis:20101023"
- },
- {
- "name": "com.paulscode:codecwav:20101023"
- },
- {
- "name": "com.paulscode:libraryjavasound:20101123"
- },
- {
- "name": "com.paulscode:librarylwjglopenal:20100824"
- },
- {
- "name": "com.paulscode:soundsystem:20120107"
- },
- {
- "name": "io.netty:netty-all:4.0.23.Final"
- },
- {
- "name": "com.google.guava:guava:17.0"
- },
- {
- "name": "org.apache.commons:commons-lang3:3.3.2"
- },
- {
- "name": "commons-io:commons-io:2.4"
- },
- {
- "name": "commons-codec:commons-codec:1.9"
- },
- {
- "name": "net.java.jinput:jinput:2.0.5"
- },
- {
- "name": "net.java.jutils:jutils:1.0.0"
- },
- {
- "name": "com.google.code.gson:gson:2.2.4"
- },
- {
- "name": "com.mojang:authlib:1.5.22"
- },
- {
- "name": "com.mojang:realms:1.8.4"
- },
- {
- "name": "org.apache.commons:commons-compress:1.8.1"
- },
- {
- "name": "org.apache.httpcomponents:httpclient:4.3.3"
- },
- {
- "name": "commons-logging:commons-logging:1.1.3"
- },
- {
- "name": "org.apache.httpcomponents:httpcore:4.3.2"
- },
- {
- "name": "org.apache.logging.log4j:log4j-api:2.0-beta9"
- },
- {
- "name": "org.apache.logging.log4j:log4j-core:2.0-beta9"
- },
- {
- "name": "org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209",
- "rules": [
- {
- "action": "allow"
- },
- {
- "action": "disallow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209",
- "rules": [
- {
- "action": "allow"
- },
- {
- "action": "disallow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "extract": {
- "exclude": [
- "META-INF/"
- ]
- },
- "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209",
- "natives": {
- "linux": "natives-linux",
- "osx": "natives-osx",
- "windows": "natives-windows"
- },
- "rules": [
- {
- "action": "allow"
- },
- {
- "action": "disallow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "name": "org.lwjgl.lwjgl:lwjgl:2.9.2-nightly-20140822",
- "rules": [
- {
- "action": "allow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.2-nightly-20140822",
- "rules": [
- {
- "action": "allow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "extract": {
- "exclude": [
- "META-INF/"
- ]
- },
- "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.2-nightly-20140822",
- "natives": {
- "linux": "natives-linux",
- "osx": "natives-osx",
- "windows": "natives-windows"
- },
- "rules": [
- {
- "action": "allow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "extract": {
- "exclude": [
- "META-INF/"
- ]
- },
- "name": "net.java.jinput:jinput-platform:2.0.5",
- "natives": {
- "linux": "natives-linux",
- "osx": "natives-osx",
- "windows": "natives-windows"
- }
- }
- ],
- "mainClass": "net.minecraft.client.main.Main",
- "minecraftArguments": "--username ${auth_player_name} --version ${version_name} --gameDir ${game_directory} --assetsDir ${assets_root} --assetIndex ${assets_index_name} --uuid ${auth_uuid} --accessToken ${auth_access_token} --userType ${user_type} --versionType ${version_type}",
- "minimumLauncherVersion": 18,
- "releaseTime": "2016-02-29T13:49:54+00:00",
- "time": "2016-03-01T13:14:53+00:00",
- "type": "release"
-}
diff --git a/api/logic/minecraft/testdata/1.9.json b/api/logic/minecraft/testdata/1.9.json
deleted file mode 100644
index 697c6059..00000000
--- a/api/logic/minecraft/testdata/1.9.json
+++ /dev/null
@@ -1,529 +0,0 @@
-{
- "assetIndex": {
- "id": "1.9",
- "sha1": "cde65b47a43f638653ab1da3848b53f8a7477b16",
- "size": 136916,
- "totalSize": 119917473,
- "url": "https://launchermeta.mojang.com/mc-staging/assets/1.9/cde65b47a43f638653ab1da3848b53f8a7477b16/1.9.json"
- },
- "assets": "1.9",
- "downloads": {
- "client": {
- "sha1": "2f67dfe8953299440d1902f9124f0f2c3a2c940f",
- "size": 8697592,
- "url": "https://launcher.mojang.com/mc/game/1.9/client/2f67dfe8953299440d1902f9124f0f2c3a2c940f/client.jar"
- },
- "server": {
- "sha1": "b4d449cf2918e0f3bd8aa18954b916a4d1880f0d",
- "size": 8848015,
- "url": "https://launcher.mojang.com/mc/game/1.9/server/b4d449cf2918e0f3bd8aa18954b916a4d1880f0d/server.jar"
- }
- },
- "id": "1.9",
- "libraries": [
- {
- "downloads": {
- "artifact": {
- "path": "oshi-project/oshi-core/1.1/oshi-core-1.1.jar",
- "sha1": "9ddf7b048a8d701be231c0f4f95fd986198fd2d8",
- "size": 30973,
- "url": "https://libraries.minecraft.net/oshi-project/oshi-core/1.1/oshi-core-1.1.jar"
- }
- },
- "name": "oshi-project:oshi-core:1.1"
- },
- {
- "downloads": {
- "artifact": {
- "path": "net/java/dev/jna/jna/3.4.0/jna-3.4.0.jar",
- "sha1": "803ff252fedbd395baffd43b37341dc4a150a554",
- "size": 1008730,
- "url": "https://libraries.minecraft.net/net/java/dev/jna/jna/3.4.0/jna-3.4.0.jar"
- }
- },
- "name": "net.java.dev.jna:jna:3.4.0"
- },
- {
- "downloads": {
- "artifact": {
- "path": "net/java/dev/jna/platform/3.4.0/platform-3.4.0.jar",
- "sha1": "e3f70017be8100d3d6923f50b3d2ee17714e9c13",
- "size": 913436,
- "url": "https://libraries.minecraft.net/net/java/dev/jna/platform/3.4.0/platform-3.4.0.jar"
- }
- },
- "name": "net.java.dev.jna:platform:3.4.0"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/ibm/icu/icu4j-core-mojang/51.2/icu4j-core-mojang-51.2.jar",
- "sha1": "63d216a9311cca6be337c1e458e587f99d382b84",
- "size": 1634692,
- "url": "https://libraries.minecraft.net/com/ibm/icu/icu4j-core-mojang/51.2/icu4j-core-mojang-51.2.jar"
- }
- },
- "name": "com.ibm.icu:icu4j-core-mojang:51.2"
- },
- {
- "downloads": {
- "artifact": {
- "path": "net/sf/jopt-simple/jopt-simple/4.6/jopt-simple-4.6.jar",
- "sha1": "306816fb57cf94f108a43c95731b08934dcae15c",
- "size": 62477,
- "url": "https://libraries.minecraft.net/net/sf/jopt-simple/jopt-simple/4.6/jopt-simple-4.6.jar"
- }
- },
- "name": "net.sf.jopt-simple:jopt-simple:4.6"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/paulscode/codecjorbis/20101023/codecjorbis-20101023.jar",
- "sha1": "c73b5636faf089d9f00e8732a829577de25237ee",
- "size": 103871,
- "url": "https://libraries.minecraft.net/com/paulscode/codecjorbis/20101023/codecjorbis-20101023.jar"
- }
- },
- "name": "com.paulscode:codecjorbis:20101023"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/paulscode/codecwav/20101023/codecwav-20101023.jar",
- "sha1": "12f031cfe88fef5c1dd36c563c0a3a69bd7261da",
- "size": 5618,
- "url": "https://libraries.minecraft.net/com/paulscode/codecwav/20101023/codecwav-20101023.jar"
- }
- },
- "name": "com.paulscode:codecwav:20101023"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/paulscode/libraryjavasound/20101123/libraryjavasound-20101123.jar",
- "sha1": "5c5e304366f75f9eaa2e8cca546a1fb6109348b3",
- "size": 21679,
- "url": "https://libraries.minecraft.net/com/paulscode/libraryjavasound/20101123/libraryjavasound-20101123.jar"
- }
- },
- "name": "com.paulscode:libraryjavasound:20101123"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/paulscode/librarylwjglopenal/20100824/librarylwjglopenal-20100824.jar",
- "sha1": "73e80d0794c39665aec3f62eee88ca91676674ef",
- "size": 18981,
- "url": "https://libraries.minecraft.net/com/paulscode/librarylwjglopenal/20100824/librarylwjglopenal-20100824.jar"
- }
- },
- "name": "com.paulscode:librarylwjglopenal:20100824"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/paulscode/soundsystem/20120107/soundsystem-20120107.jar",
- "sha1": "419c05fe9be71f792b2d76cfc9b67f1ed0fec7f6",
- "size": 65020,
- "url": "https://libraries.minecraft.net/com/paulscode/soundsystem/20120107/soundsystem-20120107.jar"
- }
- },
- "name": "com.paulscode:soundsystem:20120107"
- },
- {
- "downloads": {
- "artifact": {
- "path": "io/netty/netty-all/4.0.23.Final/netty-all-4.0.23.Final.jar",
- "sha1": "0294104aaf1781d6a56a07d561e792c5d0c95f45",
- "size": 1779991,
- "url": "https://libraries.minecraft.net/io/netty/netty-all/4.0.23.Final/netty-all-4.0.23.Final.jar"
- }
- },
- "name": "io.netty:netty-all:4.0.23.Final"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/google/guava/guava/17.0/guava-17.0.jar",
- "sha1": "9c6ef172e8de35fd8d4d8783e4821e57cdef7445",
- "size": 2243036,
- "url": "https://libraries.minecraft.net/com/google/guava/guava/17.0/guava-17.0.jar"
- }
- },
- "name": "com.google.guava:guava:17.0"
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/apache/commons/commons-lang3/3.3.2/commons-lang3-3.3.2.jar",
- "sha1": "90a3822c38ec8c996e84c16a3477ef632cbc87a3",
- "size": 412739,
- "url": "https://libraries.minecraft.net/org/apache/commons/commons-lang3/3.3.2/commons-lang3-3.3.2.jar"
- }
- },
- "name": "org.apache.commons:commons-lang3:3.3.2"
- },
- {
- "downloads": {
- "artifact": {
- "path": "commons-io/commons-io/2.4/commons-io-2.4.jar",
- "sha1": "b1b6ea3b7e4aa4f492509a4952029cd8e48019ad",
- "size": 185140,
- "url": "https://libraries.minecraft.net/commons-io/commons-io/2.4/commons-io-2.4.jar"
- }
- },
- "name": "commons-io:commons-io:2.4"
- },
- {
- "downloads": {
- "artifact": {
- "path": "commons-codec/commons-codec/1.9/commons-codec-1.9.jar",
- "sha1": "9ce04e34240f674bc72680f8b843b1457383161a",
- "size": 263965,
- "url": "https://libraries.minecraft.net/commons-codec/commons-codec/1.9/commons-codec-1.9.jar"
- }
- },
- "name": "commons-codec:commons-codec:1.9"
- },
- {
- "downloads": {
- "artifact": {
- "path": "net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar",
- "sha1": "39c7796b469a600f72380316f6b1f11db6c2c7c4",
- "size": 208338,
- "url": "https://libraries.minecraft.net/net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar"
- }
- },
- "name": "net.java.jinput:jinput:2.0.5"
- },
- {
- "downloads": {
- "artifact": {
- "path": "net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar",
- "sha1": "e12fe1fda814bd348c1579329c86943d2cd3c6a6",
- "size": 7508,
- "url": "https://libraries.minecraft.net/net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar"
- }
- },
- "name": "net.java.jutils:jutils:1.0.0"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/google/code/gson/gson/2.2.4/gson-2.2.4.jar",
- "sha1": "a60a5e993c98c864010053cb901b7eab25306568",
- "size": 190432,
- "url": "https://libraries.minecraft.net/com/google/code/gson/gson/2.2.4/gson-2.2.4.jar"
- }
- },
- "name": "com.google.code.gson:gson:2.2.4"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/mojang/authlib/1.5.22/authlib-1.5.22.jar",
- "sha1": "afaa8f6df976fcb5520e76ef1d5798c9e6b5c0b2",
- "size": 64539,
- "url": "https://libraries.minecraft.net/com/mojang/authlib/1.5.22/authlib-1.5.22.jar"
- }
- },
- "name": "com.mojang:authlib:1.5.22"
- },
- {
- "downloads": {
- "artifact": {
- "path": "com/mojang/realms/1.8.4/realms-1.8.4.jar",
- "sha1": "15f8dc326c97a96dee6e65392e145ad6d1cb46cb",
- "size": 1131574,
- "url": "https://libraries.minecraft.net/com/mojang/realms/1.8.4/realms-1.8.4.jar"
- }
- },
- "name": "com.mojang:realms:1.8.4"
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/apache/commons/commons-compress/1.8.1/commons-compress-1.8.1.jar",
- "sha1": "a698750c16740fd5b3871425f4cb3bbaa87f529d",
- "size": 365552,
- "url": "https://libraries.minecraft.net/org/apache/commons/commons-compress/1.8.1/commons-compress-1.8.1.jar"
- }
- },
- "name": "org.apache.commons:commons-compress:1.8.1"
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/apache/httpcomponents/httpclient/4.3.3/httpclient-4.3.3.jar",
- "sha1": "18f4247ff4572a074444572cee34647c43e7c9c7",
- "size": 589512,
- "url": "https://libraries.minecraft.net/org/apache/httpcomponents/httpclient/4.3.3/httpclient-4.3.3.jar"
- }
- },
- "name": "org.apache.httpcomponents:httpclient:4.3.3"
- },
- {
- "downloads": {
- "artifact": {
- "path": "commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar",
- "sha1": "f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f",
- "size": 62050,
- "url": "https://libraries.minecraft.net/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar"
- }
- },
- "name": "commons-logging:commons-logging:1.1.3"
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/apache/httpcomponents/httpcore/4.3.2/httpcore-4.3.2.jar",
- "sha1": "31fbbff1ddbf98f3aa7377c94d33b0447c646b6e",
- "size": 282269,
- "url": "https://libraries.minecraft.net/org/apache/httpcomponents/httpcore/4.3.2/httpcore-4.3.2.jar"
- }
- },
- "name": "org.apache.httpcomponents:httpcore:4.3.2"
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/apache/logging/log4j/log4j-api/2.0-beta9/log4j-api-2.0-beta9.jar",
- "sha1": "1dd66e68cccd907880229f9e2de1314bd13ff785",
- "size": 108161,
- "url": "https://libraries.minecraft.net/org/apache/logging/log4j/log4j-api/2.0-beta9/log4j-api-2.0-beta9.jar"
- }
- },
- "name": "org.apache.logging.log4j:log4j-api:2.0-beta9"
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/apache/logging/log4j/log4j-core/2.0-beta9/log4j-core-2.0-beta9.jar",
- "sha1": "678861ba1b2e1fccb594bb0ca03114bb05da9695",
- "size": 681134,
- "url": "https://libraries.minecraft.net/org/apache/logging/log4j/log4j-core/2.0-beta9/log4j-core-2.0-beta9.jar"
- }
- },
- "name": "org.apache.logging.log4j:log4j-core:2.0-beta9"
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/lwjgl/lwjgl/lwjgl/2.9.4-nightly-20150209/lwjgl-2.9.4-nightly-20150209.jar",
- "sha1": "697517568c68e78ae0b4544145af031c81082dfe",
- "size": 1047168,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl/2.9.4-nightly-20150209/lwjgl-2.9.4-nightly-20150209.jar"
- }
- },
- "name": "org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209",
- "rules": [
- {
- "action": "allow"
- },
- {
- "action": "disallow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/lwjgl/lwjgl/lwjgl_util/2.9.4-nightly-20150209/lwjgl_util-2.9.4-nightly-20150209.jar",
- "sha1": "d51a7c040a721d13efdfbd34f8b257b2df882ad0",
- "size": 173887,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl_util/2.9.4-nightly-20150209/lwjgl_util-2.9.4-nightly-20150209.jar"
- }
- },
- "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209",
- "rules": [
- {
- "action": "allow"
- },
- {
- "action": "disallow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar",
- "sha1": "b04f3ee8f5e43fa3b162981b50bb72fe1acabb33",
- "size": 22,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar"
- },
- "classifiers": {
- "natives-linux": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar",
- "sha1": "931074f46c795d2f7b30ed6395df5715cfd7675b",
- "size": 578680,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar"
- },
- "natives-osx": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar",
- "sha1": "bcab850f8f487c3f4c4dbabde778bb82bd1a40ed",
- "size": 426822,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar"
- },
- "natives-windows": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar",
- "sha1": "b84d5102b9dbfabfeb5e43c7e2828d98a7fc80e0",
- "size": 613748,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar"
- }
- }
- },
- "extract": {
- "exclude": [
- "META-INF/"
- ]
- },
- "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209",
- "natives": {
- "linux": "natives-linux",
- "osx": "natives-osx",
- "windows": "natives-windows"
- },
- "rules": [
- {
- "action": "allow"
- },
- {
- "action": "disallow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/lwjgl/lwjgl/lwjgl/2.9.2-nightly-20140822/lwjgl-2.9.2-nightly-20140822.jar",
- "sha1": "7707204c9ffa5d91662de95f0a224e2f721b22af",
- "size": 1045632,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl/2.9.2-nightly-20140822/lwjgl-2.9.2-nightly-20140822.jar"
- }
- },
- "name": "org.lwjgl.lwjgl:lwjgl:2.9.2-nightly-20140822",
- "rules": [
- {
- "action": "allow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "downloads": {
- "artifact": {
- "path": "org/lwjgl/lwjgl/lwjgl_util/2.9.2-nightly-20140822/lwjgl_util-2.9.2-nightly-20140822.jar",
- "sha1": "f0e612c840a7639c1f77f68d72a28dae2f0c8490",
- "size": 173887,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl_util/2.9.2-nightly-20140822/lwjgl_util-2.9.2-nightly-20140822.jar"
- }
- },
- "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.2-nightly-20140822",
- "rules": [
- {
- "action": "allow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "downloads": {
- "classifiers": {
- "natives-linux": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-linux.jar",
- "sha1": "d898a33b5d0a6ef3fed3a4ead506566dce6720a5",
- "size": 578539,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-linux.jar"
- },
- "natives-osx": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-osx.jar",
- "sha1": "79f5ce2fea02e77fe47a3c745219167a542121d7",
- "size": 468116,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-osx.jar"
- },
- "natives-windows": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-windows.jar",
- "sha1": "78b2a55ce4dc29c6b3ec4df8ca165eba05f9b341",
- "size": 613680,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-windows.jar"
- }
- }
- },
- "extract": {
- "exclude": [
- "META-INF/"
- ]
- },
- "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.2-nightly-20140822",
- "natives": {
- "linux": "natives-linux",
- "osx": "natives-osx",
- "windows": "natives-windows"
- },
- "rules": [
- {
- "action": "allow",
- "os": {
- "name": "osx"
- }
- }
- ]
- },
- {
- "downloads": {
- "classifiers": {
- "natives-linux": {
- "path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar",
- "sha1": "7ff832a6eb9ab6a767f1ade2b548092d0fa64795",
- "size": 10362,
- "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar"
- },
- "natives-osx": {
- "path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-osx.jar",
- "sha1": "53f9c919f34d2ca9de8c51fc4e1e8282029a9232",
- "size": 12186,
- "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-osx.jar"
- },
- "natives-windows": {
- "path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-windows.jar",
- "sha1": "385ee093e01f587f30ee1c8a2ee7d408fd732e16",
- "size": 155179,
- "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-windows.jar"
- }
- }
- },
- "extract": {
- "exclude": [
- "META-INF/"
- ]
- },
- "name": "net.java.jinput:jinput-platform:2.0.5",
- "natives": {
- "linux": "natives-linux",
- "osx": "natives-osx",
- "windows": "natives-windows"
- }
- }
- ],
- "mainClass": "net.minecraft.client.main.Main",
- "minecraftArguments": "--username ${auth_player_name} --version ${version_name} --gameDir ${game_directory} --assetsDir ${assets_root} --assetIndex ${assets_index_name} --uuid ${auth_uuid} --accessToken ${auth_access_token} --userType ${user_type} --versionType ${version_type}",
- "minimumLauncherVersion": 18,
- "releaseTime": "2016-02-29T13:49:54+00:00",
- "time": "2016-03-01T13:14:53+00:00",
- "type": "release"
-}
diff --git a/api/logic/minecraft/testdata/codecwav-20101023.jar b/api/logic/minecraft/testdata/codecwav-20101023.jar
deleted file mode 100644
index f5236083..00000000
--- a/api/logic/minecraft/testdata/codecwav-20101023.jar
+++ /dev/null
@@ -1 +0,0 @@
-dummy test file.
diff --git a/api/logic/minecraft/testdata/lib-native-arch.json b/api/logic/minecraft/testdata/lib-native-arch.json
deleted file mode 100644
index 501826ae..00000000
--- a/api/logic/minecraft/testdata/lib-native-arch.json
+++ /dev/null
@@ -1,46 +0,0 @@
-{
- "downloads": {
- "classifiers": {
- "natives-osx": {
- "path": "tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-osx.jar",
- "sha1": "62503ee712766cf77f97252e5902786fd834b8c5",
- "size": 418331,
- "url": "https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-osx.jar"
- },
- "natives-windows-32": {
- "path": "tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar",
- "sha1": "7c6affe439099806a4f552da14c42f9d643d8b23",
- "size": 386792,
- "url": "https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar"
- },
- "natives-windows-64": {
- "path": "tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar",
- "sha1": "39d0c3d363735b4785598e0e7fbf8297c706a9f9",
- "size": 463390,
- "url": "https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar"
- }
- }
- },
- "extract": {
- "exclude": [
- "META-INF/"
- ]
- },
- "name": "tv.twitch:twitch-platform:5.16",
- "natives": {
- "linux": "natives-linux",
- "osx": "natives-osx",
- "windows": "natives-windows-${arch}"
- },
- "rules": [
- {
- "action": "allow"
- },
- {
- "action": "disallow",
- "os": {
- "name": "linux"
- }
- }
- ]
-}
diff --git a/api/logic/minecraft/testdata/lib-native.json b/api/logic/minecraft/testdata/lib-native.json
deleted file mode 100644
index 5b9f3b55..00000000
--- a/api/logic/minecraft/testdata/lib-native.json
+++ /dev/null
@@ -1,52 +0,0 @@
-{
- "downloads": {
- "artifact": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar",
- "sha1": "b04f3ee8f5e43fa3b162981b50bb72fe1acabb33",
- "size": 22,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar"
- },
- "classifiers": {
- "natives-linux": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar",
- "sha1": "931074f46c795d2f7b30ed6395df5715cfd7675b",
- "size": 578680,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar"
- },
- "natives-osx": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar",
- "sha1": "bcab850f8f487c3f4c4dbabde778bb82bd1a40ed",
- "size": 426822,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar"
- },
- "natives-windows": {
- "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar",
- "sha1": "b84d5102b9dbfabfeb5e43c7e2828d98a7fc80e0",
- "size": 613748,
- "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar"
- }
- }
- },
- "extract": {
- "exclude": [
- "META-INF/"
- ]
- },
- "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209",
- "natives": {
- "linux": "natives-linux",
- "osx": "natives-osx",
- "windows": "natives-windows"
- },
- "rules": [
- {
- "action": "allow"
- },
- {
- "action": "disallow",
- "os": {
- "name": "osx"
- }
- }
- ]
-}
diff --git a/api/logic/minecraft/testdata/lib-simple.json b/api/logic/minecraft/testdata/lib-simple.json
deleted file mode 100644
index 90bbff07..00000000
--- a/api/logic/minecraft/testdata/lib-simple.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "downloads": {
- "artifact": {
- "path": "com/paulscode/codecwav/20101023/codecwav-20101023.jar",
- "sha1": "12f031cfe88fef5c1dd36c563c0a3a69bd7261da",
- "size": 5618,
- "url": "https://libraries.minecraft.net/com/paulscode/codecwav/20101023/codecwav-20101023.jar"
- }
- },
- "name": "com.paulscode:codecwav:20101023"
-}
diff --git a/api/logic/minecraft/testdata/testname-testversion-linux-32.jar b/api/logic/minecraft/testdata/testname-testversion-linux-32.jar
deleted file mode 100644
index f5236083..00000000
--- a/api/logic/minecraft/testdata/testname-testversion-linux-32.jar
+++ /dev/null
@@ -1 +0,0 @@
-dummy test file.
diff --git a/api/logic/minecraft/update/AssetUpdateTask.cpp b/api/logic/minecraft/update/AssetUpdateTask.cpp
deleted file mode 100644
index e26ab4ef..00000000
--- a/api/logic/minecraft/update/AssetUpdateTask.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-#include "Env.h"
-#include "AssetUpdateTask.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-#include "net/ChecksumValidator.h"
-#include "minecraft/AssetsUtils.h"
-
-AssetUpdateTask::AssetUpdateTask(MinecraftInstance * inst)
-{
- m_inst = inst;
-}
-
-AssetUpdateTask::~AssetUpdateTask()
-{
-}
-
-void AssetUpdateTask::executeTask()
-{
- setStatus(tr("Updating assets index..."));
- auto components = m_inst->getPackProfile();
- auto profile = components->getProfile();
- auto assets = profile->getMinecraftAssets();
- QUrl indexUrl = assets->url;
- QString localPath = assets->id + ".json";
- auto job = new NetJob(tr("Asset index for %1").arg(m_inst->name()));
-
- auto metacache = ENV.metacache();
- auto entry = metacache->resolveEntry("asset_indexes", localPath);
- entry->setStale(true);
- auto hexSha1 = assets->sha1.toLatin1();
- qDebug() << "Asset index SHA1:" << hexSha1;
- auto dl = Net::Download::makeCached(indexUrl, entry);
- auto rawSha1 = QByteArray::fromHex(assets->sha1.toLatin1());
- dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
- job->addNetAction(dl);
-
- downloadJob.reset(job);
-
- connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::assetIndexFinished);
- connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetIndexFailed);
- connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress);
-
- qDebug() << m_inst->name() << ": Starting asset index download";
- downloadJob->start();
-}
-
-bool AssetUpdateTask::canAbort() const
-{
- return true;
-}
-
-void AssetUpdateTask::assetIndexFinished()
-{
- AssetsIndex index;
- qDebug() << m_inst->name() << ": Finished asset index download";
-
- auto components = m_inst->getPackProfile();
- auto profile = components->getProfile();
- auto assets = profile->getMinecraftAssets();
-
- QString asset_fname = "assets/indexes/" + assets->id + ".json";
- // FIXME: this looks like a job for a generic validator based on json schema?
- if (!AssetsUtils::loadAssetsIndexJson(assets->id, asset_fname, index))
- {
- auto metacache = ENV.metacache();
- auto entry = metacache->resolveEntry("asset_indexes", assets->id + ".json");
- metacache->evictEntry(entry);
- emitFailed(tr("Failed to read the assets index!"));
- }
-
- auto job = index.getDownloadJob();
- if(job)
- {
- setStatus(tr("Getting the assets files from Mojang..."));
- downloadJob = job;
- connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::emitSucceeded);
- connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed);
- connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress);
- downloadJob->start();
- return;
- }
- emitSucceeded();
-}
-
-void AssetUpdateTask::assetIndexFailed(QString reason)
-{
- qDebug() << m_inst->name() << ": Failed asset index download";
- emitFailed(tr("Failed to download the assets index:\n%1").arg(reason));
-}
-
-void AssetUpdateTask::assetsFailed(QString reason)
-{
- emitFailed(tr("Failed to download assets:\n%1").arg(reason));
-}
-
-bool AssetUpdateTask::abort()
-{
- if(downloadJob)
- {
- return downloadJob->abort();
- }
- else
- {
- qWarning() << "Prematurely aborted AssetUpdateTask";
- }
- return true;
-}
diff --git a/api/logic/minecraft/update/AssetUpdateTask.h b/api/logic/minecraft/update/AssetUpdateTask.h
deleted file mode 100644
index fdfa8f1c..00000000
--- a/api/logic/minecraft/update/AssetUpdateTask.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-#include "tasks/Task.h"
-#include "net/NetJob.h"
-class MinecraftInstance;
-
-class AssetUpdateTask : public Task
-{
- Q_OBJECT
-public:
- AssetUpdateTask(MinecraftInstance * inst);
- virtual ~AssetUpdateTask();
-
- void executeTask() override;
-
- bool canAbort() const override;
-
-private slots:
- void assetIndexFinished();
- void assetIndexFailed(QString reason);
- void assetsFailed(QString reason);
-
-public slots:
- bool abort() override;
-
-private:
- MinecraftInstance *m_inst;
- NetJobPtr downloadJob;
-};
diff --git a/api/logic/minecraft/update/FMLLibrariesTask.cpp b/api/logic/minecraft/update/FMLLibrariesTask.cpp
deleted file mode 100644
index a05a7c2a..00000000
--- a/api/logic/minecraft/update/FMLLibrariesTask.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-#include "Env.h"
-#include <FileSystem.h>
-#include <minecraft/VersionFilterData.h>
-#include "FMLLibrariesTask.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-#include "BuildConfig.h"
-
-FMLLibrariesTask::FMLLibrariesTask(MinecraftInstance * inst)
-{
- m_inst = inst;
-}
-void FMLLibrariesTask::executeTask()
-{
- // Get the mod list
- MinecraftInstance *inst = (MinecraftInstance *)m_inst;
- auto components = inst->getPackProfile();
- auto profile = components->getProfile();
-
- if (!profile->hasTrait("legacyFML"))
- {
- emitSucceeded();
- return;
- }
-
- QString version = components->getComponentVersion("net.minecraft");
- auto &fmlLibsMapping = g_VersionFilterData.fmlLibsMapping;
- if (!fmlLibsMapping.contains(version))
- {
- emitSucceeded();
- return;
- }
-
- auto &libList = fmlLibsMapping[version];
-
- // determine if we need some libs for FML or forge
- setStatus(tr("Checking for FML libraries..."));
- if(!components->getComponent("net.minecraftforge"))
- {
- emitSucceeded();
- return;
- }
-
- // now check the lib folder inside the instance for files.
- for (auto &lib : libList)
- {
- QFileInfo libInfo(FS::PathCombine(inst->libDir(), lib.filename));
- if (libInfo.exists())
- continue;
- fmlLibsToProcess.append(lib);
- }
-
- // if everything is in place, there's nothing to do here...
- if (fmlLibsToProcess.isEmpty())
- {
- emitSucceeded();
- return;
- }
-
- // download missing libs to our place
- setStatus(tr("Dowloading FML libraries..."));
- auto dljob = new NetJob("FML libraries");
- auto metacache = ENV.metacache();
- for (auto &lib : fmlLibsToProcess)
- {
- auto entry = metacache->resolveEntry("fmllibs", lib.filename);
- QString urlString = BuildConfig.FMLLIBS_BASE_URL + lib.filename;
- dljob->addNetAction(Net::Download::makeCached(QUrl(urlString), entry));
- }
-
- connect(dljob, &NetJob::succeeded, this, &FMLLibrariesTask::fmllibsFinished);
- connect(dljob, &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed);
- connect(dljob, &NetJob::progress, this, &FMLLibrariesTask::progress);
- downloadJob.reset(dljob);
- downloadJob->start();
-}
-
-bool FMLLibrariesTask::canAbort() const
-{
- return true;
-}
-
-void FMLLibrariesTask::fmllibsFinished()
-{
- downloadJob.reset();
- if (!fmlLibsToProcess.isEmpty())
- {
- setStatus(tr("Copying FML libraries into the instance..."));
- MinecraftInstance *inst = (MinecraftInstance *)m_inst;
- auto metacache = ENV.metacache();
- int index = 0;
- for (auto &lib : fmlLibsToProcess)
- {
- progress(index, fmlLibsToProcess.size());
- auto entry = metacache->resolveEntry("fmllibs", lib.filename);
- auto path = FS::PathCombine(inst->libDir(), lib.filename);
- if (!FS::ensureFilePathExists(path))
- {
- emitFailed(tr("Failed creating FML library folder inside the instance."));
- return;
- }
- if (!QFile::copy(entry->getFullPath(), FS::PathCombine(inst->libDir(), lib.filename)))
- {
- emitFailed(tr("Failed copying Forge/FML library: %1.").arg(lib.filename));
- return;
- }
- index++;
- }
- progress(index, fmlLibsToProcess.size());
- }
- emitSucceeded();
-}
-void FMLLibrariesTask::fmllibsFailed(QString reason)
-{
- QStringList failed = downloadJob->getFailedFiles();
- QString failed_all = failed.join("\n");
- emitFailed(tr("Failed to download the following files:\n%1\n\nReason:%2\nPlease try again.").arg(failed_all, reason));
-}
-
-bool FMLLibrariesTask::abort()
-{
- if(downloadJob)
- {
- return downloadJob->abort();
- }
- else
- {
- qWarning() << "Prematurely aborted FMLLibrariesTask";
- }
- return true;
-}
diff --git a/api/logic/minecraft/update/FMLLibrariesTask.h b/api/logic/minecraft/update/FMLLibrariesTask.h
deleted file mode 100644
index a1e70ed4..00000000
--- a/api/logic/minecraft/update/FMLLibrariesTask.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-#include "tasks/Task.h"
-#include "net/NetJob.h"
-#include "minecraft/VersionFilterData.h"
-
-class MinecraftInstance;
-
-class FMLLibrariesTask : public Task
-{
- Q_OBJECT
-public:
- FMLLibrariesTask(MinecraftInstance * inst);
- virtual ~FMLLibrariesTask() {};
-
- void executeTask() override;
-
- bool canAbort() const override;
-
-private slots:
- void fmllibsFinished();
- void fmllibsFailed(QString reason);
-
-public slots:
- bool abort() override;
-
-private:
- MinecraftInstance *m_inst;
- NetJobPtr downloadJob;
- QList<FMLlib> fmlLibsToProcess;
-};
-
diff --git a/api/logic/minecraft/update/FoldersTask.cpp b/api/logic/minecraft/update/FoldersTask.cpp
deleted file mode 100644
index e2b1bb48..00000000
--- a/api/logic/minecraft/update/FoldersTask.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "FoldersTask.h"
-#include "minecraft/MinecraftInstance.h"
-#include <QDir>
-
-FoldersTask::FoldersTask(MinecraftInstance * inst)
- :Task()
-{
- m_inst = inst;
-}
-
-void FoldersTask::executeTask()
-{
- // Make directories
- QDir mcDir(m_inst->gameRoot());
- if (!mcDir.exists() && !mcDir.mkpath("."))
- {
- emitFailed(tr("Failed to create folder for minecraft binaries."));
- return;
- }
- emitSucceeded();
-}
diff --git a/api/logic/minecraft/update/FoldersTask.h b/api/logic/minecraft/update/FoldersTask.h
deleted file mode 100644
index f6ed5e6d..00000000
--- a/api/logic/minecraft/update/FoldersTask.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-#include "tasks/Task.h"
-
-class MinecraftInstance;
-class FoldersTask : public Task
-{
- Q_OBJECT
-public:
- FoldersTask(MinecraftInstance * inst);
- virtual ~FoldersTask() {};
-
- void executeTask() override;
-private:
- MinecraftInstance *m_inst;
-};
-
diff --git a/api/logic/minecraft/update/LibrariesTask.cpp b/api/logic/minecraft/update/LibrariesTask.cpp
deleted file mode 100644
index 7f66a651..00000000
--- a/api/logic/minecraft/update/LibrariesTask.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-#include "Env.h"
-#include "LibrariesTask.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-
-LibrariesTask::LibrariesTask(MinecraftInstance * inst)
-{
- m_inst = inst;
-}
-
-void LibrariesTask::executeTask()
-{
- setStatus(tr("Getting the library files from Mojang..."));
- qDebug() << m_inst->name() << ": downloading libraries";
- MinecraftInstance *inst = (MinecraftInstance *)m_inst;
-
- // Build a list of URLs that will need to be downloaded.
- auto components = inst->getPackProfile();
- auto profile = components->getProfile();
-
- auto job = new NetJob(tr("Libraries for instance %1").arg(inst->name()));
- downloadJob.reset(job);
-
- auto metacache = ENV.metacache();
-
- auto processArtifactPool = [&](const QList<LibraryPtr> & pool, QStringList & errors, const QString & localPath)
- {
- for (auto lib : pool)
- {
- if(!lib)
- {
- emitFailed(tr("Null jar is specified in the metadata, aborting."));
- return false;
- }
- auto dls = lib->getDownloads(currentSystem, metacache.get(), errors, localPath);
- for(auto dl : dls)
- {
- downloadJob->addNetAction(dl);
- }
- }
- return true;
- };
-
- QStringList failedLocalLibraries;
- QList<LibraryPtr> libArtifactPool;
- libArtifactPool.append(profile->getLibraries());
- libArtifactPool.append(profile->getNativeLibraries());
- libArtifactPool.append(profile->getMavenFiles());
- libArtifactPool.append(profile->getMainJar());
- processArtifactPool(libArtifactPool, failedLocalLibraries, inst->getLocalLibraryPath());
-
- QStringList failedLocalJarMods;
- processArtifactPool(profile->getJarMods(), failedLocalJarMods, inst->jarModsDir());
-
- if (!failedLocalJarMods.empty() || !failedLocalLibraries.empty())
- {
- downloadJob.reset();
- QString failed_all = (failedLocalLibraries + failedLocalJarMods).join("\n");
- emitFailed(tr("Some artifacts marked as 'local' are missing their files:\n%1\n\nYou need to either add the files, or removed the packages that require them.\nYou'll have to correct this problem manually.").arg(failed_all));
- return;
- }
-
- connect(downloadJob.get(), &NetJob::succeeded, this, &LibrariesTask::emitSucceeded);
- connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed);
- connect(downloadJob.get(), &NetJob::progress, this, &LibrariesTask::progress);
- downloadJob->start();
-}
-
-bool LibrariesTask::canAbort() const
-{
- return true;
-}
-
-void LibrariesTask::jarlibFailed(QString reason)
-{
- emitFailed(tr("Game update failed: it was impossible to fetch the required libraries.\nReason:\n%1").arg(reason));
-}
-
-bool LibrariesTask::abort()
-{
- if(downloadJob)
- {
- return downloadJob->abort();
- }
- else
- {
- qWarning() << "Prematurely aborted LibrariesTask";
- }
- return true;
-}
diff --git a/api/logic/minecraft/update/LibrariesTask.h b/api/logic/minecraft/update/LibrariesTask.h
deleted file mode 100644
index 49f76932..00000000
--- a/api/logic/minecraft/update/LibrariesTask.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-#include "tasks/Task.h"
-#include "net/NetJob.h"
-class MinecraftInstance;
-
-class LibrariesTask : public Task
-{
- Q_OBJECT
-public:
- LibrariesTask(MinecraftInstance * inst);
- virtual ~LibrariesTask() {};
-
- void executeTask() override;
-
- bool canAbort() const override;
-
-private slots:
- void jarlibFailed(QString reason);
-
-public slots:
- bool abort() override;
-
-private:
- MinecraftInstance *m_inst;
- NetJobPtr downloadJob;
-};