diff options
74 files changed, 1232 insertions, 900 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d8fc1ff2..4e52af4e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,6 +61,7 @@ jobs: pacboy: >- toolchain:p cmake:p + extra-cmake-modules:p ninja:p qt5:p ccache:p @@ -107,7 +108,7 @@ jobs: if: runner.os == 'macOS' run: | brew update - brew install qt@5 ninja + brew install qt@5 ninja extra-cmake-modules - name: Update Qt (AppImage) if: runner.os == 'Linux' && matrix.appimage == true @@ -121,7 +122,7 @@ jobs: if: runner.os == 'Linux' run: | sudo apt-get -y update - sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 ninja-build qt5-image-formats-plugins + sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 ninja-build qt5-image-formats-plugins extra-cmake-modules - name: Prepare AppImage (Linux) if: runner.os == 'Linux' && matrix.appimage == true @@ -168,6 +169,21 @@ jobs: cmake --build ${{ env.BUILD_DIR }} ## + # TEST + ## + + - name: Test + if: runner.os != 'Windows' + run: | + ctest --test-dir build --output-on-failure + + - name: Test (Windows) + if: runner.os == 'Windows' + shell: msys2 {0} + run: | + ctest --test-dir build --output-on-failure + + ## # PACKAGE BUILDS ## diff --git a/CMakeLists.txt b/CMakeLists.txt index b09e7fd2..5c5144f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,14 +6,12 @@ if(WIN32) endif() project(Launcher) -include(CTest) string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD) if(IS_IN_SOURCE_BUILD) message(FATAL_ERROR "You are building the Launcher in-source. Please separate the build tree from the source tree.") endif() - ##################################### Set CMake options ##################################### set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) @@ -63,6 +61,16 @@ if(ENABLE_LTO) endif() endif() +option(BUILD_TESTING "Build the testing tree." ON) + +find_package(ECM REQUIRED NO_MODULE) +set(CMAKE_MODULE_PATH "${ECM_MODULE_PATH};${CMAKE_MODULE_PATH}") +include(CTest) +include(ECMAddTests) +if (BUILD_TESTING) + enable_testing() +endif() + ##################################### Set Application options ##################################### ######## Set URLs ######## @@ -102,8 +110,6 @@ set(Launcher_MATRIX_URL "https://matrix.to/#/#polymc:matrix.org" CACHE STRING "U # Discord URL set(Launcher_DISCORD_URL "https://discord.gg/Z52pwxWCHP" CACHE STRING "URL for the Discord guild.") - - # Subreddit URL set(Launcher_SUBREDDIT_URL "https://www.reddit.com/r/PolyMCLauncher/" CACHE STRING "URL for the subreddit.") @@ -1,33 +1,36 @@ # PolyMC - Copyright (C) 2012-2021 MultiMC Contributors - Copyright (C) 2021-2022 PolyMC Contributors + PolyMC - Minecraft Launcher + Copyright (C) 2021-2022 PolyMC Contributors - 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 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. + 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: - You should have received a copy of the GNU General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. + Copyright 2013-2021 MultiMC Contributors -# Launcher (https://github.com/MultiMC/Launcher) - Copyright 2012-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 + 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 + 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. + 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. # MinGW runtime (Windows) @@ -213,6 +216,57 @@ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# launcher (`libraries/launcher`) + + PolyMC - Minecraft Launcher + Copyright (C) 2021-2022 PolyMC Contributors + + 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. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give + you permission to link this library with independent modules to + produce an executable, regardless of the license terms of these + independent modules, and to copy and distribute the resulting + executable under terms of your choice, provided that you also meet, + for each linked independent module, the terms and conditions of the + license of that module. An independent module is a module which is + not derived from or based on this library. If you modify this + library, you may extend this exception to your version of the + library, but you are not obliged to do so. If you do not wish to do + so, delete this exception statement from your version. + + 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. + # lionshead Code has been taken from https://github.com/natefoo/lionshead and loosely diff --git a/cmake/UnitTest.cmake b/cmake/UnitTest.cmake deleted file mode 100644 index 7d7bd4ad..00000000 --- a/cmake/UnitTest.cmake +++ /dev/null @@ -1,50 +0,0 @@ -find_package(Qt5Test REQUIRED) - -set(TEST_RESOURCE_PATH ${CMAKE_CURRENT_LIST_DIR}) - -message(${TEST_RESOURCE_PATH}) - -function(add_unit_test name) - if(BUILD_TESTING) - set(options "") - set(oneValueArgs DATA) - set(multiValueArgs SOURCES LIBS) - - 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}") - 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() - - target_link_libraries(${name}_test Qt5::Test ${OPT_LIBS}) - - target_include_directories(${name}_test PRIVATE "${TEST_RESOURCE_PATH}/UnitTest/") - - add_test(NAME ${name} COMMAND ${name}_test WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - endif() -endfunction() diff --git a/cmake/UnitTest/TestUtil.h b/cmake/UnitTest/TestUtil.h deleted file mode 100644 index ebe3c662..00000000 --- a/cmake/UnitTest/TestUtil.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include <QFile> -#include <QCoreApplication> -#include <QTest> -#include <QDir> - -#define expandstr(s) expandstr2(s) -#define expandstr2(s) #s - -class TestsInternal -{ -public: - static QByteArray readFile(const QString &fileName) - { - QFile f(fileName); - f.open(QFile::ReadOnly); - return f.readAll(); - } - static QString readFileUtf8(const QString &fileName) - { - return QString::fromUtf8(readFile(fileName)); - } -}; - -#define GET_TEST_FILE(file) TestsInternal::readFile(QFINDTESTDATA(file)) -#define GET_TEST_FILE_UTF8(file) TestsInternal::readFileUtf8(QFINDTESTDATA(file)) - diff --git a/cmake/UnitTest/generate_test_data.cmake b/cmake/UnitTest/generate_test_data.cmake deleted file mode 100644 index d0bd4ab1..00000000 --- a/cmake/UnitTest/generate_test_data.cmake +++ /dev/null @@ -1,23 +0,0 @@ -# Copy files from source directory to destination directory, substituting any -# variables. Create destination directory if it does not exist. - -function(configure_files srcDir destDir) - make_directory(${destDir}) - - file(GLOB templateFiles RELATIVE ${srcDir} ${srcDir}/*) - foreach(templateFile ${templateFiles}) - set(srcTemplatePath ${srcDir}/${templateFile}) - if(NOT IS_DIRECTORY ${srcTemplatePath}) - configure_file( - ${srcTemplatePath} - ${destDir}/${templateFile} - @ONLY - NEWLINE_STYLE LF - ) - else() - configure_files("${srcTemplatePath}" "${destDir}/${templateFile}") - endif() - endforeach() -endfunction() - -configure_files(${SOURCE} ${DESTINATION})
\ No newline at end of file diff --git a/cmake/UnitTest/test.manifest b/cmake/UnitTest/test.manifest deleted file mode 100644 index dc5f9d8f..00000000 --- a/cmake/UnitTest/test.manifest +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"> - <assemblyIdentity name="Launcher.Test.0" type="win32" version="5.0.0.0" /> - <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> - <security> - <requestedPrivileges> - <requestedExecutionLevel level="asInvoker" uiAccess="false"/> - </requestedPrivileges> - </security> - </trustInfo> - <dependency> - <dependentAssembly> - <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"/> - </dependentAssembly> - </dependency> - <description>Custom Minecraft launcher for managing multiple installs.</description> - <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> - <application> - <!--The ID below indicates app support for Windows Vista --> - <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> - <!--The ID below indicates app support for Windows 7 --> - <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> - <!--The ID below indicates app support for Windows Developer Preview / Windows 8 --> - <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> - </application> - </compatibility> -</assembly> diff --git a/cmake/UnitTest/test.rc b/cmake/UnitTest/test.rc deleted file mode 100644 index 6c0f0641..00000000 --- a/cmake/UnitTest/test.rc +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include <windows.h> - -1 RT_MANIFEST "test.manifest" - -VS_VERSION_INFO VERSIONINFO -FILEVERSION 1,0,0,0 -FILEOS VOS_NT_WINDOWS32 -FILETYPE VFT_APP -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "000004b0" - BEGIN - VALUE "CompanyName", "MultiMC & PolyMC Contributors" - VALUE "FileDescription", "Testcase" - VALUE "FileVersion", "1.0.0.0" - VALUE "ProductName", "Launcher Testcase" - VALUE "ProductVersion", "5" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x0000, 0x04b0 // Unicode - END -END diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index b8db803b..dff5cb67 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -4,8 +4,6 @@ project(application) ######## Sources and headers ######## -include (UnitTest) - set(CORE_SOURCES # LOGIC - Base classes and infrastructure BaseInstaller.h @@ -90,16 +88,11 @@ set(CORE_SOURCES MMCTime.cpp ) -add_unit_test(FileSystem - SOURCES FileSystem_test.cpp - LIBS Launcher_logic - DATA testdata - ) +ecm_add_test(FileSystem_test.cpp LINK_LIBRARIES Launcher_logic Qt5::Test + TEST_NAME FileSystem) # TODO: needs testdata -add_unit_test(GZip - SOURCES GZip_test.cpp - LIBS Launcher_logic - ) +ecm_add_test(GZip_test.cpp LINK_LIBRARIES Launcher_logic Qt5::Test + TEST_NAME GZip) set(PATHMATCHER_SOURCES # Path matchers @@ -168,16 +161,10 @@ if (Launcher_UPDATER_BASE) updater/DownloadTask.cpp ) - add_unit_test(UpdateChecker - SOURCES updater/UpdateChecker_test.cpp - LIBS Launcher_logic - DATA updater/testdata - ) - add_unit_test(DownloadTask - SOURCES updater/DownloadTask_test.cpp - LIBS Launcher_logic - DATA updater/testdata - ) + ecm_add_test(updater/UpdateChecker_test.cpp LINK_LIBRARIES Launcher_logic Qt5::Test + TEST_NAME UpdateChecker) + ecm_add_test(updater/DownloadTask_test.cpp LINK_LIBRARIES Launcher_logic Qt5::Test + TEST_NAME DownloadTask) endif() # Backend for the news bar... there's usually no news. @@ -359,10 +346,8 @@ set(MINECRAFT_SOURCES mojang/PackageManifest.cpp minecraft/Agent.h) -add_unit_test(GradleSpecifier - SOURCES minecraft/GradleSpecifier_test.cpp - LIBS Launcher_logic - ) +ecm_add_test(minecraft/GradleSpecifier_test.cpp LINK_LIBRARIES Launcher_logic Qt5::Test + TEST_NAME GradleSpecifier) if(BUILD_TESTING) add_executable(PackageManifest @@ -382,28 +367,20 @@ if(BUILD_TESTING) ) endif() -add_unit_test(MojangVersionFormat - SOURCES minecraft/MojangVersionFormat_test.cpp - LIBS Launcher_logic - DATA minecraft/testdata - ) +# TODO: needs minecraft/testdata +ecm_add_test(minecraft/MojangVersionFormat_test.cpp LINK_LIBRARIES Launcher_logic Qt5::Test + TEST_NAME MojangVersionFormat) -add_unit_test(Library - SOURCES minecraft/Library_test.cpp - LIBS Launcher_logic - ) +ecm_add_test(minecraft/Library_test.cpp LINK_LIBRARIES Launcher_logic Qt5::Test + TEST_NAME Library) # FIXME: shares data with FileSystem test -add_unit_test(ModFolderModel - SOURCES minecraft/mod/ModFolderModel_test.cpp - DATA testdata - LIBS Launcher_logic - ) +# TODO: needs testdata +ecm_add_test(minecraft/mod/ModFolderModel_test.cpp LINK_LIBRARIES Launcher_logic Qt5::Test + TEST_NAME ModFolderModel) -add_unit_test(ParseUtils - SOURCES minecraft/ParseUtils_test.cpp - LIBS Launcher_logic - ) +ecm_add_test(minecraft/ParseUtils_test.cpp LINK_LIBRARIES Launcher_logic Qt5::Test + TEST_NAME ParseUtils) # the screenshots feature set(SCREENSHOTS_SOURCES @@ -418,14 +395,14 @@ set(TASKS_SOURCES # Tasks tasks/Task.h tasks/Task.cpp + tasks/ConcurrentTask.h + tasks/ConcurrentTask.cpp tasks/SequentialTask.h tasks/SequentialTask.cpp ) -add_unit_test(Task - SOURCES tasks/Task_test.cpp - LIBS Launcher_logic - ) +ecm_add_test(tasks/Task_test.cpp LINK_LIBRARIES Launcher_logic Qt5::Test + TEST_NAME Task) set(SETTINGS_SOURCES # Settings @@ -443,10 +420,8 @@ set(SETTINGS_SOURCES settings/SettingsObject.h ) -add_unit_test(INIFile - SOURCES settings/INIFile_test.cpp - LIBS Launcher_logic - ) +ecm_add_test(settings/INIFile_test.cpp LINK_LIBRARIES Launcher_logic Qt5::Test + TEST_NAME INIFile) set(JAVA_SOURCES java/JavaChecker.h @@ -463,10 +438,8 @@ set(JAVA_SOURCES java/JavaVersion.cpp ) -add_unit_test(JavaVersion - SOURCES java/JavaVersion_test.cpp - LIBS Launcher_logic - ) +ecm_add_test(java/JavaVersion_test.cpp LINK_LIBRARIES Launcher_logic Qt5::Test + TEST_NAME JavaVersion) set(TRANSLATIONS_SOURCES translations/TranslationsModel.h @@ -558,11 +531,9 @@ set(PACKWIZ_SOURCES modplatform/packwiz/Packwiz.cpp ) -add_unit_test(Packwiz - SOURCES modplatform/packwiz/Packwiz_test.cpp - DATA modplatform/packwiz/testdata - LIBS Launcher_logic - ) +# TODO: needs modplatform/packwiz/testdata +ecm_add_test(modplatform/packwiz/Packwiz_test.cpp LINK_LIBRARIES Launcher_logic Qt5::Test + TEST_NAME Packwiz) set(TECHNIC_SOURCES modplatform/technic/SingleZipPackInstallTask.h @@ -586,10 +557,8 @@ set(ATLAUNCHER_SOURCES modplatform/atlauncher/ATLShareCode.h ) -add_unit_test(Index - SOURCES meta/Index_test.cpp - LIBS Launcher_logic - ) +ecm_add_test(meta/Index_test.cpp LINK_LIBRARIES Launcher_logic Qt5::Test + TEST_NAME Index) ################################ COMPILE ################################ @@ -717,6 +686,8 @@ SET(LAUNCHER_SOURCES ui/pages/BasePageProvider.h # GUI - instance pages + ui/pages/instance/ExternalResourcesPage.cpp + ui/pages/instance/ExternalResourcesPage.h ui/pages/instance/GameOptionsPage.cpp ui/pages/instance/GameOptionsPage.h ui/pages/instance/VersionPage.cpp @@ -845,6 +816,8 @@ SET(LAUNCHER_SOURCES ui/dialogs/NewComponentDialog.h ui/dialogs/NewInstanceDialog.cpp ui/dialogs/NewInstanceDialog.h + ui/dialogs/NewsDialog.cpp + ui/dialogs/NewsDialog.h ui/pagedialog/PageDialog.cpp ui/pagedialog/PageDialog.h ui/dialogs/ProgressDialog.cpp @@ -924,7 +897,7 @@ qt5_wrap_ui(LAUNCHER_UI ui/pages/global/ProxyPage.ui ui/pages/global/MinecraftPage.ui ui/pages/global/ExternalToolsPage.ui - ui/pages/instance/ModFolderPage.ui + ui/pages/instance/ExternalResourcesPage.ui ui/pages/instance/NotesPage.ui ui/pages/instance/LogPage.ui ui/pages/instance/ServersPage.ui @@ -954,6 +927,7 @@ qt5_wrap_ui(LAUNCHER_UI ui/dialogs/NewInstanceDialog.ui ui/dialogs/UpdateDialog.ui ui/dialogs/NewComponentDialog.ui + ui/dialogs/NewsDialog.ui ui/dialogs/ProfileSelectDialog.ui ui/dialogs/SkinUploadDialog.ui ui/dialogs/ExportInstanceDialog.ui diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index bc942ab3..31c7af70 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -120,11 +120,6 @@ bool checkProblemticPathJava(QDir folder); // Get the Directory representing the User's Desktop QString getDesktopDir(); -// Create a shortcut at *location*, pointing to *dest* called with the arguments *args* -// call it *name* and assign it the icon *icon* -// return true if operation succeeded -bool createShortCut(QString location, QString dest, QStringList args, QString name, QString iconLocation); - // Overrides one folder with the contents of another, preserving items exclusive to the first folder // Equivalent to doing QDir::rename, but allowing for overrides bool overrideFolder(QString overwritten_path, QString override_path); diff --git a/launcher/FileSystem_test.cpp b/launcher/FileSystem_test.cpp index 90ddc499..99ae9269 100644 --- a/launcher/FileSystem_test.cpp +++ b/launcher/FileSystem_test.cpp @@ -1,7 +1,6 @@ #include <QTest> #include <QTemporaryDir> #include <QStandardPaths> -#include "TestUtil.h" #include "FileSystem.h" @@ -81,7 +80,7 @@ slots: void test_copy() { - QString folder = QFINDTESTDATA("data/test_folder"); + QString folder = QFINDTESTDATA("testdata/test_folder"); auto f = [&folder]() { QTemporaryDir tempDir; @@ -116,47 +115,6 @@ slots: { QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); } - -// this is only valid on linux -// FIXME: implement on windows, OSX, then test. -#if defined(Q_OS_LINUX) - void test_createShortcut_data() - { - QTest::addColumn<QString>("location"); - QTest::addColumn<QString>("dest"); - QTest::addColumn<QStringList>("args"); - QTest::addColumn<QString>("name"); - QTest::addColumn<QString>("iconLocation"); - QTest::addColumn<QByteArray>("result"); - - QTest::newRow("unix") << QDir::currentPath() - << "asdfDest" - << (QStringList() << "arg1" << "arg2") - << "asdf" - << QString() - #if defined(Q_OS_LINUX) - << GET_TEST_FILE("data/FileSystem-test_createShortcut-unix") - #elif defined(Q_OS_WIN) - << QByteArray() - #endif - ; - } - - void test_createShortcut() - { - QFETCH(QString, location); - QFETCH(QString, dest); - QFETCH(QStringList, args); - QFETCH(QString, name); - QFETCH(QString, iconLocation); - QFETCH(QByteArray, result); - - QVERIFY(FS::createShortCut(location, dest, args, name, iconLocation)); - QCOMPARE(QString::fromLocal8Bit(TestsInternal::readFile(location + QDir::separator() + name + ".desktop")), QString::fromLocal8Bit(result)); - - //QDir().remove(location); - } -#endif }; QTEST_GUILESS_MAIN(FileSystemTest) diff --git a/launcher/GZip_test.cpp b/launcher/GZip_test.cpp index 3f4d181c..73859fbc 100644 --- a/launcher/GZip_test.cpp +++ b/launcher/GZip_test.cpp @@ -1,5 +1,4 @@ #include <QTest> -#include "TestUtil.h" #include "GZip.h" #include <random> diff --git a/launcher/InstancePageProvider.h b/launcher/InstancePageProvider.h index 357157d0..78fb7016 100644 --- a/launcher/InstancePageProvider.h +++ b/launcher/InstancePageProvider.h @@ -33,10 +33,10 @@ public: values.append(new LogPage(inst)); std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst); values.append(new VersionPage(onesix.get())); - auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList(), "mods", "loadermods", tr("Mods"), "Loader-mods"); + auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList()); modsPage->setFilter("%1 (*.zip *.jar *.litemod)"); values.append(modsPage); - values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList(), "coremods", "coremods", tr("Core mods"), "Core-mods")); + values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList())); values.append(new ResourcePackPage(onesix.get())); values.append(new TexturePackPage(onesix.get())); values.append(new ShaderPackPage(onesix.get())); diff --git a/launcher/java/JavaVersion_test.cpp b/launcher/java/JavaVersion_test.cpp index 10ae13a7..545947ef 100644 --- a/launcher/java/JavaVersion_test.cpp +++ b/launcher/java/JavaVersion_test.cpp @@ -1,5 +1,4 @@ #include <QTest> -#include "TestUtil.h" #include "java/JavaVersion.h" diff --git a/launcher/meta/Index_test.cpp b/launcher/meta/Index_test.cpp index 5d3ddc50..261858c4 100644 --- a/launcher/meta/Index_test.cpp +++ b/launcher/meta/Index_test.cpp @@ -1,5 +1,4 @@ #include <QTest> -#include "TestUtil.h" #include "meta/Index.h" #include "meta/VersionList.h" diff --git a/launcher/minecraft/ComponentUpdateTask.cpp b/launcher/minecraft/ComponentUpdateTask.cpp index ff7ed0af..6db21622 100644 --- a/launcher/minecraft/ComponentUpdateTask.cpp +++ b/launcher/minecraft/ComponentUpdateTask.cpp @@ -197,6 +197,10 @@ void ComponentUpdateTask::loadComponents() { remoteLoadFailed(taskIndex, error); }); + connect(indexLoadTask.get(), &Task::aborted, [=]() + { + remoteLoadFailed(taskIndex, tr("Aborted")); + }); taskIndex++; } } @@ -243,6 +247,10 @@ void ComponentUpdateTask::loadComponents() { remoteLoadFailed(taskIndex, error); }); + connect(loadTask.get(), &Task::aborted, [=]() + { + remoteLoadFailed(taskIndex, tr("Aborted")); + }); RemoteLoadStatus status; status.type = loadType; status.PackProfileIndex = componentIndex; diff --git a/launcher/minecraft/GradleSpecifier_test.cpp b/launcher/minecraft/GradleSpecifier_test.cpp index 0900c9d8..a062dfac 100644 --- a/launcher/minecraft/GradleSpecifier_test.cpp +++ b/launcher/minecraft/GradleSpecifier_test.cpp @@ -1,5 +1,4 @@ #include <QTest> -#include "TestUtil.h" #include "minecraft/GradleSpecifier.h" diff --git a/launcher/minecraft/Library_test.cpp b/launcher/minecraft/Library_test.cpp index 47531ad6..834dd558 100644 --- a/launcher/minecraft/Library_test.cpp +++ b/launcher/minecraft/Library_test.cpp @@ -1,5 +1,4 @@ #include <QTest> -#include "TestUtil.h" #include "minecraft/MojangVersionFormat.h" #include "minecraft/OneSixVersionFormat.h" @@ -11,15 +10,14 @@ class LibraryTest : public QObject { Q_OBJECT private: - LibraryPtr readMojangJson(const char *file) + LibraryPtr readMojangJson(const QString path) { - 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); + return MojangVersionFormat::libraryFromJson(problems, QJsonDocument::fromJson(data).object(), path); } // get absolute path to expected storage, assuming default cache prefix QStringList getStorage(QString relative) @@ -32,7 +30,7 @@ slots: { cache.reset(new HttpMetaCache()); cache->addBase("libraries", QDir("libraries").absolutePath()); - dataDir = QDir("data").absolutePath(); + dataDir = QDir(QFINDTESTDATA("testdata")).absolutePath(); } void test_legacy() { @@ -74,14 +72,14 @@ slots: QCOMPARE(test.isNative(), false); QStringList failedFiles; test.setHint("local"); - auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString("data")); + auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QFINDTESTDATA("testdata")); 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()}); + test.getApplicableFiles(currentSystem, jar, native, native32, native64, QFINDTESTDATA("testdata")); + QCOMPARE(jar, {QFileInfo(QFINDTESTDATA("testdata/codecwav-20101023.jar")).absoluteFilePath()}); QCOMPARE(native, {}); QCOMPARE(native32, {}); QCOMPARE(native64, {}); @@ -167,20 +165,20 @@ slots: test.setRepositoryURL("file://foo/bar"); { QStringList jar, native, native32, native64; - test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QString("data")); + test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QFINDTESTDATA("testdata")); QCOMPARE(jar, {}); QCOMPARE(native, {}); - QCOMPARE(native32, {QFileInfo("data/testname-testversion-linux-32.jar").absoluteFilePath()}); - QCOMPARE(native64, {QFileInfo("data/testname-testversion-linux-64.jar").absoluteFilePath()}); + QCOMPARE(native32, {QFileInfo(QFINDTESTDATA("testdata/testname-testversion-linux-32.jar")).absoluteFilePath()}); + QCOMPARE(native64, {QFileInfo(QFINDTESTDATA("testdata") + "/testname-testversion-linux-64.jar").absoluteFilePath()}); QStringList failedFiles; - auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString("data")); + auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QFINDTESTDATA("testdata")); QCOMPARE(dls.size(), 0); - QCOMPARE(failedFiles, {"data/testname-testversion-linux-64.jar"}); + QCOMPARE(failedFiles, {QFileInfo(QFINDTESTDATA("testdata") + "/testname-testversion-linux-64.jar").absoluteFilePath()}); } } void test_onenine() { - auto test = readMojangJson("data/lib-simple.json"); + auto test = readMojangJson(QFINDTESTDATA("testdata/lib-simple.json")); { QStringList jar, native, native32, native64; test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString()); @@ -199,41 +197,41 @@ slots: 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()}); + test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QFINDTESTDATA("testdata")); + QCOMPARE(jar, {QFileInfo(QFINDTESTDATA("testdata/codecwav-20101023.jar")).absoluteFilePath()}); QCOMPARE(native, {}); QCOMPARE(native32, {}); QCOMPARE(native64, {}); } { QStringList failedFiles; - auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString("data")); + auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QFINDTESTDATA("testdata")); QCOMPARE(dls.size(), 0); QCOMPARE(failedFiles, {}); } } void test_onenine_local_override() { - auto test = readMojangJson("data/lib-simple.json"); + auto test = readMojangJson(QFINDTESTDATA("testdata/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()}); + test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QFINDTESTDATA("testdata")); + QCOMPARE(jar, {QFileInfo(QFINDTESTDATA("testdata/codecwav-20101023.jar")).absoluteFilePath()}); QCOMPARE(native, {}); QCOMPARE(native32, {}); QCOMPARE(native64, {}); } { QStringList failedFiles; - auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString("data")); + auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QFINDTESTDATA("testdata")); QCOMPARE(dls.size(), 0); QCOMPARE(failedFiles, {}); } } void test_onenine_native() { - auto test = readMojangJson("data/lib-native.json"); + auto test = readMojangJson(QFINDTESTDATA("testdata/lib-native.json")); QStringList jar, native, native32, native64; test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString()); QCOMPARE(jar, QStringList()); @@ -248,7 +246,7 @@ slots: } void test_onenine_native_arch() { - auto test = readMojangJson("data/lib-native-arch.json"); + auto test = readMojangJson(QFINDTESTDATA("testdata/lib-native-arch.json")); QStringList jar, native, native32, native64; test->getApplicableFiles(Os_Windows, jar, native, native32, native64, QString()); QCOMPARE(jar, {}); diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 7e72601f..88b1f7f3 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -747,7 +747,9 @@ QMap<QString, QString> MinecraftInstance::createCensorFilterFromSession(AuthSess { addToFilter(sessionRef.session, tr("<SESSION ID>")); } - addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>")); + if (sessionRef.access_token != "offline") { + addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>")); + } if(sessionRef.client_token.size()) { addToFilter(sessionRef.client_token, tr("<CLIENT TOKEN>")); } @@ -826,8 +828,16 @@ QString MinecraftInstance::getStatusbarDescription() traits.append(tr("broken")); } + QString mcVersion = m_components->getComponentVersion("net.minecraft"); + if (mcVersion.isEmpty()) + { + // Load component info if needed + m_components->reload(Net::Mode::Offline); + mcVersion = m_components->getComponentVersion("net.minecraft"); + } + QString description; - description.append(tr("Minecraft %1 (%2)").arg(m_components->getComponentVersion("net.minecraft")).arg(typeName())); + description.append(tr("Minecraft %1").arg(mcVersion)); if(m_settings->get("ShowGameTime").toBool()) { if (lastTimePlayed() > 0) { diff --git a/launcher/minecraft/MinecraftLoadAndCheck.cpp b/launcher/minecraft/MinecraftLoadAndCheck.cpp index 79b0c484..d72bc7be 100644 --- a/launcher/minecraft/MinecraftLoadAndCheck.cpp +++ b/launcher/minecraft/MinecraftLoadAndCheck.cpp @@ -20,6 +20,7 @@ void MinecraftLoadAndCheck::executeTask() } connect(m_task.get(), &Task::succeeded, this, &MinecraftLoadAndCheck::subtaskSucceeded); connect(m_task.get(), &Task::failed, this, &MinecraftLoadAndCheck::subtaskFailed); + connect(m_task.get(), &Task::aborted, this, [this]{ subtaskFailed(tr("Aborted")); }); connect(m_task.get(), &Task::progress, this, &MinecraftLoadAndCheck::progress); connect(m_task.get(), &Task::status, this, &MinecraftLoadAndCheck::setStatus); } diff --git a/launcher/minecraft/MinecraftUpdate.cpp b/launcher/minecraft/MinecraftUpdate.cpp index 32e9cbb6..0ce0c347 100644 --- a/launcher/minecraft/MinecraftUpdate.cpp +++ b/launcher/minecraft/MinecraftUpdate.cpp @@ -98,6 +98,7 @@ void MinecraftUpdate::next() 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::aborted, this, &Task::abort); disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress); disconnect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus); } @@ -115,6 +116,7 @@ void MinecraftUpdate::next() } connect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded); connect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed); + connect(task.get(), &Task::aborted, this, &Task::abort); 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 diff --git a/launcher/minecraft/MinecraftUpdate.h b/launcher/minecraft/MinecraftUpdate.h index 9ebef656..acf2eb86 100644 --- a/launcher/minecraft/MinecraftUpdate.h +++ b/launcher/minecraft/MinecraftUpdate.h @@ -27,6 +27,8 @@ class MinecraftVersion; class MinecraftInstance; +// FIXME: This looks very similar to a SequentialTask. Maybe we can reduce code duplications? :^) + class MinecraftUpdate : public Task { Q_OBJECT diff --git a/launcher/minecraft/MojangVersionFormat_test.cpp b/launcher/minecraft/MojangVersionFormat_test.cpp index 9d095340..71df784b 100644 --- a/launcher/minecraft/MojangVersionFormat_test.cpp +++ b/launcher/minecraft/MojangVersionFormat_test.cpp @@ -1,6 +1,5 @@ #include <QTest> #include <QDebug> -#include "TestUtil.h" #include "minecraft/MojangVersionFormat.h" @@ -8,9 +7,8 @@ class MojangVersionFormatTest : public QObject { Q_OBJECT - static QJsonDocument readJson(const char *file) + static QJsonDocument readJson(const QString path) { - auto path = QFINDTESTDATA(file); QFile jsonFile(path); jsonFile.open(QIODevice::ReadOnly); auto data = jsonFile.readAll(); @@ -31,7 +29,7 @@ private slots: void test_Through_Simple() { - QJsonDocument doc = readJson("data/1.9-simple.json"); + QJsonDocument doc = readJson(QFINDTESTDATA("testdata/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); @@ -41,7 +39,7 @@ slots: void test_Through() { - QJsonDocument doc = readJson("data/1.9.json"); + QJsonDocument doc = readJson(QFINDTESTDATA("testdata/1.9.json")); auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9.json"); auto doc2 = MojangVersionFormat::versionFileToJson(vfile); writeJson("1.9-passthorugh.json", doc2); diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 125048f0..01d42b00 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -346,6 +346,7 @@ void PackProfile::resolve(Net::Mode netmode) d->m_updateTask.reset(updateTask); connect(updateTask, &ComponentUpdateTask::succeeded, this, &PackProfile::updateSucceeded); connect(updateTask, &ComponentUpdateTask::failed, this, &PackProfile::updateFailed); + connect(updateTask, &ComponentUpdateTask::aborted, this, [this]{ updateFailed(tr("Aborted")); }); d->m_updateTask->start(); } diff --git a/launcher/minecraft/ParseUtils_test.cpp b/launcher/minecraft/ParseUtils_test.cpp index fcc137e5..7721a46d 100644 --- a/launcher/minecraft/ParseUtils_test.cpp +++ b/launcher/minecraft/ParseUtils_test.cpp @@ -1,5 +1,4 @@ #include <QTest> -#include "TestUtil.h" #include "minecraft/ParseUtils.h" @@ -42,4 +41,3 @@ slots: QTEST_GUILESS_MAIN(ParseUtilsTest) #include "ParseUtils_test.moc" - diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index ec86fa5c..9c8eb70b 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -135,6 +135,7 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::login(QString password) { m_currentTask.reset(new MojangLogin(&data, password)); connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); + connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; } @@ -145,6 +146,7 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::loginMSA() { m_currentTask.reset(new MSAInteractive(&data)); connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); + connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; } @@ -155,6 +157,7 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::loginOffline() { m_currentTask.reset(new OfflineLogin(&data)); connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); + connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; } @@ -176,6 +179,7 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::refresh() { connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); + connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; } diff --git a/launcher/minecraft/auth/steps/YggdrasilStep.cpp b/launcher/minecraft/auth/steps/YggdrasilStep.cpp index 4c6b1624..e1d33172 100644 --- a/launcher/minecraft/auth/steps/YggdrasilStep.cpp +++ b/launcher/minecraft/auth/steps/YggdrasilStep.cpp @@ -9,6 +9,7 @@ YggdrasilStep::YggdrasilStep(AccountData* data, QString password) : AuthStep(dat connect(m_yggdrasil, &Task::failed, this, &YggdrasilStep::onAuthFailed); connect(m_yggdrasil, &Task::succeeded, this, &YggdrasilStep::onAuthSucceeded); + connect(m_yggdrasil, &Task::aborted, this, &YggdrasilStep::onAuthFailed); } YggdrasilStep::~YggdrasilStep() noexcept = default; diff --git a/launcher/minecraft/launch/LauncherPartLaunch.cpp b/launcher/minecraft/launch/LauncherPartLaunch.cpp index 427bc32b..fe8a1b1b 100644 --- a/launcher/minecraft/launch/LauncherPartLaunch.cpp +++ b/launcher/minecraft/launch/LauncherPartLaunch.cpp @@ -142,7 +142,7 @@ void LauncherPartLaunch::executeTask() #else args << classPath.join(':'); #endif - args << "org.multimc.EntryPoint"; + args << "org.polymc.EntryPoint"; qDebug() << args.join(' '); diff --git a/launcher/minecraft/mod/ModFolderModel_test.cpp b/launcher/minecraft/mod/ModFolderModel_test.cpp index 34a3b83a..b4d37ce5 100644 --- a/launcher/minecraft/mod/ModFolderModel_test.cpp +++ b/launcher/minecraft/mod/ModFolderModel_test.cpp @@ -35,7 +35,6 @@ #include <QTest> #include <QTemporaryDir> -#include "TestUtil.h" #include "FileSystem.h" #include "minecraft/mod/ModFolderModel.h" @@ -50,7 +49,7 @@ slots: void test_1178() { // source - QString source = QFINDTESTDATA("data/test_folder"); + QString source = QFINDTESTDATA("testdata/test_folder"); // sanity check QVERIFY(!source.endsWith('/')); diff --git a/launcher/minecraft/mod/testdata/test_folder/assets/minecraft/textures/blah.txt b/launcher/minecraft/mod/testdata/test_folder/assets/minecraft/textures/blah.txt new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/launcher/minecraft/mod/testdata/test_folder/assets/minecraft/textures/blah.txt @@ -0,0 +1 @@ + diff --git a/launcher/minecraft/mod/testdata/test_folder/pack.mcmeta b/launcher/minecraft/mod/testdata/test_folder/pack.mcmeta new file mode 100644 index 00000000..67ee0434 --- /dev/null +++ b/launcher/minecraft/mod/testdata/test_folder/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "pack_format": 1, + "description": "Some resource pack maybe" + } +} diff --git a/launcher/minecraft/mod/testdata/test_folder/pack.nfo b/launcher/minecraft/mod/testdata/test_folder/pack.nfo new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/launcher/minecraft/mod/testdata/test_folder/pack.nfo @@ -0,0 +1 @@ + diff --git a/launcher/minecraft/update/AssetUpdateTask.cpp b/launcher/minecraft/update/AssetUpdateTask.cpp index c4bddb08..dd246665 100644 --- a/launcher/minecraft/update/AssetUpdateTask.cpp +++ b/launcher/minecraft/update/AssetUpdateTask.cpp @@ -43,6 +43,7 @@ void AssetUpdateTask::executeTask() connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::assetIndexFinished); connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetIndexFailed); + connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); qDebug() << m_inst->name() << ": Starting asset index download"; @@ -80,6 +81,7 @@ void AssetUpdateTask::assetIndexFinished() downloadJob = job; connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::emitSucceeded); connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed); + connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); downloadJob->start(); return; diff --git a/launcher/minecraft/update/FMLLibrariesTask.cpp b/launcher/minecraft/update/FMLLibrariesTask.cpp index 58141991..b6238ce9 100644 --- a/launcher/minecraft/update/FMLLibrariesTask.cpp +++ b/launcher/minecraft/update/FMLLibrariesTask.cpp @@ -72,6 +72,7 @@ void FMLLibrariesTask::executeTask() connect(dljob, &NetJob::succeeded, this, &FMLLibrariesTask::fmllibsFinished); connect(dljob, &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed); + connect(dljob, &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); connect(dljob, &NetJob::progress, this, &FMLLibrariesTask::progress); downloadJob.reset(dljob); downloadJob->start(); diff --git a/launcher/minecraft/update/LibrariesTask.cpp b/launcher/minecraft/update/LibrariesTask.cpp index 26679110..aa2bf407 100644 --- a/launcher/minecraft/update/LibrariesTask.cpp +++ b/launcher/minecraft/update/LibrariesTask.cpp @@ -68,6 +68,7 @@ void LibrariesTask::executeTask() connect(downloadJob.get(), &NetJob::succeeded, this, &LibrariesTask::emitSucceeded); connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed); + connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); connect(downloadJob.get(), &NetJob::progress, this, &LibrariesTask::progress); downloadJob->start(); } diff --git a/launcher/modplatform/packwiz/Packwiz_test.cpp b/launcher/modplatform/packwiz/Packwiz_test.cpp index 3d47f9f7..d6251148 100644 --- a/launcher/modplatform/packwiz/Packwiz_test.cpp +++ b/launcher/modplatform/packwiz/Packwiz_test.cpp @@ -21,7 +21,6 @@ #include <QTest> #include "Packwiz.h" -#include "TestUtil.h" class PackwizTest : public QObject { Q_OBJECT diff --git a/launcher/mojang/PackageManifest_test.cpp b/launcher/mojang/PackageManifest_test.cpp index d4c55c5a..e8da4266 100644 --- a/launcher/mojang/PackageManifest_test.cpp +++ b/launcher/mojang/PackageManifest_test.cpp @@ -1,6 +1,5 @@ #include <QTest> #include <QDebug> -#include "TestUtil.h" #include "mojang/PackageManifest.h" diff --git a/launcher/news/NewsEntry.cpp b/launcher/news/NewsEntry.cpp index 137703d1..cfe07e86 100644 --- a/launcher/news/NewsEntry.cpp +++ b/launcher/news/NewsEntry.cpp @@ -54,7 +54,7 @@ inline QString childValue(const QDomElement& element, const QString& childName, bool NewsEntry::fromXmlElement(const QDomElement& element, NewsEntry* entry, QString* errorMsg) { QString title = childValue(element, "title", tr("Untitled")); - QString content = childValue(element, "description", tr("No content.")); + QString content = childValue(element, "content", tr("No content.")); QString link = childValue(element, "id"); entry->title = title; diff --git a/launcher/settings/INIFile_test.cpp b/launcher/settings/INIFile_test.cpp index 08c2155e..d23f9fdf 100644 --- a/launcher/settings/INIFile_test.cpp +++ b/launcher/settings/INIFile_test.cpp @@ -1,5 +1,4 @@ #include <QTest> -#include "TestUtil.h" #include "settings/INIFile.h" diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp new file mode 100644 index 00000000..b88cfb13 --- /dev/null +++ b/launcher/tasks/ConcurrentTask.cpp @@ -0,0 +1,144 @@ +#include "ConcurrentTask.h" + +#include <QDebug> + +ConcurrentTask::ConcurrentTask(QObject* parent, QString task_name, int max_concurrent) + : Task(parent), m_name(task_name), m_total_max_size(max_concurrent) +{} + +ConcurrentTask::~ConcurrentTask() +{ + for (auto task : m_queue) { + if (task) + task->deleteLater(); + } +} + +auto ConcurrentTask::getStepProgress() const -> qint64 +{ + return m_stepProgress; +} + +auto ConcurrentTask::getStepTotalProgress() const -> qint64 +{ + return m_stepTotalProgress; +} + +void ConcurrentTask::addTask(Task::Ptr task) +{ + if (!isRunning()) + m_queue.append(task); + else + qWarning() << "Tried to add a task to a running concurrent task!"; +} + +void ConcurrentTask::executeTask() +{ + m_total_size = m_queue.size(); + + for (int i = 0; i < m_total_max_size; i++) + startNext(); +} + +bool ConcurrentTask::abort() +{ + if (m_doing.isEmpty()) { + // Don't call emitAborted() here, we want to bypass the 'is the task running' check + emit aborted(); + emit finished(); + + m_aborted = true; + return true; + } + + m_queue.clear(); + + m_aborted = true; + for (auto task : m_doing) + m_aborted &= task->abort(); + + if (m_aborted) + emitAborted(); + + return m_aborted; +} + +void ConcurrentTask::startNext() +{ + if (m_aborted || m_doing.count() > m_total_max_size) + return; + + if (m_queue.isEmpty() && m_doing.isEmpty()) { + emitSucceeded(); + return; + } + + if (m_queue.isEmpty()) + return; + + Task::Ptr next = m_queue.dequeue(); + + connect(next.get(), &Task::succeeded, this, [this, next] { subTaskSucceeded(next); }); + connect(next.get(), &Task::failed, this, [this, next](QString msg) { subTaskFailed(next, msg); }); + + connect(next.get(), &Task::status, this, &ConcurrentTask::subTaskStatus); + connect(next.get(), &Task::stepStatus, this, &ConcurrentTask::subTaskStatus); + + connect(next.get(), &Task::progress, this, &ConcurrentTask::subTaskProgress); + + m_doing.insert(next.get(), next); + + setStepStatus(next->isMultiStep() ? next->getStepStatus() : next->getStatus()); + updateState(); + + next->start(); +} + +void ConcurrentTask::subTaskSucceeded(Task::Ptr task) +{ + m_done.insert(task.get(), task); + m_doing.remove(task.get()); + + disconnect(task.get(), 0, this, 0); + + updateState(); + + startNext(); +} + +void ConcurrentTask::subTaskFailed(Task::Ptr task, const QString& msg) +{ + m_done.insert(task.get(), task); + m_failed.insert(task.get(), task); + + m_doing.remove(task.get()); + + disconnect(task.get(), 0, this, 0); + + updateState(); + + startNext(); +} + +void ConcurrentTask::subTaskStatus(const QString& msg) +{ + setStepStatus(msg); +} + +void ConcurrentTask::subTaskProgress(qint64 current, qint64 total) +{ + if (total == 0) { + setProgress(0, 100); + return; + } + + m_stepProgress = current; + m_stepTotalProgress = total; +} + +void ConcurrentTask::updateState() +{ + setProgress(m_done.count(), m_total_size); + setStatus(tr("Executing %1 task(s) (%2 out of %3 are done)") + .arg(QString::number(m_doing.count()), QString::number(m_done.count()), QString::number(m_total_size))); +} diff --git a/launcher/tasks/ConcurrentTask.h b/launcher/tasks/ConcurrentTask.h new file mode 100644 index 00000000..5898899d --- /dev/null +++ b/launcher/tasks/ConcurrentTask.h @@ -0,0 +1,58 @@ +#pragma once + +#include <QQueue> +#include <QSet> + +#include "tasks/Task.h" + +class ConcurrentTask : public Task { + Q_OBJECT +public: + explicit ConcurrentTask(QObject* parent = nullptr, QString task_name = "", int max_concurrent = 6); + virtual ~ConcurrentTask(); + + inline auto isMultiStep() const -> bool override { return m_queue.size() > 1; }; + auto getStepProgress() const -> qint64 override; + auto getStepTotalProgress() const -> qint64 override; + + inline auto getStepStatus() const -> QString override { return m_step_status; } + + void addTask(Task::Ptr task); + +public slots: + bool abort() override; + +protected +slots: + void executeTask() override; + + virtual void startNext(); + + void subTaskSucceeded(Task::Ptr); + void subTaskFailed(Task::Ptr, const QString &msg); + void subTaskStatus(const QString &msg); + void subTaskProgress(qint64 current, qint64 total); + +protected: + void setStepStatus(QString status) { m_step_status = status; emit stepStatus(status); }; + + virtual void updateState(); + +protected: + QString m_name; + QString m_step_status; + + QQueue<Task::Ptr> m_queue; + + QHash<Task*, Task::Ptr> m_doing; + QHash<Task*, Task::Ptr> m_done; + QHash<Task*, Task::Ptr> m_failed; + + int m_total_max_size; + int m_total_size; + + qint64 m_stepProgress = 0; + qint64 m_stepTotalProgress = 100; + + bool m_aborted = false; +}; diff --git a/launcher/tasks/Task_test.cpp b/launcher/tasks/Task_test.cpp index 9b6cc2e5..ef153a6a 100644 --- a/launcher/tasks/Task_test.cpp +++ b/launcher/tasks/Task_test.cpp @@ -1,5 +1,4 @@ #include <QTest> -#include "TestUtil.h" #include "Task.h" diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 82a65643..f68cf61a 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -95,6 +95,7 @@ #include "ui/instanceview/InstanceDelegate.h" #include "ui/widgets/LabeledToolButton.h" #include "ui/dialogs/NewInstanceDialog.h" +#include "ui/dialogs/NewsDialog.h" #include "ui/dialogs/ProgressDialog.h" #include "ui/dialogs/AboutDialog.h" #include "ui/dialogs/VersionSelectDialog.h" @@ -1946,20 +1947,17 @@ void MainWindow::on_actionOpenWiki_triggered() void MainWindow::on_actionMoreNews_triggered() { - DesktopServices::openUrl(QUrl(BuildConfig.NEWS_OPEN_URL)); + auto entries = m_newsChecker->getNewsEntries(); + NewsDialog news_dialog(entries, this); + news_dialog.exec(); } void MainWindow::newsButtonClicked() { - QList<NewsEntryPtr> entries = m_newsChecker->getNewsEntries(); - if (entries.count() > 0) - { - DesktopServices::openUrl(QUrl(entries[0]->link)); - } - else - { - DesktopServices::openUrl(QUrl(BuildConfig.NEWS_OPEN_URL)); - } + auto entries = m_newsChecker->getNewsEntries(); + NewsDialog news_dialog(entries, this); + news_dialog.toggleArticleList(); + news_dialog.exec(); } void MainWindow::on_actionAbout_triggered() diff --git a/launcher/ui/dialogs/NewsDialog.cpp b/launcher/ui/dialogs/NewsDialog.cpp new file mode 100644 index 00000000..df620464 --- /dev/null +++ b/launcher/ui/dialogs/NewsDialog.cpp @@ -0,0 +1,49 @@ +#include "NewsDialog.h" +#include "ui_NewsDialog.h" + +NewsDialog::NewsDialog(QList<NewsEntryPtr> entries, QWidget* parent) : QDialog(parent), ui(new Ui::NewsDialog()) +{ + ui->setupUi(this); + + for (auto entry : entries) { + ui->articleListWidget->addItem(entry->title); + m_entries.insert(entry->title, entry); + } + + connect(ui->articleListWidget, &QListWidget::currentTextChanged, this, &NewsDialog::selectedArticleChanged); + connect(ui->toggleListButton, &QPushButton::clicked, this, &NewsDialog::toggleArticleList); + + m_article_list_hidden = ui->articleListWidget->isHidden(); + + auto first_item = ui->articleListWidget->item(0); + ui->articleListWidget->setItemSelected(first_item, true); + + auto article_entry = m_entries.constFind(first_item->text()).value(); + ui->articleTitleLabel->setText(QString("<a href='%1'>%2</a>").arg(article_entry->link, first_item->text())); + ui->currentArticleContentBrowser->setText(article_entry->content); +} + +NewsDialog::~NewsDialog() +{ + delete ui; +} + +void NewsDialog::selectedArticleChanged(const QString& new_title) +{ + auto const& article_entry = m_entries.constFind(new_title).value(); + + ui->articleTitleLabel->setText(QString("<a href='%1'>%2</a>").arg(article_entry->link, new_title)); + ui->currentArticleContentBrowser->setText(article_entry->content); +} + +void NewsDialog::toggleArticleList() +{ + m_article_list_hidden = !m_article_list_hidden; + + ui->articleListWidget->setHidden(m_article_list_hidden); + + if (m_article_list_hidden) + ui->toggleListButton->setText(tr("Show article list")); + else + ui->toggleListButton->setText(tr("Hide article list")); +} diff --git a/launcher/ui/dialogs/NewsDialog.h b/launcher/ui/dialogs/NewsDialog.h new file mode 100644 index 00000000..add6b8dd --- /dev/null +++ b/launcher/ui/dialogs/NewsDialog.h @@ -0,0 +1,30 @@ +#pragma once + +#include <QDialog> +#include <QHash> + +#include "news/NewsEntry.h" + +namespace Ui { +class NewsDialog; +} + +class NewsDialog : public QDialog { + Q_OBJECT + + public: + NewsDialog(QList<NewsEntryPtr> entries, QWidget* parent = nullptr); + ~NewsDialog(); + + public slots: + void toggleArticleList(); + + private slots: + void selectedArticleChanged(const QString& new_title); + + private: + Ui::NewsDialog* ui; + + QHash<QString, NewsEntryPtr> m_entries; + bool m_article_list_hidden = false; +}; diff --git a/launcher/ui/dialogs/NewsDialog.ui b/launcher/ui/dialogs/NewsDialog.ui new file mode 100644 index 00000000..2aaa08f1 --- /dev/null +++ b/launcher/ui/dialogs/NewsDialog.ui @@ -0,0 +1,113 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>NewsDialog</class> + <widget class="QDialog" name="NewsDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>500</height> + </rect> + </property> + <property name="windowTitle"> + <string>News</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QVBoxLayout" name="leftVerticalLayout"> + <item> + <widget class="QListWidget" name="articleListWidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" name="rightVerticalLayout"> + <item> + <widget class="QLabel" name="articleTitleLabel"> + <property name="text"> + <string notr="true">Placeholder</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QTextBrowser" name="currentArticleContentBrowser"> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + <property name="openLinks"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="1"> + <widget class="QPushButton" name="closeButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>10</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Close</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QPushButton" name="toggleListButton"> + <property name="text"> + <string>Hide article list</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>closeButton</sender> + <signal>clicked()</signal> + <receiver>NewsDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>199</x> + <y>277</y> + </hint> + <hint type="destinationlabel"> + <x>199</x> + <y>149</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp new file mode 100644 index 00000000..02eeae3d --- /dev/null +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -0,0 +1,297 @@ +#include "ExternalResourcesPage.h" +#include "ui_ExternalResourcesPage.h" + +#include "DesktopServices.h" +#include "Version.h" +#include "minecraft/mod/ModFolderModel.h" +#include "ui/GuiUtil.h" + +#include <QKeyEvent> +#include <QMenu> + +namespace { +// FIXME: wasteful +void RemoveThePrefix(QString& string) +{ + QRegularExpression regex(QStringLiteral("^(?:the|teh) +"), QRegularExpression::CaseInsensitiveOption); + string.remove(regex); + string = string.trimmed(); +} +} // namespace + +class SortProxy : public QSortFilterProxyModel { + public: + explicit SortProxy(QObject* parent = nullptr) : QSortFilterProxyModel(parent) {} + + protected: + bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override + { + ModFolderModel* model = qobject_cast<ModFolderModel*>(sourceModel()); + if (!model) + return false; + + const auto& mod = model->at(source_row); + + if (mod.name().contains(filterRegExp())) + return true; + if (mod.description().contains(filterRegExp())) + return true; + + for (auto& author : mod.authors()) { + if (author.contains(filterRegExp())) { + return true; + } + } + + return false; + } + + bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override + { + ModFolderModel* model = qobject_cast<ModFolderModel*>(sourceModel()); + if (!model || !source_left.isValid() || !source_right.isValid() || source_left.column() != source_right.column()) { + return QSortFilterProxyModel::lessThan(source_left, source_right); + } + + // we are now guaranteed to have two valid indexes in the same column... we love the provided invariants unconditionally and + // proceed. + + auto column = (ModFolderModel::Columns) source_left.column(); + bool invert = false; + switch (column) { + // GH-2550 - sort by enabled/disabled + case ModFolderModel::ActiveColumn: { + auto dataL = source_left.data(Qt::CheckStateRole).toBool(); + auto dataR = source_right.data(Qt::CheckStateRole).toBool(); + if (dataL != dataR) + return dataL > dataR; + + // fallthrough + invert = sortOrder() == Qt::DescendingOrder; + } + // GH-2722 - sort mod names in a way that discards "The" prefixes + case ModFolderModel::NameColumn: { + auto dataL = model->data(model->index(source_left.row(), ModFolderModel::NameColumn)).toString(); + RemoveThePrefix(dataL); + auto dataR = model->data(model->index(source_right.row(), ModFolderModel::NameColumn)).toString(); + RemoveThePrefix(dataR); + + auto less = dataL.compare(dataR, sortCaseSensitivity()); + if (less != 0) + return invert ? (less > 0) : (less < 0); + + // fallthrough + invert = sortOrder() == Qt::DescendingOrder; + } + // GH-2762 - sort versions by parsing them as versions + case ModFolderModel::VersionColumn: { + auto dataL = Version(model->data(model->index(source_left.row(), ModFolderModel::VersionColumn)).toString()); + auto dataR = Version(model->data(model->index(source_right.row(), ModFolderModel::VersionColumn)).toString()); + return invert ? (dataL > dataR) : (dataL < dataR); + } + default: { + return QSortFilterProxyModel::lessThan(source_left, source_right); + } + } + } +}; + +ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared_ptr<ModFolderModel> model, QWidget* parent) + : QMainWindow(parent), m_instance(instance), ui(new Ui::ExternalResourcesPage), m_model(model) +{ + ui->setupUi(this); + + runningStateChanged(m_instance && m_instance->isRunning()); + + ui->actionsToolbar->insertSpacer(ui->actionViewConfigs); + + m_filterModel = new SortProxy(this); + m_filterModel->setDynamicSortFilter(true); + m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + m_filterModel->setSortCaseSensitivity(Qt::CaseInsensitive); + m_filterModel->setSourceModel(m_model.get()); + m_filterModel->setFilterKeyColumn(-1); + ui->treeView->setModel(m_filterModel); + + ui->treeView->installEventFilter(this); + ui->treeView->sortByColumn(1, Qt::AscendingOrder); + ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu); + + // The default function names by Qt are pretty ugly, so let's just connect the actions manually, + // to make it easier to read :) + connect(ui->actionAddItem, &QAction::triggered, this, &ExternalResourcesPage::addItem); + connect(ui->actionRemoveItem, &QAction::triggered, this, &ExternalResourcesPage::removeItem); + connect(ui->actionEnableItem, &QAction::triggered, this, &ExternalResourcesPage::enableItem); + connect(ui->actionDisableItem, &QAction::triggered, this, &ExternalResourcesPage::disableItem); + connect(ui->actionViewConfigs, &QAction::triggered, this, &ExternalResourcesPage::viewConfigs); + connect(ui->actionViewFolder, &QAction::triggered, this, &ExternalResourcesPage::viewFolder); + + connect(ui->treeView, &ModListView::customContextMenuRequested, this, &ExternalResourcesPage::ShowContextMenu); + connect(ui->treeView, &ModListView::activated, this, &ExternalResourcesPage::itemActivated); + + auto selection_model = ui->treeView->selectionModel(); + connect(selection_model, &QItemSelectionModel::currentChanged, this, &ExternalResourcesPage::current); + connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged); + connect(m_instance, &BaseInstance::runningStatusChanged, this, &ExternalResourcesPage::runningStateChanged); +} + +ExternalResourcesPage::~ExternalResourcesPage() +{ + m_model->stopWatching(); + delete ui; +} + +void ExternalResourcesPage::itemActivated(const QModelIndex&) +{ + if (!m_controlsEnabled) + return; + + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()); + m_model->setModStatus(selection.indexes(), ModFolderModel::Toggle); +} + +QMenu* ExternalResourcesPage::createPopupMenu() +{ + QMenu* filteredMenu = QMainWindow::createPopupMenu(); + filteredMenu->removeAction(ui->actionsToolbar->toggleViewAction()); + return filteredMenu; +} + +void ExternalResourcesPage::ShowContextMenu(const QPoint& pos) +{ + auto menu = ui->actionsToolbar->createContextMenu(this, tr("Context menu")); + menu->exec(ui->treeView->mapToGlobal(pos)); + delete menu; +} + +void ExternalResourcesPage::openedImpl() +{ + m_model->startWatching(); +} + +void ExternalResourcesPage::closedImpl() +{ + m_model->stopWatching(); +} + +void ExternalResourcesPage::retranslate() +{ + ui->retranslateUi(this); +} + +void ExternalResourcesPage::filterTextChanged(const QString& newContents) +{ + m_viewFilter = newContents; + m_filterModel->setFilterFixedString(m_viewFilter); +} + +void ExternalResourcesPage::runningStateChanged(bool running) +{ + if (m_controlsEnabled == !running) + return; + + m_controlsEnabled = !running; + ui->actionAddItem->setEnabled(m_controlsEnabled); + ui->actionDisableItem->setEnabled(m_controlsEnabled); + ui->actionEnableItem->setEnabled(m_controlsEnabled); + ui->actionRemoveItem->setEnabled(m_controlsEnabled); +} + +bool ExternalResourcesPage::shouldDisplay() const +{ + return true; +} + +bool ExternalResourcesPage::listFilter(QKeyEvent* keyEvent) +{ + switch (keyEvent->key()) { + case Qt::Key_Delete: + removeItem(); + return true; + case Qt::Key_Plus: + addItem(); + return true; + default: + break; + } + return QWidget::eventFilter(ui->treeView, keyEvent); +} + +bool ExternalResourcesPage::eventFilter(QObject* obj, QEvent* ev) +{ + if (ev->type() != QEvent::KeyPress) + return QWidget::eventFilter(obj, ev); + + QKeyEvent* keyEvent = static_cast<QKeyEvent*>(ev); + if (obj == ui->treeView) + return listFilter(keyEvent); + + return QWidget::eventFilter(obj, ev); +} + +void ExternalResourcesPage::addItem() +{ + if (!m_controlsEnabled) + return; + + + auto list = GuiUtil::BrowseForFiles( + helpPage(), tr("Select %1", "Select whatever type of files the page contains. Example: 'Loader Mods'").arg(displayName()), + m_fileSelectionFilter.arg(displayName()), APPLICATION->settings()->get("CentralModsDir").toString(), this->parentWidget()); + + if (!list.isEmpty()) { + for (auto filename : list) { + m_model->installMod(filename); + } + } +} + +void ExternalResourcesPage::removeItem() +{ + if (!m_controlsEnabled) + return; + + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()); + m_model->deleteMods(selection.indexes()); +} + +void ExternalResourcesPage::enableItem() +{ + if (!m_controlsEnabled) + return; + + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()); + m_model->setModStatus(selection.indexes(), ModFolderModel::Enable); +} + +void ExternalResourcesPage::disableItem() +{ + if (!m_controlsEnabled) + return; + + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()); + m_model->setModStatus(selection.indexes(), ModFolderModel::Disable); +} + +void ExternalResourcesPage::viewConfigs() +{ + DesktopServices::openDirectory(m_instance->instanceConfigFolder(), true); +} + +void ExternalResourcesPage::viewFolder() +{ + DesktopServices::openDirectory(m_model->dir().absolutePath(), true); +} + +void ExternalResourcesPage::current(const QModelIndex& current, const QModelIndex& previous) +{ + if (!current.isValid()) { + ui->frame->clear(); + return; + } + + auto sourceCurrent = m_filterModel->mapToSource(current); + int row = sourceCurrent.row(); + Mod& m = m_model->operator[](row); + ui->frame->updateWithMod(m); +} diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.h b/launcher/ui/pages/instance/ExternalResourcesPage.h new file mode 100644 index 00000000..41237139 --- /dev/null +++ b/launcher/ui/pages/instance/ExternalResourcesPage.h @@ -0,0 +1,73 @@ +#pragma once + +#include <QMainWindow> +#include <QSortFilterProxyModel> + +#include "Application.h" +#include "minecraft/MinecraftInstance.h" +#include "ui/pages/BasePage.h" + +class ModFolderModel; + +namespace Ui { +class ExternalResourcesPage; +} + +/* This page is used as a base for pages in which the user can manage external resources + * related to the game, such as mods, shaders or resource packs. */ +class ExternalResourcesPage : public QMainWindow, public BasePage { + Q_OBJECT + + public: + // FIXME: Switch to different model (or change the name of this one) + explicit ExternalResourcesPage(BaseInstance* instance, std::shared_ptr<ModFolderModel> model, QWidget* parent = nullptr); + virtual ~ExternalResourcesPage(); + + virtual QString displayName() const override = 0; + virtual QIcon icon() const override = 0; + virtual QString id() const override = 0; + virtual QString helpPage() const override = 0; + + virtual bool shouldDisplay() const override = 0; + + void openedImpl() override; + void closedImpl() override; + + void retranslate() override; + + protected: + bool eventFilter(QObject* obj, QEvent* ev) override; + bool listFilter(QKeyEvent* ev); + QMenu* createPopupMenu() override; + + public slots: + void current(const QModelIndex& current, const QModelIndex& previous); + + protected slots: + void itemActivated(const QModelIndex& index); + void filterTextChanged(const QString& newContents); + void runningStateChanged(bool running); + + virtual void addItem(); + virtual void removeItem(); + + virtual void enableItem(); + virtual void disableItem(); + + virtual void viewFolder(); + virtual void viewConfigs(); + + void ShowContextMenu(const QPoint& pos); + + protected: + BaseInstance* m_instance = nullptr; + + Ui::ExternalResourcesPage* ui = nullptr; + std::shared_ptr<ModFolderModel> m_model; + QSortFilterProxyModel* m_filterModel = nullptr; + + QString m_fileSelectionFilter; + QString m_viewFilter; + + bool m_controlsEnabled = true; +}; diff --git a/launcher/ui/pages/instance/ModFolderPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index ab59b0df..17bf455a 100644 --- a/launcher/ui/pages/instance/ModFolderPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>ModFolderPage</class> - <widget class="QMainWindow" name="ModFolderPage"> + <class>ExternalResourcesPage</class> + <widget class="QMainWindow" name="ExternalResourcesPage"> <property name="geometry"> <rect> <x>0</x> @@ -53,7 +53,7 @@ </widget> </item> <item row="1" column="1" colspan="3"> - <widget class="ModListView" name="modTreeView"> + <widget class="ModListView" name="treeView"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <horstretch>0</horstretch> @@ -83,15 +83,15 @@ <attribute name="toolBarBreak"> <bool>false</bool> </attribute> - <addaction name="actionAdd"/> + <addaction name="actionAddItem"/> <addaction name="separator"/> - <addaction name="actionRemove"/> - <addaction name="actionEnable"/> - <addaction name="actionDisable"/> - <addaction name="actionView_configs"/> - <addaction name="actionView_Folder"/> + <addaction name="actionRemoveItem"/> + <addaction name="actionEnableItem"/> + <addaction name="actionDisableItem"/> + <addaction name="actionViewConfigs"/> + <addaction name="actionViewFolder"/> </widget> - <action name="actionAdd"> + <action name="actionAddItem"> <property name="text"> <string>&Add</string> </property> @@ -99,31 +99,31 @@ <string>Add</string> </property> </action> - <action name="actionRemove"> + <action name="actionRemoveItem"> <property name="text"> <string>&Remove</string> </property> <property name="toolTip"> - <string>Remove selected mods</string> + <string>Remove selected item</string> </property> </action> - <action name="actionEnable"> + <action name="actionEnableItem"> <property name="text"> <string>&Enable</string> </property> <property name="toolTip"> - <string>Enable selected mods</string> + <string>Enable selected item</string> </property> </action> - <action name="actionDisable"> + <action name="actionDisableItem"> <property name="text"> <string>&Disable</string> </property> <property name="toolTip"> - <string>Disable selected mods</string> + <string>Disable selected item</string> </property> </action> - <action name="actionView_configs"> + <action name="actionViewConfigs"> <property name="text"> <string>View &Configs</string> </property> @@ -131,11 +131,22 @@ <string>Open the 'config' folder in the system file manager.</string> </property> </action> - <action name="actionView_Folder"> + <action name="actionViewFolder"> <property name="text"> <string>View &Folder</string> </property> </action> + <action name="actionDownloadItem"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&Download</string> + </property> + <property name="toolTip"> + <string>Download a new resource</string> + </property> + </action> </widget> <customwidgets> <customwidget> @@ -156,7 +167,7 @@ </customwidget> </customwidgets> <tabstops> - <tabstop>modTreeView</tabstop> + <tabstop>treeView</tabstop> <tabstop>filterEdit</tabstop> </tabstops> <resources/> diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index d929a0ea..4432ccc8 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -35,370 +35,99 @@ */ #include "ModFolderPage.h" -#include "ui_ModFolderPage.h" +#include "ui_ExternalResourcesPage.h" -#include <QMessageBox> +#include <QAbstractItemModel> #include <QEvent> #include <QKeyEvent> -#include <QAbstractItemModel> #include <QMenu> +#include <QMessageBox> #include <QSortFilterProxyModel> #include "Application.h" +#include "ui/GuiUtil.h" #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ModDownloadDialog.h" -#include "ui/GuiUtil.h" #include "DesktopServices.h" -#include "minecraft/mod/ModFolderModel.h" -#include "minecraft/mod/Mod.h" -#include "minecraft/VersionFilterData.h" #include "minecraft/PackProfile.h" +#include "minecraft/VersionFilterData.h" +#include "minecraft/mod/Mod.h" +#include "minecraft/mod/ModFolderModel.h" #include "modplatform/ModAPI.h" #include "Version.h" +#include "tasks/ConcurrentTask.h" #include "ui/dialogs/ProgressDialog.h" -#include "tasks/SequentialTask.h" - -namespace { - // FIXME: wasteful - void RemoveThePrefix(QString & string) { - QRegularExpression regex(QStringLiteral("^(([Tt][Hh][eE])|([Tt][eE][Hh])) +")); - string.remove(regex); - string = string.trimmed(); - } -} - -class ModSortProxy : public QSortFilterProxyModel -{ -public: - explicit ModSortProxy(QObject *parent = 0) : QSortFilterProxyModel(parent) - { - } - -protected: - bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const override { - ModFolderModel *model = qobject_cast<ModFolderModel *>(sourceModel()); - if(!model) { - return false; - } - const auto &mod = model->at(source_row); - if(mod.name().contains(filterRegExp())) { - return true; - } - if(mod.description().contains(filterRegExp())) { - return true; - } - for(auto & author: mod.authors()) { - if (author.contains(filterRegExp())) { - return true; - } - } - return false; - } - - bool lessThan(const QModelIndex & source_left, const QModelIndex & source_right) const override - { - ModFolderModel *model = qobject_cast<ModFolderModel *>(sourceModel()); - if( - !model || - !source_left.isValid() || - !source_right.isValid() || - source_left.column() != source_right.column() - ) { - return QSortFilterProxyModel::lessThan(source_left, source_right); - } - - // we are now guaranteed to have two valid indexes in the same column... we love the provided invariants unconditionally and proceed. - auto column = (ModFolderModel::Columns) source_left.column(); - bool invert = false; - switch(column) { - // GH-2550 - sort by enabled/disabled - case ModFolderModel::ActiveColumn: { - auto dataL = source_left.data(Qt::CheckStateRole).toBool(); - auto dataR = source_right.data(Qt::CheckStateRole).toBool(); - if(dataL != dataR) { - return dataL > dataR; - } - // fallthrough - invert = sortOrder() == Qt::DescendingOrder; - } - // GH-2722 - sort mod names in a way that discards "The" prefixes - case ModFolderModel::NameColumn: { - auto dataL = model->data(model->index(source_left.row(), ModFolderModel::NameColumn)).toString(); - RemoveThePrefix(dataL); - auto dataR = model->data(model->index(source_right.row(), ModFolderModel::NameColumn)).toString(); - RemoveThePrefix(dataR); - - auto less = dataL.compare(dataR, sortCaseSensitivity()); - if(less != 0) { - return invert ? (less > 0) : (less < 0); - } - // fallthrough - invert = sortOrder() == Qt::DescendingOrder; - } - // GH-2762 - sort versions by parsing them as versions - case ModFolderModel::VersionColumn: { - auto dataL = Version(model->data(model->index(source_left.row(), ModFolderModel::VersionColumn)).toString()); - auto dataR = Version(model->data(model->index(source_right.row(), ModFolderModel::VersionColumn)).toString()); - return invert ? (dataL > dataR) : (dataL < dataR); - } - default: { - return QSortFilterProxyModel::lessThan(source_left, source_right); - } - } - } -}; - -ModFolderPage::ModFolderPage( - BaseInstance *inst, - std::shared_ptr<ModFolderModel> mods, - QString id, - QString iconName, - QString displayName, - QString helpPage, - QWidget *parent -) : - QMainWindow(parent), - ui(new Ui::ModFolderPage) +ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel> mods, QWidget* parent) + : ExternalResourcesPage(inst, mods, parent) { - ui->setupUi(this); - // This is structured like that so that these changes - // do not affect the Resouce pack and Shader pack tabs - if(id == "mods") { - auto act = new QAction(tr("Download mods"), this); - act->setToolTip(tr("Download mods from online mod platforms")); - ui->actionsToolbar->insertActionBefore(ui->actionAdd, act); - connect(act, &QAction::triggered, this, &ModFolderPage::on_actionInstall_mods_triggered); - - ui->actionAdd->setText(tr("Add .jar")); - ui->actionAdd->setToolTip(tr("Add mods via local file")); - } - - ui->actionsToolbar->insertSpacer(ui->actionView_configs); - - m_inst = inst; - on_RunningState_changed(m_inst && m_inst->isRunning()); - m_mods = mods; - m_id = id; - m_displayName = displayName; - m_iconName = iconName; - m_helpName = helpPage; - m_fileSelectionFilter = "%1 (*.zip *.jar)"; - m_filterModel = new ModSortProxy(this); - m_filterModel->setDynamicSortFilter(true); - m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive); - m_filterModel->setSortCaseSensitivity(Qt::CaseInsensitive); - m_filterModel->setSourceModel(m_mods.get()); - m_filterModel->setFilterKeyColumn(-1); - ui->modTreeView->setModel(m_filterModel); - ui->modTreeView->installEventFilter(this); - ui->modTreeView->sortByColumn(1, Qt::AscendingOrder); - ui->modTreeView->setContextMenuPolicy(Qt::CustomContextMenu); - connect(ui->modTreeView, &ModListView::customContextMenuRequested, this, &ModFolderPage::ShowContextMenu); - connect(ui->modTreeView, &ModListView::activated, this, &ModFolderPage::modItemActivated); + // do not affect the Resource pack and Shader pack tabs + { + ui->actionDownloadItem->setText(tr("Download mods")); + ui->actionDownloadItem->setToolTip(tr("Download mods from online mod platforms")); + ui->actionDownloadItem->setEnabled(true); + ui->actionAddItem->setText(tr("Add file")); + ui->actionAddItem->setToolTip(tr("Add a locally downloaded file")); - auto smodel = ui->modTreeView->selectionModel(); - connect(smodel, &QItemSelectionModel::currentChanged, this, &ModFolderPage::modCurrent); - connect(ui->filterEdit, &QLineEdit::textChanged, this, &ModFolderPage::on_filterTextChanged); - connect(m_inst, &BaseInstance::runningStatusChanged, this, &ModFolderPage::on_RunningState_changed); -} + ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); -void ModFolderPage::modItemActivated(const QModelIndex&) -{ - if(!m_controlsEnabled) { - return; + connect(ui->actionDownloadItem, &QAction::triggered, this, &ModFolderPage::installMods); } - auto selection = m_filterModel->mapSelectionToSource(ui->modTreeView->selectionModel()->selection()); - m_mods->setModStatus(selection.indexes(), ModFolderModel::Toggle); -} - -QMenu * ModFolderPage::createPopupMenu() -{ - QMenu* filteredMenu = QMainWindow::createPopupMenu(); - filteredMenu->removeAction(ui->actionsToolbar->toggleViewAction() ); - return filteredMenu; } -void ModFolderPage::ShowContextMenu(const QPoint& pos) -{ - auto menu = ui->actionsToolbar->createContextMenu(this, tr("Context menu")); - menu->exec(ui->modTreeView->mapToGlobal(pos)); - delete menu; -} - -void ModFolderPage::openedImpl() -{ - m_mods->startWatching(); -} - -void ModFolderPage::closedImpl() -{ - m_mods->stopWatching(); -} - -void ModFolderPage::on_filterTextChanged(const QString& newContents) -{ - m_viewFilter = newContents; - m_filterModel->setFilterFixedString(m_viewFilter); -} - - -CoreModFolderPage::CoreModFolderPage(BaseInstance *inst, std::shared_ptr<ModFolderModel> mods, - QString id, QString iconName, QString displayName, - QString helpPage, QWidget *parent) - : ModFolderPage(inst, mods, id, iconName, displayName, helpPage, parent) -{ -} - -ModFolderPage::~ModFolderPage() -{ - m_mods->stopWatching(); - delete ui; -} - -void ModFolderPage::on_RunningState_changed(bool running) -{ - if(m_controlsEnabled == !running) { - return; - } - m_controlsEnabled = !running; - ui->actionsToolbar->setEnabled(m_controlsEnabled); -} +CoreModFolderPage::CoreModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel> mods, QWidget* parent) + : ModFolderPage(inst, mods, parent) +{} bool ModFolderPage::shouldDisplay() const { return true; } -void ModFolderPage::retranslate() -{ - ui->retranslateUi(this); -} - bool CoreModFolderPage::shouldDisplay() const { - if (ModFolderPage::shouldDisplay()) - { - auto inst = dynamic_cast<MinecraftInstance *>(m_inst); + if (ModFolderPage::shouldDisplay()) { + auto inst = dynamic_cast<MinecraftInstance*>(m_instance); if (!inst) return true; + auto version = inst->getPackProfile(); + if (!version) return true; - if(!version->getComponent("net.minecraftforge")) - { + if (!version->getComponent("net.minecraftforge")) return false; - } - if(!version->getComponent("net.minecraft")) - { + if (!version->getComponent("net.minecraft")) return false; - } - if(version->getComponent("net.minecraft")->getReleaseDateTime() < g_VersionFilterData.legacyCutoffDate) - { + if (version->getComponent("net.minecraft")->getReleaseDateTime() < g_VersionFilterData.legacyCutoffDate) return true; - } + } return false; } -bool ModFolderPage::modListFilter(QKeyEvent *keyEvent) +void ModFolderPage::installMods() { - switch (keyEvent->key()) - { - case Qt::Key_Delete: - on_actionRemove_triggered(); - return true; - case Qt::Key_Plus: - on_actionAdd_triggered(); - return true; - default: - break; - } - return QWidget::eventFilter(ui->modTreeView, keyEvent); -} - -bool ModFolderPage::eventFilter(QObject *obj, QEvent *ev) -{ - if (ev->type() != QEvent::KeyPress) - { - return QWidget::eventFilter(obj, ev); - } - QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev); - if (obj == ui->modTreeView) - return modListFilter(keyEvent); - return QWidget::eventFilter(obj, ev); -} - -void ModFolderPage::on_actionAdd_triggered() -{ - if(!m_controlsEnabled) { + if (!m_controlsEnabled) return; - } - auto list = GuiUtil::BrowseForFiles( - m_helpName, - tr("Select %1", - "Select whatever type of files the page contains. Example: 'Loader Mods'") - .arg(m_displayName), - m_fileSelectionFilter.arg(m_displayName), APPLICATION->settings()->get("CentralModsDir").toString(), - this->parentWidget()); - if (!list.empty()) - { - for (auto filename : list) - { - m_mods->installMod(filename); - } - } -} - -void ModFolderPage::on_actionEnable_triggered() -{ - if(!m_controlsEnabled) { - return; - } - auto selection = m_filterModel->mapSelectionToSource(ui->modTreeView->selectionModel()->selection()); - m_mods->setModStatus(selection.indexes(), ModFolderModel::Enable); -} - -void ModFolderPage::on_actionDisable_triggered() -{ - if(!m_controlsEnabled) { - return; - } - auto selection = m_filterModel->mapSelectionToSource(ui->modTreeView->selectionModel()->selection()); - m_mods->setModStatus(selection.indexes(), ModFolderModel::Disable); -} - -void ModFolderPage::on_actionRemove_triggered() -{ - if(!m_controlsEnabled) { - return; - } - auto selection = m_filterModel->mapSelectionToSource(ui->modTreeView->selectionModel()->selection()); - m_mods->deleteMods(selection.indexes()); -} - -void ModFolderPage::on_actionInstall_mods_triggered() -{ - if(!m_controlsEnabled) { - return; - } - if(m_inst->typeName() != "Minecraft"){ - return; //this is a null instance or a legacy instance - } - auto profile = ((MinecraftInstance *)m_inst)->getPackProfile(); + if (m_instance->typeName() != "Minecraft") + return; // this is a null instance or a legacy instance + + auto profile = static_cast<MinecraftInstance*>(m_instance)->getPackProfile(); if (profile->getModLoaders() == ModAPI::Unspecified) { - QMessageBox::critical(this,tr("Error"),tr("Please install a mod loader first!")); + QMessageBox::critical(this, tr("Error"), tr("Please install a mod loader first!")); return; } - ModDownloadDialog mdownload(m_mods, this, m_inst); + + ModDownloadDialog mdownload(m_model, this, m_instance); if (mdownload.exec()) { - SequentialTask* tasks = new SequentialTask(this); + ConcurrentTask* tasks = new ConcurrentTask(this); connect(tasks, &Task::failed, [this, tasks](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); tasks->deleteLater(); @@ -409,40 +138,20 @@ void ModFolderPage::on_actionInstall_mods_triggered() }); connect(tasks, &Task::succeeded, [this, tasks]() { QStringList warnings = tasks->warnings(); - if (warnings.count()) { CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); } + if (warnings.count()) + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); + tasks->deleteLater(); }); - for (auto task : mdownload.getTasks()) { + for (auto& task : mdownload.getTasks()) { tasks->addTask(task); } ProgressDialog loadDialog(this); loadDialog.setSkipButton(true, tr("Abort")); loadDialog.execWithTask(tasks); - m_mods->update(); - } -} -void ModFolderPage::on_actionView_configs_triggered() -{ - DesktopServices::openDirectory(m_inst->instanceConfigFolder(), true); -} - -void ModFolderPage::on_actionView_Folder_triggered() -{ - DesktopServices::openDirectory(m_mods->dir().absolutePath(), true); -} - -void ModFolderPage::modCurrent(const QModelIndex ¤t, const QModelIndex &previous) -{ - if (!current.isValid()) - { - ui->frame->clear(); - return; + m_model->update(); } - auto sourceCurrent = m_filterModel->mapToSource(current); - int row = sourceCurrent.row(); - Mod &m = m_mods->operator[](row); - ui->frame->updateWithMod(m); } diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h index 2dd44e85..1a9ed7db 100644 --- a/launcher/ui/pages/instance/ModFolderPage.h +++ b/launcher/ui/pages/instance/ModFolderPage.h @@ -35,109 +35,31 @@ #pragma once -#include <QMainWindow> +#include "ExternalResourcesPage.h" -#include "minecraft/MinecraftInstance.h" -#include "ui/pages/BasePage.h" - -#include <Application.h> -#include <QSortFilterProxyModel> - -class ModFolderModel; -namespace Ui -{ -class ModFolderPage; -} - -class ModFolderPage : public QMainWindow, public BasePage -{ +class ModFolderPage : public ExternalResourcesPage { Q_OBJECT -public: - explicit ModFolderPage( - BaseInstance *inst, - std::shared_ptr<ModFolderModel> mods, - QString id, - QString iconName, - QString displayName, - QString helpPage = "", - QWidget *parent = 0 - ); - virtual ~ModFolderPage(); - - void setFilter(const QString & filter) - { - m_fileSelectionFilter = filter; - } + public: + explicit ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel> mods, QWidget* parent = nullptr); + virtual ~ModFolderPage() = default; - virtual QString displayName() const override - { - return m_displayName; - } - virtual QIcon icon() const override - { - return APPLICATION->getThemedIcon(m_iconName); - } - virtual QString id() const override - { - return m_id; - } - virtual QString helpPage() const override - { - return m_helpName; - } - virtual bool shouldDisplay() const override; - void retranslate() override; - - virtual void openedImpl() override; - virtual void closedImpl() override; -protected: - bool eventFilter(QObject *obj, QEvent *ev) override; - bool modListFilter(QKeyEvent *ev); - QMenu * createPopupMenu() override; + void setFilter(const QString& filter) { m_fileSelectionFilter = filter; } -protected: - BaseInstance *m_inst = nullptr; + virtual QString displayName() const override { return tr("Mods"); } + virtual QIcon icon() const override { return APPLICATION->getThemedIcon("loadermods"); } + virtual QString id() const override { return "mods"; } + virtual QString helpPage() const override { return "Loader-mods"; } -protected: - Ui::ModFolderPage *ui = nullptr; - std::shared_ptr<ModFolderModel> m_mods; - QSortFilterProxyModel *m_filterModel = nullptr; - QString m_iconName; - QString m_id; - QString m_displayName; - QString m_helpName; - QString m_fileSelectionFilter; - QString m_viewFilter; - bool m_controlsEnabled = true; - -public -slots: - void modCurrent(const QModelIndex ¤t, const QModelIndex &previous); + virtual bool shouldDisplay() const override; -private -slots: - void modItemActivated(const QModelIndex &index); - void on_filterTextChanged(const QString & newContents); - void on_RunningState_changed(bool running); - void on_actionAdd_triggered(); - void on_actionRemove_triggered(); - void on_actionEnable_triggered(); - void on_actionDisable_triggered(); - void on_actionInstall_mods_triggered(); - void on_actionView_Folder_triggered(); - void on_actionView_configs_triggered(); - void ShowContextMenu(const QPoint &pos); + private slots: + void installMods(); }; -class CoreModFolderPage : public ModFolderPage -{ -public: - explicit CoreModFolderPage(BaseInstance *inst, std::shared_ptr<ModFolderModel> mods, QString id, - QString iconName, QString displayName, QString helpPage = "", - QWidget *parent = 0); - virtual ~CoreModFolderPage() - { - } +class CoreModFolderPage : public ModFolderPage { + public: + explicit CoreModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel> mods, QWidget* parent = 0); + virtual ~CoreModFolderPage() = default; virtual bool shouldDisplay() const; }; diff --git a/launcher/ui/pages/instance/ResourcePackPage.h b/launcher/ui/pages/instance/ResourcePackPage.h index 8054926c..a6c9fdd3 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.h +++ b/launcher/ui/pages/instance/ResourcePackPage.h @@ -35,24 +35,28 @@ #pragma once -#include "ModFolderPage.h" -#include "ui_ModFolderPage.h" +#include "ExternalResourcesPage.h" +#include "ui_ExternalResourcesPage.h" -class ResourcePackPage : public ModFolderPage +class ResourcePackPage : public ExternalResourcesPage { Q_OBJECT public: explicit ResourcePackPage(MinecraftInstance *instance, QWidget *parent = 0) - : ModFolderPage(instance, instance->resourcePackList(), "resourcepacks", - "resourcepacks", tr("Resource packs"), "Resource-packs", parent) + : ExternalResourcesPage(instance, instance->resourcePackList(), parent) { - ui->actionView_configs->setVisible(false); + ui->actionViewConfigs->setVisible(false); } virtual ~ResourcePackPage() {} + QString displayName() const override { return tr("Resource packs"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("resourcepacks"); } + QString id() const override { return "resourcepacks"; } + QString helpPage() const override { return "Resource-packs"; } + virtual bool shouldDisplay() const override { - return !m_inst->traits().contains("no-texturepacks") && - !m_inst->traits().contains("texturepacks"); + return !m_instance->traits().contains("no-texturepacks") && + !m_instance->traits().contains("texturepacks"); } }; diff --git a/launcher/ui/pages/instance/ShaderPackPage.h b/launcher/ui/pages/instance/ShaderPackPage.h index 7d4f5074..2cc056c8 100644 --- a/launcher/ui/pages/instance/ShaderPackPage.h +++ b/launcher/ui/pages/instance/ShaderPackPage.h @@ -35,21 +35,25 @@ #pragma once -#include "ModFolderPage.h" -#include "ui_ModFolderPage.h" +#include "ExternalResourcesPage.h" +#include "ui_ExternalResourcesPage.h" -class ShaderPackPage : public ModFolderPage +class ShaderPackPage : public ExternalResourcesPage { Q_OBJECT public: explicit ShaderPackPage(MinecraftInstance *instance, QWidget *parent = 0) - : ModFolderPage(instance, instance->shaderPackList(), "shaderpacks", - "shaderpacks", tr("Shader packs"), "Resource-packs", parent) + : ExternalResourcesPage(instance, instance->shaderPackList(), parent) { - ui->actionView_configs->setVisible(false); + ui->actionViewConfigs->setVisible(false); } virtual ~ShaderPackPage() {} + QString displayName() const override { return tr("Shader packs"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("shaderpacks"); } + QString id() const override { return "shaderpacks"; } + QString helpPage() const override { return "Resource-packs"; } + virtual bool shouldDisplay() const override { return true; diff --git a/launcher/ui/pages/instance/TexturePackPage.h b/launcher/ui/pages/instance/TexturePackPage.h index e8cefe6e..f550a5bc 100644 --- a/launcher/ui/pages/instance/TexturePackPage.h +++ b/launcher/ui/pages/instance/TexturePackPage.h @@ -35,23 +35,27 @@ #pragma once -#include "ModFolderPage.h" -#include "ui_ModFolderPage.h" +#include "ExternalResourcesPage.h" +#include "ui_ExternalResourcesPage.h" -class TexturePackPage : public ModFolderPage +class TexturePackPage : public ExternalResourcesPage { Q_OBJECT public: explicit TexturePackPage(MinecraftInstance *instance, QWidget *parent = 0) - : ModFolderPage(instance, instance->texturePackList(), "texturepacks", "resourcepacks", - tr("Texture packs"), "Texture-packs", parent) + : ExternalResourcesPage(instance, instance->texturePackList(), parent) { - ui->actionView_configs->setVisible(false); + ui->actionViewConfigs->setVisible(false); } virtual ~TexturePackPage() {} + QString displayName() const override { return tr("Texture packs"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("resourcepacks"); } + QString id() const override { return "texturepacks"; } + QString helpPage() const override { return "Texture-packs"; } + virtual bool shouldDisplay() const override { - return m_inst->traits().contains("texturepacks"); + return m_instance->traits().contains("texturepacks"); } }; diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index 98eec31c..4917b890 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -53,7 +53,11 @@ auto ListModel::data(const QModelIndex& index, int role) const -> QVariant } case Qt::DecorationRole: { if (m_logoMap.contains(pack.logoName)) { - return (m_logoMap.value(pack.logoName)); + auto icon = m_logoMap.value(pack.logoName); + // FIXME: This doesn't really belong here, but Qt doesn't offer a good way right now ;( + auto icon_scaled = QIcon(icon.pixmap(48, 48).scaledToWidth(48)); + + return icon_scaled; } QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder"); // un-const-ify this diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 96118284..39b935a6 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -87,6 +87,7 @@ auto ModpackListModel::data(const QModelIndex& index, int role) const -> QVarian } else if (role == Qt::DecorationRole) { if (m_logoMap.contains(pack.iconName)) { auto icon = m_logoMap.value(pack.iconName); + // FIXME: This doesn't really belong here, but Qt doesn't offer a good way right now ;( auto icon_scaled = QIcon(icon.pixmap(48, 48).scaledToWidth(48)); return icon_scaled; diff --git a/launcher/updater/DownloadTask_test.cpp b/launcher/updater/DownloadTask_test.cpp index 8e823a63..deba2632 100644 --- a/launcher/updater/DownloadTask_test.cpp +++ b/launcher/updater/DownloadTask_test.cpp @@ -1,8 +1,6 @@ #include <QTest> #include <QSignalSpy> -#include "TestUtil.h" - #include "updater/GoUpdate.h" #include "updater/DownloadTask.h" #include "updater/UpdateChecker.h" @@ -71,13 +69,23 @@ slots: void test_parseVersionInfo_data() { + QFile f1(QFINDTESTDATA("testdata/1.json")); + f1.open(QFile::ReadOnly); + QByteArray data1 = f1.readAll(); + f1.close(); + + QFile f2(QFINDTESTDATA("testdata/2.json")); + f2.open(QFile::ReadOnly); + QByteArray data2 = f2.readAll(); + f2.close(); + QTest::addColumn<QByteArray>("data"); QTest::addColumn<VersionFileList>("list"); QTest::addColumn<QString>("error"); QTest::addColumn<bool>("ret"); QTest::newRow("one") - << GET_TEST_FILE("data/1.json") + << data1 << (VersionFileList() << VersionFileEntry{"fileOne", 493, @@ -93,7 +101,7 @@ slots: "f12df554b21e320be6471d7154130e70"}) << QString() << true; QTest::newRow("two") - << GET_TEST_FILE("data/2.json") + << data2 << (VersionFileList() << VersionFileEntry{"fileOne", 493, @@ -133,42 +141,42 @@ slots: QTest::newRow("test 1") << tempFolder << (VersionFileList() << VersionFileEntry{ - "data/fileOne", 493, + QFINDTESTDATA("testdata/fileOne"), 493, FileSourceList() << FileSource( "http", "http://host/path/fileOne-1"), "9eb84090956c484e32cb6c08455a667b"} << VersionFileEntry{ - "data/fileTwo", 644, + QFINDTESTDATA("testdata/fileTwo"), 644, FileSourceList() << FileSource( "http", "http://host/path/fileTwo-1"), "38f94f54fa3eb72b0ea836538c10b043"} << VersionFileEntry{ - "data/fileThree", 420, + QFINDTESTDATA("testdata/fileThree"), 420, FileSourceList() << FileSource( "http", "http://host/path/fileThree-1"), "f12df554b21e320be6471d7154130e70"}) << (VersionFileList() << VersionFileEntry{ - "data/fileOne", 493, + QFINDTESTDATA("testdata/fileOne"), 493, FileSourceList() << FileSource("http", "http://host/path/fileOne-2"), "42915a71277c9016668cce7b82c6b577"} << VersionFileEntry{ - "data/fileTwo", 644, + QFINDTESTDATA("testdata/fileTwo"), 644, FileSourceList() << FileSource("http", "http://host/path/fileTwo-2"), "38f94f54fa3eb72b0ea836538c10b043"}) << (OperationList() - << Operation::DeleteOp("data/fileThree") + << Operation::DeleteOp(QFINDTESTDATA("testdata/fileThree")) << Operation::CopyOp( FS::PathCombine(tempFolder, - QString("data/fileOne").replace("/", "_")), - "data/fileOne", 493)); + QFINDTESTDATA("data/fileOne").replace("/", "_")), + QFINDTESTDATA("data/fileOne"), 493)); } void test_processFileLists() { diff --git a/launcher/updater/UpdateChecker_test.cpp b/launcher/updater/UpdateChecker_test.cpp index ec55a40e..70e3381f 100644 --- a/launcher/updater/UpdateChecker_test.cpp +++ b/launcher/updater/UpdateChecker_test.cpp @@ -1,7 +1,6 @@ #include <QTest> #include <QSignalSpy> -#include "TestUtil.h" #include "updater/UpdateChecker.h" Q_DECLARE_METATYPE(UpdateChecker::ChannelListEntry) @@ -50,36 +49,36 @@ slots: QTest::newRow("garbage") << QString() - << findTestDataUrl("data/garbageChannels.json") + << findTestDataUrl("testdata/garbageChannels.json") << false << false << QList<UpdateChecker::ChannelListEntry>(); QTest::newRow("errors") << QString() - << findTestDataUrl("data/errorChannels.json") + << findTestDataUrl("testdata/errorChannels.json") << false << true << QList<UpdateChecker::ChannelListEntry>(); QTest::newRow("no channels") << QString() - << findTestDataUrl("data/noChannels.json") + << findTestDataUrl("testdata/noChannels.json") << false << true << QList<UpdateChecker::ChannelListEntry>(); QTest::newRow("one channel") << QString("develop") - << findTestDataUrl("data/oneChannel.json") + << findTestDataUrl("testdata/oneChannel.json") << true << true << (QList<UpdateChecker::ChannelListEntry>() << UpdateChecker::ChannelListEntry{"develop", "Develop", "The channel called \"develop\"", "http://example.org/stuff"}); QTest::newRow("several channels") << QString("develop") - << findTestDataUrl("data/channels.json") + << findTestDataUrl("testdata/channels.json") << true << true << (QList<UpdateChecker::ChannelListEntry>() - << UpdateChecker::ChannelListEntry{"develop", "Develop", "The channel called \"develop\"", findTestDataUrl("data")} - << UpdateChecker::ChannelListEntry{"stable", "Stable", "It's stable at least", findTestDataUrl("data")} + << UpdateChecker::ChannelListEntry{"develop", "Develop", "The channel called \"develop\"", findTestDataUrl("testdata")} + << UpdateChecker::ChannelListEntry{"stable", "Stable", "It's stable at least", findTestDataUrl("testdata")} << UpdateChecker::ChannelListEntry{"42", "The Channel", "This is the channel that is going to answer all of your questions", "https://dent.me/tea"}); } void tst_ChannelListParsing() @@ -117,7 +116,7 @@ slots: void tst_UpdateChecking() { QString channel = "develop"; - QString channelUrl = findTestDataUrl("data/channels.json"); + QString channelUrl = findTestDataUrl("testdata/channels.json"); int currentBuild = 2; shared_qobject_ptr<QNetworkAccessManager> nam = new QNetworkAccessManager(); @@ -132,7 +131,7 @@ slots: QVERIFY(channelListLoadedSpy.wait()); qDebug() << "CWD:" << QDir::current().absolutePath(); - checker.m_channels[0].url = findTestDataUrl("data/"); + checker.m_channels[0].url = findTestDataUrl("testdata/"); checker.checkForUpdate(channel, false); QVERIFY(updateAvailableSpy.wait()); diff --git a/libraries/README.md b/libraries/README.md index 7e7e740d..49879a26 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -125,7 +125,7 @@ cp /home/peterix/minecraft/FTB/versions/1.7.10/1.7.10.jar launcher onesix ``` -Available under the Apache 2.0 license. +Available under `GPL-3.0-only` (with classpath exception), sublicensed from its original `Apache-2.0` codebase ## libnbtplusplus libnbt++ is a free C++ library for Minecraft's file format Named Binary Tag (NBT). It can read and write compressed and uncompressed NBT files and provides a code interface for working with NBT data. diff --git a/libraries/javacheck/CMakeLists.txt b/libraries/javacheck/CMakeLists.txt index 735de443..fd545d2b 100644 --- a/libraries/javacheck/CMakeLists.txt +++ b/libraries/javacheck/CMakeLists.txt @@ -4,7 +4,7 @@ find_package(Java 1.7 REQUIRED COMPONENTS Development) include(UseJava) set(CMAKE_JAVA_JAR_ENTRY_POINT JavaCheck) -set(CMAKE_JAVA_COMPILE_FLAGS -target 8 -source 8 -Xlint:deprecation -Xlint:unchecked) +set(CMAKE_JAVA_COMPILE_FLAGS -target 7 -source 7 -Xlint:deprecation -Xlint:unchecked) set(SRC JavaCheck.java diff --git a/libraries/javacheck/JavaCheck.java b/libraries/javacheck/JavaCheck.java index 560abbc0..4bf43a54 100644 --- a/libraries/javacheck/JavaCheck.java +++ b/libraries/javacheck/JavaCheck.java @@ -1,24 +1,25 @@ -import java.lang.Integer; +public final class JavaCheck { -public class JavaCheck -{ - private static final String[] keys = {"os.arch", "java.version", "java.vendor"}; - public static void main (String [] args) - { - int ret = 0; - for(String key : keys) - { + private static final String[] CHECKED_PROPERTIES = new String[] { + "os.arch", + "java.version", + "java.vendor" + }; + + public static void main(String[] args) { + int returnCode = 0; + + for (String key : CHECKED_PROPERTIES) { String property = System.getProperty(key); - if(property != null) - { + + if (property != null) { System.out.println(key + "=" + property); - } - else - { - ret = 1; + } else { + returnCode = 1; } } - - System.exit(ret); + + System.exit(returnCode); } + } diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index 2c859499..c4dfa5b7 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -3,19 +3,19 @@ project(launcher Java) find_package(Java 1.7 REQUIRED COMPONENTS Development) include(UseJava) -set(CMAKE_JAVA_JAR_ENTRY_POINT org.multimc.EntryPoint) +set(CMAKE_JAVA_JAR_ENTRY_POINT org.polymc.EntryPoint) set(CMAKE_JAVA_COMPILE_FLAGS -target 7 -source 7 -Xlint:deprecation -Xlint:unchecked) set(SRC - org/multimc/EntryPoint.java - org/multimc/Launcher.java - org/multimc/LauncherFactory.java - org/multimc/impl/OneSixLauncher.java - org/multimc/applet/LegacyFrame.java - org/multimc/exception/ParameterNotFoundException.java - org/multimc/exception/ParseException.java - org/multimc/utils/Parameters.java - org/multimc/utils/Utils.java + org/polymc/EntryPoint.java + org/polymc/Launcher.java + org/polymc/LauncherFactory.java + org/polymc/impl/OneSixLauncher.java + org/polymc/applet/LegacyFrame.java + org/polymc/exception/ParameterNotFoundException.java + org/polymc/exception/ParseException.java + org/polymc/utils/Parameters.java + org/polymc/utils/Utils.java net/minecraft/Launcher.java ) add_jar(NewLaunch ${SRC}) diff --git a/libraries/launcher/LICENSE b/libraries/launcher/LICENSE new file mode 120000 index 00000000..30cff740 --- /dev/null +++ b/libraries/launcher/LICENSE @@ -0,0 +1 @@ +../../LICENSE
\ No newline at end of file diff --git a/libraries/launcher/org/multimc/EntryPoint.java b/libraries/launcher/org/polymc/EntryPoint.java index c0500bbe..20f418eb 100644 --- a/libraries/launcher/org/multimc/EntryPoint.java +++ b/libraries/launcher/org/polymc/EntryPoint.java @@ -12,6 +12,23 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * @@ -33,10 +50,10 @@ * limitations under the License. */ -package org.multimc; +package org.polymc; -import org.multimc.exception.ParseException; -import org.multimc.utils.Parameters; +import org.polymc.exception.ParseException; +import org.polymc.utils.Parameters; import java.io.BufferedReader; import java.io.IOException; diff --git a/libraries/launcher/org/multimc/Launcher.java b/libraries/launcher/org/polymc/Launcher.java index bc0b525e..5bff123e 100644 --- a/libraries/launcher/org/multimc/Launcher.java +++ b/libraries/launcher/org/polymc/Launcher.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.multimc; +package org.polymc; public interface Launcher { diff --git a/libraries/launcher/org/multimc/LauncherFactory.java b/libraries/launcher/org/polymc/LauncherFactory.java index a2af8581..86862929 100644 --- a/libraries/launcher/org/multimc/LauncherFactory.java +++ b/libraries/launcher/org/polymc/LauncherFactory.java @@ -12,14 +12,31 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package org.multimc; +package org.polymc; -import org.multimc.impl.OneSixLauncher; -import org.multimc.utils.Parameters; +import org.polymc.impl.OneSixLauncher; +import org.polymc.utils.Parameters; import java.util.HashMap; import java.util.Map; diff --git a/libraries/launcher/org/multimc/applet/LegacyFrame.java b/libraries/launcher/org/polymc/applet/LegacyFrame.java index caec079c..2cdd17d7 100644 --- a/libraries/launcher/org/multimc/applet/LegacyFrame.java +++ b/libraries/launcher/org/polymc/applet/LegacyFrame.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.multimc.applet; +package org.polymc.applet; import net.minecraft.Launcher; diff --git a/libraries/launcher/org/multimc/exception/ParameterNotFoundException.java b/libraries/launcher/org/polymc/exception/ParameterNotFoundException.java index 9edbb826..2044814e 100644 --- a/libraries/launcher/org/multimc/exception/ParameterNotFoundException.java +++ b/libraries/launcher/org/polymc/exception/ParameterNotFoundException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.multimc.exception; +package org.polymc.exception; public final class ParameterNotFoundException extends IllegalArgumentException { diff --git a/libraries/launcher/org/multimc/exception/ParseException.java b/libraries/launcher/org/polymc/exception/ParseException.java index 848b395d..2f2f8294 100644 --- a/libraries/launcher/org/multimc/exception/ParseException.java +++ b/libraries/launcher/org/polymc/exception/ParseException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.multimc.exception; +package org.polymc.exception; public final class ParseException extends IllegalArgumentException { diff --git a/libraries/launcher/org/multimc/impl/OneSixLauncher.java b/libraries/launcher/org/polymc/impl/OneSixLauncher.java index b981e4ff..362ff8d6 100644 --- a/libraries/launcher/org/multimc/impl/OneSixLauncher.java +++ b/libraries/launcher/org/polymc/impl/OneSixLauncher.java @@ -13,12 +13,12 @@ * limitations under the License. */ -package org.multimc.impl; +package org.polymc.impl; -import org.multimc.Launcher; -import org.multimc.applet.LegacyFrame; -import org.multimc.utils.Parameters; -import org.multimc.utils.Utils; +import org.polymc.Launcher; +import org.polymc.applet.LegacyFrame; +import org.polymc.utils.Parameters; +import org.polymc.utils.Utils; import java.applet.Applet; import java.io.File; diff --git a/libraries/launcher/org/multimc/utils/Parameters.java b/libraries/launcher/org/polymc/utils/Parameters.java index 7be790c2..864d3cd2 100644 --- a/libraries/launcher/org/multimc/utils/Parameters.java +++ b/libraries/launcher/org/polymc/utils/Parameters.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package org.multimc.utils; +package org.polymc.utils; -import org.multimc.exception.ParameterNotFoundException; +import org.polymc.exception.ParameterNotFoundException; import java.util.ArrayList; import java.util.HashMap; diff --git a/libraries/launcher/org/multimc/utils/Utils.java b/libraries/launcher/org/polymc/utils/Utils.java index 416eff26..12d6e1aa 100644 --- a/libraries/launcher/org/multimc/utils/Utils.java +++ b/libraries/launcher/org/polymc/utils/Utils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.multimc.utils; +package org.polymc.utils; import java.io.File; import java.lang.reflect.Field; diff --git a/libraries/systeminfo/CMakeLists.txt b/libraries/systeminfo/CMakeLists.txt index 548a589c..d68904f8 100644 --- a/libraries/systeminfo/CMakeLists.txt +++ b/libraries/systeminfo/CMakeLists.txt @@ -22,8 +22,4 @@ add_library(systeminfo STATIC ${systeminfo_SOURCES}) target_link_libraries(systeminfo Qt5::Core Qt5::Gui Qt5::Network) target_include_directories(systeminfo PUBLIC include) -include (UnitTest) -add_unit_test(sys - SOURCES src/sys_test.cpp - LIBS systeminfo -) +ecm_add_test(src/sys_test.cpp LINK_LIBRARIES systeminfo Qt5::Test TEST_NAME sys) diff --git a/libraries/systeminfo/src/sys_test.cpp b/libraries/systeminfo/src/sys_test.cpp index 315050d2..9a5f9dfa 100644 --- a/libraries/systeminfo/src/sys_test.cpp +++ b/libraries/systeminfo/src/sys_test.cpp @@ -1,5 +1,4 @@ #include <QTest> -#include "TestUtil.h" #include <sys.h> |