diff --git a/.github/workflows/update-flake.yml b/.github/workflows/update-flake.yml index ce86ff798..123028ff2 100644 --- a/.github/workflows/update-flake.yml +++ b/.github/workflows/update-flake.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@cebd211ec2008b83bda8fb0b21c3c072f004fe04 # v31 + - uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31 - uses: DeterminateSystems/update-flake-lock@v26 with: diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index 10c38e3d6..045d987d4 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -166,7 +166,7 @@ class Config { QString DISCORD_URL; QString SUBREDDIT_URL; - QString RESOURCE_BASE = "https://resources.download.minecraft.net/"; + QString DEFAULT_RESOURCE_BASE = "https://resources.download.minecraft.net/"; QString LIBRARY_BASE = "https://libraries.minecraft.net/"; QString IMGUR_BASE_URL = "https://api.imgur.com/3/"; QString FMLLIBS_BASE_URL; diff --git a/flake.lock b/flake.lock index 17b77e22b..b5f6258e1 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1751637120, - "narHash": "sha256-xVNy/XopSfIG9c46nRmPaKfH1Gn/56vQ8++xWA8itO4=", + "lastModified": 1751984180, + "narHash": "sha256-LwWRsENAZJKUdD3SpLluwDmdXY9F45ZEgCb0X+xgOL0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5c724ed1388e53cc231ed98330a60eb2f7be4be3", + "rev": "9807714d6944a957c2e036f84b0ff8caf9930bc0", "type": "github" }, "original": { diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 99b72870b..d7182c48d 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -864,6 +864,15 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // get rid of invalid meta urls if (!metaUrl.isValid() || (metaUrl.scheme() != "http" && metaUrl.scheme() != "https")) m_settings->reset("MetaURLOverride"); + + // Resource URL + m_settings->registerSetting("ResourceURL", BuildConfig.DEFAULT_RESOURCE_BASE); + + QUrl resourceUrl(m_settings->get("ResourceURL").toString()); + + // get rid of invalid resource urls + if (!resourceUrl.isValid() || (resourceUrl.scheme() != "http" && resourceUrl.scheme() != "https")) + m_settings->reset("ResourceURL"); } m_settings->registerSetting("CloseAfterLaunch", false); diff --git a/launcher/minecraft/AssetsUtils.cpp b/launcher/minecraft/AssetsUtils.cpp index 083924dc6..410d1e689 100644 --- a/launcher/minecraft/AssetsUtils.cpp +++ b/launcher/minecraft/AssetsUtils.cpp @@ -298,7 +298,8 @@ QString AssetObject::getLocalPath() QUrl AssetObject::getUrl() { - return BuildConfig.RESOURCE_BASE + getRelPath(); + auto resourceURL = APPLICATION->settings()->get("ResourceURL").toString(); + return resourceURL + getRelPath(); } QString AssetObject::getRelPath() diff --git a/launcher/minecraft/update/AssetUpdateTask.cpp b/launcher/minecraft/update/AssetUpdateTask.cpp index acdddc833..f4a4022e9 100644 --- a/launcher/minecraft/update/AssetUpdateTask.cpp +++ b/launcher/minecraft/update/AssetUpdateTask.cpp @@ -1,5 +1,6 @@ #include "AssetUpdateTask.h" +#include "BuildConfig.h" #include "launch/LaunchStep.h" #include "minecraft/AssetsUtils.h" #include "minecraft/MinecraftInstance.h" @@ -71,7 +72,12 @@ void AssetUpdateTask::assetIndexFinished() auto job = index.getDownloadJob(); if (job) { - setStatus(tr("Getting the assets files from Mojang...")); + QString resourceURL = APPLICATION->settings()->get("ResourceURL").toString(); + QString source = tr("Mojang"); + if (resourceURL != BuildConfig.DEFAULT_RESOURCE_BASE) { + source = QUrl(resourceURL).host(); + } + setStatus(tr("Getting the assets files from %1...").arg(source)); downloadJob = job; connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::emitSucceeded); connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed); diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index c80187c42..adf4c1065 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -493,16 +493,35 @@ bool FlameCreationTask::createInstance() void FlameCreationTask::idResolverSucceeded(QEventLoop& loop) { - auto results = m_modIdResolver->getResults(); + auto results = m_modIdResolver->getResults().files; + + QStringList optionalFiles; + for (auto& result : results) { + if (!result.required) { + optionalFiles << FS::PathCombine(result.targetFolder, result.version.fileName); + } + } + + if (!optionalFiles.empty()) { + OptionalModDialog optionalModDialog(m_parent, optionalFiles); + if (optionalModDialog.exec() == QDialog::Rejected) { + emitAborted(); + loop.quit(); + return; + } + + m_selectedOptionalMods = optionalModDialog.getResult(); + } // first check for blocked mods QList blocked_mods; auto anyBlocked = false; - for (const auto& result : results.files.values()) { + for (const auto& result : results.values()) { if (result.resourceType != PackedResourceType::Mod) { m_otherResources.append(std::make_pair(result.version.fileName, result.targetFolder)); } + // skip optional mods that were not selected if (result.version.downloadUrl.isEmpty()) { BlockedMod blocked_mod; blocked_mod.name = result.version.fileName; @@ -511,6 +530,10 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop) blocked_mod.matched = false; blocked_mod.localPath = ""; blocked_mod.targetFolder = result.targetFolder; + auto fileName = result.version.fileName; + fileName = FS::RemoveInvalidPathChars(fileName); + auto relpath = FS::PathCombine(result.targetFolder, fileName); + blocked_mod.disabled = !result.required && !m_selectedOptionalMods.contains(relpath); blocked_mods.append(blocked_mod); @@ -546,30 +569,12 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) m_filesJob.reset(new NetJob(tr("Mod Download Flame"), APPLICATION->network())); auto results = m_modIdResolver->getResults().files; - QStringList optionalFiles; - for (auto& result : results) { - if (!result.required) { - optionalFiles << FS::PathCombine(result.targetFolder, result.version.fileName); - } - } - - QStringList selectedOptionalMods; - if (!optionalFiles.empty()) { - OptionalModDialog optionalModDialog(m_parent, optionalFiles); - if (optionalModDialog.exec() == QDialog::Rejected) { - emitAborted(); - loop.quit(); - return; - } - - selectedOptionalMods = optionalModDialog.getResult(); - } for (const auto& result : results) { auto fileName = result.version.fileName; fileName = FS::RemoveInvalidPathChars(fileName); auto relpath = FS::PathCombine(result.targetFolder, fileName); - if (!result.required && !selectedOptionalMods.contains(relpath)) { + if (!result.required && !m_selectedOptionalMods.contains(relpath)) { relpath += ".disabled"; } @@ -617,6 +622,8 @@ void FlameCreationTask::copyBlockedMods(QList const& blocked_mods) } auto destPath = FS::PathCombine(m_stagingPath, "minecraft", mod.targetFolder, mod.name); + if (mod.disabled) + destPath += ".disabled"; setStatus(tr("Copying Blocked Mods (%1 out of %2 are done)").arg(QString::number(i), QString::number(total))); diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.h b/launcher/modplatform/flame/FlameInstanceCreationTask.h index 3e586a416..e41ce742e 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.h +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.h @@ -92,4 +92,6 @@ class FlameCreationTask final : public InstanceCreationTask { QList> m_otherResources; std::optional m_instance; + + QStringList m_selectedOptionalMods; }; diff --git a/launcher/ui/dialogs/BlockedModsDialog.h b/launcher/ui/dialogs/BlockedModsDialog.h index b24e76bbf..15d4d4770 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.h +++ b/launcher/ui/dialogs/BlockedModsDialog.h @@ -42,6 +42,7 @@ struct BlockedMod { bool matched; QString localPath; QString targetFolder; + bool disabled = false; bool move = false; }; diff --git a/launcher/ui/pages/global/APIPage.cpp b/launcher/ui/pages/global/APIPage.cpp index a030bf316..3fedbff72 100644 --- a/launcher/ui/pages/global/APIPage.cpp +++ b/launcher/ui/pages/global/APIPage.cpp @@ -76,6 +76,7 @@ APIPage::APIPage(QWidget* parent) : QWidget(parent), ui(new Ui::APIPage) updateBaseURLPlaceholder(ui->pasteTypeComboBox->currentIndex()); // NOTE: this allows http://, but we replace that with https later anyway ui->metaURL->setValidator(new QRegularExpressionValidator(s_validUrlRegExp, ui->metaURL)); + ui->resourceURL->setValidator(new QRegularExpressionValidator(s_validUrlRegExp, ui->resourceURL)); ui->baseURLEntry->setValidator(new QRegularExpressionValidator(s_validUrlRegExp, ui->baseURLEntry)); ui->msaClientID->setValidator(new QRegularExpressionValidator(s_validMSAClientID, ui->msaClientID)); ui->flameKey->setValidator(new QRegularExpressionValidator(s_validFlameKey, ui->flameKey)); @@ -137,6 +138,8 @@ void APIPage::loadSettings() ui->msaClientID->setText(msaClientID); QString metaURL = s->get("MetaURLOverride").toString(); ui->metaURL->setText(metaURL); + QString resourceURL = s->get("ResourceURL").toString(); + ui->resourceURL->setText(resourceURL); QString flameKey = s->get("FlameKeyOverride").toString(); ui->flameKey->setText(flameKey); QString modrinthToken = s->get("ModrinthToken").toString(); @@ -156,18 +159,31 @@ void APIPage::applySettings() QString msaClientID = ui->msaClientID->text(); s->set("MSAClientIDOverride", msaClientID); QUrl metaURL(ui->metaURL->text()); + QUrl resourceURL(ui->resourceURL->text()); // Add required trailing slash if (!metaURL.isEmpty() && !metaURL.path().endsWith('/')) { QString path = metaURL.path(); path.append('/'); metaURL.setPath(path); } + + if (!resourceURL.isEmpty() && !resourceURL.path().endsWith('/')) { + QString path = resourceURL.path(); + path.append('/'); + resourceURL.setPath(path); + } // Don't allow HTTP, since meta is basically RCE with all the jar files. if (!metaURL.isEmpty() && metaURL.scheme() == "http") { metaURL.setScheme("https"); } + // Also don't allow HTTP + if (!resourceURL.isEmpty() && resourceURL.scheme() == "http") { + resourceURL.setScheme("https"); + } + s->set("MetaURLOverride", metaURL.toString()); + s->set("ResourceURL", resourceURL.toString()); QString flameKey = ui->flameKey->text(); s->set("FlameKeyOverride", flameKey); QString modrinthToken = ui->modrinthToken->text(); diff --git a/launcher/ui/pages/global/APIPage.ui b/launcher/ui/pages/global/APIPage.ui index a822f2b99..abc19f006 100644 --- a/launcher/ui/pages/global/APIPage.ui +++ b/launcher/ui/pages/global/APIPage.ui @@ -129,6 +129,38 @@ + + + + Assets Server + + + + + + You can set this to another server if you have problems with downloading assets. + + + Qt::RichText + + + true + + + true + + + + + + + Use Default + + + + + +