aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.yml8
-rw-r--r--.github/ISSUE_TEMPLATE/config.yml4
-rw-r--r--.github/ISSUE_TEMPLATE/suggestion.yml10
-rwxr-xr-x.github/scripts/prepare_JREs.sh41
-rw-r--r--.github/workflows/build.yml186
-rw-r--r--.gitignore5
-rw-r--r--.gitmodules6
-rw-r--r--BUILD.md302
-rw-r--r--CMakeLists.txt114
-rw-r--r--COPYING.md19
-rw-r--r--LICENSE674
-rw-r--r--README.md190
-rw-r--r--buildconfig/BuildConfig.cpp.in18
-rw-r--r--buildconfig/BuildConfig.h17
-rw-r--r--changelog.md1532
-rw-r--r--cmake/UnitTest/test.rc2
-rw-r--r--default.nix1
-rw-r--r--doc/polymc.1.txt (renamed from doc/multimc.1.txt)22
-rw-r--r--flake.lock94
-rw-r--r--flake.nix42
-rw-r--r--launcher/Application.cpp199
-rw-r--r--launcher/Application.h12
-rw-r--r--launcher/CMakeLists.txt50
-rw-r--r--launcher/InstanceImportTask.cpp2
-rw-r--r--launcher/InstancePageProvider.h3
-rw-r--r--launcher/LaunchController.cpp6
-rwxr-xr-xlauncher/Launcher.in2
-rw-r--r--launcher/MMCZip.cpp89
-rw-r--r--launcher/MMCZip.h32
-rw-r--r--launcher/ModDownloadTask.cpp39
-rw-r--r--launcher/ModDownloadTask.h34
-rw-r--r--launcher/UpdateController.cpp2
-rw-r--r--launcher/java/JavaInstallList.cpp4
-rw-r--r--launcher/java/JavaUtils.cpp33
-rw-r--r--launcher/launch/LaunchTask.cpp3
-rw-r--r--launcher/launch/steps/CheckJava.cpp4
-rw-r--r--launcher/main.cpp2
-rw-r--r--launcher/minecraft/Library.h15
-rw-r--r--launcher/minecraft/MinecraftInstance.cpp2
-rw-r--r--launcher/minecraft/MinecraftLoadAndCheck.h2
-rw-r--r--launcher/minecraft/MinecraftUpdate.h2
-rw-r--r--launcher/minecraft/VersionFile.h27
-rw-r--r--launcher/minecraft/World.cpp6
-rw-r--r--launcher/minecraft/auth/AccountData.cpp10
-rw-r--r--launcher/minecraft/auth/AccountData.h3
-rw-r--r--launcher/minecraft/auth/AccountList.cpp2
-rw-r--r--launcher/minecraft/auth/AccountList.h3
-rw-r--r--launcher/minecraft/auth/MinecraftAccount.cpp31
-rw-r--r--launcher/minecraft/auth/MinecraftAccount.h15
-rw-r--r--launcher/minecraft/auth/flows/Offline.cpp17
-rw-r--r--launcher/minecraft/auth/flows/Offline.h22
-rw-r--r--launcher/minecraft/auth/steps/MSAStep.cpp3
-rw-r--r--launcher/minecraft/auth/steps/OfflineStep.cpp18
-rw-r--r--launcher/minecraft/auth/steps/OfflineStep.h19
-rw-r--r--launcher/minecraft/launch/ExtractNatives.cpp4
-rw-r--r--launcher/minecraft/launch/LauncherPartLaunch.cpp15
-rw-r--r--launcher/minecraft/launch/MinecraftServerTarget.cpp2
-rw-r--r--launcher/minecraft/mod/LocalModParseTask.cpp4
-rw-r--r--launcher/modplatform/atlauncher/ATLPackInstallTask.cpp2
-rw-r--r--launcher/modplatform/flame/FlameModIndex.cpp100
-rw-r--r--launcher/modplatform/flame/FlameModIndex.h50
-rw-r--r--launcher/modplatform/legacy_ftb/PackInstallTask.h4
-rw-r--r--launcher/modplatform/modrinth/ModrinthPackIndex.cpp95
-rw-r--r--launcher/modplatform/modrinth/ModrinthPackIndex.h48
-rw-r--r--launcher/modplatform/technic/SingleZipPackInstallTask.h2
-rw-r--r--launcher/modplatform/technic/TechnicPackProcessor.cpp6
-rw-r--r--launcher/net/PasteUpload.cpp82
-rw-r--r--launcher/net/PasteUpload.h22
-rw-r--r--launcher/news/NewsChecker.cpp2
-rw-r--r--launcher/news/NewsEntry.cpp16
-rw-r--r--launcher/news/NewsEntry.h10
-rw-r--r--launcher/notifications/NotificationChecker.cpp2
-rw-r--r--launcher/package/rpm/MultiMC5.spec65
-rw-r--r--launcher/package/rpm/README.md12
-rw-r--r--launcher/package/ubuntu/README.md14
-rw-r--r--launcher/package/ubuntu/multimc/DEBIAN/control12
-rwxr-xr-xlauncher/package/ubuntu/multimc/DEBIAN/postrm3
-rw-r--r--launcher/package/ubuntu/multimc/opt/multimc/icon.svg353
-rwxr-xr-xlauncher/package/ubuntu/multimc/opt/multimc/run.sh33
-rwxr-xr-xlauncher/package/ubuntu/multimc/usr/share/applications/multimc.desktop16
-rw-r--r--launcher/package/ubuntu/multimc/usr/share/man/man1/multimc.197
-rw-r--r--launcher/package/ubuntu/multimc/usr/share/metainfo/multimc.metainfo.xml54
-rw-r--r--launcher/resources/OSX/OSX.qrc1
-rw-r--r--launcher/resources/OSX/scalable/launcher.svg37
-rw-r--r--launcher/resources/flat/flat.qrc1
-rw-r--r--launcher/resources/flat/scalable/launcher.svg21
-rw-r--r--launcher/resources/iOS/iOS.qrc1
-rw-r--r--launcher/resources/iOS/scalable/launcher.svg33
-rw-r--r--launcher/resources/multimc/128x128/instances/modrinth.pngbin0 -> 10575 bytes
-rw-r--r--launcher/resources/multimc/32x32/instances/modrinth.pngbin0 -> 1913 bytes
-rw-r--r--launcher/resources/multimc/multimc.qrc11
-rw-r--r--launcher/resources/multimc/scalable/launcher.svg204
-rw-r--r--launcher/resources/pe_blue/pe_blue.qrc1
-rw-r--r--launcher/resources/pe_blue/scalable/launcher.svg62
-rw-r--r--launcher/resources/pe_colored/pe_colored.qrc1
-rw-r--r--launcher/resources/pe_colored/scalable/launcher.svg33
-rw-r--r--launcher/resources/pe_dark/pe_dark.qrc1
-rw-r--r--launcher/resources/pe_dark/scalable/launcher.svg62
-rw-r--r--launcher/resources/pe_light/pe_light.qrc2
-rw-r--r--launcher/resources/pe_light/scalable/launcher.svg62
-rw-r--r--launcher/settings/Setting.h3
-rw-r--r--launcher/tools/MCEditTool.cpp2
-rw-r--r--launcher/translations/TranslationsModel.cpp54
-rw-r--r--launcher/ui/GuiUtil.cpp17
-rw-r--r--launcher/ui/MainWindow.cpp18
-rw-r--r--launcher/ui/MainWindow.h3
-rw-r--r--launcher/ui/dialogs/AboutDialog.cpp59
-rw-r--r--launcher/ui/dialogs/AboutDialog.h9
-rw-r--r--launcher/ui/dialogs/AboutDialog.ui34
-rw-r--r--launcher/ui/dialogs/ExportInstanceDialog.cpp8
-rw-r--r--launcher/ui/dialogs/MSALoginDialog.cpp12
-rw-r--r--launcher/ui/dialogs/MSALoginDialog.ui27
-rw-r--r--launcher/ui/dialogs/ModDownloadDialog.cpp98
-rw-r--r--launcher/ui/dialogs/ModDownloadDialog.h54
-rw-r--r--launcher/ui/dialogs/OfflineLoginDialog.cpp98
-rw-r--r--launcher/ui/dialogs/OfflineLoginDialog.h43
-rw-r--r--launcher/ui/dialogs/OfflineLoginDialog.ui67
-rw-r--r--launcher/ui/dialogs/UpdateDialog.cpp8
-rw-r--r--launcher/ui/dialogs/UpdateDialog.ui2
-rw-r--r--launcher/ui/pages/global/APIPage.cpp (renamed from launcher/ui/pages/global/PasteEEPage.cpp)54
-rw-r--r--launcher/ui/pages/global/APIPage.h (renamed from launcher/ui/pages/global/PasteEEPage.h)22
-rw-r--r--launcher/ui/pages/global/APIPage.ui179
-rw-r--r--launcher/ui/pages/global/AccountListPage.cpp41
-rw-r--r--launcher/ui/pages/global/AccountListPage.h1
-rw-r--r--launcher/ui/pages/global/AccountListPage.ui6
-rw-r--r--launcher/ui/pages/global/LauncherPage.cpp56
-rw-r--r--launcher/ui/pages/global/LauncherPage.ui73
-rw-r--r--launcher/ui/pages/global/MinecraftPage.cpp5
-rw-r--r--launcher/ui/pages/global/MinecraftPage.ui20
-rw-r--r--launcher/ui/pages/global/PasteEEPage.ui128
-rw-r--r--launcher/ui/pages/instance/LegacyUpgradePage.ui9
-rw-r--r--launcher/ui/pages/instance/LogPage.ui2
-rw-r--r--launcher/ui/pages/instance/ModFolderPage.cpp45
-rw-r--r--launcher/ui/pages/instance/ModFolderPage.h1
-rw-r--r--launcher/ui/pages/instance/OtherLogsPage.ui2
-rw-r--r--launcher/ui/pages/instance/VersionPage.cpp3
-rw-r--r--launcher/ui/pages/modplatform/flame/FlameModModel.cpp273
-rw-r--r--launcher/ui/pages/modplatform/flame/FlameModModel.h79
-rw-r--r--launcher/ui/pages/modplatform/flame/FlameModPage.cpp196
-rw-r--r--launcher/ui/pages/modplatform/flame/FlameModPage.h67
-rw-r--r--launcher/ui/pages/modplatform/flame/FlameModPage.ui90
-rw-r--r--launcher/ui/pages/modplatform/flame/FlameModel.cpp11
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp276
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthModel.h79
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp180
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthPage.h67
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui90
-rw-r--r--launcher/ui/setupwizard/AnalyticsWizardPage.cpp63
-rw-r--r--launcher/ui/setupwizard/AnalyticsWizardPage.h25
-rw-r--r--launcher/ui/setupwizard/SetupWizard.cpp2
-rw-r--r--launcher/ui/widgets/LanguageSelectionWidget.cpp14
-rw-r--r--launcher/ui/widgets/LanguageSelectionWidget.h2
-rw-r--r--launcher/ui/widgets/PageContainer.cpp2
-rw-r--r--launcher/ui/widgets/WideBar.cpp14
-rw-r--r--launcher/ui/widgets/WideBar.h1
-rw-r--r--launcher/updater/DownloadTask.h3
-rw-r--r--launcher/updater/GoUpdate.cpp2
-rw-r--r--launcher/updater/testdata/tst_DownloadTask-test_writeInstallScript.xml4
-rw-r--r--libraries/README.md7
-rw-r--r--libraries/classparser/CMakeLists.txt2
-rw-r--r--libraries/classparser/src/classparser.cpp2
-rw-r--r--libraries/ganalytics/CMakeLists.txt17
-rw-r--r--libraries/ganalytics/LICENSE.txt24
-rw-r--r--libraries/ganalytics/README.md34
-rw-r--r--libraries/ganalytics/include/ganalytics.h67
-rw-r--r--libraries/ganalytics/src/ganalytics.cpp237
-rw-r--r--libraries/ganalytics/src/ganalytics_worker.cpp254
-rw-r--r--libraries/ganalytics/src/ganalytics_worker.h65
-rw-r--r--libraries/iconfix/CMakeLists.txt10
-rw-r--r--libraries/javacheck/CMakeLists.txt2
-rw-r--r--libraries/katabasis/CMakeLists.txt8
-rw-r--r--libraries/katabasis/README.md4
-rw-r--r--libraries/launcher/CMakeLists.txt2
m---------libraries/quazip0
-rw-r--r--libraries/rainbow/CMakeLists.txt8
-rw-r--r--notsecrets/CMakeLists.txt18
-rw-r--r--notsecrets/Launcher.icnsbin304757 -> 0 bytes
-rw-r--r--notsecrets/Launcher.icobin102134 -> 0 bytes
-rw-r--r--notsecrets/README.md25
-rw-r--r--notsecrets/Secrets.cpp42
-rw-r--r--notsecrets/Secrets.h8
-rwxr-xr-xnotsecrets/genicons.sh18
-rw-r--r--notsecrets/logo.svg271
-rw-r--r--packages/nix/NIX.md31
-rw-r--r--packages/nix/flake-compat.nix9
-rw-r--r--packages/nix/polymc/default.nix82
-rw-r--r--program_info/CMakeLists.txt20
-rw-r--r--program_info/LICENSE439
-rw-r--r--program_info/README.md6
-rwxr-xr-xprogram_info/genicons.sh24
-rw-r--r--program_info/org.polymc.PolyMC.Source.svg261
-rw-r--r--program_info/org.polymc.PolyMC.desktop.in12
-rw-r--r--program_info/org.polymc.PolyMC.metainfo.xml.in57
-rw-r--r--program_info/org.polymc.PolyMC.svg21
-rw-r--r--program_info/polymc-header-black.svg31
-rw-r--r--program_info/polymc-header.svg31
-rw-r--r--program_info/polymc.icnsbin0 -> 272578 bytes
-rw-r--r--program_info/polymc.icobin0 -> 102134 bytes
-rw-r--r--program_info/polymc.manifest (renamed from notsecrets/Launcher.manifest)4
-rw-r--r--program_info/polymc.qrc (renamed from notsecrets/logo.qrc)4
-rw-r--r--program_info/polymc.rc (renamed from notsecrets/launcher.rc)12
201 files changed, 5864 insertions, 4766 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 72986e94..eb560f0e 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -5,12 +5,12 @@ body:
- type: markdown
attributes:
value: |
- If you need help with running Minecraft, please visit us [on our Discord](https://discord.gg/multimc) before making a bug report.
+ If you need help with running Minecraft, please visit us on our Discord before making a bug report.
Before submitting a bug report, please make sure you have read this *entire* form, and that:
- * You have read the [FAQ](https://github.com/MultiMC/Launcher/wiki/FAQ) and it has not answered your question
+ * You have read the [FAQ](https://github.com/PolyMC/PolyMC/wiki/FAQ) and it has not answered your question
* Your bug is not caused by Minecraft or any mods you have installed.
- * Your issue has not been reported before, [make sure to use the search function!](https://github.com/MultiMC/Launcher/issues)
+ * Your issue has not been reported before, [make sure to use the search function!](https://github.com/PolyMC/PolyMC/issues)
**Do not forget to give your issue a descriptive title.** "Bug in the instance screen" makes it hard to distinguish issues at a glance.
- type: dropdown
@@ -48,4 +48,4 @@ body:
label: This issue is unique
options:
- label: I have searched the issue tracker and did not find an issue describing my bug.
- required: true
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 089f1eb5..932d0c8f 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,5 +1,5 @@
blank_issues_enabled: true
contact_links:
- - name: MultiMC Discord
- url: https://discord.gg/multimc
+ - name: PolyMC Matrix Support Room
+ url: https://matrix.to/#/#support:polymc.org
about: Please ask for support here before opening an issue.
diff --git a/.github/ISSUE_TEMPLATE/suggestion.yml b/.github/ISSUE_TEMPLATE/suggestion.yml
index 88bf66cf..b58a6672 100644
--- a/.github/ISSUE_TEMPLATE/suggestion.yml
+++ b/.github/ISSUE_TEMPLATE/suggestion.yml
@@ -5,25 +5,25 @@ body:
- type: markdown
attributes:
value: |
- ### Use this form to suggest a feature for MultiMC.
+ ### Use this form to suggest a feature for PolyMC.
- type: input
attributes:
label: Role
- description: In what way do you use MultiMC that needs this feature?
+ description: In what way do you use PolyMC that needs this feature?
placeholder: I play modded Minecraft.
validations:
required: true
- type: input
attributes:
label: Suggestion
- description: What do you want MultiMC to do?
+ description: What do you want PolyMC to do?
placeholder: I want the cat button to meow.
validations:
required: true
- type: input
attributes:
label: Benefit
- description: Why do you need MultiMC to do this?
+ description: Why do you need PolyMC to do this?
placeholder: so that I can always hear a cat when I need to.
validations:
required: true
@@ -32,7 +32,7 @@ body:
label: This suggestion is unique
options:
- label: I have searched the issue tracker and did not find an issue describing my suggestion, especially not one that has been rejected.
- required: true
+ required: true
- type: textarea
attributes:
label: You may use the editor below to elaborate further.
diff --git a/.github/scripts/prepare_JREs.sh b/.github/scripts/prepare_JREs.sh
new file mode 100755
index 00000000..b85e9c2f
--- /dev/null
+++ b/.github/scripts/prepare_JREs.sh
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+
+URL_JDK8="https://api.adoptium.net/v3/binary/latest/8/ga/linux/x64/jre/hotspot/normal/eclipse"
+URL_JDK17="https://api.adoptium.net/v3/binary/latest/17/ga/linux/x64/jre/hotspot/normal/eclipse"
+
+mkdir -p JREs
+pushd JREs
+
+wget --content-disposition "$URL_JDK8"
+wget --content-disposition "$URL_JDK17"
+
+for file in *;
+do
+ mkdir temp
+
+ re='(OpenJDK([[:digit:]]+)U-jre_x64_linux_hotspot_([[:digit:]]+)(.*).tar.gz)'
+ if [[ $file =~ $re ]];
+ then
+ version_major=${BASH_REMATCH[2]}
+ version_trailing=${BASH_REMATCH[4]}
+
+ if [ $version_major = 17 ];
+ then
+ hyphen='-'
+ else
+ hyphen=''
+ fi
+
+ version_edit=$(echo $version_trailing | sed -e 's/_/+/g' | sed -e 's/b/-b/g')
+ dir_name=jdk$hyphen$version_major$version_edit-jre
+ mkdir jre$version_major
+ tar -xzf $file -C temp
+ pushd temp/$dir_name
+ cp -r . ../../jre$version_major
+ popd
+ fi
+
+ rm -rf temp
+done
+
+popd
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 00000000..d2ccc59e
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,186 @@
+name: build_portable
+
+on:
+ [push, pull_request, workflow_dispatch]
+
+jobs:
+ build:
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+
+ - os: ubuntu-20.04
+ qt_version: 5.15.2
+ qt_host: linux
+
+ - os: windows-2022
+ qt_version: 5.15.2
+ qt_host: windows
+ qt_arch: win32_mingw81
+
+ - os: macos-11
+ qt_version: 5.12.12
+ qt_host: mac
+ macosx_deployment_target: 10.12
+
+ runs-on: ${{ matrix.os }}
+
+ env:
+ MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }}
+ INSTALL_DIR: "install"
+ BUILD_DIR: "build"
+
+ steps:
+ - name: Install 32bit mingw on Windows
+ if: runner.os == 'Windows'
+ uses: egor-tensin/setup-mingw@v2
+ with:
+ platform: x86
+
+ - name: Install 32bit zlib via Strawberry on Windows
+ if: runner.os == 'Windows'
+ run: |
+ choco install strawberryperl -y --force --x86
+
+ - name: Checkout
+ uses: actions/checkout@v2
+ with:
+ submodules: 'true'
+
+ # We need to do this here because it inexplicably fails if we split the step
+ - name: Download and install OpenSSL libs on Windows
+ if: runner.os == 'Windows'
+ run: |
+ python -m pip install --upgrade pip
+ python -m pip install aqtinstall==2.0.5
+ python -m aqt install-tool -O "${{ github.workspace }}\Qt\" windows desktop tools_openssl_x86
+ mkdir ${{ env.INSTALL_DIR }}
+ copy "${{ github.workspace }}\Qt\Tools\OpenSSL\Win_x86\bin\libssl-1_1.dll" "${{ github.workspace }}\${{ env.INSTALL_DIR }}\"
+ copy "${{ github.workspace }}\Qt\Tools\OpenSSL\Win_x86\bin\libcrypto-1_1.dll" "${{ github.workspace }}\${{ env.INSTALL_DIR }}\"
+
+ - name: Install OpenJDK
+ uses: AdoptOpenJDK/install-jdk@v1
+ with:
+ version: '17'
+
+ - name: Cache Qt
+ id: cache-qt
+ uses: actions/cache@v2
+ with:
+ path: "${{ github.workspace }}/Qt/"
+ key: ${{ runner.os }}-${{ matrix.qt_version }}-${{ matrix.qt_arch }}-qt_cache
+
+ - name: Install Qt
+ uses: jurplel/install-qt-action@v2
+ with:
+ version: ${{ matrix.qt_version }}
+ host: ${{ matrix.qt_host }}
+ arch: ${{ matrix.qt_arch }}
+ cached: ${{ steps.cache-qt.outputs.cache-hit }}
+ dir: "${{ github.workspace }}/Qt/"
+
+ - name: Install Ninja
+ uses: urkle/action-get-ninja@v1
+
+ - name: Download linuxdeploy family
+ if: runner.os == 'Linux'
+ run: |
+ wget "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
+ wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage"
+ wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage"
+
+ - name: Download JREs for AppImage on Linux
+ if: runner.os == 'Linux'
+ shell: bash
+ run: |
+ ${{ github.workspace }}/.github/scripts/prepare_JREs.sh
+
+ - name: Configure CMake
+ if: runner.os != 'Linux'
+ run: |
+ cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=Debug -G Ninja
+
+ - name: Configure CMake on Linux
+ if: runner.os == 'Linux'
+ run: |
+ cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DLauncher_LAYOUT=lin-system -G Ninja
+
+ - name: Build
+ run: |
+ cmake --build ${{ env.BUILD_DIR }}
+
+ - name: Install
+ if: runner.os != 'Linux'
+ run: |
+ cmake --install ${{ env.BUILD_DIR }}
+
+ - name: Install for AppImage on Linux
+ if: runner.os == 'Linux'
+ run: |
+ DESTDIR=${{ env.INSTALL_DIR }} cmake --install ${{ env.BUILD_DIR }}
+
+ - name: Bundle AppImage
+ if: runner.os == 'Linux'
+ shell: bash
+ run: |
+ export OUTPUT="PolyMC-${{ github.sha }}-x86_64.AppImage"
+
+ chmod +x linuxdeploy-*.AppImage
+
+ mkdir -p ${{ env.INSTALL_DIR }}/usr/lib/jvm/java-{8,17}-openjdk
+
+ cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_DIR }}/usr/lib/jvm/java-8-openjdk
+
+ cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_DIR }}/usr/lib/jvm/java-17-openjdk
+
+ export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_DIR }}/usr/lib"
+ LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
+ LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64"
+ LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_DIR }}/usr/lib/jvm/java-17-openjdk/lib/server"
+ LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
+
+ ./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_DIR }}/usr/share/icons/hicolor/scalable/apps/org.polymc.PolyMC.svg
+
+ - name: Run windeployqt
+ if: runner.os == 'Windows'
+ run: |
+ windeployqt --no-translations "${{ env.INSTALL_DIR }}/polymc.exe"
+
+ - name: Run macdeployqt
+ if: runner.os == 'macOS'
+ run: |
+ cd ${{ env.INSTALL_DIR }}
+ macdeployqt "PolyMC.app" -executable="PolyMC.app/Contents/MacOS/polymc" -always-overwrite -use-debug-libs
+
+ - name: chmod binary on macOS
+ if: runner.os == 'macOS'
+ run: |
+ chmod +x "${{ github.workspace }}/${{ env.INSTALL_DIR }}/PolyMC.app/Contents/MacOS/polymc"
+
+ - name: tar bundle on macOS
+ if: runner.os == 'macOS'
+ run: |
+ cd ${{ env.INSTALL_DIR }}
+ tar -czf ../polymc.tar.gz *
+
+ - name: Upload AppImage for Linux
+ if: runner.os == 'Linux'
+ uses: actions/upload-artifact@v2
+ with:
+ name: PolyMC-${{ github.sha }}-x86_64.AppImage
+ path: PolyMC-${{ github.sha }}-x86_64.AppImage
+
+ - name: Upload package for Windows
+ if: runner.os == 'Windows'
+ uses: actions/upload-artifact@v2
+ with:
+ name: polymc-${{ runner.os }}-${{ github.sha }}-portable
+ path: ${{ env.INSTALL_DIR }}/**
+
+ - name: Upload package for macOS
+ if: runner.os == 'macOS'
+ uses: actions/upload-artifact@v2
+ with:
+ name: polymc-${{ runner.os }}-${{ github.sha }}-portable
+ path: polymc.tar.gz
diff --git a/.gitignore b/.gitignore
index 69dda72c..ba90e8f8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,3 +37,8 @@ tags
branding/
secrets/
run/
+
+.cache/
+
+# Nix/NixOS
+result/
diff --git a/.gitmodules b/.gitmodules
index 04a561c2..10575207 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -2,7 +2,7 @@
path = libraries/libnbtplusplus
url = https://github.com/MultiMC/libnbtplusplus.git
pushurl = git@github.com:MultiMC/libnbtplusplus.git
+
[submodule "libraries/quazip"]
- path = libraries/quazip
- url = https://github.com/MultiMC/quazip.git
- pushurl = git@github.com:MultiMC/quazip.git
+ path = libraries/quazip
+ url = https://github.com/stachenov/quazip.git
diff --git a/BUILD.md b/BUILD.md
index 585b088c..3b6e6446 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -1,158 +1,231 @@
-Build Instructions
-==================
+# Build Instructions
# Contents
-* [Note](#note)
-* [Getting the source](#source)
-* [Linux](#linux)
-* [Windows](#windows)
-* [macOS](#macos)
-
-# Note
-
-MultiMC is a portable application and is not supposed to be installed into any system folders.
-That would be anything outside your home folder. Before running `make install`, make sure
-you set the install path to something you have write access to. Never build this under
-an administrator/root level account. Don't use `sudo`. It won't work and it's not supposed to work.
-Also note that this guide is for development purposes only.
-**No support is given for building your own fork or special build for any reason whatsoever**.
-
-# Branding, identifying marks and API keys
-
-The logo and related assets are All Rights Reserved and may only be used in official builds of MultiMC hosted on multimc.org, and as such, are not, and will not be included in this repository. The source is only provided for the purpose of collaboration.
-
-API keys are necessary for Microsoft account functionality. More info in [(Not) Secrets](https://github.com/MultiMC/Launcher/tree/develop/notsecrets)
+- [Getting the source](#getting-the-source)
+- [Linux](#linux)
+- [Windows](#windows)
+- [macOS](#macos)
# Getting the source
Clone the source code using git and grab all the submodules:
```
-git clone https://github.com/MultiMC/Launcher.git
+git clone https://github.com/PolyMC/PolyMC.git
+cd PolyMC
git submodule init
git submodule update
```
-# Linux
+The rest of the documentation assumes you have already cloned the repository.
-Getting the project to build and run on Linux is easy if you use any modern and up-to-date linux distribution.
+# Linux and FreeBSD
+
+Getting the project to build and run on Linux is easy if you use any modern and up-to-date linux distribution. If you're using FreeBSD you should use 13.0-RELEASE or newer.
## Build dependencies
-* A C++ compiler capable of building C++11 code.
-* Qt 5.6+ Development tools (http://qt-project.org/downloads) ("Qt Online Installer for Linux (64 bit)") or the equivalent from your package manager. It is always better to use the Qt from your distribution, as long as it has a new enough version. (for example, `qttools5-dev`)
-* cmake 3.1 or newer
-* zlib (for example, `zlib1g-dev`)
-* Java JDK 8 (for example, `openjdk-8-jdk`)
-* GL headers (for example, `libgl1-mesa-dev`)
+- A C++ compiler capable of building C++11 code.
+- Qt Development tools 5.6 or newer (`qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5` on Debian-based system)
+- cmake 3.1 or newer (`cmake` on Debian-based system)
+- zlib (`zlib1g-dev` on Debian-based system)
+- Java JDK (`openjdk-17-jdk`on Debian-based system)
+- GL headers (`libgl1-mesa-dev` on Debian-based system)
+- games/lwjgl port if using FreeBSD
-### Building from command line
-You need a source folder, a build folder and an install folder.
+You can use IDEs like KDevelop or QtCreator to open the CMake project if you want to work on the code.
-Let's say you want everything in `~/MultiMC/`:
+### Building a portable binary
-```
-# make all the folders
-mkdir ~/MultiMC && cd ~/MultiMC
-mkdir build
+```sh
mkdir install
-# clone the complete source
-git clone --recursive https://github.com/MultiMC/Launcher.git src
# configure the project
+cmake -S . -B build \
+ -DCMAKE_INSTALL_PREFIX=./install
+# build
cd build
-cmake -DCMAKE_INSTALL_PREFIX=../install ../src
-# build & install (use -j with the number of cores your CPU has)
-make -j8 install
+make -j$(nproc) install
```
-You can use IDEs like KDevelop or QtCreator to open the CMake project if you want to work on the code.
+### Building & Installing to the System
+
+This is the preferred method for installation, and is suitable for packages.
+
+```sh
+# configure everything
+cmake -S . -B build \
+  -DCMAKE_BUILD_TYPE=Release \
+ -DCMAKE_INSTALL_PREFIX="/usr" \ # Use "/usr" when building Linux packages. If building on FreeBSD or not for package, use "/usr/local"
+ -DLauncher_LAYOUT=lin-system
+cd build
+make -j$(nproc) install # Optionally specify DESTDIR for packages (i.e. DESTDIR=${pkgdir})
+```
+
+### Building a .deb
+
+Requirements: [makedeb](https://docs.makedeb.org/) installed on your system.
+
+```
+git clone https://mpr.makedeb.org/polymc.git
+cd polymc
+makedeb -s
+```
+
+The deb will be located in the directory the repo was cloned in.
+
+### Building an .rpm
+
+Build dependencies are automatically installed using `dnf`, but you do need the `rpmdevtools` package (on Fedora)
+in order to fetch sources and setup your tree.
+You don't need to clone the repo for this; the spec file handles that
+
+```
+cd ~
+# setup your ~/rpmbuild directory, required for rpmbuild to work.
+rpmdev-setuptree
+# get the rpm spec file from the polymc-misc repo
+wget https://raw.githubusercontent.com/PolyMC/polymc-misc/master/rpm/polymc.spec
+# install build dependencies
+sudo dnf builddep polymc.spec
+# download build sources
+spectool -g -R polymc.spec
+# now build!
+rpmbuild -bb polymc.spec
+```
+
+The path to the rpm packages will be printed when the build is complete.
+
+### Building a Slackware package
+
+To build a Slackware package, first install [qt5 SlackBuild](http://slackbuilds.org/repository/14.2/libraries/qt5/) (on 15.0 and newer installed by defualt), then set up a [JDK](https://codeberg.org/glowiak/SlackBuilds/raw/branch/master/tgz/adoptium-jdk8.tar.gz).
+
+If you're using Slackware 14.2, update cmake with these commands:
+
+```
+mkdir -p /tmp/SBo
+cd /tmp/SBo
+wget -c https://github.com/Kitware/CMake/releases/download/v3.22.2/cmake-3.22.2.tar.gz
+tar xzvf cmake-3.22.2.tar.gz
+cd cmake-3.22.2
+./configure --prefix=/usr
+make
+sudo make install
+```
+
+Next, download the [SlackBuild](https://codeberg.org/glowiak/SlackBuilds/raw/branch/master/tgz/polymc.tar.gz), unpack it and type in extracted directory:
+
+```
+sudo ./polymc.SlackBuild # script will do everything, just sit up and wait
+sudo /sbin/installpkg /tmp/polymc-version-arch-1_SBo.tgz # install the created package
+```
+
+### Building a flatpak
+
+You don't need to clone the entire PolyMC repo for this; the flatpak file handles that.
+`flatpak` and `flatpak-builder` need to be installed on your system
+
+```sh
+git clone https://github.com/flathub/org.polymc.PolyMC
+cd org.polymc.PolyMC
+# remove --user --install if you want to build without installing
+flatpak-builder --user --install flatbuild org.polymc.PolyMC.yml
+```
### Installing Qt using the installer (optional)
+
1. Run the Qt installer.
2. Choose a place to install Qt.
3. Choose the components you want to install.
- - You need Qt 5.6.x 64-bit ticked.
- - You need Tools/Qt Creator ticked.
- - Other components are selected by default, you can untick them if you don't need them.
+ - You need Qt 5.6.x 64-bit ticked.
+ - You need Tools/Qt Creator ticked.
+ - Other components are selected by default, you can untick them if you don't need them.
4. Accept the license agreements.
5. Double check the install details and then click "Install".
- - Installation can take a very long time, go grab a cup of tea or something and let it work.
+ - Installation can take a very long time, go grab a cup of tea or something and let it work.
### Loading the project in Qt Creator (optional)
+
1. Open Qt Creator.
2. Choose `File->Open File or Project`.
3. Navigate to the Launcher source folder you cloned and choose CMakeLists.txt.
4. Read the instructions that just popped up about a build location and choose one.
5. You should see "Run CMake" in the window.
- - Make sure that Generator is set to "Unix Generator (Desktop Qt 5.6.x GCC 64bit)".
- - Hit the "Run CMake" button.
- - You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
- - Hit "Finish" if CMake ran successfully.
+ - Make sure that Generator is set to "Unix Generator (Desktop Qt 5.6.x GCC 64bit)".
+ - Hit the "Run CMake" button.
+ - You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
+ - Hit "Finish" if CMake ran successfully.
6. Cross your fingers and press the Run button (bottom left of Qt Creator).
- - If the project builds successfully it will run and the Launcher window will pop up.
+ - If the project builds successfully it will run and the Launcher window will pop up.
-**If this doesn't work for you, let us know on IRC ([Esper/#MultiMC](http://webchat.esper.net/?nick=&channels=MultiMC))!**
+**If this doesn't work for you, let us know on our Discord.**
# Windows
Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt Creator. The project will simply not compile using Microsoft build tools, because that's not something we do. If it does compile, it is by chance only.
## Dependencies
-* [Qt 5.6+ Development tools](http://qt-project.org/downloads) -- Qt Online Installer for Windows
- - http://download.qt.io/new_archive/qt/5.6/5.6.0/qt-opensource-windows-x86-mingw492-5.6.0.exe
- - Download the MinGW version (MSVC version does not work).
-* [OpenSSL](https://github.com/IndySockets/OpenSSL-Binaries/tree/master/Archive/) -- Win32 OpenSSL, version 1.0.2g (from 2016)
- - https://github.com/IndySockets/OpenSSL-Binaries/raw/master/Archive/openssl-1.0.2g-i386-win32.zip
- - the usual OpenSSL for Windows (http://slproweb.com/products/Win32OpenSSL.html) only provides the newest version of OpenSSL, and we need the 1.0.2g version
- - **Download the 32-bit version, not 64-bit.**
- - Microsoft Visual C++ 2008 Redist is required for this, there's a link on the OpenSSL download page above next to the main download.
- - We use a custom build of OpenSSL that doesn't have this dependency. For normal development, the custom build is not necessary though.
-* [zlib 1.2+](http://gnuwin32.sourceforge.net/packages/zlib.htm) - the Setup is fine
-* [Java JDK 8](https://adoptium.net/releases.html?variant=openjdk8) - Use the MSI installer.
-* [CMake](http://www.cmake.org/cmake/resources/software.html) -- Windows (Win32 Installer)
+
+- [Qt 5.6+ Development tools](http://qt-project.org/downloads) -- Qt Online Installer for Windows
+ - http://download.qt.io/new_archive/qt/5.6/5.6.0/qt-opensource-windows-x86-mingw492-5.6.0.exe
+ - Download the MinGW version (MSVC version does not work).
+- [OpenSSL](https://github.com/IndySockets/OpenSSL-Binaries/tree/master/Archive/) -- Win32 OpenSSL, version 1.0.2g (from 2016)
+ - https://github.com/IndySockets/OpenSSL-Binaries/raw/master/Archive/openssl-1.0.2g-i386-win32.zip
+ - the usual OpenSSL for Windows (http://slproweb.com/products/Win32OpenSSL.html) only provides the newest version of OpenSSL, and we need the 1.0.2g version
+ - **Download the 32-bit version, not 64-bit.**
+ - Microsoft Visual C++ 2008 Redist is required for this, there's a link on the OpenSSL download page above next to the main download.
+ - We use a custom build of OpenSSL that doesn't have this dependency. For normal development, the custom build is not necessary though.
+- [zlib 1.2+](http://gnuwin32.sourceforge.net/packages/zlib.htm) - the Setup is fine
+- [Java JDK 8](https://adoptium.net/releases.html?variant=openjdk8) - Use the MSI installer.
+- [CMake](http://www.cmake.org/cmake/resources/software.html) -- Windows (Win32 Installer)
Ensure that OpenSSL, zlib, Java and CMake are on `PATH`.
## Getting set up
### Installing Qt
+
1. Run the Qt installer
2. Choose a place to install Qt (C:\Qt is the default),
3. Choose the components you want to install
- - You need Qt 5.6 (32 bit) ticked,
- - You need Tools/Qt Creator ticked,
- - Other components are selected by default, you can untick them if you don't need them.
+ - You need Qt 5.6 (32 bit) ticked,
+ - You need Tools/Qt Creator ticked,
+ - Other components are selected by default, you can untick them if you don't need them.
4. Accept the license agreements,
5. Double check the install details and then click "Install"
- - Installation can take a very long time, go grab a cup of tea or something and let it work.
+ - Installation can take a very long time, go grab a cup of tea or something and let it work.
### Installing OpenSSL
+
1. Download .zip file from the link above.
2. Unzip and add the directory to PATH, so CMake can find it.
### Installing CMake
+
1. Run the CMake installer,
2. It's easiest if you choose to add CMake to the PATH for all users,
- - If you don't choose to do this, remember where you installed CMake.
+ - If you don't choose to do this, remember where you installed CMake.
### Loading the project
+
1. Open Qt Creator,
2. Choose File->Open File or Project,
3. Navigate to the Launcher source folder you cloned and choose CMakeLists.txt,
4. Read the instructions that just popped up about a build location and choose one,
5. If you chose not to add CMake to the system PATH, tell Qt Creator where you installed it,
- - Otherwise you can skip this step.
+ - Otherwise you can skip this step.
6. You should see "Run CMake" in the window,
- - Make sure that Generator is set to "MinGW Generator (Desktop Qt 5.6.x MinGW 32bit)",
- - Hit the "Run CMake" button,
- - You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
- - Hit "Finish" if CMake ran successfully.
+ - Make sure that Generator is set to "MinGW Generator (Desktop Qt 5.6.x MinGW 32bit)",
+ - Hit the "Run CMake" button,
+ - You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
+ - Hit "Finish" if CMake ran successfully.
7. Cross your fingers and press the Run button (bottom left of Qt Creator)!
- - If the project builds successfully it will run and the Launcher window will pop up,
- - Test OpenSSL by making an instance and trying to log in. If Qt Creator couldn't find OpenSSL during the CMake stage, login will fail and you'll get an error.
+ - If the project builds successfully it will run and the Launcher window will pop up,
+ - Test OpenSSL by making an instance and trying to log in. If Qt Creator couldn't find OpenSSL during the CMake stage, login will fail and you'll get an error.
The following .dlls are needed for the app to run (copy them to build directory if you want to be able to move the build to another pc):
+
```
+
platforms/qwindows.dll
libeay32.dll
libgcc_s_dw2-1.dll
@@ -167,25 +240,31 @@ Qt5Widgets.dll
Qt5Xml.dll
ssleay32.dll
zlib1.dll
+
```
-**These build instructions worked for me (Drayshak) on a fresh Windows 8 x64 Professional install. If they don't work for you, let us know on IRC ([Esper/#MultiMC](http://webchat.esper.net/?nick=&channels=MultiMC))!**
+**These build instructions worked for me (Drayshak) on a fresh Windows 8 x64 Professional install. If they don't work for you, let us know on our Discord.**
+
### Compile from command line on Windows
+
1. If you installed Qt with the web installer, there should be a shortcut called `Qt 5.4 for Desktop (MinGW 4.9 32-bit)` in the Start menu on Windows 7 and 10. Best way to find it is to search for it. Do note you cannot just use cmd.exe, you have to use the shortcut, otherwise the proper MinGW software will not be on the PATH.
-2. Once that is open, change into your user directory, and clone MultiMC by doing `git clone --recursive https://github.com/MultiMC/Launcher.git`, and change directory to the folder you cloned to.
+2. Once that is open, change into your user directory, and clone PolyMC by doing `git clone --recursive https://github.com/PolyMC/PolyMC.git`, and change directory to the folder you cloned to.
3. Make a build directory, and change directory to the directory and do `cmake -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=C:\Path\that\makes\sense\for\you`. By default, it will install to C:\Program Files (x86), which you might not want, if you want a local installation. If you want to install it to that directory, make sure to run the command window as administrator.
-3. Do `mingw32-make -jX`, where X is the number of cores your CPU has plus one.
-4. Now to wait for it to compile. This could take some time. Hopefully it compiles properly.
-5. Run the command `mingw32-make install`, and it should install MultiMC, to whatever the `-DCMAKE_INSTALL_PREFIX` was.
-6. In most cases, whenever compiling, the OpenSSL dll's aren't put into the directory to where MultiMC installs, meaning you cannot log in. The best way to fix this is just to do `copy C:\OpenSSL-Win32\*.dll C:\Where\you\installed\MultiMC\to`. This should copy the required OpenSSL dll's to log in.
+4. Do `mingw32-make -jX`, where X is the number of cores your CPU has plus one.
+5. Now to wait for it to compile. This could take some time. Hopefully it compiles properly.
+6. Run the command `mingw32-make install`, and it should install PolyMC, to whatever the `-DCMAKE_INSTALL_PREFIX` was.
+7. In most cases, whenever compiling, the OpenSSL dll's aren't put into the directory to where PolyMC installs, meaning you cannot log in. The best way to fix this is just to do `copy C:\OpenSSL-Win32\*.dll C:\Where\you\installed\PolyMC\to`. This should copy the required OpenSSL dll's to log in.
# macOS
### Install prerequisites:
+
- Install XCode Command Line tools
- Install the official build of CMake (https://cmake.org/download/)
-- Install JDK 8 (https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html)
-- Get Qt 5.6 and install it (https://download.qt.io/new_archive/qt/5.6/5.6.3/)
+- Install JDK 8 (https://adoptium.net/releases.html?variant=openjdk8&jvmVariant=hotspot)
+- Get Qt 5.6 and install it (https://download.qt.io/new_archive/qt/5.6/5.6.3/) or higher (tested) (https://www.qt.io/download-qt-installer?utm_referrer=https%3A%2F%2Fwww.qt.io%2Fdownload-open-source)
+
+You can use `homebrew` to simplify the installation of build dependencies
### XCode Command Line tools
@@ -197,11 +276,9 @@ xcode-select --install
### Build
-Pick an installation path - this is where the final `.app` will be constructed when you run `make install`. Supply it as the `CMAKE_INSTALL_PREFIX` argument during CMake configuration.
+Pick an installation path - this is where the final `PolyMC.app` will be constructed when you run `make install`. Supply it as the `CMAKE_INSTALL_PREFIX` argument during CMake configuration. By default, it's in the dist folder under PolyMC
```
-git clone --recursive https://github.com/MultiMC/Launcher.git
-cd Launcher
mkdir build
cd build
cmake \
@@ -209,13 +286,56 @@ cmake \
-DCMAKE_CXX_COMPILER=/usr/bin/clang++ \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX:PATH="$(dirname $PWD)/dist/" \
- -DCMAKE_PREFIX_PATH="/path/to/Qt5.6/" \
- -DQt5_DIR="/path/to/Qt5.6/" \
+ -DCMAKE_PREFIX_PATH="/path/to/Qt/" \
+ -DQt5_DIR="/path/to/Qt/" \
-DLauncher_LAYOUT=mac-bundle \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.7 \
..
make install
```
+Remember to replace `/path/to/Qt/` with the actual path. For newer Qt installations, it is often in your home directory.
+
**Note:** The final app bundle may not run due to code signing issues, which
need to be fixed with `codesign -fs -`.
+
+# OpenBSD
+
+Tested on OpenBSD 7.0-alpha i386, on older should work too
+
+## Build dependencies
+- A C++ compiler capable of building C++11 code (included in base system)
+- Qt Development tools 5.6 or newer ([meta/qt5](https://openports.se/meta/qt5))
+- cmake 3.1 or newer ([devel/cmake](https://openports.se/devel/cmake))
+- zlib (included in base system)
+- Java JDK ([devel/jdk-1.8](https://openports.se/devel/jdk/1.8))
+- GL headers (included in base system)
+- lwjgl ([games/lwjgl](https://openports.se/games/lwjgl) and [games/lwjgl3](https://openports.se/games/lwjgl3))
+
+You can use IDEs like KDevelop or QtCreator to open the CMake project if you want to work on the code.
+
+### Building a portable binary
+
+```sh
+mkdir install
+# configure the project
+cmake -S . -B build \
+ -DCMAKE_INSTALL_PREFIX=./install -DCMAKE_PREFIX_PATH=/usr/local/lib/qt5/cmake
+# build
+cd build
+make -j$(nproc) install
+```
+
+### Building & Installing to the System
+
+This is the preferred method for installation, and is suitable for packages.
+
+```sh
+# configure everything
+cmake -S . -B build \
+  -DCMAKE_BUILD_TYPE=Release \
+ -DCMAKE_INSTALL_PREFIX="/usr/local" \ # /usr/local is default in OpenBSD and FreeBSD
+ -DLauncher_LAYOUT=lin-system -DCMAKE_PREFIX_PATH=/usr/local/lib/qt5/cmake # use linux layout and point to qt5 libs
+cd build
+make -j$(nproc) install # Optionally specify DESTDIR for packages (i.e. DESTDIR=${pkgdir})
+```
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d1c6ac3b..d4a27260 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,17 +13,10 @@ 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()
-if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
- if(CMAKE_HOST_SYSTEM_VERSION MATCHES ".*[Mm]icrosoft.*" OR
- CMAKE_HOST_SYSTEM_VERSION MATCHES ".*WSL.*"
- )
- message(FATAL_ERROR "Building the Launcher is not supported in Linux-on-Windows distributions.")
- endif()
-endif()
-
##################################### Set CMake options #####################################
set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
@@ -53,12 +46,13 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
##################################### Set Application options #####################################
######## Set URLs ########
-set(Launcher_NEWS_RSS_URL "https://multimc.org/rss.xml" CACHE STRING "URL to fetch Launcher's news RSS feed from.")
+set(Launcher_NEWS_RSS_URL "https://polymc.github.io/feed/feed.xml" CACHE STRING "URL to fetch PolyMC's news RSS feed from.")
+set(Launcher_NEWS_OPEN_URL "https://polymc.github.io/news/" CACHE STRING "URL that gets opened when the user clicks 'More News'")
######## Set version numbers ########
-set(Launcher_VERSION_MAJOR 0)
-set(Launcher_VERSION_MINOR 6)
-set(Launcher_VERSION_HOTFIX 14)
+set(Launcher_VERSION_MAJOR 1)
+set(Launcher_VERSION_MINOR 0)
+set(Launcher_VERSION_HOTFIX 6)
# Build number
set(Launcher_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
@@ -73,28 +67,31 @@ set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
set(Launcher_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
# The metadata server
-set(Launcher_META_URL "https://meta.multimc.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
-
-# paste.ee API key
-set(Launcher_PASTE_EE_API_KEY "utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ" CACHE STRING "API key you can get from paste.ee when you register an account")
+set(Launcher_META_URL "https://meta.polymc.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
# Imgur API Client ID
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
-# Google analytics ID
-set(Launcher_ANALYTICS_ID "UA-87731965-2" CACHE STRING "ID you can get from Google analytics")
+# MSA Client ID
+set(Launcher_MSA_CLIENT_ID "17b47edd-c884-4997-926d-9e7f9a6b4647" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
# Bug tracker URL
-set(Launcher_BUG_TRACKER_URL "" CACHE STRING "URL for the bug tracker.")
+set(Launcher_BUG_TRACKER_URL "https://github.com/PolyMC/PolyMC/issues" CACHE STRING "URL for the bug tracker.")
+
+# Translations Platform URL
+set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/polymc/polymc/" CACHE STRING "URL for the translations platform.")
# Discord URL
-set(Launcher_DISCORD_URL "" CACHE STRING "URL for the Discord guild.")
+set(Launcher_DISCORD_URL "https://discord.gg/Z52pwxWCHP" CACHE STRING "URL for the Discord guild.")
# Subreddit URL
set(Launcher_SUBREDDIT_URL "" CACHE STRING "URL for the subreddit.")
-# Use the secrets library or a public stub?
-option(Launcher_EMBED_SECRETS "Determines whether to embed secrets. Secrets are separate and non-public." OFF)
+# Builds
+# TODO: Launcher_FORCE_BUNDLED_LIBS should be off in the future, but as of QuaZip 1.2, we can't do that yet.
+set(Launcher_FORCE_BUNDLED_LIBS ON CACHE BOOL "Prevent using system libraries, if they are available as submodules")
+set(Launcher_QT_VERSION_MAJOR "5" CACHE STRING "Major Qt version to build against")
+
#### Check the current Git commit and branch
include(GetGitRevisionDescription)
@@ -104,6 +101,8 @@ message(STATUS "Git commit: ${Launcher_GIT_COMMIT}")
message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}")
set(Launcher_RELEASE_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
+string(TIMESTAMP TODAY "%Y-%m-%d")
+set(Launcher_RELEASE_TIMESTAMP "${TODAY}")
#### Custom target to just print the version.
add_custom_target(version echo "Version: ${Launcher_RELEASE_VERSION_NAME}")
@@ -112,12 +111,20 @@ add_custom_target(tcversion echo "\\#\\#teamcity[setParameter name=\\'env.LAUNCH
################################ 3rd Party Libs ################################
# Find the required Qt parts
-find_package(Qt5Core REQUIRED)
-find_package(Qt5Widgets REQUIRED)
-find_package(Qt5Concurrent REQUIRED)
-find_package(Qt5Network REQUIRED)
-find_package(Qt5Test REQUIRED)
-find_package(Qt5Xml REQUIRED)
+if(Launcher_QT_VERSION_MAJOR EQUAL 5)
+ set(QT_VERSION_MAJOR 5)
+ find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml)
+
+ if(NOT Launcher_FORCE_BUNDLED_LIBS)
+ find_package(QuaZip-Qt5 REQUIRED)
+ endif()
+ if (NOT QuaZip-Qt5_FOUND)
+ set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE)
+ set(FORCE_BUNDLED_QUAZIP 1)
+ endif()
+else()
+ message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported")
+endif()
# The Qt5 cmake files don't provide its install paths, so ask qmake.
include(QMakeQuery)
@@ -132,19 +139,16 @@ if (Qt5_POSITION_INDEPENDENT_CODE)
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
-####################################### Secrets #######################################
+####################################### Program Info #######################################
-if(Launcher_EMBED_SECRETS)
- add_subdirectory(secrets)
-else()
- add_subdirectory(notsecrets)
-endif()
+set(Launcher_APP_BINARY_NAME "polymc" CACHE STRING "Name of the Launcher binary")
+add_subdirectory(program_info)
####################################### Install layout #######################################
# How to install the build results
-set(Launcher_LAYOUT "auto" CACHE STRING "The layout for the launcher installation (auto, win-bundle, lin-nodeps, mac-bundle)")
-set_property(CACHE Launcher_LAYOUT PROPERTY STRINGS auto win-bundle lin-nodeps mac-bundle)
+set(Launcher_LAYOUT "auto" CACHE STRING "The layout for the launcher installation (auto, win-bundle, lin-nodeps, lin-system, mac-bundle)")
+set_property(CACHE Launcher_LAYOUT PROPERTY STRINGS auto win-bundle lin-nodeps lin-system mac-bundle)
if(Launcher_LAYOUT STREQUAL "auto")
if(UNIX AND APPLE)
@@ -175,7 +179,7 @@ if(Launcher_LAYOUT_REAL STREQUAL "mac-bundle")
# Mac bundle settings
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_Name}")
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_Name}: Minecraft launcher and management utility.")
- set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.multimc.${Launcher_Name}")
+ set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.polymc.${Launcher_Name}")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
@@ -209,6 +213,28 @@ elseif(Launcher_LAYOUT_REAL STREQUAL "lin-nodeps")
configure_file(launcher/Launcher.in "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" @ONLY)
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" DESTINATION ${BUNDLE_DEST_DIR} RENAME ${Launcher_Name})
+elseif(Launcher_LAYOUT_REAL STREQUAL "lin-system")
+ set(Launcher_BINARY_DEST_DIR "bin" CACHE STRING "Path to the binary directory")
+ set(Launcher_LIBRARY_DEST_DIR "lib${LIB_SUFFIX}" CACHE STRING "Path to the library directory")
+ set(Launcher_SHARE_DEST_DIR "share/polymc" CACHE STRING "Path to the shared data directory")
+ set(JARS_DEST_DIR "${Launcher_SHARE_DEST_DIR}/jars")
+ set(Launcher_DESKTOP_DEST_DIR "share/applications" CACHE STRING "Path to the desktop file directory")
+ set(Launcher_METAINFO_DEST_DIR "share/metainfo" CACHE STRING "Path to the metainfo directory")
+ set(Launcher_ICON_DEST_DIR "share/icons/hicolor/scalable/apps" CACHE STRING "Path to the scalable icon directory")
+
+ set(BINARY_DEST_DIR ${Launcher_BINARY_DEST_DIR})
+ set(LIBRARY_DEST_DIR ${Launcher_LIBRARY_DEST_DIR})
+
+ MESSAGE(STATUS "Compiling for linux system with ${Launcher_SHARE_DEST_DIR} and LAUNCHER_LINUX_DATADIR")
+ SET(Launcher_APP_BINARY_DEFS "-DMULTIMC_JARS_LOCATION=${CMAKE_INSTALL_PREFIX}/${JARS_DEST_DIR}" "-DLAUNCHER_LINUX_DATADIR")
+
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${Launcher_DESKTOP_DEST_DIR})
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${Launcher_METAINFO_DEST_DIR})
+ install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION ${Launcher_ICON_DEST_DIR})
+
+ # install as bundle with no dependencies included
+ set(INSTALL_BUNDLE "nodeps")
+
elseif(Launcher_LAYOUT_REAL STREQUAL "win-bundle")
set(BINARY_DEST_DIR ".")
set(LIBRARY_DEST_DIR ".")
@@ -234,24 +260,26 @@ endif()
include(ExternalProject)
set_directory_properties(PROPERTIES EP_BASE External)
-option(NBT_BUILD_SHARED "Build NBT shared library" ON)
+option(NBT_BUILD_SHARED "Build NBT shared library" OFF)
option(NBT_USE_ZLIB "Build NBT library with zlib support" OFF)
option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests.
-set(NBT_NAME Launcher_nbt++)
-set(NBT_DEST_DIR ${LIBRARY_DEST_DIR})
add_subdirectory(libraries/libnbtplusplus)
-add_subdirectory(libraries/ganalytics) # google analytics library
add_subdirectory(libraries/systeminfo) # system information library
add_subdirectory(libraries/hoedown) # markdown parser
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
add_subdirectory(libraries/javacheck) # java compatibility checker
add_subdirectory(libraries/xz-embedded) # xz compression
-add_subdirectory(libraries/quazip) # zip manipulation library
+if (FORCE_BUNDLED_QUAZIP)
+ message(STATUS "Using bundled QuaZip")
+ set(BUILD_SHARED_LIBS 0) # link statically to avoid conflicts.
+ set(QUAZIP_INSTALL 0)
+ add_subdirectory(libraries/quazip) # zip manipulation library
+endif()
add_subdirectory(libraries/rainbow) # Qt extension for colors
add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
-add_subdirectory(libraries/classparser) # google analytics library
+add_subdirectory(libraries/classparser) # class parser library
add_subdirectory(libraries/optional-bare)
add_subdirectory(libraries/tomlc99) # toml parser
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
diff --git a/COPYING.md b/COPYING.md
index 4c19bbc2..1ac6d5cb 100644
--- a/COPYING.md
+++ b/COPYING.md
@@ -1,5 +1,22 @@
-# MultiMC
+# PolyMC
+ Copyright (C) 2012-2021 MultiMC Contributors
+ 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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/>.
+
+# 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.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..f288702d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/README.md b/README.md
index b158625c..b7df751c 100644
--- a/README.md
+++ b/README.md
@@ -1,53 +1,177 @@
<p align="center">
- <img src="https://avatars2.githubusercontent.com/u/5411890" alt="MultiMC logo"/>
+ <img src="./program_info/polymc-header-black.svg#gh-light-mode-only" alt="PolyMC logo"/>
+ <img src="./program_info/polymc-header.svg#gh-dark-mode-only" alt="PolyMC logo"/>
</p>
+<br>
-MultiMC
-=======
+PolyMC is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.
-MultiMC is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.
+This is a **fork** of the MultiMC Launcher and not endorsed by MultiMC. The PolyMC community felt that the maintainer was not acting in the spirit of Free Software so this fork was made.
+<br>
-## Development
-If you want to contribute, talk to us on [Discord](https://discord.gg/multimc) first.
+# Installation
+- All packages (archived by version) can be found [here](https://packages.polymc.org/) ([latest](https://packages.polymc.org/latest)).
+- Last build status: https://jenkins.polymc.org/job/PolyMC/lastBuild/
-While blindly submitting PRs is definitely possible, they're not necessarily going to get accepted.
+## 🐧 Linux
-We aren't looking for flashy features, but expanding upon the existing feature set without distruption or endangering future viability of the project is OK.
+### <img src="https://www.vectorlogo.zone/logos/linuxfoundation/linuxfoundation-icon.svg" height="20" alt=""/> Cross-distro packages
-### Building
-If you want to build the launcher yourself, check [BUILD.md](BUILD.md) for build instructions.
+<a href='https://flathub.org/apps/details/org.polymc.PolyMC'><img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/></a>
-### Code formatting
-Just follow the existing formatting.
+<a href="https://packages.polymc.org/latest/appimage/PolyMC-latest-x86_64.AppImage"><img src="https://docs.appimage.org/_images/download-appimage-banner.svg" width="240" alt="Download as AppImage" /></a>
-In general, in order of importance:
-* Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
-* Prefer readability over dogma.
-* Keep to the existing formatting.
-* Indent with 4 space unless it's in a submodule.
-* Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
+- [AppImage SHA256](https://packages.polymc.org/latest/appimage/PolyMC-latest-x86_64.AppImage.sha256)
-## Translations
-Translations can be done [on crowdin](https://translate.multimc.org). Please avoid making direct pull requests to the translations repository.
+### <img src="https://www.vectorlogo.zone/logos/archlinux/archlinux-icon.svg" height="20"/> Arch Linux
-## License
-Copyright &copy; 2013-2022 MultiMC Contributors
+There are several AUR packages available:
-Licensed under the Apache License, Version 2.0 (the "License"); you may not use this program 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).
+[![polymc](https://img.shields.io/badge/aur-polymc-blue)](https://aur.archlinux.org/packages/polymc/)
+[![polymc-bin](https://img.shields.io/badge/aur-polymc--bin-blue)](https://aur.archlinux.org/packages/polymc-bin/)
+[![polymc-git](https://img.shields.io/badge/aur-polymc--git-blue)](https://aur.archlinux.org/packages/polymc-git/)
-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.
+```sh
+# stable source package:
+yay -S polymc
+# stable binary package:
+yay -S polymc-bin
+# latest git package:
+yay -S polymc-git
+```
-## Forking/Redistributing/Custom builds policy
-We keep Launcher open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.
+### <img src="https://www.vectorlogo.zone/logos/debian/debian-icon.svg" height="20" /> Debian
+
+We use [makedeb](https://docs.makedeb.org/) for our Debian packages.
+Several MPR packages are available:
+
+[![polymc](https://img.shields.io/badge/mpr-polymc-orange)](https://mpr.makedeb.org/packages/polymc)
+[![polymc-bin](https://img.shields.io/badge/mpr-polymc--bin-orange)](https://mpr.makedeb.org/packages/polymc-bin)
+[![polymc-git](https://img.shields.io/badge/mpr-polymc--git-orange)](https://mpr.makedeb.org/packages/polymc-git)
+
+```sh
+# stable source package:
+sudo tap install polymc
+# stable binary package:
+sudo tap install polymc-bin
+# latest git package:
+sudo tap install polymc-git
+```
+
+### <img src="https://www.vectorlogo.zone/logos/nixos/nixos-icon.svg" height="20" /> Nix
+
+A [Nix derivation](packages/nix/NIX.md) is available.
+
+### <img src="https://www.gentoo.org/assets/img/logo/gentoo-signet.svg" height="20" /> Gentoo
+
+A Gentoo ebuild is available in the [swirl](https://git.swurl.xyz/swirl/ebuilds) overlay, named `games-action/polymc`.
+
+```sh
+# as root:
+emerge --oneshot eselect-repository
+eselect repository enable swirl
+emaint sync -r swirl
+emerge polymc
+# to use latest git version:
+sudo tee -a /etc/portage/package.accept_keywords <<< "=games-action/polymc-9999 **"
+```
+
+### <img src="https://www.vectorlogo.zone/logos/getfedora/getfedora-icon.svg" height="20"> Fedora
+
+An RPM package is available on [COPR](https://copr.fedorainfracloud.org/coprs/polymc/polymc/).
+
+```sh
+sudo dnf copr enable polymc/polymc
+sudo dnf install polymc
+```
+
+Alternatively, a COPR maintained by a PolyMC user (instead of Jenkins' automated builds) is available [here](https://copr.fedorainfracloud.org/coprs/sentry/polymc).
+
+```sh
+sudo dnf copr enable sentry/polymc
+sudo dnf install polymc
+```
+
+### <img src="https://lotar.altervista.org/wiki/_media/news/slackware-logo.png" height="20" /> Slackware
+
+[A SlackBuild](https://codeberg.org/glowiak/SlackBuilds/src/branch/master/repository/polymc.md) is available. You will need [qt5](http://slackbuilds.org/repository/14.2/libraries/qt5/) (on 15.0 installed by default), [a JDK](https://codeberg.org/glowiak/SlackBuilds/src/branch/master/repository/adoptium-jdk8.md), and if you're on 14.2, you need to compile newer CMake version manually. To build, type in extracted directory with all dependiences met:
+
+ sudo ./polymc.SlackBuild
+ sudo installpkg /tmp/polymc-version-arch-1_SBo.tgz
+
+If you are too lazy to do all these steps, you can just download [a prebuild x86_64 package](http://glowiak.github.io/file/polymc-latest-slackware) and install it with /sbin/installpkg:
+
+ sudo /sbin/installpkg ~/Downloads/polymc-version-x86_64-1_SBo.tgz
+
+## <img src="https://www.vectorlogo.zone/logos/microsoft/microsoft-icon.svg" height="20" /> Windows
+
+[Windows (32-bit)](https://packages.polymc.org/latest/win32/win32.zip) ([SHA256](https://packages.polymc.org/latest/win32/win32.zip.sha256)) - this is a portable package, you can extract it anywhere and run it. This package needs testing.
+
+## <img src="https://www.vectorlogo.zone/logos/apple/apple-tile.svg" height="20" /> MacOS
+
+MacOS currently does not have any packages. We are still working on setting up MacOS packaging. Meanwhile, you can [build](https://github.com/PolyMC/PolyMC/blob/develop/BUILD.md#macos) it for yourself.
+
+## <img src="https://www.vectorlogo.zone/logos/freebsd/freebsd-icon.svg" height="20" /> FreeBSD
-The license gives you access to the source MultiMC is build from, but:
-- Not the name, logo and other branding.
-- Not the API tokens required to talk to services the launcher depends on.
+For FreeBSD available are:
-Because of the nature of the agreements required to interact with the Microsoft identity platform, it's impossible for us to continue allowing everyone to build the code as 'MultiMC'. The source code has been debranded and now builds as `DevLauncher` by default.
+- [AppBSD Image](http://glowiak.github.io/file/polymc-latest-fbsd64-appbsd) - a portable application, requires [AppBSD](https://codeberg.org/glowiak/appbsd/) to be installed.
-You must provide your own branding if you want to distribute your own builds.
+- [Gzipped binaries](http://glowiak.github.io/file/polymc-latest-fbsd64-raw) - traditional way to distribute, unpack and run.
-You will also have to register your own app on Azure to be able to handle Microsoft account logins.
+In both cases you need X11, Qt5 and Java installed. Both files are 64bit only.
+
+## <img src="https://raw.githubusercontent.com/AliasIO/wappalyzer/master/src/drivers/webextension/images/icons/OpenBSD%20httpd.svg" height="20" /> OpenBSD
+
+For OpenBSD available are [gzipped 32-bit binaries](http://glowiak.github.io/file/polymc-latest-obsd32-raw), download, unpack and run.
+
+You need X11, Qt5 and Java installed.
+
+## Development Builds
+
+There are per-commit development builds available [here](https://github.com/PolyMC/PolyMC/actions). These have debug information in the binaries, so their file sizes are relatively larger.
+Builds are provided for Linux, AppImage on Linux, Windows, and macOS.
+
+# Help & Support
+
+Feel free to create an issue if you need help. However, you might find it easier to ask in the Discord server.
+
+[![PolyMC Discord](https://img.shields.io/discord/923671181020766230?label=PolyMC%20Discord)](https://discord.gg/xq7fxrgtMP)
+
+For people who don't want to use Discord, we have a Matrix Space which is bridged to the Discord server:
+
+[![PolyMC Space](https://img.shields.io/matrix/polymc:polymc.org?label=PolyMC%20Space&server_fqdn=matrix.polymc.org)](https://matrix.to/#/#polymc:polymc.org)
+
+If there are any issues with the space or you are using a client that does not support the feature here are the individual rooms:
+
+[![Support](https://img.shields.io/matrix/support:polymc.org?label=%23support&server_fqdn=matrix.polymc.org)](https://matrix.to/#/#support:polymc.org)
+[![Discussion](https://img.shields.io/matrix/discussion:polymc.org?label=%23discussion&server_fqdn=matrix.polymc.org)](https://matrix.to/#/#discussion:polymc.org)
+[![Development](https://img.shields.io/matrix/development:polymc.org?label=%23development&server_fqdn=matrix.polymc.org)](https://matrix.to/#/#development:polymc.org)
+[![News](https://img.shields.io/matrix/news:polymc.org?label=%23news&server_fqdn=matrix.polymc.org)](https://matrix.to/#/#news:polymc.org)
+
+# Development
+
+If you want to contribute to PolyMC you might find it useful to join our Discord Server or Matrix Space.
+
+## Building
+
+If you want to build PolyMC yourself, check [BUILD.md](BUILD.md) for build instructions.
+
+## Code formatting
+Just follow the existing formatting.
+
+In general, in order of importance:
+
+- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
+- Prefer readability over dogma.
+- Keep to the existing formatting.
+- Indent with 4 space unless it's in a submodule.
+- Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
+
+## Translations
+
+The translation effort for PolyMC is hosted on [Weblate](https://hosted.weblate.org/projects/polymc/polymc/) and information about translating PolyMC is available at https://github.com/PolyMC/Translations
+
+## Forking/Redistributing/Custom builds policy
-If you decide to fork the project, a mention of its origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork *without* implying that you have our blessing.
+Do whatever you want, we don't care. Just follow the license. If you have any questions about this feel free to ask in an issue.
diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in
index 9e449aec..0ffc9326 100644
--- a/buildconfig/BuildConfig.cpp.in
+++ b/buildconfig/BuildConfig.cpp.in
@@ -12,6 +12,7 @@ Config::Config()
LAUNCHER_DOMAIN = "@Launcher_Domain@";
LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@";
LAUNCHER_GIT = "@Launcher_Git@";
+ LAUNCHER_DESKTOPFILENAME = "@Launcher_DesktopFileName@";
USER_AGENT = "@Launcher_UserAgent@";
USER_AGENT_UNCACHED = USER_AGENT + " (Uncached)";
@@ -24,30 +25,37 @@ Config::Config()
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
UPDATER_BASE = "@Launcher_UPDATER_BASE@";
- ANALYTICS_ID = "@Launcher_ANALYTICS_ID@";
NOTIFICATION_URL = "@Launcher_NOTIFICATION_URL@";
FULL_VERSION_STR = "@Launcher_VERSION_MAJOR@.@Launcher_VERSION_MINOR@.@Launcher_VERSION_BUILD@";
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
- if(GIT_REFSPEC.startsWith("refs/heads/") && !UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty() && VERSION_BUILD >= 0)
+ if(GIT_REFSPEC.startsWith("refs/heads/"))
{
VERSION_CHANNEL = GIT_REFSPEC;
VERSION_CHANNEL.remove("refs/heads/");
- UPDATER_ENABLED = true;
+ if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty() && VERSION_BUILD >= 0) {
+ UPDATER_ENABLED = true;
+ }
+ }
+ else if (!GIT_COMMIT.isEmpty())
+ {
+ VERSION_CHANNEL = GIT_COMMIT.mid(0, 8);
}
else
{
- VERSION_CHANNEL = QObject::tr("custom");
+ VERSION_CHANNEL = QObject::tr("unknown");
}
VERSION_STR = "@Launcher_VERSION_STRING@";
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
- PASTE_EE_KEY = "@Launcher_PASTE_EE_API_KEY@";
+ NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@";
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
+ MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
META_URL = "@Launcher_META_URL@";
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
+ TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@";
DISCORD_URL = "@Launcher_DISCORD_URL@";
SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@";
}
diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h
index ac05f288..111381ab 100644
--- a/buildconfig/BuildConfig.h
+++ b/buildconfig/BuildConfig.h
@@ -14,6 +14,7 @@ public:
QString LAUNCHER_DOMAIN;
QString LAUNCHER_CONFIGFILE;
QString LAUNCHER_GIT;
+ QString LAUNCHER_DESKTOPFILENAME;
/// The major version number.
int VERSION_MAJOR;
@@ -46,9 +47,6 @@ public:
QString USER_AGENT_UNCACHED;
- /// Google analytics ID
- QString ANALYTICS_ID;
-
/// URL for notifications
QString NOTIFICATION_URL;
@@ -71,9 +69,9 @@ public:
QString NEWS_RSS_URL;
/**
- * API key you can get from paste.ee when you register an account
+ * URL that gets opened when the user clicks "More News"
*/
- QString PASTE_EE_KEY;
+ QString NEWS_OPEN_URL;
/**
* Client ID you can get from Imgur when you register an application
@@ -81,11 +79,17 @@ public:
QString IMGUR_CLIENT_ID;
/**
+ * Client ID you can get from Microsoft Identity Platform when you register an application
+ */
+ QString MSA_CLIENT_ID;
+
+ /**
* Metadata repository URL prefix
*/
QString META_URL;
QString BUG_TRACKER_URL;
+ QString TRANSLATIONS_URL;
QString DISCORD_URL;
QString SUBREDDIT_URL;
@@ -94,7 +98,7 @@ public:
QString AUTH_BASE = "https://authserver.mojang.com/";
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
QString FMLLIBS_BASE_URL = "https://files.multimc.org/fmllibs/";
- QString TRANSLATIONS_BASE_URL = "https://files.multimc.org/translations/";
+ QString TRANSLATIONS_BASE_URL = "https://meta.polymc.org/translations/";
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
@@ -110,3 +114,4 @@ public:
};
extern const Config BuildConfig;
+
diff --git a/changelog.md b/changelog.md
deleted file mode 100644
index 7b1d4ae8..00000000
--- a/changelog.md
+++ /dev/null
@@ -1,1532 +0,0 @@
-# MultiMC 0.6.14
-
-This further refines Microsoft account support, along with small fixes related to modpack platforms and Java runtime detection.
-
-It's also been 10 years since the first release of MultiMC. All background cats are now ready to party!
-
-### Microsoft accounts
-
-The account system now refreshes accounts in the background while the application is running.
-
-- GH-4071: Errors encountered while refreshing account tokens no longer always result in the tokens expiring:
- - Network errors encountered when refreshing the main account tokens result in the account being **Offline**.
- - **Hard** errors are produced by the main tokens becoming provably invalid.
- - Errors encountered later are treated as **Soft** - they do make the account unusable, but still recoverable by trying again.
- - **Soft** errors are treated as **Hard** errors when adding the account initially.
-
-In general, this should make MultiMC much more forgiving towards various temporary and non-fatal errors.
-
-- GH-4217: Added support for GamePass accounts and Minecraft profile setup:
- - The new endpoint for logging in with Microsoft is now used (`/launcher/login`), enabling compatibility with GamePass.
- - Game ownership is checked instead of only relying on Minecraft profile presence.
- - Accounts can now be added even when they do not have a profile.
- - The launcher should guide you through selecting a Minecraft name if you don't have one yet.
-
-### Modpack platform changes
-
-- GH-4055: MultiMC now tries to avoid downloading multiple files to the same path for FTB modpacks.
-
-- Search as you type is now used for FTB.
-
-- GH-4185: Version of the modpack is now included in the name of the instance by default.
-
-- The modpack platform UIs now include text field clear buttons.
-
-### Other changes
-
-- Adjusted warnings about Java runtime required for Minecraft 1.18 (it's not Java 16, it's Java 17).
-
-- GH-3490: Instance sorting is now aware of numbers (and sorts 99 before 100).
-
-- GH-4164: Reimplemented assigning instances to groups using drag & drop.
-
-- GH-1795: Added terminal launch option to use a specific Minecraft profile (in-game player name).
-
- Used like this:
- ```
- ./MultiMC --launch 1.17.1 --profile MultiMCTest --server mc.hypixel.net
- ```
-
-- GH-4227: Fix crash related to invalid Forge mod metadata.
-
-- GH-4200: Search for the *Eclipse Foundation* and *Adoptium* Java runtimes in the Windows Registry.
-
-- Added shader packs page to instances.
-
-- Removed Mojang services status information from the main window - the status is no longer provided by Mojang.
-
-- It is now possible to turn of global tracking of play time.
-
-### Technical changes
-
-- Debranding is mostly finished. You may see some changes in the logo being used in less places.
-
-# Previous releases
-
-## MultiMC 0.6.13
-
-This release brings initial support for Microsoft accounts, along with a nice pile of modpack platform support changes and improved Java runtime detection.
-
-Java runtimes still need an overhaul, so we're staying on the 0.6 version for a little longer.
-
-Next release should also tackle the current Forge 1.17.x issues in a systematic way.
-
-#### Microsoft accounts
-
-This is the first release with Microsoft accounts in.
-
-Implementation is loosely based on documentation available from [wiki.vg](https://wiki.vg/Microsoft_Authentication_Scheme) with some notable changes:
-
-- More complete implementation including getting and displaying GamerTags [(see XR-046)](https://docs.microsoft.com/en-us/gaming/gdk/_content/gc/policies/pc/live-policies-pc#xr-046-display-name-and-gamerpic-).
-
-- Using the OAuth Device Flow instead of closely integrating with a browser engine.
-
- MultiMC asks you to open a Microsoft login web page and put in a code that lets MultiMC authenticate.
-
- This lets you authenticate on a completely separate device like your phone, leaving code we ship and the computer you may not even trust out of the picture.
-
-As part of this, the skin fetching no longer uses a third party service and instead gets skins directly from Mojang.
-
-Capes can also be selected in MultiMC now. With how many people will now get one for migrating their accounts, it only makes sense.
-
-#### macOS update
-
-Because of issues with the Microsoft accounts, we now have two builds on macOS:
-
-- The old build with Qt 5.6 that does not work with Microsoft accounts, but can run on macOS older than 10.13.
-
-- A new build with Qt 5.15.2 that does work with Microsoft accounts, can use the new macOS dark theme and highlight colors, but requires at least macOS 10.13.
-
-MultiMC will update to the 5.15.2 builds when it detects that this is possible. **It may look like it is updating twice, just let it do its thing.**
-
-Similar approach got attempted on Windows, aiming to fix various display scaling and theming issues, but it ran into too many problems and will be attempted later, with more caution.
-
-#### Modpack platforms
-
-In general, the modpack platform pages have been made more consistent with each other (GH-3118, GH-3720, GH-3731).
-
-- FTB improvements:
-
- - Modpack file downloads are now checked with checksums and cached.
-
- - GH-1949: Allow Legacy FTB and FTB pack downloads to be aborted.
-
-- CurseForge improvements:
-
- - CurseForge modpack platform is now presented as CurseForge, not Twitch.
-
- - UI has been updated to match other platforms
-
- - Added sorting
-
- - GH-3667: Added version selection
-
- - GH-3611: Added ability to install beta versions
-
- - GH-3633: When a CurseForge pack is available for multiple Minecraft versions, we assume the latest one.
-
-- ATLauncher improvements:
-
- - Handling latest/custom/recommended mod loader versions.
-
- - Fabric loader packs should now work.
-
- - GH-3764: Only client mods are installed now for ATL packs.
-
- - Improved error handling
-
- - Optional mods are supported.
-
- - GH-1949: Allow ATLauncher pack downloads to be aborted
-
-
-- Fixed bugs in FTB platform search.
-
-#### Other changes
-
-- Forge installation is disabled on Minecraft 1.17+ because of incompatible/unresolved changes on the Forge side.
-
- We're going to aim for fixing it in time for 1.18. Thankfully, 1.17 is more of a in-between release, so go play some 1.16.x packs!
-
-- GH-2529: On macOS, MultiMC will ask to move all the instance data to a new `Data` folder in order to fix long load times caused by macOS checking all files.
-
-- Detection of a large amount of various Java runtime flavors have been added.
-
-- It is now possible to join servers when starting an instance:
-
- - From command line via the `--launch` and `--server` arguments.
-
- - Or by setting this up in the instance settings page.
-
- This may not work correctly in some cases, because it is a rarely used feature and modders do not test with it.
-
-- MultiMC now prints resolved IP addresses of Minecraft services into the game log for diagnostic purposes.
-
-- Updated instance icons based on Minecraft textures.
-
-- Forge `mods.toml` files are now used for displaying mods in the UI.
-
-- Datapack button is now disabled when no world is selected.
-
-- MultiMC warns about GLFW and OpenAL workarounds being enabled in the game log.
-
-- Languages in the translations list are now sorted by their two/three letter key
-
-- GH-3450: Displaying and recording gameplay time is now optional and can be turned off.
-
-- GH-3930: MultiMC can now track the gameplay time of the last session.
-
-- GH-3033: The version pages of instances now have a filter bar.
-
-- GH-2971: UI descriptions of texture and resource packs no longer mention mods.
-
-- Quick and dirty minimum Java runtime versions checks have been added. This needs to be expanded in the future.
-
-#### Technical changes
-
-- The codebase continues to move towards being debranded and harder to build as 'MultiMC' for third parties.
-
-
-## MultiMC 0.6.12
-
-After roughly one year of maintenance and development work by various contributors, we're just calling it a good time to release.
-
-What got added since the last time? Quite a bit! But in general, this is more of a spring cleaning before the major changes that we need to make come in.
-
-#### Modpack platforms
-
-We've added a whole bunch of new modpack platforms to pick from right into the new instance dialog. If you run into any unusual issues with the imported packs, report them on the bug tracker.
-
-- Added a CurseForge pack browser
-
-- GH-3095: Added an FTB pack browser
-
- Temporarily, MultiMC ignores download failures for FTB packs (GH-3304). This is because the platform has consistency issues.
-
-- GH-469: Added a Technic/Solder pack browser
-
-- GH-405: Added a ATLauncher pack browser
-
-#### Other changes
-
-- Added the option to not use OpenAL and/or GLFW libraries bundled with the game.
-
- This is interesting if you have ones that come with your system and work better.
-
-- Skins (the part used for account icons) are now rendered with the overlay on.
-
-- GH-3130: Skin upload has been switched over to the new Mojang API and should have less issues.
-
-- MultiMC now shows world icons and allows resetting world icons in `View Worlds`.
-
-- GH-3229: Copy seed button has been updated to be compatible with newer versions of the game.
-
-- GH-3427: `View Worlds` now has a very simple `Datapacks` button - it just opens the system file browser.
-
-- GH-3189: Updated nbt library - this makes `View Worlds` work properly again for newer versions of the game.
-
-- Fixed online saving in Classic versions.
-
-- GH-3131: Fixed not working with proxy ports greater than 32767.
-
-- Proxy login details are no longer logged in files.
-
-- GH-3467: The launch could stall in the ScanModFolders task if the mod folders didn't exist yet.
-
-- GH-3602: Pre-launch commands could fail on first launch of the instance because the .minecraft folder has not been created yet.
-
-#### Technical changes
-
-- GH-3234: At build time, the meta URL can be changed.
-
-- Removed some hacks previously required to get Forge working
-
- MultiMC no longer contains pack200 and the custom lzma format support used by Forge only.
-
-- Some preparations have been done to allow downloading Java runtimes from Mojang - support for the Piston repository.
-
-- Compatibility with unusual build environments has been increased
-
-## MultiMC 0.6.11
-
-This adds Forge 1.13+ support using [ForgeWrapper](https://github.com/ZekerZhayard/ForgeWrapper) by ZekerZhayard.
-
-#### New or changed features
-
-- GH-2988: You can now import instances and curse modpacks from the command line with the `--import` option followed by either an URL or a local file path.
-
-- GH-2544: MultiMC now supports downloading library files without including them on the Java classpath.
-
- This is done by adding them to the `mavenFiles` list instead of the `libraries` list.
-
- Such downloads are not deduplicated or version upgraded like libraries are.
-
- This enables ForgeWrapper to work - MultiMC downloads all the files, and ForgeWrapper runs the Forge installer during instance start when needed.
-
-## MultiMC 0.6.8
-
-This is mostly about removal of the 'curse URL' related features, because they were of low quality and generally unreliable.
-
-There are some bug fixes included.
-
-MultiMC also migrated to a new continuous deployment system, which makes everything that much smoother.
-
-### New or changed features
-
-- GH-852: Instance group expansion status now saves/loads as expected.
-
-- The bees have invaded the launcher. We now have a bee icon.
-
-- Translations have been overhauled, yet again...
-
- - We now have a [crowdin site](https://translate.multimc.org/) for all the translation work.
-
- - Translations are made based on the development version, and for the development version.
-
- - Many strings have been tweaked to make translating the application easier.
-
- - When selecting languages, European Portuguese is now displaying properly.
-
-- Accessibility has been further improved - the main window reads as `MultiMC`, not a long string of nonsensical version numbers, when announced by a screen reader.
-
-- Removed the unimplemented Technic page from instance creation dialog.
-
-- GH-2859: Broken twitch URL import method was removed.
-
-- GH-2819: Filter bar in mod lists now also works with descriptions and author lists.
-
-- GH-2832: Version page now has buttons for opening the Minecraft and internal libraries folders of the instance.
-
-- GH-2769: When copying an instance, there's now an option to keep or remove the total play time from the copy.
-
-### Bugfixes
-
-- GH-2880: Clicking the service status indicators now opens a valid site again, instead of going nowhere.
-
-- GH-2853: When collapsing groups in instance view, the action no longer becomes 'sticky' and doesn't apply to items clicked afterwards.
-
-- GH-2787: "Download All" button works again.
-
-- When a component is customized, the launcher will not try to update it in an infinite loop when something else requires a different version.
-
-
-## MultiMC 0.6.7
-
-The previous release introduced some extra buttons that made the instance window way too big for some displays. This release is aimed at fixing that, along with other UI and performance improvements.
-
-There are some accessibility fixes thrown in too.
-
-### New or changed features
-
-- Mod lists are now asynchronous and heavily threaded.
-
- Basically, both faster and more responsive.
-
- The changes necessary for this also pave the way for having other sources of mod metadata, and for adding more mod-related features support in general.
-
-- Mod list printed in log has been improved.
-
- It now also shows disabled mods, and has prefix and suffix that shows if the mod is enabled, and if it is a folder.
-
-- You can now enable and disable mods with the keyboard.
-
- Toggle with enter.
-
-- Enabling and disabling mods no longer makes the list forget what was selected.
-
-- GH-358: Switched all the dialog pages from using buttons in layouts to toolbars.
-
- Toolbar buttons are smaller, and the toolbars can overflow buttons into an overflow space. This allows requiring a lot less space for the windows.
-
- All of the relevant pages now also have context menus to offset the issues toolbars create when using screen readers.
-
-- Main window instance list is now compatible with screen readers.
-
- If you have poor or no eyesight, this makes MultiMC usable.
-
-- More instance pages are now visible when the instance is running.
-
- Mods, version and the like should now be visible, but most of the controls are disabled until the game closes.
-
-- GH-2550, GH-2722, GH-2762: Mod list sorting is much improved.
-
- You can now sort mods by enabled status.
-
- Sorting by version actually looks at the versions as versions, not words.
-
- Sorting by name ignores 'The' prefixes in mod names. For example, 'The Betweenlands' will be sorted as 'Betweenlands'.
-
- Sorting cascades from 'Enabled' to 'Name' and then 'Version'. This means that if you sort 'Enabled', the enabled and disabled mods are still sorted
- by name and mods with the same name will be also sorted by version.
-
-## MultiMC 0.6.6
-
-This release is mostly the smaller things that have accumulated over time, along with a big change in linux packaging.
-
-No 1.13+ Forge news yet. That's going to be a major overhaul of many of the internals of MultiMC.
-
-### **IMPORTANT**
-
-On linux, MultiMC no longer bundles the Qt libraries. This fixes many issues, but it might not run after the update unless you have the required libraries installed.
-
-Make sure you have the following packages before you update:
-
-- Arch: `qt5-base`
-- Debian/Ubuntu: `qt5-default`
-- CentOS/RHEL/Fedora: `qt5-qtbase-gui`
-- Suse: `libqt5-qtbase`
-
-MultiMC on linux is built with Qt 5.4 and older versions of Qt will not work.
-
-This should be a massive improvement to system integration on linux and resolves GH-1784, GH-2605, GH-1979, GH-2271, GH-1992, GH-1816 and their many duplicates.
-
-### New or changed features
-
-- GH-2487: No is now the default button when deleting instances.
-
-- It is now possible to launch with profilers in offline mode.
-
-- Massively improved support for icon formats when importing and exporting instances.
-
- All of the formats MultiMC supports are now supported in exported instances too, instead of just PNG.
-
-- Added the pocket fox icon.
-
- We still have the big one under the staircase. It's cute. Just hide your chickens.
-
-- Global settings can be opened from instance settings where appropriate.
-
- Many people use the instance overrides where using the global settings would be more appropriate. Hopefully this makes it clearer that the instance settings are overrides for the global settings.
-
-- Added direct Fabric loader support.
-
- Much overdue. It's good. Fabric mod metadata is also supported in the mod pages.
-
-- MultiMC now recognizes the new `experimental` Minecraft versions.
-
- Go mess with the combat experiment. It's interesting.
-
-- Added Twitch URL as an option to the Add Instance dialog.
-
- You can now drag the purple download buttons from CurseForge into MultiMC and get a modpack out of it. Much easier!
-
-### Bugfixes
-
-- Translation folder is now created sooner, making first launch translation fetch work again.
-
-- GH-2716: MultiMC will no longer try to censor values shorter than 4 characters in logs.
-
- It was actually leaking information and destroying the logs instead of helping.
-
-- GH-2551: Trim server name and IP before saving them.
-
-- GH-2591: Fix multiple potential memory leaks and crashes related to destroying objects with Qt memory lifecycle model.
-
-- `run.sh` on linux now passes all arguments to MultiMC.
-
-- Adding a disabled mod duplicate now replaces the existing mod.
-
-- GH-2592: Newly created instances are now selected again. This was a very old regression.
-
-- GH-689: MultiMC no longer creates an imgur album for single screenshot uploads.
-
-- GH-1813: `#` is now saved properly when used in instance notes.
-
-- GH-2515: Deleting an instance externally while the delete dialog is open no longer leads to some other instance being deleted when you click OK.
-
-- GH-2499: Proxy settings are applied immediately and no longer need an application restart.
-
-- GH-1701: When downloading updates, the text now reflects the number of downloaded files better.
-
-- Icon scaling issues on macOS should now be fixed.
-
-## MultiMC 0.6.5
-
-Finalizing the translation workflow improvements and adding fixes for sounds missing in old game versions.
-
-### New or changed features
-
-- UI for the language settings has been unified across the application
-
-- GH-2209: Sounds in old (pre-1.6) versions should now work again
-
- The launcher now downloads the correct assets and reconstructs the `resources` folder inside instances. This mirrors the same fix implemented in vanilla.
-
- Also, a minor issue with the reconstruction being done twice per launch has been fixed.
-
-
-## MultiMC 0.6.4
-
-Update for a better translation workflow, and new FTB API location.
-
-### New or changed features
-
-- FTB API location has changed
-
- MultiMC now uses the new location and should keep working.
-
-- Translations have been overhauled, again
-
- It is now possible to put the translation source `.po` files into the `translations` folder and see changes in MultiMC immediately.
-
- The new translation workflow is like this:
- * Get a `.po` file from here the [translations repository](https://github.com/MultiMC/MultiMC5-translate).
- * Alternatively, get the `template.pot` and start a new translation based on it.
- * Put it in the `translations` folder.
- * Edit it with [POEdit](https://poedit.net/).
- * See the changes in real time.
- * When done, post the changed files on discord, or github.
-
- When using a `.po` file, MultiMC logs which strings are missing from the translation on the currently displayed UI screen(s), and which one are marked as fuzzy. This should make it easy to determine what's important.
-
-## MultiMC 0.6.3
-
-This is a release mostly aimed at getting all the small changes and fixes out of the door.
-
-### Potentially breaking changes
-
-- Local libraries are only loaded from inside the instances now.
-
- Before, MultiMC allowed loading local libraries from the main `libraries` folder.
- This in turn allowed existence of instances which could not be transported from one installation of MultiMC to another.
-
- GH-2475: A bug that allowed the launch to continue with missing local libraries has also been fixed.
-
- Effectively, you will get errors from launching such instances. You can fix the errors by copying the libraries to the locations indicated in the error log.
-
-### New or changed features
-
-- FTB import now has support for third party modpack codes.
-
- Better late than never?
-
-- Instance creation can now be interrupted / aborted.
-
-- GH-2053: You can now inspect and change the `servers.dat` file from MultiMC.
-
-- MultiMC now uses the https protocol for many more network requests.
-
-- GH-2352: There is now a button to open the `.minecraft` folder inside the selected instance.
-
-- GH-2232: MultiMC can now use `.gif` icons (not animated).
-
-- GH-2101: Instance renaming is now done inline, in the actual instance list.
-
-- GH-2452: When deleting a group, MultiMC asks for confirmation.
-
-- GH-1552: PermGen is no longer shown when it's not appropriate (java 8 and up).
-
-- GH-2144: When changing versions of a component like Forge, the current version is marked with `(installed)`.
-
-- GH-2374: World list has been improved:
-
- - Alternating line background colors have been added.
- - The world game type is now shown in a column.
-
-- GH-2384: When installing a mod, existing mod with the same file name will be replaced.
-
-- The background cat sometimes wears a silly hat.
-
-### Bugfixes
-
-- GH-2252: Fixed odd drag and drop behaviour on Windows
-
- Drag and drop of URLs from a browser locked up the browser. This needs further fixes on macOS.
-
-- Instance naming fixes:
-
- - GH-2355: Whitespace prefix or suffix is no longer allowed.
- - GH-2238: Newlines in instance names are no longer allowed either.
-
-- GH-2412: MultiMC no longer leaves behind zombie processes after launch on linux.
-
-- GH-2382: Version filter for the forge/liteloader version lists was not matching the whole version name.
-
-- GH-2488: More issues with broken relative URL redirection in Qt have been fixed.
-
-- Some memory leaks of downloaded data have been fixed.
-
-- MultiMC now handles instance groups and instance group saving better.
-
- Long deleted groups no longer persist in the group list.
-
-- GH-2467: Broken (and nonsensical) sorting indicators have been removed from the versions page header.
-
-## MultiMC 0.6.2
-
-### New or changed features
-
-- MultiMC now has FTB integration:
-
- - Official and third-party modpacks work.
- - Codes for private modpacks are not implemented yet.
-
-- Version lists now show release dates where available.
-
-- New instance dialog:
-
- - It has been completely overhauled and now uses the same kind of paged dialog design as the rest of MultiMC.
- - Vanilla version list now has a filter for version types.
- - FTB integration gets a page here, along with zip import and vanilla.
- - Technic integration is a definite possibility in the future.
- - If there is a decent way to list Twitch modpacks, proper Twitch modpack integration is a possibility too.
- - There still is no modpack updating. Much more work is needed for that.
-
-- Other Logs page:
-
- - Now has a search bar, just like the main log page.
- - GH-604: Uses the same font settings as the main log.
-
-- Icon selection dialog now has a button for opening the icons folder.
-- MultiMC now has a shinier, updated logo.
-- GH-2150: Custom commands have been split from the java settings into a new page.
-
- The use of variables in custom commands is now better documented.
- The label shows that they need to be prefixed by `$`.
-
-- Player name is no longer censored in logs.
-- MultiMC now probes the system for the name of the linux distribution as part of analytics. This will be used to focus future packaging efforts.
-- Secret cheat code has been added... What does it do?
-
-### Bugfixes
-
-- VisualVM integration now works when VisualVM is bundled inside the MultiMC folder (uses a relative path).
-- When reinstalling a component, or changing a component version, the custom version is now removed first.
-- GH-2134: Fix multiple issues with the skin upload:
-
- - When uploading a skin, the model selection now works correctly again.
- - When the new skin file is specified using the `file://` URL scheme, it will now work correctly.
-
-- GH-2143: Mojang services status display now reflects the current set of services.
-- GH-2154: MultiMC now ignores the `hidden` flag of instance folders and they should show up correctly.
-- When migrating Legacy instances, custom `minecraft.jar` will be preserved.
-
-## MultiMC 0.6.1
-
-### New or changed features
-
-- GH-2087: The version page now has a button that will download all the necessary files without launching the game.
-
-### Bugfixes
-
-- Several issues related to bad URLs returned by the Curse servers have been fixed.
-
- The Curse platform does not use valid URLs according to [RFC 3986, section 2](https://tools.ietf.org/html/rfc3986#section-2) by including spaces and UTF-8 characters without percent encoding them.
- MultiMC has been improved to handle these invalid URLs and report errors in case other invalid URLs are encountered.
- This affected pretty much all modpack imports. You may want to reimport them if you were affected by this.
-
-- GH-1780, GH-2102, GH-2103: Multiple issues with the build system and packaging on linux have been fixed.
-
- - Installed libraries now no longer have `RPATH` set and have the correct file permissions when using the `lin-system` layout.
- - Installation using the `lin-bundle` layout has been fixed on platforms that use position independent code.
- - `CMAKE_INSTALL_PREFIX` and `DESTDIR` now behave as expected on linux platforms.
-
-- MultiMC no longer logs the process environment and launch scripts to its log files.
-
-- GH-2089: Mention of instance tracking has been removed from the deletion confirmation dialog.
-
-- GH-2087: The obsolete 'revert to vanilla' logic that was previously applied to versions has been removed.
-
- This should remove some confusing situations that could happen while changing and manipulation instance versions.
-
-- The temporary `Minecraft.jar` is now removed from the instance after it stops running.
-
-- GH-2119: The main instance view scrollbar now correctly updates when the window is resized without changing the number of icons that can fit into it horizontally.
-
-## MultiMC 0.6.0
-
-### New or changed features
-
-- Contact with Mojang, Forge and LiteLoader servers is no longer handled by MultiMC, but a metadata server. Instead of generating and storing the files at the point of installation, they are updated hourly on the server and can be fixed when something goes wrong.
-
- This goes along with some changes to the instance format and to the metadata format.
-
- Instead of including the metadata JSON files directly in the instances, the instances now contain a new `mmc-pack.json` file that specifies versions to be used.
-
- The metadata can be found at [v1.meta.multimc.org](https://v1.meta.multimc.org), the [meta.multimc.org](https://meta.multimc.org) endpoint that was used during development will be replaced by documentation.
-
- This should be a much more reliable solution going forward, because it allows fixing issues without releasing new versions of MultiMC or reinstalling Forge/LiteLoader/others.
-
-- Tracking of FTB launcher instances has been replaced with direct import of Curse modpacks.
-
- You can import the modpack zip files from CurseForge and FTB:
- - Get the zip, for example from [here](https://www.feed-the-beast.com/projects/ftb-retro-ssp/files/2219693).
- - Drag & Drop it on top of the main window, or select it in the new instance dialog.
- - Let the magic happen.
-
- If you need help moving over your old instances or worlds from the FTB launcher, stop by in the MultiMC discord server.
-
- The Curse import functionality is there thanks to the work [@Dries007](https://twitter.com/driesk007) and [@NikkiAI](https://twitter.com/NikkyAI) have done on [CurseMeta](https://cursemeta.dries007.net/).
-
-- GH-1314: MultiMC now allows replacing the main jar in an instance without having to mod the Mojang jars.
-
- This goes along with changing the wording of the jar mod button to make it clear that it adds files to the main Minecraft jar instead of installing mod files with the `.jar` extension.
-
-- Because the current instance format can now handle replacing the main jar, Legacy format instances are no longer directly supported.
-
- Instead of launching, you will be prompted to convert them to the current instance format.
- If the automated process fails, stop by in the MultiMC discord server and ask for help.
-
-- Main window UI has been changed for increased clarity.
-
- Many people had issues finding the settings and instead ended up using the per-instance overrides. The main toolbar now has labels and the per-instance overrides have been deemphasized by removing the direct path to them from the main window. In general, it should be easier to find the right settings menu without getting things completely wrong on the first try.
-
-- GH-1997: MultiMC now supports Java 9.
-
- This does not mean that the current mod loaders and mods do, but you can run Vanilla Minecraft with Java 9 now.
-
- However, Java 9 will come up last in the lists when multiple versions are installed and its use is strongly discouraged.
-
-- GH-2026: You can launch Minecraft 1.13 snapshots - and hopefully also 1.13 once it is released.
-
- The bare minimum of changes needed for 1.13 to launch has been done.
-
- This does not mean support for modded 1.13!
-
- It is not yet clear what it will even look like and what exactly will be needed for Forge to be able to install properly.
-
-- Bundled Qt libraries have been updated to version 5.6.3 on macOS and Windows
-
- This means less issues with SSL encryption on macOS and better support for HiDPI/retina displays, along with many bug fixes.
- The workarounds for SSL problems on macOS have been removed thanks to this.
-
-- Linux builds were moved to a newer version of Ubuntu (14.04)
-
- This means better support on newer distribution releases, and dropping support for older distributions.
-
-- Bundled OpenSSL library on Windows no longer requires Visual Studio runtime libraries.
-
- This should avoid issues with missing runtime libraries.
-
-- GH-1855: The instance window now has an offline launch button.
-
-- GH-1886: UI now clarifies that MultiMC proxy settings do not apply to the game.
-
-- It is now possible to package MultiMC on linux without hacks.
-
- The build system has a concept of 'install layouts'. Example Arch linux package that uses this (multimc-git) is [available in the AUR](https://aur.archlinux.org/packages/multimc-git).
-
-- Wrapper commands now support arguments.
-
- Previously, they would be treated as a single command -- spaces and all.
-
-- UI elements that set maximum JVM memory are now limited to the amount of system memory present.
-
- Before, they were hardcoded.
-
-
-
- This is to accommodate the needs of some new mods for ancient Minecraft versions that do not work well with the applet wrapper.
-
-- On instance launch, the used GPU and graphics driver are reported - but only on linux.
-
- Other platforms will hopefully get this in the future.
-
-- There are some under the hood improvements for ancient Minecraft versions and versions not provided by Mojang.
-
- - The `haspaid` parameter is set for the applet wrapper.
- - MultiMC will prefer to use `.minecraft` instead of `minecraft` folder inside the instances now.
- - There is some preliminary support for classic multiplayer - see [this workflowy list](https://workflowy.com/s/2EyDMcp7CU#/1cbfc198cf28) for details.
- - A new `noapplet` trait has been added to allow running legacy Minecraft versions without the applet wrapper.
-
-- Mods without changed metadata (Example Mod) are now listed under their filename instead.
-
-- Tweaker list in metadata now overrides the order of already present tweakers.
-
- This allows running [Vivecraft](http://www.vivecraft.org/). Official support will hopefully follow.
-
-- Instance icons can now be in the SVG format. Also, aspect ratio of SVG icons is now preserved in the instance toolbar.
-
-- GH-1082: It is now possible to disable and enable version components (packages) similarly to mods.
-
-- A new material design / flat icon theme has been added.
-
-- When changing instance component versions, the present version is selected first.
-
-### Bugfixes
-
-- paste.ee upload now works again.
-
- MultiMC now uses its new API. If you used a custom API key before, you will need to generate a new one.
-
-- GH-1873, GH-1873, GH-1875 : The main window can now be closed regardless of running instances and running instances directly will not create a main window.
-
-- GH-1854: MultiMC should no longer crash when the instance is closed while the kill confirmation dialog is open.
-
-- GH-1956: Launch will abort sooner when important files are missing.
-
-- GH-1874: Instance launching and updating MultiMC are now mutually exclusive.
-
- It was possible to do both at the same time, with undefined results.
-
-- GH-1864: imgur album creation now works again.
-
-- GH-1876: Various included libraries have been changed to satisfy their license terms.
-
- Namely:
- - pack200 (GPL with classpath exception, now a shared library)
- - iconfix (LGPL, now a shared library)
- - quazip (LGPL, now a shared library)
- - ColumnResizer (replaced with a BSD-3 version).
-
-- GH-1882: Update dialog will now save its location and size.
-
-- GH-1885: MultiMC will now correctly download zero-byte files.
-
- No content does not equal no file and a presence of a file can mean the difference between something working or not.
-
-- When importing modpacks, file permissions from the pack archive will no longer be preserved.
-
- The archives are sometimes broken and have invalid permissions, especially when coming from sources other than MultiMC.
-
-- Instance export filter has been fixed.
-
- The filtering logic was picking and ignoring incorrect files under some conditions. Also, hidden files were ignored.
-
-- Download progress bars are now less jumpy.
-
- Instead of tracking the total size of all downloads, each download gets a fixed share of the progress bar.
- In many cases, the size of files is unknown before a download starts. The change means that the total progress bar size cannot increase as new downloads start and file sizes are discovered.
-
-- GH-1927: fix crash bugs related to FML library downloads succeeding multiple times.
-
-- Rare problems with error 201 during Mojang authentication have been fixed.
-
-- GH-1971: MultiMC will now no longer check path prefixes when importing instances.
-
- This has caused more issues than it solved. Now it will simply try to move the files instead of giving up early.
-
-- Instance import and creation have been overhauled in general for increased reliability.
-
-- Hardcoded link colors in various dialogs and dialog pages have been fixed and now should follow theme palettes.
-
-- GH-1993: Minimum and maximum JVM memory settings will now get swapped if set the wrong way.
-
- The values self-correct on both settings save and load now.
-
-- GH-2050: Fixed behavior of cancel buttons when browsing for paths.
-
- This affected various settings dialogs and pages, setting the paths to an invalid value when the dialogs were closed with the `Cancel` button.
-
-- The checkboxes in the accounts settings page now have the correct appearance.
-
-- MultiMC responds to account manipulation better.
-
- - Setting and resetting default account will update the account list properly.
- - Removing the active account will now also reset it (previously, it would 'stay around').
- - The accounts model is no longer reset by every action.
-
-- When closing and reopening the instance window, the log settings are preserved.
-
-- In the instance export dialog, the sorting order has been changed to go from `a` to `z`, not backwards.
-
-## MultiMC 0.5.1
-
-### Improvements
-
-- Log uploads now use HTTPS because the [paste.ee](https://paste.ee) site is switching to HTTPS only.
-
-- Console now has the line limit and overflow settings properly set when hidden.
-
- Before, if you didn't have the console set to show up on launch, it would have some low default values set.
- This meant that you wouldn't get enough of the log when the game crashed.
-
-- GH-1802: Log resize is now handled properly.
-
- The log could end up with many empty lines because the wrong maximum size was used during the resize, potentially losing some lines.
-
-- GH-1807: Fixed 'loggging' typo in console overflow notification.
-
-- GH-1801: Launch script is no longer dumped into MultiMC's log on instance launch.
-
-- GH-1065: Use of 'folder' and 'directory' in the UI has been unified to 'folder'.
-
-- GH-1788: A problem with missing translation templates in the setup wizard pages has been fixed.
-
- It should be possible to translate everything again.
-
-- GH-1789: Deletion of custom icon has been fixed.
-
- It wasn't possible to do it from the MultiMC icon selection dialog.
-
-- GH-1790: While using the system theme on macOS, dialogs had wrong colors.
-
- The wrong colors are now only visible immediately after changing the theme to 'System'. An application restart will fix the colors.
-
- The underlying issue cannot be easily fixed.
-
- Upstream bug: https://bugreports.qt.io/browse/QTBUG-58268
-
-- GH-1793: The Java wizard page did not show up as expected when moving MultiMC between different computers.
-
- The page should now show up as expected.
-
-- GH-1794: Copied FTB instances did not work properly.
-
- The instance type of the copy was not set, causing it to not be usable.
-
-## MultiMC 0.5.0
-
-### New or changed features
-
-- GH-338, GH-513, GH-700: Edit instance dialog and Console window have been unified
-
- The resulting instance window can be closed or reopened at any point, it does not matter if the instance is running or not. The list of available pages in the instance window changes with instance state.
-
- Multiple instances can now run from the same MultiMC - It's even more **multi** now.
-
- On launch, the main window is kept open and running instances are marked with a badge. Opening the instance window is no longer the default action. Second activation of a running instance opens the instance window.
-
- MultiMC can be entirely closed, keeping Minecraft instances running. However, if you close MultiMC, play time tracking, logging and crash reporting will not work.
-
- Accounts which are in use are marked as such. If you plan to run multiple instances with multiple accounts, it is advisable to not set a default account to make it ask which one to use on launch.
-
-- It is no longer possible to run multiple copies of MultiMC from a single folder
-
- This generally caused strange configuration and Mojang login issues because the running MultiMC copies did not know about each other.
-
- With the ability to launch multiple instances with different accounts, it is no longer needed.
-
- Trying to run a second copy will focus the existing window. If MultiMC was started without a main window, a new main window will be opened. If the second copy is launching an instance from the command line, it will launch in the first copy instead.
-
- This feature is also used for better checking of correct update completion (GH-1726). It should no longer be possible for MultiMC to end up in a state when it is unable to start - the old version checks that the new one can start and respond to liveness checks by writing a file.
-
-- GH-903: MultiMC now supports theming
-
- By default, it comes with a Dark, Bright, System (the old default) and Custom theme.
-
- The Custom theme can change all of the colors, change the Qt widget theme and style the whole UI with CSS rules.
- Files you can customize are created in `themes/custom/`. The CSS theming is similar to what TeamSpeak uses.
-
- Ultimately, this is a start, not a final solution. If you are interested in making custom themes and would like to shape the direction this takes in the future, ask on Discord. :)
-
-- Translations have been overhauled
-
- You no longer need to restart MultiMC to change its active translation. MultiMC also asks which translation to use on the first start.
-
- There is a lot that has to be done with translations, but at least now it should be easier to work with them and use them.
-
-- MultiMC now includes Google Analytics
-
- The purpose of this is to determine where to focus future effort. Generally, only basic technical information is collected:
-
- - OS name, version, and architecture
- - Java version, architecture and memory settings
- - MultiMC version
- - System RAM size
-
- It does not activate until you agree with it. It may be expanded upon later, in which case you will be asked to agree again.
-
-- Java selection on start has been replaced with a more robust solution
-
- You can select from the list as before, but also provide your own Java and set the basic memory sizes - Heap and PermGen (for java < 8).
-
- It is checking the configuration and selected Java on the fly and provides more or less instant feedback.
-
-- Java detection has been improved
-
- MultiMC will prefer looking for `javaw.exe` on Windows and now can scan most, if not all the usual Linux java paths.
-
-- Java memory settings now allow running with less memory
-
- The minimum has been changed to 128 MB.
-
-- There is now an initial setup wizard
-
- So far, it is used for selecting the translation to use, the analytics agreement and initial Java setup.
-
-- Existing MCEdit integration has been replaced by the Worlds page in the Instance/Console window
-
- It supports renaming, copying, and deleting worlds, opening them in MCEdit and copying the world seed without the need to launch Minecraft.
-
- The Linux version of MCEdit is now also started from the shell script, fixing some compatibility issues.
-
-- GH-767: Minecraft skin upload
-
- The `Upload Skin` button is located on the Accounts page.
-
-- It is now possible to turn off line wrapping in the Minecraft log
-- Groups now have a proper context menu
-
- You can delete groups and create instances in them using the context menu. Just right click anywhere inside a group that's not an instance.
-
-- Exporting of tracked FTB instances has been disabled
-
- It did not produce viable instances.
-
-- Added support for Liteloader snapshots
-
- Requested many times, it's finally available.
-
-- GH-1635, GH-1273, GH-589, GH-842, GH-901, GH-1117: Mod lists have been improved heavily
-
- - There is filter bar to allow finding mods in large packs quickly.
- - Extended selection is allowed (does not have to be continuous).
- - You can enable and disable many mods at the same time.
- - Sorting by clicking on the column headers is now possible.
- - Mod lists have a column for when a mod was changed last time (or added using the mod list).
- - You can open the `config` folder from the mods list now.
-
-- GH-352: It is now possible to cancel an instance update.
-
-- Instance launch button now has a drop-down arrow instead of click and hold.
-
- This should make launching with profilers more discoverable.
-
-- When instances do not exit properly (crash), they get a badge
-
- This should make it easier to spot what crashed if you have multiple running.
-
-- Instances can now contain libraries
-
- Any libraries stored in `$instanceroot/libraries/` will override the libraries from MultiMC's global folders, as long as they are marked `local` in the JSON patch.
-
- This should make installing library-based mods easier in the future, and allow to include them in modpacks.
-
-### Improvements
-
-- GH-1433: The account selection dialog no longer shows e-mail addresses when no default account is selected.
-
- Instead, it shows Minecraft profile names.
-
-- GH-1643: The preferred language property is no longer being censored in logs.
-
- Because the values are often very short (`en` for example), it was simply not usable.
-
-- GH-1521: JSON editor now works when customized.
-
-- GH-1560: Leading whitespace is now removed from instance names on creation and renaming
-
- Leading and trailing spaces in names can confuse Windows Explorer and Java.
-
-- GH-1586: MultiMC now prints to command line on Windows, so you can review the command line options.
-
-- GH-1699: Linux builds no longer contain the XCB library
-
- This caused many compatibility issues on with certain Linux graphics drivers and prevented MultiMC from starting.
-
-- GH-1731: it was possible for the Screenshots page to show a list of all system drives.
-
- Trying to delete said system drives obviously lead to data loss. Additional checks have been added to prevent this from happening.
-
-- GH-1670: "Instance update failed because: Too soon! Let the LWJGL list load :)." has been fixed.
-
- This fixes launching of legacy (and legacy FTB) instances.
-
-- GH-1778: Jar modded Minecraft.jar location breaks mod assumptions
-
- Some ancient mods require the modded `Minecraft.jar` to be in `.minecraft/bin`, inside the instance. Now it is placed there.
-
-### Internals
-
-- Full support for the current Mojang downloads JSON format.
-
- This includes checksum verification, when available.
-
-- Minecraft logging has been overhauled
-
- The log now persists after the instance/console window is closed.
-
-- GH-575: Mod lists got a refactor
-
- The original issue is about adding sub-folder listings to mod lists. However, this is simply a refactor that separates the old Jar mod list from the less complex Loader mods. It allowed all of the mod list improvements to happen.
-
-- The network code has been heavily reworked
-
- Most issues related to slow networks and failing downloads should be a thing of the past.
- This also includes post-download validation of the download - like using SHA1 checksums.
-
-- Minecraft launching has been reworked
-
- It is now a lot of tiny reusable tasks that chain together.
-
- MultiMC now also has a separate launch method that works more like the Mojang launcher (not using a launcher part, but running Java directly).
-
-## MultiMC 0.4.11
-
-This release contains mainly a workaround for Minecraft 1.9 support and returned support for OSX 10.7.
-
-### **IMPORTANT**
-
-- GH-1410: MultiMC crashes on launch on OSX 10.7
-
- MultiMC didn't work on OSX 10.7 because of an oversight in the build server setup. This has been fixed.
-
-- GH-1453: Minecraft 1.9 snapshots didn't download and launch properly
-
- This has been caused by a change on Mojang servers - the data is now stored in a different location and the files describing the releases have a different format. The required changes on MultiMC side aren't complete yet, but it's enough to get snapshots working.
-
- Full support for the new version file format will come in the next release.
-
-- MultiMC version file format was simplified
-
- Some undocumented and unused features were removed from the format. Mostly version patches that removed libraries, advanced library application, and merging rules, and things of similar nature. If you used them, you used an undocumented feature that is impossible to reach from the UI.
-
-### Improvements
-
-- GH-1502: When the locally cached Minecraft version was deleted, the instance that needed it would have to be started twice
-
- This was caused by generating the list of launch instructions before the update. It is now fixed.
-
-- Version file issues are now reported in the instance's `Version` page.
-
- This doesn't apply to every possible issue yet and will be expanded upon in the next release.
-
-## MultiMC 0.4.10
-
-Second hotfix for issues with wifi connections.
-
-### **IMPORTANT**
-
-- GH-1422: Huge ping spikes while using MultiMC
-
- Another day, another fix. The bearer plugins added in 0.4.9 didn't really help and we ran into more bugs.
-
- This time, the presence of the network bearer plugins caused a lot of network lag for people on wifi connections.
-
- Because this wasn't a problem on the previous version of Qt MultiMC used (5.4.2), I ended up reverting to that. This is a temporary solution until the Qt framework can be rebuilt and retested for every platform without this broken feature.
-
- The upstream bug is [QTBUG-40332](https://bugreports.qt.io/browse/QTBUG-40332) and despite being closed, it is far from fixed.
-
-Because of the reverted Qt version, OSX 10.7 *might* work again. If it does, please do tell, it would help with figuring out what went wrong there :)
-
-
-## MultiMC 0.4.9
-
-Hotfix for issues with wifi connections.
-
-### **IMPORTANT**
-
-- GH-1408: MultiMC 0.4.8 doesn't work on wireless connections.
-
- This is especially the case on Windows. If you already updated to 0.4.8, you will need to do a manual update, or use a wired connection to do the update.
-
- The issue was caused by a change in the underlying framework (Qt), and MultiMC not including the network bearer plugins. This made it think that the connection is always down and not try to contact any servers because of that.
-
- The upstream bug is [QTBUG-49267](https://bugreports.qt.io/browse/QTBUG-49267).
-
-- GH-1410: MultiMC crashes on launch on OS X 10.7.5
-
- OSX 10.7.x is no longer supported by Apple and I do not have a system to test and fix this.
-
- So, this is likely **NOT** going to be fixed - please update your OS if you are still running 10.7.
-
-### Improvements
-
-- GH-1362: When uploading or copying the Minecraft log, the action is logged, including a full timestamp.
-
-## MultiMC 0.4.8
-
-Fluffy and functional!
-
-### **IMPORTANT**
-
-- GH-1402: MultiMC will keep its binary filename after an update if you rename it.
-
- Note that this doesn't happen with this (0.4.8) update yet, because the old update method is still used.
-
- If you renamed `MultiMC.exe` for any reason, you will have to manually remove the renamed file after the update and rename the new `MultiMC.exe`.
-
- Future updates should no longer have this issue.
-
-
-### New features
-
-- GH-1047, GH-1233: MultiMC now includes basic Minecraft world management.
-
- This is a new page in the console/edit instance window.
-
- You can:
- - Copy worlds
- - Delete worlds
- - Copy the world seed value
- - Run MCEdit - the MCEdit feature has been moved here.
-
-- GH-1217: MultiMC now tracks instance play time and displays it when the instance is selected.
-
-- New buttons on the top toolbar:
- - GH-1238: button for the [MultiMC subreddit](https://www.reddit.com/r/MultiMC/).
- - GH-1397: button for joining the [MultiMC discord voice/chat server](https://discord.gg/0k2zsXGNHs0fE4Wm).
-
- Both are there for you to interact with other MultiMC users and us.
-
-- GH-253, GH-1300: MultiMC can now be started with the `-l "Instance ID"` parameter, launching the specified instance directly.
-
-### Improvements
-
-- Instance list
- - GH-1121: Instances are now selected after you create them.
- - GH-93: When copying an instance, you can tell MultiMC to not copy the worlds.
-
-- Mod and resource pack lists
- - GH-1237: Mod info is now clickable and selectable.
- - GH-1322: Mod description `...` link will no longer pop up multiple dialogs.
- - GH-1178: When dragged and dropped, folder based mods and resource packs will be copied properly on OSX.
-
-- MCEdit integration:
- - GH-1009: MCEdit Unified on linux is now recognized properly.
-
-- Mojang login and accounts:
- - GH-1158: A unique ID is generated on the MultiMC side before login, instead of letting the server decide.
- - When a password is required, the user login is partially obscured.
- - The dropdown menu on the main window now lists profiles, not accounts.
-
-- Modpacks:
- - GH-1140: Modpack downloads now check for update on the server even if the file is already locally available.
- - GH-1148: When creating an instance from modpack, the instance name will be guessed based on the modpack file or URL (unless you set it yourself).
- - GH-1280: While importing modpacks, the progress dialog now says what is happening.
- - When selecting the modpack field in the new instance dialog, the contents are selected for easy replacement.
-
-- Instance settings
- - Wrapper commands now use the proper UI field and do not get replaced with pre-launch commands.
-
-- Minecraft launching:
- - GH-1053, GH-1338: Minecraft launching has been completely redone.
- - GH-1275: Server resource pack folder is created on launch.
- - This is a workaround for Minecraft bug MCL-3732.
- - GH-1320: Improve compatibility with non-Oracle Java.
- - GH-1355: LAUNCHER environment will no longer leak into Minecraft after MultiMC updates itself.
-
-- Minecraft log:
- - Java exception detection in Minecraft logs has been improved.
- - GH-719: You can now use your own [paste.ee](https://paste.ee/) account for uploading logs.
- - New [paste.ee](https://paste.ee/) settings page has been added to the global settings dialog.
- - GH-1197: Text colors in log window now adapt to the background color.
- - GH-1164: The censor filter could be initialized with empty values, leading to unreadable log.
- - GH-1008, GH-1046, GH-1067: Log size limiting.
-
- The log window now has a configurable limit for the number of lines remembered. You can also specify whether it stops logging or forgets on the fly once the limit is breached.
-
- This prevents the MultiMC log window from using too much memory on logging. The default limit is 100000 lines and the logging stops.
-
- Minecraft logging this much is a sign of a problem that needs to be fixed. Any complaints should be addressed to the responsible mod authors.
-
-- Screenshot upload
- - GH-1339: While uploading screenshots, the console window will not close (prevents a crash).
-
-- Other logs:
- - GH-926: 'Other Logs' now has a button for removing all log files.
- - Hidden log files are shown in 'Other logs'.
-
-- User skins:
- - MultiMC now uses [crafatar.com](https://crafatar.com/) for skin downloads instead of the Mojang servers.
-
-- Java:
- - GH-1365: MultiMC now supports Java 9 and its new version numbering.
- - GH-1262: Java can now be placed in a folder relative to MultiMC's folder. This allows bundling of JREs with MultiMC.
-
-- Translations:
- - GH-1313: Some parts of the MultiMC user interface have been marked as 'not for translation'.
-
-### Internals and internal bug fixes
-
-- GH-1052: All the dependencies were rebuilt and the build environment upgraded to the latest compiler versions.
-- GH-1051: The CDPATH environment variable is now ignored.
-- GH-77, GH-1059, GH-1060: The MultiMC updater is no longer used or necessary.
-
- It is only present to preserve compatibility with previous versions.
- Updates now work properly on Windows systems when you have Unicode (like ❄, Ǣ or Ω) characters in the path.
-
-- GH-1069, GH-1100: `LD_LIBRARY_PATH` and `LD_PRELOAD` environment variables supplied to MultiMC now don't affect MultiMC but affect the launched game.
-
- This means you can use something like the Steam overlay in MultiMC instances on Linux.
- If you need to use these variables for MultiMC itself, you can use `LAUNCHER_LIBRARY_PATH` and `LAUNCHER_PRELOAD` instead.
-
-- GH-1389: External processes (like folder views, editors, etc.) are now started in a way that prevents the MultiMC environment to be reused by them.
-- GH-1355: If `LD_LIBRARY_PATH` contains any of MultiMC's internal folders, this will not propagate to started processes.
-- GH-1231, GH-1378: libpng is now included with the Linux version of MultiMC
-- GH-1202: SSL certificates are now rebuilt on start on OSX.
-
-- GH-1303: Translations and notification cache are stored in the normal data folder now, not alongside the binaries. This only affects third party Linux packaging.
-- GH-1266, GH-1301: Linux runner scripts has been improved.
-- GH-1360: Development and other unstable versions of MultiMC now uses GitHub commits instead of this manually maintained changelog.
-
-## MultiMC 0.4.7
-
-This is what 0.4.6 should have been. Oh well, at least it's here now!
-
-### Functional changes
-- GH-974: A copy of the libstdc++ library is now included in Linux releases, improving compatibility
-- GH-985: Jar mods are now movable and removable after adding
-- GH-983: Use `minecraft.jar` as the main jar when using jar mods - fixes NEI in Legacy Minecraft versions
-- GH-977: Fix FTB paths on Windows
-
- This removes some very old compatibility code. If you get any issues, make sure you run the FTB Launcher and let it update its files.
-- GH-992 and GH-1003: Improved performance when saving settings:
- - Bad performance was caused by improved data consistency
- - Each config file is now saved only once, not once for every setting
- - When loading FTB instances, there are no writes to config files anymore
-- GH-991: Implemented wrapper command functionality:
-
- There is an extra field in the MultiMC Java settings that allows running Java inside a wrapper program or script. This means you can run Minecraft with wrappers like `optirun` and get better performance with hybrid graphics on Linux without workarounds.
-- GH-997: Fixed saving of multi-line settings. This fixes notes.
-- GH-967: It is now possible to add patches (Forge and LiteLoader) to tracked FTB instances properly.
-
- Libraries added by the patches will be taken from MultiMC's `libraries` folder, while the tracked patches will use FTB's folders.
-
-- GH-1011 and GH-1015: Fixed various issues when the patch versions aren't complete
-
- This applies when Minecraft versions are missing or when patches are broken and the profile is manipulated by adding, moving, removing, customizing and reverting patches.
-
-- GH-1021: Built in legacy Minecraft versions aren't customizable anymore
-
- The internal format for Legacy Minecraft versions does not translate to the external patch format and would cause crashes
-- GH-1016: MultiMC prints a list of mods, core mods (contents of the core mods folder) and jar mods to the log on instance start. This should help with troubleshooting.
-- GH-1031: Icons are exported and imported along with instances
-
- This only applies if the icon was custom (not built-in) when exporting and the user doesn't choose an icon while importing the pack.
-
-### UI changes
-- GH-970: Fixed help button for the External tools and Accounts dialog pages not linking to the proper wiki places
- - Same for the Versions dialog page
-
-- GH-994: Rearranged the buttons on the Versions page to make jar mods less prominent
-
- Using the `Add jar mods` button will also show a nag dialog until it's been used successfully
-
-## MultiMC 0.4.6
-
-Long time coming, this release brought a lot of incremental improvements and fixes.
-
-### Functional changes
-- Old version.json and custom.json version files will be transformed into a Minecraft version patch:
- - The process is automated
- - LWJGL entries are stripped from the original file - you may have to re-do LWJGL version customizations
- - Old files will be renamed - .old extension is added
-- It's now possible to:
- - Customize, edit and revert built in version patches (Minecraft, LWJGL)
- - Edit custom version patches (Forge, LiteLoader, other)
-- Blocked various environment variables from affecting Minecraft:
- - `JAVA_ARGS`
- - `CLASSPATH`
- - `CONFIGPATH`
- - `JAVA_HOME`
- - `JRE_HOME`
- - `_JAVA_OPTIONS`
- - `JAVA_OPTIONS`
- - `JAVA_TOOL_OPTIONS`
- - If you rely on those in any way, now would be a time to fix that
-- Improved handling of LWJGL on OSX (.dylib vs. .jnilib extensions)
-- Jar mods are now always put into a generated temporary Minecraft jar instead of being put on the classpath
-- PermGen settings:
- - Changed default PermGen value to 128M because of many issues from new users
- - MultiMC now recognizes the Java version used and will not add PermGen settings to Java >= 1.8
-- Implemented simple modpack import and export feature:
- - Export allows selecting which files go into the resulting zip archive
- - Only MultiMC instances for now, other pack formats are planned
- - Import is either from local file or URL, URL can't have ad/click/pay gates
-- Instance copy doesn't follow symlinks on Linux anymore
- - Still does on Windows because copying symlinks requires Administrator level access
-- Instance delete doesn't follow symlinks anymore - anywhere
-- MCEdit tool now recognizes MCEdit2.exe as a valid file to runtime
-- Log uploads now follow the maximum allowed paste sizes of paste.ee and are encoded properly
-- MultiMC now doesn't use a proxy by default
-- Running profilers now works on Windows
-- MultiMC will warn you if you run it from WinRAR or temporary folders
-- Minecraft process ID is printed in the log on start
-- SSL certificates are fixed on OSX 10.10.3 and newer - see [explanation](http://www.infoworld.com/article/2911209/mac-os-x/yosemite-10103-breaks-some-applications-and-https-sites.html).
-
-### UI changes
-- Version lists:
- - All version lists now include latest and recommended versions - recommended are pre-selected
- - Java version list now sorts versions based on suitability - best on top
- - Forge version list includes the development branch the version came from
- - Minecraft list marks the latest release as 'recommended' and latest snapshot as 'latest' if it is newer than the release
-- Mod lists:
- - Are updated and sorted after adding mods
- - Browse buttons now properly open the central mods folder
- - Are no longer watching for updates when the user doesn't look at them
- - Loader mod list now recognizes .litemod files as valid mod files
-- Improved wording of instance delete dialog
-- Icon themes:
- - Can be changed without restarting
- - Added a workaround for icon themes broken in KDE Plasma 5 (only relevant for custom builds)
-- Status icons:
- - Included a 'yellow' one
- - Are clickable and link to [help.mojang.com](https://help.mojang.com/)
- - Refresh when the icon theme does
-- Changed default console font to Courier 10pt on Windows
-- Description text in the main window status bar now updates when Minecraft version is changed
-- Inserted blatant self-promotion (Only Minecraft 1.8 and up)
- - This adds a bit of unobtrusive flavor text to the Minecraft F3 screen
-- Log page now has a button to scroll to bottom
-- Errors are reported while updating the instance on the Version page
-- Fixed typos (forge -> Forge)
-
-### Internals
-- Massive internal restructuring (ongoing)
-- Downloads now follow redirects
-- Minecraft window size is now always at least 1x1 pixel (prevents crash from bad settings)
-- Better handling of Forge downloads (obviously invalid/broken files are redownloaded)
-- All download tasks now only start 6 downloads, using a queue (fixes issues with assets downloads)
-- Fixed bugs related to corrupted settings files (settings and patch order file saves are now atomic)
-- Updated zip manipulation library - files inside newly written zip/jar files should have proper access rights and timestamps
-- Made Minecraft resource downloads more resilient (throwing away invalid/broken index files)
-- Minecraft asset import from old format has been removed
-- Generally improved MultiMC logging:
- - More error logging for network tasks
- - Added timestamps relative to application start
-- Fixed issue with the application getting stuck in a modal dialog when screenshot uploads fail
-- Instance profiles and patches are now loaded lazily (speeds up MultiMC start)
-- Groups are saved after copying an instance
-- MultiMC launcher part will now exit cleanly when MultiMC crashes or is closed during instance launch
-
-
-## MultiMC 0.4.5
-- Copies of FTB instances should work again (GH-619)
-- Fixed OSX version not including the hotfix number
-- If the currently used java version goes missing, it now triggers auto-detect (GH-608)
-- Improved 'refresh' and 'update check' icons of the dark and bright simple icon themes (GH-618)
-- Fixed console window hiding - it no longer results in windowless/unusable MultiMC
-
-## MultiMC 0.4.4
-- Other logs larger than 10MB will not load to prevent logs eating the whole available memory
-- Translations are now updated independently from MultiMC
-- Added new and reworked the old simple icon themes
-- LWJGL on OSX should no longer clash with Java 8
-- Update to newer Qt version
- - Look and feel updated for latest OSX
-- Fixed issues caused by Minecraft inheriting the environment variables from MultiMC
-- Minecraft log improvements:
- - Implemented search and pause
- - Automated coloring is updated for log format used by Minecraft 1.7+
- - Added settings for the font used in the console, using sensible defaults for the OS
-- Removed MultiMC crash handler, it will be replaced by a better one in the future
-
-## MultiMC 0.4.3
-- Fix for issues with Minecraft version file updates
-- Fix for console window related memory leak
-- Fix for travis.ci build
-
-## MultiMC 0.4.2
-- Show a warning in the log if a library is missing
-- Fixes for relocating instances to other MultiMC installs:
- - Libraries now use full Gradle dependency specifiers
- - Rework of forge installer (forge can reinstall itself using only the information already in the instance)
- - Fixed bugs in rarely used library insertion rules
-- Make the global settings dialog into a page dialog
-- Check if the Java binary can be found before launch
-- Show a warning for paths containing a '!' (Java can't handle that properly)
-- Many smaller fixes
-
-## MultiMC 0.4.1
-- Fix LWJGL version list (SourceForge has changed the download API)
-
-## MultiMC 0.4.0
-- Jar support in 1.6+
-- Deprecated legacy instances
- - Legacy instances can still be used but not created
- - All Minecraft versions are supported in the new instance format
-- All instance editing and settings dialogs were turned into pages
- - The edit instance dialog contains pages relevant to editing and settings
- - The console window contains pages useful when playing the game
-- Redone the screenshot management and upload (page)
-- Added a way to display and manage log files and crash reports generated by Minecraft (page)
-- Added measures to prevent corruption of version files
- - Minecraft version files are no longer part of the instances by default
-- Added help for the newly added dialog pages
-- Made logs uploaded to paste.ee expire after a month
-- Fixed a few bugs related to liteloader and forge (1.7.10 issues)
-- Icon themes. Two new themes were added (work in progress)
-- Changelog and update channel are now visible in the update dialog
-- Several performance improvements to the group view
-- Added keyboard navigation to the group view
-
-## MultiMC 0.3.9
-- Workaround for 1.7.10 Forge
-
-## MultiMC 0.3.8
-- Workaround for performance issues with Intel integrated graphics chips
-
-## MultiMC 0.3.7
-- Fixed forge for 1.7.10-pre4 (and any future prereleases)
-
-## MultiMC 0.3.6
-- New server status - now with more color
-- Fix for FTB tracking issues
-- Fix for translations on OSX not working
-- Screenshot dialog should be harder to lose track of when used from the console window
-- A crash handler implementation has been added.
-
-## MultiMC 0.3.5
-- More versions are now selectable when changing instance versions
-- Fix for Forge/FML changing its mcmod.info metadata format
-
-## MultiMC 0.3.4
-- Show a list of Patreon patrons in credits section of the about dialog
-- Make the console window raise itself after Minecraft closes
-- Add Control/Command+q shortcut to quit from the main window
-- Add french translation
-- Download and cache FML libs for legacy versions
-- Update the OS X icon
-- Fix FTB libraries not being used properly
-
-## MultiMC 0.3.3
-- Tweak context menu to prevent accidental clicks
-- Fix adding icons to custom icon directories
-- Added a Patreon button to the toolbar
-- Minecraft authentication tasks now provide better error reports
-
-## MultiMC 0.3.2
-- Fix issues with libraries not getting replaced properly (fixes instance startup for new instances)
-- Fix April fools
-
-## MultiMC 0.3.1
-- Fix copying of FTB instances (instance type is changed properly now)
-- Customizing FTB pack versions will remove the FTB pack patch file
-
-## MultiMC 0.3
-- Improved instance view
-- Overhauled 1.6+ version loading
-- Added a patch system for instance modification
- - There is no longer a single `custom.json` file that overrides `version.json`
- - Instead, there are now "patch" files in `<instance>/patches/`, one for each main tweaker (forge, liteloader etc.)
- - These patches are applied after `version.json` in a customizable order,
- - A list of these files is shown in the leftmost tab in the Edit Mods dialog, where a list of libraries was shown before.
- - `custom.json` can still be used for overriding everything.
-- Offline mode can be used even when online
-- Show an "empty" message in version selector dialogs
-- Fix FTB paths on windows
-- Tooling support
- - JProfiler
- - JVisualVM
- - MCEdit
-- Don't assume forge in FTB instances and allow other libraries (liteloader, mc patcher, etc.) in FTB instances
-- Screenshot uploading/managing
-- Instance badges
-- Some pre/post command stuff (remove the timeout, variable substitution)
-- Fix logging when the system language is not en_US
-- Setting PermGen to 64 will now omit the java parameter because it is the default
-- Fix encoding of escape sequences (tabs and newlines) in config files
-
-## MultiMC 0.2.1
-- Hotfix - move the native library extraction into the onesix launcher part.
-
-## MultiMC 0.2
-- Java memory settings have MB added to the number to make the units obvious.
-- Complete rework of the launcher part. No more sensitive information in the process arguments.
-- Cached downloads now do not destroy files on failure.
-- Mojang service status is now on the MultiMC status bar.
-- Java checker is no longer needed/used on instance launch.
-- Support for private FTB packs.
-- Fixed instance ID issues related to copying FTB packs without changing the instance name.
-- Forge versions are better sorted (build numbers above 999 were sorted wrong).
-- Fixed crash related to the MultiMC update channel picker in offline mode.
-- Started using icon themes for the application icons, fixing many OSX graphical glitches.
-- Icon sources have been located, along with icon licenses.
-- Update to the German translation.
-
-## MultiMC 0.1.1
-- Hotfix - Changed the issue tracker URL to [GitHub issues](https://github.com/MultiMC/Launcher/issues).
-
-## MultiMC 0.1
-- Reworked the version numbering system to support our [new Git workflow](http://nvie.com/posts/a-successful-git-branching-model/).
-- Added a tray icon for the console window.
-- Fixed instances getting deselected after FTB instances are loaded (or whenever the model is reset).
-- Implemented proxy settings.
-- Fixed sorting of Java installations in the Java list.
-- Jar files are now distributed separately, rather than being extracted from the binary at runtime.
-- Added additional information to the about dialog.
-
-## MultiMC 0.0
-- Initial release.
diff --git a/cmake/UnitTest/test.rc b/cmake/UnitTest/test.rc
index 9fe4147e..6c0f0641 100644
--- a/cmake/UnitTest/test.rc
+++ b/cmake/UnitTest/test.rc
@@ -14,7 +14,7 @@ BEGIN
BEGIN
BLOCK "000004b0"
BEGIN
- VALUE "CompanyName", "MultiMC Contributors"
+ VALUE "CompanyName", "MultiMC & PolyMC Contributors"
VALUE "FileDescription", "Testcase"
VALUE "FileVersion", "1.0.0.0"
VALUE "ProductName", "Launcher Testcase"
diff --git a/default.nix b/default.nix
new file mode 100644
index 00000000..5abfc1bd
--- /dev/null
+++ b/default.nix
@@ -0,0 +1 @@
+(import packages/nix/flake-compat.nix).defaultNix
diff --git a/doc/multimc.1.txt b/doc/polymc.1.txt
index da65af2e..9ba34662 100644
--- a/doc/multimc.1.txt
+++ b/doc/polymc.1.txt
@@ -1,35 +1,35 @@
-MULTIMC(1)
+POLYMC(1)
==========
:doctype: manpage
NAME
----
-multimc - a launcher and instance manager for Minecraft.
+polymc - a launcher and instance manager for Minecraft.
SYNOPSIS
--------
-*multimc* ['OPTIONS']
+*polymc* ['OPTIONS']
DESCRIPTION
-----------
-MultiMC is a custom launcher for Minecraft that allows you to easily manage
+PolyMC is a custom launcher for Minecraft that allows you to easily manage
multiple installations of Minecraft at once. It also allows you to easily
install and remove mods by simply dragging and dropping.
-Here are the current features of MultiMC.
+Here are the current features of PolyMC.
OPTIONS
-------
*-d, --dir*='DIRECTORY'::
- Use 'DIRECTORY' as the MultiMC root.
+ Use 'DIRECTORY' as the PolyMC root.
*-l, --launch*='INSTANCE_ID'::
Launch the instance specified by 'INSTANCE_ID'.
*--alive*::
- Write a small 'live.check' file after MultiMC starts.
+ Write a small 'live.check' file after PolyMC starts.
*-h, --help*::
Display help text and exit.
@@ -49,16 +49,18 @@ EXIT STATUS
BUGS
----
-<https://github.com/MultiMC/Launcher/issues>
+<https://github.com/PolyMC/PolyMC/issues>
RESOURCES
---------
-GitHub: <https://github.com/MultiMC/Launcher>
+GitHub: <https://github.com/PolyMC/PolyMC>
-Main website: <https://multimc.org>
+Main website: <https://polymc.org>
AUTHORS
-------
peterix <peterix@gmail.com>
+swurl <swurl@swurl.xyz>
+
// vim: syntax=asciidoc
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 00000000..f2205416
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,94 @@
+{
+ "nodes": {
+ "flake-compat": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1641205782,
+ "narHash": "sha256-4jY7RCWUoZ9cKD8co0/4tFARpWB+57+r1bLLvXNJliY=",
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "rev": "b7547d3eed6f32d06102ead8991ec52ab0a4f1a7",
+ "type": "github"
+ },
+ "original": {
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "type": "github"
+ }
+ },
+ "flake-utils": {
+ "locked": {
+ "lastModified": 1642700792,
+ "narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "846b2ae0fc4cc943637d3d1def4454213e203cba",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "libnbtplusplus": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1591558203,
+ "narHash": "sha256-QgvNvaoFflCXEPCCFBCeZvYTpuiwScBG7EosUgFwFNQ=",
+ "owner": "multimc",
+ "repo": "libnbtplusplus",
+ "rev": "dc72a20b7efd304d12af2025223fad07b4b78464",
+ "type": "github"
+ },
+ "original": {
+ "owner": "multimc",
+ "repo": "libnbtplusplus",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1643169865,
+ "narHash": "sha256-+KIpNRazbc8Gac9jdWCKQkFv9bjceaLaLhlwqUEYu8c=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "945ec499041db73043f745fad3b2a3a01e826081",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "quazip": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1643049383,
+ "narHash": "sha256-LcJY6yd6GyeL7X5MP4L94diceM1TYespWByliBsjK98=",
+ "owner": "stachenov",
+ "repo": "quazip",
+ "rev": "09ec1d10c6d627f895109b21728dda000cbfa7d1",
+ "type": "github"
+ },
+ "original": {
+ "owner": "stachenov",
+ "repo": "quazip",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "flake-compat": "flake-compat",
+ "flake-utils": "flake-utils",
+ "libnbtplusplus": "libnbtplusplus",
+ "nixpkgs": "nixpkgs",
+ "quazip": "quazip"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 00000000..d809066a
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,42 @@
+{
+ description = "PolyMC flake";
+ inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
+ inputs.flake-utils.url = "github:numtide/flake-utils";
+ inputs.flake-compat = {
+ url = "github:edolstra/flake-compat";
+ flake = false;
+ };
+ inputs.libnbtplusplus = {
+ url = "github:multimc/libnbtplusplus";
+ flake = false;
+ };
+ inputs.quazip = {
+ url = "github:stachenov/quazip";
+ flake = false;
+ };
+
+ outputs = args@{ self, nixpkgs, flake-utils, libnbtplusplus, quazip, ... }:
+ {
+ overlay = final: prev: {
+ inherit (self.packages.${final.system}) polymc;
+ };
+ } // flake-utils.lib.eachDefaultSystem (system:
+ let pkgs = import nixpkgs { inherit system; };
+ in {
+ packages = {
+ polymc = pkgs.libsForQt5.callPackage ./packages/nix/polymc {
+ inherit self;
+ submoduleQuazip = quazip;
+ submoduleNbt = libnbtplusplus;
+ };
+ };
+ apps = {
+ polymc = flake-utils.lib.mkApp {
+ name = "polymc";
+ drv = self.packages.${system}.polymc;
+ };
+ };
+ defaultPackage = self.packages.${system}.polymc;
+ defaultApp = self.apps.${system}.polymc;
+ });
+}
diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index 958c5e3d..a3d6216e 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -14,7 +14,7 @@
#include "ui/pages/global/ProxyPage.h"
#include "ui/pages/global/ExternalToolsPage.h"
#include "ui/pages/global/AccountListPage.h"
-#include "ui/pages/global/PasteEEPage.h"
+#include "ui/pages/global/APIPage.h"
#include "ui/pages/global/CustomCommandsPage.h"
#include "ui/themes/ITheme.h"
@@ -26,7 +26,6 @@
#include "ui/setupwizard/SetupWizard.h"
#include "ui/setupwizard/LanguageWizardPage.h"
#include "ui/setupwizard/JavaWizardPage.h"
-#include "ui/setupwizard/AnalyticsWizardPage.h"
#include "ui/dialogs/CustomMessageBox.h"
@@ -73,11 +72,8 @@
#include <DesktopServices.h>
#include <LocalPeer.h>
-#include <ganalytics.h>
#include <sys.h>
-#include <Secrets.h>
-
#if defined Q_OS_WIN32
#ifndef WIN32_LEAN_AND_MEAN
@@ -191,7 +187,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
setApplicationName(BuildConfig.LAUNCHER_NAME);
setApplicationDisplayName(BuildConfig.LAUNCHER_DISPLAYNAME);
setApplicationVersion(BuildConfig.printableVersionString());
-
+ #if (QT_VERSION >= QT_VERSION_CHECK(5,7,0))
+ setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME);
+ #endif
startTime = QDateTime::currentDateTime();
#ifdef Q_OS_LINUX
@@ -308,8 +306,14 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
}
else
{
-#if defined(Q_OS_MAC)
- QDir foo(FS::PathCombine(applicationDirPath(), "../../Data"));
+#ifdef LAUNCHER_LINUX_DATADIR
+ QString xdgDataHome = QFile::decodeName(qgetenv("XDG_DATA_HOME"));
+ if (xdgDataHome.isEmpty())
+ xdgDataHome = QDir::homePath() + QLatin1String("/.local/share");
+ dataPath = xdgDataHome + "/polymc";
+ adjustedBy += "XDG standard " + dataPath;
+#elif defined(Q_OS_MAC)
+ QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), ".."));
dataPath = foo.absolutePath();
adjustedBy += "Fallback to special Mac location " + dataPath;
#else
@@ -433,7 +437,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
#endif
/*
- * Establish the mechanism for communication with an already running MultiMC that uses the same data path.
+ * Establish the mechanism for communication with an already running PolyMC that uses the same data path.
* If there is one, tell it what the user actually wanted to do and exit.
* We want to initialize this before logging to avoid messing with the log of a potential already running copy.
*/
@@ -527,10 +531,12 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
#elif defined(Q_OS_WIN32)
m_rootPath = binPath;
#elif defined(Q_OS_MAC)
- QDir foo(FS::PathCombine(binPath, "../.."));
+ QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), ".."));
m_rootPath = foo.absolutePath();
- // on macOS, touch the root to force Finder to reload the .app metadata (and fix any icon change issues)
- FS::updateTimestamp(m_rootPath);
+#endif
+
+#ifdef MULTIMC_JARS_LOCATION
+ m_jarsPath = TOSTRING(MULTIMC_JARS_LOCATION);
#endif
qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT;
@@ -589,7 +595,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->registerSetting("AutoUpdate", true);
// Theming
- m_settings->registerSetting("IconTheme", QString("multimc"));
+ m_settings->registerSetting("IconTheme", QString("pe_colored"));
m_settings->registerSetting("ApplicationTheme", QString("system"));
// Notifications
@@ -656,7 +662,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
// Memory
m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512);
- m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, 1024);
+ m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, 4096);
m_settings->registerSetting("PermGen", 128);
// Java Settings
@@ -708,16 +714,13 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->registerSetting("UpdateDialogGeometry", "");
- // paste.ee API key
- m_settings->registerSetting("PasteEEAPIKey", "multimc");
+ // pastebin URL
+ m_settings->registerSetting("PastebinURL", "https://0x0.st");
- if(!BuildConfig.ANALYTICS_ID.isEmpty())
- {
- // Analytics
- m_settings->registerSetting("Analytics", true);
- m_settings->registerSetting("AnalyticsSeen", 0);
- m_settings->registerSetting("AnalyticsClientID", QString());
- }
+ m_settings->registerSetting("CloseAfterLaunch", false);
+
+ // Custom MSA credentials
+ m_settings->registerSetting("MSAClientIDOverride", "");
// Init page provider
{
@@ -730,7 +733,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_globalSettingsProvider->addPage<ProxyPage>();
m_globalSettingsProvider->addPage<ExternalToolsPage>();
m_globalSettingsProvider->addPage<AccountListPage>();
- m_globalSettingsProvider->addPage<PasteEEPage>();
+ m_globalSettingsProvider->addPage<APIPage>();
}
qDebug() << "<> Settings loaded.";
}
@@ -900,46 +903,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
qDebug() << "<> Application theme set.";
}
- // Initialize analytics
- [this]()
- {
- const int analyticsVersion = 2;
- if(BuildConfig.ANALYTICS_ID.isEmpty())
- {
- return;
- }
-
- auto analyticsSetting = m_settings->getSetting("Analytics");
- connect(analyticsSetting.get(), &Setting::SettingChanged, this, &Application::analyticsSettingChanged);
- QString clientID = m_settings->get("AnalyticsClientID").toString();
- if(clientID.isEmpty())
- {
- clientID = QUuid::createUuid().toString();
- clientID.remove(QLatin1Char('{'));
- clientID.remove(QLatin1Char('}'));
- m_settings->set("AnalyticsClientID", clientID);
- }
- m_analytics = new GAnalytics(BuildConfig.ANALYTICS_ID, clientID, analyticsVersion, this);
- m_analytics->setLogLevel(GAnalytics::Debug);
- m_analytics->setAnonymizeIPs(true);
- // FIXME: the ganalytics library has no idea about our fancy shared pointers...
- m_analytics->setNetworkAccessManager(network().get());
-
- if(m_settings->get("AnalyticsSeen").toInt() < m_analytics->version())
- {
- qDebug() << "Analytics info not seen by user yet (or old version).";
- return;
- }
- if(!m_settings->get("Analytics").toBool())
- {
- qDebug() << "Analytics disabled by user.";
- return;
- }
-
- m_analytics->enable();
- qDebug() << "<> Initialized analytics with tid" << BuildConfig.ANALYTICS_ID;
- }();
-
if(createSetupWizard())
{
return;
@@ -966,29 +929,13 @@ bool Application::createSetupWizard()
}
return false;
}();
- bool analyticsRequired = [&]()
- {
- if(BuildConfig.ANALYTICS_ID.isEmpty())
- {
- return false;
- }
- if (!settings()->get("Analytics").toBool())
- {
- return false;
- }
- if (settings()->get("AnalyticsSeen").toInt() < analytics()->version())
- {
- return true;
- }
- return false;
- }();
bool languageRequired = [&]()
{
if (settings()->get("Language").toString().isEmpty())
return true;
return false;
}();
- bool wizardRequired = javaRequired || analyticsRequired || languageRequired;
+ bool wizardRequired = javaRequired || languageRequired;
if(wizardRequired)
{
@@ -1001,10 +948,6 @@ bool Application::createSetupWizard()
{
m_setupWizard->addPage(new JavaWizardPage(m_setupWizard));
}
- if(analyticsRequired)
- {
- m_setupWizard->addPage(new AnalyticsWizardPage(m_setupWizard));
- }
connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished);
m_setupWizard->show();
return true;
@@ -1161,22 +1104,6 @@ void Application::messageReceived(const QByteArray& message)
}
}
-void Application::analyticsSettingChanged(const Setting&, QVariant value)
-{
- if(!m_analytics)
- return;
- bool enabled = value.toBool();
- if(enabled)
- {
- qDebug() << "Analytics enabled by user.";
- }
- else
- {
- qDebug() << "Analytics disabled by user.";
- }
- m_analytics->enable(enabled);
-}
-
std::shared_ptr<TranslationsModel> Application::translations()
{
return m_translations;
@@ -1226,7 +1153,7 @@ void Application::setIconTheme(const QString& name)
QIcon Application::getThemedIcon(const QString& name)
{
if(name == "logo") {
- return QIcon(":/logo.svg");
+ return QIcon(":/org.polymc.PolyMC.svg");
}
return XdgIcon::fromTheme(name);
}
@@ -1446,60 +1373,6 @@ MainWindow* Application::showMainWindow(bool minimized)
connect(m_mainWindow, &MainWindow::isClosing, this, &Application::on_windowClose);
m_openWindows++;
}
- // FIXME: move this somewhere else...
- if(m_analytics)
- {
- auto windowSize = m_mainWindow->size();
- auto sizeString = QString("%1x%2").arg(windowSize.width()).arg(windowSize.height());
- qDebug() << "Viewport size" << sizeString;
- m_analytics->setViewportSize(sizeString);
- /*
- * cm1 = java min heap [MB]
- * cm2 = java max heap [MB]
- * cm3 = system RAM [MB]
- *
- * cd1 = java version
- * cd2 = java architecture
- * cd3 = system architecture
- * cd4 = CPU architecture
- */
- QVariantMap customValues;
- int min = m_settings->get("MinMemAlloc").toInt();
- int max = m_settings->get("MaxMemAlloc").toInt();
- if(min < max)
- {
- customValues["cm1"] = min;
- customValues["cm2"] = max;
- }
- else
- {
- customValues["cm1"] = max;
- customValues["cm2"] = min;
- }
-
- constexpr uint64_t Mega = 1024ull * 1024ull;
- int ramSize = int(Sys::getSystemRam() / Mega);
- qDebug() << "RAM size is" << ramSize << "MB";
- customValues["cm3"] = ramSize;
-
- customValues["cd1"] = m_settings->get("JavaVersion");
- customValues["cd2"] = m_settings->get("JavaArchitecture");
- customValues["cd3"] = Sys::isSystem64bit() ? "64":"32";
- customValues["cd4"] = Sys::isCPU64bit() ? "64":"32";
- auto kernelInfo = Sys::getKernelInfo();
- customValues["cd5"] = kernelInfo.kernelName;
- customValues["cd6"] = kernelInfo.kernelVersion;
- auto distInfo = Sys::getDistributionInfo();
- if(!distInfo.distributionName.isEmpty())
- {
- customValues["cd7"] = distInfo.distributionName;
- }
- if(!distInfo.distributionVersion.isEmpty())
- {
- customValues["cd8"] = distInfo.distributionVersion;
- }
- m_analytics->sendScreenView("Main Window", customValues);
- }
return m_mainWindow;
}
@@ -1558,10 +1431,6 @@ void Application::on_windowClose()
}
}
-QString Application::msaClientId() const {
- return Secrets::getMSAClientID('-');
-}
-
void Application::updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password)
{
// Set the application proxy settings.
@@ -1650,3 +1519,13 @@ QString Application::getJarsPath()
}
return m_jarsPath;
}
+
+QString Application::getMSAClientID()
+{
+ QString clientIDOverride = m_settings->get("MSAClientIDOverride").toString();
+ if (!clientIDOverride.isEmpty()) {
+ return clientIDOverride;
+ }
+
+ return BuildConfig.MSA_CLIENT_ID;
+}
diff --git a/launcher/Application.h b/launcher/Application.h
index 1b2a2b60..fb41d647 100644
--- a/launcher/Application.h
+++ b/launcher/Application.h
@@ -33,7 +33,6 @@ class BaseDetachedToolFactory;
class TranslationsModel;
class ITheme;
class MCEditTool;
-class GAnalytics;
namespace Meta {
class Index;
@@ -60,10 +59,6 @@ public:
Application(int &argc, char **argv);
virtual ~Application();
- GAnalytics *analytics() const {
- return m_analytics;
- }
-
std::shared_ptr<SettingsObject> settings() const {
return m_settings;
}
@@ -104,8 +99,6 @@ public:
return m_accounts;
}
- QString msaClientId() const;
-
Status status() const {
return m_status;
}
@@ -124,6 +117,8 @@ public:
QString getJarsPath();
+ QString getMSAClientID();
+
/// this is the root of the 'installation'. Used for automatic updates
const QString &root() {
return m_rootPath;
@@ -163,7 +158,6 @@ private slots:
void messageReceived(const QByteArray & message);
void controllerSucceeded();
void controllerFailed(const QString & error);
- void analyticsSettingChanged(const Setting &setting, QVariant value);
void setupWizardFinished(int status);
private:
@@ -228,7 +222,6 @@ private:
// peer launcher instance connector - used to implement single instance launcher and signalling
LocalPeer * m_peerInstance = nullptr;
- GAnalytics * m_analytics = nullptr;
SetupWizard * m_setupWizard = nullptr;
public:
QString m_instanceIdToLaunch;
@@ -238,3 +231,4 @@ public:
QUrl m_zipToImport;
std::unique_ptr<QFile> logFile;
};
+
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 2dfc78b5..90149c3b 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -37,6 +37,10 @@ set(CORE_SOURCES
InstanceImportTask.h
InstanceImportTask.cpp
+ # Mod downloading task
+ ModDownloadTask.h
+ ModDownloadTask.cpp
+
# Use tracking separate from memory management
Usable.h
@@ -221,7 +225,11 @@ set(MINECRAFT_SOURCES
minecraft/auth/flows/Mojang.h
minecraft/auth/flows/MSA.cpp
minecraft/auth/flows/MSA.h
+ minecraft/auth/flows/Offline.cpp
+ minecraft/auth/flows/Offline.h
+ minecraft/auth/steps/OfflineStep.cpp
+ minecraft/auth/steps/OfflineStep.h
minecraft/auth/steps/EntitlementsStep.cpp
minecraft/auth/steps/EntitlementsStep.h
minecraft/auth/steps/GetSkinStep.cpp
@@ -506,12 +514,19 @@ set(FLAME_SOURCES
# Flame
modplatform/flame/FlamePackIndex.cpp
modplatform/flame/FlamePackIndex.h
+ modplatform/flame/FlameModIndex.cpp
+ modplatform/flame/FlameModIndex.h
modplatform/flame/PackManifest.h
modplatform/flame/PackManifest.cpp
modplatform/flame/FileResolvingTask.h
modplatform/flame/FileResolvingTask.cpp
)
+set(MODRINTH_SOURCES
+ modplatform/modrinth/ModrinthPackIndex.cpp
+ modplatform/modrinth/ModrinthPackIndex.h
+)
+
set(MODPACKSCH_SOURCES
modplatform/modpacksch/FTBPackInstallTask.h
modplatform/modpacksch/FTBPackInstallTask.cpp
@@ -566,6 +581,7 @@ set(LOGIC_SOURCES
${ICONS_SOURCES}
${FTB_SOURCES}
${FLAME_SOURCES}
+ ${MODRINTH_SOURCES}
${MODPACKSCH_SOURCES}
${TECHNIC_SOURCES}
${ATLAUNCHER_SOURCES}
@@ -627,8 +643,6 @@ SET(LAUNCHER_SOURCES
# GUI - setup wizard
ui/setupwizard/SetupWizard.h
ui/setupwizard/SetupWizard.cpp
- ui/setupwizard/AnalyticsWizardPage.cpp
- ui/setupwizard/AnalyticsWizardPage.h
ui/setupwizard/BaseWizardPage.h
ui/setupwizard/JavaWizardPage.cpp
ui/setupwizard/JavaWizardPage.h
@@ -709,8 +723,8 @@ SET(LAUNCHER_SOURCES
ui/pages/global/LauncherPage.h
ui/pages/global/ProxyPage.cpp
ui/pages/global/ProxyPage.h
- ui/pages/global/PasteEEPage.cpp
- ui/pages/global/PasteEEPage.h
+ ui/pages/global/APIPage.cpp
+ ui/pages/global/APIPage.h
# GUI - platform pages
ui/pages/modplatform/VanillaPage.cpp
@@ -741,6 +755,10 @@ SET(LAUNCHER_SOURCES
ui/pages/modplatform/flame/FlameModel.h
ui/pages/modplatform/flame/FlamePage.cpp
ui/pages/modplatform/flame/FlamePage.h
+ ui/pages/modplatform/flame/FlameModModel.cpp
+ ui/pages/modplatform/flame/FlameModModel.h
+ ui/pages/modplatform/flame/FlameModPage.cpp
+ ui/pages/modplatform/flame/FlameModPage.h
ui/pages/modplatform/technic/TechnicModel.cpp
ui/pages/modplatform/technic/TechnicModel.h
@@ -750,6 +768,11 @@ SET(LAUNCHER_SOURCES
ui/pages/modplatform/ImportPage.cpp
ui/pages/modplatform/ImportPage.h
+ ui/pages/modplatform/modrinth/ModrinthModel.cpp
+ ui/pages/modplatform/modrinth/ModrinthModel.h
+ ui/pages/modplatform/modrinth/ModrinthPage.cpp
+ ui/pages/modplatform/modrinth/ModrinthPage.h
+
# GUI - dialogs
ui/dialogs/AboutDialog.cpp
ui/dialogs/AboutDialog.h
@@ -771,6 +794,8 @@ SET(LAUNCHER_SOURCES
ui/dialogs/LoginDialog.h
ui/dialogs/MSALoginDialog.cpp
ui/dialogs/MSALoginDialog.h
+ ui/dialogs/OfflineLoginDialog.cpp
+ ui/dialogs/OfflineLoginDialog.h
ui/dialogs/NewComponentDialog.cpp
ui/dialogs/NewComponentDialog.h
ui/dialogs/NewInstanceDialog.cpp
@@ -787,6 +812,8 @@ SET(LAUNCHER_SOURCES
ui/dialogs/VersionSelectDialog.h
ui/dialogs/SkinUploadDialog.cpp
ui/dialogs/SkinUploadDialog.h
+ ui/dialogs/ModDownloadDialog.cpp
+ ui/dialogs/ModDownloadDialog.h
# GUI - widgets
@@ -844,7 +871,7 @@ qt5_wrap_ui(LAUNCHER_UI
ui/pages/global/AccountListPage.ui
ui/pages/global/JavaPage.ui
ui/pages/global/LauncherPage.ui
- ui/pages/global/PasteEEPage.ui
+ ui/pages/global/APIPage.ui
ui/pages/global/ProxyPage.ui
ui/pages/global/MinecraftPage.ui
ui/pages/global/ExternalToolsPage.ui
@@ -863,10 +890,12 @@ qt5_wrap_ui(LAUNCHER_UI
ui/pages/modplatform/atlauncher/AtlPage.ui
ui/pages/modplatform/VanillaPage.ui
ui/pages/modplatform/flame/FlamePage.ui
+ ui/pages/modplatform/flame/FlameModPage.ui
ui/pages/modplatform/legacy_ftb/Page.ui
ui/pages/modplatform/ImportPage.ui
ui/pages/modplatform/ftb/FtbPage.ui
ui/pages/modplatform/technic/TechnicPage.ui
+ ui/pages/modplatform/modrinth/ModrinthPage.ui
ui/widgets/InstanceCardWidget.ui
ui/widgets/CustomCommands.ui
ui/widgets/MCModInfoFrame.ui
@@ -882,6 +911,7 @@ qt5_wrap_ui(LAUNCHER_UI
ui/dialogs/ExportInstanceDialog.ui
ui/dialogs/IconPickerDialog.ui
ui/dialogs/MSALoginDialog.ui
+ ui/dialogs/OfflineLoginDialog.ui
ui/dialogs/AboutDialog.ui
ui/dialogs/LoginDialog.ui
ui/dialogs/EditAccountDialog.ui
@@ -910,9 +940,8 @@ endif()
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
target_link_libraries(Launcher_logic
systeminfo
- Launcher_quazip
Launcher_classparser
- ${NBT_NAME}
+ nbt++
${ZLIB_LIBRARIES}
optional-bare
tomlc99
@@ -928,14 +957,13 @@ target_link_libraries(Launcher_logic
)
target_link_libraries(Launcher_logic
Launcher_iconfix
- ${QUAZIP_LIBRARIES}
+ QuaZip::QuaZip
hoedown
- Launcher_rainbow
+ PolyMC_rainbow
LocalPeer
- ganalytics
)
-target_link_libraries(Launcher_logic secrets)
+target_link_libraries(Launcher_logic)
add_executable(${Launcher_Name} MACOSX_BUNDLE WIN32 main.cpp ${LAUNCHER_RCS})
target_link_libraries(${Launcher_Name} Launcher_logic)
diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp
index 8cd68d7b..ec378538 100644
--- a/launcher/InstanceImportTask.cpp
+++ b/launcher/InstanceImportTask.cpp
@@ -29,7 +29,7 @@
#include "modplatform/flame/FileResolvingTask.h"
#include "modplatform/flame/PackManifest.h"
#include "Json.h"
-#include <quazipdir.h>
+#include <quazip/quazipdir.h>
#include "modplatform/technic/TechnicPackProcessor.h"
#include "icons/IconList.h"
diff --git a/launcher/InstancePageProvider.h b/launcher/InstancePageProvider.h
index 2af90b91..97eeab8c 100644
--- a/launcher/InstancePageProvider.h
+++ b/launcher/InstancePageProvider.h
@@ -37,7 +37,7 @@ public:
if(onesix)
{
values.append(new VersionPage(onesix.get()));
- auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList(), "mods", "loadermods", tr("Loader mods"), "Loader-mods");
+ auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList(), "mods", "loadermods", tr("Mods"), "Loader-mods");
modsPage->setFilter("%1 (*.zip *.jar *.litemod)");
values.append(modsPage);
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList(), "coremods", "coremods", tr("Core mods"), "Core-mods"));
@@ -74,3 +74,4 @@ public:
protected:
InstancePtr inst;
};
+
diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp
index 7750be1a..32fc99cb 100644
--- a/launcher/LaunchController.cpp
+++ b/launcher/LaunchController.cpp
@@ -116,6 +116,12 @@ void LaunchController::login() {
m_session->wants_online = m_online;
m_accountToUse->fillSession(m_session);
+ // Launch immediately in true offline mode
+ if(m_accountToUse->isOffline()) {
+ launchInstance();
+ return;
+ }
+
switch(m_accountToUse->accountState()) {
case AccountState::Offline: {
m_session->wants_online = false;
diff --git a/launcher/Launcher.in b/launcher/Launcher.in
index b79b276b..5e5e2c2b 100755
--- a/launcher/Launcher.in
+++ b/launcher/Launcher.in
@@ -14,7 +14,7 @@ if [[ $EUID -eq 0 ]]; then
fi
-LAUNCHER_NAME=@Launcher_Name@
+LAUNCHER_NAME=@Launcher_APP_BINARY_NAME@
LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")"
echo "Launcher Dir: ${LAUNCHER_DIR}"
diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp
index b25c61e7..9d7e4cc2 100644
--- a/launcher/MMCZip.cpp
+++ b/launcher/MMCZip.cpp
@@ -13,17 +13,16 @@
* limitations under the License.
*/
-#include <quazip.h>
-#include <quazipdir.h>
-#include <quazipfile.h>
-#include <JlCompress.h>
+#include <quazip/quazip.h>
+#include <quazip/quazipdir.h>
+#include <quazip/quazipfile.h>
#include "MMCZip.h"
#include "FileSystem.h"
#include <QDebug>
// ours
-bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, const JlCompress::FilterFunction filter)
+bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, const FilterFunction filter)
{
QuaZip modZip(from.filePath());
modZip.open(QuaZip::mdUnzip);
@@ -74,6 +73,39 @@ bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &containe
return true;
}
+bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files)
+{
+ QDir directory(dir);
+ if (!directory.exists()) return false;
+
+ for (auto e : files) {
+ auto filePath = directory.relativeFilePath(e.absoluteFilePath());
+ if( !JlCompress::compressFile(zip, e.absoluteFilePath(), filePath)) return false;
+ }
+
+ return true;
+}
+
+bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files)
+{
+ QuaZip zip(fileCompressed);
+ QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
+ if(!zip.open(QuaZip::mdCreate)) {
+ QFile::remove(fileCompressed);
+ return false;
+ }
+
+ auto result = compressDirFiles(&zip, dir, files);
+
+ zip.close();
+ if(zip.getZipError()!=0) {
+ QFile::remove(fileCompressed);
+ return false;
+ }
+
+ return result;
+}
+
// ours
bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods)
{
@@ -122,13 +154,22 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
}
else if (mod.type() == Mod::MOD_FOLDER)
{
+ // untested, but seems to be unused / not possible to reach
// FIXME: buggy - does not work with addedFiles
auto filename = mod.filename();
QString what_to_zip = filename.absoluteFilePath();
QDir dir(what_to_zip);
dir.cdUp();
QString parent_dir = dir.absolutePath();
- if (!JlCompress::compressSubDir(&zipOut, what_to_zip, parent_dir, addedFiles))
+ auto files = QFileInfoList();
+ MMCZip::collectFileListRecursively(what_to_zip, nullptr, &files, nullptr);
+
+ for (auto e : files) {
+ if (addedFiles.contains(e.filePath()))
+ files.removeAll(e);
+ }
+
+ if (!MMCZip::compressDirFiles(&zipOut, parent_dir, files))
{
zipOut.close();
QFile::remove(targetJarPath);
@@ -136,7 +177,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
return false;
}
qDebug() << "Adding folder " << filename.fileName() << " from "
- << filename.absoluteFilePath();
+ << filename.absoluteFilePath();
}
else
{
@@ -310,3 +351,37 @@ bool MMCZip::extractFile(QString fileCompressed, QString file, QString target)
}
return MMCZip::extractRelFile(&zip, file, target);
}
+
+bool MMCZip::collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList *files,
+ MMCZip::FilterFunction excludeFilter) {
+ QDir rootDirectory(rootDir);
+ if (!rootDirectory.exists()) return false;
+
+ QDir directory;
+ if (subDir == nullptr)
+ directory = rootDirectory;
+ else
+ directory = QDir(subDir);
+
+ if (!directory.exists()) return false; // shouldn't ever happen
+
+ // recurse directories
+ QFileInfoList entries = directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden);
+ for (const auto& e: entries) {
+ if (!collectFileListRecursively(rootDir, e.filePath(), files, excludeFilter))
+ return false;
+ }
+
+ // collect files
+ entries = directory.entryInfoList(QDir::Files);
+ for (const auto& e: entries) {
+ QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath());
+ if (excludeFilter && excludeFilter(relativeFilePath)) {
+ qDebug() << "Skipping file " << relativeFilePath;
+ continue;
+ }
+
+ files->append(e.filePath()); // we want the original paths for MMCZip::compressDirFiles
+ }
+ return true;
+}
diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h
index 9c47fa11..0f7aa254 100644
--- a/launcher/MMCZip.h
+++ b/launcher/MMCZip.h
@@ -21,17 +21,36 @@
#include "minecraft/mod/Mod.h"
#include <functional>
-#include <JlCompress.h>
+#include <quazip/JlCompress.h>
#include <nonstd/optional>
namespace MMCZip
{
+ using FilterFunction = std::function<bool(const QString &)>;
/**
* Merge two zip files, using a filter function
*/
bool mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
- const JlCompress::FilterFunction filter = nullptr);
+ const FilterFunction filter = nullptr);
+
+ /**
+ * Compress directory, by providing a list of files to compress
+ * \param zip target archive
+ * \param dir directory that will be compressed (to compress with relative paths)
+ * \param files list of files to compress
+ * \return true for success or false for failure
+ */
+ bool compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files);
+
+ /**
+ * Compress directory, by providing a list of files to compress
+ * \param fileCompressed target archive file
+ * \param dir directory that will be compressed (to compress with relative paths)
+ * \param files list of files to compress
+ * \return true for success or false for failure
+ */
+ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files);
/**
* take a source jar, add mods to it, resulting in target jar
@@ -89,4 +108,13 @@ namespace MMCZip
*/
bool extractFile(QString fileCompressed, QString file, QString dir);
+ /**
+ * Populate a QFileInfoList with a directory tree recursively, while allowing to excludeFilter what shouldn't be included.
+ * \param rootDir directory to start off
+ * \param subDir subdirectory, should be nullptr for first invocation
+ * \param files resulting list of QFileInfo
+ * \param excludeFilter function to excludeFilter which files shouldn't be included (returning true means to excude)
+ * \return true for success or false for failure
+ */
+ bool collectFileListRecursively(const QString &rootDir, const QString &subDir, QFileInfoList *files, FilterFunction excludeFilter);
}
diff --git a/launcher/ModDownloadTask.cpp b/launcher/ModDownloadTask.cpp
new file mode 100644
index 00000000..08a02d29
--- /dev/null
+++ b/launcher/ModDownloadTask.cpp
@@ -0,0 +1,39 @@
+#include "ModDownloadTask.h"
+#include "Application.h"
+
+ModDownloadTask::ModDownloadTask(const QUrl sourceUrl,const QString filename, const std::shared_ptr<ModFolderModel> mods)
+: m_sourceUrl(sourceUrl), mods(mods), filename(filename) {
+}
+
+void ModDownloadTask::executeTask() {
+ setStatus(tr("Downloading mod:\n%1").arg(m_sourceUrl.toString()));
+
+ m_filesNetJob.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
+ m_filesNetJob->addNetAction(Net::Download::makeFile(m_sourceUrl, mods->dir().absoluteFilePath(filename)));
+ connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ModDownloadTask::downloadSucceeded);
+ connect(m_filesNetJob.get(), &NetJob::progress, this, &ModDownloadTask::downloadProgressChanged);
+ connect(m_filesNetJob.get(), &NetJob::failed, this, &ModDownloadTask::downloadFailed);
+ m_filesNetJob->start();
+}
+
+void ModDownloadTask::downloadSucceeded()
+{
+ emitSucceeded();
+ m_filesNetJob.reset();
+}
+
+void ModDownloadTask::downloadFailed(QString reason)
+{
+ emitFailed(reason);
+ m_filesNetJob.reset();
+}
+
+void ModDownloadTask::downloadProgressChanged(qint64 current, qint64 total)
+{
+ emit progress(current, total);
+}
+
+bool ModDownloadTask::abort() {
+ return m_filesNetJob->abort();
+}
+
diff --git a/launcher/ModDownloadTask.h b/launcher/ModDownloadTask.h
new file mode 100644
index 00000000..7e4f1b7d
--- /dev/null
+++ b/launcher/ModDownloadTask.h
@@ -0,0 +1,34 @@
+#pragma once
+#include "QObjectPtr.h"
+#include "tasks/Task.h"
+#include "minecraft/mod/ModFolderModel.h"
+#include "net/NetJob.h"
+#include <QUrl>
+
+
+class ModDownloadTask : public Task {
+ Q_OBJECT
+public:
+ explicit ModDownloadTask(const QUrl sourceUrl, const QString filename, const std::shared_ptr<ModFolderModel> mods);
+
+public slots:
+ bool abort() override;
+protected:
+ //! Entry point for tasks.
+ void executeTask() override;
+
+private:
+ QUrl m_sourceUrl;
+ NetJob::Ptr m_filesNetJob;
+ const std::shared_ptr<ModFolderModel> mods;
+ const QString filename;
+
+ void downloadProgressChanged(qint64 current, qint64 total);
+
+ void downloadFailed(QString reason);
+
+ void downloadSucceeded();
+};
+
+
+
diff --git a/launcher/UpdateController.cpp b/launcher/UpdateController.cpp
index f9b7d349..c02cd1e7 100644
--- a/launcher/UpdateController.cpp
+++ b/launcher/UpdateController.cpp
@@ -93,7 +93,7 @@ void UpdateController::installUpdates()
qDebug() << "Installing updates.";
#ifdef Q_OS_WIN
QString finishCmd = QApplication::applicationFilePath();
-#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
+#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined (Q_OS_OPENBSD)
QString finishCmd = FS::PathCombine(m_root, BuildConfig.LAUNCHER_NAME);
#elif defined Q_OS_MAC
QString finishCmd = QApplication::applicationFilePath();
diff --git a/launcher/java/JavaInstallList.cpp b/launcher/java/JavaInstallList.cpp
index 07f2bd8c..a0a60871 100644
--- a/launcher/java/JavaInstallList.cpp
+++ b/launcher/java/JavaInstallList.cpp
@@ -120,8 +120,8 @@ void JavaInstallList::updateListData(QList<BaseVersionPtr> versions)
bool sortJavas(BaseVersionPtr left, BaseVersionPtr right)
{
- auto rleft = std::dynamic_pointer_cast<JavaInstall>(left);
- auto rright = std::dynamic_pointer_cast<JavaInstall>(right);
+ auto rleft = std::dynamic_pointer_cast<JavaInstall>(right);
+ auto rright = std::dynamic_pointer_cast<JavaInstall>(left);
return (*rleft) > (*rright);
}
diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp
index fd7e43e9..6e5dfeae 100644
--- a/launcher/java/JavaUtils.cpp
+++ b/launcher/java/JavaUtils.cpp
@@ -77,14 +77,14 @@ QProcessEnvironment CleanEnviroment()
qDebug() << "Env: ignoring" << key << value;
continue;
}
- // filter MultiMC-related things
+ // filter PolyMC-related things
if(key.startsWith("QT_"))
{
qDebug() << "Env: ignoring" << key << value;
continue;
}
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
- // Do not pass LD_* variables to java. They were intended for MultiMC
+ // Do not pass LD_* variables to java. They were intended for PolyMC
if(key.startsWith("LD_"))
{
qDebug() << "Env: ignoring" << key << value;
@@ -149,6 +149,21 @@ JavaInstallPtr JavaUtils::GetDefaultJava()
return javaVersion;
}
+QStringList addJavasFromEnv(QList<QString> javas)
+{
+ QByteArray env = qgetenv("POLYMC_JAVA_PATHS");
+#if defined(Q_OS_WIN32)
+ QList<QString> javaPaths = QString::fromLocal8Bit(env).split(QLatin1String(";"));
+#else
+ QList<QString> javaPaths = QString::fromLocal8Bit(env).split(QLatin1String(":"));
+#endif
+ for(QString i : javaPaths)
+ {
+ javas.append(i);
+ };
+ return javas;
+}
+
#if defined(Q_OS_WIN32)
QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName, QString keyJavaDir, QString subkeySuffix)
{
@@ -290,7 +305,7 @@ QList<QString> JavaUtils::FindJavaPaths()
KEY_WOW64_64KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath");
QList<JavaInstallPtr> ZULU32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath");
-
+
// BellSoft Liberica
QList<JavaInstallPtr> LIBERICA64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath");
@@ -328,7 +343,7 @@ QList<QString> JavaUtils::FindJavaPaths()
java_candidates.append(ADOPTIUMJDK32s);
java_candidates.append(ZULU32s);
java_candidates.append(LIBERICA32s);
-
+
java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path));
QList<QString> candidates;
@@ -363,7 +378,7 @@ QList<QString> JavaUtils::FindJavaPaths()
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
}
- return javas;
+ return addJavasFromEnv(javas);
}
#elif defined(Q_OS_LINUX)
@@ -402,12 +417,14 @@ QList<QString> JavaUtils::FindJavaPaths()
scanJavaDir("/usr/lib/jvm");
scanJavaDir("/usr/lib64/jvm");
scanJavaDir("/usr/lib32/jvm");
- // javas stored in MultiMC's folder
+ // javas stored in PolyMC's folder
scanJavaDir("java");
// manually installed JDKs in /opt
scanJavaDir("/opt/jdk");
scanJavaDir("/opt/jdks");
- return javas;
+ // flatpak
+ scanJavaDir("/app/jdk");
+ return addJavasFromEnv(javas);
}
#else
QList<QString> JavaUtils::FindJavaPaths()
@@ -417,6 +434,6 @@ QList<QString> JavaUtils::FindJavaPaths()
QList<QString> javas;
javas.append(this->GetDefaultJava()->path);
- return javas;
+ return addJavasFromEnv(javas);
}
#endif
diff --git a/launcher/launch/LaunchTask.cpp b/launcher/launch/LaunchTask.cpp
index e6f6bbac..231a6398 100644
--- a/launcher/launch/LaunchTask.cpp
+++ b/launcher/launch/LaunchTask.cpp
@@ -212,7 +212,7 @@ shared_qobject_ptr<LogModel> LaunchTask::getLogModel()
m_logModel->setMaxLines(m_instance->getConsoleMaxLines());
m_logModel->setStopOnOverflow(m_instance->shouldStopOnConsoleOverflow());
// FIXME: should this really be here?
- m_logModel->setOverflowMessage(tr("MultiMC stopped watching the game log because the log length surpassed %1 lines.\n"
+ m_logModel->setOverflowMessage(tr("PolyMC stopped watching the game log because the log length surpassed %1 lines.\n"
"You may have to fix your mods because the game is still logging to files and"
" likely wasting harddrive space at an alarming rate!").arg(m_logModel->getMaxLines()));
}
@@ -277,4 +277,3 @@ QString LaunchTask::substituteVariables(const QString &cmd) const
}
return out;
}
-
diff --git a/launcher/launch/steps/CheckJava.cpp b/launcher/launch/steps/CheckJava.cpp
index fb338231..d3f2148c 100644
--- a/launcher/launch/steps/CheckJava.cpp
+++ b/launcher/launch/steps/CheckJava.cpp
@@ -87,14 +87,14 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
// Error message displayed if java can't start
emit logLine(QString("Could not start java:"), MessageLevel::Error);
emit logLines(result.errorLog.split('\n'), MessageLevel::Error);
- emit logLine("\nCheck your MultiMC Java settings.", MessageLevel::Launcher);
+ emit logLine("\nCheck your PolyMC Java settings.", MessageLevel::Launcher);
printSystemInfo(false, false);
emitFailed(QString("Could not start java!"));
return;
}
case JavaCheckResult::Validity::ReturnedInvalidData:
{
- emit logLine(QString("Java checker returned some invalid data MultiMC doesn't understand:"), MessageLevel::Error);
+ emit logLine(QString("Java checker returned some invalid data PolyMC doesn't understand:"), MessageLevel::Error);
emit logLines(result.outLog.split('\n'), MessageLevel::Warning);
emit logLine("\nMinecraft might not start properly.", MessageLevel::Launcher);
printSystemInfo(false, false);
diff --git a/launcher/main.cpp b/launcher/main.cpp
index aabb5a06..8b572743 100644
--- a/launcher/main.cpp
+++ b/launcher/main.cpp
@@ -40,7 +40,7 @@ int main(int argc, char *argv[])
Q_INIT_RESOURCE(multimc);
Q_INIT_RESOURCE(backgrounds);
Q_INIT_RESOURCE(documents);
- Q_INIT_RESOURCE(logo);
+ Q_INIT_RESOURCE(polymc);
Q_INIT_RESOURCE(pe_dark);
Q_INIT_RESOURCE(pe_light);
diff --git a/launcher/minecraft/Library.h b/launcher/minecraft/Library.h
index 41d41a8b..0740a7ca 100644
--- a/launcher/minecraft/Library.h
+++ b/launcher/minecraft/Library.h
@@ -156,7 +156,7 @@ public: /* methods */
QStringList & failedLocalFiles, const QString & overridePath) const;
private: /* methods */
- /// the default storage prefix used by MultiMC
+ /// the default storage prefix used by PolyMC
static QString defaultStoragePrefix();
/// Get the prefix - root of the storage to be used
@@ -177,23 +177,23 @@ protected: /* data */
/// DEPRECATED URL prefix of the maven repo where the file can be downloaded
QString m_repositoryURL;
- /// DEPRECATED: MultiMC-specific absolute URL. takes precedence over the implicit maven repo URL, if defined
+ /// DEPRECATED: PolyMC-specific absolute URL. takes precedence over the implicit maven repo URL, if defined
QString m_absoluteURL;
- /// MultiMC extension - filename override
+ /// PolyMC extension - filename override
QString m_filename;
- /// DEPRECATED MultiMC extension - display name
+ /// DEPRECATED PolyMC extension - display name
QString m_displayname;
/**
- * MultiMC-specific type hint - modifies how the library is treated
+ * PolyMC-specific type hint - modifies how the library is treated
*/
QString m_hint;
/**
- * storage - by default the local libraries folder in multimc, but could be elsewhere
- * MultiMC specific, because of FTB.
+ * storage - by default the local libraries folder in polymc, but could be elsewhere
+ * PolyMC specific, because of FTB.
*/
QString m_storagePrefix;
@@ -215,3 +215,4 @@ protected: /* data */
/// MOJANG: container with Mojang style download info
MojangLibraryDownloadInfo::Ptr m_mojangDownloads;
};
+
diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp
index 0b3c049b..7327f9d5 100644
--- a/launcher/minecraft/MinecraftInstance.cpp
+++ b/launcher/minecraft/MinecraftInstance.cpp
@@ -445,7 +445,7 @@ QStringList MinecraftInstance::processMinecraftArgs(
}
// blatant self-promotion.
- token_mapping["profile_name"] = token_mapping["version_name"] = "MultiMC5";
+ token_mapping["profile_name"] = token_mapping["version_name"] = "PolyMC";
token_mapping["version_type"] = profile->getMinecraftVersionType();
diff --git a/launcher/minecraft/MinecraftLoadAndCheck.h b/launcher/minecraft/MinecraftLoadAndCheck.h
index bfeae46b..d9af3ace 100644
--- a/launcher/minecraft/MinecraftLoadAndCheck.h
+++ b/launcher/minecraft/MinecraftLoadAndCheck.h
@@ -20,7 +20,7 @@
#include <QUrl>
#include "tasks/Task.h"
-#include <quazip.h>
+#include <quazip/quazip.h>
#include "QObjectPtr.h"
diff --git a/launcher/minecraft/MinecraftUpdate.h b/launcher/minecraft/MinecraftUpdate.h
index fadebff9..9ebef656 100644
--- a/launcher/minecraft/MinecraftUpdate.h
+++ b/launcher/minecraft/MinecraftUpdate.h
@@ -22,7 +22,7 @@
#include "net/NetJob.h"
#include "tasks/Task.h"
#include "minecraft/VersionFilterData.h"
-#include <quazip.h>
+#include <quazip/quazip.h>
class MinecraftVersion;
class MinecraftInstance;
diff --git a/launcher/minecraft/VersionFile.h b/launcher/minecraft/VersionFile.h
index b79fcd4f..239a4069 100644
--- a/launcher/minecraft/VersionFile.h
+++ b/launcher/minecraft/VersionFile.h
@@ -27,19 +27,19 @@ public: /* methods */
void applyTo(LaunchProfile* profile);
public: /* data */
- /// MultiMC: order hint for this version file if no explicit order is set
+ /// PolyMC: order hint for this version file if no explicit order is set
int order = 0;
- /// MultiMC: human readable name of this package
+ /// PolyMC: human readable name of this package
QString name;
- /// MultiMC: package ID of this package
+ /// PolyMC: package ID of this package
QString uid;
- /// MultiMC: version of this package
+ /// PolyMC: version of this package
QString version;
- /// MultiMC: DEPRECATED dependency on a Minecraft version
+ /// PolyMC: DEPRECATED dependency on a Minecraft version
QString dependsOnMinecraftVersion;
/// Mojang: DEPRECATED used to version the Mojang version format
@@ -51,7 +51,7 @@ public: /* data */
/// Mojang: class to launch Minecraft with
QString mainClass;
- /// MultiMC: class to launch legacy Minecraft with (embed in a custom window)
+ /// PolyMC: class to launch legacy Minecraft with (embed in a custom window)
QString appletClass;
/// Mojang: Minecraft launch arguments (may contain placeholders for variable substitution)
@@ -69,35 +69,35 @@ public: /* data */
/// Mojang: DEPRECATED asset group to be used with Minecraft
QString assets;
- /// MultiMC: list of tweaker mod arguments for launchwrapper
+ /// PolyMC: list of tweaker mod arguments for launchwrapper
QStringList addTweakers;
/// Mojang: list of libraries to add to the version
QList<LibraryPtr> libraries;
- /// MultiMC: list of maven files to put in the libraries folder, but not in classpath
+ /// PolyMC: list of maven files to put in the libraries folder, but not in classpath
QList<LibraryPtr> mavenFiles;
/// The main jar (Minecraft version library, normally)
LibraryPtr mainJar;
- /// MultiMC: list of attached traits of this version file - used to enable features
+ /// PolyMC: list of attached traits of this version file - used to enable features
QSet<QString> traits;
- /// MultiMC: list of jar mods added to this version
+ /// PolyMC: list of jar mods added to this version
QList<LibraryPtr> jarMods;
- /// MultiMC: list of mods added to this version
+ /// PolyMC: list of mods added to this version
QList<LibraryPtr> mods;
/**
- * MultiMC: set of packages this depends on
+ * PolyMC: set of packages this depends on
* NOTE: this is shared with the meta format!!!
*/
Meta::RequireSet requires;
/**
- * MultiMC: set of packages this conflicts with
+ * PolyMC: set of packages this conflicts with
* NOTE: this is shared with the meta format!!!
*/
Meta::RequireSet conflicts;
@@ -112,3 +112,4 @@ public:
// Mojang: extended asset index download information
std::shared_ptr<MojangAssetIndexInfo> mojangAssetIndex;
};
+
diff --git a/launcher/minecraft/World.cpp b/launcher/minecraft/World.cpp
index a2b4dac7..2937c116 100644
--- a/launcher/minecraft/World.cpp
+++ b/launcher/minecraft/World.cpp
@@ -26,9 +26,9 @@
#include <io/stream_reader.h>
#include <tag_string.h>
#include <tag_primitive.h>
-#include <quazip.h>
-#include <quazipfile.h>
-#include <quazipdir.h>
+#include <quazip/quazip.h>
+#include <quazip/quazipfile.h>
+#include <quazip/quazipdir.h>
#include <QCoreApplication>
diff --git a/launcher/minecraft/auth/AccountData.cpp b/launcher/minecraft/auth/AccountData.cpp
index 7526c951..9b84fe1a 100644
--- a/launcher/minecraft/auth/AccountData.cpp
+++ b/launcher/minecraft/auth/AccountData.cpp
@@ -314,6 +314,8 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
type = AccountType::MSA;
} else if (typeS == "Mojang") {
type = AccountType::Mojang;
+ } else if (typeS == "Offline") {
+ type = AccountType::Offline;
} else {
qWarning() << "Failed to parse account data: type is not recognized.";
return false;
@@ -363,6 +365,9 @@ QJsonObject AccountData::saveState() const {
tokenToJSONV3(output, xboxApiToken, "xrp-main");
tokenToJSONV3(output, mojangservicesToken, "xrp-mc");
}
+ else if (type == AccountType::Offline) {
+ output["type"] = "Offline";
+ }
tokenToJSONV3(output, yggdrasilToken, "ygg");
profileToJSONV3(output, minecraftProfile, "profile");
@@ -371,7 +376,7 @@ QJsonObject AccountData::saveState() const {
}
QString AccountData::userName() const {
- if(type != AccountType::Mojang) {
+ if(type == AccountType::MSA) {
return QString();
}
return yggdrasilToken.extra["userName"].toString();
@@ -427,6 +432,9 @@ QString AccountData::accountDisplayString() const {
case AccountType::Mojang: {
return userName();
}
+ case AccountType::Offline: {
+ return userName();
+ }
case AccountType::MSA: {
if(xboxApiToken.extra.contains("gtg")) {
return xboxApiToken.extra["gtg"].toString();
diff --git a/launcher/minecraft/auth/AccountData.h b/launcher/minecraft/auth/AccountData.h
index abf84e43..606c1ad1 100644
--- a/launcher/minecraft/auth/AccountData.h
+++ b/launcher/minecraft/auth/AccountData.h
@@ -38,7 +38,8 @@ struct MinecraftProfile {
enum class AccountType {
MSA,
- Mojang
+ Mojang,
+ Offline
};
enum class AccountState {
diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp
index ef8b435d..04470e1c 100644
--- a/launcher/minecraft/auth/AccountList.cpp
+++ b/launcher/minecraft/auth/AccountList.cpp
@@ -302,7 +302,7 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
}
case MigrationColumn: {
- if(account->isMSA()) {
+ if(account->isMSA() || account->isOffline()) {
return tr("N/A", "Can Migrate?");
}
if (account->canMigrate()) {
diff --git a/launcher/minecraft/auth/AccountList.h b/launcher/minecraft/auth/AccountList.h
index fa1e7431..025926ae 100644
--- a/launcher/minecraft/auth/AccountList.h
+++ b/launcher/minecraft/auth/AccountList.h
@@ -24,7 +24,7 @@
/*!
* List of available Mojang accounts.
- * This should be loaded in the background by MultiMC on startup.
+ * This should be loaded in the background by PolyMC on startup.
*/
class AccountList : public QAbstractListModel
{
@@ -158,3 +158,4 @@ protected:
*/
bool m_autosave = false;
};
+
diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp
index ed9e945e..ffc81ed8 100644
--- a/launcher/minecraft/auth/MinecraftAccount.cpp
+++ b/launcher/minecraft/auth/MinecraftAccount.cpp
@@ -30,6 +30,7 @@
#include "flows/MSA.h"
#include "flows/Mojang.h"
+#include "flows/Offline.h"
MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) {
data.internalId = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
@@ -68,6 +69,23 @@ MinecraftAccountPtr MinecraftAccount::createBlankMSA()
return account;
}
+MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username)
+{
+ MinecraftAccountPtr account = new MinecraftAccount();
+ account->data.type = AccountType::Offline;
+ account->data.yggdrasilToken.token = "offline";
+ account->data.yggdrasilToken.validity = Katabasis::Validity::Certain;
+ account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc();
+ account->data.yggdrasilToken.extra["userName"] = username;
+ account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
+ account->data.minecraftEntitlement.ownsMinecraft = true;
+ account->data.minecraftEntitlement.canPlayMinecraft = true;
+ account->data.minecraftProfile.id = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
+ account->data.minecraftProfile.name = username;
+ account->data.minecraftProfile.validity = Katabasis::Validity::Certain;
+ return account;
+}
+
QJsonObject MinecraftAccount::saveToJson() const
{
@@ -111,6 +129,16 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::loginMSA() {
return m_currentTask;
}
+shared_qobject_ptr<AccountTask> MinecraftAccount::loginOffline() {
+ Q_ASSERT(m_currentTask.get() == nullptr);
+
+ m_currentTask.reset(new OfflineLogin(&data));
+ connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
+ connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
+ emit activityChanged(true);
+ return m_currentTask;
+}
+
shared_qobject_ptr<AccountTask> MinecraftAccount::refresh() {
if(m_currentTask) {
return m_currentTask;
@@ -119,6 +147,9 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::refresh() {
if(data.type == AccountType::MSA) {
m_currentTask.reset(new MSASilent(&data));
}
+ else if(data.type == AccountType::Offline) {
+ m_currentTask.reset(new OfflineRefresh(&data));
+ }
else {
m_currentTask.reset(new MojangRefresh(&data));
}
diff --git a/launcher/minecraft/auth/MinecraftAccount.h b/launcher/minecraft/auth/MinecraftAccount.h
index 4ac0a3e5..6592f9c0 100644
--- a/launcher/minecraft/auth/MinecraftAccount.h
+++ b/launcher/minecraft/auth/MinecraftAccount.h
@@ -41,7 +41,7 @@ Q_DECLARE_METATYPE(MinecraftAccountPtr)
* A profile within someone's Mojang account.
*
* Currently, the profile system has not been implemented by Mojang yet,
- * but we might as well add some things for it in MultiMC right now so
+ * but we might as well add some things for it in PolyMC right now so
* we don't have to rip the code to pieces to add it later.
*/
struct AccountProfile
@@ -73,6 +73,8 @@ public: /* construction */
static MinecraftAccountPtr createBlankMSA();
+ static MinecraftAccountPtr createOffline(const QString &username);
+
static MinecraftAccountPtr loadFromJsonV2(const QJsonObject &json);
static MinecraftAccountPtr loadFromJsonV3(const QJsonObject &json);
@@ -89,6 +91,8 @@ public: /* manipulation */
shared_qobject_ptr<AccountTask> loginMSA();
+ shared_qobject_ptr<AccountTask> loginOffline();
+
shared_qobject_ptr<AccountTask> refresh();
shared_qobject_ptr<AccountTask> currentTask();
@@ -128,6 +132,10 @@ public: /* queries */
return data.type == AccountType::MSA;
}
+ bool isOffline() const {
+ return data.type == AccountType::Offline;
+ }
+
bool ownsMinecraft() const {
return data.minecraftEntitlement.ownsMinecraft;
}
@@ -149,6 +157,10 @@ public: /* queries */
return "msa";
}
break;
+ case AccountType::Offline: {
+ return "offline";
+ }
+ break;
default: {
return "unknown";
}
@@ -198,3 +210,4 @@ slots:
void authSucceeded();
void authFailed(QString reason);
};
+
diff --git a/launcher/minecraft/auth/flows/Offline.cpp b/launcher/minecraft/auth/flows/Offline.cpp
new file mode 100644
index 00000000..fc614a8c
--- /dev/null
+++ b/launcher/minecraft/auth/flows/Offline.cpp
@@ -0,0 +1,17 @@
+#include "Offline.h"
+
+#include "minecraft/auth/steps/OfflineStep.h"
+
+OfflineRefresh::OfflineRefresh(
+ AccountData *data,
+ QObject *parent
+) : AuthFlow(data, parent) {
+ m_steps.append(new OfflineStep(m_data));
+}
+
+OfflineLogin::OfflineLogin(
+ AccountData *data,
+ QObject *parent
+) : AuthFlow(data, parent) {
+ m_steps.append(new OfflineStep(m_data));
+}
diff --git a/launcher/minecraft/auth/flows/Offline.h b/launcher/minecraft/auth/flows/Offline.h
new file mode 100644
index 00000000..5d1f83a4
--- /dev/null
+++ b/launcher/minecraft/auth/flows/Offline.h
@@ -0,0 +1,22 @@
+#pragma once
+#include "AuthFlow.h"
+
+class OfflineRefresh : public AuthFlow
+{
+ Q_OBJECT
+public:
+ explicit OfflineRefresh(
+ AccountData *data,
+ QObject *parent = 0
+ );
+};
+
+class OfflineLogin : public AuthFlow
+{
+ Q_OBJECT
+public:
+ explicit OfflineLogin(
+ AccountData *data,
+ QObject *parent = 0
+ );
+};
diff --git a/launcher/minecraft/auth/steps/MSAStep.cpp b/launcher/minecraft/auth/steps/MSAStep.cpp
index be711f7e..779aee43 100644
--- a/launcher/minecraft/auth/steps/MSAStep.cpp
+++ b/launcher/minecraft/auth/steps/MSAStep.cpp
@@ -2,6 +2,7 @@
#include <QNetworkRequest>
+#include "BuildConfig.h"
#include "minecraft/auth/AuthRequest.h"
#include "minecraft/auth/Parsers.h"
@@ -13,7 +14,7 @@ using Activity = Katabasis::Activity;
MSAStep::MSAStep(AccountData* data, Action action) : AuthStep(data), m_action(action) {
OAuth2::Options opts;
opts.scope = "XboxLive.signin offline_access";
- opts.clientIdentifier = APPLICATION->msaClientId();
+ opts.clientIdentifier = APPLICATION->getMSAClientID();
opts.authorizationUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode";
opts.accessTokenUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token";
diff --git a/launcher/minecraft/auth/steps/OfflineStep.cpp b/launcher/minecraft/auth/steps/OfflineStep.cpp
new file mode 100644
index 00000000..dc092bfd
--- /dev/null
+++ b/launcher/minecraft/auth/steps/OfflineStep.cpp
@@ -0,0 +1,18 @@
+#include "OfflineStep.h"
+
+#include "Application.h"
+
+OfflineStep::OfflineStep(AccountData* data) : AuthStep(data) {}
+OfflineStep::~OfflineStep() noexcept = default;
+
+QString OfflineStep::describe() {
+ return tr("Creating offline account.");
+}
+
+void OfflineStep::rehydrate() {
+ // NOOP
+}
+
+void OfflineStep::perform() {
+ emit finished(AccountTaskState::STATE_WORKING, tr("Created offline account."));
+}
diff --git a/launcher/minecraft/auth/steps/OfflineStep.h b/launcher/minecraft/auth/steps/OfflineStep.h
new file mode 100644
index 00000000..436597cd
--- /dev/null
+++ b/launcher/minecraft/auth/steps/OfflineStep.h
@@ -0,0 +1,19 @@
+#pragma once
+#include <QObject>
+
+#include "QObjectPtr.h"
+#include "minecraft/auth/AuthStep.h"
+
+#include <katabasis/DeviceFlow.h>
+
+class OfflineStep : public AuthStep {
+ Q_OBJECT
+public:
+ explicit OfflineStep(AccountData *data);
+ virtual ~OfflineStep() noexcept;
+
+ void perform() override;
+ void rehydrate() override;
+
+ QString describe() override;
+};
diff --git a/launcher/minecraft/launch/ExtractNatives.cpp b/launcher/minecraft/launch/ExtractNatives.cpp
index 8cd439b1..7d5f4179 100644
--- a/launcher/minecraft/launch/ExtractNatives.cpp
+++ b/launcher/minecraft/launch/ExtractNatives.cpp
@@ -17,8 +17,8 @@
#include <minecraft/MinecraftInstance.h>
#include <launch/LaunchTask.h>
-#include <quazip.h>
-#include <quazipdir.h>
+#include <quazip/quazip.h>
+#include <quazip/quazipdir.h>
#include "MMCZip.h"
#include "FileSystem.h"
#include <QDir>
diff --git a/launcher/minecraft/launch/LauncherPartLaunch.cpp b/launcher/minecraft/launch/LauncherPartLaunch.cpp
index 8fd11eca..f461b847 100644
--- a/launcher/minecraft/launch/LauncherPartLaunch.cpp
+++ b/launcher/minecraft/launch/LauncherPartLaunch.cpp
@@ -25,6 +25,19 @@
LauncherPartLaunch::LauncherPartLaunch(LaunchTask *parent) : LaunchStep(parent)
{
+ if (APPLICATION->settings()->get("CloseAfterLaunch").toBool())
+ {
+ std::shared_ptr<QMetaObject::Connection> connection{new QMetaObject::Connection};
+ *connection = connect(&m_process, &LoggedProcess::log, this, [=](QStringList lines, MessageLevel::Enum level) {
+ qDebug() << lines;
+ if (lines.filter(QRegularExpression(".*Setting user.+", QRegularExpression::CaseInsensitiveOption)).length() != 0)
+ {
+ APPLICATION->closeAllWindows();
+ disconnect(*connection);
+ }
+ });
+ }
+
connect(&m_process, &LoggedProcess::log, this, &LauncherPartLaunch::logLines);
connect(&m_process, &LoggedProcess::stateChanged, this, &LauncherPartLaunch::on_state);
}
@@ -155,6 +168,8 @@ void LauncherPartLaunch::on_state(LoggedProcess::State state)
}
case LoggedProcess::Finished:
{
+ if (APPLICATION->settings()->get("CloseAfterLaunch").toBool())
+ APPLICATION->showMainWindow();
m_parent->setPid(-1);
// if the exit code wasn't 0, report this as a crash
auto exitCode = m_process.exitCode();
diff --git a/launcher/minecraft/launch/MinecraftServerTarget.cpp b/launcher/minecraft/launch/MinecraftServerTarget.cpp
index 0f98f356..78a33359 100644
--- a/launcher/minecraft/launch/MinecraftServerTarget.cpp
+++ b/launcher/minecraft/launch/MinecraftServerTarget.cpp
@@ -23,7 +23,7 @@ MinecraftServerTarget MinecraftServerTarget::parse(const QString &fullAddress) {
// The logic below replicates the exact logic minecraft uses for parsing server addresses.
// While the conversion is not lossless and eats errors, it ensures the same behavior
- // within Minecraft and MultiMC when entering server addresses.
+ // within Minecraft and PolyMC when entering server addresses.
if (fullAddress.startsWith("["))
{
int bracket = fullAddress.indexOf("]");
diff --git a/launcher/minecraft/mod/LocalModParseTask.cpp b/launcher/minecraft/mod/LocalModParseTask.cpp
index 8ac5885f..757a2187 100644
--- a/launcher/minecraft/mod/LocalModParseTask.cpp
+++ b/launcher/minecraft/mod/LocalModParseTask.cpp
@@ -4,8 +4,8 @@
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
-#include <quazip.h>
-#include <quazipfile.h>
+#include <quazip/quazip.h>
+#include <quazip/quazipfile.h>
#include <toml.h>
#include "settings/INIFile.h"
diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
index e5db512e..8de5fc9f 100644
--- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
+++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
@@ -19,7 +19,7 @@
#include <QtConcurrent/QtConcurrent>
-#include <quazip.h>
+#include <quazip/quazip.h>
#include "MMCZip.h"
#include "minecraft/OneSixVersionFormat.h"
diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp
new file mode 100644
index 00000000..a8b2495a
--- /dev/null
+++ b/launcher/modplatform/flame/FlameModIndex.cpp
@@ -0,0 +1,100 @@
+#include <QObject>
+#include "FlameModIndex.h"
+#include "Json.h"
+#include "net/NetJob.h"
+#include "BaseInstance.h"
+#include "minecraft/MinecraftInstance.h"
+#include "minecraft/PackProfile.h"
+
+
+void FlameMod::loadIndexedPack(FlameMod::IndexedPack & pack, QJsonObject & obj)
+{
+ pack.addonId = Json::requireInteger(obj, "id");
+ pack.name = Json::requireString(obj, "name");
+ pack.websiteUrl = Json::ensureString(obj, "websiteUrl", "");
+ pack.description = Json::ensureString(obj, "summary", "");
+
+ bool thumbnailFound = false;
+ auto attachments = Json::requireArray(obj, "attachments");
+ for(auto attachmentRaw: attachments) {
+ auto attachmentObj = Json::requireObject(attachmentRaw);
+ bool isDefault = attachmentObj.value("isDefault").toBool(false);
+ if(isDefault) {
+ thumbnailFound = true;
+ pack.logoName = Json::requireString(attachmentObj, "title");
+ pack.logoUrl = Json::requireString(attachmentObj, "thumbnailUrl");
+ break;
+ }
+ }
+
+ if(!thumbnailFound) {
+ throw JSONValidationError(QString("Pack without an icon, skipping: %1").arg(pack.name));
+ }
+
+
+ auto authors = Json::requireArray(obj, "authors");
+ for(auto authorIter: authors) {
+ auto author = Json::requireObject(authorIter);
+ FlameMod::ModpackAuthor packAuthor;
+ packAuthor.name = Json::requireString(author, "name");
+ packAuthor.url = Json::requireString(author, "url");
+ pack.authors.append(packAuthor);
+ }
+}
+
+void FlameMod::loadIndexedPackVersions(FlameMod::IndexedPack & pack, QJsonArray & arr, const shared_qobject_ptr<QNetworkAccessManager>& network, BaseInstance * inst)
+{
+ QVector<FlameMod::IndexedVersion> unsortedVersions;
+ bool hasFabric = !((MinecraftInstance *)inst)->getPackProfile()->getComponentVersion("net.fabricmc.fabric-loader").isEmpty();
+ QString mcVersion = ((MinecraftInstance *)inst)->getPackProfile()->getComponentVersion("net.minecraft");
+
+ for(auto versionIter: arr) {
+ auto obj = versionIter.toObject();
+ FlameMod::IndexedVersion file;
+ file.addonId = pack.addonId;
+ file.fileId = Json::requireInteger(obj, "id");
+ file.date = Json::requireString(obj, "fileDate");
+ auto versionArray = Json::requireArray(obj, "gameVersion");
+ if (versionArray.empty()) {
+ continue;
+ }
+ for(auto mcVer : versionArray){
+ file.mcVersion.append(mcVer.toString());
+ }
+
+ file.version = Json::requireString(obj, "displayName");
+ file.downloadUrl = Json::requireString(obj, "downloadUrl");
+ file.fileName = Json::requireString(obj, "fileName");
+
+ auto modules = Json::requireArray(obj, "modules");
+ bool valid = false;
+ for(auto m : modules){
+ auto fname = Json::requireString(m.toObject(),"foldername");
+ if(hasFabric){
+ if(fname == "fabric.mod.json"){
+ valid = true;
+ break;
+ }
+ }else{
+ //this cannot check for the recent mcmod.toml formats
+ if(fname == "mcmod.info"){
+ valid = true;
+ break;
+ }
+ }
+ }
+ if(!valid && hasFabric){
+ continue;
+ }
+
+ unsortedVersions.append(file);
+ }
+ auto orderSortPredicate = [](const IndexedVersion & a, const IndexedVersion & b) -> bool
+ {
+ //dates are in RFC 3339 format
+ return a.date > b.date;
+ };
+ std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
+ pack.versions = unsortedVersions;
+ pack.versionsLoaded = true;
+}
diff --git a/launcher/modplatform/flame/FlameModIndex.h b/launcher/modplatform/flame/FlameModIndex.h
new file mode 100644
index 00000000..0293bb23
--- /dev/null
+++ b/launcher/modplatform/flame/FlameModIndex.h
@@ -0,0 +1,50 @@
+//
+// Created by timoreo on 16/01/2022.
+//
+
+#pragma once
+#include <QList>
+#include <QMetaType>
+#include <QString>
+#include <QVector>
+#include <QNetworkAccessManager>
+#include <QObjectPtr.h>
+#include "net/NetJob.h"
+#include "BaseInstance.h"
+
+namespace FlameMod {
+ struct ModpackAuthor {
+ QString name;
+ QString url;
+ };
+
+ struct IndexedVersion {
+ int addonId;
+ int fileId;
+ QString version;
+ QVector<QString> mcVersion;
+ QString downloadUrl;
+ QString date;
+ QString fileName;
+ };
+
+ struct IndexedPack
+ {
+ int addonId;
+ QString name;
+ QString description;
+ QList<ModpackAuthor> authors;
+ QString logoName;
+ QString logoUrl;
+ QString websiteUrl;
+
+ bool versionsLoaded = false;
+ QVector<IndexedVersion> versions;
+ };
+
+ void loadIndexedPack(IndexedPack & m, QJsonObject & obj);
+ void loadIndexedPackVersions(IndexedPack &pack, QJsonArray &arr, const shared_qobject_ptr<QNetworkAccessManager> &network, BaseInstance *inst);
+
+}
+
+Q_DECLARE_METATYPE(FlameMod::IndexedPack)
diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.h b/launcher/modplatform/legacy_ftb/PackInstallTask.h
index 305635a1..a7395220 100644
--- a/launcher/modplatform/legacy_ftb/PackInstallTask.h
+++ b/launcher/modplatform/legacy_ftb/PackInstallTask.h
@@ -1,8 +1,8 @@
#pragma once
#include "InstanceTask.h"
#include "net/NetJob.h"
-#include "quazip.h"
-#include "quazipdir.h"
+#include <quazip/quazip.h>
+#include <quazip/quazipdir.h>
#include "meta/Index.h"
#include "meta/Version.h"
#include "meta/VersionList.h"
diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp
new file mode 100644
index 00000000..9017eb67
--- /dev/null
+++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp
@@ -0,0 +1,95 @@
+#include <QObject>
+#include "ModrinthPackIndex.h"
+
+#include "Json.h"
+#include "net/NetJob.h"
+#include "BaseInstance.h"
+#include "minecraft/MinecraftInstance.h"
+#include "minecraft/PackProfile.h"
+
+
+void Modrinth::loadIndexedPack(Modrinth::IndexedPack & pack, QJsonObject & obj)
+{
+ pack.addonId = Json::requireString(obj, "project_id");
+ pack.name = Json::requireString(obj, "title");
+ pack.websiteUrl = Json::ensureString(obj, "page_url", "");
+ pack.description = Json::ensureString(obj, "description", "");
+
+ pack.logoUrl = Json::requireString(obj, "icon_url");
+ pack.logoName = pack.addonId;
+
+ Modrinth::ModpackAuthor modAuthor;
+ modAuthor.name = Json::requireString(obj, "author");
+ modAuthor.url = "https://modrinth.com/user/"+modAuthor.name;
+ pack.author = modAuthor;
+}
+
+void Modrinth::loadIndexedPackVersions(Modrinth::IndexedPack & pack, QJsonArray & arr, const shared_qobject_ptr<QNetworkAccessManager>& network, BaseInstance * inst)
+{
+ QVector<Modrinth::IndexedVersion> unsortedVersions;
+ bool hasFabric = !((MinecraftInstance *)inst)->getPackProfile()->getComponentVersion("net.fabricmc.fabric-loader").isEmpty();
+ QString mcVersion = ((MinecraftInstance *)inst)->getPackProfile()->getComponentVersion("net.minecraft");
+
+ for(auto versionIter: arr) {
+ auto obj = versionIter.toObject();
+ Modrinth::IndexedVersion file;
+ file.addonId = Json::requireString(obj,"project_id") ;
+ file.fileId = Json::requireString(obj, "id");
+ file.date = Json::requireString(obj, "date_published");
+ auto versionArray = Json::requireArray(obj, "game_versions");
+ if (versionArray.empty()) {
+ continue;
+ }
+ for(auto mcVer : versionArray){
+ file.mcVersion.append(mcVer.toString());
+ }
+ auto loaders = Json::requireArray(obj,"loaders");
+ for(auto loader : loaders){
+ file.loaders.append(loader.toString());
+ }
+ file.version = Json::requireString(obj, "name");
+
+ auto files = Json::requireArray(obj, "files");
+ int i = 0;
+ while (files.count() > 1 && i < files.count()){
+ //try to resolve the correct file
+ auto parent = files[i].toObject();
+ auto fileName = Json::requireString(parent, "filename");
+ //avoid grabbing "dev" files
+ if(fileName.contains("javadocs",Qt::CaseInsensitive) || fileName.contains("sources",Qt::CaseInsensitive)){
+ i++;
+ continue;
+ }
+ //grab the correct mod loader
+ if(fileName.contains("forge",Qt::CaseInsensitive) || fileName.contains("fabric",Qt::CaseInsensitive) ){
+ if(hasFabric){
+ if(fileName.contains("forge",Qt::CaseInsensitive)){
+ i++;
+ continue;
+ }
+ }else{
+ if(fileName.contains("fabric",Qt::CaseInsensitive)){
+ i++;
+ continue;
+ }
+ }
+ }
+ break;
+ }
+ auto parent = files[i].toObject();
+ if(parent.contains("url")) {
+ file.downloadUrl = Json::requireString(parent, "url");
+ file.fileName = Json::requireString(parent, "filename");
+
+ unsortedVersions.append(file);
+ }
+ }
+ auto orderSortPredicate = [](const IndexedVersion & a, const IndexedVersion & b) -> bool
+ {
+ //dates are in RFC 3339 format
+ return a.date > b.date;
+ };
+ std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
+ pack.versions = unsortedVersions;
+ pack.versionsLoaded = true;
+}
diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.h b/launcher/modplatform/modrinth/ModrinthPackIndex.h
new file mode 100644
index 00000000..3a4cd270
--- /dev/null
+++ b/launcher/modplatform/modrinth/ModrinthPackIndex.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <QList>
+#include <QMetaType>
+#include <QString>
+#include <QVector>
+#include <QNetworkAccessManager>
+#include <QObjectPtr.h>
+#include "net/NetJob.h"
+#include "BaseInstance.h"
+
+namespace Modrinth {
+
+struct ModpackAuthor {
+ QString name;
+ QString url;
+};
+
+struct IndexedVersion {
+ QString addonId;
+ QString fileId;
+ QString version;
+ QVector<QString> mcVersion;
+ QString downloadUrl;
+ QString date;
+ QString fileName;
+ QVector<QString> loaders;
+};
+
+struct IndexedPack
+{
+ QString addonId;
+ QString name;
+ QString description;
+ ModpackAuthor author;
+ QString logoName;
+ QString logoUrl;
+ QString websiteUrl;
+
+ bool versionsLoaded = false;
+ QVector<IndexedVersion> versions;
+};
+
+void loadIndexedPack(IndexedPack & m, QJsonObject & obj);
+void loadIndexedPackVersions(IndexedPack &pack, QJsonArray &arr, const shared_qobject_ptr<QNetworkAccessManager> &network, BaseInstance *inst);
+}
+
+Q_DECLARE_METATYPE(Modrinth::IndexedPack)
diff --git a/launcher/modplatform/technic/SingleZipPackInstallTask.h b/launcher/modplatform/technic/SingleZipPackInstallTask.h
index 74f60941..4d1fcbff 100644
--- a/launcher/modplatform/technic/SingleZipPackInstallTask.h
+++ b/launcher/modplatform/technic/SingleZipPackInstallTask.h
@@ -18,7 +18,7 @@
#include "InstanceTask.h"
#include "net/NetJob.h"
-#include "quazip.h"
+#include <quazip/quazip.h>
#include <QFutureWatcher>
#include <QStringList>
diff --git a/launcher/modplatform/technic/TechnicPackProcessor.cpp b/launcher/modplatform/technic/TechnicPackProcessor.cpp
index 52979b7c..c45061ac 100644
--- a/launcher/modplatform/technic/TechnicPackProcessor.cpp
+++ b/launcher/modplatform/technic/TechnicPackProcessor.cpp
@@ -19,9 +19,9 @@
#include <Json.h>
#include <minecraft/MinecraftInstance.h>
#include <minecraft/PackProfile.h>
-#include <quazip.h>
-#include <quazipdir.h>
-#include <quazipfile.h>
+#include <quazip/quazip.h>
+#include <quazip/quazipdir.h>
+#include <quazip/quazipfile.h>
#include <settings/INISettingsObject.h>
#include <memory>
diff --git a/launcher/net/PasteUpload.cpp b/launcher/net/PasteUpload.cpp
index 4b69b68a..52b82a0e 100644
--- a/launcher/net/PasteUpload.cpp
+++ b/launcher/net/PasteUpload.cpp
@@ -8,44 +8,34 @@
#include <QJsonDocument>
#include <QFile>
-PasteUpload::PasteUpload(QWidget *window, QString text, QString key) : m_window(window)
+PasteUpload::PasteUpload(QWidget *window, QString text, QString url) : m_window(window), m_uploadUrl(url), m_text(text.toUtf8())
{
- m_key = key;
- QByteArray temp;
- QJsonObject topLevelObj;
- QJsonObject sectionObject;
- sectionObject.insert("contents", text);
- QJsonArray sectionArray;
- sectionArray.append(sectionObject);
- topLevelObj.insert("description", "Log Upload");
- topLevelObj.insert("sections", sectionArray);
- QJsonDocument docOut;
- docOut.setObject(topLevelObj);
- m_jsonContent = docOut.toJson();
}
PasteUpload::~PasteUpload()
{
}
-bool PasteUpload::validateText()
-{
- return m_jsonContent.size() <= maxSize();
-}
-
void PasteUpload::executeTask()
{
- QNetworkRequest request(QUrl("https://api.paste.ee/v1/pastes"));
+ QNetworkRequest request{QUrl(m_uploadUrl)};
request.setHeader(QNetworkRequest::UserAgentHeader, BuildConfig.USER_AGENT_UNCACHED);
- request.setRawHeader("Content-Type", "application/json");
- request.setRawHeader("Content-Length", QByteArray::number(m_jsonContent.size()));
- request.setRawHeader("X-Auth-Token", m_key.toStdString().c_str());
+ QHttpMultiPart *multiPart = new QHttpMultiPart{QHttpMultiPart::FormDataType};
+
+ QHttpPart filePart;
+ filePart.setBody(m_text);
+ filePart.setHeader(QNetworkRequest::ContentTypeHeader, "text/plain");
+ filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"file\"; filename=\"log.txt\"");
- QNetworkReply *rep = APPLICATION->network()->post(request, m_jsonContent);
+ multiPart->append(filePart);
+
+ QNetworkReply *rep = APPLICATION->network()->post(request, multiPart);
+ multiPart->setParent(rep);
m_reply = std::shared_ptr<QNetworkReply>(rep);
- setStatus(tr("Uploading to paste.ee"));
+ setStatus(tr("Uploading to %1").arg(m_uploadUrl));
+
connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress);
connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished()));
@@ -61,45 +51,23 @@ void PasteUpload::downloadError(QNetworkReply::NetworkError error)
void PasteUpload::downloadFinished()
{
QByteArray data = m_reply->readAll();
- // if the download succeeded
- if (m_reply->error() == QNetworkReply::NetworkError::NoError)
+ int statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ if (m_reply->error() != QNetworkReply::NetworkError::NoError)
{
+ emitFailed(tr("Network error: %1").arg(m_reply->errorString()));
m_reply.reset();
- QJsonParseError jsonError;
- QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
- if (jsonError.error != QJsonParseError::NoError)
- {
- emitFailed(jsonError.errorString());
- return;
- }
- if (!parseResult(doc))
- {
- emitFailed(tr("paste.ee returned an error. Please consult the logs for more information"));
- return;
- }
+ return;
}
- // else the download failed
- else
+ else if (statusCode != 200 && statusCode != 201)
{
- emitFailed(QString("Network error: %1").arg(m_reply->errorString()));
+ QString reasonPhrase = m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
+ emitFailed(tr("Error: %1 returned unexpected status code %2 %3").arg(m_uploadUrl).arg(statusCode).arg(reasonPhrase));
+ qCritical() << m_uploadUrl << " returned unexpected status code " << statusCode << " with body: " << data;
m_reply.reset();
return;
}
- emitSucceeded();
-}
-bool PasteUpload::parseResult(QJsonDocument doc)
-{
- auto object = doc.object();
- auto status = object.value("success").toBool();
- if (!status)
- {
- qCritical() << "paste.ee reported error:" << QString(object.value("error").toString());
- return false;
- }
- m_pasteLink = object.value("link").toString();
- m_pasteID = object.value("id").toString();
- qDebug() << m_pasteLink;
- return true;
+ m_pasteLink = QString::fromUtf8(data).trimmed();
+ emitSucceeded();
}
-
diff --git a/launcher/net/PasteUpload.h b/launcher/net/PasteUpload.h
index 5514e058..62b2dc36 100644
--- a/launcher/net/PasteUpload.h
+++ b/launcher/net/PasteUpload.h
@@ -8,37 +8,21 @@ class PasteUpload : public Task
{
Q_OBJECT
public:
- PasteUpload(QWidget *window, QString text, QString key = "public");
+ PasteUpload(QWidget *window, QString text, QString url);
virtual ~PasteUpload();
QString pasteLink()
{
return m_pasteLink;
}
- QString pasteID()
- {
- return m_pasteID;
- }
- int maxSize()
- {
- // 2MB for paste.ee - public
- if(m_key == "public")
- return 1024*1024*2;
- // 12MB for paste.ee - with actual key
- return 1024*1024*12;
- }
- bool validateText();
protected:
virtual void executeTask();
private:
- bool parseResult(QJsonDocument doc);
- QString m_error;
QWidget *m_window;
- QString m_pasteID;
QString m_pasteLink;
- QString m_key;
- QByteArray m_jsonContent;
+ QString m_uploadUrl;
+ QByteArray m_text;
std::shared_ptr<QNetworkReply> m_reply;
public
slots:
diff --git a/launcher/news/NewsChecker.cpp b/launcher/news/NewsChecker.cpp
index 4f4359b8..6724950f 100644
--- a/launcher/news/NewsChecker.cpp
+++ b/launcher/news/NewsChecker.cpp
@@ -70,7 +70,7 @@ void NewsChecker::rssDownloadFinished()
}
// If the parsing succeeded, read it.
- QDomNodeList items = doc.elementsByTagName("item");
+ QDomNodeList items = doc.elementsByTagName("entry");
m_newsEntries.clear();
for (int i = 0; i < items.length(); i++)
{
diff --git a/launcher/news/NewsEntry.cpp b/launcher/news/NewsEntry.cpp
index 7eff657b..137703d1 100644
--- a/launcher/news/NewsEntry.cpp
+++ b/launcher/news/NewsEntry.cpp
@@ -24,18 +24,14 @@ NewsEntry::NewsEntry(QObject* parent) :
this->title = tr("Untitled");
this->content = tr("No content.");
this->link = "";
- this->author = tr("Unknown Author");
- this->pubDate = QDateTime::currentDateTime();
}
-NewsEntry::NewsEntry(const QString& title, const QString& content, const QString& link, const QString& author, const QDateTime& pubDate, QObject* parent) :
+NewsEntry::NewsEntry(const QString& title, const QString& content, const QString& link, QObject* parent) :
QObject(parent)
{
this->title = title;
this->content = content;
this->link = link;
- this->author = author;
- this->pubDate = pubDate;
}
/*!
@@ -59,19 +55,11 @@ bool NewsEntry::fromXmlElement(const QDomElement& element, NewsEntry* entry, QSt
{
QString title = childValue(element, "title", tr("Untitled"));
QString content = childValue(element, "description", tr("No content."));
- QString link = childValue(element, "link");
- QString author = childValue(element, "dc:creator", tr("Unknown Author"));
- QString pubDateStr = childValue(element, "pubDate");
-
- // FIXME: For now, we're just ignoring timezones. We assume that all time zones in the RSS feed are the same.
- QString dateFormat("ddd, dd MMM yyyy hh:mm:ss");
- QDateTime pubDate = QDateTime::fromString(pubDateStr, dateFormat);
+ QString link = childValue(element, "id");
entry->title = title;
entry->content = content;
entry->link = link;
- entry->author = author;
- entry->pubDate = pubDate;
return true;
}
diff --git a/launcher/news/NewsEntry.h b/launcher/news/NewsEntry.h
index 0dbc70a5..1fe95623 100644
--- a/launcher/news/NewsEntry.h
+++ b/launcher/news/NewsEntry.h
@@ -18,8 +18,6 @@
#include <QObject>
#include <QString>
#include <QDomElement>
-#include <QDateTime>
-
#include <memory>
class NewsEntry : public QObject
@@ -36,7 +34,7 @@ public:
* Constructs a new news entry.
* Note that content may contain HTML.
*/
- NewsEntry(const QString& title, const QString& content, const QString& link, const QString& author, const QDateTime& pubDate, QObject* parent=0);
+ NewsEntry(const QString& title, const QString& content, const QString& link, QObject* parent=0);
/*!
* Attempts to load information from the given XML element into the given news entry pointer.
@@ -53,12 +51,6 @@ public:
//! URL to the post.
QString link;
-
- //! The post's author.
- QString author;
-
- //! The date and time that this post was published.
- QDateTime pubDate;
};
typedef std::shared_ptr<NewsEntry> NewsEntryPtr;
diff --git a/launcher/notifications/NotificationChecker.cpp b/launcher/notifications/NotificationChecker.cpp
index c08bcdcb..10b91691 100644
--- a/launcher/notifications/NotificationChecker.cpp
+++ b/launcher/notifications/NotificationChecker.cpp
@@ -44,7 +44,7 @@ void NotificationChecker::checkForNotifications()
if (!m_notificationsUrl.isValid())
{
qCritical() << "Failed to check for notifications. No notifications URL set."
- << "If you'd like to use MultiMC's notification system, please pass the "
+ << "If you'd like to use PolyMC's notification system, please pass the "
"URL to CMake at compile time.";
return;
}
diff --git a/launcher/package/rpm/MultiMC5.spec b/launcher/package/rpm/MultiMC5.spec
deleted file mode 100644
index 4b7e5002..00000000
--- a/launcher/package/rpm/MultiMC5.spec
+++ /dev/null
@@ -1,65 +0,0 @@
-Name: MultiMC5
-Version: 1.4
-Release: 4%{?dist}
-Summary: A local install wrapper for MultiMC
-
-License: ASL 2.0
-URL: https://multimc.org
-ExclusiveArch: %{ix86} x86_64
-
-BuildRequires: desktop-file-utils
-BuildRequires: libappstream-glib
-Requires: zenity %{?suse_version:lib}qt5-qtbase wget xrandr
-Provides: multimc = %{version}
-Provides: MultiMC = %{version}
-Provides: multimc5 = %{version}
-
-
-
-%description
-A local install wrapper for MultiMC
-
-%prep
-
-
-%build
-
-%install
-mkdir -p %{buildroot}/opt/multimc
-install -m 0644 ../ubuntu/multimc/opt/multimc/icon.svg %{buildroot}/opt/multimc/icon.svg
-install -m 0755 ../ubuntu/multimc/opt/multimc/run.sh %{buildroot}/opt/multimc/run.sh
-mkdir -p %{buildroot}/%{_datadir}/applications
-desktop-file-install --dir=%{buildroot}%{_datadir}/applications ../ubuntu/multimc/usr/share/applications/multimc.desktop
-
-mkdir -p %{buildroot}/%{_datadir}/metainfo
-install -m 0644 ../ubuntu/multimc/usr/share/metainfo/multimc.metainfo.xml %{buildroot}/%{_metainfodir}/multimc.metainfo.xml
-mkdir -p %{buildroot}/%{_mandir}/man1
-install -m 0644 ../ubuntu/multimc/usr/share/man/man1/multimc.1 %{buildroot}/%{_mandir}/man1/multimc.1
-
-%check
-appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/multimc.metainfo.xml
-
-%files
-%dir /opt/multimc
-/opt/multimc/icon.svg
-/opt/multimc/run.sh
-%{_datadir}/applications/multimc.desktop
-%{_metainfodir}/multimc.metainfo.xml
-%dir %{_mandir}/man1
-%{_mandir}/man1/multimc.1*
-
-%changelog
-* Fri Jan 28 2022 Jan Drögehoff <sentrycraft123@gmail.com> - 1.4-4
-- Update spec to support OpenSuse and conform to Fedora guidelines
-
-* Sun Oct 03 2021 imperatorstorm <30777770+ImperatorStorm@users.noreply.github.com>
-- added manpage
-
-* Tue Jun 01 2021 kb1000 <fedora@kb1000.de> - 1.4-2
-- Add xrandr to the dependencies
-
-* Tue Dec 08 00:34:35 CET 2020 joshua-stone <joshua.gage.stone@gmail.com>
-- Add metainfo.xml for improving package metadata
-
-* Wed Nov 25 22:53:59 CET 2020 kb1000 <fedora@kb1000.de>
-- Initial version of the RPM package, based on the Ubuntu package
diff --git a/launcher/package/rpm/README.md b/launcher/package/rpm/README.md
deleted file mode 100644
index 0c2b1e49..00000000
--- a/launcher/package/rpm/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# What is this?
-A simple RPM package for MultiMC that contains a script that downloads and installs real MultiMC on Red Hat based systems.
-
-It contains a `.desktop` file, a `.metainfo.xml` file, an icon, and a simple script that does the heavy lifting.
-
-# How to build this?
-You need the `rpm-build` package. Switch into this directory, then run:
-```
-rpmbuild --build-in-place -bb MultiMC5.spec
-```
-
-Replace the version with whatever is appropriate.
diff --git a/launcher/package/ubuntu/README.md b/launcher/package/ubuntu/README.md
deleted file mode 100644
index ddc97ae6..00000000
--- a/launcher/package/ubuntu/README.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# What is this?
-A simple Ubuntu package for MultiMC that contains a script that downloads and installs real MultiMC on Ubuntu based systems.
-
-It contains a `.desktop` file, an icon, and a simple script that does the heavy lifting.
-
-This is also the source for the files in the [RPM package](../rpm). If you rename, create or delete files here, you'll likely also have to update the RPM spec file there.
-
-# How to build this?
-You need dpkg utils. Rename the `multimc` folder to `multimc_1.6-1` and then run:
-```
-fakeroot dpkg-deb --build multimc_1.6-1
-```
-
-Replace the version with whatever is appropriate.
diff --git a/launcher/package/ubuntu/multimc/DEBIAN/control b/launcher/package/ubuntu/multimc/DEBIAN/control
deleted file mode 100644
index bfa3f1f2..00000000
--- a/launcher/package/ubuntu/multimc/DEBIAN/control
+++ /dev/null
@@ -1,12 +0,0 @@
-Package: multimc
-Version: 1.6-2
-Architecture: all
-Maintainer: Petr Mrázek <peterix@gmail.com>
-Section: games
-Priority: optional
-Installed-Size: 75
-Depends: zenity, desktop-file-utils, libqt5widgets5, libqt5gui5, libqt5network5, libqt5core5a, libqt5xml5, libqt5concurrent5, wget
-Recommends: openjdk-8-jre
-Homepage: http://multimc.org
-Description: A local install wrapper for MultiMC
-
diff --git a/launcher/package/ubuntu/multimc/DEBIAN/postrm b/launcher/package/ubuntu/multimc/DEBIAN/postrm
deleted file mode 100755
index f9bbc8a7..00000000
--- a/launcher/package/ubuntu/multimc/DEBIAN/postrm
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-set -e
-update-desktop-database
diff --git a/launcher/package/ubuntu/multimc/opt/multimc/icon.svg b/launcher/package/ubuntu/multimc/opt/multimc/icon.svg
deleted file mode 100644
index 8bb0e289..00000000
--- a/launcher/package/ubuntu/multimc/opt/multimc/icon.svg
+++ /dev/null
@@ -1,353 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="68.26667"
- height="68.26667"
- id="svg4427"
- version="1.1"
- inkscape:version="0.92.1 r"
- sodipodi:docname="multimc-smooth-biginfinity.svg"
- inkscape:export-filename="/home/peterix/playground/MultiMC-icons/multimc-smooth-biginfinity.png"
- inkscape:export-xdpi="180"
- inkscape:export-ydpi="180">
- <defs
- id="defs4429">
- <linearGradient
- inkscape:collect="always"
- id="linearGradient4809">
- <stop
- style="stop-color:#98c867;stop-opacity:1"
- offset="0"
- id="stop4805" />
- <stop
- style="stop-color:#5c9a33;stop-opacity:1"
- offset="1"
- id="stop4807" />
- </linearGradient>
- <linearGradient
- id="linearGradient5668"
- inkscape:collect="always">
- <stop
- id="stop5670"
- offset="0"
- style="stop-color:#75b54b;stop-opacity:1;" />
- <stop
- id="stop5672"
- offset="1"
- style="stop-color:#75b54b;stop-opacity:0.6" />
- </linearGradient>
- <linearGradient
- id="linearGradient5084"
- inkscape:collect="always">
- <stop
- id="stop5086"
- offset="0"
- style="stop-color:#000000;stop-opacity:0.8" />
- <stop
- id="stop5088"
- offset="1"
- style="stop-color:#000000;stop-opacity:0.35" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5668"
- id="linearGradient5072"
- x1="6.7342591"
- y1="28.510933"
- x2="50.506943"
- y2="61.773685"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(-0.01532073,-0.00938002)" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5084"
- id="linearGradient5082"
- x1="14.312115"
- y1="9.7948904"
- x2="44.097023"
- y2="82.973114"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5668"
- id="linearGradient3281"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(-0.01532073,-0.00938002)"
- x1="6.7342591"
- y1="28.510933"
- x2="50.506943"
- y2="61.773685" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5668"
- id="linearGradient3283"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(-0.01532073,-0.00938002)"
- x1="6.7342591"
- y1="28.510933"
- x2="50.506943"
- y2="61.773685" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5668"
- id="linearGradient3286"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(1.2671525,0,0,0.89790119,-0.01941371,-0.00842234)"
- x1="6.7342591"
- y1="28.510933"
- x2="50.506943"
- y2="61.773685" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5084"
- id="linearGradient3288"
- gradientUnits="userSpaceOnUse"
- x1="14.312115"
- y1="9.7948904"
- x2="44.097023"
- y2="82.973114" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5084"
- id="linearGradient3290"
- gradientUnits="userSpaceOnUse"
- x1="14.312115"
- y1="9.7948904"
- x2="44.097023"
- y2="82.973114" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5084"
- id="linearGradient3293"
- gradientUnits="userSpaceOnUse"
- x1="14.312115"
- y1="9.7948904"
- x2="44.097023"
- y2="82.973114"
- gradientTransform="scale(1.2671525,0.89790119)" />
- <linearGradient
- inkscape:collect="always"
- id="linearGradient5580">
- <stop
- style="stop-color:#000000;stop-opacity:0.0627451"
- offset="0"
- id="stop5576" />
- <stop
- style="stop-color:#322217;stop-opacity:0.58823532"
- offset="1"
- id="stop5578" />
- </linearGradient>
- <linearGradient
- id="linearGradient3999"
- inkscape:collect="always">
- <stop
- id="stop3995"
- offset="0"
- style="stop-color:#a3704b;stop-opacity:1" />
- <stop
- id="stop3997"
- offset="1"
- style="stop-color:#6a4a33;stop-opacity:1" />
- </linearGradient>
- <linearGradient
- id="linearGradient2727"
- inkscape:collect="always">
- <stop
- id="stop2723"
- offset="0"
- style="stop-color:#966c4a;stop-opacity:1" />
- <stop
- id="stop2725"
- offset="1"
- style="stop-color:#593d29;stop-opacity:1" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient2727"
- id="linearGradient2050"
- gradientUnits="userSpaceOnUse"
- x1="36.546478"
- y1="33.80484"
- x2="86.415741"
- y2="97.065842" />
- <radialGradient
- inkscape:collect="always"
- xlink:href="#linearGradient3999"
- id="radialGradient2052"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(-9.105292e-4,-0.00104444)"
- cx="34.133331"
- cy="34.133335"
- fx="34.133331"
- fy="34.133335"
- r="29.866665" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5580"
- id="linearGradient2140"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(-0.0010513,-9.083059e-4)"
- x1="29.866674"
- y1="29.867579"
- x2="38.400005"
- y2="38.400913" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5084"
- id="linearGradient4790"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(1.2671525,0,0,0.89790119,-0.82864077,-1.0012743)"
- x1="14.312115"
- y1="9.7948904"
- x2="44.097023"
- y2="82.973114" />
- <radialGradient
- inkscape:collect="always"
- xlink:href="#linearGradient4809"
- id="radialGradient4803"
- cx="-42.66758"
- cy="-34.134373"
- fx="-42.66758"
- fy="-34.134373"
- r="34.132812"
- gradientTransform="matrix(1.7500268,0.1250019,-0.01781176,0.24936465,95.393964,18.110151)"
- gradientUnits="userSpaceOnUse" />
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="3.6203867"
- inkscape:cx="52.171166"
- inkscape:cy="11.292073"
- inkscape:current-layer="layer1"
- showgrid="false"
- inkscape:document-units="px"
- inkscape:grid-bbox="true"
- inkscape:window-width="1368"
- inkscape:window-height="905"
- inkscape:window-x="2452"
- inkscape:window-y="723"
- inkscape:window-maximized="0"
- inkscape:snap-bbox="true"
- inkscape:bbox-paths="false"
- inkscape:snap-bbox-edge-midpoints="false"
- inkscape:bbox-nodes="true"
- inkscape:snap-bbox-midpoints="false"
- inkscape:snap-smooth-nodes="true"
- inkscape:snap-midpoints="false"
- inkscape:snap-intersection-paths="true"
- inkscape:object-paths="true"
- inkscape:snap-object-midpoints="true"
- inkscape:snap-text-baseline="true"
- inkscape:snap-center="true">
- <inkscape:grid
- type="xygrid"
- id="grid4446"
- empspacing="16"
- visible="true"
- enabled="true"
- snapvisiblegridlinesonly="true"
- spacingx="4.2666667"
- spacingy="4.2666667"
- originx="0"
- originy="0" />
- </sodipodi:namedview>
- <metadata
- id="metadata4432">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- id="layer1"
- inkscape:label="Layer 1"
- inkscape:groupmode="layer">
- <g
- id="g2048"
- transform="translate(9.113e-4,0.00104183)">
- <rect
- rx="8.5333338"
- ry="8.5333338"
- style="fill:url(#linearGradient2050);fill-opacity:1;stroke:none;stroke-width:17.06666756"
- id="rect2026"
- width="68.26667"
- height="68.26667"
- x="-1.3322676e-15"
- y="3.0270508e-06" />
- <rect
- rx="4.2666626"
- y="4.2656283"
- x="4.2657552"
- height="59.733334"
- width="59.73333"
- id="rect2028"
- style="fill:url(#radialGradient2052);fill-opacity:1;stroke:none;stroke-width:14.93333435"
- ry="4.2666669" />
- <path
- inkscape:connector-curvature="0"
- id="path4811"
- d="m 4.2669272,4.2645856 -9.11e-4,8.5333334 h 4.267577 v 4.267579 h 8.5332038 v 4.265625 h 4.265625 V 8.5322946 H 25.6 v 8.5332034 h 4.265625 v -4.267579 h 4.267578 v 8.533204 h 4.265625 v -4.265625 h 4.267578 v 4.265625 h 4.267579 v -4.265625 h 4.265624 v -4.267579 h 4.267579 v 4.267579 h 8.533203 l -1.3e-4,-12.8009124 z"
- style="opacity:0.6;fill:#593d29;fill-opacity:1;stroke:none;stroke-width:17.06666756"
- sodipodi:nodetypes="ccccccccccccccccccccccccccc" />
- <path
- style="fill:url(#radialGradient4803);fill-opacity:1;stroke:none;stroke-width:17.06666756"
- d="m 8.5329442,-0.0018207 c -4.7274675,0 -8.5332035,3.805736 -8.5332035,8.533203 v 4.2675787 h 4.265625 V 8.5313823 c 0,-0.521698 0.105433,-1.01339 0.27539,-1.47461 -0.169616,0.460814 -0.27539,0.953462 -0.27539,1.47461 h 4.2675785 v 4.2675787 h 4.2656248 4.267578 v 4.265625 h 4.265625 V 12.798961 8.5313823 4.2657573 h 4.267578 v 4.265625 4.2675787 h 4.265625 V 8.5313823 h 4.267578 v 4.2675787 4.265625 h 4.265625 v -4.265625 h 4.267578 v 4.265625 h 4.267579 v -4.265625 h 4.265624 V 8.5313823 h 4.267579 v 4.2675787 h 4.265625 4.267578 V 8.5313823 h 4.265625 c 0,-4.727467 -3.805737,-8.533203 -8.533203,-8.533203 z m -3.019531,5.513671 c -0.318089,0.317888 -0.570428,0.695824 -0.7753915,1.101563 0.2048795,-0.405231 0.4576385,-0.784012 0.7753915,-1.101563 z"
- id="path4794"
- inkscape:connector-curvature="0" />
- <path
- style="opacity:1;fill:url(#linearGradient2140);fill-opacity:1;stroke:none;stroke-width:17.06666756"
- d="m 8.5322887,-9.083059e-4 c -4.72747,0 -8.5332,3.8057359059 -8.5332,8.5332029059 V 59.731515 c 0,4.727467 3.80573,8.535156 8.5332,8.535156 H 59.731502 c 4.72747,0 8.5332,-3.807689 8.5332,-8.535156 V 8.5322946 c 0,-4.727467 -3.80573,-8.5332029059 -8.5332,-8.5332029059 z m 0,4.2675779059 H 59.731502 c 2.36373,0 4.26758,1.901892 4.26758,4.265625 V 59.731515 c 0,2.363733 -1.90385,4.267578 -4.26758,4.267578 H 8.5322887 c -2.36373,0 -4.26758,-1.903845 -4.26758,-4.267578 V 8.5322946 c 0,-2.363733 1.90385,-4.265625 4.26758,-4.265625 z"
- id="path2046"
- inkscape:connector-curvature="0" />
- </g>
- <g
- id="g1092">
- <path
- inkscape:connector-curvature="0"
- id="path4786"
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.18933868px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient4790);fill-opacity:1;stroke:none;stroke-width:1.06666672;opacity:0.5"
- d="m 38.886673,44.940882 c -0.974277,-0.801673 -2.231353,-2.137814 -3.771231,-4.008427 -2.105641,2.672298 -4.085536,4.598569 -5.939688,5.778816 -2.325625,1.425227 -5.295467,2.137836 -8.909534,2.137828 -4.242656,8e-6 -7.762467,-1.124578 -10.5594458,-3.37376 C 6.7526311,43.114834 5.275567,39.986037 5.2755773,36.088937 5.275567,32.347763 6.7526311,29.207831 9.7067742,26.669132 12.346618,24.419991 15.897857,23.295407 20.360501,23.295373 c 2.294138,3.4e-5 4.289747,0.334069 5.986829,1.002107 1.979863,0.73491 3.645488,1.737016 4.996881,3.00632 1.257039,1.135751 2.514115,2.471891 3.771231,4.008428 2.105563,-2.672257 4.085457,-4.598527 5.939689,-5.778816 2.325544,-1.425186 5.295385,-2.137794 8.909533,-2.137828 4.242577,3.4e-5 7.762388,1.124618 10.559447,3.37376 2.954063,2.360546 4.431127,5.489343 4.431197,9.386401 -7e-5,3.741216 -1.477134,6.881147 -4.431197,9.419806 -2.639925,2.24918 -6.191163,3.373767 -10.653727,3.373758 -2.294219,9e-6 -4.289826,-0.334026 -5.98683,-1.002106 -1.697101,-0.601255 -3.362726,-1.603361 -4.996881,-3.006321 M 19.747676,44.473233 c 5.185412,1.1e-5 9.333763,-2.672271 12.445062,-8.016856 -3.991253,-5.834464 -8.139602,-8.751705 -12.445062,-8.751733 -3.142715,2.8e-5 -5.515446,0.801713 -7.118198,2.405057 -1.728498,1.71474 -2.592737,3.707818 -2.592722,5.979236 -1.5e-5,2.494152 0.864224,4.509499 2.592722,6.046046 1.759887,1.558846 4.132618,2.338261 7.118198,2.33825 M 50.483209,27.77145 c -4.682663,2.9e-5 -8.831013,2.672312 -12.445062,8.016856 3.959745,5.834503 8.108095,8.751746 12.445062,8.751733 3.142633,1.3e-5 5.515364,-0.801671 7.118198,-2.405056 1.728416,-1.714701 2.592656,-3.707778 2.592722,-5.979238 -6.6e-5,-2.49411 -0.864306,-4.509456 -2.592722,-6.046044 -1.759968,-1.558805 -4.132699,-2.338222 -7.118198,-2.338251" />
- <path
- d="m 39.715314,45.942156 c -0.974277,-0.801673 -2.231353,-2.137814 -3.771231,-4.008427 -2.105641,2.672298 -4.085536,4.598569 -5.939688,5.778816 -2.325625,1.425227 -5.295467,2.137836 -8.909534,2.137828 -4.242656,8e-6 -7.762467,-1.124578 -10.559446,-3.37376 -2.9541431,-2.360505 -4.4312072,-5.489302 -4.4311969,-9.386402 -1.03e-5,-3.741174 1.4770538,-6.881106 4.4311969,-9.419805 2.639844,-2.249141 6.191083,-3.373725 10.653727,-3.373759 2.294138,3.4e-5 4.289747,0.334069 5.986829,1.002107 1.979863,0.73491 3.645488,1.737016 4.996881,3.00632 1.257039,1.135751 2.514115,2.471891 3.771231,4.008428 2.105563,-2.672257 4.085457,-4.598527 5.939689,-5.778816 2.325544,-1.425186 5.295385,-2.137794 8.909533,-2.137828 4.242577,3.4e-5 7.762388,1.124618 10.559447,3.37376 2.954063,2.360546 4.431127,5.489343 4.431197,9.386401 -7e-5,3.741216 -1.477134,6.881147 -4.431197,9.419806 -2.639925,2.24918 -6.191163,3.373767 -10.653727,3.373758 -2.294219,9e-6 -4.289826,-0.334026 -5.98683,-1.002106 -1.697101,-0.601255 -3.362726,-1.603361 -4.996881,-3.006321 M 20.576317,45.474507 c 5.185412,1.1e-5 9.333763,-2.672271 12.445062,-8.016856 -3.991253,-5.834464 -8.139602,-8.751705 -12.445062,-8.751733 -3.142715,2.8e-5 -5.515446,0.801713 -7.118198,2.405057 -1.728498,1.71474 -2.592737,3.707818 -2.592722,5.979236 -1.5e-5,2.494152 0.864224,4.509499 2.592722,6.046046 1.759887,1.558846 4.132618,2.338261 7.118198,2.33825 M 51.31185,28.772724 c -4.682663,2.9e-5 -8.831013,2.672312 -12.445062,8.016856 3.959745,5.834503 8.108095,8.751746 12.445062,8.751733 3.142633,1.3e-5 5.515364,-0.801671 7.118198,-2.405056 1.728416,-1.714701 2.592656,-3.707778 2.592722,-5.979238 -6.6e-5,-2.49411 -0.864306,-4.509456 -2.592722,-6.046044 C 56.67008,29.55217 54.297349,28.772753 51.31185,28.772724"
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.18933868px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3293);fill-opacity:1;stroke:none;stroke-width:1.06666672;opacity:0.5"
- id="path3279"
- inkscape:connector-curvature="0" />
- <path
- d="m 37.904564,42.951873 c -0.974278,-0.801672 -2.231352,-2.137814 -3.771231,-4.008428 -2.105642,2.672298 -4.085537,4.598568 -5.939688,5.778817 -2.325625,1.425227 -5.295466,2.137836 -8.909534,2.137828 -4.242656,8e-6 -7.762467,-1.124577 -10.5594464,-3.37376 -2.9541428,-2.360505 -4.4312068,-5.489302 -4.4311963,-9.386401 -1.05e-5,-3.741175 1.4770535,-6.881107 4.4311963,-9.419805 2.6398444,-2.249142 6.1910824,-3.373727 10.6537284,-3.37376 2.294137,3.3e-5 4.289745,0.334068 5.986829,1.002107 1.979863,0.734909 3.645487,1.737016 4.99688,3.00632 1.257039,1.13575 2.514116,2.471891 3.771231,4.008428 2.105562,-2.672257 4.085456,-4.598528 5.939689,-5.778817 2.325544,-1.425185 5.295387,-2.137795 8.909534,-2.137828 4.242576,3.3e-5 7.762387,1.12462 10.559446,3.373761 2.954062,2.360545 4.431127,5.489343 4.431197,9.386401 -7e-5,3.741216 -1.477135,6.881148 -4.431197,9.419805 -2.639924,2.249182 -6.191164,3.373767 -10.653728,3.37376 -2.294217,7e-6 -4.289826,-0.334028 -5.986828,-1.002107 -1.697101,-0.601254 -3.362727,-1.603361 -4.996882,-3.006321 m -19.138997,-0.46765 c 5.185412,1.3e-5 9.333762,-2.67227 12.445062,-8.016856 -3.991252,-5.834462 -8.139602,-8.751704 -12.445062,-8.751733 -3.142714,2.9e-5 -5.515444,0.801714 -7.118198,2.405056 -1.7284972,1.714743 -2.5927368,3.707819 -2.5927216,5.979239 -1.52e-5,2.49415 0.8642244,4.509496 2.5927216,6.046045 1.759888,1.558845 4.132618,2.338262 7.118198,2.338249 M 49.5011,25.782442 c -4.682663,2.8e-5 -8.831014,2.672311 -12.445063,8.016855 3.959745,5.834504 8.108096,8.751745 12.445063,8.751733 3.142634,1.2e-5 5.515365,-0.801673 7.118198,-2.405056 1.728417,-1.7147 2.592657,-3.707778 2.592721,-5.979238 -6.4e-5,-2.49411 -0.864304,-4.509456 -2.592721,-6.046046 C 54.85933,26.561886 52.486599,25.78247 49.5011,25.782442"
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.18933868px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3286);fill-opacity:1;stroke:none;stroke-width:1.06666672"
- id="path3272"
- inkscape:connector-curvature="0" />
- <path
- sodipodi:nodetypes="ccscsccccccccccccccccccccscscccccsccscccccc"
- inkscape:connector-curvature="0"
- id="text5100"
- d="m 19.4,21.166667 c -4.462644,3.3e-5 -8.026822,1.150858 -10.6666667,3.4 -2.9541428,2.538698 -4.4333436,5.658825 -4.4333333,9.4 -1.03e-5,3.897098 1.4791905,7.039495 4.4333333,9.4 -1.622701,-2.044271 -2.433341,-4.51168 -2.4333333,-7.4 -1.03e-5,-3.741175 1.4791905,-6.861302 4.433333,-9.4 2.639845,-2.249142 6.204023,-3.399967 10.666667,-3.4 2.294138,3.3e-5 4.302916,0.365295 6,1.033333 1.979862,0.73491 3.615274,1.730695 4.966667,3 0.06836,0.06177 0.131637,0.137049 0.2,0.2 -0.731813,-0.797005 -1.468213,-1.538822 -2.2,-2.2 -1.351393,-1.269305 -2.986805,-2.26509 -4.966667,-3 -1.697084,-0.668038 -3.705862,-1.0333 -6,-1.033333 z m 29.6,0.1 c -3.614148,3.3e-5 -6.574457,0.74148 -8.9,2.166666 -1.818222,1.157367 -3.923451,3.291388 -5.983333,5.883334 0.618278,0.658774 1.248369,1.377605 1.866666,2.133333 2.105562,-2.672257 4.262434,-4.836378 6.116667,-6.016667 2.325543,-1.425186 5.285852,-2.166633 8.9,-2.166666 4.242576,3.3e-5 7.769607,1.150858 10.566667,3.4 -0.570388,-0.722129 -1.227721,-1.382884 -2,-2 C 56.769607,22.417525 53.242576,21.2667 49,21.266667 Z m 8.866667,8.1 c 0.9092,1.305235 1.366619,2.857751 1.366666,4.666666 -6.5e-5,2.271461 -0.871584,4.285301 -2.6,6 -1.602834,1.603384 -3.957366,2.400012 -7.1,2.4 -2.653707,8e-6 -5.320858,-1.032242 -7.833333,-3.216666 3.136636,3.509305 6.469807,5.216676 9.833333,5.216666 3.142634,1.2e-5 5.497166,-0.796616 7.1,-2.4 1.728416,-1.714699 2.599935,-3.728539 2.6,-6 -6.5e-5,-2.49411 -0.871584,-4.496744 -2.6,-6.033333 -0.24943,-0.220921 -0.49262,-0.443723 -0.766666,-0.633333 z m -26.633334,4.966666 c -3.1113,5.344585 -7.247921,8.033345 -12.433333,8.033334 -2.58055,1e-5 -4.543473,-0.352086 -6.208333,-1.516667 0.348871,0.50642 0.590094,0.752276 1.075,1.183333 1.759888,1.558846 4.147753,2.333345 7.133333,2.333334 5.185412,1.1e-5 9.322033,-2.688749 12.433333,-8.033334 z m 4.933334,6.5 c -0.04103,0.05207 -0.09239,0.08182 -0.133334,0.133334 0.687326,0.744419 1.306949,1.359747 1.833334,1.8 -0.529404,-0.580895 -1.078447,-1.178283 -1.7,-1.933334 z"
- style="font-style:normal;font-weight:normal;font-size:76.18933868px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;opacity:0.3;fill:#ccff00;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
- <path
- sodipodi:nodetypes="ccsccscccccccccccccccccccscscccccsccsccccccc"
- id="text5058-0"
- d="m 19.730474,21.54714 c -4.462645,3.3e-5 -8.026823,1.150859 -10.6666669,3.4 -2.9541429,2.538699 -4.433344,5.658826 -4.4333333,9.4 -1.07e-5,3.897099 1.4791904,7.039495 4.4333333,9.4 0.042837,0.03444 0.090155,0.06608 0.1333334,0.1 -2.2392086,-2.228193 -3.3666752,-5.040417 -3.3666667,-8.433333 -1.07e-5,-3.741174 1.4791904,-6.861301 4.4333332,-9.4 2.639844,-2.249141 6.204022,-3.399967 10.666667,-3.4 2.294137,3.3e-5 4.302916,0.365295 6,1.033333 1.870874,0.694455 3.42364,1.628367 4.733333,2.8 -0.314265,-0.308986 -0.652406,-0.582729 -0.966667,-0.866666 -1.351393,-1.269305 -2.986804,-2.265091 -4.966666,-3 -1.697084,-0.668039 -3.705863,-1.033301 -6,-1.033334 z m 29.6,0.1 c -3.614149,3.3e-5 -6.574457,0.741481 -8.9,2.166667 -1.813279,1.154221 -3.963039,3.235656 -6.016667,5.816667 0.355649,0.402628 0.711011,0.798625 1.066667,1.233333 2.105561,-2.672257 4.295767,-4.803044 6.15,-5.983333 2.325543,-1.425187 5.285851,-2.166634 8.9,-2.166667 4.22442,3.3e-5 7.742084,1.136734 10.533333,3.366667 -0.36096,-0.367566 -0.745726,-0.696967 -1.166667,-1.033334 -2.797059,-2.249141 -6.32409,-3.399967 -10.566666,-3.4 z m 8.233333,7.333334 c 1.323326,1.449243 1.999942,3.250987 2,5.433333 -6.5e-5,2.27146 -0.871584,4.2853 -2.6,6 -1.602834,1.603383 -3.957366,2.400012 -7.1,2.4 -2.406328,6e-6 -4.776468,-0.90386 -7.066667,-2.7 2.669147,2.483838 5.436929,3.766674 8.266667,3.766667 3.142634,1.1e-5 5.497166,-0.796617 7.1,-2.4 1.728416,-1.7147 2.599935,-3.72854 2.6,-6 -6.5e-5,-2.49411 -0.871584,-4.496745 -2.6,-6.033334 -0.185641,-0.164422 -0.400724,-0.319587 -0.6,-0.466666 z m -26,5.733333 c -3.1113,5.344584 -7.247921,8.033345 -12.433333,8.033333 -2.612382,1.1e-5 -4.759372,-0.60651 -6.433334,-1.8 0.166027,0.176488 0.313947,0.367942 0.5,0.533334 1.759888,1.558845 4.147754,2.333345 7.133334,2.333333 5.185412,1.2e-5 9.322033,-2.688749 12.433333,-8.033333 z m 4.133333,5.566667 c -0.04657,0.05909 -0.08689,0.108298 -0.133333,0.166666 1.038571,1.18897 1.9748,2.169945 2.7,2.766667 0.06249,0.05364 0.137426,0.08086 0.2,0.133333 -0.792178,-0.781249 -1.706288,-1.778539 -2.766667,-3.066666 z"
- style="font-style:normal;font-weight:normal;font-size:76.18933868px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;opacity:0.6;fill:#ccff00;fill-opacity:1;stroke:none;stroke-width:1.06666672"
- inkscape:connector-curvature="0" />
- </g>
- </g>
-</svg>
diff --git a/launcher/package/ubuntu/multimc/opt/multimc/run.sh b/launcher/package/ubuntu/multimc/opt/multimc/run.sh
deleted file mode 100755
index 12a9b45c..00000000
--- a/launcher/package/ubuntu/multimc/opt/multimc/run.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/bash
-
-INSTDIR="${XDG_DATA_HOME-$HOME/.local/share}/multimc"
-
-if [ `getconf LONG_BIT` = "64" ]
-then
- PACKAGE="mmc-stable-lin64.tar.gz"
-else
- PACKAGE="mmc-stable-lin32.tar.gz"
-fi
-
-deploy() {
- mkdir -p $INSTDIR
- cd ${INSTDIR}
-
- wget --progress=dot:force "https://files.multimc.org/downloads/${PACKAGE}" 2>&1 | sed -u 's/.* \([0-9]\+%\)\ \+\([0-9.]\+.\) \(.*\)/\1\n# Downloading at \2\/s, ETA \3/' | zenity --progress --auto-close --auto-kill --title="Downloading MultiMC..."
-
- tar -xzf ${PACKAGE} --transform='s,MultiMC/,,'
- rm ${PACKAGE}
- chmod +x MultiMC
-}
-
-runmmc() {
- cd ${INSTDIR}
- exec ./MultiMC "$@"
-}
-
-if [[ ! -f ${INSTDIR}/MultiMC ]]; then
- deploy
- runmmc "$@"
-else
- runmmc "$@"
-fi
diff --git a/launcher/package/ubuntu/multimc/usr/share/applications/multimc.desktop b/launcher/package/ubuntu/multimc/usr/share/applications/multimc.desktop
deleted file mode 100755
index e0456f89..00000000
--- a/launcher/package/ubuntu/multimc/usr/share/applications/multimc.desktop
+++ /dev/null
@@ -1,16 +0,0 @@
-[Desktop Entry]
-Categories=Game;
-Exec=/opt/multimc/run.sh
-Icon=/opt/multimc/icon.svg
-Keywords=game;Minecraft;
-MimeType=
-Name=MultiMC 5
-Path=
-StartupNotify=true
-Terminal=false
-TerminalOptions=
-Type=Application
-X-DBUS-ServiceName=
-X-DBUS-StartupType=
-X-KDE-SubstituteUID=false
-X-KDE-Username=
diff --git a/launcher/package/ubuntu/multimc/usr/share/man/man1/multimc.1 b/launcher/package/ubuntu/multimc/usr/share/man/man1/multimc.1
deleted file mode 100644
index b4af25e3..00000000
--- a/launcher/package/ubuntu/multimc/usr/share/man/man1/multimc.1
+++ /dev/null
@@ -1,97 +0,0 @@
-'\" t
-.\" Title: multimc
-.\" Author: [see the "AUTHORS" section]
-.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
-.\" Date: 11/07/2021
-.\" Manual: \ \&
-.\" Source: \ \&
-.\" Language: English
-.\"
-.TH "MULTIMC" "1" "11/07/2021" "\ \&" "\ \&"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-multimc \- a launcher and instance manager for Minecraft\&.
-.SH "SYNOPSIS"
-.sp
-\fBmultimc\fR [\fIOPTIONS\fR]
-.SH "DESCRIPTION"
-.sp
-MultiMC is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once\&. It also allows you to easily install and remove mods by simply dragging and dropping\&. Here are the current features of MultiMC\&.
-.SH "OPTIONS"
-.PP
-\fB\-d, \-\-dir\fR=\fIDIRECTORY\fR
-.RS 4
-Use
-\fIDIRECTORY\fR
-as the MultiMC root\&.
-.RE
-.PP
-\fB\-l, \-\-launch\fR=\fIINSTANCE_ID\fR
-.RS 4
-Launch the instance specified by
-\fIINSTANCE_ID\fR\&.
-.RE
-.PP
-\fB\-\-alive\fR
-.RS 4
-Write a small
-\fIlive\&.check\fR
-file after MultiMC starts\&.
-.RE
-.PP
-\fB\-h, \-\-help\fR
-.RS 4
-Display help text and exit\&.
-.RE
-.PP
-\fB\-v, \-\-version\fR
-.RS 4
-Display program version and exit\&.
-.RE
-.PP
-\fB\-a, \-\-profile\fR=\fIPROFILE\fR
-.RS 4
-Use the account specified by
-\fIPROFILE\fR
-(only valid in combination with \-\-launch)\&.
-.RE
-.SH "EXIT STATUS"
-.PP
-\fB0\fR
-.RS 4
-Success
-.RE
-.PP
-\fB1\fR
-.RS 4
-Failure (syntax or usage error; configuration error; unexpected error)\&.
-.RE
-.SH "BUGS"
-.sp
-https://github\&.com/MultiMC/Launcher/issues
-.SH "RESOURCES"
-.sp
-GitHub: https://github\&.com/MultiMC/Launcher
-.sp
-Main website: https://multimc\&.org
-.SH "AUTHORS"
-.sp
-peterix <peterix@gmail\&.com>
diff --git a/launcher/package/ubuntu/multimc/usr/share/metainfo/multimc.metainfo.xml b/launcher/package/ubuntu/multimc/usr/share/metainfo/multimc.metainfo.xml
deleted file mode 100644
index 3bccba47..00000000
--- a/launcher/package/ubuntu/multimc/usr/share/metainfo/multimc.metainfo.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<component type="desktop">
- <id>multimc</id>
- <launchable type="desktop-id">multimc.desktop</launchable>
- <name>MultiMC</name>
- <summary>Manage Minecraft instances with ease</summary>
- <description>
- <p>Overview</p>
- <p>MultiMC is a free, open source launcher for Minecraft. It allows you to have multiple, cleanly separated instances of Minecraft (each with their own mods, texture packs, saves, etc) and helps you manage them and their associated options with a simple and powerful interface.</p>
- <p>Features</p>
- <ul>
- <li>Manage multiple instances of Minecraft at once</li>
- <li>Start Minecraft with a custom resolution</li>
- <li>Change Java's runtime options (including memory options)</li>
- <li>Shows Minecraft's console output in a colour coded window</li>
- <li>Kill Minecraft easily if it crashes / freezes</li>
- <li>Custom icons and groups for instances</li>
- <li>Forge integration (automatic installation, version downloads, mod management)</li>
- <li>Minecraft world management</li>
- <li>Import and export Minecraft instances to share them with anyone</li>
- <li>Supports every version of Minecraft that the vanilla launcher does</li>
- </ul>
- </description>
- <screenshots>
- <screenshot type="default">
- <image type="source" width="936" height="921">https://multimc.org/images/screenshots/main.png</image>
- </screenshot>
- <screenshot>
- <image type="source" width="936" height="998">https://multimc.org/images/screenshots/editmods.png</image>
- </screenshot>
- <screenshot>
- <image type="source" width="936" height="998">https://multimc.org/images/screenshots/version.png</image>
- </screenshot>
- <screenshot>
- <image type="source" width="936" height="998">https://multimc.org/images/screenshots/console.png</image>
- </screenshot>
- <screenshot>
- <image type="source" width="936" height="921">https://multimc.org/images/screenshots/settings.png</image>
- </screenshot>
- </screenshots>
- <releases>
- <release date="2021-01-07" version="5"/>
- </releases>
- <url type="homepage">https://multimc.org/</url>
- <url type="help">https://discord.com/invite/0k2zsXGNHs0fE4Wm</url>
- <url type="faq">https://github.com/MultiMC/Launcher/wiki/FAQ</url>
- <url type="bugtracker">https://github.com/MultiMC/Launcher/issues</url>
- <url type="translate">https://translate.multimc.org/</url>
- <url type="donation">https://www.patreon.com/multimc</url>
- <developer_name>The MultiMC Team</developer_name>
- <metadata_license>CC0-1.0</metadata_license>
- <project_license>Apache-2.0</project_license>
- <update_contact>peterix_at_gmail.com</update_contact>
-</component>
diff --git a/launcher/resources/OSX/OSX.qrc b/launcher/resources/OSX/OSX.qrc
index b85313e1..3eca8e19 100644
--- a/launcher/resources/OSX/OSX.qrc
+++ b/launcher/resources/OSX/OSX.qrc
@@ -23,7 +23,6 @@
<file>scalable/new.svg</file>
<file>scalable/news.svg</file>
<file>scalable/notes.svg</file>
- <file>scalable/patreon.svg</file>
<file>scalable/proxy.svg</file>
<file>scalable/refresh.svg</file>
<file>scalable/resourcepacks.svg</file>
diff --git a/launcher/resources/OSX/scalable/launcher.svg b/launcher/resources/OSX/scalable/launcher.svg
index e0aaad87..c192d503 100644
--- a/launcher/resources/OSX/scalable/launcher.svg
+++ b/launcher/resources/OSX/scalable/launcher.svg
@@ -1,18 +1,21 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
-<rect fill="none" width="24" height="24"/>
-<path fill-rule="evenodd" clip-rule="evenodd" fill="#E6E6E6" d="M22,6c0-0.6-0.4-1-1-1H9C8.4,5,8,5.4,8,6v12c0,0.6,0.4,1,1,1h12
- c0.6,0,1-0.4,1-1V6z"/>
-<polygon fill="#FFFFFF" points="12.5,8.1 12.5,15.9 18.6,12 "/>
-<g>
- <path fill-rule="evenodd" clip-rule="evenodd" fill="#585858" d="M21,20H9c-1.1,0-2-0.9-2-2V6c0-1.1,0.9-2,2-2h12c1.1,0,2,0.9,2,2
- v12C23,19.1,22.1,20,21,20z M22,6c0-0.6-0.4-1-1-1H9C8.4,5,8,5.4,8,6v12c0,0.6,0.4,1,1,1h12c0.6,0,1-0.4,1-1V6z"/>
- <g>
- <path fill="#585858" d="M12.3,17c-0.4,0-0.8-0.3-0.8-0.9V7.9c0-0.6,0.4-0.9,0.8-0.9c0.2,0,0.4,0.1,0.5,0.2l6.5,4.1
- c0.3,0.2,0.4,0.4,0.4,0.7s-0.2,0.6-0.4,0.7l-6.5,4.1C12.7,16.9,12.5,17,12.3,17z M12.5,8.1l0,7.7l6.1-3.9L12.5,8.1z"/>
- </g>
-</g>
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg width="64" height="64" version="1.1" viewBox="0 0 16.933 16.933" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="linearGradient84726" x1="4.4979" x2="12.435" y1="3.8011" y2="9.5681" gradientUnits="userSpaceOnUse">
+ <stop stop-color="#88b858" offset="0"/>
+ <stop stop-color="#72b147" offset=".5"/>
+ <stop stop-color="#5a9a30" offset="1"/>
+ </linearGradient>
+ </defs>
+ <g>
+ <path d="m3.561 16.016s0-3.5642 4.9056-3.5642c4.9069 0 4.9056 3.5642 4.9056 3.5642z" fill="#765338"/>
+ <path d="m8.4667 12.452-4.9056 3.5642-3.0319-9.3311z" fill="#b7835a"/>
+ <path d="m8.4667 12.452 7.9375-5.7669-3.0319 9.3311z" fill="#5b422d"/>
+ <path d="m8.8308 12.716-0.36417 0.26458-0.36417-0.26458c0-0.26458 0.36417-0.26458 0.36417-0.26458s0.36417 0 0.36417 0.26458z" fill="#72b147"/>
+ <path d="m8.4667 12.452s-2e-7 -5.7669 7.9375-5.7669l-0.22507 0.69269-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819z" fill="#5a9a30"/>
+ <path d="m8.1025 12.716-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.22507-0.69269c7.9375 1e-7 7.9375 5.7669 7.9375 5.7669z" fill="#88b858"/>
+ <path d="m0.52917 6.6846 7.9375 5.7669 7.9375-5.7669-7.9375-5.7669z" fill="url(#linearGradient84726)"/>
+ </g>
+ <path d="m0.75424 7.3773-0.22507-0.69269 7.9375 5.7669 7.9375-5.7669-0.22507 0.69269-7.7124 5.6034z" fill-opacity="0"/>
</svg>
diff --git a/launcher/resources/flat/flat.qrc b/launcher/resources/flat/flat.qrc
index a888f6d4..d2b752b1 100644
--- a/launcher/resources/flat/flat.qrc
+++ b/launcher/resources/flat/flat.qrc
@@ -26,7 +26,6 @@
<file>scalable/news.svg</file>
<file>scalable/notes.svg</file>
<file>scalable/packages.svg</file>
- <file>scalable/patreon.svg</file>
<file>scalable/proxy.svg</file>
<file>scalable/quickmods.svg</file>
<file>scalable/reddit-alien.svg</file>
diff --git a/launcher/resources/flat/scalable/launcher.svg b/launcher/resources/flat/scalable/launcher.svg
index 2c4964db..c192d503 100644
--- a/launcher/resources/flat/scalable/launcher.svg
+++ b/launcher/resources/flat/scalable/launcher.svg
@@ -1,2 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
-<svg width="24" height="24" fill="#757575" version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m20 4h-16v16h16zm0 18h-16c-1.1046 0-2-0.89543-2-2v-16c0-1.1046 0.89543-2 2-2h16c1.1046 0 2 0.89543 2 2v16c0 1.1046-0.89543 2-2 2z"/><path d="m7.2 18c-0.225 0-0.45-0.075-0.6-0.15-0.375-0.225-0.6-0.6-0.6-1.05v-9.6c0-0.45 0.225-0.825 0.6-1.05 0.225-0.15 0.375-0.15 0.6-0.15 0.15 0 0.375 0.075 0.525 0.15l9.6 4.8c0.375 0.225 0.675 0.6 0.675 1.05 0 0.45-0.225 0.9-0.675 1.05l-9.6 4.8c-0.15 0.075-0.375 0.15-0.525 0.15z" clip-rule="evenodd" fill="#757575" fill-rule="evenodd" stroke-width=".99999"/></svg>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg width="64" height="64" version="1.1" viewBox="0 0 16.933 16.933" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="linearGradient84726" x1="4.4979" x2="12.435" y1="3.8011" y2="9.5681" gradientUnits="userSpaceOnUse">
+ <stop stop-color="#88b858" offset="0"/>
+ <stop stop-color="#72b147" offset=".5"/>
+ <stop stop-color="#5a9a30" offset="1"/>
+ </linearGradient>
+ </defs>
+ <g>
+ <path d="m3.561 16.016s0-3.5642 4.9056-3.5642c4.9069 0 4.9056 3.5642 4.9056 3.5642z" fill="#765338"/>
+ <path d="m8.4667 12.452-4.9056 3.5642-3.0319-9.3311z" fill="#b7835a"/>
+ <path d="m8.4667 12.452 7.9375-5.7669-3.0319 9.3311z" fill="#5b422d"/>
+ <path d="m8.8308 12.716-0.36417 0.26458-0.36417-0.26458c0-0.26458 0.36417-0.26458 0.36417-0.26458s0.36417 0 0.36417 0.26458z" fill="#72b147"/>
+ <path d="m8.4667 12.452s-2e-7 -5.7669 7.9375-5.7669l-0.22507 0.69269-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819z" fill="#5a9a30"/>
+ <path d="m8.1025 12.716-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.22507-0.69269c7.9375 1e-7 7.9375 5.7669 7.9375 5.7669z" fill="#88b858"/>
+ <path d="m0.52917 6.6846 7.9375 5.7669 7.9375-5.7669-7.9375-5.7669z" fill="url(#linearGradient84726)"/>
+ </g>
+ <path d="m0.75424 7.3773-0.22507-0.69269 7.9375 5.7669 7.9375-5.7669-0.22507 0.69269-7.7124 5.6034z" fill-opacity="0"/>
+</svg>
diff --git a/launcher/resources/iOS/iOS.qrc b/launcher/resources/iOS/iOS.qrc
index 5eec744c..f05cd67c 100644
--- a/launcher/resources/iOS/iOS.qrc
+++ b/launcher/resources/iOS/iOS.qrc
@@ -23,7 +23,6 @@
<file>scalable/new.svg</file>
<file>scalable/news.svg</file>
<file>scalable/notes.svg</file>
- <file>scalable/patreon.svg</file>
<file>scalable/proxy.svg</file>
<file>scalable/refresh.svg</file>
<file>scalable/resourcepacks.svg</file>
diff --git a/launcher/resources/iOS/scalable/launcher.svg b/launcher/resources/iOS/scalable/launcher.svg
index cd63ba71..c192d503 100644
--- a/launcher/resources/iOS/scalable/launcher.svg
+++ b/launcher/resources/iOS/scalable/launcher.svg
@@ -1,14 +1,21 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
-<g>
- <path fill-rule="evenodd" clip-rule="evenodd" fill="#3366CC" d="M28,32H4c-2.2,0-4-1.8-4-4V4c0-2.2,1.8-4,4-4h24c2.2,0,4,1.8,4,4
- v24C32,30.2,30.2,32,28,32z M30,4c0-1.1-0.9-2-2-2H4C2.9,2,2,2.9,2,4v24c0,1.1,0.9,2,2,2h24c1.1,0,2-0.9,2-2V4z"/>
- <g>
- <path fill="#3366CC" d="M10.6,26C9.8,26,9,25.4,9,24.2V7.8C9,6.6,9.8,6,10.6,6c0.4,0,0.7,0.1,1.1,0.3l13,8.2
- c0.6,0.3,0.9,0.9,0.9,1.5s-0.3,1.1-0.9,1.5l-13,8.2C11.3,25.9,10.9,26,10.6,26z M11,8.3l0,15.4L23.2,16L11,8.3z"/>
- </g>
-</g>
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg width="64" height="64" version="1.1" viewBox="0 0 16.933 16.933" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="linearGradient84726" x1="4.4979" x2="12.435" y1="3.8011" y2="9.5681" gradientUnits="userSpaceOnUse">
+ <stop stop-color="#88b858" offset="0"/>
+ <stop stop-color="#72b147" offset=".5"/>
+ <stop stop-color="#5a9a30" offset="1"/>
+ </linearGradient>
+ </defs>
+ <g>
+ <path d="m3.561 16.016s0-3.5642 4.9056-3.5642c4.9069 0 4.9056 3.5642 4.9056 3.5642z" fill="#765338"/>
+ <path d="m8.4667 12.452-4.9056 3.5642-3.0319-9.3311z" fill="#b7835a"/>
+ <path d="m8.4667 12.452 7.9375-5.7669-3.0319 9.3311z" fill="#5b422d"/>
+ <path d="m8.8308 12.716-0.36417 0.26458-0.36417-0.26458c0-0.26458 0.36417-0.26458 0.36417-0.26458s0.36417 0 0.36417 0.26458z" fill="#72b147"/>
+ <path d="m8.4667 12.452s-2e-7 -5.7669 7.9375-5.7669l-0.22507 0.69269-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819z" fill="#5a9a30"/>
+ <path d="m8.1025 12.716-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.22507-0.69269c7.9375 1e-7 7.9375 5.7669 7.9375 5.7669z" fill="#88b858"/>
+ <path d="m0.52917 6.6846 7.9375 5.7669 7.9375-5.7669-7.9375-5.7669z" fill="url(#linearGradient84726)"/>
+ </g>
+ <path d="m0.75424 7.3773-0.22507-0.69269 7.9375 5.7669 7.9375-5.7669-0.22507 0.69269-7.7124 5.6034z" fill-opacity="0"/>
</svg>
diff --git a/launcher/resources/multimc/128x128/instances/modrinth.png b/launcher/resources/multimc/128x128/instances/modrinth.png
new file mode 100644
index 00000000..740bc8f0
--- /dev/null
+++ b/launcher/resources/multimc/128x128/instances/modrinth.png
Binary files differ
diff --git a/launcher/resources/multimc/32x32/instances/modrinth.png b/launcher/resources/multimc/32x32/instances/modrinth.png
new file mode 100644
index 00000000..025ed065
--- /dev/null
+++ b/launcher/resources/multimc/32x32/instances/modrinth.png
Binary files differ
diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc
index 5b36cb3d..ef29cf9b 100644
--- a/launcher/resources/multimc/multimc.qrc
+++ b/launcher/resources/multimc/multimc.qrc
@@ -77,14 +77,6 @@
<!-- Custom commands. -->
<file>scalable/custom-commands.svg</file>
- <!-- Patron logo. (C) 2014 Patreon, Inc., http://www.patreon.com/toolbox?ftyp=media -->
- <file>16x16/patreon.png</file>
- <file>22x22/patreon.png</file>
- <file>24x24/patreon.png</file>
- <file>32x32/patreon.png</file>
- <file>48x48/patreon.png</file>
- <file>64x64/patreon.png</file>
-
<!-- The cat button. Freeware, http://findicons.com/icon/73096/black_cat -->
<file>16x16/cat.png</file>
<file>22x22/cat.png</file>
@@ -276,6 +268,9 @@
<file>32x32/instances/flame.png</file>
<file>128x128/instances/flame.png</file>
+ <file>32x32/instances/modrinth.png</file>
+ <file>128x128/instances/modrinth.png</file>
+
<file>32x32/instances/gear.png</file>
<file>128x128/instances/gear.png</file>
diff --git a/launcher/resources/multimc/scalable/launcher.svg b/launcher/resources/multimc/scalable/launcher.svg
index 42a056d6..c192d503 100644
--- a/launcher/resources/multimc/scalable/launcher.svg
+++ b/launcher/resources/multimc/scalable/launcher.svg
@@ -1,187 +1,21 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- version="1.1"
- width="32"
- height="32"
- id="svg2"
- inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
- sodipodi:docname="launcher.svg"
- inkscape:export-filename="/home/peterix/minecraft/src/MultiMC5/application/resources/multimc/64x64/status-running.png"
- inkscape:export-xdpi="180"
- inkscape:export-ydpi="180"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:dc="http://purl.org/dc/elements/1.1/">
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1502"
- inkscape:window-height="1405"
- id="namedview32"
- showgrid="true"
- inkscape:snap-bbox="true"
- inkscape:bbox-nodes="false"
- inkscape:bbox-paths="false"
- inkscape:snap-bbox-midpoints="false"
- inkscape:snap-bbox-edge-midpoints="false"
- inkscape:object-paths="true"
- inkscape:snap-intersection-paths="true"
- inkscape:object-nodes="true"
- inkscape:snap-smooth-nodes="true"
- inkscape:snap-midpoints="false"
- inkscape:zoom="29.5"
- inkscape:cx="7.9152543"
- inkscape:cy="16.576271"
- inkscape:window-x="2015"
- inkscape:window-y="586"
- inkscape:window-maximized="0"
- inkscape:current-layer="layer1"
- inkscape:pagecheckerboard="0">
- <inkscape:grid
- type="xygrid"
- id="grid4160"
- spacingx="0.5"
- spacingy="0.5"
- empspacing="8" />
- </sodipodi:namedview>
- <defs
- id="defs4">
- <linearGradient
- id="linearGradient4162">
- <stop
- offset="0"
- style="stop-color:#0071f1;stop-opacity:1"
- id="stop4164" />
- <stop
- offset="1"
- style="stop-color:#007ec3;stop-opacity:1"
- id="stop4166" />
- </linearGradient>
- <linearGradient
- id="linearGradient3827">
- <stop
- id="stop3829"
- style="stop-color:#b80000;stop-opacity:1"
- offset="0" />
- <stop
- id="stop3831"
- style="stop-color:#600000;stop-opacity:1"
- offset="1" />
- </linearGradient>
- <linearGradient
- id="linearGradient3801">
- <stop
- id="stop3803"
- style="stop-color:#f1ab00;stop-opacity:1"
- offset="0" />
- <stop
- id="stop3805"
- style="stop-color:#c39a00;stop-opacity:1"
- offset="1" />
- </linearGradient>
- <linearGradient
- id="linearGradient3005">
- <stop
- id="stop3007"
- style="stop-color:#ffffff;stop-opacity:1"
- offset="0" />
- <stop
- id="stop3781"
- style="stop-color:#ffffff;stop-opacity:0.49803922"
- offset="0.8142857" />
- <stop
- id="stop3009"
- style="stop-color:#ffffff;stop-opacity:0"
- offset="1" />
- </linearGradient>
- <filter
- color-interpolation-filters="sRGB"
- id="filter3797">
- <feGaussianBlur
- id="feGaussianBlur3799"
- stdDeviation="0.52592593" />
- </filter>
- <radialGradient
- cx="3.9371533"
- cy="7.5016646"
- r="2.5"
- fx="3.9371533"
- fy="7.5016646"
- id="radialGradient3807"
- xlink:href="#linearGradient4162"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(1.4496779,1.5407764,-0.90127514,0.84794135,4.9906134,-4.9255796)" />
- <radialGradient
- cx="3.9371533"
- cy="7.5016646"
- r="2.5"
- fx="3.9371533"
- fy="7.5016646"
- id="radialGradient3823"
- xlink:href="#linearGradient3827"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(1.4496779,1.5407764,-0.90127514,0.84794135,4.9906134,-4.9255796)" />
- <radialGradient
- cx="3.9371533"
- cy="7.5016646"
- r="2.5"
- fx="3.9371533"
- fy="7.5016646"
- id="radialGradient3786"
- xlink:href="#linearGradient3801"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(1.4496779,1.5407764,-0.90127514,0.84794135,4.9906134,-4.9255796)" />
- </defs>
- <metadata
- id="metadata7">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- transform="translate(0,-1020.3622)"
- id="layer1">
- <path
- d="m 8,9.5 a 2.5,2.5 0 1 1 -5,0 2.5,2.5 0 1 1 5,0 z"
- transform="matrix(6.4,0,0,6.4,-19.2,975.5622)"
- id="path2997"
- style="color:#000000;fill:url(#radialGradient3807);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
- <path
- d="M 31,16 A 15,15 0 1 1 1,16 15,15 0 1 1 31,16 z"
- transform="matrix(0.93333333,0,0,0.93333444,1.0666666,1021.4288)"
- id="path2999"
- style="color:#000000;fill:#009cff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
- <g
- id="g4187">
- <path
- inkscape:connector-curvature="0"
- id="path4168"
- d="m 10.455414,1028.3241 0,16.0761 L 24,1036.3622 Z"
- style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.95251006px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
- <path
- sodipodi:nodetypes="cccc"
- inkscape:connector-curvature="0"
- style="opacity:0.54887217;fill:#ffffff;stroke:none"
- id="path3809-3"
- d="m 10.5,1044.3622 0,-16 13.5,8 z" />
- </g>
- </g>
+<svg width="64" height="64" version="1.1" viewBox="0 0 16.933 16.933" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="linearGradient84726" x1="4.4979" x2="12.435" y1="3.8011" y2="9.5681" gradientUnits="userSpaceOnUse">
+ <stop stop-color="#88b858" offset="0"/>
+ <stop stop-color="#72b147" offset=".5"/>
+ <stop stop-color="#5a9a30" offset="1"/>
+ </linearGradient>
+ </defs>
+ <g>
+ <path d="m3.561 16.016s0-3.5642 4.9056-3.5642c4.9069 0 4.9056 3.5642 4.9056 3.5642z" fill="#765338"/>
+ <path d="m8.4667 12.452-4.9056 3.5642-3.0319-9.3311z" fill="#b7835a"/>
+ <path d="m8.4667 12.452 7.9375-5.7669-3.0319 9.3311z" fill="#5b422d"/>
+ <path d="m8.8308 12.716-0.36417 0.26458-0.36417-0.26458c0-0.26458 0.36417-0.26458 0.36417-0.26458s0.36417 0 0.36417 0.26458z" fill="#72b147"/>
+ <path d="m8.4667 12.452s-2e-7 -5.7669 7.9375-5.7669l-0.22507 0.69269-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819z" fill="#5a9a30"/>
+ <path d="m8.1025 12.716-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.22507-0.69269c7.9375 1e-7 7.9375 5.7669 7.9375 5.7669z" fill="#88b858"/>
+ <path d="m0.52917 6.6846 7.9375 5.7669 7.9375-5.7669-7.9375-5.7669z" fill="url(#linearGradient84726)"/>
+ </g>
+ <path d="m0.75424 7.3773-0.22507-0.69269 7.9375 5.7669 7.9375-5.7669-0.22507 0.69269-7.7124 5.6034z" fill-opacity="0"/>
</svg>
diff --git a/launcher/resources/pe_blue/pe_blue.qrc b/launcher/resources/pe_blue/pe_blue.qrc
index c78d21ad..456963b7 100644
--- a/launcher/resources/pe_blue/pe_blue.qrc
+++ b/launcher/resources/pe_blue/pe_blue.qrc
@@ -23,7 +23,6 @@
<file>scalable/new.svg</file>
<file>scalable/news.svg</file>
<file>scalable/notes.svg</file>
- <file>scalable/patreon.svg</file>
<file>scalable/proxy.svg</file>
<file>scalable/refresh.svg</file>
<file>scalable/resourcepacks.svg</file>
diff --git a/launcher/resources/pe_blue/scalable/launcher.svg b/launcher/resources/pe_blue/scalable/launcher.svg
index 8b577376..c192d503 100644
--- a/launcher/resources/pe_blue/scalable/launcher.svg
+++ b/launcher/resources/pe_blue/scalable/launcher.svg
@@ -1,43 +1,21 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
-<path fill-rule="evenodd" clip-rule="evenodd" fill="#DAEEFF" d="M28,6c0-1.1-0.9-2-2-2H6C4.9,4,4,4.9,4,6v20c0,1.1,0.9,2,2,2h20
- c1.1,0,2-0.9,2-2V6z"/>
-<path fill-rule="evenodd" clip-rule="evenodd" fill="#3366CC" d="M26,32H6c-3.3,0-6-2.7-6-6V6c0-3.3,2.7-6,6-6h20c3.3,0,6,2.7,6,6
- v20C32,29.3,29.3,32,26,32z M28,6c0-1.1-0.9-2-2-2H6C4.9,4,4,4.9,4,6v20c0,1.1,0.9,2,2,2h20c1.1,0,2-0.9,2-2V6z"/>
-<path fill-rule="evenodd" clip-rule="evenodd" fill="#C1272D" d="M10.6,24.2c-0.3,0-0.6-0.1-0.8-0.2C9.3,23.7,9,23.2,9,22.6V9.8
- c0-0.6,0.3-1.1,0.8-1.4c0.3-0.2,0.5-0.2,0.8-0.2c0.2,0,0.5,0.1,0.7,0.2l12.8,6.4c0.5,0.3,0.9,0.8,0.9,1.4c0,0.6-0.3,1.2-0.9,1.4
- L11.3,24C11.1,24.1,10.8,24.2,10.6,24.2z"/>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg width="64" height="64" version="1.1" viewBox="0 0 16.933 16.933" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="linearGradient84726" x1="4.4979" x2="12.435" y1="3.8011" y2="9.5681" gradientUnits="userSpaceOnUse">
+ <stop stop-color="#88b858" offset="0"/>
+ <stop stop-color="#72b147" offset=".5"/>
+ <stop stop-color="#5a9a30" offset="1"/>
+ </linearGradient>
+ </defs>
+ <g>
+ <path d="m3.561 16.016s0-3.5642 4.9056-3.5642c4.9069 0 4.9056 3.5642 4.9056 3.5642z" fill="#765338"/>
+ <path d="m8.4667 12.452-4.9056 3.5642-3.0319-9.3311z" fill="#b7835a"/>
+ <path d="m8.4667 12.452 7.9375-5.7669-3.0319 9.3311z" fill="#5b422d"/>
+ <path d="m8.8308 12.716-0.36417 0.26458-0.36417-0.26458c0-0.26458 0.36417-0.26458 0.36417-0.26458s0.36417 0 0.36417 0.26458z" fill="#72b147"/>
+ <path d="m8.4667 12.452s-2e-7 -5.7669 7.9375-5.7669l-0.22507 0.69269-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819z" fill="#5a9a30"/>
+ <path d="m8.1025 12.716-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.22507-0.69269c7.9375 1e-7 7.9375 5.7669 7.9375 5.7669z" fill="#88b858"/>
+ <path d="m0.52917 6.6846 7.9375 5.7669 7.9375-5.7669-7.9375-5.7669z" fill="url(#linearGradient84726)"/>
+ </g>
+ <path d="m0.75424 7.3773-0.22507-0.69269 7.9375 5.7669 7.9375-5.7669-0.22507 0.69269-7.7124 5.6034z" fill-opacity="0"/>
</svg>
diff --git a/launcher/resources/pe_colored/pe_colored.qrc b/launcher/resources/pe_colored/pe_colored.qrc
index abe2cdb4..92a78b5c 100644
--- a/launcher/resources/pe_colored/pe_colored.qrc
+++ b/launcher/resources/pe_colored/pe_colored.qrc
@@ -23,7 +23,6 @@
<file>scalable/new.svg</file>
<file>scalable/news.svg</file>
<file>scalable/notes.svg</file>
- <file>scalable/patreon.svg</file>
<file>scalable/proxy.svg</file>
<file>scalable/refresh.svg</file>
<file>scalable/resourcepacks.svg</file>
diff --git a/launcher/resources/pe_colored/scalable/launcher.svg b/launcher/resources/pe_colored/scalable/launcher.svg
index 199b2dae..c192d503 100644
--- a/launcher/resources/pe_colored/scalable/launcher.svg
+++ b/launcher/resources/pe_colored/scalable/launcher.svg
@@ -1,14 +1,21 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
-<path fill-rule="evenodd" clip-rule="evenodd" fill="#F2F2F2" d="M28,6c0-1.1-0.9-2-2-2H6C4.9,4,4,4.9,4,6v20c0,1.1,0.9,2,2,2h20
- c1.1,0,2-0.9,2-2V6z"/>
-<path fill="none" d="M6,28h20c1.1,0,2-0.9,2-2V9V6c0-1.1-0.9-2-2-2H6C4.9,4,4,4.9,4,6v3v17C4,27.1,4.9,28,6,28z"/>
-<path fill="#39B54A" d="M26,0H6C2.7,0,0,2.7,0,6v3h4V6c0-1.1,0.9-2,2-2h20c1.1,0,2,0.9,2,2v3h4V6C32,2.7,29.3,0,26,0z"/>
-<path fill="#8C6239" d="M28,26c0,1.1-0.9,2-2,2H6c-1.1,0-2-0.9-2-2V9H0v17c0,3.3,2.7,6,6,6h20c3.3,0,6-2.7,6-6V9h-4V26z"/>
-<path fill-rule="evenodd" clip-rule="evenodd" fill="#C1272D" d="M10.6,24.2c-0.3,0-0.6-0.1-0.8-0.2C9.3,23.7,9,23.2,9,22.6V9.8
- c0-0.6,0.3-1.1,0.8-1.4c0.3-0.2,0.5-0.2,0.8-0.2c0.2,0,0.5,0.1,0.7,0.2l12.8,6.4c0.5,0.3,0.9,0.8,0.9,1.4c0,0.6-0.3,1.2-0.9,1.4
- L11.3,24C11.1,24.1,10.8,24.2,10.6,24.2z"/>
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg width="64" height="64" version="1.1" viewBox="0 0 16.933 16.933" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="linearGradient84726" x1="4.4979" x2="12.435" y1="3.8011" y2="9.5681" gradientUnits="userSpaceOnUse">
+ <stop stop-color="#88b858" offset="0"/>
+ <stop stop-color="#72b147" offset=".5"/>
+ <stop stop-color="#5a9a30" offset="1"/>
+ </linearGradient>
+ </defs>
+ <g>
+ <path d="m3.561 16.016s0-3.5642 4.9056-3.5642c4.9069 0 4.9056 3.5642 4.9056 3.5642z" fill="#765338"/>
+ <path d="m8.4667 12.452-4.9056 3.5642-3.0319-9.3311z" fill="#b7835a"/>
+ <path d="m8.4667 12.452 7.9375-5.7669-3.0319 9.3311z" fill="#5b422d"/>
+ <path d="m8.8308 12.716-0.36417 0.26458-0.36417-0.26458c0-0.26458 0.36417-0.26458 0.36417-0.26458s0.36417 0 0.36417 0.26458z" fill="#72b147"/>
+ <path d="m8.4667 12.452s-2e-7 -5.7669 7.9375-5.7669l-0.22507 0.69269-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819z" fill="#5a9a30"/>
+ <path d="m8.1025 12.716-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.22507-0.69269c7.9375 1e-7 7.9375 5.7669 7.9375 5.7669z" fill="#88b858"/>
+ <path d="m0.52917 6.6846 7.9375 5.7669 7.9375-5.7669-7.9375-5.7669z" fill="url(#linearGradient84726)"/>
+ </g>
+ <path d="m0.75424 7.3773-0.22507-0.69269 7.9375 5.7669 7.9375-5.7669-0.22507 0.69269-7.7124 5.6034z" fill-opacity="0"/>
</svg>
diff --git a/launcher/resources/pe_dark/pe_dark.qrc b/launcher/resources/pe_dark/pe_dark.qrc
index 03ae7ef9..929b310d 100644
--- a/launcher/resources/pe_dark/pe_dark.qrc
+++ b/launcher/resources/pe_dark/pe_dark.qrc
@@ -23,7 +23,6 @@
<file>scalable/new.svg</file>
<file>scalable/news.svg</file>
<file>scalable/notes.svg</file>
- <file>scalable/patreon.svg</file>
<file>scalable/proxy.svg</file>
<file>scalable/refresh.svg</file>
<file>scalable/resourcepacks.svg</file>
diff --git a/launcher/resources/pe_dark/scalable/launcher.svg b/launcher/resources/pe_dark/scalable/launcher.svg
index 346729f1..c192d503 100644
--- a/launcher/resources/pe_dark/scalable/launcher.svg
+++ b/launcher/resources/pe_dark/scalable/launcher.svg
@@ -1,43 +1,21 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
-<path fill-rule="evenodd" clip-rule="evenodd" fill="#F2F2F2" d="M28,6c0-1.1-0.9-2-2-2H6C4.9,4,4,4.9,4,6v20c0,1.1,0.9,2,2,2h20
- c1.1,0,2-0.9,2-2V6z"/>
-<path fill-rule="evenodd" clip-rule="evenodd" d="M26,32H6c-3.3,0-6-2.7-6-6V6c0-3.3,2.7-6,6-6h20c3.3,0,6,2.7,6,6v20
- C32,29.3,29.3,32,26,32z M28,6c0-1.1-0.9-2-2-2H6C4.9,4,4,4.9,4,6v20c0,1.1,0.9,2,2,2h20c1.1,0,2-0.9,2-2V6z"/>
-<path fill-rule="evenodd" clip-rule="evenodd" fill="#666666" d="M10.6,24.2c-0.3,0-0.6-0.1-0.8-0.2C9.3,23.7,9,23.2,9,22.6V9.8
- c0-0.6,0.3-1.1,0.8-1.4c0.3-0.2,0.5-0.2,0.8-0.2c0.2,0,0.5,0.1,0.7,0.2l12.8,6.4c0.5,0.3,0.9,0.8,0.9,1.4c0,0.6-0.3,1.2-0.9,1.4
- L11.3,24C11.1,24.1,10.8,24.2,10.6,24.2z"/>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg width="64" height="64" version="1.1" viewBox="0 0 16.933 16.933" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="linearGradient84726" x1="4.4979" x2="12.435" y1="3.8011" y2="9.5681" gradientUnits="userSpaceOnUse">
+ <stop stop-color="#88b858" offset="0"/>
+ <stop stop-color="#72b147" offset=".5"/>
+ <stop stop-color="#5a9a30" offset="1"/>
+ </linearGradient>
+ </defs>
+ <g>
+ <path d="m3.561 16.016s0-3.5642 4.9056-3.5642c4.9069 0 4.9056 3.5642 4.9056 3.5642z" fill="#765338"/>
+ <path d="m8.4667 12.452-4.9056 3.5642-3.0319-9.3311z" fill="#b7835a"/>
+ <path d="m8.4667 12.452 7.9375-5.7669-3.0319 9.3311z" fill="#5b422d"/>
+ <path d="m8.8308 12.716-0.36417 0.26458-0.36417-0.26458c0-0.26458 0.36417-0.26458 0.36417-0.26458s0.36417 0 0.36417 0.26458z" fill="#72b147"/>
+ <path d="m8.4667 12.452s-2e-7 -5.7669 7.9375-5.7669l-0.22507 0.69269-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819z" fill="#5a9a30"/>
+ <path d="m8.1025 12.716-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.22507-0.69269c7.9375 1e-7 7.9375 5.7669 7.9375 5.7669z" fill="#88b858"/>
+ <path d="m0.52917 6.6846 7.9375 5.7669 7.9375-5.7669-7.9375-5.7669z" fill="url(#linearGradient84726)"/>
+ </g>
+ <path d="m0.75424 7.3773-0.22507-0.69269 7.9375 5.7669 7.9375-5.7669-0.22507 0.69269-7.7124 5.6034z" fill-opacity="0"/>
</svg>
diff --git a/launcher/resources/pe_light/pe_light.qrc b/launcher/resources/pe_light/pe_light.qrc
index 93d00205..25fde872 100644
--- a/launcher/resources/pe_light/pe_light.qrc
+++ b/launcher/resources/pe_light/pe_light.qrc
@@ -23,7 +23,6 @@
<file>scalable/new.svg</file>
<file>scalable/news.svg</file>
<file>scalable/notes.svg</file>
- <file>scalable/patreon.svg</file>
<file>scalable/proxy.svg</file>
<file>scalable/refresh.svg</file>
<file>scalable/resourcepacks.svg</file>
@@ -37,4 +36,3 @@
<file>scalable/worlds.svg</file>
</qresource>
</RCC>
-
diff --git a/launcher/resources/pe_light/scalable/launcher.svg b/launcher/resources/pe_light/scalable/launcher.svg
index 6dbeab52..c192d503 100644
--- a/launcher/resources/pe_light/scalable/launcher.svg
+++ b/launcher/resources/pe_light/scalable/launcher.svg
@@ -1,43 +1,21 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
-<path fill-rule="evenodd" clip-rule="evenodd" fill="#4D4D4D" d="M28,6c0-1.1-0.9-2-2-2H6C4.9,4,4,4.9,4,6v20c0,1.1,0.9,2,2,2h20
- c1.1,0,2-0.9,2-2V6z"/>
-<path fill-rule="evenodd" clip-rule="evenodd" fill="#F2F2F2" d="M26,32H6c-3.3,0-6-2.7-6-6V6c0-3.3,2.7-6,6-6h20c3.3,0,6,2.7,6,6
- v20C32,29.3,29.3,32,26,32z M28,6c0-1.1-0.9-2-2-2H6C4.9,4,4,4.9,4,6v20c0,1.1,0.9,2,2,2h20c1.1,0,2-0.9,2-2V6z"/>
-<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M10.6,24.2c-0.3,0-0.6-0.1-0.8-0.2C9.3,23.7,9,23.2,9,22.6V9.8
- c0-0.6,0.3-1.1,0.8-1.4c0.3-0.2,0.5-0.2,0.8-0.2c0.2,0,0.5,0.1,0.7,0.2l12.8,6.4c0.5,0.3,0.9,0.8,0.9,1.4c0,0.6-0.3,1.2-0.9,1.4
- L11.3,24C11.1,24.1,10.8,24.2,10.6,24.2z"/>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg width="64" height="64" version="1.1" viewBox="0 0 16.933 16.933" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="linearGradient84726" x1="4.4979" x2="12.435" y1="3.8011" y2="9.5681" gradientUnits="userSpaceOnUse">
+ <stop stop-color="#88b858" offset="0"/>
+ <stop stop-color="#72b147" offset=".5"/>
+ <stop stop-color="#5a9a30" offset="1"/>
+ </linearGradient>
+ </defs>
+ <g>
+ <path d="m3.561 16.016s0-3.5642 4.9056-3.5642c4.9069 0 4.9056 3.5642 4.9056 3.5642z" fill="#765338"/>
+ <path d="m8.4667 12.452-4.9056 3.5642-3.0319-9.3311z" fill="#b7835a"/>
+ <path d="m8.4667 12.452 7.9375-5.7669-3.0319 9.3311z" fill="#5b422d"/>
+ <path d="m8.8308 12.716-0.36417 0.26458-0.36417-0.26458c0-0.26458 0.36417-0.26458 0.36417-0.26458s0.36417 0 0.36417 0.26458z" fill="#72b147"/>
+ <path d="m8.4667 12.452s-2e-7 -5.7669 7.9375-5.7669l-0.22507 0.69269-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819z" fill="#5a9a30"/>
+ <path d="m8.1025 12.716-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.22507-0.69269c7.9375 1e-7 7.9375 5.7669 7.9375 5.7669z" fill="#88b858"/>
+ <path d="m0.52917 6.6846 7.9375 5.7669 7.9375-5.7669-7.9375-5.7669z" fill="url(#linearGradient84726)"/>
+ </g>
+ <path d="m0.75424 7.3773-0.22507-0.69269 7.9375 5.7669 7.9375-5.7669-0.22507 0.69269-7.7124 5.6034z" fill-opacity="0"/>
</svg>
diff --git a/launcher/settings/Setting.h b/launcher/settings/Setting.h
index 9beeb35e..9a5b8210 100644
--- a/launcher/settings/Setting.h
+++ b/launcher/settings/Setting.h
@@ -33,7 +33,7 @@ public:
* Construct a Setting
*
* Synonyms are all the possible names used in the settings object, in order of preference.
- * First synonym is the ID, which identifies the setting in MultiMC.
+ * First synonym is the ID, which identifies the setting in PolyMC.
*
* defVal is the default value that will be returned when the settings object
* doesn't have any value for this setting.
@@ -115,3 +115,4 @@ protected:
QStringList m_synonyms;
QVariant m_defVal;
};
+
diff --git a/launcher/tools/MCEditTool.cpp b/launcher/tools/MCEditTool.cpp
index 21e1a3b0..2c1ec613 100644
--- a/launcher/tools/MCEditTool.cpp
+++ b/launcher/tools/MCEditTool.cpp
@@ -52,7 +52,7 @@ QString MCEditTool::getProgramPath()
#else
const QString mceditPath = path();
QDir mceditDir(mceditPath);
-#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
+#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
if (mceditDir.exists("mcedit.sh"))
{
return mceditDir.absoluteFilePath("mcedit.sh");
diff --git a/launcher/translations/TranslationsModel.cpp b/launcher/translations/TranslationsModel.cpp
index 2e744007..250854d3 100644
--- a/launcher/translations/TranslationsModel.cpp
+++ b/launcher/translations/TranslationsModel.cpp
@@ -143,6 +143,11 @@ struct TranslationsModel::Private
std::unique_ptr<POTranslator> m_po_translator;
QFileSystemWatcher *watcher;
+
+ const QString m_system_locale = QLocale::system().name();
+ const QString m_system_language = m_system_locale.split('_').front();
+
+ bool no_language_set = false;
};
TranslationsModel::TranslationsModel(QString path, QObject* parent): QAbstractListModel(parent)
@@ -164,7 +169,10 @@ TranslationsModel::~TranslationsModel()
void TranslationsModel::translationDirChanged(const QString& path)
{
qDebug() << "Dir changed:" << path;
- reloadLocalFiles();
+ if (!d->no_language_set)
+ {
+ reloadLocalFiles();
+ }
selectLanguage(selectedLanguage());
}
@@ -172,7 +180,26 @@ void TranslationsModel::indexReceived()
{
qDebug() << "Got translations index!";
d->m_index_job.reset();
- if(d->m_selectedLanguage != defaultLangCode)
+
+ if (d->no_language_set)
+ {
+ reloadLocalFiles();
+
+ auto language = d->m_system_locale;
+ if (!findLanguage(language))
+ {
+ language = d->m_system_language;
+ }
+ selectLanguage(language);
+ if (selectedLanguage() != defaultLangCode)
+ {
+ updateLanguage(selectedLanguage());
+ }
+ APPLICATION->settings()->set("Language", selectedLanguage());
+ d->no_language_set = false;
+ }
+
+ else if(d->m_selectedLanguage != defaultLangCode)
{
downloadTranslation(d->m_selectedLanguage);
}
@@ -319,8 +346,19 @@ void TranslationsModel::reloadLocalFiles()
{
d->m_languages.append(language);
}
- std::sort(d->m_languages.begin(), d->m_languages.end(), [](const Language& a, const Language& b) {
- return a.key.compare(b.key) < 0;
+ std::sort(d->m_languages.begin(), d->m_languages.end(), [this](const Language& a, const Language& b) {
+ if (a.key != b.key)
+ {
+ if (a.key == d->m_system_locale || a.key == d->m_system_language)
+ {
+ return true;
+ }
+ if (b.key == d->m_system_locale || b.key == d->m_system_language)
+ {
+ return false;
+ }
+ }
+ return a.key < b.key;
});
endInsertRows();
}
@@ -439,6 +477,12 @@ bool TranslationsModel::selectLanguage(QString key)
{
QString &langCode = key;
auto langPtr = findLanguage(key);
+
+ if (langCode.isEmpty())
+ {
+ d->no_language_set = true;
+ }
+
if(!langPtr)
{
qWarning() << "Selected invalid language" << key << ", defaulting to" << defaultLangCode;
@@ -576,7 +620,7 @@ void TranslationsModel::downloadIndex()
d->m_index_job = new NetJob("Translations Index", APPLICATION->network());
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("translations", "index_v2.json");
entry->setStale(true);
- d->m_index_task = Net::Download::makeCached(QUrl("https://files.multimc.org/translations/index_v2.json"), entry);
+ d->m_index_task = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATIONS_BASE_URL + "index_v2.json"), entry);
d->m_index_job->addNetAction(d->m_index_task);
connect(d->m_index_job.get(), &NetJob::failed, this, &TranslationsModel::indexFailed);
connect(d->m_index_job.get(), &NetJob::succeeded, this, &TranslationsModel::indexReceived);
diff --git a/launcher/ui/GuiUtil.cpp b/launcher/ui/GuiUtil.cpp
index efb1a4df..9eb658e2 100644
--- a/launcher/ui/GuiUtil.cpp
+++ b/launcher/ui/GuiUtil.cpp
@@ -16,21 +16,8 @@
QString GuiUtil::uploadPaste(const QString &text, QWidget *parentWidget)
{
ProgressDialog dialog(parentWidget);
- auto APIKeySetting = APPLICATION->settings()->get("PasteEEAPIKey").toString();
- if(APIKeySetting == "multimc")
- {
- APIKeySetting = BuildConfig.PASTE_EE_KEY;
- }
- std::unique_ptr<PasteUpload> paste(new PasteUpload(parentWidget, text, APIKeySetting));
-
- if (!paste->validateText())
- {
- CustomMessageBox::selectable(
- parentWidget, QObject::tr("Upload failed"),
- QObject::tr("The log file is too big. You'll have to upload it manually."),
- QMessageBox::Warning)->exec();
- return QString();
- }
+ auto pasteUrlSetting = APPLICATION->settings()->get("PastebinURL").toString();
+ std::unique_ptr<PasteUpload> paste(new PasteUpload(parentWidget, text, pasteUrlSetting));
dialog.execWithTask(paste.get());
if (!paste->wasSuccessful())
diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp
index c0ba8839..ad7227cc 100644
--- a/launcher/ui/MainWindow.cpp
+++ b/launcher/ui/MainWindow.cpp
@@ -201,7 +201,6 @@ public:
//TranslatedAction actionRefresh;
TranslatedAction actionCheckUpdate;
TranslatedAction actionSettings;
- TranslatedAction actionPatreon;
TranslatedAction actionMoreNews;
TranslatedAction actionManageAccounts;
TranslatedAction actionLaunchInstance;
@@ -400,14 +399,6 @@ public:
mainToolBar->addSeparator();
- actionPatreon = TranslatedAction(MainWindow);
- actionPatreon->setObjectName(QStringLiteral("actionPatreon"));
- actionPatreon->setIcon(APPLICATION->getThemedIcon("patreon"));
- actionPatreon.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Support %1"));
- actionPatreon.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the %1 Patreon page."));
- all_actions.append(&actionPatreon);
- mainToolBar->addAction(actionPatreon);
-
actionCAT = TranslatedAction(MainWindow);
actionCAT->setObjectName(QStringLiteral("actionCAT"));
actionCAT->setCheckable(true);
@@ -1694,14 +1685,9 @@ void MainWindow::on_actionReportBug_triggered()
DesktopServices::openUrl(QUrl(BuildConfig.BUG_TRACKER_URL));
}
-void MainWindow::on_actionPatreon_triggered()
-{
- DesktopServices::openUrl(QUrl("https://www.patreon.com/multimc"));
-}
-
void MainWindow::on_actionMoreNews_triggered()
{
- DesktopServices::openUrl(QUrl("https://multimc.org/posts.html"));
+ DesktopServices::openUrl(QUrl(BuildConfig.NEWS_OPEN_URL));
}
void MainWindow::newsButtonClicked()
@@ -1713,7 +1699,7 @@ void MainWindow::newsButtonClicked()
}
else
{
- DesktopServices::openUrl(QUrl("https://multimc.org/posts.html"));
+ DesktopServices::openUrl(QUrl(BuildConfig.NEWS_OPEN_URL));
}
}
diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h
index e462c524..f6940ab0 100644
--- a/launcher/ui/MainWindow.h
+++ b/launcher/ui/MainWindow.h
@@ -109,8 +109,6 @@ private slots:
void on_actionReportBug_triggered();
- void on_actionPatreon_triggered();
-
void on_actionMoreNews_triggered();
void newsButtonClicked();
@@ -223,3 +221,4 @@ private:
// managed by the application object
Task *m_versionLoadTask = nullptr;
};
+
diff --git a/launcher/ui/dialogs/AboutDialog.cpp b/launcher/ui/dialogs/AboutDialog.cpp
index 9795c38b..ef96cc23 100644
--- a/launcher/ui/dialogs/AboutDialog.cpp
+++ b/launcher/ui/dialogs/AboutDialog.cpp
@@ -26,15 +26,20 @@
namespace {
// Credits
// This is a hack, but I can't think of a better way to do this easily without screwing with QTextDocument...
-QString getCreditsHtml(QStringList patrons)
+QString getCreditsHtml()
{
- QString patronsHeading = QObject::tr("Patrons", "About Credits");
QString output;
QTextStream stream(&output);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
stream << "<center>\n";
+
+ stream << "<h3>" << QObject::tr("PolyMC Developers", "About Credits") << "</h3>\n";
+ stream << "<p>swirl &lt;<a href='mailto:swurl@swurl.xyz'>swurl@swurl.xyz </a>&gt;</p>\n";
+ stream << "<p>LennyMcLennington &lt;<a href='mailto:lenny@sneed.church'>lenny@sneed.church</a>&gt;</p>\n";
+ stream << "<br />\n";
+
// TODO: possibly retrieve from git history at build time?
- stream << "<h3>" << QObject::tr("Developers", "About Credits") << "</h3>\n";
+ stream << "<h3>" << QObject::tr("MultiMC Developers", "About Credits") << "</h3>\n";
stream << "<p>Andrew Okin &lt;<a href='mailto:forkk@forkk.net'>forkk@forkk.net</a>&gt;</p>\n";
stream << "<p>Petr Mrázek &lt;<a href='mailto:peterix@gmail.com'>peterix@gmail.com</a>&gt;</p>\n";
stream << "<p>Sky Welch &lt;<a href='mailto:multimc@bunnies.io'>multimc@bunnies.io</a>&gt;</p>\n";
@@ -48,15 +53,9 @@ QString getCreditsHtml(QStringList patrons)
stream << "<p>Kilobyte &lt;<a href='mailto:stiepen22@gmx.de'>stiepen22@gmx.de</a>&gt;</p>\n";
stream << "<p>Rootbear75 &lt;<a href='https://twitter.com/rootbear75'>@rootbear75</a>&gt;</p>\n";
stream << "<p>Zeker Zhayard &lt;<a href='https://twitter.com/zeker_zhayard'>@Zeker_Zhayard</a>&gt;</p>\n";
+ stream << "<p>Everyone else who <a href='https://github.com/PolyMC/PolyMC/graphs/contributors'>contributed</a>!</p>\n";
stream << "<br />\n";
- if(!patrons.isEmpty()) {
- stream << "<h3>" << QObject::tr("Patrons", "About Credits") << "</h3>\n";
- for (QString patron : patrons)
- {
- stream << "<p>" << patron << "</p>\n";
- }
- }
stream << "</center>\n";
return output;
}
@@ -80,7 +79,7 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia
setWindowTitle(tr("About %1").arg(launcherName));
- QString chtml = getCreditsHtml(QStringList());
+ QString chtml = getCreditsHtml();
ui->creditsText->setHtml(chtml);
QString lhtml = getLicenseHtml();
@@ -91,8 +90,12 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia
ui->icon->setPixmap(APPLICATION->getThemedIcon("logo").pixmap(64));
ui->title->setText(launcherName);
- ui->versionLabel->setText(tr("Version") +": " + BuildConfig.printableVersionString());
- ui->platformLabel->setText(tr("Platform") +": " + BuildConfig.BUILD_PLATFORM);
+ ui->versionLabel->setText(BuildConfig.printableVersionString());
+
+ if (!BuildConfig.BUILD_PLATFORM.isEmpty())
+ ui->platformLabel->setText(tr("Platform") +": " + BuildConfig.BUILD_PLATFORM);
+ else
+ ui->platformLabel->setVisible(false);
if (BuildConfig.VERSION_BUILD >= 0)
ui->buildNumLabel->setText(tr("Build Number") +": " + QString::number(BuildConfig.VERSION_BUILD));
@@ -104,46 +107,18 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia
else
ui->channelLabel->setVisible(false);
- ui->redistributionText->setHtml(tr(
-"<p>We keep MultiMC open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.</p>\n"
-"<p>Part of the reason for using the Apache license is we don't want people using the &quot;MultiMC&quot; name when redistributing the project. "
-"This means people must take the time to go through the source code and remove all references to &quot;MultiMC&quot;, including but not limited to the project "
-"icon and the title of windows, (no <b>MultiMC-fork</b> in the title).</p>\n"
-"<p>The Apache license covers reasonable use for the name - a mention of the project's origins in the About dialog and the license is acceptable. "
-"However, it should be abundantly clear that the project is a fork <b>without</b> implying that you have our blessing.</p>"
- ));
-
QString urlText("<html><head/><body><p><a href=\"%1\">%1</a></p></body></html>");
ui->urlLabel->setText(urlText.arg(BuildConfig.LAUNCHER_GIT));
- QString copyText("© 2012-2021 %1");
+ QString copyText("© 2021-2022 %1");
ui->copyLabel->setText(copyText.arg(BuildConfig.LAUNCHER_COPYRIGHT));
connect(ui->closeButton, SIGNAL(clicked()), SLOT(close()));
connect(ui->aboutQt, &QPushButton::clicked, &QApplication::aboutQt);
-
- loadPatronList();
}
AboutDialog::~AboutDialog()
{
delete ui;
}
-
-void AboutDialog::loadPatronList()
-{
- netJob = new NetJob("Patreon Patron List", APPLICATION->network());
- netJob->addNetAction(Net::Download::makeByteArray(QUrl("https://files.multimc.org/patrons.txt"), &dataSink));
- connect(netJob.get(), &NetJob::succeeded, this, &AboutDialog::patronListLoaded);
- netJob->start();
-}
-
-void AboutDialog::patronListLoaded()
-{
- QString patronListStr(dataSink);
- dataSink.clear();
- QString html = getCreditsHtml(patronListStr.split("\n", QString::SkipEmptyParts));
- ui->creditsText->setHtml(html);
-}
-
diff --git a/launcher/ui/dialogs/AboutDialog.h b/launcher/ui/dialogs/AboutDialog.h
index cc4b8850..814fd98c 100644
--- a/launcher/ui/dialogs/AboutDialog.h
+++ b/launcher/ui/dialogs/AboutDialog.h
@@ -31,17 +31,10 @@ public:
explicit AboutDialog(QWidget *parent = 0);
~AboutDialog();
-public
-slots:
- /// Starts loading a list of Patreon patrons.
- void loadPatronList();
-
- /// Slot for when the patron list loads successfully.
- void patronListLoaded();
-
private:
Ui::AboutDialog *ui;
NetJob::Ptr netJob;
QByteArray dataSink;
};
+
diff --git a/launcher/ui/dialogs/AboutDialog.ui b/launcher/ui/dialogs/AboutDialog.ui
index 422e877b..58275c66 100644
--- a/launcher/ui/dialogs/AboutDialog.ui
+++ b/launcher/ui/dialogs/AboutDialog.ui
@@ -80,13 +80,20 @@
</font>
</property>
<property name="text">
- <string notr="true">MultiMC 5</string>
+ <string notr="true">PolyMC</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
+ <item>
+ <widget class="QLabel" name="versionLabel">
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
@@ -152,16 +159,6 @@
</widget>
</item>
<item>
- <widget class="QLabel" name="versionLabel">
- <property name="text">
- <string>Version:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item>
<widget class="QLabel" name="platformLabel">
<property name="text">
<string>Platform:</string>
@@ -251,20 +248,6 @@
</item>
</layout>
</widget>
- <widget class="QWidget" name="forkingTab">
- <attribute name="title">
- <string>Forking/Redistribution</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_4">
- <item>
- <widget class="QTextEdit" name="redistributionText">
- <property name="textInteractionFlags">
- <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
</widget>
</item>
<item>
@@ -307,7 +290,6 @@
<tabstop>tabWidget</tabstop>
<tabstop>creditsText</tabstop>
<tabstop>licenseText</tabstop>
- <tabstop>redistributionText</tabstop>
<tabstop>aboutQt</tabstop>
<tabstop>closeButton</tabstop>
</tabstops>
diff --git a/launcher/ui/dialogs/ExportInstanceDialog.cpp b/launcher/ui/dialogs/ExportInstanceDialog.cpp
index 1a164875..f3bf7abe 100644
--- a/launcher/ui/dialogs/ExportInstanceDialog.cpp
+++ b/launcher/ui/dialogs/ExportInstanceDialog.cpp
@@ -403,7 +403,13 @@ bool ExportInstanceDialog::doExport()
auto & blocked = proxyModel->blockedPaths();
using std::placeholders::_1;
- if (!JlCompress::compressDir(output, m_instance->instanceRoot(), name, std::bind(&SeparatorPrefixTree<'/'>::covers, blocked, _1)))
+ auto files = QFileInfoList();
+ if (!MMCZip::collectFileListRecursively(m_instance->instanceRoot(), nullptr, &files,
+ std::bind(&SeparatorPrefixTree<'/'>::covers, blocked, _1))) {
+ QMessageBox::warning(this, tr("Error"), tr("Unable to export instance"));
+ return false;
+ }
+ if (!MMCZip::compressDirFiles(output, m_instance->instanceRoot(), files))
{
QMessageBox::warning(this, tr("Error"), tr("Unable to export instance"));
return false;
diff --git a/launcher/ui/dialogs/MSALoginDialog.cpp b/launcher/ui/dialogs/MSALoginDialog.cpp
index f46aa3b9..174ad46c 100644
--- a/launcher/ui/dialogs/MSALoginDialog.cpp
+++ b/launcher/ui/dialogs/MSALoginDialog.cpp
@@ -16,15 +16,19 @@
#include "MSALoginDialog.h"
#include "ui_MSALoginDialog.h"
+#include "DesktopServices.h"
#include "minecraft/auth/AccountTask.h"
#include <QtWidgets/QPushButton>
#include <QUrl>
+#include <QApplication>
+#include <QClipboard>
MSALoginDialog::MSALoginDialog(QWidget *parent) : QDialog(parent), ui(new Ui::MSALoginDialog)
{
ui->setupUi(this);
ui->progressBar->setVisible(false);
+ ui->actionButton->setVisible(false);
// ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
@@ -81,10 +85,17 @@ void MSALoginDialog::showVerificationUriAndCode(const QUrl& uri, const QString&
QString urlString = uri.toString();
QString linkString = QString("<a href=\"%1\">%2</a>").arg(urlString, urlString);
ui->label->setText(tr("<p>Please open up %1 in a browser and put in the code <b>%2</b> to proceed with login.</p>").arg(linkString, code));
+ ui->actionButton->setVisible(true);
+ connect(ui->actionButton, &QPushButton::clicked, [=]() {
+ DesktopServices::openUrl(uri);
+ QClipboard* cb = QApplication::clipboard();
+ cb->setText(code);
+ });
}
void MSALoginDialog::hideVerificationUriAndCode() {
m_externalLoginTimer.stop();
+ ui->actionButton->setVisible(false);
}
void MSALoginDialog::setUserInputsEnabled(bool enable)
@@ -110,6 +121,7 @@ void MSALoginDialog::onTaskFailed(const QString &reason)
// Re-enable user-interaction
setUserInputsEnabled(true);
ui->progressBar->setVisible(false);
+ ui->actionButton->setVisible(false);
}
void MSALoginDialog::onTaskSucceeded()
diff --git a/launcher/ui/dialogs/MSALoginDialog.ui b/launcher/ui/dialogs/MSALoginDialog.ui
index 78cbfb26..c18d01a1 100644
--- a/launcher/ui/dialogs/MSALoginDialog.ui
+++ b/launcher/ui/dialogs/MSALoginDialog.ui
@@ -49,14 +49,25 @@ aaaaa</string>
</widget>
</item>
<item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel</set>
- </property>
- </widget>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="actionButton">
+ <property name="text">
+ <string>Open page and copy code</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
</layout>
</widget>
diff --git a/launcher/ui/dialogs/ModDownloadDialog.cpp b/launcher/ui/dialogs/ModDownloadDialog.cpp
new file mode 100644
index 00000000..6b807b8c
--- /dev/null
+++ b/launcher/ui/dialogs/ModDownloadDialog.cpp
@@ -0,0 +1,98 @@
+#include "ModDownloadDialog.h"
+
+#include <BaseVersion.h>
+#include <icons/IconList.h>
+#include <InstanceList.h>
+
+#include "ProgressDialog.h"
+
+#include <QLayout>
+#include <QPushButton>
+#include <QValidator>
+#include <QDialogButtonBox>
+
+#include "ui/widgets/PageContainer.h"
+#include "ui/pages/modplatform/modrinth/ModrinthPage.h"
+#include "ModDownloadTask.h"
+
+
+ModDownloadDialog::ModDownloadDialog(const std::shared_ptr<ModFolderModel> &mods, QWidget *parent,
+ BaseInstance *instance)
+ : QDialog(parent), mods(mods), m_instance(instance)
+{
+ setObjectName(QStringLiteral("ModDownloadDialog"));
+ resize(400, 347);
+ m_verticalLayout = new QVBoxLayout(this);
+ m_verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
+
+ setWindowIcon(APPLICATION->getThemedIcon("new"));
+ // NOTE: m_buttons must be initialized before PageContainer, because it indirectly accesses m_buttons through setSuggestedPack! Do not move this below.
+ m_buttons = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+
+ m_container = new PageContainer(this);
+ m_container->setSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Expanding);
+ m_container->layout()->setContentsMargins(0, 0, 0, 0);
+ m_verticalLayout->addWidget(m_container);
+
+ m_container->addButtons(m_buttons);
+
+ // Bonk Qt over its stupid head and make sure it understands which button is the default one...
+ // See: https://stackoverflow.com/questions/24556831/qbuttonbox-set-default-button
+ auto OkButton = m_buttons->button(QDialogButtonBox::Ok);
+ OkButton->setDefault(true);
+ OkButton->setAutoDefault(true);
+ connect(OkButton, &QPushButton::clicked, this, &ModDownloadDialog::accept);
+
+ auto CancelButton = m_buttons->button(QDialogButtonBox::Cancel);
+ CancelButton->setDefault(false);
+ CancelButton->setAutoDefault(false);
+ connect(CancelButton, &QPushButton::clicked, this, &ModDownloadDialog::reject);
+
+ auto HelpButton = m_buttons->button(QDialogButtonBox::Help);
+ HelpButton->setDefault(false);
+ HelpButton->setAutoDefault(false);
+ connect(HelpButton, &QPushButton::clicked, m_container, &PageContainer::help);
+ QMetaObject::connectSlotsByName(this);
+ setWindowModality(Qt::WindowModal);
+ setWindowTitle("Download mods");
+}
+
+QString ModDownloadDialog::dialogTitle()
+{
+ return tr("Download mods");
+}
+
+void ModDownloadDialog::reject()
+{
+ QDialog::reject();
+}
+
+void ModDownloadDialog::accept()
+{
+ QDialog::accept();
+}
+
+QList<BasePage *> ModDownloadDialog::getPages()
+{
+ modrinthPage = new ModrinthPage(this, m_instance);
+ flameModPage = new FlameModPage(this, m_instance);
+ return
+ {
+ modrinthPage,
+ flameModPage
+ };
+}
+
+void ModDownloadDialog::setSuggestedMod(const QString& name, ModDownloadTask* task)
+{
+ modTask.reset(task);
+ m_buttons->button(QDialogButtonBox::Ok)->setEnabled(task);
+}
+
+ModDownloadDialog::~ModDownloadDialog()
+{
+}
+
+ModDownloadTask *ModDownloadDialog::getTask() {
+ return modTask.release();
+}
diff --git a/launcher/ui/dialogs/ModDownloadDialog.h b/launcher/ui/dialogs/ModDownloadDialog.h
new file mode 100644
index 00000000..ece8e328
--- /dev/null
+++ b/launcher/ui/dialogs/ModDownloadDialog.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <QDialog>
+#include <QVBoxLayout>
+
+#include "BaseVersion.h"
+#include "ui/pages/BasePageProvider.h"
+#include "minecraft/mod/ModFolderModel.h"
+#include "ModDownloadTask.h"
+#include "ui/pages/modplatform/flame/FlameModPage.h"
+
+namespace Ui
+{
+class ModDownloadDialog;
+}
+
+class PageContainer;
+class QDialogButtonBox;
+class ModrinthPage;
+
+class ModDownloadDialog : public QDialog, public BasePageProvider
+{
+ Q_OBJECT
+
+public:
+ explicit ModDownloadDialog(const std::shared_ptr<ModFolderModel> &mods, QWidget *parent, BaseInstance *instance);
+ ~ModDownloadDialog();
+
+ QString dialogTitle() override;
+ QList<BasePage *> getPages() override;
+
+ void setSuggestedMod(const QString & name = QString(), ModDownloadTask * task = nullptr);
+
+ ModDownloadTask * getTask();
+ const std::shared_ptr<ModFolderModel> &mods;
+
+public slots:
+ void accept() override;
+ void reject() override;
+
+//private slots:
+
+private:
+ Ui::ModDownloadDialog *ui = nullptr;
+ PageContainer * m_container = nullptr;
+ QDialogButtonBox * m_buttons = nullptr;
+ QVBoxLayout *m_verticalLayout = nullptr;
+
+
+ ModrinthPage *modrinthPage = nullptr;
+ FlameModPage *flameModPage = nullptr;
+ std::unique_ptr<ModDownloadTask> modTask;
+ BaseInstance *m_instance;
+};
diff --git a/launcher/ui/dialogs/OfflineLoginDialog.cpp b/launcher/ui/dialogs/OfflineLoginDialog.cpp
new file mode 100644
index 00000000..345ed40a
--- /dev/null
+++ b/launcher/ui/dialogs/OfflineLoginDialog.cpp
@@ -0,0 +1,98 @@
+#include "OfflineLoginDialog.h"
+#include "ui_OfflineLoginDialog.h"
+
+#include "minecraft/auth/AccountTask.h"
+
+#include <QtWidgets/QPushButton>
+
+OfflineLoginDialog::OfflineLoginDialog(QWidget *parent) : QDialog(parent), ui(new Ui::OfflineLoginDialog)
+{
+ ui->setupUi(this);
+ ui->progressBar->setVisible(false);
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+
+ connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+}
+
+OfflineLoginDialog::~OfflineLoginDialog()
+{
+ delete ui;
+}
+
+// Stage 1: User interaction
+void OfflineLoginDialog::accept()
+{
+ setUserInputsEnabled(false);
+ ui->progressBar->setVisible(true);
+
+ // Setup the login task and start it
+ m_account = MinecraftAccount::createOffline(ui->userTextBox->text());
+ m_loginTask = m_account->loginOffline();
+ connect(m_loginTask.get(), &Task::failed, this, &OfflineLoginDialog::onTaskFailed);
+ connect(m_loginTask.get(), &Task::succeeded, this, &OfflineLoginDialog::onTaskSucceeded);
+ connect(m_loginTask.get(), &Task::status, this, &OfflineLoginDialog::onTaskStatus);
+ connect(m_loginTask.get(), &Task::progress, this, &OfflineLoginDialog::onTaskProgress);
+ m_loginTask->start();
+}
+
+void OfflineLoginDialog::setUserInputsEnabled(bool enable)
+{
+ ui->userTextBox->setEnabled(enable);
+ ui->buttonBox->setEnabled(enable);
+}
+
+// Enable the OK button only when the textbox contains something.
+void OfflineLoginDialog::on_userTextBox_textEdited(const QString &newText)
+{
+ ui->buttonBox->button(QDialogButtonBox::Ok)
+ ->setEnabled(!newText.isEmpty());
+}
+
+void OfflineLoginDialog::onTaskFailed(const QString &reason)
+{
+ // Set message
+ auto lines = reason.split('\n');
+ QString processed;
+ for(auto line: lines) {
+ if(line.size()) {
+ processed += "<font color='red'>" + line + "</font><br />";
+ }
+ else {
+ processed += "<br />";
+ }
+ }
+ ui->label->setText(processed);
+
+ // Re-enable user-interaction
+ setUserInputsEnabled(true);
+ ui->progressBar->setVisible(false);
+}
+
+void OfflineLoginDialog::onTaskSucceeded()
+{
+ QDialog::accept();
+}
+
+void OfflineLoginDialog::onTaskStatus(const QString &status)
+{
+ ui->label->setText(status);
+}
+
+void OfflineLoginDialog::onTaskProgress(qint64 current, qint64 total)
+{
+ ui->progressBar->setMaximum(total);
+ ui->progressBar->setValue(current);
+}
+
+// Public interface
+MinecraftAccountPtr OfflineLoginDialog::newAccount(QWidget *parent, QString msg)
+{
+ OfflineLoginDialog dlg(parent);
+ dlg.ui->label->setText(msg);
+ if (dlg.exec() == QDialog::Accepted)
+ {
+ return dlg.m_account;
+ }
+ return 0;
+}
diff --git a/launcher/ui/dialogs/OfflineLoginDialog.h b/launcher/ui/dialogs/OfflineLoginDialog.h
new file mode 100644
index 00000000..5e608379
--- /dev/null
+++ b/launcher/ui/dialogs/OfflineLoginDialog.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <QtWidgets/QDialog>
+#include <QtCore/QEventLoop>
+
+#include "minecraft/auth/MinecraftAccount.h"
+#include "tasks/Task.h"
+
+namespace Ui
+{
+class OfflineLoginDialog;
+}
+
+class OfflineLoginDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ ~OfflineLoginDialog();
+
+ static MinecraftAccountPtr newAccount(QWidget *parent, QString message);
+
+private:
+ explicit OfflineLoginDialog(QWidget *parent = 0);
+
+ void setUserInputsEnabled(bool enable);
+
+protected
+slots:
+ void accept();
+
+ void onTaskFailed(const QString &reason);
+ void onTaskSucceeded();
+ void onTaskStatus(const QString &status);
+ void onTaskProgress(qint64 current, qint64 total);
+
+ void on_userTextBox_textEdited(const QString &newText);
+
+private:
+ Ui::OfflineLoginDialog *ui;
+ MinecraftAccountPtr m_account;
+ Task::Ptr m_loginTask;
+};
diff --git a/launcher/ui/dialogs/OfflineLoginDialog.ui b/launcher/ui/dialogs/OfflineLoginDialog.ui
new file mode 100644
index 00000000..d8964a2e
--- /dev/null
+++ b/launcher/ui/dialogs/OfflineLoginDialog.ui
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>OfflineLoginDialog</class>
+ <widget class="QDialog" name="OfflineLoginDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>150</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Add Account</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string notr="true">Message label placeholder.</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="userTextBox">
+ <property name="placeholderText">
+ <string>Username</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QProgressBar" name="progressBar">
+ <property name="value">
+ <number>69</number>
+ </property>
+ <property name="textVisible">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/launcher/ui/dialogs/UpdateDialog.cpp b/launcher/ui/dialogs/UpdateDialog.cpp
index c0f6074c..ec77d146 100644
--- a/launcher/ui/dialogs/UpdateDialog.cpp
+++ b/launcher/ui/dialogs/UpdateDialog.cpp
@@ -38,12 +38,12 @@ void UpdateDialog::loadChangelog()
QString url;
if(channel == "stable")
{
- url = QString("https://raw.githubusercontent.com/MultiMC/Launcher/%1/changelog.md").arg(channel);
+ url = QString("https://raw.githubusercontent.com/PolyMC/PolyMC/%1/changelog.md").arg(channel);
m_changelogType = CHANGELOG_MARKDOWN;
}
else
{
- url = QString("https://api.github.com/repos/MultiMC/Launcher/compare/%1...%2").arg(BuildConfig.GIT_COMMIT, channel);
+ url = QString("https://api.github.com/repos/PolyMC/PolyMC/compare/%1...%2").arg(BuildConfig.GIT_COMMIT, channel);
m_changelogType = CHANGELOG_COMMITS;
}
dljob->addNetAction(Net::Download::makeByteArray(QUrl(url), &changelogData));
@@ -58,7 +58,7 @@ QString reprocessMarkdown(QByteArray markdown)
QString output = hoedown.process(markdown);
// HACK: easier than customizing hoedown
- output.replace(QRegExp("GH-([0-9]+)"), "<a href=\"https://github.com/MultiMC/Launcher/issues/\\1\">GH-\\1</a>");
+ output.replace(QRegExp("GH-([0-9]+)"), "<a href=\"https://github.com/PolyMC/PolyMC/issues/\\1\">GH-\\1</a>");
qDebug() << output;
return output;
}
@@ -100,7 +100,7 @@ QString reprocessCommits(QByteArray json)
result += "<tr><td>";
if(issuenr.length())
{
- result += QString("<a href=\"https://github.com/MultiMC/Launcher/issues/%1\">GH-%2</a>").arg(issuenr, issuenr);
+ result += QString("<a href=\"https://github.com/PolyMC/PolyMC/issues/%1\">GH-%2</a>").arg(issuenr, issuenr);
}
else if(prefix.length())
{
diff --git a/launcher/ui/dialogs/UpdateDialog.ui b/launcher/ui/dialogs/UpdateDialog.ui
index b0b3dd83..bd94a554 100644
--- a/launcher/ui/dialogs/UpdateDialog.ui
+++ b/launcher/ui/dialogs/UpdateDialog.ui
@@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
- <string>MultiMC Update</string>
+ <string>PolyMC Update</string>
</property>
<property name="windowIcon">
<iconset>
diff --git a/launcher/ui/pages/global/PasteEEPage.cpp b/launcher/ui/pages/global/APIPage.cpp
index 4b375d9a..ad79e00c 100644
--- a/launcher/ui/pages/global/PasteEEPage.cpp
+++ b/launcher/ui/pages/global/APIPage.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2021 MultiMC Contributors
+/* Copyright 2013-2021 MultiMC & PolyMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,69 +13,55 @@
* limitations under the License.
*/
-#include "PasteEEPage.h"
-#include "ui_PasteEEPage.h"
+#include "APIPage.h"
+#include "ui_APIPage.h"
#include <QMessageBox>
#include <QFileDialog>
#include <QStandardPaths>
#include <QTabBar>
+#include <QVariant>
#include "settings/SettingsObject.h"
#include "tools/BaseProfiler.h"
#include "Application.h"
-PasteEEPage::PasteEEPage(QWidget *parent) :
+APIPage::APIPage(QWidget *parent) :
QWidget(parent),
- ui(new Ui::PasteEEPage)
+ ui(new Ui::APIPage)
{
+ static QRegularExpression validUrlRegExp("https?://.+");
ui->setupUi(this);
+ ui->urlChoices->setValidator(new QRegularExpressionValidator(validUrlRegExp, ui->urlChoices));
ui->tabWidget->tabBar()->hide();\
- connect(ui->customAPIkeyEdit, &QLineEdit::textEdited, this, &PasteEEPage::textEdited);
loadSettings();
}
-PasteEEPage::~PasteEEPage()
+APIPage::~APIPage()
{
delete ui;
}
-void PasteEEPage::loadSettings()
+void APIPage::loadSettings()
{
auto s = APPLICATION->settings();
- QString keyToUse = s->get("PasteEEAPIKey").toString();
- if(keyToUse == "multimc")
- {
- ui->multimcButton->setChecked(true);
- }
- else
- {
- ui->customButton->setChecked(true);
- ui->customAPIkeyEdit->setText(keyToUse);
- }
+ QString pastebinURL = s->get("PastebinURL").toString();
+ ui->urlChoices->setCurrentText(pastebinURL);
+ QString msaClientID = s->get("MSAClientIDOverride").toString();
+ ui->msaClientID->setText(msaClientID);
}
-void PasteEEPage::applySettings()
+void APIPage::applySettings()
{
auto s = APPLICATION->settings();
-
- QString pasteKeyToUse;
- if (ui->customButton->isChecked())
- pasteKeyToUse = ui->customAPIkeyEdit->text();
- else
- {
- pasteKeyToUse = "multimc";
- }
- s->set("PasteEEAPIKey", pasteKeyToUse);
+ QString pastebinURL = ui->urlChoices->currentText();
+ s->set("PastebinURL", pastebinURL);
+ QString msaClientID = ui->msaClientID->text();
+ s->set("MSAClientIDOverride", msaClientID);
}
-bool PasteEEPage::apply()
+bool APIPage::apply()
{
applySettings();
return true;
}
-
-void PasteEEPage::textEdited(const QString& text)
-{
- ui->customButton->setChecked(true);
-}
diff --git a/launcher/ui/pages/global/PasteEEPage.h b/launcher/ui/pages/global/APIPage.h
index a1c7d434..9474ebbb 100644
--- a/launcher/ui/pages/global/PasteEEPage.h
+++ b/launcher/ui/pages/global/APIPage.h
@@ -21,32 +21,32 @@
#include <Application.h>
namespace Ui {
-class PasteEEPage;
+class APIPage;
}
-class PasteEEPage : public QWidget, public BasePage
+class APIPage : public QWidget, public BasePage
{
Q_OBJECT
public:
- explicit PasteEEPage(QWidget *parent = 0);
- ~PasteEEPage();
+ explicit APIPage(QWidget *parent = 0);
+ ~APIPage();
QString displayName() const override
{
- return tr("Log Upload");
+ return tr("APIs");
}
QIcon icon() const override
{
- return APPLICATION->getThemedIcon("log");
+ return APPLICATION->getThemedIcon("worlds");
}
QString id() const override
{
- return "log-upload";
+ return "apis";
}
QString helpPage() const override
{
- return "Log-Upload";
+ return "APIs";
}
virtual bool apply() override;
@@ -54,9 +54,7 @@ private:
void loadSettings();
void applySettings();
-private slots:
- void textEdited(const QString &text);
-
private:
- Ui::PasteEEPage *ui;
+ Ui::APIPage *ui;
};
+
diff --git a/launcher/ui/pages/global/APIPage.ui b/launcher/ui/pages/global/APIPage.ui
new file mode 100644
index 00000000..28c53b79
--- /dev/null
+++ b/launcher/ui/pages/global/APIPage.ui
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>APIPage</class>
+ <widget class="QWidget" name="APIPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>491</width>
+ <height>474</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string notr="true">Tab 1</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QGroupBox" name="groupBox_paste">
+ <property name="title">
+ <string>Pastebin URL</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Note: only input that starts with &lt;span style=&quot; font-weight:600;&quot;&gt;http://&lt;/span&gt; or &lt;span style=&quot; font-weight:600;&quot;&gt;https://&lt;/span&gt; will be accepted.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="urlChoices">
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ <property name="insertPolicy">
+ <enum>QComboBox::NoInsert</enum>
+ </property>
+ <item>
+ <property name="text">
+ <string>https://0x0.st</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>https://paste.polymc.org</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Here you can choose from a predefined list of paste services, or input the URL of a different paste service of your choice, provided it supports the same protocol as 0x0.st, that is POST a file parameter to the URL and return a link in the response body.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_msa">
+ <property name="title">
+ <string>Microsoft Authentication</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="Line" name="line_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Note: you probably don't need to set this if logging in via Microsoft Authentication already works.</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="msaClientID">
+ <property name="placeholderText">
+ <string>(Default)</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Enter a custom client ID for Microsoft Authentication here. </string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>tabWidget</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp
index d3eb2655..eb1ee8d3 100644
--- a/launcher/ui/pages/global/AccountListPage.cpp
+++ b/launcher/ui/pages/global/AccountListPage.cpp
@@ -24,6 +24,7 @@
#include "net/NetJob.h"
#include "ui/dialogs/ProgressDialog.h"
+#include "ui/dialogs/OfflineLoginDialog.h"
#include "ui/dialogs/LoginDialog.h"
#include "ui/dialogs/MSALoginDialog.h"
#include "ui/dialogs/CustomMessageBox.h"
@@ -37,8 +38,6 @@
#include "BuildConfig.h"
-#include "Secrets.h"
-
AccountListPage::AccountListPage(QWidget *parent)
: QMainWindow(parent), ui(new Ui::AccountListPage)
{
@@ -74,7 +73,10 @@ AccountListPage::AccountListPage(QWidget *parent)
updateButtonStates();
// Xbox authentication won't work without a client identifier, so disable the button if it is missing
- ui->actionAddMicrosoft->setVisible(Secrets::hasMSAClientID());
+ if (APPLICATION->getMSAClientID().isEmpty()) {
+ ui->actionAddMicrosoft->setVisible(false);
+ ui->actionAddMicrosoft->setToolTip(tr("No Microsoft Authentication client ID was set."));
+ }
}
AccountListPage::~AccountListPage()
@@ -134,8 +136,8 @@ void AccountListPage::on_actionAddMicrosoft_triggered()
this,
tr("Microsoft Accounts not available"),
tr(
- "Microsoft accounts are only usable on macOS 10.13 or newer, with fully updated MultiMC.\n\n"
- "Please update both your operating system and MultiMC."
+ "Microsoft accounts are only usable on macOS 10.13 or newer, with fully updated PolyMC.\n\n"
+ "Please update both your operating system and PolyMC."
),
QMessageBox::Warning
)->exec();
@@ -155,6 +157,35 @@ void AccountListPage::on_actionAddMicrosoft_triggered()
}
}
+void AccountListPage::on_actionAddOffline_triggered()
+{
+ if (!m_accounts->anyAccountIsValid()) {
+ QMessageBox::warning(
+ this,
+ tr("Error"),
+ tr(
+ "You must add a Microsoft or Mojang account that owns Minecraft before you can add an offline account."
+ "<br><br>"
+ "If you have lost your account you can contact Microsoft for support."
+ )
+ );
+ return;
+ }
+
+ MinecraftAccountPtr account = OfflineLoginDialog::newAccount(
+ this,
+ tr("Please enter your desired username to add your offline account.")
+ );
+
+ if (account)
+ {
+ m_accounts->addAccount(account);
+ if (m_accounts->count() == 1) {
+ m_accounts->setDefaultAccount(account);
+ }
+ }
+}
+
void AccountListPage::on_actionRemove_triggered()
{
QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes();
diff --git a/launcher/ui/pages/global/AccountListPage.h b/launcher/ui/pages/global/AccountListPage.h
index 1c65e708..841c3fd2 100644
--- a/launcher/ui/pages/global/AccountListPage.h
+++ b/launcher/ui/pages/global/AccountListPage.h
@@ -62,6 +62,7 @@ public:
public slots:
void on_actionAddMojang_triggered();
void on_actionAddMicrosoft_triggered();
+ void on_actionAddOffline_triggered();
void on_actionRemove_triggered();
void on_actionRefresh_triggered();
void on_actionSetDefault_triggered();
diff --git a/launcher/ui/pages/global/AccountListPage.ui b/launcher/ui/pages/global/AccountListPage.ui
index 29738c02..d21a92e2 100644
--- a/launcher/ui/pages/global/AccountListPage.ui
+++ b/launcher/ui/pages/global/AccountListPage.ui
@@ -54,6 +54,7 @@
</attribute>
<addaction name="actionAddMicrosoft"/>
<addaction name="actionAddMojang"/>
+ <addaction name="actionAddOffline"/>
<addaction name="actionRefresh"/>
<addaction name="actionRemove"/>
<addaction name="actionSetDefault"/>
@@ -103,6 +104,11 @@
<string>Add Microsoft</string>
</property>
</action>
+ <action name="actionAddOffline">
+ <property name="text">
+ <string>Add Offline</string>
+ </property>
+ </action>
<action name="actionRefresh">
<property name="text">
<string>Refresh</string>
diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp
index 2eb73e44..0ffe8050 100644
--- a/launcher/ui/pages/global/LauncherPage.cpp
+++ b/launcher/ui/pages/global/LauncherPage.cpp
@@ -73,11 +73,6 @@ LauncherPage::LauncherPage(QWidget *parent) : QWidget(parent), ui(new Ui::Launch
{
ui->updateSettingsBox->setHidden(true);
}
- // Analytics
- if(BuildConfig.ANALYTICS_ID.isEmpty())
- {
- ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->analyticsTab));
- }
connect(ui->fontSizeBox, SIGNAL(valueChanged(int)), SLOT(refreshFontPreview()));
connect(ui->consoleFont, SIGNAL(currentFontChanged(QFont)), SLOT(refreshFontPreview()));
@@ -251,32 +246,31 @@ void LauncherPage::applySettings()
//FIXME: make generic
switch (ui->themeComboBox->currentIndex())
{
- case 1:
+ case 0:
s->set("IconTheme", "pe_dark");
break;
- case 2:
+ case 1:
s->set("IconTheme", "pe_light");
break;
- case 3:
+ case 2:
s->set("IconTheme", "pe_blue");
break;
- case 4:
+ case 3:
s->set("IconTheme", "pe_colored");
break;
- case 5:
+ case 4:
s->set("IconTheme", "OSX");
break;
- case 6:
+ case 5:
s->set("IconTheme", "iOS");
break;
- case 7:
+ case 6:
s->set("IconTheme", "flat");
break;
- case 8:
+ case 7:
s->set("IconTheme", "custom");
break;
- case 0:
- default:
+ case 8:
s->set("IconTheme", "multimc");
break;
}
@@ -321,12 +315,6 @@ void LauncherPage::applySettings()
s->set("InstSortMode", "Name");
break;
}
-
- // Analytics
- if(!BuildConfig.ANALYTICS_ID.isEmpty())
- {
- s->set("Analytics", ui->analyticsCheck->isChecked());
- }
}
void LauncherPage::loadSettings()
{
@@ -338,40 +326,40 @@ void LauncherPage::loadSettings()
auto theme = s->get("IconTheme").toString();
if (theme == "pe_dark")
{
- ui->themeComboBox->setCurrentIndex(1);
+ ui->themeComboBox->setCurrentIndex(0);
}
else if (theme == "pe_light")
{
- ui->themeComboBox->setCurrentIndex(2);
+ ui->themeComboBox->setCurrentIndex(1);
}
else if (theme == "pe_blue")
{
- ui->themeComboBox->setCurrentIndex(3);
+ ui->themeComboBox->setCurrentIndex(2);
}
else if (theme == "pe_colored")
{
- ui->themeComboBox->setCurrentIndex(4);
+ ui->themeComboBox->setCurrentIndex(3);
}
else if (theme == "OSX")
{
- ui->themeComboBox->setCurrentIndex(5);
+ ui->themeComboBox->setCurrentIndex(4);
}
else if (theme == "iOS")
{
- ui->themeComboBox->setCurrentIndex(6);
+ ui->themeComboBox->setCurrentIndex(5);
}
else if (theme == "flat")
{
+ ui->themeComboBox->setCurrentIndex(6);
+ }
+ else if (theme == "multimc")
+ {
ui->themeComboBox->setCurrentIndex(7);
}
else if (theme == "custom")
{
ui->themeComboBox->setCurrentIndex(8);
}
- else
- {
- ui->themeComboBox->setCurrentIndex(0);
- }
{
auto currentTheme = s->get("ApplicationTheme").toString();
@@ -422,12 +410,6 @@ void LauncherPage::loadSettings()
{
ui->sortByNameBtn->setChecked(true);
}
-
- // Analytics
- if(!BuildConfig.ANALYTICS_ID.isEmpty())
- {
- ui->analyticsCheck->setChecked(s->get("Analytics").toBool());
- }
}
void LauncherPage::refreshFontPreview()
diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui
index 62a66d73..47fed873 100644
--- a/launcher/ui/pages/global/LauncherPage.ui
+++ b/launcher/ui/pages/global/LauncherPage.ui
@@ -264,11 +264,6 @@
</property>
<item>
<property name="text">
- <string>Default</string>
- </property>
- </item>
- <item>
- <property name="text">
<string>Simple (Dark Icons)</string>
</property>
</item>
@@ -307,6 +302,11 @@
<string>Custom</string>
</property>
</item>
+ <item>
+ <property name="text">
+ <string>MultiMC</string>
+ </property>
+ </item>
</widget>
</item>
<item row="1" column="1">
@@ -485,69 +485,6 @@
</item>
</layout>
</widget>
- <widget class="QWidget" name="analyticsTab">
- <attribute name="title">
- <string>Analytics</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_8">
- <item>
- <widget class="QGroupBox" name="consoleSettingsBox_2">
- <property name="title">
- <string>Analytics Settings</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_4">
- <item>
- <widget class="QCheckBox" name="analyticsCheck">
- <property name="text">
- <string>Send anonymous usage statistics?</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="Line" name="line">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="label_5">
- <property name="text">
- <string>&lt;html&gt;&lt;head/&gt;
-&lt;body&gt;
-&lt;p&gt;The launcher sends anonymous usage statistics on every start of the application.&lt;/p&gt;&lt;p&gt;The following data is collected:&lt;/p&gt;
-&lt;ul&gt;
-&lt;li&gt;Launcher version.&lt;/li&gt;
-&lt;li&gt;Operating system name, version and architecture.&lt;/li&gt;
-&lt;li&gt;CPU architecture (kernel architecture on linux).&lt;/li&gt;
-&lt;li&gt;Size of system memory.&lt;/li&gt;
-&lt;li&gt;Java version, architecture and memory settings.&lt;/li&gt;
-&lt;/ul&gt;
-&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
</widget>
</item>
</layout>
diff --git a/launcher/ui/pages/global/MinecraftPage.cpp b/launcher/ui/pages/global/MinecraftPage.cpp
index c763f8ac..5470a586 100644
--- a/launcher/ui/pages/global/MinecraftPage.cpp
+++ b/launcher/ui/pages/global/MinecraftPage.cpp
@@ -71,6 +71,9 @@ void MinecraftPage::applySettings()
s->set("ShowGameTime", ui->showGameTime->isChecked());
s->set("ShowGlobalGameTime", ui->showGlobalGameTime->isChecked());
s->set("RecordGameTime", ui->recordGameTime->isChecked());
+
+ // Miscellaneous
+ s->set("CloseAfterLaunch", ui->closeAfterLaunchCheck->isChecked());
}
void MinecraftPage::loadSettings()
@@ -88,4 +91,6 @@ void MinecraftPage::loadSettings()
ui->showGameTime->setChecked(s->get("ShowGameTime").toBool());
ui->showGlobalGameTime->setChecked(s->get("ShowGlobalGameTime").toBool());
ui->recordGameTime->setChecked(s->get("RecordGameTime").toBool());
+
+ ui->closeAfterLaunchCheck->setChecked(s->get("CloseAfterLaunch").toBool());
}
diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui
index 857b8cfb..a28b1f59 100644
--- a/launcher/ui/pages/global/MinecraftPage.ui
+++ b/launcher/ui/pages/global/MinecraftPage.ui
@@ -165,6 +165,25 @@
</widget>
</item>
<item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Miscellaneous</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QCheckBox" name="closeAfterLaunchCheck">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;PolyMC will automatically reopen when the game crashes or exits.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Close PolyMC after game window opens</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
<spacer name="verticalSpacerMinecraft">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -184,7 +203,6 @@
</layout>
</widget>
<tabstops>
- <tabstop>tabWidget</tabstop>
<tabstop>maximizedCheckBox</tabstop>
<tabstop>windowWidthSpinBox</tabstop>
<tabstop>windowHeightSpinBox</tabstop>
diff --git a/launcher/ui/pages/global/PasteEEPage.ui b/launcher/ui/pages/global/PasteEEPage.ui
deleted file mode 100644
index 10883781..00000000
--- a/launcher/ui/pages/global/PasteEEPage.ui
+++ /dev/null
@@ -1,128 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>PasteEEPage</class>
- <widget class="QWidget" name="PasteEEPage">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>491</width>
- <height>474</height>
- </rect>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <widget class="QTabWidget" name="tabWidget">
- <property name="currentIndex">
- <number>0</number>
- </property>
- <widget class="QWidget" name="tab">
- <attribute name="title">
- <string notr="true">Tab 1</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_2">
- <item>
- <widget class="QGroupBox" name="groupBox_2">
- <property name="title">
- <string>paste.ee API key</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_10">
- <item>
- <widget class="QRadioButton" name="multimcButton">
- <property name="text">
- <string>MultiMC key - 12MB &amp;upload limit</string>
- </property>
- <attribute name="buttonGroup">
- <string notr="true">pasteButtonGroup</string>
- </attribute>
- </widget>
- </item>
- <item>
- <widget class="QRadioButton" name="customButton">
- <property name="text">
- <string>&amp;Your own key - 12MB upload limit:</string>
- </property>
- <attribute name="buttonGroup">
- <string notr="true">pasteButtonGroup</string>
- </attribute>
- </widget>
- </item>
- <item>
- <widget class="QLineEdit" name="customAPIkeyEdit">
- <property name="echoMode">
- <enum>QLineEdit::Password</enum>
- </property>
- <property name="placeholderText">
- <string>Paste your API key here!</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="Line" name="line">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="label">
- <property name="text">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://paste.ee&quot;&gt;paste.ee&lt;/a&gt; is used by MultiMC for log uploads. If you have a &lt;a href=&quot;https://paste.ee&quot;&gt;paste.ee&lt;/a&gt; account, you can add your API key here and have your uploaded logs paired with your account.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- <property name="textFormat">
- <enum>Qt::RichText</enum>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- <property name="openExternalLinks">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>216</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- </widget>
- </item>
- </layout>
- </widget>
- <tabstops>
- <tabstop>tabWidget</tabstop>
- <tabstop>multimcButton</tabstop>
- <tabstop>customButton</tabstop>
- <tabstop>customAPIkeyEdit</tabstop>
- </tabstops>
- <resources/>
- <connections/>
- <buttongroups>
- <buttongroup name="pasteButtonGroup"/>
- </buttongroups>
-</ui>
diff --git a/launcher/ui/pages/instance/LegacyUpgradePage.ui b/launcher/ui/pages/instance/LegacyUpgradePage.ui
index 085919e3..b22c03e5 100644
--- a/launcher/ui/pages/instance/LegacyUpgradePage.ui
+++ b/launcher/ui/pages/instance/LegacyUpgradePage.ui
@@ -26,7 +26,14 @@
<item>
<widget class="QTextBrowser" name="textBrowser">
<property name="html">
- <string>&lt;html&gt;&lt;body&gt;&lt;h1&gt;Upgrade is required&lt;/h1&gt;&lt;p&gt;MultiMC now supports old Minecraft versions and all the required features in the new (OneSix) instance format. As a consequence, the old (Legacy) format has been entirely disabled and old instances need to be upgraded.&lt;/p&gt;&lt;p&gt;The upgrade will create a new instance with the same contents as the current one, in the new format. The original instance will remain untouched, in case anything goes wrong in the process.&lt;/p&gt;&lt;p&gt;Please report any issues on our &lt;a href=&quot;https://github.com/MultiMC/Launcher/issues&quot;&gt;github issues page&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;There is also a &lt;a href=&quot;https://discord.gg/GtPmv93&quot;&gt;discord channel for testing here&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Noto Sans'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;h1 style=&quot; margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:xx-large; font-weight:600;&quot;&gt;Upgrade is required&lt;/span&gt;&lt;/h1&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;PolyMC now supports old Minecraft versions and all the required features in the new (OneSix) instance format. As a consequence, the old (Legacy) format has been entirely disabled and old instances need to be upgraded.&lt;/p&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The upgrade will create a new instance with the same contents as the current one, in the new format. The original instance will remain untouched, in case anything goes wrong in the process.&lt;/p&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Please report any issues on our &lt;a href=&quot;https://github.com/PolyMC/PolyMC/issues&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#3584e4;&quot;&gt;github issues page&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
diff --git a/launcher/ui/pages/instance/LogPage.ui b/launcher/ui/pages/instance/LogPage.ui
index ccfc1551..31bb368c 100644
--- a/launcher/ui/pages/instance/LogPage.ui
+++ b/launcher/ui/pages/instance/LogPage.ui
@@ -100,7 +100,7 @@
<item>
<widget class="QPushButton" name="btnPaste">
<property name="toolTip">
- <string>Upload the log to paste.ee - it will stay online for a month</string>
+ <string>Upload the log to the paste service configured in preferences</string>
</property>
<property name="text">
<string>Upload</string>
diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp
index e63b1434..494d32f0 100644
--- a/launcher/ui/pages/instance/ModFolderPage.cpp
+++ b/launcher/ui/pages/instance/ModFolderPage.cpp
@@ -26,6 +26,7 @@
#include "Application.h"
#include "ui/dialogs/CustomMessageBox.h"
+#include "ui/dialogs/ModDownloadDialog.h"
#include "ui/GuiUtil.h"
#include "DesktopServices.h"
@@ -36,6 +37,7 @@
#include "minecraft/PackProfile.h"
#include "Version.h"
+#include "ui/dialogs/ProgressDialog.h"
namespace {
// FIXME: wasteful
@@ -141,6 +143,11 @@ ModFolderPage::ModFolderPage(
ui(new Ui::ModFolderPage)
{
ui->setupUi(this);
+ if(id == "mods") {
+ auto act = new QAction(tr("Install Mods"), this);
+ ui->actionsToolbar->insertActionBefore(ui->actionView_configs,act);
+ connect(act, &QAction::triggered, this, &ModFolderPage::on_actionInstall_mods_triggered);
+ }
ui->actionsToolbar->insertSpacer(ui->actionView_configs);
m_inst = inst;
@@ -342,6 +349,44 @@ void ModFolderPage::on_actionRemove_triggered()
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
+ }
+ bool hasFabric = !((MinecraftInstance *)m_inst)->getPackProfile()->getComponentVersion("net.fabricmc.fabric-loader").isEmpty();
+ bool hasForge = !((MinecraftInstance *)m_inst)->getPackProfile()->getComponentVersion("net.minecraftforge").isEmpty();
+ if (!hasFabric && !hasForge) {
+ QMessageBox::critical(this,tr("Error"),tr("Please install a mod loader first!"));
+ return;
+ }
+ ModDownloadDialog mdownload(m_mods, this, m_inst);
+ if(mdownload.exec()) {
+ ModDownloadTask *task = mdownload.getTask();
+ if (task) {
+ connect(task, &Task::failed, [this, task](QString reason) {
+ task->deleteLater();
+ CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show();
+ });
+ connect(task, &Task::succeeded, [this, task]() {
+ QStringList warnings = task->warnings();
+ if (warnings.count()) {
+ CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'),
+ QMessageBox::Warning)->show();
+ }
+ task->deleteLater();
+ });
+ ProgressDialog loadDialog(this);
+ loadDialog.setSkipButton(true, tr("Abort"));
+ loadDialog.execWithTask(task);
+ m_mods->update();
+ }
+ }
+}
+
void ModFolderPage::on_actionView_configs_triggered()
{
DesktopServices::openDirectory(m_inst->instanceConfigFolder(), true);
diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h
index 8ef7559b..fbda3cd8 100644
--- a/launcher/ui/pages/instance/ModFolderPage.h
+++ b/launcher/ui/pages/instance/ModFolderPage.h
@@ -102,6 +102,7 @@ slots:
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);
diff --git a/launcher/ui/pages/instance/OtherLogsPage.ui b/launcher/ui/pages/instance/OtherLogsPage.ui
index 56ff3b62..77f3e647 100644
--- a/launcher/ui/pages/instance/OtherLogsPage.ui
+++ b/launcher/ui/pages/instance/OtherLogsPage.ui
@@ -84,7 +84,7 @@
<item row="3" column="2">
<widget class="QPushButton" name="btnPaste">
<property name="toolTip">
- <string>Upload the log to paste.ee - it will stay online for a month</string>
+ <string>Upload the log to the paste service configured in preferences.</string>
</property>
<property name="text">
<string>Upload</string>
diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp
index 6e57909b..0fa5f68d 100644
--- a/launcher/ui/pages/instance/VersionPage.cpp
+++ b/launcher/ui/pages/instance/VersionPage.cpp
@@ -395,7 +395,7 @@ void VersionPage::on_actionDownload_All_triggered()
{
CustomMessageBox::selectable(
this, tr("Error"),
- tr("MultiMC cannot download Minecraft or update instances unless you have at least "
+ tr("PolyMC cannot download Minecraft or update instances unless you have at least "
"one account added.\nPlease add your Mojang or Minecraft account."),
QMessageBox::Warning)->show();
return;
@@ -635,4 +635,3 @@ void VersionPage::onFilterTextChanged(const QString &newContents)
}
#include "VersionPage.moc"
-
diff --git a/launcher/ui/pages/modplatform/flame/FlameModModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModModel.cpp
new file mode 100644
index 00000000..2cf83261
--- /dev/null
+++ b/launcher/ui/pages/modplatform/flame/FlameModModel.cpp
@@ -0,0 +1,273 @@
+#include "FlameModModel.h"
+#include "Application.h"
+#include "minecraft/MinecraftInstance.h"
+#include "minecraft/PackProfile.h"
+#include "FlameModPage.h"
+#include <Json.h>
+
+#include <MMCStrings.h>
+#include <Version.h>
+
+#include <QtMath>
+
+
+namespace FlameMod {
+
+ListModel::ListModel(FlameModPage *parent) : QAbstractListModel(parent)
+{
+}
+
+ListModel::~ListModel()
+{
+}
+
+int ListModel::rowCount(const QModelIndex &parent) const
+{
+ return modpacks.size();
+}
+
+int ListModel::columnCount(const QModelIndex &parent) const
+{
+ return 1;
+}
+
+QVariant ListModel::data(const QModelIndex &index, int role) const
+{
+ int pos = index.row();
+ if(pos >= modpacks.size() || pos < 0 || !index.isValid())
+ {
+ return QString("INVALID INDEX %1").arg(pos);
+ }
+
+ IndexedPack pack = modpacks.at(pos);
+ if(role == Qt::DisplayRole)
+ {
+ return pack.name;
+ }
+ else if (role == Qt::ToolTipRole)
+ {
+ if(pack.description.length() > 100)
+ {
+ //some magic to prevent to long tooltips and replace html linebreaks
+ QString edit = pack.description.left(97);
+ edit = edit.left(edit.lastIndexOf("<br>")).left(edit.lastIndexOf(" ")).append("...");
+ return edit;
+
+ }
+ return pack.description;
+ }
+ else if(role == Qt::DecorationRole)
+ {
+ if(m_logoMap.contains(pack.logoName))
+ {
+ return (m_logoMap.value(pack.logoName));
+ }
+ QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder");
+ ((ListModel *)this)->requestLogo(pack.logoName, pack.logoUrl);
+ return icon;
+ }
+ else if(role == Qt::UserRole)
+ {
+ QVariant v;
+ v.setValue(pack);
+ return v;
+ }
+
+ return QVariant();
+}
+
+void ListModel::logoLoaded(QString logo, QIcon out)
+{
+ m_loadingLogos.removeAll(logo);
+ m_logoMap.insert(logo, out);
+ for(int i = 0; i < modpacks.size(); i++) {
+ if(modpacks[i].logoName == logo) {
+ emit dataChanged(createIndex(i, 0), createIndex(i, 0), {Qt::DecorationRole});
+ }
+ }
+}
+
+void ListModel::logoFailed(QString logo)
+{
+ m_failedLogos.append(logo);
+ m_loadingLogos.removeAll(logo);
+}
+
+void ListModel::requestLogo(QString logo, QString url)
+{
+ if(m_loadingLogos.contains(logo) || m_failedLogos.contains(logo))
+ {
+ return;
+ }
+
+ MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FlameMods", QString("logos/%1").arg(logo.section(".", 0, 0)));
+ auto job = new NetJob(QString("Flame Icon Download %1").arg(logo), APPLICATION->network());
+ job->addNetAction(Net::Download::makeCached(QUrl(url), entry));
+
+ auto fullPath = entry->getFullPath();
+ QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job]
+ {
+ job->deleteLater();
+ emit logoLoaded(logo, QIcon(fullPath));
+ if(waitingCallbacks.contains(logo))
+ {
+ waitingCallbacks.value(logo)(fullPath);
+ }
+ });
+
+ QObject::connect(job, &NetJob::failed, this, [this, logo, job]
+ {
+ job->deleteLater();
+ emit logoFailed(logo);
+ });
+
+ job->start();
+ m_loadingLogos.append(logo);
+}
+
+void ListModel::getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback)
+{
+ if(m_logoMap.contains(logo))
+ {
+ callback(APPLICATION->metacache()->resolveEntry("FlameMods", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath());
+ }
+ else
+ {
+ requestLogo(logo, logoUrl);
+ }
+}
+
+Qt::ItemFlags ListModel::flags(const QModelIndex &index) const
+{
+ return QAbstractListModel::flags(index);
+}
+
+bool ListModel::canFetchMore(const QModelIndex& parent) const
+{
+ return searchState == CanPossiblyFetchMore;
+}
+
+void ListModel::fetchMore(const QModelIndex& parent)
+{
+ if (parent.isValid())
+ return;
+ if(nextSearchOffset == 0) {
+ qWarning() << "fetchMore with 0 offset is wrong...";
+ return;
+ }
+ performPaginatedSearch();
+}
+const char* sorts[6]{"Featured","Popularity","LastUpdated","Name","Author","TotalDownloads"};
+
+void ListModel::performPaginatedSearch()
+{
+
+ QString mcVersion = ((MinecraftInstance *)((FlameModPage *)parent())->m_instance)->getPackProfile()->getComponentVersion("net.minecraft");
+ bool hasFabric = !((MinecraftInstance *)((FlameModPage *)parent())->m_instance)->getPackProfile()->getComponentVersion("net.fabricmc.fabric-loader").isEmpty();
+ auto netJob = new NetJob("Flame::Search", APPLICATION->network());
+ auto searchUrl = QString(
+ "https://addons-ecs.forgesvc.net/api/v2/addon/search?"
+ "gameId=432&"
+ "categoryId=0&"
+ "sectionId=6&"
+
+ "index=%1&"
+ "pageSize=25&"
+ "searchFilter=%2&"
+ "sort=%3&"
+ "%4"
+ "gameVersion=%5"
+ )
+ .arg(nextSearchOffset)
+ .arg(currentSearchTerm)
+ .arg(sorts[currentSort])
+ .arg(hasFabric ? "modLoaderType=4&" : "")
+ .arg(mcVersion);
+
+ netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
+ jobPtr = netJob;
+ jobPtr->start();
+ QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::searchRequestFinished);
+ QObject::connect(netJob, &NetJob::failed, this, &ListModel::searchRequestFailed);
+}
+
+void ListModel::searchWithTerm(const QString &term, const int sort)
+{
+ if(currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull() && currentSort == sort) {
+ return;
+ }
+ currentSearchTerm = term;
+ currentSort = sort;
+ if(jobPtr) {
+ jobPtr->abort();
+ searchState = ResetRequested;
+ return;
+ }
+ else {
+ beginResetModel();
+ modpacks.clear();
+ endResetModel();
+ searchState = None;
+ }
+ nextSearchOffset = 0;
+ performPaginatedSearch();
+}
+
+void ListModel::searchRequestFinished()
+{
+ jobPtr.reset();
+
+ QJsonParseError parse_error;
+ QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
+ if(parse_error.error != QJsonParseError::NoError) {
+ qWarning() << "Error while parsing JSON response from Flame at " << parse_error.offset << " reason: " << parse_error.errorString();
+ qWarning() << response;
+ return;
+ }
+
+ QList<FlameMod::IndexedPack> newList;
+ auto packs = doc.array();
+ for(auto packRaw : packs) {
+ auto packObj = packRaw.toObject();
+
+ FlameMod::IndexedPack pack;
+ try
+ {
+ FlameMod::loadIndexedPack(pack, packObj);
+ newList.append(pack);
+ }
+ catch(const JSONValidationError &e)
+ {
+ qWarning() << "Error while loading mod from Flame: " << e.cause();
+ continue;
+ }
+ }
+ if(packs.size() < 25) {
+ searchState = Finished;
+ } else {
+ nextSearchOffset += 25;
+ searchState = CanPossiblyFetchMore;
+ }
+ beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + newList.size() - 1);
+ modpacks.append(newList);
+ endInsertRows();
+}
+
+void ListModel::searchRequestFailed(QString reason)
+{
+ jobPtr.reset();
+
+ if(searchState == ResetRequested) {
+ beginResetModel();
+ modpacks.clear();
+ endResetModel();
+
+ nextSearchOffset = 0;
+ performPaginatedSearch();
+ } else {
+ searchState = Finished;
+ }
+}
+
+}
+
diff --git a/launcher/ui/pages/modplatform/flame/FlameModModel.h b/launcher/ui/pages/modplatform/flame/FlameModModel.h
new file mode 100644
index 00000000..0c1cb95e
--- /dev/null
+++ b/launcher/ui/pages/modplatform/flame/FlameModModel.h
@@ -0,0 +1,79 @@
+#pragma once
+
+#include <RWStorage.h>
+
+#include <QAbstractListModel>
+#include <QSortFilterProxyModel>
+#include <QThreadPool>
+#include <QIcon>
+#include <QStyledItemDelegate>
+#include <QList>
+#include <QString>
+#include <QStringList>
+#include <QMetaType>
+
+#include <functional>
+#include <net/NetJob.h>
+
+#include <modplatform/flame/FlamePackIndex.h>
+#include "modplatform/flame/FlameModIndex.h"
+#include "BaseInstance.h"
+#include "FlameModPage.h"
+
+namespace FlameMod {
+
+
+typedef QMap<QString, QIcon> LogoMap;
+typedef std::function<void(QString)> LogoCallback;
+
+class ListModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ ListModel(FlameModPage *parent);
+ virtual ~ListModel();
+
+ int rowCount(const QModelIndex &parent) const override;
+ int columnCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+ bool canFetchMore(const QModelIndex & parent) const override;
+ void fetchMore(const QModelIndex & parent) override;
+
+ void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback);
+ void searchWithTerm(const QString &term, const int sort);
+
+private slots:
+ void performPaginatedSearch();
+
+ void logoFailed(QString logo);
+ void logoLoaded(QString logo, QIcon out);
+
+ void searchRequestFinished();
+ void searchRequestFailed(QString reason);
+
+private:
+ void requestLogo(QString file, QString url);
+
+private:
+ QList<IndexedPack> modpacks;
+ QStringList m_failedLogos;
+ QStringList m_loadingLogos;
+ LogoMap m_logoMap;
+ QMap<QString, LogoCallback> waitingCallbacks;
+
+ QString currentSearchTerm;
+ int currentSort = 0;
+ int nextSearchOffset = 0;
+ enum SearchState {
+ None,
+ CanPossiblyFetchMore,
+ ResetRequested,
+ Finished
+ } searchState = None;
+ NetJob::Ptr jobPtr;
+ QByteArray response;
+};
+
+}
diff --git a/launcher/ui/pages/modplatform/flame/FlameModPage.cpp b/launcher/ui/pages/modplatform/flame/FlameModPage.cpp
new file mode 100644
index 00000000..a816c681
--- /dev/null
+++ b/launcher/ui/pages/modplatform/flame/FlameModPage.cpp
@@ -0,0 +1,196 @@
+#include "FlameModPage.h"
+#include "ui_FlameModPage.h"
+
+#include <QKeyEvent>
+
+#include "Application.h"
+#include "Json.h"
+#include "ui/dialogs/ModDownloadDialog.h"
+#include "InstanceImportTask.h"
+#include "FlameModModel.h"
+#include "ModDownloadTask.h"
+#include "minecraft/MinecraftInstance.h"
+#include "minecraft/PackProfile.h"
+
+FlameModPage::FlameModPage(ModDownloadDialog *dialog, BaseInstance *instance)
+ : QWidget(dialog), m_instance(instance), ui(new Ui::FlameModPage), dialog(dialog)
+{
+ ui->setupUi(this);
+ connect(ui->searchButton, &QPushButton::clicked, this, &FlameModPage::triggerSearch);
+ ui->searchEdit->installEventFilter(this);
+ listModel = new FlameMod::ListModel(this);
+ ui->packView->setModel(listModel);
+
+ ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
+
+ // index is used to set the sorting with the flame api
+ ui->sortByBox->addItem(tr("Sort by Featured"));
+ ui->sortByBox->addItem(tr("Sort by Popularity"));
+ ui->sortByBox->addItem(tr("Sort by last updated"));
+ ui->sortByBox->addItem(tr("Sort by Name"));
+ ui->sortByBox->addItem(tr("Sort by Author"));
+ ui->sortByBox->addItem(tr("Sort by Downloads"));
+
+ connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch()));
+ connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FlameModPage::onSelectionChanged);
+ connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FlameModPage::onVersionSelectionChanged);
+}
+
+FlameModPage::~FlameModPage()
+{
+ delete ui;
+}
+
+bool FlameModPage::eventFilter(QObject* watched, QEvent* event)
+{
+ if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) {
+ QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
+ if (keyEvent->key() == Qt::Key_Return) {
+ triggerSearch();
+ keyEvent->accept();
+ return true;
+ }
+ }
+ return QWidget::eventFilter(watched, event);
+}
+
+bool FlameModPage::shouldDisplay() const
+{
+ return true;
+}
+
+void FlameModPage::openedImpl()
+{
+ suggestCurrent();
+ triggerSearch();
+}
+
+void FlameModPage::triggerSearch()
+{
+ listModel->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex());
+}
+
+void FlameModPage::onSelectionChanged(QModelIndex first, QModelIndex second)
+{
+ ui->versionSelectionBox->clear();
+
+ if(!first.isValid())
+ {
+ if(isOpened)
+ {
+ dialog->setSuggestedMod();
+ }
+ return;
+ }
+
+ current = listModel->data(first, Qt::UserRole).value<FlameMod::IndexedPack>();
+ QString text = "";
+ QString name = current.name;
+
+ if (current.websiteUrl.isEmpty())
+ text = name;
+ else
+ text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>";
+ if (!current.authors.empty()) {
+ auto authorToStr = [](FlameMod::ModpackAuthor & author) {
+ if(author.url.isEmpty()) {
+ return author.name;
+ }
+ return QString("<a href=\"%1\">%2</a>").arg(author.url, author.name);
+ };
+ QStringList authorStrs;
+ for(auto & author: current.authors) {
+ authorStrs.push_back(authorToStr(author));
+ }
+ text += "<br>" + tr(" by ") + authorStrs.join(", ");
+ }
+ text += "<br><br>";
+
+ ui->packDescription->setHtml(text + current.description);
+
+ if (!current.versionsLoaded)
+ {
+ qDebug() << "Loading flame mod versions";
+ auto netJob = new NetJob(QString("Flame::ModVersions(%1)").arg(current.name), APPLICATION->network());
+ std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>();
+ int addonId = current.addonId;
+ netJob->addNetAction(Net::Download::makeByteArray(QString("https://addons-ecs.forgesvc.net/api/v2/addon/%1/files").arg(addonId), response.get()));
+
+ QObject::connect(netJob, &NetJob::succeeded, this, [this, response, netJob]
+ {
+ netJob->deleteLater();
+ QJsonParseError parse_error;
+ QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
+ if(parse_error.error != QJsonParseError::NoError) {
+ qWarning() << "Error while parsing JSON response from Flame at " << parse_error.offset << " reason: " << parse_error.errorString();
+ qWarning() << *response;
+ return;
+ }
+ QJsonArray arr = doc.array();
+ try
+ {
+ FlameMod::loadIndexedPackVersions(current, arr, APPLICATION->network(), m_instance);
+ }
+ catch(const JSONValidationError &e)
+ {
+ qDebug() << *response;
+ qWarning() << "Error while reading Flame mod version: " << e.cause();
+ }
+ auto packProfile = ((MinecraftInstance *)m_instance)->getPackProfile();
+ QString mcVersion = packProfile->getComponentVersion("net.minecraft");
+ QString loaderString = (packProfile->getComponentVersion("net.minecraftforge").isEmpty()) ? "fabric" : "forge";
+ for(int i = 0; i < current.versions.size(); i++) {
+ auto version = current.versions[i];
+ if(!version.mcVersion.contains(mcVersion)){
+ continue;
+ }
+ ui->versionSelectionBox->addItem(version.version, QVariant(i));
+ }
+ if(ui->versionSelectionBox->count() == 0){
+ ui->versionSelectionBox->addItem(tr("No Valid Version found!"), QVariant(-1));
+ }
+
+ suggestCurrent();
+ });
+ netJob->start();
+ }
+ else
+ {
+ for(int i = 0; i < current.versions.size(); i++) {
+ ui->versionSelectionBox->addItem(current.versions[i].version, QVariant(i));
+ }
+ if(ui->versionSelectionBox->count() == 0){
+ ui->versionSelectionBox->addItem(tr("No Valid Version found!"), QVariant(-1));
+ }
+ suggestCurrent();
+ }
+}
+
+void FlameModPage::suggestCurrent()
+{
+ if(!isOpened)
+ {
+ return;
+ }
+
+ if (selectedVersion == -1)
+ {
+ dialog->setSuggestedMod();
+ return;
+ }
+
+ auto version = current.versions[selectedVersion];
+ dialog->setSuggestedMod(current.name, new ModDownloadTask(version.downloadUrl, version.fileName , dialog->mods));
+}
+
+void FlameModPage::onVersionSelectionChanged(QString data)
+{
+ if(data.isNull() || data.isEmpty())
+ {
+ selectedVersion = -1;
+ return;
+ }
+ selectedVersion = ui->versionSelectionBox->currentData().toInt();
+ suggestCurrent();
+}
diff --git a/launcher/ui/pages/modplatform/flame/FlameModPage.h b/launcher/ui/pages/modplatform/flame/FlameModPage.h
new file mode 100644
index 00000000..8fa3248a
--- /dev/null
+++ b/launcher/ui/pages/modplatform/flame/FlameModPage.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include <QWidget>
+
+#include "ui/pages/BasePage.h"
+#include <Application.h>
+#include "tasks/Task.h"
+#include "modplatform/flame/FlameModIndex.h"
+
+namespace Ui
+{
+class FlameModPage;
+}
+
+class ModDownloadDialog;
+
+namespace FlameMod {
+ class ListModel;
+}
+
+class FlameModPage : public QWidget, public BasePage
+{
+ Q_OBJECT
+
+public:
+ explicit FlameModPage(ModDownloadDialog *dialog, BaseInstance *instance);
+ virtual ~FlameModPage();
+ virtual QString displayName() const override
+ {
+ return tr("CurseForge");
+ }
+ virtual QIcon icon() const override
+ {
+ return APPLICATION->getThemedIcon("flame");
+ }
+ virtual QString id() const override
+ {
+ return "curseforge";
+ }
+ virtual QString helpPage() const override
+ {
+ return "Flame-platform";
+ }
+ virtual bool shouldDisplay() const override;
+
+ void openedImpl() override;
+
+ bool eventFilter(QObject * watched, QEvent * event) override;
+
+ BaseInstance *m_instance;
+
+private:
+ void suggestCurrent();
+
+private slots:
+ void triggerSearch();
+ void onSelectionChanged(QModelIndex first, QModelIndex second);
+ void onVersionSelectionChanged(QString data);
+
+private:
+ Ui::FlameModPage *ui = nullptr;
+ ModDownloadDialog* dialog = nullptr;
+ FlameMod::ListModel* listModel = nullptr;
+ FlameMod::IndexedPack current;
+
+ int selectedVersion = -1;
+};
diff --git a/launcher/ui/pages/modplatform/flame/FlameModPage.ui b/launcher/ui/pages/modplatform/flame/FlameModPage.ui
new file mode 100644
index 00000000..7da0bb4a
--- /dev/null
+++ b/launcher/ui/pages/modplatform/flame/FlameModPage.ui
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>FlameModPage</class>
+ <widget class="QWidget" name="FlameModPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>837</width>
+ <height>685</height>
+ </rect>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="0" colspan="2">
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="1" column="0">
+ <widget class="QListView" name="packView">
+ <property name="iconSize">
+ <size>
+ <width>48</width>
+ <height>48</height>
+ </size>
+ </property>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QTextBrowser" name="packDescription">
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ <property name="openLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0" rowminimumheight="0" columnminimumwidth="0,0,0">
+ <item row="0" column="2">
+ <widget class="QComboBox" name="versionSelectionBox"/>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Version selected:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QComboBox" name="sortByBox"/>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="searchButton">
+ <property name="text">
+ <string>Search</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLineEdit" name="searchEdit">
+ <property name="placeholderText">
+ <string>Search and filter ...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>searchEdit</tabstop>
+ <tabstop>searchButton</tabstop>
+ <tabstop>packView</tabstop>
+ <tabstop>packDescription</tabstop>
+ <tabstop>sortByBox</tabstop>
+ <tabstop>versionSelectionBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp
index 891676cf..fe163cae 100644
--- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp
+++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp
@@ -6,9 +6,6 @@
#include <Version.h>
#include <QtMath>
-#include <QLabel>
-
-#include <RWStorage.h>
namespace Flame {
@@ -100,12 +97,13 @@ void ListModel::requestLogo(QString logo, QString url)
}
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FlamePacks", QString("logos/%1").arg(logo.section(".", 0, 0)));
- NetJob *job = new NetJob(QString("Flame Icon Download %1").arg(logo), APPLICATION->network());
+ auto job = new NetJob(QString("Flame Icon Download %1").arg(logo), APPLICATION->network());
job->addNetAction(Net::Download::makeCached(QUrl(url), entry));
auto fullPath = entry->getFullPath();
- QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath]
+ QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job]
{
+ job->deleteLater();
emit logoLoaded(logo, QIcon(fullPath));
if(waitingCallbacks.contains(logo))
{
@@ -113,8 +111,9 @@ void ListModel::requestLogo(QString logo, QString url)
}
});
- QObject::connect(job, &NetJob::failed, this, [this, logo]
+ QObject::connect(job, &NetJob::failed, this, [this, logo, job]
{
+ job->deleteLater();
emit logoFailed(logo);
});
diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp
new file mode 100644
index 00000000..5a18830a
--- /dev/null
+++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp
@@ -0,0 +1,276 @@
+#include "ModrinthModel.h"
+#include "Application.h"
+#include "minecraft/MinecraftInstance.h"
+#include "minecraft/PackProfile.h"
+#include "ModrinthPage.h"
+#include "ui/dialogs/ModDownloadDialog.h"
+#include <Json.h>
+
+#include <MMCStrings.h>
+#include <Version.h>
+
+#include <QtMath>
+#include <QMessageBox>
+
+
+namespace Modrinth {
+
+ListModel::ListModel(ModrinthPage *parent) : QAbstractListModel(parent)
+{
+}
+
+ListModel::~ListModel()
+{
+}
+
+int ListModel::rowCount(const QModelIndex &parent) const
+{
+ return modpacks.size();
+}
+
+int ListModel::columnCount(const QModelIndex &parent) const
+{
+ return 1;
+}
+
+QVariant ListModel::data(const QModelIndex &index, int role) const
+{
+ int pos = index.row();
+ if(pos >= modpacks.size() || pos < 0 || !index.isValid())
+ {
+ return QString("INVALID INDEX %1").arg(pos);
+ }
+
+ IndexedPack pack = modpacks.at(pos);
+ if(role == Qt::DisplayRole)
+ {
+ return pack.name;
+ }
+ else if (role == Qt::ToolTipRole)
+ {
+ if(pack.description.length() > 100)
+ {
+ //some magic to prevent to long tooltips and replace html linebreaks
+ QString edit = pack.description.left(97);
+ edit = edit.left(edit.lastIndexOf("<br>")).left(edit.lastIndexOf(" ")).append("...");
+ return edit;
+
+ }
+ return pack.description;
+ }
+ else if(role == Qt::DecorationRole)
+ {
+ if(m_logoMap.contains(pack.logoName))
+ {
+ return (m_logoMap.value(pack.logoName));
+ }
+ QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder");
+ ((ListModel *)this)->requestLogo(pack.logoName, pack.logoUrl);
+ return icon;
+ }
+ else if(role == Qt::UserRole)
+ {
+ QVariant v;
+ v.setValue(pack);
+ return v;
+ }
+
+ return QVariant();
+}
+
+void ListModel::logoLoaded(QString logo, QIcon out)
+{
+ m_loadingLogos.removeAll(logo);
+ m_logoMap.insert(logo, out);
+ for(int i = 0; i < modpacks.size(); i++) {
+ if(modpacks[i].logoName == logo) {
+ emit dataChanged(createIndex(i, 0), createIndex(i, 0), {Qt::DecorationRole});
+ }
+ }
+}
+
+void ListModel::logoFailed(QString logo)
+{
+ m_failedLogos.append(logo);
+ m_loadingLogos.removeAll(logo);
+}
+
+void ListModel::requestLogo(QString logo, QString url)
+{
+ if(m_loadingLogos.contains(logo) || m_failedLogos.contains(logo))
+ {
+ return;
+ }
+
+ MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("ModrinthPacks", QString("logos/%1").arg(logo.section(".", 0, 0)));
+ auto job = new NetJob(QString("Modrinth Icon Download %1").arg(logo), APPLICATION->network());
+ job->addNetAction(Net::Download::makeCached(QUrl(url), entry));
+
+ auto fullPath = entry->getFullPath();
+ QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job]
+ {
+ job->deleteLater();
+ emit logoLoaded(logo, QIcon(fullPath));
+ if(waitingCallbacks.contains(logo))
+ {
+ waitingCallbacks.value(logo)(fullPath);
+ }
+ });
+
+ QObject::connect(job, &NetJob::failed, this, [this, logo, job]
+ {
+ job->deleteLater();
+ emit logoFailed(logo);
+ });
+
+ job->start();
+ m_loadingLogos.append(logo);
+}
+
+void ListModel::getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback)
+{
+ if(m_logoMap.contains(logo))
+ {
+ callback(APPLICATION->metacache()->resolveEntry("ModrinthPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath());
+ }
+ else
+ {
+ requestLogo(logo, logoUrl);
+ }
+}
+
+Qt::ItemFlags ListModel::flags(const QModelIndex &index) const
+{
+ return QAbstractListModel::flags(index);
+}
+
+bool ListModel::canFetchMore(const QModelIndex& parent) const
+{
+ return searchState == CanPossiblyFetchMore;
+}
+
+void ListModel::fetchMore(const QModelIndex& parent)
+{
+ if (parent.isValid())
+ return;
+ if(nextSearchOffset == 0) {
+ qWarning() << "fetchMore with 0 offset is wrong...";
+ return;
+ }
+ performPaginatedSearch();
+}
+const char* sorts[5]{"relevance","downloads","follows","updated","newest"};
+
+void ListModel::performPaginatedSearch()
+{
+
+ QString mcVersion = ((MinecraftInstance *)((ModrinthPage *)parent())->m_instance)->getPackProfile()->getComponentVersion("net.minecraft");
+ bool hasFabric = !((MinecraftInstance *)((ModrinthPage *)parent())->m_instance)->getPackProfile()->getComponentVersion("net.fabricmc.fabric-loader").isEmpty();
+ auto netJob = new NetJob("Modrinth::Search", APPLICATION->network());
+ auto searchUrl = QString(
+ "https://api.modrinth.com/v2/search?"
+ "offset=%1&"
+ "limit=25&"
+ "query=%2&"
+ "index=%3&"
+ "facets=[[\"categories:%4\"],[\"versions:%5\"],[\"project_type:mod\"]]"
+ )
+ .arg(nextSearchOffset)
+ .arg(currentSearchTerm)
+ .arg(sorts[currentSort])
+ .arg(hasFabric ? "fabric" : "forge")
+ .arg(mcVersion);
+
+ netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
+ jobPtr = netJob;
+ jobPtr->start();
+ QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::searchRequestFinished);
+ QObject::connect(netJob, &NetJob::failed, this, &ListModel::searchRequestFailed);
+}
+
+void ListModel::searchWithTerm(const QString &term, const int sort)
+{
+ if(currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull() && currentSort == sort) {
+ return;
+ }
+ currentSearchTerm = term;
+ currentSort = sort;
+ if(jobPtr) {
+ jobPtr->abort();
+ searchState = ResetRequested;
+ return;
+ }
+ else {
+ beginResetModel();
+ modpacks.clear();
+ endResetModel();
+ searchState = None;
+ }
+ nextSearchOffset = 0;
+ performPaginatedSearch();
+}
+
+void Modrinth::ListModel::searchRequestFinished()
+{
+ jobPtr.reset();
+
+ QJsonParseError parse_error;
+ QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
+ if(parse_error.error != QJsonParseError::NoError) {
+ qWarning() << "Error while parsing JSON response from Modrinth at " << parse_error.offset << " reason: " << parse_error.errorString();
+ qWarning() << response;
+ return;
+ }
+
+ QList<Modrinth::IndexedPack> newList;
+ auto packs = doc.object().value("hits").toArray();
+ for(auto packRaw : packs) {
+ auto packObj = packRaw.toObject();
+
+ Modrinth::IndexedPack pack;
+ try
+ {
+ Modrinth::loadIndexedPack(pack, packObj);
+ newList.append(pack);
+ }
+ catch(const JSONValidationError &e)
+ {
+ qWarning() << "Error while loading mod from Modrinth: " << e.cause();
+ continue;
+ }
+ }
+ if(packs.size() < 25) {
+ searchState = Finished;
+ } else {
+ nextSearchOffset += 25;
+ searchState = CanPossiblyFetchMore;
+ }
+ beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + newList.size() - 1);
+ modpacks.append(newList);
+ endInsertRows();
+}
+
+void Modrinth::ListModel::searchRequestFailed(QString reason)
+{
+ if(jobPtr->first()->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 409){
+ //409 Gone, notify user to update
+ QMessageBox::critical(nullptr, tr("Error"), tr("Modrinth API version too old!\nPlease update PolyMC!"));
+ //self-destruct
+ ((ModDownloadDialog *)((ModrinthPage *)parent())->parentWidget())->reject();
+ }
+ jobPtr.reset();
+
+ if(searchState == ResetRequested) {
+ beginResetModel();
+ modpacks.clear();
+ endResetModel();
+
+ nextSearchOffset = 0;
+ performPaginatedSearch();
+ } else {
+ searchState = Finished;
+ }
+}
+
+}
+
diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h
new file mode 100644
index 00000000..53f1f134
--- /dev/null
+++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h
@@ -0,0 +1,79 @@
+#pragma once
+
+#include <RWStorage.h>
+
+#include <QAbstractListModel>
+#include <QSortFilterProxyModel>
+#include <QThreadPool>
+#include <QIcon>
+#include <QStyledItemDelegate>
+#include <QList>
+#include <QString>
+#include <QStringList>
+#include <QMetaType>
+
+#include <functional>
+#include <net/NetJob.h>
+
+#include <modplatform/flame/FlamePackIndex.h>
+#include "modplatform/modrinth/ModrinthPackIndex.h"
+#include "BaseInstance.h"
+#include "ModrinthPage.h"
+
+namespace Modrinth {
+
+
+typedef QMap<QString, QIcon> LogoMap;
+typedef std::function<void(QString)> LogoCallback;
+
+class ListModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ ListModel(ModrinthPage *parent);
+ virtual ~ListModel();
+
+ int rowCount(const QModelIndex &parent) const override;
+ int columnCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+ bool canFetchMore(const QModelIndex & parent) const override;
+ void fetchMore(const QModelIndex & parent) override;
+
+ void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback);
+ void searchWithTerm(const QString &term, const int sort);
+
+private slots:
+ void performPaginatedSearch();
+
+ void logoFailed(QString logo);
+ void logoLoaded(QString logo, QIcon out);
+
+ void searchRequestFinished();
+ void searchRequestFailed(QString reason);
+
+private:
+ void requestLogo(QString file, QString url);
+
+private:
+ QList<IndexedPack> modpacks;
+ QStringList m_failedLogos;
+ QStringList m_loadingLogos;
+ LogoMap m_logoMap;
+ QMap<QString, LogoCallback> waitingCallbacks;
+
+ QString currentSearchTerm;
+ int currentSort = 0;
+ int nextSearchOffset = 0;
+ enum SearchState {
+ None,
+ CanPossiblyFetchMore,
+ ResetRequested,
+ Finished
+ } searchState = None;
+ NetJob::Ptr jobPtr;
+ QByteArray response;
+};
+
+}
diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp
new file mode 100644
index 00000000..c5a54c29
--- /dev/null
+++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp
@@ -0,0 +1,180 @@
+#include "ModrinthPage.h"
+#include "ui_ModrinthPage.h"
+
+#include <QKeyEvent>
+
+#include "Application.h"
+#include "Json.h"
+#include "ui/dialogs/ModDownloadDialog.h"
+#include "InstanceImportTask.h"
+#include "ModrinthModel.h"
+#include "ModDownloadTask.h"
+#include "minecraft/MinecraftInstance.h"
+#include "minecraft/PackProfile.h"
+
+ModrinthPage::ModrinthPage(ModDownloadDialog *dialog, BaseInstance *instance)
+ : QWidget(dialog), m_instance(instance), ui(new Ui::ModrinthPage), dialog(dialog)
+{
+ ui->setupUi(this);
+ connect(ui->searchButton, &QPushButton::clicked, this, &ModrinthPage::triggerSearch);
+ ui->searchEdit->installEventFilter(this);
+ listModel = new Modrinth::ListModel(this);
+ ui->packView->setModel(listModel);
+
+ ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
+
+ // index is used to set the sorting with the modrinth api
+ ui->sortByBox->addItem(tr("Sort by Relevence"));
+ ui->sortByBox->addItem(tr("Sort by Downloads"));
+ ui->sortByBox->addItem(tr("Sort by Follows"));
+ ui->sortByBox->addItem(tr("Sort by last updated"));
+ ui->sortByBox->addItem(tr("Sort by newest"));
+
+ connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch()));
+ connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthPage::onSelectionChanged);
+ connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &ModrinthPage::onVersionSelectionChanged);
+}
+
+ModrinthPage::~ModrinthPage()
+{
+ delete ui;
+}
+
+bool ModrinthPage::eventFilter(QObject* watched, QEvent* event)
+{
+ if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) {
+ QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
+ if (keyEvent->key() == Qt::Key_Return) {
+ triggerSearch();
+ keyEvent->accept();
+ return true;
+ }
+ }
+ return QWidget::eventFilter(watched, event);
+}
+
+bool ModrinthPage::shouldDisplay() const
+{
+ return true;
+}
+
+void ModrinthPage::openedImpl()
+{
+ suggestCurrent();
+ triggerSearch();
+}
+
+void ModrinthPage::triggerSearch()
+{
+ listModel->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex());
+}
+
+void ModrinthPage::onSelectionChanged(QModelIndex first, QModelIndex second)
+{
+ ui->versionSelectionBox->clear();
+
+ if(!first.isValid())
+ {
+ if(isOpened)
+ {
+ dialog->setSuggestedMod();
+ }
+ return;
+ }
+
+ current = listModel->data(first, Qt::UserRole).value<Modrinth::IndexedPack>();
+ QString text = "";
+ QString name = current.name;
+
+ if (current.websiteUrl.isEmpty())
+ text = name;
+ else
+ text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>";
+ text += "<br>"+ tr(" by ") + "<a href=\""+current.author.url+"\">"+current.author.name+"</a><br><br>";
+ ui->packDescription->setHtml(text + current.description);
+
+ if (!current.versionsLoaded)
+ {
+ qDebug() << "Loading Modrinth mod versions";
+ auto netJob = new NetJob(QString("Modrinth::ModVersions(%1)").arg(current.name), APPLICATION->network());
+ std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>();
+ QString addonId = current.addonId;
+ netJob->addNetAction(Net::Download::makeByteArray(QString("https://api.modrinth.com/v2/project/%1/version").arg(addonId), response.get()));
+
+ QObject::connect(netJob, &NetJob::succeeded, this, [this, response, netJob]
+ {
+ netJob->deleteLater();
+ QJsonParseError parse_error;
+ QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
+ if(parse_error.error != QJsonParseError::NoError) {
+ qWarning() << "Error while parsing JSON response from Modrinth at " << parse_error.offset << " reason: " << parse_error.errorString();
+ qWarning() << *response;
+ return;
+ }
+ QJsonArray arr = doc.array();
+ try
+ {
+ Modrinth::loadIndexedPackVersions(current, arr, APPLICATION->network(), m_instance);
+ }
+ catch(const JSONValidationError &e)
+ {
+ qDebug() << *response;
+ qWarning() << "Error while reading Modrinth mod version: " << e.cause();
+ }
+ auto packProfile = ((MinecraftInstance *)m_instance)->getPackProfile();
+ QString mcVersion = packProfile->getComponentVersion("net.minecraft");
+ QString loaderString = (packProfile->getComponentVersion("net.minecraftforge").isEmpty()) ? "fabric" : "forge";
+ for(int i = 0; i < current.versions.size(); i++) {
+ auto version = current.versions[i];
+ if(!version.mcVersion.contains(mcVersion) || !version.loaders.contains(loaderString)){
+ continue;
+ }
+ ui->versionSelectionBox->addItem(version.version, QVariant(i));
+ }
+ if(ui->versionSelectionBox->count() == 0){
+ ui->versionSelectionBox->addItem(tr("No Valid Version found !"), QVariant(-1));
+ }
+
+ suggestCurrent();
+ });
+ netJob->start();
+ }
+ else
+ {
+ for(int i = 0; i < current.versions.size(); i++) {
+ ui->versionSelectionBox->addItem(current.versions[i].version, QVariant(i));
+ }
+ if(ui->versionSelectionBox->count() == 0){
+ ui->versionSelectionBox->addItem(tr("No Valid Version found !"), QVariant(-1));
+ }
+ suggestCurrent();
+ }
+}
+
+void ModrinthPage::suggestCurrent()
+{
+ if(!isOpened)
+ {
+ return;
+ }
+
+ if (selectedVersion == -1)
+ {
+ dialog->setSuggestedMod();
+ return;
+ }
+ auto version = current.versions[selectedVersion];
+ dialog->setSuggestedMod(current.name, new ModDownloadTask(version.downloadUrl, version.fileName , dialog->mods));
+}
+
+void ModrinthPage::onVersionSelectionChanged(QString data)
+{
+ if(data.isNull() || data.isEmpty())
+ {
+ selectedVersion = -1;
+ return;
+ }
+ selectedVersion = ui->versionSelectionBox->currentData().toInt();
+ suggestCurrent();
+}
diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h
new file mode 100644
index 00000000..3c517069
--- /dev/null
+++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include <QWidget>
+
+#include "ui/pages/BasePage.h"
+#include <Application.h>
+#include "tasks/Task.h"
+#include "modplatform/modrinth/ModrinthPackIndex.h"
+
+namespace Ui
+{
+class ModrinthPage;
+}
+
+class ModDownloadDialog;
+
+namespace Modrinth {
+ class ListModel;
+}
+
+class ModrinthPage : public QWidget, public BasePage
+{
+ Q_OBJECT
+
+public:
+ explicit ModrinthPage(ModDownloadDialog *dialog, BaseInstance *instance);
+ virtual ~ModrinthPage();
+ virtual QString displayName() const override
+ {
+ return tr("Modrinth");
+ }
+ virtual QIcon icon() const override
+ {
+ return APPLICATION->getThemedIcon("modrinth");
+ }
+ virtual QString id() const override
+ {
+ return "modrinth";
+ }
+ virtual QString helpPage() const override
+ {
+ return "Modrinth-platform";
+ }
+ virtual bool shouldDisplay() const override;
+
+ void openedImpl() override;
+
+ bool eventFilter(QObject * watched, QEvent * event) override;
+
+ BaseInstance *m_instance;
+
+private:
+ void suggestCurrent();
+
+private slots:
+ void triggerSearch();
+ void onSelectionChanged(QModelIndex first, QModelIndex second);
+ void onVersionSelectionChanged(QString data);
+
+private:
+ Ui::ModrinthPage *ui = nullptr;
+ ModDownloadDialog* dialog = nullptr;
+ Modrinth::ListModel* listModel = nullptr;
+ Modrinth::IndexedPack current;
+
+ int selectedVersion = -1;
+};
diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui
new file mode 100644
index 00000000..6d183de5
--- /dev/null
+++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ModrinthPage</class>
+ <widget class="QWidget" name="ModrinthPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>837</width>
+ <height>685</height>
+ </rect>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="0" colspan="2">
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="1" column="0">
+ <widget class="QListView" name="packView">
+ <property name="iconSize">
+ <size>
+ <width>48</width>
+ <height>48</height>
+ </size>
+ </property>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QTextBrowser" name="packDescription">
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ <property name="openLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0" rowminimumheight="0" columnminimumwidth="0,0,0">
+ <item row="0" column="2">
+ <widget class="QComboBox" name="versionSelectionBox"/>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Version selected:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QComboBox" name="sortByBox"/>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="searchButton">
+ <property name="text">
+ <string>Search</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLineEdit" name="searchEdit">
+ <property name="placeholderText">
+ <string>Search and filter ...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>searchEdit</tabstop>
+ <tabstop>searchButton</tabstop>
+ <tabstop>packView</tabstop>
+ <tabstop>packDescription</tabstop>
+ <tabstop>sortByBox</tabstop>
+ <tabstop>versionSelectionBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/launcher/ui/setupwizard/AnalyticsWizardPage.cpp b/launcher/ui/setupwizard/AnalyticsWizardPage.cpp
deleted file mode 100644
index 3db2f6dc..00000000
--- a/launcher/ui/setupwizard/AnalyticsWizardPage.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-#include "AnalyticsWizardPage.h"
-#include <Application.h>
-
-#include <QVBoxLayout>
-#include <QTextBrowser>
-#include <QCheckBox>
-
-#include <ganalytics.h>
-#include <BuildConfig.h>
-
-AnalyticsWizardPage::AnalyticsWizardPage(QWidget *parent)
- : BaseWizardPage(parent)
-{
- setObjectName(QStringLiteral("analyticsPage"));
- verticalLayout_3 = new QVBoxLayout(this);
- verticalLayout_3->setObjectName(QStringLiteral("verticalLayout_3"));
- textBrowser = new QTextBrowser(this);
- textBrowser->setObjectName(QStringLiteral("textBrowser"));
- textBrowser->setAcceptRichText(false);
- textBrowser->setOpenExternalLinks(true);
- verticalLayout_3->addWidget(textBrowser);
-
- checkBox = new QCheckBox(this);
- checkBox->setObjectName(QStringLiteral("checkBox"));
- checkBox->setChecked(true);
- verticalLayout_3->addWidget(checkBox);
- retranslate();
-}
-
-AnalyticsWizardPage::~AnalyticsWizardPage()
-{
-}
-
-bool AnalyticsWizardPage::validatePage()
-{
- auto settings = APPLICATION->settings();
- auto analytics = APPLICATION->analytics();
- auto status = checkBox->isChecked();
- settings->set("AnalyticsSeen", analytics->version());
- settings->set("Analytics", status);
- return true;
-}
-
-void AnalyticsWizardPage::retranslate()
-{
- setTitle(tr("Analytics"));
- setSubTitle(tr("We track some anonymous statistics about users."));
- textBrowser->setHtml(tr(
- "<html><body>"
- "<p>The launcher sends anonymous usage statistics on every start of the application. This helps us decide what platforms and issues to focus on.</p>"
- "<p>The data is processed by Google Analytics, see their <a href=\"https://support.google.com/analytics/answer/6004245?hl=en\">article on the "
- "matter</a>.</p>"
- "<p>The following data is collected:</p>"
- "<ul><li>A random unique ID of the installation.<br />It is stored in the application settings file.</li>"
- "<li>Anonymized (partial) IP address.</li>"
- "<li>Launcher version.</li>"
- "<li>Operating system name, version and architecture.</li>"
- "<li>CPU architecture (kernel architecture on linux).</li>"
- "<li>Size of system memory.</li>"
- "<li>Java version, architecture and memory settings.</li></ul>"
- "<p>If we change the tracked information, you will see this page again.</p></body></html>"));
- checkBox->setText(tr("Enable Analytics"));
-}
diff --git a/launcher/ui/setupwizard/AnalyticsWizardPage.h b/launcher/ui/setupwizard/AnalyticsWizardPage.h
deleted file mode 100644
index c451db2c..00000000
--- a/launcher/ui/setupwizard/AnalyticsWizardPage.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-#include "BaseWizardPage.h"
-
-class QVBoxLayout;
-class QTextBrowser;
-class QCheckBox;
-
-class AnalyticsWizardPage : public BaseWizardPage
-{
- Q_OBJECT
-public:
- explicit AnalyticsWizardPage(QWidget *parent = Q_NULLPTR);
- virtual ~AnalyticsWizardPage();
-
- bool validatePage() override;
-
-protected:
- void retranslate() override;
-
-private:
- QVBoxLayout *verticalLayout_3 = nullptr;
- QTextBrowser *textBrowser = nullptr;
- QCheckBox *checkBox = nullptr;
-}; \ No newline at end of file
diff --git a/launcher/ui/setupwizard/SetupWizard.cpp b/launcher/ui/setupwizard/SetupWizard.cpp
index 5af5ba91..22eab60e 100644
--- a/launcher/ui/setupwizard/SetupWizard.cpp
+++ b/launcher/ui/setupwizard/SetupWizard.cpp
@@ -2,12 +2,10 @@
#include "LanguageWizardPage.h"
#include "JavaWizardPage.h"
-#include "AnalyticsWizardPage.h"
#include "translations/TranslationsModel.h"
#include <Application.h>
#include <FileSystem.h>
-#include <ganalytics.h>
#include <QAbstractButton>
#include <BuildConfig.h>
diff --git a/launcher/ui/widgets/LanguageSelectionWidget.cpp b/launcher/ui/widgets/LanguageSelectionWidget.cpp
index cf70c7b4..256b09da 100644
--- a/launcher/ui/widgets/LanguageSelectionWidget.cpp
+++ b/launcher/ui/widgets/LanguageSelectionWidget.cpp
@@ -5,7 +5,9 @@
#include <QHeaderView>
#include <QLabel>
#include "Application.h"
+#include "BuildConfig.h"
#include "translations/TranslationsModel.h"
+#include "settings/Setting.h"
LanguageSelectionWidget::LanguageSelectionWidget(QWidget *parent) :
QWidget(parent)
@@ -37,6 +39,9 @@ LanguageSelectionWidget::LanguageSelectionWidget(QWidget *parent) :
languageView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
connect(languageView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &LanguageSelectionWidget::languageRowChanged);
verticalLayout->setContentsMargins(0,0,0,0);
+
+ auto language_setting = APPLICATION->settings()->getSetting("Language");
+ connect(language_setting.get(), &Setting::SettingChanged, this, &LanguageSelectionWidget::languageSettingChanged);
}
QString LanguageSelectionWidget::getSelectedLanguageKey() const
@@ -48,7 +53,7 @@ QString LanguageSelectionWidget::getSelectedLanguageKey() const
void LanguageSelectionWidget::retranslate()
{
QString text = tr("Don't see your language or the quality is poor?<br/><a href=\"%1\">Help us with translations!</a>")
- .arg("https://github.com/MultiMC/Launcher/wiki/Translating-MultiMC");
+ .arg(BuildConfig.TRANSLATIONS_URL);
helpUsLabel->setText(text);
}
@@ -64,3 +69,10 @@ void LanguageSelectionWidget::languageRowChanged(const QModelIndex& current, con
translations->selectLanguage(key);
translations->updateLanguage(key);
}
+
+void LanguageSelectionWidget::languageSettingChanged(const Setting &, const QVariant)
+{
+ auto translations = APPLICATION->translations();
+ auto index = translations->selectedIndex();
+ languageView->setCurrentIndex(index);
+}
diff --git a/launcher/ui/widgets/LanguageSelectionWidget.h b/launcher/ui/widgets/LanguageSelectionWidget.h
index e65936db..4a88924c 100644
--- a/launcher/ui/widgets/LanguageSelectionWidget.h
+++ b/launcher/ui/widgets/LanguageSelectionWidget.h
@@ -20,6 +20,7 @@
class QVBoxLayout;
class QTreeView;
class QLabel;
+class Setting;
class LanguageSelectionWidget: public QWidget
{
@@ -33,6 +34,7 @@ public:
protected slots:
void languageRowChanged(const QModelIndex &current, const QModelIndex &previous);
+ void languageSettingChanged(const Setting &, const QVariant);
private:
QVBoxLayout *verticalLayout = nullptr;
diff --git a/launcher/ui/widgets/PageContainer.cpp b/launcher/ui/widgets/PageContainer.cpp
index 74a6dff3..6de49467 100644
--- a/launcher/ui/widgets/PageContainer.cpp
+++ b/launcher/ui/widgets/PageContainer.cpp
@@ -207,7 +207,7 @@ void PageContainer::help()
QString pageId = m_currentPage->helpPage();
if (pageId.isEmpty())
return;
- DesktopServices::openUrl(QUrl("https://github.com/MultiMC/Launcher/wiki/" + pageId));
+ DesktopServices::openUrl(QUrl("https://github.com/PolyMC/PolyMC/wiki/" + pageId));
}
}
diff --git a/launcher/ui/widgets/WideBar.cpp b/launcher/ui/widgets/WideBar.cpp
index cbd6c617..8d5bd12d 100644
--- a/launcher/ui/widgets/WideBar.cpp
+++ b/launcher/ui/widgets/WideBar.cpp
@@ -76,6 +76,20 @@ void WideBar::addSeparator()
m_entries.push_back(entry);
}
+void WideBar::insertActionBefore(QAction* before, QAction* action){
+ auto iter = std::find_if(m_entries.begin(), m_entries.end(), [before](BarEntry * entry) {
+ return entry->wideAction == before;
+ });
+ if(iter == m_entries.end()) {
+ return;
+ }
+ auto entry = new BarEntry();
+ entry->qAction = insertWidget((*iter)->qAction, new ActionButton(action, this));
+ entry->wideAction = action;
+ entry->type = BarEntry::Action;
+ m_entries.insert(iter, entry);
+}
+
void WideBar::insertSpacer(QAction* action)
{
auto iter = std::find_if(m_entries.begin(), m_entries.end(), [action](BarEntry * entry) {
diff --git a/launcher/ui/widgets/WideBar.h b/launcher/ui/widgets/WideBar.h
index d1b8cbe7..2b676a8c 100644
--- a/launcher/ui/widgets/WideBar.h
+++ b/launcher/ui/widgets/WideBar.h
@@ -18,6 +18,7 @@ public:
void addAction(QAction *action);
void addSeparator();
void insertSpacer(QAction *action);
+ void insertActionBefore(QAction *before, QAction *action);
QMenu *createContextMenu(QWidget *parent = nullptr, const QString & title = QString());
private:
diff --git a/launcher/updater/DownloadTask.h b/launcher/updater/DownloadTask.h
index eac26238..f47a3048 100644
--- a/launcher/updater/DownloadTask.h
+++ b/launcher/updater/DownloadTask.h
@@ -54,7 +54,7 @@ protected:
/*!
* Downloads the version info files from the repository.
* The files for both the current build, and the build that we're updating to need to be downloaded.
- * If the current version's info file can't be found, MultiMC will not delete files that
+ * If the current version's info file can't be found, PolyMC will not delete files that
* were removed between versions. It will still replace files that have changed, however.
* Note that although the repository URL for the current version is not given to the update task,
* the task will attempt to look it up in the UpdateChecker's channel list.
@@ -97,3 +97,4 @@ private:
};
}
+
diff --git a/launcher/updater/GoUpdate.cpp b/launcher/updater/GoUpdate.cpp
index 76f68b55..91f30b5d 100644
--- a/launcher/updater/GoUpdate.cpp
+++ b/launcher/updater/GoUpdate.cpp
@@ -104,7 +104,7 @@ bool processFileLists
}
}
- // Next, check each file in MultiMC's folder and see if we need to update them.
+ // Next, check each file in PolyMC's folder and see if we need to update them.
for (VersionFileEntry entry : newVersion)
{
// TODO: Let's not MD5sum a ton of files on the GUI thread. We should probably find a
diff --git a/launcher/updater/testdata/tst_DownloadTask-test_writeInstallScript.xml b/launcher/updater/testdata/tst_DownloadTask-test_writeInstallScript.xml
index 09c162ca..38ecc809 100644
--- a/launcher/updater/testdata/tst_DownloadTask-test_writeInstallScript.xml
+++ b/launcher/updater/testdata/tst_DownloadTask-test_writeInstallScript.xml
@@ -6,8 +6,8 @@
<mode>0777</mode>
</file>
<file>
- <source>MultiMC.exe</source>
- <dest>M/u/l/t/i/M/C/e/x/e</dest>
+ <source>PolyMC.exe</source>
+ <dest>P/o/l/y/M/C/e/x/e</dest>
<mode>0644</mode>
</file>
</install>
diff --git a/libraries/README.md b/libraries/README.md
index 39505881..7e7e740d 100644
--- a/libraries/README.md
+++ b/libraries/README.md
@@ -9,13 +9,6 @@ This library has served as a base for some (much more full-featured and advanced
Copyright belongs to Petr Mrázek, unless explicitly stated otherwise in the source files. Available under the Apache 2.0 license.
-## ganalytics
-A Google Analytics library for Qt.
-
-BSD licensed, derived from [qt-google-analytics](https://github.com/HSAnet/qt-google-analytics).
-
-Modifications include better handling of IP anonymization (can be enabled) and general improvements of the API (application handles persistence and ID generation instead of the library).
-
## hoedown
Hoedown is a revived fork of Sundown, the Markdown parser based on the original code of the Upskirt library by Natacha Porté.
diff --git a/libraries/classparser/CMakeLists.txt b/libraries/classparser/CMakeLists.txt
index c07e871c..fc510e68 100644
--- a/libraries/classparser/CMakeLists.txt
+++ b/libraries/classparser/CMakeLists.txt
@@ -38,4 +38,4 @@ add_definitions(-DCLASSPARSER_LIBRARY)
add_library(Launcher_classparser STATIC ${CLASSPARSER_SOURCES} ${CLASSPARSER_HEADERS})
target_include_directories(Launcher_classparser PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
-target_link_libraries(Launcher_classparser Launcher_quazip Qt5::Core)
+target_link_libraries(Launcher_classparser QuaZip::QuaZip Qt5::Core)
diff --git a/libraries/classparser/src/classparser.cpp b/libraries/classparser/src/classparser.cpp
index 8825ea39..601521f6 100644
--- a/libraries/classparser/src/classparser.cpp
+++ b/libraries/classparser/src/classparser.cpp
@@ -18,7 +18,7 @@
#include "classparser.h"
#include <QFile>
-#include <quazipfile.h>
+#include <quazip/quazipfile.h>
#include <QDebug>
namespace classparser
diff --git a/libraries/ganalytics/CMakeLists.txt b/libraries/ganalytics/CMakeLists.txt
deleted file mode 100644
index cf1cc559..00000000
--- a/libraries/ganalytics/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-project(ganalytics)
-
-find_package(Qt5Core)
-find_package(Qt5Gui)
-find_package(Qt5Network)
-
-set(ganalytics_SOURCES
-src/ganalytics.cpp
-src/ganalytics_worker.cpp
-src/ganalytics_worker.h
-include/ganalytics.h
-)
-
-add_library(ganalytics STATIC ${ganalytics_SOURCES})
-target_link_libraries(ganalytics Qt5::Core Qt5::Gui Qt5::Network)
-target_include_directories(ganalytics PUBLIC include)
-target_link_libraries(ganalytics systeminfo)
diff --git a/libraries/ganalytics/LICENSE.txt b/libraries/ganalytics/LICENSE.txt
deleted file mode 100644
index 795497ff..00000000
--- a/libraries/ganalytics/LICENSE.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-Copyright (c) 2014-2015, University of Applied Sciences Augsburg
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- * Neither the name of the University of Applied Sciences Augsburg nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
-OODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-UT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/libraries/ganalytics/README.md b/libraries/ganalytics/README.md
deleted file mode 100644
index d7e1e33c..00000000
--- a/libraries/ganalytics/README.md
+++ /dev/null
@@ -1,34 +0,0 @@
-qt-google-analytics
-================
-
-Qt5 classes for providing google analytics usage in a Qt/QML application.
-
-## Building
-Include ```qt-google-analytics.pri``` in your .pro file.
-
-## Using
-Please make sure you have set your application information using ```QApplication::setApplicationName``` and ```QApplication::setApplicationVersion```.
-
-### In C++:
-```
-GAnalytics tracker("UA-my-id");
-tracker.sendScreenView("Main Screen");
-```
-
-### In QtQuick:
-Register the class on the C++ side using ```qmlRegisterType<GAnalytics>("analytics", 0, 1, "Tracker");```
-```
-Tracker {
- id: tracker
- trackingID: "UA-my-id"
-}
-
-[...]
-tracker.sendScreenView("Main Screen")
-```
-
-There is also an example application in the examples folder.
-
-## License
-Copyright (c) 2014-2016, University of Applied Sciences Augsburg.
-All rights reserved. Distributed under the terms and conditions of the BSD License. See separate LICENSE.txt.
diff --git a/libraries/ganalytics/include/ganalytics.h b/libraries/ganalytics/include/ganalytics.h
deleted file mode 100644
index ba422457..00000000
--- a/libraries/ganalytics/include/ganalytics.h
+++ /dev/null
@@ -1,67 +0,0 @@
-#pragma once
-
-#include <QObject>
-#include <QVariantMap>
-
-class QNetworkAccessManager;
-class GAnalyticsWorker;
-
-class GAnalytics : public QObject
-{
- Q_OBJECT
- Q_ENUMS(LogLevel)
-
-public:
- explicit GAnalytics(const QString &trackingID, const QString &clientID, const int version, QObject *parent = 0);
- ~GAnalytics();
-
-public:
- enum LogLevel
- {
- Debug,
- Info,
- Error
- };
-
- int version();
-
- void setLogLevel(LogLevel logLevel);
- LogLevel logLevel() const;
-
- // Getter and Setters
- void setViewportSize(const QString &viewportSize);
- QString viewportSize() const;
-
- void setLanguage(const QString &language);
- QString language() const;
-
- void setAnonymizeIPs(bool anonymize);
- bool anonymizeIPs();
-
- void setSendInterval(int milliseconds);
- int sendInterval() const;
-
- void enable(bool state = true);
- bool isEnabled();
-
- /// Get or set the network access manager. If none is set, the class creates its own on the first request
- void setNetworkAccessManager(QNetworkAccessManager *networkAccessManager);
- QNetworkAccessManager *networkAccessManager() const;
-
-public slots:
- void sendScreenView(const QString &screenName, const QVariantMap &customValues = QVariantMap());
- void sendEvent(const QString &category, const QString &action, const QString &label = QString(), const QVariant &value = QVariant(),
- const QVariantMap &customValues = QVariantMap());
- void sendException(const QString &exceptionDescription, bool exceptionFatal = true, const QVariantMap &customValues = QVariantMap());
- void startSession();
- void endSession();
-
-private:
- GAnalyticsWorker *d;
-
- friend QDataStream &operator<<(QDataStream &outStream, const GAnalytics &analytics);
- friend QDataStream &operator>>(QDataStream &inStream, GAnalytics &analytics);
-};
-
-QDataStream &operator<<(QDataStream &outStream, const GAnalytics &analytics);
-QDataStream &operator>>(QDataStream &inStream, GAnalytics &analytics);
diff --git a/libraries/ganalytics/src/ganalytics.cpp b/libraries/ganalytics/src/ganalytics.cpp
deleted file mode 100644
index a4b7394f..00000000
--- a/libraries/ganalytics/src/ganalytics.cpp
+++ /dev/null
@@ -1,237 +0,0 @@
-#include "ganalytics.h"
-#include "ganalytics_worker.h"
-#include "sys.h"
-
-#include <QDataStream>
-#include <QDebug>
-#include <QLocale>
-#include <QNetworkAccessManager>
-#include <QNetworkReply>
-#include <QNetworkRequest>
-#include <QQueue>
-#include <QSettings>
-#include <QTimer>
-#include <QUrlQuery>
-#include <QUuid>
-
-GAnalytics::GAnalytics(const QString &trackingID, const QString &clientID, const int version, QObject *parent) : QObject(parent)
-{
- d = new GAnalyticsWorker(this);
- d->m_trackingID = trackingID;
- d->m_clientID = clientID;
- d->m_version = version;
-}
-
-/**
- * Destructor of class GAnalytics.
- */
-GAnalytics::~GAnalytics()
-{
- delete d;
-}
-
-void GAnalytics::setLogLevel(GAnalytics::LogLevel logLevel)
-{
- d->m_logLevel = logLevel;
-}
-
-GAnalytics::LogLevel GAnalytics::logLevel() const
-{
- return d->m_logLevel;
-}
-
-// SETTER and GETTER
-void GAnalytics::setViewportSize(const QString &viewportSize)
-{
- d->m_viewportSize = viewportSize;
-}
-
-QString GAnalytics::viewportSize() const
-{
- return d->m_viewportSize;
-}
-
-void GAnalytics::setLanguage(const QString &language)
-{
- d->m_language = language;
-}
-
-QString GAnalytics::language() const
-{
- return d->m_language;
-}
-
-void GAnalytics::setAnonymizeIPs(bool anonymize)
-{
- d->m_anonymizeIPs = anonymize;
-}
-
-bool GAnalytics::anonymizeIPs()
-{
- return d->m_anonymizeIPs;
-}
-
-void GAnalytics::setSendInterval(int milliseconds)
-{
- d->m_timer.setInterval(milliseconds);
-}
-
-int GAnalytics::sendInterval() const
-{
- return (d->m_timer.interval());
-}
-
-bool GAnalytics::isEnabled()
-{
- return d->m_isEnabled;
-}
-
-void GAnalytics::enable(bool state)
-{
- d->enable(state);
-}
-
-int GAnalytics::version()
-{
- return d->m_version;
-}
-
-void GAnalytics::setNetworkAccessManager(QNetworkAccessManager *networkAccessManager)
-{
- if (d->networkManager != networkAccessManager)
- {
- // Delete the old network manager if it was our child
- if (d->networkManager && d->networkManager->parent() == this)
- {
- d->networkManager->deleteLater();
- }
-
- d->networkManager = networkAccessManager;
- }
-}
-
-QNetworkAccessManager *GAnalytics::networkAccessManager() const
-{
- return d->networkManager;
-}
-
-static void appendCustomValues(QUrlQuery &query, const QVariantMap &customValues)
-{
- for (QVariantMap::const_iterator iter = customValues.begin(); iter != customValues.end(); ++iter)
- {
- query.addQueryItem(iter.key(), iter.value().toString());
- }
-}
-
-/**
- * Sent screen view is called when the user changed the applications view.
- * These action of the user should be noticed and reported. Therefore
- * a QUrlQuery is build in this method. It holts all the parameter for
- * a http POST. The UrlQuery will be stored in a message Queue.
- */
-void GAnalytics::sendScreenView(const QString &screenName, const QVariantMap &customValues)
-{
- d->logMessage(Info, QString("ScreenView: %1").arg(screenName));
-
- QUrlQuery query = d->buildStandardPostQuery("screenview");
- query.addQueryItem("cd", screenName);
- query.addQueryItem("an", d->m_appName);
- query.addQueryItem("av", d->m_appVersion);
- appendCustomValues(query, customValues);
-
- d->enqueQueryWithCurrentTime(query);
-}
-
-/**
- * This method is called whenever a button was pressed in the application.
- * A query for a POST message will be created to report this event. The
- * created query will be stored in a message queue.
- */
-void GAnalytics::sendEvent(const QString &category, const QString &action, const QString &label, const QVariant &value, const QVariantMap &customValues)
-{
- QUrlQuery query = d->buildStandardPostQuery("event");
- query.addQueryItem("an", d->m_appName);
- query.addQueryItem("av", d->m_appVersion);
- query.addQueryItem("ec", category);
- query.addQueryItem("ea", action);
- if (!label.isEmpty())
- query.addQueryItem("el", label);
- if (value.isValid())
- query.addQueryItem("ev", value.toString());
-
- appendCustomValues(query, customValues);
-
- d->enqueQueryWithCurrentTime(query);
-}
-
-/**
- * Method is called after an exception was raised. It builds a
- * query for a POST message. These query will be stored in a
- * message queue.
- */
-void GAnalytics::sendException(const QString &exceptionDescription, bool exceptionFatal, const QVariantMap &customValues)
-{
- QUrlQuery query = d->buildStandardPostQuery("exception");
- query.addQueryItem("an", d->m_appName);
- query.addQueryItem("av", d->m_appVersion);
-
- query.addQueryItem("exd", exceptionDescription);
-
- if (exceptionFatal)
- {
- query.addQueryItem("exf", "1");
- }
- else
- {
- query.addQueryItem("exf", "0");
- }
- appendCustomValues(query, customValues);
-
- d->enqueQueryWithCurrentTime(query);
-}
-
-/**
- * Session starts. This event will be sent by a POST message.
- * Query is setup in this method and stored in the message
- * queue.
- */
-void GAnalytics::startSession()
-{
- QVariantMap customValues;
- customValues.insert("sc", "start");
- sendEvent("Session", "Start", QString(), QVariant(), customValues);
-}
-
-/**
- * Session ends. This event will be sent by a POST message.
- * Query is setup in this method and stored in the message
- * queue.
- */
-void GAnalytics::endSession()
-{
- QVariantMap customValues;
- customValues.insert("sc", "end");
- sendEvent("Session", "End", QString(), QVariant(), customValues);
-}
-
-/**
- * Qut stream to persist class GAnalytics.
- */
-QDataStream &operator<<(QDataStream &outStream, const GAnalytics &analytics)
-{
- outStream << analytics.d->persistMessageQueue();
-
- return outStream;
-}
-
-/**
- * In stream to read GAnalytics from file.
- */
-QDataStream &operator>>(QDataStream &inStream, GAnalytics &analytics)
-{
- QList<QString> dataList;
- inStream >> dataList;
- analytics.d->readMessagesFromFile(dataList);
-
- return inStream;
-}
diff --git a/libraries/ganalytics/src/ganalytics_worker.cpp b/libraries/ganalytics/src/ganalytics_worker.cpp
deleted file mode 100644
index b0ae75a4..00000000
--- a/libraries/ganalytics/src/ganalytics_worker.cpp
+++ /dev/null
@@ -1,254 +0,0 @@
-#include "ganalytics.h"
-#include "ganalytics_worker.h"
-#include "sys.h"
-
-#include <QCoreApplication>
-#include <QNetworkAccessManager>
-#include <QNetworkReply>
-
-#include <QGuiApplication>
-#include <QScreen>
-
-const QLatin1String GAnalyticsWorker::dateTimeFormat("yyyy,MM,dd-hh:mm::ss:zzz");
-
-GAnalyticsWorker::GAnalyticsWorker(GAnalytics *parent)
- : QObject(parent), q(parent), m_logLevel(GAnalytics::Error)
-{
- m_appName = QCoreApplication::instance()->applicationName();
- m_appVersion = QCoreApplication::instance()->applicationVersion();
- m_request.setUrl(QUrl("https://www.google-analytics.com/collect"));
- m_request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
- m_request.setHeader(QNetworkRequest::UserAgentHeader, getUserAgent());
-
- m_language = QLocale::system().name().toLower().replace("_", "-");
- m_screenResolution = getScreenResolution();
-
- m_timer.setInterval(m_timerInterval);
- connect(&m_timer, &QTimer::timeout, this, &GAnalyticsWorker::postMessage);
-}
-
-void GAnalyticsWorker::enable(bool state)
-{
- // state change to the same is not valid.
- if(m_isEnabled == state)
- {
- return;
- }
-
- m_isEnabled = state;
- if(m_isEnabled)
- {
- // enable -> start doing things :)
- m_timer.start();
- }
- else
- {
- // disable -> stop the timer
- m_timer.stop();
- }
-}
-
-void GAnalyticsWorker::logMessage(GAnalytics::LogLevel level, const QString &message)
-{
- if (m_logLevel > level)
- {
- return;
- }
-
- qDebug() << "[Analytics]" << message;
-}
-
-/**
- * Build the POST query. Adds all parameter to the query
- * which are used in every POST.
- * @param type Type of POST message. The event which is to post.
- * @return query Most used parameter in a query for a POST.
- */
-QUrlQuery GAnalyticsWorker::buildStandardPostQuery(const QString &type)
-{
- QUrlQuery query;
- query.addQueryItem("v", "1");
- query.addQueryItem("tid", m_trackingID);
- query.addQueryItem("cid", m_clientID);
- if (!m_userID.isEmpty())
- {
- query.addQueryItem("uid", m_userID);
- }
- query.addQueryItem("t", type);
- query.addQueryItem("ul", m_language);
- query.addQueryItem("vp", m_viewportSize);
- query.addQueryItem("sr", m_screenResolution);
- if(m_anonymizeIPs)
- {
- query.addQueryItem("aip", "1");
- }
- return query;
-}
-
-/**
- * Get primary screen resolution.
- * @return A QString like "800x600".
- */
-QString GAnalyticsWorker::getScreenResolution()
-{
- QScreen *screen = QGuiApplication::primaryScreen();
- QSize size = screen->size();
-
- return QString("%1x%2").arg(size.width()).arg(size.height());
-}
-
-/**
- * Try to gain information about the system where this application
- * is running. It needs to get the name and version of the operating
- * system, the language and screen resolution.
- * All this information will be send in POST messages.
- * @return agent A QString with all the information formatted for a POST message.
- */
-QString GAnalyticsWorker::getUserAgent()
-{
- return QString("%1/%2").arg(m_appName).arg(m_appVersion);
-}
-
-/**
- * The message queue contains a list of QueryBuffer object.
- * QueryBuffer holds a QUrlQuery object and a QDateTime object.
- * These both object are freed from the buffer object and
- * inserted as QString objects in a QList.
- * @return dataList The list with concartinated queue data.
- */
-QList<QString> GAnalyticsWorker::persistMessageQueue()
-{
- QList<QString> dataList;
- foreach (QueryBuffer buffer, m_messageQueue)
- {
- dataList << buffer.postQuery.toString();
- dataList << buffer.time.toString(dateTimeFormat);
- }
- return dataList;
-}
-
-/**
- * Reads persistent messages from a file.
- * Gets all message data as a QList<QString>.
- * Two lines in the list build a QueryBuffer object.
- */
-void GAnalyticsWorker::readMessagesFromFile(const QList<QString> &dataList)
-{
- QListIterator<QString> iter(dataList);
- while (iter.hasNext())
- {
- QString queryString = iter.next();
- QString dateString = iter.next();
- QUrlQuery query;
- query.setQuery(queryString);
- QDateTime dateTime = QDateTime::fromString(dateString, dateTimeFormat);
- QueryBuffer buffer;
- buffer.postQuery = query;
- buffer.time = dateTime;
- m_messageQueue.enqueue(buffer);
- }
-}
-
-/**
- * Takes a QUrlQuery object and wrapp it together with
- * a QTime object into a QueryBuffer struct. These struct
- * will be stored in the message queue.
- */
-void GAnalyticsWorker::enqueQueryWithCurrentTime(const QUrlQuery &query)
-{
- QueryBuffer buffer;
- buffer.postQuery = query;
- buffer.time = QDateTime::currentDateTime();
-
- m_messageQueue.enqueue(buffer);
-}
-
-/**
- * This function is called by a timer interval.
- * The function tries to send a messages from the queue.
- * If message was successfully send then this function
- * will be called back to send next message.
- * If message queue contains more than one message then
- * the connection will kept open.
- * The message POST is asyncroniously when the server
- * answered a signal will be emitted.
- */
-void GAnalyticsWorker::postMessage()
-{
- if (m_messageQueue.isEmpty())
- {
- // queue empty -> try sending later
- m_timer.start();
- return;
- }
- else
- {
- // queue has messages -> stop timer and start sending
- m_timer.stop();
- }
-
- QString connection = "close";
- if (m_messageQueue.count() > 1)
- {
- connection = "keep-alive";
- }
-
- QueryBuffer buffer = m_messageQueue.head();
- QDateTime sendTime = QDateTime::currentDateTime();
- qint64 timeDiff = buffer.time.msecsTo(sendTime);
-
- if (timeDiff > fourHours)
- {
- // too old.
- m_messageQueue.dequeue();
- emit postMessage();
- return;
- }
-
- buffer.postQuery.addQueryItem("qt", QString::number(timeDiff));
- m_request.setRawHeader("Connection", connection.toUtf8());
- m_request.setHeader(QNetworkRequest::ContentLengthHeader, buffer.postQuery.toString().length());
-
- logMessage(GAnalytics::Debug, "Query string = " + buffer.postQuery.toString());
-
- // Create a new network access manager if we don't have one yet
- if (networkManager == NULL)
- {
- networkManager = new QNetworkAccessManager(this);
- }
-
- QNetworkReply *reply = networkManager->post(m_request, buffer.postQuery.query(QUrl::EncodeUnicode).toUtf8());
- connect(reply, SIGNAL(finished()), this, SLOT(postMessageFinished()));
-}
-
-/**
- * NetworkAccsessManager has finished to POST a message.
- * If POST message was successfully send then the message
- * query should be removed from queue.
- * SIGNAL "postMessage" will be emitted to send next message
- * if there is any.
- * If message couldn't be send then next try is when the
- * timer emits its signal.
- */
-void GAnalyticsWorker::postMessageFinished()
-{
- QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
-
- int httpStausCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
- if (httpStausCode < 200 || httpStausCode > 299)
- {
- logMessage(GAnalytics::Error, QString("Error posting message: %1").arg(reply->errorString()));
-
- // An error ocurred. Try sending later.
- m_timer.start();
- return;
- }
- else
- {
- logMessage(GAnalytics::Debug, "Message sent");
- }
-
- m_messageQueue.dequeue();
- postMessage();
- reply->deleteLater();
-}
diff --git a/libraries/ganalytics/src/ganalytics_worker.h b/libraries/ganalytics/src/ganalytics_worker.h
deleted file mode 100644
index 1962f799..00000000
--- a/libraries/ganalytics/src/ganalytics_worker.h
+++ /dev/null
@@ -1,65 +0,0 @@
-#pragma once
-
-#include <QUrlQuery>
-#include <QDateTime>
-#include <QTimer>
-#include <QNetworkRequest>
-#include <QQueue>
-
-struct QueryBuffer
-{
- QUrlQuery postQuery;
- QDateTime time;
-};
-
-class GAnalyticsWorker : public QObject
-{
- Q_OBJECT
-
-public:
- explicit GAnalyticsWorker(GAnalytics *parent = 0);
-
- GAnalytics *q;
-
- QNetworkAccessManager *networkManager = nullptr;
-
- QQueue<QueryBuffer> m_messageQueue;
- QTimer m_timer;
- QNetworkRequest m_request;
- GAnalytics::LogLevel m_logLevel;
-
- QString m_trackingID;
- QString m_clientID;
- QString m_userID;
- QString m_appName;
- QString m_appVersion;
- QString m_language;
- QString m_screenResolution;
- QString m_viewportSize;
-
- bool m_anonymizeIPs = false;
- bool m_isEnabled = false;
- int m_timerInterval = 30000;
- int m_version = 0;
-
- const static int fourHours = 4 * 60 * 60 * 1000;
- const static QLatin1String dateTimeFormat;
-
-public:
- void logMessage(GAnalytics::LogLevel level, const QString &message);
-
- QUrlQuery buildStandardPostQuery(const QString &type);
- QString getScreenResolution();
- QString getUserAgent();
- QList<QString> persistMessageQueue();
- void readMessagesFromFile(const QList<QString> &dataList);
-
- void enqueQueryWithCurrentTime(const QUrlQuery &query);
- void setIsSending(bool doSend);
- void enable(bool state);
-
-public slots:
- void postMessage();
- void postMessageFinished();
-};
-
diff --git a/libraries/iconfix/CMakeLists.txt b/libraries/iconfix/CMakeLists.txt
index 52a31c68..049879c4 100644
--- a/libraries/iconfix/CMakeLists.txt
+++ b/libraries/iconfix/CMakeLists.txt
@@ -12,17 +12,9 @@ internal/qiconloader.cpp
internal/qiconloader_p.h
)
-add_library(Launcher_iconfix SHARED ${ICONFIX_SOURCES})
+add_library(Launcher_iconfix ${ICONFIX_SOURCES})
target_include_directories(Launcher_iconfix PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}" )
target_link_libraries(Launcher_iconfix Qt5::Core Qt5::Widgets)
-set_target_properties(Launcher_iconfix PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1)
generate_export_header(Launcher_iconfix)
-
-# Install it
-install(
- TARGETS Launcher_iconfix
- RUNTIME DESTINATION ${LIBRARY_DEST_DIR}
- LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
-) \ No newline at end of file
diff --git a/libraries/javacheck/CMakeLists.txt b/libraries/javacheck/CMakeLists.txt
index d0bea2a5..f599bf15 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 7 -source 7 -Xlint:deprecation -Xlint:unchecked)
+set(CMAKE_JAVA_COMPILE_FLAGS -target 8 -source 8 -Xlint:deprecation -Xlint:unchecked)
set(SRC
JavaCheck.java
diff --git a/libraries/katabasis/CMakeLists.txt b/libraries/katabasis/CMakeLists.txt
index c6115881..d579dc29 100644
--- a/libraries/katabasis/CMakeLists.txt
+++ b/libraries/katabasis/CMakeLists.txt
@@ -5,14 +5,6 @@ if(IS_IN_SOURCE_BUILD)
message(FATAL_ERROR "You are building Katabasis in-source. Please separate the build tree from the source tree.")
endif()
-if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
- if(CMAKE_HOST_SYSTEM_VERSION MATCHES ".*[Mm]icrosoft.*" OR
- CMAKE_HOST_SYSTEM_VERSION MATCHES ".*WSL.*"
- )
- message(FATAL_ERROR "Building Katabasis is not supported in Linux-on-Windows distributions. Use a real Linux machine, not a fraudulent one.")
- endif()
-endif()
-
project(Katabasis)
enable_testing()
diff --git a/libraries/katabasis/README.md b/libraries/katabasis/README.md
index a4dc0994..08f3c9d1 100644
--- a/libraries/katabasis/README.md
+++ b/libraries/katabasis/README.md
@@ -1,4 +1,4 @@
-# Katabasis - MS-flavoerd OAuth for Qt, derived from the O2 library
+# Katabasis - MS-flavored OAuth for Qt, derived from the O2 library
This library's sole purpose is to make interacting with MSA and various MSA and XBox authenticated services less painful.
@@ -10,7 +10,7 @@ Notes to contributors:
* Please follow the coding style of the existing source, where reasonable
* Code contributions are released under Simplified BSD License, as specified in LICENSE. Do not contribute if this license does not suit your code
- * If you are interested in working on this, come to the MultiMC Discord server and talk first
+ * If you are interested in working on this, come to the PolyMC Discord server and talk first
## Installation
diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt
index ff2a4149..54913fd4 100644
--- a/libraries/launcher/CMakeLists.txt
+++ b/libraries/launcher/CMakeLists.txt
@@ -4,7 +4,7 @@ find_package(Java 1.7 REQUIRED COMPONENTS Development)
include(UseJava)
set(CMAKE_JAVA_JAR_ENTRY_POINT org.multimc.EntryPoint)
-set(CMAKE_JAVA_COMPILE_FLAGS -target 7 -source 7 -Xlint:deprecation -Xlint:unchecked)
+set(CMAKE_JAVA_COMPILE_FLAGS -target 8 -source 8 -Xlint:deprecation -Xlint:unchecked)
set(SRC
org/multimc/EntryPoint.java
diff --git a/libraries/quazip b/libraries/quazip
-Subproject b1a72ac0bb5a732bf887a535ab75c6f9bedb6b6
+Subproject 09ec1d10c6d627f895109b21728dda000cbfa7d
diff --git a/libraries/rainbow/CMakeLists.txt b/libraries/rainbow/CMakeLists.txt
index 833538e3..a07135c3 100644
--- a/libraries/rainbow/CMakeLists.txt
+++ b/libraries/rainbow/CMakeLists.txt
@@ -9,14 +9,14 @@ src/rainbow.cpp
)
add_definitions(-DRAINBOW_LIBRARY)
-add_library(Launcher_rainbow SHARED ${RAINBOW_SOURCES})
-target_include_directories(Launcher_rainbow PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
+add_library(PolyMC_rainbow SHARED ${RAINBOW_SOURCES})
+target_include_directories(PolyMC_rainbow PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
-target_link_libraries(Launcher_rainbow Qt5::Core Qt5::Gui)
+target_link_libraries(PolyMC_rainbow Qt5::Core Qt5::Gui)
# Install it
install(
- TARGETS Launcher_rainbow
+ TARGETS PolyMC_rainbow
RUNTIME DESTINATION ${LIBRARY_DEST_DIR}
LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
)
diff --git a/notsecrets/CMakeLists.txt b/notsecrets/CMakeLists.txt
deleted file mode 100644
index b5dd3cf8..00000000
--- a/notsecrets/CMakeLists.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-add_library(secrets STATIC Secrets.cpp Secrets.h)
-target_link_libraries(secrets Qt5::Core)
-target_compile_definitions(secrets PUBLIC -DEMBED_SECRETS)
-target_include_directories(secrets PUBLIC .)
-
-set(Launcher_CommonName "DevLauncher")
-
-set(Launcher_Copyright "MultiMC Contributors" PARENT_SCOPE)
-set(Launcher_Domain "multimc.org" PARENT_SCOPE)
-set(Launcher_Name "${Launcher_CommonName}" PARENT_SCOPE)
-set(Launcher_DisplayName "${Launcher_CommonName} 5" PARENT_SCOPE)
-set(Launcher_UserAgent "${Launcher_CommonName}/5.0" PARENT_SCOPE)
-set(Launcher_ConfigFile "devlauncher.cfg" PARENT_SCOPE)
-set(Launcher_Git "https://github.com/MultiMC/Launcher" PARENT_SCOPE)
-
-set(Launcher_Branding_ICNS "notsecrets/Launcher.icns" PARENT_SCOPE)
-set(Launcher_Branding_WindowsRC "notsecrets/launcher.rc" PARENT_SCOPE)
-set(Launcher_Branding_LogoQRC "notsecrets/logo.qrc" PARENT_SCOPE)
diff --git a/notsecrets/Launcher.icns b/notsecrets/Launcher.icns
deleted file mode 100644
index 951b74fc..00000000
--- a/notsecrets/Launcher.icns
+++ /dev/null
Binary files differ
diff --git a/notsecrets/Launcher.ico b/notsecrets/Launcher.ico
deleted file mode 100644
index 9308958f..00000000
--- a/notsecrets/Launcher.ico
+++ /dev/null
Binary files differ
diff --git a/notsecrets/README.md b/notsecrets/README.md
deleted file mode 100644
index 4664fa89..00000000
--- a/notsecrets/README.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# DevLauncher (Not) Secrets
-
-This is a dummy implementation of MultiMC's _Secrets_ library, used to store private information needed for:
-- Application name and logo (and branding in general)
-- Various URLs and API endpoints
-- Your Microsoft Identity Platform client ID. **This is required to use Microsoft accounts to play!**
- - If omitted, adding Microsoft accounts will be completely disabled.
-
-## MultiMC development
-
-In its current state, the `notsecrets` library is suitable for MultiMC code contributions (the code builds as `DevLauncher`).
-
-All you have to do is add the Microsoft client ID. See `Secrets.cpp` for details.
-
-## Forking
-
-Forks of this project that intend to distribute binaries to users should use their own implementation of this library that does not impersonate MultiMC in any way (see the Apache 2.0 license, common sense and trademark law).
-
-A fork is a serious business, especially if you intend to distribute binaries to users. This is because you need to agree to the Microsoft identity platform Terms of Use:
-
-https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use
-
-If you truly want to accept such an agreement, a starting point is to copy `notsecrets` to `secrets`, enable the `Launcher_EMBED_SECRETS` CMake option and customize the files.
-
-We do not want confused users asking for help with your fork in MultiMC Discord or similar locations.
diff --git a/notsecrets/Secrets.cpp b/notsecrets/Secrets.cpp
deleted file mode 100644
index 88995635..00000000
--- a/notsecrets/Secrets.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#include "Secrets.h"
-
-#include <array>
-#include <cstdio>
-
-namespace {
-
-/*
- * This is the MSA client ID. It is confidential and should not be reused.
- * You can obtain one for yourself by using azure app registration:
- * https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app
- *
- * The app registration should:
- * - Be only for personal accounts.
- * - Not have any redirect URI.
- * - Not have any platform.
- * - Have no credentials.
- * - No certificates.
- * - No client secrets.
- * - Enable 'Live SDK support' for access to XBox APIs.
- * - Enable 'public client flows' for OAuth2 device flow.
- *
- * By putting one in here, you accept the terms and conditions for using the MS Identity Plaform and assume all responsibilities associated with it.
- * See: https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use
- *
- * Above all else, do not impersonate other applications! This includes the Mojang Launcher and MultiMC - your builds are *NOT* MultiMC.
- *
- * If you intend to base your own launcher on this code, take care and customize this to obfuscate the client ID, so it cannot be trivially found by casual attackers.
- */
-
-QString MSAClientID = "";
-}
-
-namespace Secrets {
-bool hasMSAClientID() {
- return !MSAClientID.isEmpty();
-}
-
-QString getMSAClientID(uint8_t separator) {
- return MSAClientID;
-}
-}
diff --git a/notsecrets/Secrets.h b/notsecrets/Secrets.h
deleted file mode 100644
index 6872b68e..00000000
--- a/notsecrets/Secrets.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma once
-#include <QString>
-#include <cstdint>
-
-namespace Secrets {
-bool hasMSAClientID();
-QString getMSAClientID(uint8_t separator);
-}
diff --git a/notsecrets/genicons.sh b/notsecrets/genicons.sh
deleted file mode 100755
index e6f704f9..00000000
--- a/notsecrets/genicons.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#/bin/bash
-
-inkscape -w 16 -h 16 -o logo_16.png logo.svg
-inkscape -w 24 -h 24 -o logo_24.png logo.svg
-inkscape -w 32 -h 32 -o logo_32.png logo.svg
-inkscape -w 48 -h 48 -o logo_48.png logo.svg
-inkscape -w 64 -h 64 -o logo_64.png logo.svg
-inkscape -w 128 -h 128 -o logo_128.png logo.svg
-
-convert logo_128.png logo_64.png logo_48.png logo_32.png logo_24.png logo_16.png Launcher.ico
-
-inkscape -w 256 -h 256 -o logo_256.png logo.svg
-inkscape -w 512 -h 512 -o logo_512.png logo.svg
-inkscape -w 1024 -h 1024 -o logo_1024.png logo.svg
-
-png2icns Launcher.icns logo_1024.png logo_512.png logo_256.png logo_128.png logo_32.png logo_16.png
-
-rm -f logo_*.png
diff --git a/notsecrets/logo.svg b/notsecrets/logo.svg
deleted file mode 100644
index a1c8e439..00000000
--- a/notsecrets/logo.svg
+++ /dev/null
@@ -1,271 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- width="68.26667"
- height="68.26667"
- id="svg4427"
- version="1.1"
- inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
- sodipodi:docname="logo.svg"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:dc="http://purl.org/dc/elements/1.1/">
- <defs
- id="defs4429">
- <linearGradient
- id="linearGradient5668"
- inkscape:collect="always">
- <stop
- id="stop5670"
- offset="0"
- style="stop-color:#75b54b;stop-opacity:1;" />
- <stop
- id="stop5672"
- offset="1"
- style="stop-color:#75b54b;stop-opacity:0.6" />
- </linearGradient>
- <linearGradient
- id="linearGradient5084"
- inkscape:collect="always">
- <stop
- id="stop5086"
- offset="0"
- style="stop-color:#000000;stop-opacity:0.8" />
- <stop
- id="stop5088"
- offset="1"
- style="stop-color:#000000;stop-opacity:0.35" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5668"
- id="linearGradient5072"
- x1="6.7342591"
- y1="28.510933"
- x2="50.506943"
- y2="61.773685"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(-0.01532073,-0.00938002)" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5084"
- id="linearGradient5082"
- x1="14.312115"
- y1="9.7948904"
- x2="44.097023"
- y2="82.973114"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5668"
- id="linearGradient3281"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(-0.01532073,-0.00938002)"
- x1="6.7342591"
- y1="28.510933"
- x2="50.506943"
- y2="61.773685" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5668"
- id="linearGradient3283"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(-0.01532073,-0.00938002)"
- x1="6.7342591"
- y1="28.510933"
- x2="50.506943"
- y2="61.773685" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5084"
- id="linearGradient3288"
- gradientUnits="userSpaceOnUse"
- x1="14.312115"
- y1="9.7948904"
- x2="44.097023"
- y2="82.973114" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5084"
- id="linearGradient3290"
- gradientUnits="userSpaceOnUse"
- x1="14.312115"
- y1="9.7948904"
- x2="44.097023"
- y2="82.973114" />
- <linearGradient
- inkscape:collect="always"
- id="linearGradient5580">
- <stop
- style="stop-color:#000000;stop-opacity:0.0627451"
- offset="0"
- id="stop5576" />
- <stop
- style="stop-color:#252424;stop-opacity:0.58823532"
- offset="1"
- id="stop5578" />
- </linearGradient>
- <linearGradient
- id="linearGradient3999"
- inkscape:collect="always">
- <stop
- id="stop3995"
- offset="0"
- style="stop-color:#777777;stop-opacity:1" />
- <stop
- id="stop3997"
- offset="1"
- style="stop-color:#4f4e4e;stop-opacity:1" />
- </linearGradient>
- <linearGradient
- id="linearGradient2727"
- inkscape:collect="always">
- <stop
- id="stop2723"
- offset="0"
- style="stop-color:#7f8080;stop-opacity:1" />
- <stop
- id="stop2725"
- offset="1"
- style="stop-color:#404040;stop-opacity:1" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient2727"
- id="linearGradient2050"
- gradientUnits="userSpaceOnUse"
- x1="36.546478"
- y1="33.80484"
- x2="86.415741"
- y2="97.065842"
- gradientTransform="translate(-0.0016031,-4.5764148e-4)" />
- <radialGradient
- inkscape:collect="always"
- xlink:href="#linearGradient3999"
- id="radialGradient2052"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(-0.00142193,-4.5702768e-4)"
- cx="34.133331"
- cy="34.133335"
- fx="34.133331"
- fy="34.133335"
- r="29.866665" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient5580"
- id="linearGradient2140"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(-0.0010513,-9.083059e-4)"
- x1="29.866674"
- y1="29.867579"
- x2="38.400005"
- y2="38.400913" />
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="8.0000001"
- inkscape:cx="-15.9375"
- inkscape:cy="15.75"
- inkscape:current-layer="layer1"
- showgrid="false"
- inkscape:document-units="px"
- inkscape:grid-bbox="true"
- inkscape:window-width="3840"
- inkscape:window-height="2129"
- inkscape:window-x="1200"
- inkscape:window-y="0"
- inkscape:window-maximized="1"
- inkscape:snap-bbox="true"
- inkscape:bbox-paths="true"
- inkscape:snap-bbox-edge-midpoints="true"
- inkscape:bbox-nodes="true"
- inkscape:snap-bbox-midpoints="true"
- inkscape:snap-smooth-nodes="true"
- inkscape:snap-midpoints="false"
- inkscape:snap-intersection-paths="true"
- inkscape:object-paths="true"
- inkscape:snap-object-midpoints="true"
- inkscape:snap-text-baseline="true"
- inkscape:snap-center="true"
- inkscape:pagecheckerboard="0">
- <inkscape:grid
- type="xygrid"
- id="grid4446"
- empspacing="16"
- visible="true"
- enabled="true"
- snapvisiblegridlinesonly="true"
- spacingx="4.2666667"
- spacingy="4.2666667"
- originx="0"
- originy="0" />
- </sodipodi:namedview>
- <metadata
- id="metadata4432">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- id="layer1"
- inkscape:label="Layer 1"
- inkscape:groupmode="layer">
- <g
- id="g2048"
- transform="translate(9.113e-4,0.00104183)">
- <rect
- rx="8.5333338"
- ry="8.5333338"
- style="fill:url(#linearGradient2050);fill-opacity:1;stroke:none;stroke-width:17.0667"
- id="rect2026"
- width="68.266998"
- height="68.26667"
- x="-0.0016037956"
- y="-0.00045376646" />
- <rect
- rx="4.2666626"
- y="4.2662144"
- x="4.2652307"
- height="59.733334"
- width="59.73333"
- id="rect2028"
- style="opacity:0.5;fill:url(#radialGradient2052);fill-opacity:1;stroke:none;stroke-width:14.9333"
- ry="4.2666669" />
- <path
- style="opacity:1;fill:url(#linearGradient2140);fill-opacity:1;stroke:none;stroke-width:17.06666756"
- d="m 8.5322887,-9.083059e-4 c -4.72747,0 -8.5332,3.8057359059 -8.5332,8.5332029059 V 59.731515 c 0,4.727467 3.80573,8.535156 8.5332,8.535156 H 59.731502 c 4.72747,0 8.5332,-3.807689 8.5332,-8.535156 V 8.5322946 c 0,-4.727467 -3.80573,-8.5332029059 -8.5332,-8.5332029059 z m 0,4.2675779059 H 59.731502 c 2.36373,0 4.26758,1.901892 4.26758,4.265625 V 59.731515 c 0,2.363733 -1.90385,4.267578 -4.26758,4.267578 H 8.5322887 c -2.36373,0 -4.26758,-1.903845 -4.26758,-4.267578 V 8.5322946 c 0,-2.363733 1.90385,-4.265625 4.26758,-4.265625 z"
- id="path2046"
- inkscape:connector-curvature="0" />
- </g>
- <path
- style="fill:#000000;fill-opacity:0.494118;fill-rule:evenodd;stroke:none;stroke-width:0.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m 17.343575,15.030602 -1e-6,43.517002 38.229105,-21.758501 z"
- id="path1382"
- sodipodi:nodetypes="cccc" />
- <path
- style="fill:#00ff66;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m 16.888808,11.986385 -1e-6,43.517003 38.229104,-21.758501 z"
- id="path1059"
- sodipodi:nodetypes="cccc" />
- <path
- id="path13539"
- style="fill:#ff00ff;fill-opacity:0.992157;fill-rule:evenodd;stroke:none;stroke-width:0.999996px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="M 19.576616,50.882478 C 29.612077,45.170295 39.647539,39.458112 49.682999,33.745928 39.647539,28.032869 29.612077,22.319811 19.576616,16.606753 c 0,11.425241 0,22.850484 0,34.275725 z" />
- </g>
-</svg>
diff --git a/packages/nix/NIX.md b/packages/nix/NIX.md
new file mode 100644
index 00000000..1ceba9a3
--- /dev/null
+++ b/packages/nix/NIX.md
@@ -0,0 +1,31 @@
+# How to import
+
+To import with flakes use
+```nix
+inputs = {
+ polymc.url = "github:PolyMC/PolyMC";
+};
+
+...
+
+nixpkgs.overlays = [ inputs.polymc.overlay ]; ## Within configuration.nix
+environment.systemPackages = with pkgs; [ polymc ]; ##
+```
+
+To import without flakes use channels:
+
+```
+nix-channel --add https://github.com/PolyMC/PolyMC/archive/master.tar.gz polymc
+nix-channel --update polymc
+nix-env -iA polymc
+```
+
+or alternatively you can use
+
+```
+nixpkgs.overlays = [
+ (import (builtins.fetchTarball "https://github.com/PolyMC/PolyMC/archive/develop.tar.gz")).overlay
+];
+
+environment.systemPackages = with pkgs; [ polymc ];
+```
diff --git a/packages/nix/flake-compat.nix b/packages/nix/flake-compat.nix
new file mode 100644
index 00000000..bb7ee13e
--- /dev/null
+++ b/packages/nix/flake-compat.nix
@@ -0,0 +1,9 @@
+let
+ lock = builtins.fromJSON (builtins.readFile ../../flake.lock);
+ inherit (lock.nodes.flake-compat.locked) rev narHash;
+ flake-compat = fetchTarball {
+ url = "https://github.com/edolstra/flake-compat/archive/${rev}.tar.gz";
+ sha256 = narHash;
+ };
+in
+import flake-compat { src = ../..; }
diff --git a/packages/nix/polymc/default.nix b/packages/nix/polymc/default.nix
new file mode 100644
index 00000000..e65a7e34
--- /dev/null
+++ b/packages/nix/polymc/default.nix
@@ -0,0 +1,82 @@
+{ lib
+, mkDerivation
+, fetchFromGitHub
+, cmake
+, ninja
+, jdk8
+, jdk
+, zlib
+, file
+, makeWrapper
+, xorg
+, libpulseaudio
+, qtbase
+, libGL
+, msaClientID ? ""
+
+# flake
+, self
+, submoduleNbt
+, submoduleQuazip
+}:
+
+let
+ # Libraries required to run Minecraft
+ libpath = with xorg; lib.makeLibraryPath [
+ libX11
+ libXext
+ libXcursor
+ libXrandr
+ libXxf86vm
+ libpulseaudio
+ libGL
+ ];
+
+ # This variable will be passed to Minecraft by PolyMC
+ gameLibraryPath = libpath + ":/run/opengl-driver/lib";
+in
+
+mkDerivation rec {
+ pname = "polymc";
+ version = "nightly";
+
+ src = lib.cleanSource self;
+
+ nativeBuildInputs = [ cmake ninja file makeWrapper ];
+ buildInputs = [ qtbase jdk8 zlib ];
+
+ dontWrapQtApps = true;
+
+ postPatch = ''
+ # hardcode jdk paths
+ substituteInPlace launcher/java/JavaUtils.cpp \
+ --replace 'scanJavaDir("/usr/lib/jvm")' 'javas.append("${jdk}/lib/openjdk/bin/java")' \
+ --replace 'scanJavaDir("/usr/lib32/jvm")' 'javas.append("${jdk8}/lib/openjdk/bin/java")'
+ '' + lib.optionalString (msaClientID != "") ''
+ # add client ID
+ substituteInPlace CMakeLists.txt \
+ --replace '17b47edd-c884-4997-926d-9e7f9a6b4647' '${msaClientID}'
+ '';
+
+ postUnpack = ''
+ # Copy submodules inputs
+ rm -rf source/libraries/{libnbtplusplus,quazip}
+ mkdir source/libraries/{libnbtplusplus,quazip}
+ cp -a ${submoduleNbt}/* source/libraries/libnbtplusplus
+ cp -a ${submoduleQuazip}/* source/libraries/quazip
+ chmod a+r+w source/libraries/{libnbtplusplus,quazip}/*
+ '';
+
+ cmakeFlags = [
+ "-GNinja"
+ "-DLauncher_LAYOUT=lin-system"
+ ];
+
+ postInstall = ''
+ # xorg.xrandr needed for LWJGL [2.9.2, 3) https://github.com/LWJGL/lwjgl/issues/128
+ wrapProgram $out/bin/polymc \
+ "''${qtWrapperArgs[@]}" \
+ --set GAME_LIBRARY_PATH ${gameLibraryPath} \
+ --prefix PATH : ${lib.makeBinPath [ xorg.xrandr ]}
+ '';
+}
diff --git a/program_info/CMakeLists.txt b/program_info/CMakeLists.txt
new file mode 100644
index 00000000..f9d7621d
--- /dev/null
+++ b/program_info/CMakeLists.txt
@@ -0,0 +1,20 @@
+set(Launcher_CommonName "PolyMC")
+
+set(Launcher_Copyright "PolyMC Contributors\\n© 2012-2021 MultiMC Contributors" PARENT_SCOPE)
+set(Launcher_Domain "polymc.org" PARENT_SCOPE)
+set(Launcher_Name "${Launcher_CommonName}" PARENT_SCOPE)
+set(Launcher_DisplayName "${Launcher_CommonName}" PARENT_SCOPE)
+set(Launcher_UserAgent "${Launcher_CommonName}/${Launcher_RELEASE_VERSION_NAME}" PARENT_SCOPE)
+set(Launcher_ConfigFile "polymc.cfg" PARENT_SCOPE)
+set(Launcher_Git "https://github.com/PolyMC/PolyMC" PARENT_SCOPE)
+set(Launcher_DesktopFileName "org.polymc.PolyMC.desktop" PARENT_SCOPE)
+
+set(Launcher_Desktop "program_info/org.polymc.PolyMC.desktop" PARENT_SCOPE)
+set(Launcher_MetaInfo "program_info/org.polymc.PolyMC.metainfo.xml" PARENT_SCOPE)
+set(Launcher_SVG "program_info/org.polymc.PolyMC.svg" PARENT_SCOPE)
+set(Launcher_Branding_ICNS "program_info/polymc.icns" PARENT_SCOPE)
+set(Launcher_Branding_WindowsRC "program_info/polymc.rc" PARENT_SCOPE)
+set(Launcher_Branding_LogoQRC "program_info/polymc.qrc" PARENT_SCOPE)
+
+configure_file(org.polymc.PolyMC.desktop.in org.polymc.PolyMC.desktop)
+configure_file(org.polymc.PolyMC.metainfo.xml.in org.polymc.PolyMC.metainfo.xml)
diff --git a/program_info/LICENSE b/program_info/LICENSE
new file mode 100644
index 00000000..40cc6059
--- /dev/null
+++ b/program_info/LICENSE
@@ -0,0 +1,439 @@
+Attribution-NonCommercial-ShareAlike 4.0 International
+
+This license only applies to the logos and branding in this folder.
+
+=======================================================================
+
+Creative Commons Corporation ("Creative Commons") is not a law firm and
+does not provide legal services or legal advice. Distribution of
+Creative Commons public licenses does not create a lawyer-client or
+other relationship. Creative Commons makes its licenses and related
+information available on an "as-is" basis. Creative Commons gives no
+warranties regarding its licenses, any material licensed under their
+terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the
+fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and
+conditions that creators and other rights holders may use to share
+original works of authorship and other material subject to copyright
+and certain other rights specified in the public license below. The
+following considerations are for informational purposes only, are not
+exhaustive, and do not form part of our licenses.
+
+ Considerations for licensors: Our public licenses are
+ intended for use by those authorized to give the public
+ permission to use material in ways otherwise restricted by
+ copyright and certain other rights. Our licenses are
+ irrevocable. Licensors should read and understand the terms
+ and conditions of the license they choose before applying it.
+ Licensors should also secure all rights necessary before
+ applying our licenses so that the public can reuse the
+ material as expected. Licensors should clearly mark any
+ material not subject to the license. This includes other CC-
+ licensed material, or material used under an exception or
+ limitation to copyright. More considerations for licensors:
+ wiki.creativecommons.org/Considerations_for_licensors
+
+ Considerations for the public: By using one of our public
+ licenses, a licensor grants the public permission to use the
+ licensed material under specified terms and conditions. If
+ the licensor's permission is not necessary for any reason--for
+ example, because of any applicable exception or limitation to
+ copyright--then that use is not regulated by the license. Our
+ licenses grant only permissions under copyright and certain
+ other rights that a licensor has authority to grant. Use of
+ the licensed material may still be restricted for other
+ reasons, including because others have copyright or other
+ rights in the material. A licensor may make special requests,
+ such as asking that all changes be marked or described.
+ Although not required by our licenses, you are encouraged to
+ respect those requests where reasonable. More_considerations
+ for the public:
+ wiki.creativecommons.org/Considerations_for_licensees
+
+=======================================================================
+
+Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
+Public License
+
+By exercising the Licensed Rights (defined below), You accept and agree
+to be bound by the terms and conditions of this Creative Commons
+Attribution-NonCommercial-ShareAlike 4.0 International Public License
+("Public License"). To the extent this Public License may be
+interpreted as a contract, You are granted the Licensed Rights in
+consideration of Your acceptance of these terms and conditions, and the
+Licensor grants You such rights in consideration of benefits the
+Licensor receives from making the Licensed Material available under
+these terms and conditions.
+
+
+Section 1 -- Definitions.
+
+ a. Adapted Material means material subject to Copyright and Similar
+ Rights that is derived from or based upon the Licensed Material
+ and in which the Licensed Material is translated, altered,
+ arranged, transformed, or otherwise modified in a manner requiring
+ permission under the Copyright and Similar Rights held by the
+ Licensor. For purposes of this Public License, where the Licensed
+ Material is a musical work, performance, or sound recording,
+ Adapted Material is always produced where the Licensed Material is
+ synched in timed relation with a moving image.
+
+ b. Adapter's License means the license You apply to Your Copyright
+ and Similar Rights in Your contributions to Adapted Material in
+ accordance with the terms and conditions of this Public License.
+
+ c. BY-NC-SA Compatible License means a license listed at
+ creativecommons.org/compatiblelicenses, approved by Creative
+ Commons as essentially the equivalent of this Public License.
+
+ d. Copyright and Similar Rights means copyright and/or similar rights
+ closely related to copyright including, without limitation,
+ performance, broadcast, sound recording, and Sui Generis Database
+ Rights, without regard to how the rights are labeled or
+ categorized. For purposes of this Public License, the rights
+ specified in Section 2(b)(1)-(2) are not Copyright and Similar
+ Rights.
+
+ e. Effective Technological Measures means those measures that, in the
+ absence of proper authority, may not be circumvented under laws
+ fulfilling obligations under Article 11 of the WIPO Copyright
+ Treaty adopted on December 20, 1996, and/or similar international
+ agreements.
+
+ f. Exceptions and Limitations means fair use, fair dealing, and/or
+ any other exception or limitation to Copyright and Similar Rights
+ that applies to Your use of the Licensed Material.
+
+ g. License Elements means the license attributes listed in the name
+ of a Creative Commons Public License. The License Elements of this
+ Public License are Attribution, NonCommercial, and ShareAlike.
+
+ h. Licensed Material means the artistic or literary work, database,
+ or other material to which the Licensor applied this Public
+ License.
+
+ i. Licensed Rights means the rights granted to You subject to the
+ terms and conditions of this Public License, which are limited to
+ all Copyright and Similar Rights that apply to Your use of the
+ Licensed Material and that the Licensor has authority to license.
+
+ j. Licensor means the individual(s) or entity(ies) granting rights
+ under this Public License.
+
+ k. NonCommercial means not primarily intended for or directed towards
+ commercial advantage or monetary compensation. For purposes of
+ this Public License, the exchange of the Licensed Material for
+ other material subject to Copyright and Similar Rights by digital
+ file-sharing or similar means is NonCommercial provided there is
+ no payment of monetary compensation in connection with the
+ exchange.
+
+ l. Share means to provide material to the public by any means or
+ process that requires permission under the Licensed Rights, such
+ as reproduction, public display, public performance, distribution,
+ dissemination, communication, or importation, and to make material
+ available to the public including in ways that members of the
+ public may access the material from a place and at a time
+ individually chosen by them.
+
+ m. Sui Generis Database Rights means rights other than copyright
+ resulting from Directive 96/9/EC of the European Parliament and of
+ the Council of 11 March 1996 on the legal protection of databases,
+ as amended and/or succeeded, as well as other essentially
+ equivalent rights anywhere in the world.
+
+ n. You means the individual or entity exercising the Licensed Rights
+ under this Public License. Your has a corresponding meaning.
+
+
+Section 2 -- Scope.
+
+ a. License grant.
+
+ 1. Subject to the terms and conditions of this Public License,
+ the Licensor hereby grants You a worldwide, royalty-free,
+ non-sublicensable, non-exclusive, irrevocable license to
+ exercise the Licensed Rights in the Licensed Material to:
+
+ a. reproduce and Share the Licensed Material, in whole or
+ in part, for NonCommercial purposes only; and
+
+ b. produce, reproduce, and Share Adapted Material for
+ NonCommercial purposes only.
+
+ 2. Exceptions and Limitations. For the avoidance of doubt, where
+ Exceptions and Limitations apply to Your use, this Public
+ License does not apply, and You do not need to comply with
+ its terms and conditions.
+
+ 3. Term. The term of this Public License is specified in Section
+ 6(a).
+
+ 4. Media and formats; technical modifications allowed. The
+ Licensor authorizes You to exercise the Licensed Rights in
+ all media and formats whether now known or hereafter created,
+ and to make technical modifications necessary to do so. The
+ Licensor waives and/or agrees not to assert any right or
+ authority to forbid You from making technical modifications
+ necessary to exercise the Licensed Rights, including
+ technical modifications necessary to circumvent Effective
+ Technological Measures. For purposes of this Public License,
+ simply making modifications authorized by this Section 2(a)
+ (4) never produces Adapted Material.
+
+ 5. Downstream recipients.
+
+ a. Offer from the Licensor -- Licensed Material. Every
+ recipient of the Licensed Material automatically
+ receives an offer from the Licensor to exercise the
+ Licensed Rights under the terms and conditions of this
+ Public License.
+
+ b. Additional offer from the Licensor -- Adapted Material.
+ Every recipient of Adapted Material from You
+ automatically receives an offer from the Licensor to
+ exercise the Licensed Rights in the Adapted Material
+ under the conditions of the Adapter's License You apply.
+
+ c. No downstream restrictions. You may not offer or impose
+ any additional or different terms or conditions on, or
+ apply any Effective Technological Measures to, the
+ Licensed Material if doing so restricts exercise of the
+ Licensed Rights by any recipient of the Licensed
+ Material.
+
+ 6. No endorsement. Nothing in this Public License constitutes or
+ may be construed as permission to assert or imply that You
+ are, or that Your use of the Licensed Material is, connected
+ with, or sponsored, endorsed, or granted official status by,
+ the Licensor or others designated to receive attribution as
+ provided in Section 3(a)(1)(A)(i).
+
+ b. Other rights.
+
+ 1. Moral rights, such as the right of integrity, are not
+ licensed under this Public License, nor are publicity,
+ privacy, and/or other similar personality rights; however, to
+ the extent possible, the Licensor waives and/or agrees not to
+ assert any such rights held by the Licensor to the limited
+ extent necessary to allow You to exercise the Licensed
+ Rights, but not otherwise.
+
+ 2. Patent and trademark rights are not licensed under this
+ Public License.
+
+ 3. To the extent possible, the Licensor waives any right to
+ collect royalties from You for the exercise of the Licensed
+ Rights, whether directly or through a collecting society
+ under any voluntary or waivable statutory or compulsory
+ licensing scheme. In all other cases the Licensor expressly
+ reserves any right to collect such royalties, including when
+ the Licensed Material is used other than for NonCommercial
+ purposes.
+
+
+Section 3 -- License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the
+following conditions.
+
+ a. Attribution.
+
+ 1. If You Share the Licensed Material (including in modified
+ form), You must:
+
+ a. retain the following if it is supplied by the Licensor
+ with the Licensed Material:
+
+ i. identification of the creator(s) of the Licensed
+ Material and any others designated to receive
+ attribution, in any reasonable manner requested by
+ the Licensor (including by pseudonym if
+ designated);
+
+ ii. a copyright notice;
+
+ iii. a notice that refers to this Public License;
+
+ iv. a notice that refers to the disclaimer of
+ warranties;
+
+ v. a URI or hyperlink to the Licensed Material to the
+ extent reasonably practicable;
+
+ b. indicate if You modified the Licensed Material and
+ retain an indication of any previous modifications; and
+
+ c. indicate the Licensed Material is licensed under this
+ Public License, and include the text of, or the URI or
+ hyperlink to, this Public License.
+
+ 2. You may satisfy the conditions in Section 3(a)(1) in any
+ reasonable manner based on the medium, means, and context in
+ which You Share the Licensed Material. For example, it may be
+ reasonable to satisfy the conditions by providing a URI or
+ hyperlink to a resource that includes the required
+ information.
+ 3. If requested by the Licensor, You must remove any of the
+ information required by Section 3(a)(1)(A) to the extent
+ reasonably practicable.
+
+ b. ShareAlike.
+
+ In addition to the conditions in Section 3(a), if You Share
+ Adapted Material You produce, the following conditions also apply.
+
+ 1. The Adapter's License You apply must be a Creative Commons
+ license with the same License Elements, this version or
+ later, or a BY-NC-SA Compatible License.
+
+ 2. You must include the text of, or the URI or hyperlink to, the
+ Adapter's License You apply. You may satisfy this condition
+ in any reasonable manner based on the medium, means, and
+ context in which You Share Adapted Material.
+
+ 3. You may not offer or impose any additional or different terms
+ or conditions on, or apply any Effective Technological
+ Measures to, Adapted Material that restrict exercise of the
+ rights granted under the Adapter's License You apply.
+
+
+Section 4 -- Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that
+apply to Your use of the Licensed Material:
+
+ a. for the avoidance of doubt, Section 2(a)(1) grants You the right
+ to extract, reuse, reproduce, and Share all or a substantial
+ portion of the contents of the database for NonCommercial purposes
+ only;
+
+ b. if You include all or a substantial portion of the database
+ contents in a database in which You have Sui Generis Database
+ Rights, then the database in which You have Sui Generis Database
+ Rights (but not its individual contents) is Adapted Material,
+ including for purposes of Section 3(b); and
+
+ c. You must comply with the conditions in Section 3(a) if You Share
+ all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not
+replace Your obligations under this Public License where the Licensed
+Rights include other Copyright and Similar Rights.
+
+
+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
+
+ a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
+ EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
+ AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
+ ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
+ IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
+ WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
+ ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
+ KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
+ ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
+
+ b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
+ TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+ NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
+ INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
+ COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
+ USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
+ ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
+ DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
+ IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
+
+ c. The disclaimer of warranties and limitation of liability provided
+ above shall be interpreted in a manner that, to the extent
+ possible, most closely approximates an absolute disclaimer and
+ waiver of all liability.
+
+
+Section 6 -- Term and Termination.
+
+ a. This Public License applies for the term of the Copyright and
+ Similar Rights licensed here. However, if You fail to comply with
+ this Public License, then Your rights under this Public License
+ terminate automatically.
+
+ b. Where Your right to use the Licensed Material has terminated under
+ Section 6(a), it reinstates:
+
+ 1. automatically as of the date the violation is cured, provided
+ it is cured within 30 days of Your discovery of the
+ violation; or
+
+ 2. upon express reinstatement by the Licensor.
+
+ For the avoidance of doubt, this Section 6(b) does not affect any
+ right the Licensor may have to seek remedies for Your violations
+ of this Public License.
+
+ c. For the avoidance of doubt, the Licensor may also offer the
+ Licensed Material under separate terms or conditions or stop
+ distributing the Licensed Material at any time; however, doing so
+ will not terminate this Public License.
+
+ d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
+ License.
+
+
+Section 7 -- Other Terms and Conditions.
+
+ a. The Licensor shall not be bound by any additional or different
+ terms or conditions communicated by You unless expressly agreed.
+
+ b. Any arrangements, understandings, or agreements regarding the
+ Licensed Material not stated herein are separate from and
+ independent of the terms and conditions of this Public License.
+
+
+Section 8 -- Interpretation.
+
+ a. For the avoidance of doubt, this Public License does not, and
+ shall not be interpreted to, reduce, limit, restrict, or impose
+ conditions on any use of the Licensed Material that could lawfully
+ be made without permission under this Public License.
+
+ b. To the extent possible, if any provision of this Public License is
+ deemed unenforceable, it shall be automatically reformed to the
+ minimum extent necessary to make it enforceable. If the provision
+ cannot be reformed, it shall be severed from this Public License
+ without affecting the enforceability of the remaining terms and
+ conditions.
+
+ c. No term or condition of this Public License will be waived and no
+ failure to comply consented to unless expressly agreed to by the
+ Licensor.
+
+ d. Nothing in this Public License constitutes or may be interpreted
+ as a limitation upon, or waiver of, any privileges and immunities
+ that apply to the Licensor or You, including from the legal
+ processes of any jurisdiction or authority.
+
+=======================================================================
+
+Creative Commons is not a party to its public
+licenses. Notwithstanding, Creative Commons may elect to apply one of
+its public licenses to material it publishes and in those instances
+will be considered the “Licensor.” The text of the Creative Commons
+public licenses is dedicated to the public domain under the CC0 Public
+Domain Dedication. Except for the limited purpose of indicating that
+material is shared under a Creative Commons public license or as
+otherwise permitted by the Creative Commons policies published at
+creativecommons.org/policies, Creative Commons does not authorize the
+use of the trademark "Creative Commons" or any other trademark or logo
+of Creative Commons without its prior written consent including,
+without limitation, in connection with any unauthorized modifications
+to any of its public licenses or any other arrangements,
+understandings, or agreements concerning use of licensed material. For
+the avoidance of doubt, this paragraph does not form part of the
+public licenses.
+
+Creative Commons may be contacted at creativecommons.org.
diff --git a/program_info/README.md b/program_info/README.md
new file mode 100644
index 00000000..1e805d4a
--- /dev/null
+++ b/program_info/README.md
@@ -0,0 +1,6 @@
+# PolyMC Program Info
+
+This is PolyMC's program info which contains information about:
+- Application name and logo (and branding in general)
+- Various URLs and API endpoints
+- Desktop file
diff --git a/program_info/genicons.sh b/program_info/genicons.sh
new file mode 100755
index 00000000..b553edb5
--- /dev/null
+++ b/program_info/genicons.sh
@@ -0,0 +1,24 @@
+#/bin/bash
+
+inkscape -w 16 -h 16 -o polymc_16.png org.polymc.PolyMC.svg
+inkscape -w 24 -h 24 -o polymc_24.png org.polymc.PolyMC.svg
+inkscape -w 32 -h 32 -o polymc_32.png org.polymc.PolyMC.svg
+inkscape -w 48 -h 48 -o polymc_48.png org.polymc.PolyMC.svg
+inkscape -w 64 -h 64 -o polymc_64.png org.polymc.PolyMC.svg
+inkscape -w 128 -h 128 -o polymc_128.png org.polymc.PolyMC.svg
+
+convert polymc_128.png polymc_64.png polymc_48.png polymc_32.png polymc_24.png polymc_16.png polymc.ico
+
+inkscape -w 256 -h 256 -o polymc_256.png org.polymc.PolyMC.svg
+inkscape -w 512 -h 512 -o polymc_512.png org.polymc.PolyMC.svg
+inkscape -w 1024 -h 1024 -o polymc_1024.png org.polymc.PolyMC.svg
+
+png2icns polymc.icns polymc_1024.png polymc_512.png polymc_256.png polymc_128.png polymc_32.png polymc_16.png
+
+rm -f polymc_*.png
+rm -rf polymc.iconset
+
+for dir in ../launcher/resources/*/scalable
+do
+ cp -v org.polymc.PolyMC.svg $dir/launcher.svg
+done
diff --git a/program_info/org.polymc.PolyMC.Source.svg b/program_info/org.polymc.PolyMC.Source.svg
new file mode 100644
index 00000000..0614cde2
--- /dev/null
+++ b/program_info/org.polymc.PolyMC.Source.svg
@@ -0,0 +1,261 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ inkscape:export-ydpi="1620.0001"
+ inkscape:export-xdpi="1620.0001"
+ inkscape:export-filename="/home/z/Pictures/Polymchd.png"
+ inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
+ sodipodi:docname="org.polymc.PolyMC.Source.svg"
+ id="svg5"
+ version="1.1"
+ viewBox="0 0 16.933333 16.933333"
+ height="64"
+ width="64"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview7"
+ pagecolor="#505050"
+ bordercolor="#eeeeee"
+ borderopacity="1"
+ inkscape:pageshadow="0"
+ inkscape:pageopacity="0"
+ inkscape:pagecheckerboard="0"
+ inkscape:document-units="mm"
+ showgrid="false"
+ width="64mm"
+ inkscape:object-nodes="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:object-paths="false"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-midpoints="true"
+ units="px"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:zoom="7.2348066"
+ inkscape:cx="26.331042"
+ inkscape:cy="25.84727"
+ inkscape:window-width="1920"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1"
+ inkscape:snap-center="false"
+ inkscape:snap-page="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-text-baseline="true"
+ fit-margin-left="4"
+ fit-margin-right="4">
+ <inkscape:grid
+ type="xygrid"
+ id="grid56" />
+ <sodipodi:guide
+ position="3.5610222,0.91765532"
+ orientation="1,0"
+ id="guide1024" />
+ <sodipodi:guide
+ position="13.37231,0.91765532"
+ orientation="1,0"
+ id="guide1028" />
+ <sodipodi:guide
+ position="8.4666659,3.9526473"
+ orientation="1,0"
+ id="guide1471" />
+ <sodipodi:guide
+ position="0.52916673,10.248746"
+ orientation="-0.58778528,0.80901698"
+ id="guide1629" />
+ <sodipodi:guide
+ position="8.4666659,16.015677"
+ orientation="0.58778537,0.80901691"
+ id="guide1892" />
+ <sodipodi:guide
+ position="8.4666659,4.4818143"
+ orientation="0.58778537,0.80901691"
+ id="guide2184"
+ inkscape:label=""
+ inkscape:locked="false"
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="8.4666659,4.4818143"
+ orientation="-0.58778528,0.80901698"
+ id="guide2186"
+ inkscape:label=""
+ inkscape:locked="false"
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="0.52916788,10.248746"
+ orientation="0,-1"
+ id="guide2188" />
+ <sodipodi:guide
+ position="8.8308337,4.2172313"
+ orientation="-0.58778528,0.80901698"
+ id="guide2190"
+ inkscape:label=""
+ inkscape:locked="false"
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="8.4666655,3.9526476"
+ orientation="0.58778537,0.80901691"
+ id="guide2192"
+ inkscape:label=""
+ inkscape:locked="false"
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="9.7726935,4.3858037"
+ orientation="-0.58778528,0.80901698"
+ id="guide2194"
+ inkscape:label=""
+ inkscape:locked="false"
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="7.1606718,4.3857603"
+ orientation="0.58778537,0.80901691"
+ id="guide2196"
+ inkscape:label=""
+ inkscape:locked="false"
+ inkscape:color="rgb(0,0,255)" />
+ </sodipodi:namedview>
+ <defs
+ id="defs2">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient206240">
+ <stop
+ style="stop-color:#88b858;stop-opacity:1"
+ offset="0"
+ id="stop206236" />
+ <stop
+ style="stop-color:#72b147;stop-opacity:1"
+ offset="0.50000006"
+ id="stop134589" />
+ <stop
+ style="stop-color:#5a9a30;stop-opacity:1"
+ offset="1"
+ id="stop206238" />
+ </linearGradient>
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect28303"
+ satellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1"
+ is_visible="true"
+ lpeversion="1"
+ unit="px"
+ method="auto"
+ mode="F"
+ radius="0"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect28299"
+ satellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1"
+ is_visible="true"
+ lpeversion="1"
+ unit="px"
+ method="auto"
+ mode="F"
+ radius="0"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect28255"
+ satellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1"
+ is_visible="true"
+ lpeversion="1"
+ unit="px"
+ method="auto"
+ mode="F"
+ radius="0"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect10730"
+ satellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1"
+ is_visible="true"
+ lpeversion="1"
+ unit="px"
+ method="auto"
+ mode="F"
+ radius="0"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient206240"
+ id="linearGradient84726"
+ x1="4.4979167"
+ y1="3.8011212"
+ x2="12.435414"
+ y2="9.5680542"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ style="fill:#765338;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 3.5610223,16.015678 c 0,0 0,-3.564159 4.905644,-3.564159 4.9069307,0 4.9056437,3.564159 4.9056437,3.564159 z"
+ id="path90018"
+ sodipodi:nodetypes="cccc" />
+ <path
+ style="fill:#b7835a;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
+ d="M 8.466666,12.451519 3.5610223,16.015678 0.52916674,6.6845872 Z"
+ id="path62382" />
+ <path
+ style="fill:#5b422d;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
+ d="m 8.466666,12.451519 7.9375,-5.7669323 -3.031856,9.3310913 z"
+ id="path100033" />
+ <path
+ style="fill:#72b147;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 8.8308339,12.716102 8.4666661,12.980686 8.1024982,12.716102 c 0,-0.264583 0.3641681,-0.264583 0.3641681,-0.264583 0,0 0.3641676,0 0.3641676,0.264583 z"
+ id="path986606"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:#5a9a30;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 8.4666663,12.451519 c 0,0 -2e-7,-5.7669302 7.9374987,-5.7669302 L 16.179097,7.3772762 15.260564,8.5737969 14.342031,8.7119827 13.423498,9.9085036 12.504965,10.046689 11.586433,11.24321 10.6679,11.381396 9.7493669,12.577916 8.830834,12.716102 Z"
+ id="path986472"
+ sodipodi:nodetypes="cccccccccccc" />
+ <path
+ style="fill:#88b858;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 8.1024983,12.716102 7.1839654,12.577916 6.2654325,11.381396 5.3468996,11.24321 4.4283667,10.046689 3.5098338,9.9085034 2.5913009,8.7119828 1.6727681,8.5737971 0.75423516,7.3772764 0.52916674,6.6845872 c 7.93749966,10e-8 7.93749956,5.7669318 7.93749956,5.7669318 z"
+ id="path986404"
+ sodipodi:nodetypes="cccccccccccc" />
+ <path
+ style="fill:url(#linearGradient84726);stroke:none;stroke-width:0.26458333;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
+ d="M 0.52916674,6.6845872 8.4666663,12.451519 16.404166,6.6845867 8.4666666,0.91765555 Z"
+ id="path5260" />
+ <path
+ style="fill:#000000;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:0"
+ d="M 0.75423516,7.3772764 0.529167,6.684588 8.466666,12.451519 16.404165,6.6845883 16.179097,7.3772762 8.466666,12.980686 Z"
+ id="path970771" />
+ </g>
+</svg>
diff --git a/program_info/org.polymc.PolyMC.desktop.in b/program_info/org.polymc.PolyMC.desktop.in
new file mode 100644
index 00000000..2d9e7103
--- /dev/null
+++ b/program_info/org.polymc.PolyMC.desktop.in
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Version=1.0
+Name=PolyMC
+Comment=A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.
+Type=Application
+Terminal=false
+Exec=@Launcher_APP_BINARY_NAME@
+StartupNotify=true
+Icon=org.polymc.PolyMC
+Categories=Game;
+Keywords=game;minecraft;launcher;
+StartupWMClass=PolyMC
diff --git a/program_info/org.polymc.PolyMC.metainfo.xml.in b/program_info/org.polymc.PolyMC.metainfo.xml.in
new file mode 100644
index 00000000..03401f3d
--- /dev/null
+++ b/program_info/org.polymc.PolyMC.metainfo.xml.in
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<component type="desktop">
+ <id>org.polymc.PolyMC</id>
+ <provides>
+ <id>org.polymc.PolyMC</id>
+ </provides>
+ <launchable type="desktop-id">org.polymc.PolyMC.desktop</launchable>
+ <name>PolyMC</name>
+ <developer_name>PolyMC Team</developer_name>
+ <summary>A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once</summary>
+ <metadata_license>CC0-1.0</metadata_license>
+ <project_license>GPL-3.0-or-later</project_license>
+ <url type="homepage">https://polymc.org/</url>
+ <url type="help">https://github.com/PolyMC/PolyMC#help--support</url>
+ <description>
+ <p>PolyMC is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.</p>
+ <p>Features:</p>
+ <ul>
+ <li>Easily install game modifications, such as Fabric or Forge</li>
+ <li>Control your java settings</li>
+ <li>Manage worlds and resource packs from the launcher</li>
+ <li>See logs and other details easily</li>
+ <li>Kill Minecraft in case of a crash/freeze</li>
+ <li>Isolate minecraft instances to keep everything clean</li>
+ <li>Install mods directly from the launcher</li>
+ </ul>
+ </description>
+ <screenshots>
+ <screenshot type="default">
+ <caption>The main PolyMC window</caption>
+ <image type="source" width="1011" height="994">https://polymc.github.io/assets/img/screenshots/LauncherDark.png</image>
+ </screenshot>
+ <screenshot>
+ <caption>Modpack installation</caption>
+ <image type="source" width="911" height="682">https://polymc.github.io/assets/img/screenshots/ModpackInstallDark.png</image>
+ </screenshot>
+ <screenshot>
+ <caption>Mod installation</caption>
+ <image type="source" width="987" height="723">https://polymc.github.io/assets/img/screenshots/ModInstallDark.png</image>
+ </screenshot>
+ <screenshot>
+ <caption>Instance management</caption>
+ <image type="source" width="902" height="920">https://polymc.github.io/assets/img/screenshots/PropertiesDark.png</image>
+ </screenshot>
+ <screenshot>
+ <caption>Cat :)</caption>
+ <image type="source" width="1011" height="994">https://polymc.github.io/assets/img/screenshots/LauncherCatDark.png</image>
+ </screenshot>
+ </screenshots>
+ <releases>
+ <release version="@Launcher_RELEASE_VERSION_NAME@" date="@Launcher_RELEASE_TIMESTAMP@"></release>
+ </releases>
+ <content_rating type="oars-1.1">
+ <content_attribute id="violence-fantasy">moderate</content_attribute>
+ <content_attribute id="social-chat">intense</content_attribute>
+ </content_rating>
+</component>
diff --git a/program_info/org.polymc.PolyMC.svg b/program_info/org.polymc.PolyMC.svg
new file mode 100644
index 00000000..c192d503
--- /dev/null
+++ b/program_info/org.polymc.PolyMC.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg width="64" height="64" version="1.1" viewBox="0 0 16.933 16.933" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="linearGradient84726" x1="4.4979" x2="12.435" y1="3.8011" y2="9.5681" gradientUnits="userSpaceOnUse">
+ <stop stop-color="#88b858" offset="0"/>
+ <stop stop-color="#72b147" offset=".5"/>
+ <stop stop-color="#5a9a30" offset="1"/>
+ </linearGradient>
+ </defs>
+ <g>
+ <path d="m3.561 16.016s0-3.5642 4.9056-3.5642c4.9069 0 4.9056 3.5642 4.9056 3.5642z" fill="#765338"/>
+ <path d="m8.4667 12.452-4.9056 3.5642-3.0319-9.3311z" fill="#b7835a"/>
+ <path d="m8.4667 12.452 7.9375-5.7669-3.0319 9.3311z" fill="#5b422d"/>
+ <path d="m8.8308 12.716-0.36417 0.26458-0.36417-0.26458c0-0.26458 0.36417-0.26458 0.36417-0.26458s0.36417 0 0.36417 0.26458z" fill="#72b147"/>
+ <path d="m8.4667 12.452s-2e-7 -5.7669 7.9375-5.7669l-0.22507 0.69269-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819z" fill="#5a9a30"/>
+ <path d="m8.1025 12.716-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.22507-0.69269c7.9375 1e-7 7.9375 5.7669 7.9375 5.7669z" fill="#88b858"/>
+ <path d="m0.52917 6.6846 7.9375 5.7669 7.9375-5.7669-7.9375-5.7669z" fill="url(#linearGradient84726)"/>
+ </g>
+ <path d="m0.75424 7.3773-0.22507-0.69269 7.9375 5.7669 7.9375-5.7669-0.22507 0.69269-7.7124 5.6034z" fill-opacity="0"/>
+</svg>
diff --git a/program_info/polymc-header-black.svg b/program_info/polymc-header-black.svg
new file mode 100644
index 00000000..34cda4ab
--- /dev/null
+++ b/program_info/polymc-header-black.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg width="1424" height="512" version="1.1" viewBox="0 0 376.77 135.47" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="linearGradient84726" x1="4.4979" x2="12.435" y1="3.8011" y2="9.5681" gradientUnits="userSpaceOnUse">
+ <stop stop-color="#88b858" offset="0"/>
+ <stop stop-color="#72b147" offset=".5"/>
+ <stop stop-color="#5a9a30" offset="1"/>
+ </linearGradient>
+ </defs>
+ <g transform="matrix(6.95 0 0 6.9572 3.7759 1.0225)">
+ <g>
+ <path d="m3.561 16.016s0-3.5642 4.9056-3.5642c4.9069 0 4.9056 3.5642 4.9056 3.5642z" fill="#765338"/>
+ <path d="m8.4667 12.452-4.9056 3.5642-3.0319-9.3311z" fill="#b7835a"/>
+ <path d="m8.4667 12.452 7.9375-5.7669-3.0319 9.3311z" fill="#5b422d"/>
+ <path d="m8.8308 12.716-0.36417 0.26458-0.36417-0.26458c0-0.26458 0.36417-0.26458 0.36417-0.26458s0.36417 0 0.36417 0.26458z" fill="#72b147"/>
+ <path d="m8.4667 12.452s-2e-7 -5.7669 7.9375-5.7669l-0.22507 0.69269-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819z" fill="#5a9a30"/>
+ <path d="m8.1025 12.716-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.22507-0.69269c7.9375 1e-7 7.9375 5.7669 7.9375 5.7669z" fill="#88b858"/>
+ <path d="m0.52917 6.6846 7.9375 5.7669 7.9375-5.7669-7.9375-5.7669z" fill="url(#linearGradient84726)"/>
+ </g>
+ <path d="m0.75424 7.3773-0.22507-0.69269 7.9375 5.7669 7.9375-5.7669-0.22507 0.69269-7.7124 5.6034z" fill-opacity="0"/>
+ </g>
+ <g id="polymc-header-text" fill="black" transform="matrix(6.9306 0 0 6.9306 -702.9 -659.02)" stroke-width=".26458" aria-label="PolyMC">
+ <path d="m120.51 108.48v2.7957h-1.3074v-7.5241h2.8784q1.2609 0 1.9999 0.65629 0.74414 0.65629 0.74414 1.7363 0 1.1059-0.72864 1.7208-0.72346 0.61495-2.0309 0.61495zm0-1.049h1.571q0.69763 0 1.0645-0.32556 0.3669-0.33073 0.3669-0.95084 0-0.60978-0.37207-0.97152-0.37207-0.3669-1.0232-0.37723h-1.6071z"/>
+ <path d="m125.55 108.43q0-0.82165 0.32556-1.4779 0.32556-0.66145 0.91467-1.0128 0.58911-0.35657 1.3539-0.35657 1.1317 0 1.8345 0.72864 0.70797 0.72863 0.76481 1.9327l5e-3 0.29455q0 0.82682-0.32039 1.478-0.31523 0.65112-0.9095 1.0077-0.58911 0.35657-1.3643 0.35657-1.1834 0-1.8965-0.78548-0.70796-0.79065-0.70796-2.1032zm1.2557 0.10852q0 0.863 0.35657 1.3539 0.35656 0.48576 0.99218 0.48576t0.98702-0.49609q0.35657-0.49609 0.35657-1.4521 0-0.84749-0.36691-1.3436-0.36173-0.49609-0.98701-0.49609-0.61495 0-0.97668 0.49092-0.36174 0.48576-0.36174 1.4573z"/>
+ <path d="m133.14 111.27h-1.2557v-7.9375h1.2557z"/>
+ <path d="m136.46 109.47 1.1369-3.793h1.3384l-2.2221 6.4389q-0.5116 1.4108-1.7363 1.4108-0.27388 0-0.60461-0.093v-0.97151l0.23771 0.0155q0.47542 0 0.71314-0.1757 0.24287-0.17053 0.3824-0.57877l0.18087-0.48059-1.9637-5.5655h1.3539z"/>
+ <path d="m141.48 103.75 2.1704 5.7671 2.1652-5.7671h1.6898v7.5241h-1.3022v-2.4805l0.12919-3.3176-2.2221 5.7981h-0.93534l-2.2169-5.7929 0.12919 3.3124v2.4805h-1.3022v-7.5241z"/>
+ <path d="m154.79 108.82q-0.11369 1.2041-0.88883 1.881-0.77515 0.67179-2.0619 0.67179-0.89916 0-1.5865-0.42375-0.68212-0.42891-1.0542-1.2144t-0.38758-1.8242v-0.7028q0-1.0645 0.37724-1.8758 0.37724-0.81131 1.08-1.2506 0.70797-0.43925 1.633-0.43925 1.2454 0 2.005 0.67696 0.75965 0.67696 0.88367 1.912h-1.3022q-0.093-0.81132-0.47543-1.1679-0.37723-0.36174-1.111-0.36174-0.85265 0-1.3126 0.62529-0.45475 0.62011-0.46509 1.8242v0.66662q0 1.2196 0.43408 1.8604 0.43925 0.64078 1.2816 0.64078 0.76998 0 1.1576-0.34623t0.49093-1.1524z"/>
+ </g>
+</svg>
diff --git a/program_info/polymc-header.svg b/program_info/polymc-header.svg
new file mode 100644
index 00000000..a896787f
--- /dev/null
+++ b/program_info/polymc-header.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg width="1424" height="512" version="1.1" viewBox="0 0 376.77 135.47" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="linearGradient84726" x1="4.4979" x2="12.435" y1="3.8011" y2="9.5681" gradientUnits="userSpaceOnUse">
+ <stop stop-color="#88b858" offset="0"/>
+ <stop stop-color="#72b147" offset=".5"/>
+ <stop stop-color="#5a9a30" offset="1"/>
+ </linearGradient>
+ </defs>
+ <g transform="matrix(6.95 0 0 6.9572 3.7759 1.0225)">
+ <g>
+ <path d="m3.561 16.016s0-3.5642 4.9056-3.5642c4.9069 0 4.9056 3.5642 4.9056 3.5642z" fill="#765338"/>
+ <path d="m8.4667 12.452-4.9056 3.5642-3.0319-9.3311z" fill="#b7835a"/>
+ <path d="m8.4667 12.452 7.9375-5.7669-3.0319 9.3311z" fill="#5b422d"/>
+ <path d="m8.8308 12.716-0.36417 0.26458-0.36417-0.26458c0-0.26458 0.36417-0.26458 0.36417-0.26458s0.36417 0 0.36417 0.26458z" fill="#72b147"/>
+ <path d="m8.4667 12.452s-2e-7 -5.7669 7.9375-5.7669l-0.22507 0.69269-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819-0.91853 1.1965-0.91853 0.13819z" fill="#5a9a30"/>
+ <path d="m8.1025 12.716-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.91853-0.13819-0.91853-1.1965-0.22507-0.69269c7.9375 1e-7 7.9375 5.7669 7.9375 5.7669z" fill="#88b858"/>
+ <path d="m0.52917 6.6846 7.9375 5.7669 7.9375-5.7669-7.9375-5.7669z" fill="url(#linearGradient84726)"/>
+ </g>
+ <path d="m0.75424 7.3773-0.22507-0.69269 7.9375 5.7669 7.9375-5.7669-0.22507 0.69269-7.7124 5.6034z" fill-opacity="0"/>
+ </g>
+ <g id="polymc-header-text" fill="white" transform="matrix(6.9306 0 0 6.9306 -702.9 -659.02)" stroke-width=".26458" aria-label="PolyMC">
+ <path d="m120.51 108.48v2.7957h-1.3074v-7.5241h2.8784q1.2609 0 1.9999 0.65629 0.74414 0.65629 0.74414 1.7363 0 1.1059-0.72864 1.7208-0.72346 0.61495-2.0309 0.61495zm0-1.049h1.571q0.69763 0 1.0645-0.32556 0.3669-0.33073 0.3669-0.95084 0-0.60978-0.37207-0.97152-0.37207-0.3669-1.0232-0.37723h-1.6071z"/>
+ <path d="m125.55 108.43q0-0.82165 0.32556-1.4779 0.32556-0.66145 0.91467-1.0128 0.58911-0.35657 1.3539-0.35657 1.1317 0 1.8345 0.72864 0.70797 0.72863 0.76481 1.9327l5e-3 0.29455q0 0.82682-0.32039 1.478-0.31523 0.65112-0.9095 1.0077-0.58911 0.35657-1.3643 0.35657-1.1834 0-1.8965-0.78548-0.70796-0.79065-0.70796-2.1032zm1.2557 0.10852q0 0.863 0.35657 1.3539 0.35656 0.48576 0.99218 0.48576t0.98702-0.49609q0.35657-0.49609 0.35657-1.4521 0-0.84749-0.36691-1.3436-0.36173-0.49609-0.98701-0.49609-0.61495 0-0.97668 0.49092-0.36174 0.48576-0.36174 1.4573z"/>
+ <path d="m133.14 111.27h-1.2557v-7.9375h1.2557z"/>
+ <path d="m136.46 109.47 1.1369-3.793h1.3384l-2.2221 6.4389q-0.5116 1.4108-1.7363 1.4108-0.27388 0-0.60461-0.093v-0.97151l0.23771 0.0155q0.47542 0 0.71314-0.1757 0.24287-0.17053 0.3824-0.57877l0.18087-0.48059-1.9637-5.5655h1.3539z"/>
+ <path d="m141.48 103.75 2.1704 5.7671 2.1652-5.7671h1.6898v7.5241h-1.3022v-2.4805l0.12919-3.3176-2.2221 5.7981h-0.93534l-2.2169-5.7929 0.12919 3.3124v2.4805h-1.3022v-7.5241z"/>
+ <path d="m154.79 108.82q-0.11369 1.2041-0.88883 1.881-0.77515 0.67179-2.0619 0.67179-0.89916 0-1.5865-0.42375-0.68212-0.42891-1.0542-1.2144t-0.38758-1.8242v-0.7028q0-1.0645 0.37724-1.8758 0.37724-0.81131 1.08-1.2506 0.70797-0.43925 1.633-0.43925 1.2454 0 2.005 0.67696 0.75965 0.67696 0.88367 1.912h-1.3022q-0.093-0.81132-0.47543-1.1679-0.37723-0.36174-1.111-0.36174-0.85265 0-1.3126 0.62529-0.45475 0.62011-0.46509 1.8242v0.66662q0 1.2196 0.43408 1.8604 0.43925 0.64078 1.2816 0.64078 0.76998 0 1.1576-0.34623t0.49093-1.1524z"/>
+ </g>
+</svg>
diff --git a/program_info/polymc.icns b/program_info/polymc.icns
new file mode 100644
index 00000000..84148d1a
--- /dev/null
+++ b/program_info/polymc.icns
Binary files differ
diff --git a/program_info/polymc.ico b/program_info/polymc.ico
new file mode 100644
index 00000000..d56313f3
--- /dev/null
+++ b/program_info/polymc.ico
Binary files differ
diff --git a/notsecrets/Launcher.manifest b/program_info/polymc.manifest
index 0ee781ee..2d9eb165 100644
--- a/notsecrets/Launcher.manifest
+++ b/program_info/polymc.manifest
@@ -1,6 +1,6 @@
<?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="MultiMC.Application.5" type="win32" version="5.0.0.0" />
+ <assemblyIdentity name="PolyMC.Application.1" type="win32" version="1.0.0.0" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
@@ -26,6 +26,6 @@
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!--The ID below indicates app support for Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
- </application>
+ </application>
</compatibility>
</assembly>
diff --git a/notsecrets/logo.qrc b/program_info/polymc.qrc
index f1da6fe6..9ea695de 100644
--- a/notsecrets/logo.qrc
+++ b/program_info/polymc.qrc
@@ -1,8 +1,6 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource prefix="/">
- <file>logo.svg</file>
+ <file>org.polymc.PolyMC.svg</file>
</qresource>
</RCC>
-
-
diff --git a/notsecrets/launcher.rc b/program_info/polymc.rc
index f84104fb..011e944b 100644
--- a/notsecrets/launcher.rc
+++ b/program_info/polymc.rc
@@ -3,8 +3,8 @@
#endif
#include <windows.h>
-IDI_ICON1 ICON DISCARDABLE "Launcher.ico"
-1 RT_MANIFEST "Launcher.manifest"
+IDI_ICON1 ICON DISCARDABLE "polymc.ico"
+1 RT_MANIFEST "polymc.manifest"
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0
@@ -15,11 +15,11 @@ BEGIN
BEGIN
BLOCK "000004b0"
BEGIN
- VALUE "CompanyName", "MultiMC Contributors"
- VALUE "FileDescription", "A Minecraft Launcher"
+ VALUE "CompanyName", "MultiMC & PolyMC Contributors"
+ VALUE "FileDescription", "PolyMC"
VALUE "FileVersion", "1.0.0.0"
- VALUE "ProductName", "Launcher"
- VALUE "ProductVersion", "5"
+ VALUE "ProductName", "PolyMC"
+ VALUE "ProductVersion", "1"
END
END
BLOCK "VarFileInfo"