aboutsummaryrefslogtreecommitdiff
path: root/launcher
diff options
context:
space:
mode:
Diffstat (limited to 'launcher')
-rw-r--r--launcher/Application.cpp40
-rw-r--r--launcher/CMakeLists.txt10
-rw-r--r--launcher/InstanceImportTask.cpp268
-rw-r--r--launcher/InstanceImportTask.h6
-rw-r--r--launcher/icons/IconList.cpp4
-rw-r--r--launcher/meta/BaseEntity.cpp11
-rw-r--r--launcher/minecraft/PackProfile.cpp31
-rw-r--r--launcher/minecraft/PackProfile.h2
-rw-r--r--launcher/modplatform/ModAPI.h17
-rw-r--r--launcher/modplatform/atlauncher/ATLPackInstallTask.cpp184
-rw-r--r--launcher/modplatform/atlauncher/ATLPackInstallTask.h47
-rw-r--r--launcher/modplatform/atlauncher/ATLPackManifest.cpp78
-rw-r--r--launcher/modplatform/atlauncher/ATLPackManifest.h70
-rw-r--r--launcher/modplatform/flame/FileResolvingTask.cpp138
-rw-r--r--launcher/modplatform/flame/FileResolvingTask.h8
-rw-r--r--launcher/modplatform/flame/FlameAPI.h17
-rw-r--r--launcher/modplatform/flame/FlameModIndex.cpp9
-rw-r--r--launcher/modplatform/flame/FlamePackIndex.cpp12
-rw-r--r--launcher/modplatform/flame/FlamePackIndex.h1
-rw-r--r--launcher/modplatform/flame/PackManifest.cpp32
-rw-r--r--launcher/modplatform/flame/PackManifest.h10
-rw-r--r--launcher/modplatform/modrinth/ModrinthAPI.h41
-rw-r--r--launcher/modplatform/modrinth/ModrinthPackManifest.cpp22
-rw-r--r--launcher/net/PasteUpload.cpp167
-rw-r--r--launcher/net/PasteUpload.h29
-rw-r--r--launcher/net/Upload.cpp199
-rw-r--r--launcher/net/Upload.h31
-rw-r--r--launcher/resources/multimc/128x128/instances/flame.pngbin3375 -> 6226 bytes
-rw-r--r--launcher/resources/multimc/32x32/instances/flame.pngbin849 -> 0 bytes
-rw-r--r--launcher/resources/multimc/multimc.qrc4
-rw-r--r--launcher/ui/GuiUtil.cpp40
-rw-r--r--launcher/ui/MainWindow.cpp3
-rw-r--r--launcher/ui/dialogs/ScrollMessageBox.cpp15
-rw-r--r--launcher/ui/dialogs/ScrollMessageBox.h20
-rw-r--r--launcher/ui/dialogs/ScrollMessageBox.ui84
-rw-r--r--launcher/ui/pages/global/APIPage.cpp96
-rw-r--r--launcher/ui/pages/global/APIPage.h5
-rw-r--r--launcher/ui/pages/global/APIPage.ui132
-rw-r--r--launcher/ui/pages/global/JavaPage.cpp6
-rw-r--r--launcher/ui/pages/global/JavaPage.ui69
-rw-r--r--launcher/ui/pages/instance/ModFolderPage.cpp2
-rw-r--r--launcher/ui/pages/modplatform/ImportPage.cpp4
-rw-r--r--launcher/ui/pages/modplatform/ModModel.cpp4
-rw-r--r--launcher/ui/pages/modplatform/ModPage.cpp2
-rw-r--r--launcher/ui/pages/modplatform/ModPage.h2
-rw-r--r--launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp37
-rw-r--r--launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h6
-rw-r--r--launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp18
-rw-r--r--launcher/ui/pages/modplatform/atlauncher/AtlPage.h3
-rw-r--r--launcher/ui/pages/modplatform/flame/FlameModPage.cpp4
-rw-r--r--launcher/ui/pages/modplatform/flame/FlameModPage.h2
-rw-r--r--launcher/ui/pages/modplatform/flame/FlamePage.cpp2
-rw-r--r--launcher/ui/pages/modplatform/legacy_ftb/Page.cpp2
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp4
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h2
-rw-r--r--launcher/ui/setupwizard/PasteWizardPage.cpp42
-rw-r--r--launcher/ui/setupwizard/PasteWizardPage.h27
-rw-r--r--launcher/ui/setupwizard/PasteWizardPage.ui80
58 files changed, 1773 insertions, 428 deletions
diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index ce62c41a..ba4096b6 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -2,6 +2,7 @@
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ * Copyright (C) 2022 Lenny McLennington <lenny@sneed.church>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -36,6 +37,7 @@
#include "Application.h"
#include "BuildConfig.h"
+#include "net/PasteUpload.h"
#include "ui/MainWindow.h"
#include "ui/InstanceWindow.h"
@@ -61,6 +63,7 @@
#include "ui/setupwizard/SetupWizard.h"
#include "ui/setupwizard/LanguageWizardPage.h"
#include "ui/setupwizard/JavaWizardPage.h"
+#include "ui/setupwizard/PasteWizardPage.h"
#include "ui/dialogs/CustomMessageBox.h"
@@ -671,8 +674,33 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->registerSetting("UpdateDialogGeometry", "");
- // pastebin URL
- m_settings->registerSetting("PastebinURL", "https://0x0.st");
+ // HACK: This code feels so stupid is there a less stupid way of doing this?
+ {
+ m_settings->registerSetting("PastebinURL", "");
+ m_settings->registerSetting("PastebinType", PasteUpload::PasteType::Mclogs);
+ m_settings->registerSetting("PastebinCustomAPIBase", "");
+
+ QString pastebinURL = m_settings->get("PastebinURL").toString();
+
+ bool userHadDefaultPastebin = pastebinURL == "https://0x0.st";
+ if (!pastebinURL.isEmpty() && !userHadDefaultPastebin)
+ {
+ m_settings->set("PastebinType", PasteUpload::PasteType::NullPointer);
+ m_settings->set("PastebinCustomAPIBase", pastebinURL);
+ m_settings->reset("PastebinURL");
+ }
+
+ bool ok;
+ int pasteType = m_settings->get("PastebinType").toInt(&ok);
+ // If PastebinType is invalid then reset the related settings.
+ if (!ok || !(PasteUpload::PasteType::First <= pasteType && pasteType <= PasteUpload::PasteType::Last))
+ {
+ m_settings->reset("PastebinType");
+ m_settings->reset("PastebinCustomAPIBase");
+ }
+ }
+ // meta URL
+ m_settings->registerSetting("MetaURLOverride", "");
m_settings->registerSetting("CloseAfterLaunch", false);
m_settings->registerSetting("QuitAfterGameStop", false);
@@ -899,7 +927,8 @@ bool Application::createSetupWizard()
return true;
return false;
}();
- bool wizardRequired = javaRequired || languageRequired;
+ bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
+ bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired;
if(wizardRequired)
{
@@ -913,6 +942,11 @@ bool Application::createSetupWizard()
{
m_setupWizard->addPage(new JavaWizardPage(m_setupWizard));
}
+
+ if (pasteInterventionRequired)
+ {
+ m_setupWizard->addPage(new PasteWizardPage(m_setupWizard));
+ }
connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished);
m_setupWizard->show();
return true;
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 8e75be20..bbf80185 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -128,6 +128,8 @@ set(NET_SOURCES
net/PasteUpload.h
net/Sink.h
net/Validator.h
+ net/Upload.cpp
+ net/Upload.h
)
# Game launch logic
@@ -661,6 +663,8 @@ SET(LAUNCHER_SOURCES
ui/setupwizard/JavaWizardPage.h
ui/setupwizard/LanguageWizardPage.cpp
ui/setupwizard/LanguageWizardPage.h
+ ui/setupwizard/PasteWizardPage.cpp
+ ui/setupwizard/PasteWizardPage.h
# GUI - themes
ui/themes/FusionTheme.cpp
@@ -835,6 +839,8 @@ SET(LAUNCHER_SOURCES
ui/dialogs/SkinUploadDialog.h
ui/dialogs/ModDownloadDialog.cpp
ui/dialogs/ModDownloadDialog.h
+ ui/dialogs/ScrollMessageBox.cpp
+ ui/dialogs/ScrollMessageBox.h
# GUI - widgets
ui/widgets/Common.cpp
@@ -890,6 +896,7 @@ SET(LAUNCHER_SOURCES
)
qt5_wrap_ui(LAUNCHER_UI
+ ui/setupwizard/PasteWizardPage.ui
ui/pages/global/AccountListPage.ui
ui/pages/global/JavaPage.ui
ui/pages/global/LauncherPage.ui
@@ -937,6 +944,7 @@ qt5_wrap_ui(LAUNCHER_UI
ui/dialogs/LoginDialog.ui
ui/dialogs/EditAccountDialog.ui
ui/dialogs/ReviewMessageBox.ui
+ ui/dialogs/ScrollMessageBox.ui
)
qt5_add_resources(LAUNCHER_RESOURCES
@@ -955,7 +963,7 @@ qt5_add_resources(LAUNCHER_RESOURCES
######## Windows resource files ########
if(WIN32)
- set(LAUNCHER_RCS ../${Launcher_Branding_WindowsRC})
+ set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC})
endif()
# Add executable
diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp
index 4bad7251..09c2a333 100644
--- a/launcher/InstanceImportTask.cpp
+++ b/launcher/InstanceImportTask.cpp
@@ -60,9 +60,9 @@
#include "net/ChecksumValidator.h"
#include "ui/dialogs/CustomMessageBox.h"
+#include "ui/dialogs/ScrollMessageBox.h"
#include <algorithm>
-#include <iterator>
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent)
{
@@ -72,7 +72,8 @@ InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent)
bool InstanceImportTask::abort()
{
- m_filesNetJob->abort();
+ if (m_filesNetJob)
+ m_filesNetJob->abort();
m_extractFuture.cancel();
return false;
@@ -135,18 +136,20 @@ void InstanceImportTask::processZipPack()
return;
}
- QStringList blacklist = {"instance.cfg", "manifest.json"};
- QString mmcFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
- bool technicFound = QuaZipDir(m_packZip.get()).exists("/bin/modpack.jar") || QuaZipDir(m_packZip.get()).exists("/bin/version.json");
- QString flameFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
- QString modrinthFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "modrinth.index.json");
+ QuaZipDir packZipDir(m_packZip.get());
+
+ // https://docs.modrinth.com/docs/modpacks/format_definition/#storage
+ bool modrinthFound = packZipDir.exists("/modrinth.index.json");
+ bool technicFound = packZipDir.exists("/bin/modpack.jar") || packZipDir.exists("/bin/version.json");
QString root;
- if(!mmcFound.isNull())
+
+ // NOTE: Prioritize modpack platforms that aren't searched for recursively.
+ // Especially Flame has a very common filename for its manifest, which may appear inside overrides for example
+ if(modrinthFound)
{
- // process as MultiMC instance/pack
- qDebug() << "MultiMC:" << mmcFound;
- root = mmcFound;
- m_modpackType = ModpackType::MultiMC;
+ // process as Modrinth pack
+ qDebug() << "Modrinth:" << modrinthFound;
+ m_modpackType = ModpackType::Modrinth;
}
else if (technicFound)
{
@@ -156,19 +159,25 @@ void InstanceImportTask::processZipPack()
extractDir.cd(".minecraft");
m_modpackType = ModpackType::Technic;
}
- else if(!flameFound.isNull())
- {
- // process as Flame pack
- qDebug() << "Flame:" << flameFound;
- root = flameFound;
- m_modpackType = ModpackType::Flame;
- }
- else if(!modrinthFound.isNull())
+ else
{
- // process as Modrinth pack
- qDebug() << "Modrinth:" << modrinthFound;
- root = modrinthFound;
- m_modpackType = ModpackType::Modrinth;
+ QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
+ QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
+
+ if (!mmcRoot.isNull())
+ {
+ // process as MultiMC instance/pack
+ qDebug() << "MultiMC:" << mmcRoot;
+ root = mmcRoot;
+ m_modpackType = ModpackType::MultiMC;
+ }
+ else if(!flameRoot.isNull())
+ {
+ // process as Flame pack
+ qDebug() << "Flame:" << flameRoot;
+ root = flameRoot;
+ m_modpackType = ModpackType::Flame;
+ }
}
if(m_modpackType == ModpackType::Unknown)
{
@@ -385,61 +394,136 @@ void InstanceImportTask::processFlame()
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]()
{
auto results = m_modIdResolver->getResults();
- m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
- for(auto result: results.files)
- {
- QString filename = result.fileName;
- if(!result.required)
- {
- filename += ".disabled";
+ //first check for blocked mods
+ QString text;
+ auto anyBlocked = false;
+ for(const auto& result: results.files.values()) {
+ if (!result.resolved || result.url.isEmpty()) {
+ text += QString("%1: <a href='%2'>%2</a><br/>").arg(result.fileName, result.websiteUrl);
+ anyBlocked = true;
}
+ }
+ if(anyBlocked) {
+ qWarning() << "Blocked mods found, displaying mod list";
+
+ auto message_dialog = new ScrollMessageBox(m_parent,
+ tr("Blocked mods found"),
+ tr("The following mods were blocked on third party launchers.<br/>"
+ "You will need to manually download them and add them to the modpack"),
+ text);
+ message_dialog->setModal(true);
+ message_dialog->show();
+ connect(message_dialog, &QDialog::rejected, [&]() {
+ m_modIdResolver.reset();
+ emitFailed("Canceled");
+ });
+ connect(message_dialog, &QDialog::accepted, [&]() {
+ m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
+ for (const auto &result: m_modIdResolver->getResults().files) {
+ QString filename = result.fileName;
+ if (!result.required) {
+ filename += ".disabled";
+ }
- auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
- auto path = FS::PathCombine(m_stagingPath , relpath);
+ auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
+ auto path = FS::PathCombine(m_stagingPath, relpath);
- switch(result.type)
- {
- case Flame::File::Type::Folder:
- {
- logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
- // fall-through intentional, we treat these as plain old mods and dump them wherever.
+ switch (result.type) {
+ case Flame::File::Type::Folder: {
+ logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
+ // fall-through intentional, we treat these as plain old mods and dump them wherever.
+ }
+ case Flame::File::Type::SingleFile:
+ case Flame::File::Type::Mod: {
+ if (!result.url.isEmpty()) {
+ qDebug() << "Will download" << result.url << "to" << path;
+ auto dl = Net::Download::makeFile(result.url, path);
+ m_filesNetJob->addNetAction(dl);
+ }
+ break;
+ }
+ case Flame::File::Type::Modpack:
+ logWarning(
+ tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(
+ relpath));
+ break;
+ case Flame::File::Type::Cmod2:
+ case Flame::File::Type::Ctoc:
+ case Flame::File::Type::Unknown:
+ logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
+ break;
+ }
}
- case Flame::File::Type::SingleFile:
- case Flame::File::Type::Mod:
- {
- qDebug() << "Will download" << result.url << "to" << path;
- auto dl = Net::Download::makeFile(result.url, path);
- m_filesNetJob->addNetAction(dl);
- break;
+ m_modIdResolver.reset();
+ connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() {
+ m_filesNetJob.reset();
+ emitSucceeded();
+ }
+ );
+ connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) {
+ m_filesNetJob.reset();
+ emitFailed(reason);
+ });
+ connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
+ setProgress(current, total);
+ });
+ setStatus(tr("Downloading mods..."));
+ m_filesNetJob->start();
+ });
+ }else{
+ //TODO extract to function ?
+ m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
+ for (const auto &result: m_modIdResolver->getResults().files) {
+ QString filename = result.fileName;
+ if (!result.required) {
+ filename += ".disabled";
+ }
+
+ auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
+ auto path = FS::PathCombine(m_stagingPath, relpath);
+
+ switch (result.type) {
+ case Flame::File::Type::Folder: {
+ logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
+ // fall-through intentional, we treat these as plain old mods and dump them wherever.
+ }
+ case Flame::File::Type::SingleFile:
+ case Flame::File::Type::Mod: {
+ if (!result.url.isEmpty()) {
+ qDebug() << "Will download" << result.url << "to" << path;
+ auto dl = Net::Download::makeFile(result.url, path);
+ m_filesNetJob->addNetAction(dl);
+ }
+ break;
+ }
+ case Flame::File::Type::Modpack:
+ logWarning(
+ tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(
+ relpath));
+ break;
+ case Flame::File::Type::Cmod2:
+ case Flame::File::Type::Ctoc:
+ case Flame::File::Type::Unknown:
+ logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
+ break;
}
- case Flame::File::Type::Modpack:
- logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath));
- break;
- case Flame::File::Type::Cmod2:
- case Flame::File::Type::Ctoc:
- case Flame::File::Type::Unknown:
- logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
- break;
}
+ m_modIdResolver.reset();
+ connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() {
+ m_filesNetJob.reset();
+ emitSucceeded();
+ }
+ );
+ connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) {
+ m_filesNetJob.reset();
+ emitFailed(reason);
+ });
+ connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
+ setProgress(current, total);
+ });
+ setStatus(tr("Downloading mods..."));
+ m_filesNetJob->start();
}
- m_modIdResolver.reset();
- connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
- {
- m_filesNetJob.reset();
- emitSucceeded();
- }
- );
- connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason)
- {
- m_filesNetJob.reset();
- emitFailed(reason);
- });
- connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
- {
- setProgress(current, total);
- });
- setStatus(tr("Downloading mods..."));
- m_filesNetJob->start();
}
);
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
@@ -501,6 +585,7 @@ void InstanceImportTask::processMultiMC()
void InstanceImportTask::processModrinth()
{
std::vector<Modrinth::File> files;
+ std::vector<Modrinth::File> non_whitelisted_files;
QString minecraftVersion, fabricVersion, quiltVersion, forgeVersion;
try {
QString indexPath = FS::PathCombine(m_stagingPath, "modrinth.index.json");
@@ -515,11 +600,11 @@ void InstanceImportTask::processModrinth()
auto jsonFiles = Json::requireIsArrayOf<QJsonObject>(obj, "files", "modrinth.index.json");
bool had_optional = false;
- for (auto& obj : jsonFiles) {
+ for (auto& modInfo : jsonFiles) {
Modrinth::File file;
- file.path = Json::requireString(obj, "path");
+ file.path = Json::requireString(modInfo, "path");
- auto env = Json::ensureObject(obj, "env");
+ auto env = Json::ensureObject(modInfo, "env");
QString support = Json::ensureString(env, "client", "unsupported");
if (support == "unsupported") {
continue;
@@ -537,7 +622,7 @@ void InstanceImportTask::processModrinth()
file.path += ".disabled";
}
- QJsonObject hashes = Json::requireObject(obj, "hashes");
+ QJsonObject hashes = Json::requireObject(modInfo, "hashes");
QString hash;
QCryptographicHash::Algorithm hashAlgorithm;
hash = Json::ensureString(hashes, "sha1");
@@ -557,13 +642,38 @@ void InstanceImportTask::processModrinth()
file.hashAlgorithm = hashAlgorithm;
// Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode
// (as Modrinth seems to incorrectly handle spaces)
- file.download = Json::requireString(Json::ensureArray(obj, "downloads").first(), "Download URL for " + file.path);
- if (!file.download.isValid() || !Modrinth::validateDownloadUrl(file.download)) {
- throw JSONValidationError("Download URL for " + file.path + " is not a correctly formatted URL");
+
+ file.download = Json::requireString(Json::ensureArray(modInfo, "downloads").first(), "Download URL for " + file.path);
+
+ if (!file.download.isValid()) {
+