diff options
24 files changed, 376 insertions, 144 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index eb560f0e..1ede3f74 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,6 +1,6 @@ name: Bug Report description: File a bug report -labels: [bug, needs-triage] +labels: [bug] body: - type: markdown attributes: @@ -8,7 +8,7 @@ body: If you need help with running Minecraft, please visit us on our Discord before making a bug report. Before submitting a bug report, please make sure you have read this *entire* form, and that: - * You have read the [FAQ](https://github.com/PolyMC/PolyMC/wiki/FAQ) and it has not answered your question + * You have read the [PolyMC wiki](https://polymc.org/wiki/) and it has not answered your question. * Your bug is not caused by Minecraft or any mods you have installed. * Your issue has not been reported before, [make sure to use the search function!](https://github.com/PolyMC/PolyMC/issues) diff --git a/.github/ISSUE_TEMPLATE/rfc.yml b/.github/ISSUE_TEMPLATE/rfc.yml index 664430fe..0a40d01d 100644 --- a/.github/ISSUE_TEMPLATE/rfc.yml +++ b/.github/ISSUE_TEMPLATE/rfc.yml @@ -1,7 +1,7 @@ # Template based on https://gitlab.archlinux.org/archlinux/rfcs/-/blob/0ba3b61e987e197f8d1901709409b8564958f78a/rfcs/0000-template.rst name: Request for Comment (RFC) description: Propose a larger change and start a discussion. -labels: [RFC] +labels: [rfc] body: - type: markdown attributes: @@ -21,8 +21,7 @@ body: Introduce the topic. If this is a not-well-known section of PolyMC, a detailed explanation of the background is recommended. Some example points of discussion: - What specific problems are you facing right now that you're trying to address? - - Are there any previous discussions? Link to them and summarize them (don't - - force your readers to read them though!). + - Are there any previous discussions? Link to them and summarize them (don't force your readers to read them though!). - Is there any precedent set by other software? If so, link to resources. placeholder: I don't like cats. I think many users also don't like cats. validations: diff --git a/.github/ISSUE_TEMPLATE/suggestion.yml b/.github/ISSUE_TEMPLATE/suggestion.yml index b58a6672..48f157b3 100644 --- a/.github/ISSUE_TEMPLATE/suggestion.yml +++ b/.github/ISSUE_TEMPLATE/suggestion.yml @@ -1,6 +1,6 @@ name: Suggestion description: Make a suggestion -labels: [idea, needs-triage] +labels: [enhancement] body: - type: markdown attributes: diff --git a/.github/workflows/pr-comment.yml b/.github/workflows/pr-comment.yml new file mode 100644 index 00000000..7e8e4d99 --- /dev/null +++ b/.github/workflows/pr-comment.yml @@ -0,0 +1,61 @@ +name: Comment on pull request +on: + workflow_run: + workflows: ['Test workflow with upload'] + types: [completed] +jobs: + pr_comment: + if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v5 + with: + # This snippet is public-domain, taken from + # https://github.com/oprypin/nightly.link/blob/master/.github/workflows/pr-comment.yml + script: | + async function upsertComment(owner, repo, issue_number, purpose, body) { + const {data: comments} = await github.rest.issues.listComments( + {owner, repo, issue_number}); + + const marker = `<!-- bot: ${purpose} -->`; + body = marker + "\n" + body; + + const existing = comments.filter((c) => c.body.includes(marker)); + if (existing.length > 0) { + const last = existing[existing.length - 1]; + core.info(`Updating comment ${last.id}`); + await github.rest.issues.updateComment({ + owner, repo, + body, + comment_id: last.id, + }); + } else { + core.info(`Creating a comment in issue / PR #${issue_number}`); + await github.rest.issues.createComment({issue_number, body, owner, repo}); + } + } + + const {owner, repo} = context.repo; + const run_id = ${{github.event.workflow_run.id}}; + + const pull_requests = ${{ toJSON(github.event.workflow_run.pull_requests) }}; + if (!pull_requests.length) { + return core.error("This workflow doesn't match any pull requests!"); + } + + const artifacts = await github.paginate( + github.rest.actions.listWorkflowRunArtifacts, {owner, repo, run_id}); + if (!artifacts.length) { + return core.error(`No artifacts found`); + } + let body = `Download the artifacts for this pull request:\n`; + for (const art of artifacts) { + body += `\n* [${art.name}.zip](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`; + } + + core.info("Review thread message body:", body); + + for (const pr of pull_requests) { + await upsertComment(owner, repo, pr.number, + "nightly-link", body); + } diff --git a/CMakeLists.txt b/CMakeLists.txt index 52170460..b97635c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ if(WIN32) endif() project(Launcher) -enable_testing() +include(CTest) string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD) if(IS_IN_SOURCE_BUILD) diff --git a/cmake/UnitTest.cmake b/cmake/UnitTest.cmake index 9f2bc269..7d7bd4ad 100644 --- a/cmake/UnitTest.cmake +++ b/cmake/UnitTest.cmake @@ -5,44 +5,46 @@ set(TEST_RESOURCE_PATH ${CMAKE_CURRENT_LIST_DIR}) message(${TEST_RESOURCE_PATH}) function(add_unit_test name) - set(options "") - set(oneValueArgs DATA) - set(multiValueArgs SOURCES LIBS) + if(BUILD_TESTING) + set(options "") + set(oneValueArgs DATA) + set(multiValueArgs SOURCES LIBS) - cmake_parse_arguments(OPT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + cmake_parse_arguments(OPT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) - if(WIN32) - add_executable(${name}_test ${OPT_SOURCES} ${TEST_RESOURCE_PATH}/UnitTest/test.rc) - else() - add_executable(${name}_test ${OPT_SOURCES}) - endif() - - if(NOT "${OPT_DATA}" STREQUAL "") - set(TEST_DATA_PATH "${CMAKE_CURRENT_BINARY_DIR}/data") - set(TEST_DATA_PATH_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${OPT_DATA}") - message("From ${TEST_DATA_PATH_SRC} to ${TEST_DATA_PATH}") - string(REGEX REPLACE "[/\\:]" "_" DATA_TARGET_NAME "${TEST_DATA_PATH_SRC}") - if(UNIX) - # on unix we get the third / from the filename - set(TEST_DATA_URL "file://${TEST_DATA_PATH}") + if(WIN32) + add_executable(${name}_test ${OPT_SOURCES} ${TEST_RESOURCE_PATH}/UnitTest/test.rc) else() - # we don't on windows, so we have to add it ourselves - set(TEST_DATA_URL "file:///${TEST_DATA_PATH}") + add_executable(${name}_test ${OPT_SOURCES}) endif() - if(NOT TARGET "${DATA_TARGET_NAME}") - add_custom_target(${DATA_TARGET_NAME}) - add_dependencies(${name}_test ${DATA_TARGET_NAME}) - add_custom_command( - TARGET ${DATA_TARGET_NAME} - COMMAND ${CMAKE_COMMAND} "-DTEST_DATA_URL=${TEST_DATA_URL}" -DSOURCE=${TEST_DATA_PATH_SRC} -DDESTINATION=${TEST_DATA_PATH} -P ${TEST_RESOURCE_PATH}/UnitTest/generate_test_data.cmake - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - ) + + if(NOT "${OPT_DATA}" STREQUAL "") + set(TEST_DATA_PATH "${CMAKE_CURRENT_BINARY_DIR}/data") + set(TEST_DATA_PATH_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${OPT_DATA}") + message("From ${TEST_DATA_PATH_SRC} to ${TEST_DATA_PATH}") + string(REGEX REPLACE "[/\\:]" "_" DATA_TARGET_NAME "${TEST_DATA_PATH_SRC}") + if(UNIX) + # on unix we get the third / from the filename + set(TEST_DATA_URL "file://${TEST_DATA_PATH}") + else() + # we don't on windows, so we have to add it ourselves + set(TEST_DATA_URL "file:///${TEST_DATA_PATH}") + endif() + if(NOT TARGET "${DATA_TARGET_NAME}") + add_custom_target(${DATA_TARGET_NAME}) + add_dependencies(${name}_test ${DATA_TARGET_NAME}) + add_custom_command( + TARGET ${DATA_TARGET_NAME} + COMMAND ${CMAKE_COMMAND} "-DTEST_DATA_URL=${TEST_DATA_URL}" -DSOURCE=${TEST_DATA_PATH_SRC} -DDESTINATION=${TEST_DATA_PATH} -P ${TEST_RESOURCE_PATH}/UnitTest/generate_test_data.cmake + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + endif() endif() - endif() - target_link_libraries(${name}_test Qt5::Test ${OPT_LIBS}) + target_link_libraries(${name}_test Qt5::Test ${OPT_LIBS}) - target_include_directories(${name}_test PRIVATE "${TEST_RESOURCE_PATH}/UnitTest/") + target_include_directories(${name}_test PRIVATE "${TEST_RESOURCE_PATH}/UnitTest/") - add_test(NAME ${name} COMMAND ${name}_test WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + add_test(NAME ${name} COMMAND ${name}_test WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + endif() endfunction() diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 32c7ea71..692aebe5 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -355,21 +355,23 @@ add_unit_test(GradleSpecifier LIBS Launcher_logic ) -add_executable(PackageManifest - mojang/PackageManifest_test.cpp -) -target_link_libraries(PackageManifest - Launcher_logic - Qt5::Test -) -target_include_directories(PackageManifest - PRIVATE ../cmake/UnitTest/ -) -add_test( - NAME PackageManifest - COMMAND PackageManifest - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -) +if(BUILD_TESTING) + add_executable(PackageManifest + mojang/PackageManifest_test.cpp + ) + target_link_libraries(PackageManifest + Launcher_logic + Qt5::Test + ) + target_include_directories(PackageManifest + PRIVATE ../cmake/UnitTest/ + ) + add_test( + NAME PackageManifest + COMMAND PackageManifest + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) +endif() add_unit_test(MojangVersionFormat SOURCES minecraft/MojangVersionFormat_test.cpp diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index 6e5dfeae..65a8b1db 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -153,7 +153,7 @@ QStringList addJavasFromEnv(QList<QString> javas) { QByteArray env = qgetenv("POLYMC_JAVA_PATHS"); #if defined(Q_OS_WIN32) - QList<QString> javaPaths = QString::fromLocal8Bit(env).split(QLatin1String(";")); + QList<QString> javaPaths = QString::fromLocal8Bit(env).replace("\\", "/").split(QLatin1String(";")); #else QList<QString> javaPaths = QString::fromLocal8Bit(env).split(QLatin1String(":")); #endif @@ -355,7 +355,7 @@ QList<QString> JavaUtils::FindJavaPaths() } } - return candidates; + return addJavasFromEnv(candidates); } #elif defined(Q_OS_MAC) diff --git a/launcher/launch/LaunchTask.cpp b/launcher/launch/LaunchTask.cpp index 231a6398..d5442a2b 100644 --- a/launcher/launch/LaunchTask.cpp +++ b/launcher/launch/LaunchTask.cpp @@ -1,18 +1,38 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * - * Authors: Orochimarufan <orochimarufan.x3@gmail.com> + * 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 + * the Free Software Foundation, version 3. * - * 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 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * http://www.apache.org/licenses/LICENSE-2.0 + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. * - * 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. + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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 "launch/LaunchTask.h" @@ -212,7 +232,7 @@ shared_qobject_ptr<LogModel> LaunchTask::getLogModel() m_logModel->setMaxLines(m_instance->getConsoleMaxLines()); m_logModel->setStopOnOverflow(m_instance->shouldStopOnConsoleOverflow()); // FIXME: should this really be here? - m_logModel->setOverflowMessage(tr("PolyMC stopped watching the game log because the log length surpassed %1 lines.\n" + m_logModel->setOverflowMessage(tr("Stopped watching the game log because the log length surpassed %1 lines.\n" "You may have to fix your mods because the game is still logging to files and" " likely wasting harddrive space at an alarming rate!").arg(m_logModel->getMaxLines())); } diff --git a/launcher/launch/steps/CheckJava.cpp b/launcher/launch/steps/CheckJava.cpp index d3f2148c..c2ebb334 100644 --- a/launcher/launch/steps/CheckJava.cpp +++ b/launcher/launch/steps/CheckJava.cpp @@ -1,16 +1,36 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * - * 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 + * 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 + * the Free Software Foundation, version 3. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * 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. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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 "CheckJava.h" @@ -87,14 +107,14 @@ void CheckJava::checkJavaFinished(JavaCheckResult result) // Error message displayed if java can't start emit logLine(QString("Could not start java:"), MessageLevel::Error); emit logLines(result.errorLog.split('\n'), MessageLevel::Error); - emit logLine("\nCheck your PolyMC Java settings.", MessageLevel::Launcher); + emit logLine(QString("\nCheck your Java settings."), MessageLevel::Launcher); printSystemInfo(false, false); emitFailed(QString("Could not start java!")); return; } case JavaCheckResult::Validity::ReturnedInvalidData: { - emit logLine(QString("Java checker returned some invalid data PolyMC doesn't understand:"), MessageLevel::Error); + emit logLine(QString("Java checker returned some invalid data we don't understand:"), MessageLevel::Error); emit logLines(result.outLog.split('\n'), MessageLevel::Warning); emit logLine("\nMinecraft might not start properly.", MessageLevel::Launcher); printSystemInfo(false, false); diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 90bb92a1..c7e60fda 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -1,4 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + * + * 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 + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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 "MinecraftInstance.h" +#include "BuildConfig.h" #include "minecraft/launch/CreateGameFolders.h" #include "minecraft/launch/ExtractNatives.h" #include "minecraft/launch/PrintInstanceInfo.h" @@ -435,7 +471,7 @@ QStringList MinecraftInstance::processMinecraftArgs( } // blatant self-promotion. - token_mapping["profile_name"] = token_mapping["version_name"] = "PolyMC"; + token_mapping["profile_name"] = token_mapping["version_name"] = BuildConfig.LAUNCHER_NAME; token_mapping["version_type"] = profile->getMinecraftVersionType(); diff --git a/launcher/modplatform/ModAPI.h b/launcher/modplatform/ModAPI.h index 5c7c6349..ae6ac80f 100644 --- a/launcher/modplatform/ModAPI.h +++ b/launcher/modplatform/ModAPI.h @@ -1,6 +1,7 @@ #pragma once #include <QString> +#include <QList> namespace ModPlatform { class ListModel; @@ -25,5 +26,13 @@ class ModAPI { }; virtual void searchMods(CallerType* caller, SearchArgs&& args) const = 0; - virtual void getVersions(CallerType* caller, const QString& addonId) const = 0; + + + struct VersionSearchArgs { + QString addonId; + QList<QString> mcVersions; + ModLoaderType loader; + }; + + virtual void getVersions(CallerType* caller, VersionSearchArgs&& args) const = 0; }; diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index 62accfa4..8654a693 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -25,8 +25,8 @@ class FlameAPI : public NetworkModAPI { .arg(args.version); }; - inline auto getVersionsURL(const QString& addonId) const -> QString override + inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override { - return QString("https://addons-ecs.forgesvc.net/api/v2/addon/%1/files").arg(addonId); + return QString("https://addons-ecs.forgesvc.net/api/v2/addon/%1/files").arg(args.addonId); }; }; diff --git a/launcher/modplatform/helpers/NetworkModAPI.cpp b/launcher/modplatform/helpers/NetworkModAPI.cpp index 25c7b9fd..6829b837 100644 --- a/launcher/modplatform/helpers/NetworkModAPI.cpp +++ b/launcher/modplatform/helpers/NetworkModAPI.cpp @@ -31,14 +31,14 @@ void NetworkModAPI::searchMods(CallerType* caller, SearchArgs&& args) const netJob->start(); } -void NetworkModAPI::getVersions(CallerType* caller, const QString& addonId) const +void NetworkModAPI::getVersions(CallerType* caller, VersionSearchArgs&& args) const { - auto netJob = new NetJob(QString("%1::ModVersions(%2)").arg(caller->debugName()).arg(addonId), APPLICATION->network()); + auto netJob = new NetJob(QString("%1::ModVersions(%2)").arg(caller->debugName()).arg(args.addonId), APPLICATION->network()); auto response = new QByteArray(); - netJob->addNetAction(Net::Download::makeByteArray(getVersionsURL(addonId), response)); + netJob->addNetAction(Net::Download::makeByteArray(getVersionsURL(args), response)); - QObject::connect(netJob, &NetJob::succeeded, caller, [response, caller, addonId] { + QObject::connect(netJob, &NetJob::succeeded, caller, [response, caller, args] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { @@ -48,7 +48,7 @@ void NetworkModAPI::getVersions(CallerType* caller, const QString& addonId) cons return; } - caller->versionRequestSucceeded(doc, addonId); + caller->versionRequestSucceeded(doc, args.addonId); }); QObject::connect(netJob, &NetJob::finished, caller, [response, netJob] { diff --git a/launcher/modplatform/helpers/NetworkModAPI.h b/launcher/modplatform/helpers/NetworkModAPI.h index 4d3f7005..000620b2 100644 --- a/launcher/modplatform/helpers/NetworkModAPI.h +++ b/launcher/modplatform/helpers/NetworkModAPI.h @@ -5,9 +5,9 @@ class NetworkModAPI : public ModAPI { public: void searchMods(CallerType* caller, SearchArgs&& args) const override; - void getVersions(CallerType* caller, const QString& addonId) const override; + void getVersions(CallerType* caller, VersionSearchArgs&& args) const override; protected: virtual auto getModSearchURL(SearchArgs& args) const -> QString = 0; - virtual auto getVersionsURL(const QString& addonId) const -> QString = 0; + virtual auto getVersionsURL(VersionSearchArgs& args) const -> QString = 0; }; diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index cf4dec1a..30952e99 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -30,11 +30,27 @@ class ModrinthAPI : public NetworkModAPI { .arg(args.version); }; - inline auto getVersionsURL(const QString& addonId) const -> QString override + inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override { - return QString("https://api.modrinth.com/v2/project/%1/version").arg(addonId); + return QString("https://api.modrinth.com/v2/project/%1/version?" + "game_versions=[%2]" + "loaders=[%3]") + .arg(args.addonId) + .arg(getGameVersionsString(args.mcVersions)) + .arg(getModLoaderString(args.loader)); }; + inline auto getGameVersionsString(QList<QString> mcVersions) const -> QString + { + QString s; + for(int i = 0; i < mcVersions.count(); i++){ + s += mcVersions.at(i); + if(i < mcVersions.count() - 1) + s += ","; + } + return s; + } + inline auto getModLoaderString(ModLoaderType modLoader) const -> QString { switch (modLoader) { diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 82988cf6..5b75f034 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -12,7 +12,7 @@ void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) { pack.addonId = Json::requireString(obj, "project_id"); pack.name = Json::requireString(obj, "title"); - pack.websiteUrl = Json::ensureString(obj, "page_url", ""); + pack.websiteUrl = "https://modrinth.com/mod/" + Json::ensureString(obj, "slug", ""); pack.description = Json::ensureString(obj, "description", ""); pack.logoUrl = Json::requireString(obj, "icon_url"); @@ -30,7 +30,6 @@ void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, BaseInstance* inst) { QVector<ModPlatform::IndexedVersion> unsortedVersions; - bool hasFabric = !(static_cast<MinecraftInstance*>(inst))->getPackProfile()->getComponentVersion("net.fabricmc.fabric-loader").isEmpty(); QString mcVersion = (static_cast<MinecraftInstance*>(inst))->getPackProfile()->getComponentVersion("net.minecraft"); for (auto versionIter : arr) { @@ -61,17 +60,6 @@ void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, auto parent = files[i].toObject(); auto fileName = Json::requireString(parent, "filename"); - // Grab the correct mod loader - if(hasFabric){ - if(fileName.contains("forge",Qt::CaseInsensitive)){ - i++; - continue; - } - } else if(fileName.contains("fabric", Qt::CaseInsensitive)){ - i++; - continue; - } - // Grab the primary file, if available if(Json::requireBoolean(parent, "primary")) break; diff --git a/launcher/ui/dialogs/AboutDialog.cpp b/launcher/ui/dialogs/AboutDialog.cpp index ef96cc23..8dadb755 100644 --- a/launcher/ui/dialogs/AboutDialog.cpp +++ b/launcher/ui/dialogs/AboutDialog.cpp @@ -1,29 +1,63 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * - * 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 + * 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 + * the Free Software Foundation, version 3. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * 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. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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 "AboutDialog.h" +#include "BuildConfig.h" #include "ui_AboutDialog.h" #include <QIcon> #include "Application.h" #include "BuildConfig.h" #include <net/NetJob.h> +#include <qobject.h> #include "HoeDown.h" namespace { +QString getLink(QString link, QString name) { + return QString("<<a href='%1'>%2</a>>").arg(link).arg(name); +} + +QString getWebsite(QString link) { + return getLink(link, QObject::tr("Website")); +} + +QString getGitHub(QString username) { + return getLink("https://github.com/" + username, "GitHub"); +} + // Credits // This is a hack, but I can't think of a better way to do this easily without screwing with QTextDocument... QString getCreditsHtml() @@ -33,15 +67,29 @@ QString getCreditsHtml() stream.setCodec(QTextCodec::codecForName("UTF-8")); stream << "<center>\n"; - stream << "<h3>" << QObject::tr("PolyMC Developers", "About Credits") << "</h3>\n"; - stream << "<p>swirl <<a href='mailto:swurl@swurl.xyz'>swurl@swurl.xyz </a>></p>\n"; - stream << "<p>LennyMcLennington <<a href='mailto:lenny@sneed.church'>lenny@sneed.church</a>></p>\n"; + //: %1 is the name of the launcher, determined at build time, e.g. "PolyMC Developers" + stream << "<h3>" << QObject::tr("%1 Developers", "About Credits").arg(BuildConfig.LAUNCHER_NAME) << "</h3>\n"; + stream << QString("<p>LennyMcLennington %1</p>\n") .arg(getGitHub("LennyMcLennington")); + stream << QString("<p>Sefa Eyeoglu (Scrumplex) %1</p>\n") .arg(getWebsite("https://scrumplex.net")); + stream << QString("<p>dada513 %1</p>\n") .arg(getGitHub("dada513")); + stream << QString("<p>txtsd %1</p>\n") .arg(getGitHub("txtsd")); + stream << QString("<p>timoreo %1</p>\n") .arg(getGitHub("timoreo22")); + stream << QString("<p>Ezekiel Smith (ZekeSmith) %1</p>\n") .arg(getGitHub("ZekeSmith")); + stream << QString("<p>cozyGalvinism %1</p>\n") .arg(getGitHub("cozyGalvinism")); + stream << "<br />\n"; + + //: %1 is the name of the launcher, determined at build time, e.g. "PolyMC Contributors" + stream << "<h3>" << QObject::tr("%1 Contributors", "About Credits").arg(BuildConfig.LAUNCHER_NAME) << "</h3>\n"; + stream << QString("<p>DioEgizio %1</p>\n") .arg(getGitHub("DioEgizio")); + stream << QString("<p>flowln %1</p>\n") .arg(getGitHub("flowln")); + stream << QString("<p>swirl %1</p>\n") .arg(getWebsite("https://swurl.xyz/")); stream << "<br />\n"; // TODO: possibly retrieve from git history at build time? - stream << "<h3>" << QObject::tr("MultiMC Developers", "About Credits") << "</h3>\n"; + //: %1 is the name of the launcher, determined at build time, e.g. "PolyMC Developers" + stream << "<h3>" << QObject::tr("%1 Developers", "About Credits").arg("MultiMC") << "</h3>\n"; stream << "<p>Andrew Okin <<a href='mailto:forkk@forkk.net'>forkk@forkk.net</a>></p>\n"; - stream << "<p>Petr Mrázek <<a href='mailto:peterix@gmail.com'>peterix@gmail.com</a>></p>\n"; + stream << QString("<p>Petr Mrázek <<a href='mailto:peterix@gmail.com'>peterix@gmail.com</a>></p>\n"); stream << "<p>Sky Welch <<a href='mailto:multimc@bunnies.io'>multimc@bunnies.io</a>></p>\n"; stream << "<p>Jan (02JanDal) <<a href='mailto:02jandal@gmail.com'>02jandal@gmail.com</a>></p>\n"; stream << "<p>RoboSky <<a href='https://twitter.com/RoboSky_'>@RoboSky_</a>></p>\n"; diff --git a/launcher/ui/dialogs/AboutDialog.ui b/launcher/ui/dialogs/AboutDialog.ui index 58275c66..f9665c30 100644 --- a/launcher/ui/dialogs/AboutDialog.ui +++ b/launcher/ui/dialogs/AboutDialog.ui @@ -87,7 +87,7 @@ </property> </widget> </item> - <item> + <item> <widget class="QLabel" name="versionLabel"> <property name="alignment"> <set>Qt::AlignCenter</set> @@ -209,13 +209,10 @@ </attribute> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> - <widget class="QTextEdit" name="creditsText"> - <property name="readOnly"> + <widget class="QTextBrowser" name="creditsText"> + <property name="openExternalLinks"> <bool>true</bool> </property> - <property name="textInteractionFlags"> - <set>Qt::TextBrowserInteraction</set> - </property> </widget> </item> </layout> diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index 1edba499..6e1e2183 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -161,10 +161,11 @@ void AccountListPage::on_actionAddMicrosoft_triggered() CustomMessageBox::selectable( this, tr("Microsoft Accounts not available"), + //: %1 refers to the launcher itself tr( - "Microsoft accounts are only usable on macOS 10.13 or newer, with fully updated PolyMC.\n\n" - "Please update both your operating system and PolyMC." - ), + "Microsoft accounts are only usable on macOS 10.13 or newer, with fully updated %1.\n\n" + "Please update both your operating system and %1." + ).arg(BuildConfig.LAUNCHER_NAME), QMessageBox::Warning )->exec(); return; diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index 97c6fe8f..ed37dd1a 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -2,6 +2,7 @@ /* * PolyMC - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> + * Copyright (c) 2022 Sefa Eyeoglu <contact@scrumplex.net> * * 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 @@ -422,7 +423,7 @@ void VersionPage::on_actionDownload_All_triggered() { CustomMessageBox::selectable( this, tr("Error"), - tr("PolyMC cannot download Minecraft or update instances unless you have at least " + tr("Cannot download Minecraft or update instances unless you have at least " "one account added.\nPlease add your Mojang or Minecraft account."), QMessageBox::Warning)->show(); return; diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index cc3c5326..01b5d247 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -1,5 +1,6 @@ #include "ModModel.h" +#include "BuildConfig.h" #include "Json.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" @@ -16,7 +17,6 @@ auto ListModel::debugName() const -> QString return m_parent->debugName(); } - /******** Make data requests ********/ void ListModel::fetchMore(const QModelIndex& parent) @@ -61,19 +61,14 @@ auto ListModel::data(const QModelIndex& index, int role) const -> QVariant void ListModel::requestModVersions(ModPlatform::IndexedPack const& current) { - m_parent->apiProvider()->getVersions(this, current.addonId.toString()); + m_parent->apiProvider()->getVersions(this, + { current.addonId.toString(), getMineVersions(), hasFabric() ? ModAPI::ModLoaderType::Fabric : ModAPI::ModLoaderType::Forge }); } void ListModel::performPaginatedSearch() { - QString mcVersion = (dynamic_cast<MinecraftInstance*>((dynamic_cast<ModPage*>(parent()))->m_instance))->getPackProfile()->getComponentVersion("net.minecraft"); - bool hasFabric = !(dynamic_cast<MinecraftInstance*>((dynamic_cast<ModPage*>(parent()))->m_instance)) - ->getPackProfile() - ->getComponentVersion("net.fabricmc.fabric-loader") - .isEmpty(); - - m_parent->apiProvider()->searchMods( - this, { nextSearchOffset, currentSearchTerm, getSorts()[currentSort], hasFabric ? ModAPI::Fabric : ModAPI::Forge, mcVersion }); + m_parent->apiProvider()->searchMods(this, + { nextSearchOffset, currentSearchTerm, getSorts()[currentSort], hasFabric() ? ModAPI::Fabric : ModAPI::Forge, getMineVersions().at(0) }); } void ListModel::searchWithTerm(const QString& term, const int sort) @@ -131,7 +126,6 @@ void ListModel::requestLogo(QString logo, QString url) m_loadingLogos.append(logo); } - /******** Request callbacks ********/ void ListModel::logoLoaded(QString logo, QIcon out) @@ -186,7 +180,8 @@ void ListModel::searchRequestFailed(QString reason) if (jobPtr->first()->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 409) { // 409 Gone, notify user to update QMessageBox::critical(nullptr, tr("Error"), - QString("%1 %2").arg(m_parent->displayName()).arg(tr("API version too old!\nPlease update PolyMC!"))); + //: %1 refers to the launcher itself + QString("%1 %2").arg(m_parent->displayName()).arg(tr("API version too old!\nPlease update %1!").arg(BuildConfig.LAUNCHER_NAME))); // self-destruct (dynamic_cast<ModDownloadDialog*>((dynamic_cast<ModPage*>(parent()))->parentWidget()))->reject(); } @@ -208,7 +203,7 @@ void ListModel::versionRequestSucceeded(QJsonDocument doc, QString addonId) { auto& current = m_parent->getCurrent(); if (addonId != current.addonId) { return; } - + QJsonArray arr = doc.array(); try { loadIndexedPackVersions(current, arr); @@ -221,3 +216,19 @@ void ListModel::versionRequestSucceeded(QJsonDocument doc, QString addonId) } } // namespace ModPlatform + +/******** Helpers ********/ +auto ModPlatform::ListModel::hasFabric() const -> bool +{ + return !(dynamic_cast<MinecraftInstance*>((dynamic_cast<ModPage*>(parent()))->m_instance)) + ->getPackProfile() + ->getComponentVersion("net.fabricmc.fabric-loader") + .isEmpty(); +} + +auto ModPlatform::ListModel::getMineVersions() const -> QList<QString> +{ + return { (dynamic_cast<MinecraftInstance*>((dynamic_cast<ModPage*>(parent()))->m_instance)) + ->getPackProfile() + ->getComponentVersion("net.minecraft") }; +} diff --git a/launcher/ui/pages/modplatform/ModModel.h b/launcher/ui/pages/modplatform/ModModel.h index 02be6049..64cfa71e 100644 --- a/launcher/ui/pages/modplatform/ModModel.h +++ b/launcher/ui/pages/modplatform/ModModel.h @@ -62,6 +62,9 @@ class ListModel : public QAbstractListModel { void requestLogo(QString file, QString url); + inline auto hasFabric() const -> bool; + inline auto getMineVersions() const -> QList<QString>; + protected: ModPage* m_parent; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index daa43e26..b788860a 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -1,3 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + * + * 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 + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + #include "ModrinthModel.h" #include "modplatform/modrinth/ModrinthPackIndex.h" |