diff options
44 files changed, 506 insertions, 1351 deletions
diff --git a/.github/ISSUE_TEMPLATE/rfc.yml b/.github/ISSUE_TEMPLATE/rfc.yml new file mode 100644 index 00000000..664430fe --- /dev/null +++ b/.github/ISSUE_TEMPLATE/rfc.yml @@ -0,0 +1,69 @@ +# Template based on https://gitlab.archlinux.org/archlinux/rfcs/-/blob/0ba3b61e987e197f8d1901709409b8564958f78a/rfcs/0000-template.rst +name: Request for Comment (RFC) +description: Propose a larger change and start a discussion. +labels: [RFC] +body: +- type: markdown + attributes: + value: | + ### Use this form to suggest a larger change for PolyMC. +- type: textarea + attributes: + label: Goal + description: Short description, 1-2 sentences. + placeholder: Remove the cat from the launcher. + validations: + required: true +- type: textarea + attributes: + label: Motivation + description: | + Introduce the topic. If this is a not-well-known section of PolyMC, a detailed explanation of the background is recommended. + Some example points of discussion: + - What specific problems are you facing right now that you're trying to address? + - Are there any previous discussions? Link to them and summarize them (don't + - force your readers to read them though!). + - Is there any precedent set by other software? If so, link to resources. + placeholder: I don't like cats. I think many users also don't like cats. + validations: + required: true +- type: textarea + attributes: + label: Specification + description: A concrete, thorough explanation of what is being planned. + placeholder: Remove the cat button and all references to the cat from the codebase. Including resource files. + validations: + required: true +- type: textarea + attributes: + label: Drawbacks + description: Carefully consider every possible objection and issue with your proposal. This section should be updated as feedback comes in from discussion. + placeholder: Some users might like cats. + validations: + required: true +- type: textarea + attributes: + label: Unresolved Questions + description: | + Are there any portions of your proposal which need to be discussed with the community before the RFC can proceed? + Be careful here -- an RFC with a lot of remaining questions is likely to be stalled. + If your RFC is mostly unresolved questions and not too much substance, it may not be ready. + placeholder: Do a lot of users care about the cat? + validations: + required: true +- type: textarea + attributes: + label: Alternatives Considered + description: A list of alternatives, that have been considered and offer equal or similar features to the proposed change. + placeholder: Maybe the cat could be replaced with an axolotl? + validations: + required: true +- type: checkboxes + attributes: + 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 +- type: textarea + attributes: + label: You may use the editor below to elaborate further. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d2ccc59e..793b081e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,7 +1,12 @@ -name: build_portable +name: Build on: - [push, pull_request, workflow_dispatch] + workflow_call: + inputs: + build_type: + description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel) + type: string + default: Debug jobs: build: @@ -11,8 +16,13 @@ jobs: include: - os: ubuntu-20.04 + qt_version: 5.12.8 + qt_host: linux + + - os: ubuntu-20.04 qt_version: 5.15.2 qt_host: linux + app_image: true - os: windows-2022 qt_version: 5.15.2 @@ -59,6 +69,12 @@ jobs: 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: Set short version + shell: bash + run: | + ver_short=`git rev-parse --short HEAD` + echo "VERSION=$ver_short" >> $GITHUB_ENV + - name: Install OpenJDK uses: AdoptOpenJDK/install-jdk@v1 with: @@ -72,6 +88,7 @@ jobs: key: ${{ runner.os }}-${{ matrix.qt_version }}-${{ matrix.qt_arch }}-qt_cache - name: Install Qt + if: runner.os != 'Linux' || matrix.app_image == true uses: jurplel/install-qt-action@v2 with: version: ${{ matrix.qt_version }} @@ -80,18 +97,23 @@ jobs: cached: ${{ steps.cache-qt.outputs.cache-hit }} dir: "${{ github.workspace }}/Qt/" + - name: Install System Qt on Linux + if: runner.os == 'Linux' && matrix.app_image != true + run: | + sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 + - name: Install Ninja uses: urkle/action-get-ninja@v1 - - name: Download linuxdeploy family - if: runner.os == 'Linux' + - name: Download linuxdeploy family for AppImage on Linux + if: matrix.app_image == true 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' + if: matrix.app_image == true shell: bash run: | ${{ github.workspace }}/.github/scripts/prepare_JREs.sh @@ -99,12 +121,12 @@ jobs: - 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 + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -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 + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DLauncher_LAYOUT=lin-system -G Ninja - name: Build run: | @@ -115,16 +137,16 @@ jobs: run: | cmake --install ${{ env.BUILD_DIR }} - - name: Install for AppImage on Linux + - name: Install on Linux if: runner.os == 'Linux' run: | DESTDIR=${{ env.INSTALL_DIR }} cmake --install ${{ env.BUILD_DIR }} - name: Bundle AppImage - if: runner.os == 'Linux' + if: matrix.app_image == true shell: bash run: | - export OUTPUT="PolyMC-${{ github.sha }}-x86_64.AppImage" + export OUTPUT="PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage" chmod +x linuxdeploy-*.AppImage @@ -145,13 +167,13 @@ jobs: - name: Run windeployqt if: runner.os == 'Windows' run: | - windeployqt --no-translations "${{ env.INSTALL_DIR }}/polymc.exe" + windeployqt --no-translations --no-system-d3d-compiler --no-opengl-sw "${{ 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 + macdeployqt "PolyMC.app" -executable="PolyMC.app/Contents/MacOS/polymc" -always-overwrite - name: chmod binary on macOS if: runner.os == 'macOS' @@ -162,25 +184,38 @@ jobs: if: runner.os == 'macOS' run: | cd ${{ env.INSTALL_DIR }} - tar -czf ../polymc.tar.gz * + tar -czf ../PolyMC.tar.gz * + + - name: tar on Linux + if: runner.os == 'Linux' && matrix.app_image != true + run: | + cd ${{ env.INSTALL_DIR }} + tar -czf ../PolyMC.tar.gz * + + - name: Upload Linux tar.gz + if: runner.os == 'Linux' && matrix.app_image != true + uses: actions/upload-artifact@v2 + with: + name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }} + path: PolyMC.tar.gz - name: Upload AppImage for Linux - if: runner.os == 'Linux' + if: matrix.app_image == true uses: actions/upload-artifact@v2 with: - name: PolyMC-${{ github.sha }}-x86_64.AppImage - path: PolyMC-${{ github.sha }}-x86_64.AppImage + name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage + path: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-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 + name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }} 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 + name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }} + path: PolyMC.tar.gz diff --git a/.github/workflows/trigger_builds.yml b/.github/workflows/trigger_builds.yml new file mode 100644 index 00000000..1dfc728e --- /dev/null +++ b/.github/workflows/trigger_builds.yml @@ -0,0 +1,22 @@ +name: Build Application + +on: + push: + branches-ignore: + - 'stable' + pull_request: + workflow_dispatch: + +jobs: + + build_debug: + name: Build Debug + uses: ./.github/workflows/build.yml + with: + build_type: Debug + + build_release: + name: Build Release + uses: ./.github/workflows/build.yml + with: + build_type: Release diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml new file mode 100644 index 00000000..b487e731 --- /dev/null +++ b/.github/workflows/trigger_release.yml @@ -0,0 +1,99 @@ +name: Build Application and Make Release + +on: + push: + tags: + - '*' + +jobs: + + build_release: + name: Build Release + uses: ./.github/workflows/build.yml + with: + build_type: Release + + create_release: + needs: build_release + runs-on: ubuntu-latest + outputs: + upload_url: ${{ steps.create_release.outputs.upload_url }} + steps: + - name: Grab and store version + run: | + tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$") + echo "VERSION=$tag_name" >> $GITHUB_ENV + - name: Create release + id: create_release + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + name: PolyMC ${{ env.VERSION }} + draft: true + prerelease: false + + upload_release: + needs: create_release + runs-on: ubuntu-latest + steps: + + - name: Download artifacts + uses: actions/download-artifact@v2 + + - name: Grab and store version + run: | + tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$") + echo "VERSION=$tag_name" >> $GITHUB_ENV + + - name: Package artifacts properly + run: | + mv PolyMC-Linux*/PolyMC.tar.gz PolyMC-Linux-${{ env.VERSION }}.tar.gz + mv PolyMC-*.AppImage/PolyMC-*.AppImage PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage + mv PolyMC-Windows* PolyMC-Windows-${{ env.VERSION }} + mv PolyMC-macOS*/PolyMC.tar.gz PolyMC-macOS-${{ env.VERSION }}.tar.gz + + cd PolyMC-Windows-${{ env.VERSION }} + zip -r -9 ../PolyMC-Windows-${{ env.VERSION }}.zip * + cd .. + + - name: Upload Linux asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create_release.outputs.upload_url }} + asset_name: PolyMC-Linux-${{ env.VERSION }}.tar.gz + asset_path: PolyMC-Linux-${{ env.VERSION }}.tar.gz + asset_content_type: application/gzip + + - name: Upload Linux AppImage asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create_release.outputs.upload_url }} + asset_name: PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage + asset_path: PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage + asset_content_type: application/x-executable + + - name: Upload Windows asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create_release.outputs.upload_url }} + asset_name: PolyMC-Windows-${{ env.VERSION }}.zip + asset_path: PolyMC-Windows-${{ env.VERSION }}.zip + asset_content_type: application/zip + + - name: Upload macOS asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create_release.outputs.upload_url }} + asset_name: PolyMC-macOS-${{ env.VERSION }}.tar.gz + asset_path: PolyMC-macOS-${{ env.VERSION }}.tar.gz + asset_content_type: application/gzip diff --git a/CMakeLists.txt b/CMakeLists.txt index d4a27260..003aa65d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,7 +73,7 @@ set(Launcher_META_URL "https://meta.polymc.org/v1/" CACHE STRING "URL to fetch L set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application") # 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") +set(Launcher_MSA_CLIENT_ID "549033b2-1532-4d4e-ae77-1bbaa46f9d74" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application") # Bug tracker URL set(Launcher_BUG_TRACKER_URL "https://github.com/PolyMC/PolyMC/issues" CACHE STRING "URL for the bug tracker.") diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..52a9f30a --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,136 @@ +# Contributor Covenant Code of Conduct +This is a modified version of the Contributor Covenant. +See commit history to see our changes. + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling (antagonistic, inflammatory, insincere behaviour), insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement via email at +[polymc-enforcement@scrumplex.net](mailto:polymc-enforcement@scrumplex.net) (Email +address subject to change). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations + @@ -10,7 +10,8 @@ This is a **fork** of the MultiMC Launcher and not endorsed by MultiMC. The Poly <br> # Installation -- All packages (archived by version) can be found [here](https://packages.polymc.org/) ([latest](https://packages.polymc.org/latest)). + +- 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/ ## 🐧 Linux @@ -29,33 +30,27 @@ There are several AUR packages available: [](https://aur.archlinux.org/packages/polymc/) [](https://aur.archlinux.org/packages/polymc-bin/) -[](https://aur.archlinux.org/packages/polymc-git/) ```sh -# stable source package: +# source package: yay -S polymc -# stable binary package: +# binary package: yay -S polymc-bin -# latest git package: -yay -S polymc-git ``` -### <img src="https://www.vectorlogo.zone/logos/debian/debian-icon.svg" height="20" /> Debian +### <img src="https://www.vectorlogo.zone/logos/debian/debian-icon.svg" height="20" /> Debian and <img src="https://www.vectorlogo.zone/logos/ubuntu/ubuntu-icon.svg" height="20" /> Ubuntu We use [makedeb](https://docs.makedeb.org/) for our Debian packages. Several MPR packages are available: [](https://mpr.makedeb.org/packages/polymc) [](https://mpr.makedeb.org/packages/polymc-bin) -[](https://mpr.makedeb.org/packages/polymc-git) ```sh -# stable source package: +# source package: sudo tap install polymc -# stable binary package: +# 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 @@ -85,13 +80,6 @@ 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: @@ -99,7 +87,7 @@ sudo dnf install polymc 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: +You can also download a community-maintained [prebuilt 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 @@ -109,28 +97,37 @@ If you are too lazy to do all these steps, you can just download [a prebuild x86 ## <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. +MacOS has experimental development builds available [here](https://github.com/PolyMC/PolyMC/actions) ## <img src="https://www.vectorlogo.zone/logos/freebsd/freebsd-icon.svg" height="20" /> FreeBSD -For FreeBSD available are: +There are community-maintained binary packages available: - [AppBSD Image](http://glowiak.github.io/file/polymc-latest-fbsd64-appbsd) - a portable application, requires [AppBSD](https://codeberg.org/glowiak/appbsd/) to be installed. - [Gzipped binaries](http://glowiak.github.io/file/polymc-latest-fbsd64-raw) - traditional way to distribute, unpack and run. -In both cases you need X11, Qt5 and Java installed. Both files are 64bit only. +In both cases you need X11, Qt5 and Java installed. Both files are 64bit only. +You can build from source - see [BUILD.md](./BUILD.md) ## <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. +There are community-maintained binary packages available: + +- [gzipped 32-bit binaries](http://glowiak.github.io/file/polymc-latest-obsd32-raw), download, unpack and run. -You need X11, Qt5 and Java installed. +You need X11, Qt5 and Java installed. +You can build from source - see [BUILD.md](./BUILD.md) ## 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. +Portable builds are provided for AppImage on Linux, Windows, and macOS. + +For Debian and Arch, you can use these packages for the latest development versions: +[](https://aur.archlinux.org/packages/polymc-git/) +[](https://mpr.makedeb.org/packages/polymc-git) +For flatpak, you can use [flathub-beta](https://discourse.flathub.org/t/how-to-use-flathub-beta/2111) # Help & Support @@ -158,6 +155,7 @@ If you want to contribute to PolyMC you might find it useful to join our Discord 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: @@ -16,11 +16,19 @@ }; outputs = args@{ self, nixpkgs, flake-utils, libnbtplusplus, quazip, ... }: - { + let + systems = [ + "aarch64-linux" + # "aarch64-darwin" # qtbase is currently broken + "i686-linux" + "x86_64-darwin" + "x86_64-linux" + ]; + in { overlay = final: prev: { inherit (self.packages.${final.system}) polymc; }; - } // flake-utils.lib.eachDefaultSystem (system: + } // flake-utils.lib.eachSystem systems (system: let pkgs = import nixpkgs { inherit system; }; in { packages = { diff --git a/launcher/Application.cpp b/launcher/Application.cpp index a3d6216e..e33df252 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -192,27 +192,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) #endif startTime = QDateTime::currentDateTime(); -#ifdef Q_OS_LINUX - { - QFile osrelease("/proc/sys/kernel/osrelease"); - if (osrelease.open(QFile::ReadOnly | QFile::Text)) { - QTextStream in(&osrelease); - auto contents = in.readAll(); - if( - contents.contains("WSL", Qt::CaseInsensitive) || - contents.contains("Microsoft", Qt::CaseInsensitive) - ) { - showFatalErrorMessage( - "Unsupported system detected!", - "Linux-on-Windows distributions are not supported.\n\n" - "Please use the Windows binary when playing on Windows." - ); - return; - } - } - } -#endif - // Don't quit on hiding the last window this->setQuitOnLastWindowClosed(false); @@ -285,12 +264,21 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) return; } } + m_instanceIdToLaunch = args["launch"].toString(); m_serverToJoin = args["server"].toString(); m_profileToUse = args["profile"].toString(); m_liveCheck = args["alive"].toBool(); m_zipToImport = args["import"].toUrl(); + // error if --launch is missing with --server or --profile + if((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) + { + std::cerr << "--server and --profile can only be used in combination with --launch!" << std::endl; + m_status = Application::Failed; + return; + } + QString origcwdPath = QDir::currentPath(); QString binPath = applicationDirPath(); QString adjustedBy; @@ -359,20 +347,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) return; } - if(m_instanceIdToLaunch.isEmpty() && !m_serverToJoin.isEmpty()) - { - std::cerr << "--server can only be used in combination with --launch!" << std::endl; - m_status = Application::Failed; - return; - } - - if(m_instanceIdToLaunch.isEmpty() && !m_profileToUse.isEmpty()) - { - std::cerr << "--account can only be used in combination with --launch!" << std::endl; - m_status = Application::Failed; - return; - } - #if defined(Q_OS_MAC) // move user data to new location if on macOS and it still exists in Contents/MacOS QDir fi(applicationDirPath()); @@ -566,26 +540,23 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) qDebug() << "<> Paths set."; } - do // once + if(m_liveCheck) { - if(m_liveCheck) + QFile check(liveCheckFile); + if(check.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - QFile check(liveCheckFile); - if(!check.open(QIODevice::WriteOnly | QIODevice::Truncate)) - { - qWarning() << "Could not open" << liveCheckFile << "for writing!"; - break; - } auto payload = appID.toString().toUtf8(); - if(check.write(payload) != payload.size()) + if(check.write(payload) == payload.size()) { + check.close(); + } else { qWarning() << "Could not write into" << liveCheckFile << "!"; - check.remove(); - break; + check.remove(); // also closes file! } - check.close(); + } else { + qWarning() << "Could not open" << liveCheckFile << "for writing!"; } - } while(false); + } // Initialize application settings { diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index 374d4a29..1bff9e1d 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -39,6 +39,7 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s m_settings->registerSetting("lastLaunchTime", 0); m_settings->registerSetting("totalTimePlayed", 0); m_settings->registerSetting("lastTimePlayed", 0); + m_settings->registerSetting("InstanceType", "OneSix"); // Custom Commands auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false); diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 90149c3b..86c05651 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -286,13 +286,6 @@ set(MINECRAFT_SOURCES minecraft/launch/VerifyJavaInstall.cpp minecraft/launch/VerifyJavaInstall.h - minecraft/legacy/LegacyModList.h - minecraft/legacy/LegacyModList.cpp - minecraft/legacy/LegacyInstance.h - minecraft/legacy/LegacyInstance.cpp - minecraft/legacy/LegacyUpgradeTask.h - minecraft/legacy/LegacyUpgradeTask.cpp - minecraft/GradleSpecifier.h minecraft/MinecraftInstance.cpp minecraft/MinecraftInstance.h @@ -701,8 +694,6 @@ SET(LAUNCHER_SOURCES ui/pages/instance/OtherLogsPage.h ui/pages/instance/ServersPage.cpp ui/pages/instance/ServersPage.h - ui/pages/instance/LegacyUpgradePage.cpp - ui/pages/instance/LegacyUpgradePage.h ui/pages/instance/WorldListPage.cpp ui/pages/instance/WorldListPage.h @@ -884,7 +875,6 @@ qt5_wrap_ui(LAUNCHER_UI ui/pages/instance/InstanceSettingsPage.ui ui/pages/instance/VersionPage.ui ui/pages/instance/WorldListPage.ui - ui/pages/instance/LegacyUpgradePage.ui ui/pages/instance/ScreenshotsPage.ui ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui ui/pages/modplatform/atlauncher/AtlPage.ui diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp index 35adeaf9..c2bfe839 100644 --- a/launcher/InstanceCopyTask.cpp +++ b/launcher/InstanceCopyTask.cpp @@ -42,7 +42,6 @@ void InstanceCopyTask::copyFinished() } // FIXME: shouldn't this be able to report errors? auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg")); - instanceSettings->registerSetting("InstanceType", "Legacy"); InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath)); inst->setName(m_instName); diff --git a/launcher/InstanceCreationTask.cpp b/launcher/InstanceCreationTask.cpp index eafc5126..4c37bd7f 100644 --- a/launcher/InstanceCreationTask.cpp +++ b/launcher/InstanceCreationTask.cpp @@ -17,8 +17,6 @@ void InstanceCreationTask::executeTask() { auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg")); instanceSettings->suspendSave(); - instanceSettings->registerSetting("InstanceType", "Legacy"); - instanceSettings->set("InstanceType", "OneSix"); MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath); auto components = inst.getPackProfile(); components->buildingFromScratch(); diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index ec378538..6dd615c7 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -261,8 +261,6 @@ void InstanceImportTask::processFlame() QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); auto instanceSettings = std::make_shared<INISettingsObject>(configPath); - instanceSettings->registerSetting("InstanceType", "Legacy"); - instanceSettings->set("InstanceType", "OneSix"); MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath); auto mcVersion = pack.minecraft.version; // Hack to correct some 'special sauce'... @@ -422,7 +420,6 @@ void InstanceImportTask::processMultiMC() { QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); auto instanceSettings = std::make_shared<INISettingsObject>(configPath); - instanceSettings->registerSetting("InstanceType", "Legacy"); NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath); diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index ad18740b..04b86f6b 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -32,7 +32,6 @@ #include "BaseInstance.h" #include "InstanceTask.h" #include "settings/INISettingsObject.h" -#include "minecraft/legacy/LegacyInstance.h" #include "NullInstance.h" #include "minecraft/MinecraftInstance.h" #include "FileSystem.h" @@ -545,7 +544,7 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id) auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg")); InstancePtr inst; - instanceSettings->registerSetting("InstanceType", "Legacy"); + instanceSettings->registerSetting("InstanceType", "Legacy"); // intentionally Legacy. We don't support it. QString inst_type = instanceSettings->get("InstanceType").toString(); @@ -553,10 +552,6 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id) { inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot)); } - else if (inst_type == "Legacy") - { - inst.reset(new LegacyInstance(m_globalSettings, instanceSettings, instanceRoot)); - } else { inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot)); diff --git a/launcher/InstancePageProvider.h b/launcher/InstancePageProvider.h index 97eeab8c..357157d0 100644 --- a/launcher/InstancePageProvider.h +++ b/launcher/InstancePageProvider.h @@ -1,6 +1,5 @@ #pragma once #include "minecraft/MinecraftInstance.h" -#include "minecraft/legacy/LegacyInstance.h" #include <FileSystem.h> #include "ui/pages/BasePage.h" #include "ui/pages/BasePageProvider.h" @@ -14,7 +13,6 @@ #include "ui/pages/instance/ScreenshotsPage.h" #include "ui/pages/instance/InstanceSettingsPage.h" #include "ui/pages/instance/OtherLogsPage.h" -#include "ui/pages/instance/LegacyUpgradePage.h" #include "ui/pages/instance/WorldListPage.h" #include "ui/pages/instance/ServersPage.h" #include "ui/pages/instance/GameOptionsPage.h" @@ -34,31 +32,20 @@ public: QList<BasePage *> values; values.append(new LogPage(inst)); std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst); - if(onesix) - { - values.append(new VersionPage(onesix.get())); - 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")); - values.append(new ResourcePackPage(onesix.get())); - values.append(new TexturePackPage(onesix.get())); - values.append(new ShaderPackPage(onesix.get())); - values.append(new NotesPage(onesix.get())); - values.append(new WorldListPage(onesix.get(), onesix->worldList())); - values.append(new ServersPage(onesix)); - // values.append(new GameOptionsPage(onesix.get())); - values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots"))); - values.append(new InstanceSettingsPage(onesix.get())); - } - std::shared_ptr<LegacyInstance> legacy = std::dynamic_pointer_cast<LegacyInstance>(inst); - if(legacy) - { - values.append(new LegacyUpgradePage(legacy)); - values.append(new NotesPage(legacy.get())); - values.append(new WorldListPage(legacy.get(), legacy->worldList())); - values.append(new ScreenshotsPage(FS::PathCombine(legacy->gameRoot(), "screenshots"))); - } + values.append(new VersionPage(onesix.get())); + 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")); + values.append(new ResourcePackPage(onesix.get())); + values.append(new TexturePackPage(onesix.get())); + values.append(new ShaderPackPage(onesix.get())); + values.append(new NotesPage(onesix.get())); + values.append(new WorldListPage(onesix.get(), onesix->worldList())); + values.append(new ServersPage(onesix)); + // values.append(new GameOptionsPage(onesix.get())); + values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots"))); + values.append(new InstanceSettingsPage(onesix.get())); auto logMatcher = inst->getLogFileMatcher(); if(logMatcher) { diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 32fc99cb..40178b70 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -228,6 +228,18 @@ void LaunchController::login() { emitFailed(errorString); return; } + case AccountState::Disabled: { + auto errorString = tr("The launcher's client identification has changed. Please remove this account and add it again."); + QMessageBox::warning( + m_parentWidget, + tr("Client identification changed"), + errorString, + QMessageBox::StandardButton::Ok, + QMessageBox::StandardButton::Ok + ); + emitFailed(errorString); + return; + } case AccountState::Gone: { auto errorString = tr("The account no longer exists on the servers. It may have been migrated, in which case please add the new account you migrated this one to."); QMessageBox::warning( diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 7327f9d5..6db12c42 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -124,18 +124,7 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO m_settings->registerSetting("JoinServerOnLaunch", false); m_settings->registerSetting("JoinServerOnLaunchAddress", ""); - // DEPRECATED: Read what versions the user configuration thinks should be used - m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, ""); - m_settings->registerSetting("LWJGLVersion", ""); - m_settings->registerSetting("ForgeVersion", ""); - m_settings->registerSetting("LiteloaderVersion", ""); - m_components.reset(new PackProfile(this)); - m_components->setOldConfigVersion("net.minecraft", m_settings->get("IntendedVersion").toString()); - auto setting = m_settings->getSetting("LWJGLVersion"); - m_components->setOldConfigVersion("org.lwjgl", m_settings->get("LWJGLVersion").toString()); - m_components->setOldConfigVersion("net.minecraftforge", m_settings->get("ForgeVersion").toString()); - m_components->setOldConfigVersion("com.mumfrey.liteloader", m_settings->get("LiteloaderVersion").toString()); } void MinecraftInstance::saveNow() diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 59a8f133..d516e555 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -272,18 +272,6 @@ void PackProfile::save_internal() bool PackProfile::load() { auto filename = componentsFilePath(); - QFile componentsFile(filename); - - // migrate old config to new one, if needed - if(!componentsFile.exists()) - { - if(!migratePreComponentConfig()) - { - // FIXME: the user should be notified... - qCritical() << "Failed to convert old pre-component config for instance" << d->m_instance->name(); - return false; - } - } // load the new component list and swap it with the current one... ComponentContainer newComponents; @@ -369,239 +357,6 @@ void PackProfile::updateFailed(const QString& error) invalidateLaunchProfile(); } -// NOTE this is really old stuff, and only needs to be used when loading the old hardcoded component-unaware format (loadPreComponentConfig). -static void upgradeDeprecatedFiles(QString root, QString instanceName) -{ - auto versionJsonPath = FS::PathCombine(root, "version.json"); - auto customJsonPath = FS::PathCombine(root, "custom.json"); - auto mcJson = FS::PathCombine(root, "patches" , "net.minecraft.json"); - - QString sourceFile; - QString renameFile; - - // convert old crap. - if(QFile::exists(customJsonPath)) - { - sourceFile = customJsonPath; - renameFile = versionJsonPath; - } - else if(QFile::exists(versionJsonPath)) - { - sourceFile = versionJsonPath; - } - if(!sourceFile.isEmpty() && !QFile::exists(mcJson)) - { - if(!FS::ensureFilePathExists(mcJson)) - { - qWarning() << "Couldn't create patches folder for" << instanceName; - return; - } - if(!renameFile.isEmpty() && QFile::exists(renameFile)) - { - if(!QFile::rename(renameFile, renameFile + ".old")) - { - qWarning() << "Couldn't rename" << renameFile << "to" << renameFile + ".old" << "in" << instanceName; - return; - } - } - auto file = ProfileUtils::parseJsonFile(QFileInfo(sourceFile), false); - ProfileUtils::removeLwjglFromPatch(file); - file->uid = "net.minecraft"; - file->version = file->minecraftVersion; - file->name = "Minecraft"; - - Meta::Require needsLwjgl; - needsLwjgl.uid = "org.lwjgl"; - file->requires.insert(needsLwjgl); - - if(!ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), mcJson)) - { - return; - } - if(!QFile::rename(sourceFile, sourceFile + ".old")) - { - qWarning() << "Couldn't rename" << sourceFile << "to" << sourceFile + ".old" << "in" << instanceName; - return; - } - } -} - -/* - * Migrate old layout to the component based one... - * - Part of the version information is taken from `instance.cfg` (fed to this class from outside). - * - Part is taken from the old order.json file. - * - Part is loaded from loose json files in the instance's `patches` directory. - */ -bool PackProfile::migratePreComponentConfig() -{ - // upgrade the very old files from the beginnings of MultiMC 5 - upgradeDeprecatedFiles(d->m_instance->instanceRoot(), d->m_instance->name()); - - QList<ComponentPtr> components; - QSet<QString> loaded; - - auto addBuiltinPatch = [&](const QString &uid, bool asDependency, const QString & emptyVersion, const Meta::Require & req, const Meta::Require & conflict) - { - auto jsonFilePath = FS::PathCombine(d->m_instance->instanceRoot(), "patches" , uid + ".json"); - auto intendedVersion = d->getOldConfigVersion(uid); - // load up the base minecraft patch - ComponentPtr component; - if(QFile::exists(jsonFilePath)) - { - if(intendedVersion.isEmpty()) - { - intendedVersion = emptyVersion; - } - auto file = ProfileUtils::parseJsonFile(QFileInfo(jsonFilePath), false); - // fix uid - file->uid = uid; - // if version is missing, add it from the outside. - if(file->version.isEmpty()) - { - file->version = intendedVersion; - } - // if this is a dependency (LWJGL), mark it also as volatile - if(asDependency) - { - file->m_volatile = true; - } - // insert requirements if needed - if(!req.uid.isEmpty()) - { - file->requires.insert(req); - } - // insert conflicts if needed - if(!conflict.uid.isEmpty()) - { - file->conflicts.insert(conflict); - } - // FIXME: @QUALITY do not ignore return value - ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), jsonFilePath); - component = new Component(this, uid, file); - component->m_version = intendedVersion; - } - else if(!intendedVersion.isEmpty()) - { - auto metaVersion = APPLICATION->metadataIndex()->get(uid, intendedVersion); - component = new Component(this, metaVersion); - } - else - { - return; - } - component->m_dependencyOnly = asDependency; - component->m_important = !asDependency; - components.append(component); - }; - // TODO: insert depends and conflicts here if these are customized files... - Meta::Require reqLwjgl; - reqLwjgl.uid = "org.lwjgl"; - reqLwjgl.suggests = "2.9.1"; - Meta::Require conflictLwjgl3; - conflictLwjgl3.uid = "org.lwjgl3"; - Meta::Require nullReq; - addBuiltinPatch("org.lwjgl", true, "2.9.1", nullReq, conflictLwjgl3); - addBuiltinPatch("net.minecraft", false, QString(), reqLwjgl, nullReq); - - // first, collect all other file-based patches and load them - QMap<QString, ComponentPtr> loadedComponents; - QDir patchesDir(FS::PathCombine(d->m_instance->instanceRoot(),"patches")); - for (auto info : patchesDir.entryInfoList(QStringList() << "*.json", QDir::Files)) - { - // parse the file - qDebug() << "Reading" << info.fileName(); - auto file = ProfileUtils::parseJsonFile(info, true); - - // correct missing or wrong uid based on the file name - QString uid = info.completeBaseName(); - - // ignore builtins, they've been handled already - if (uid == "net.minecraft") - continue; - if (uid == "org.lwjgl") - continue; - - // handle horrible corner cases - if(uid.isEmpty()) - { - // if you have a file named '.json', make it just go away. - // FIXME: @QUALITY do not ignore return value - QFile::remove(info.absoluteFilePath()); - continue; - } - file->uid = uid; - // FIXME: @QUALITY do not ignore return value - ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), info.absoluteFilePath()); - - auto component = new Component(this, file->uid, file); - auto version = d->getOldConfigVersion(file->uid); - if(!version.isEmpty()) - { - component->m_version = version; - } - loadedComponents[file->uid] = component; - } - // try to load the other 'hardcoded' patches (forge, liteloader), if they weren't loaded from files - auto loadSpecial = [&](const QString & uid, int order) - { - auto patchVersion = d->getOldConfigVersion(uid); - if(!patchVersion.isEmpty() && !loadedComponents.contains(uid)) - { - auto patch = new Component(this, APPLICATION->metadataIndex()->get(uid, patchVersion)); - patch->setOrder(order); - loadedComponents[uid] = patch; - } - }; - loadSpecial("net.minecraftforge", 5); - loadSpecial("com.mumfrey.liteloader", 10); - - // load the old order.json file, if present - ProfileUtils::PatchOrder userOrder; - ProfileUtils::readOverrideOrders(FS::PathCombine(d->m_instance->instanceRoot(), "order.json"), userOrder); - - // now add all the patches by user sort order - for (auto uid : userOrder) - { - // ignore builtins - if (uid == "net.minecraft") - continue; - if (uid == "org.lwjgl") - continue; - // ordering has a patch that is gone? - if(!loadedComponents.contains(uid)) - { - continue; - } - components.append(loadedComponents.take(uid)); - } - - // is there anything left to sort? - this is used when there are leftover components that aren't part of the order.json - if(!loadedComponents.isEmpty()) - { - // inserting into multimap by order number as key sorts the patches and detects duplicates - QMultiMap<int, ComponentPtr> files; - auto iter = loadedComponents.begin(); - while(iter != loadedComponents.end()) - { - files.insert((*iter)->getOrder(), *iter); - iter++; - } - - // then just extract the patches and put them in the list - for (auto order : files.keys()) - { - const auto &values = files.values(order); - for(auto &value: values) - { - // TODO: put back the insertion of problem messages here, so the user knows about the id duplication - components.append(value); - } - } - } - // new we have a complete list of components... - return savePackProfile(componentsFilePath(), components); -} - // END: save/load void PackProfile::appendComponent(ComponentPtr component) @@ -1169,15 +924,6 @@ std::shared_ptr<LaunchProfile> PackProfile::getProfile() const return d->m_profile; } -void PackProfile::setOldConfigVersion(const QString& uid, const QString& version) -{ - if(version.isEmpty()) - { - return; - } - d->m_oldConfigVersions[uid] = version; -} - bool PackProfile::setComponentVersion(const QString& uid, const QString& version, bool important) { auto iter = d->componentIndex.find(uid); diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h index f30deb5a..989d1c6a 100644 --- a/launcher/minecraft/PackProfile.h +++ b/launcher/minecraft/PackProfile.h @@ -143,8 +143,6 @@ private: bool installCustomJar_internal(QString filepath); bool removeComponent_internal(ComponentPtr patch); - bool migratePreComponentConfig(); - private: /* data */ std::unique_ptr<PackProfileData> d; diff --git a/launcher/minecraft/PackProfile_p.h b/launcher/minecraft/PackProfile_p.h index fce921bb..715e0460 100644 --- a/launcher/minecraft/PackProfile_p.h +++ b/launcher/minecraft/PackProfile_p.h @@ -18,18 +18,6 @@ struct PackProfileData // the launch profile (volatile, temporary thing created on demand) std::shared_ptr<LaunchProfile> m_profile; - // version information migrated from instance.cfg file. Single use on migration! - std::map<QString, QString> m_oldConfigVersions; - QString getOldConfigVersion(const QString& uid) const - { - const auto iter = m_oldConfigVersions.find(uid); - if(iter != m_oldConfigVersions.cend()) - { - return (*iter).second; - } - return QString(); - } - // persistent list of components and related machinery ComponentContainer components; ComponentIndex componentIndex; diff --git a/launcher/minecraft/auth/AccountData.cpp b/launcher/minecraft/auth/AccountData.cpp index 9b84fe1a..f791db14 100644 --- a/launcher/minecraft/auth/AccountData.cpp +++ b/launcher/minecraft/auth/AccountData.cpp @@ -327,6 +327,10 @@ bool AccountData::resumeStateFromV3(QJsonObject data) { } if(type == AccountType::MSA) { + auto clientIDV = data.value("msa-client-id"); + if (clientIDV.isString()) { + msaClientID = clientIDV.toString(); + } // leave msaClientID empty if it doesn't exist or isn't a string msaToken = tokenFromJSONV3(data, "msa"); userToken = tokenFromJSONV3(data, "utoken"); xboxApiToken = tokenFromJSONV3(data, "xrp-main"); @@ -360,6 +364,7 @@ QJsonObject AccountData::saveState() const { } else if (type == AccountType::MSA) { output["type"] = "MSA"; + output["msa-client-id"] = msaClientID; tokenToJSONV3(output, msaToken, "msa"); tokenToJSONV3(output, userToken, "utoken"); tokenToJSONV3(output, xboxApiToken, "xrp-main"); diff --git a/launcher/minecraft/auth/AccountData.h b/launcher/minecraft/auth/AccountData.h index 606c1ad1..6749a471 100644 --- a/launcher/minecraft/auth/AccountData.h +++ b/launcher/minecraft/auth/AccountData.h @@ -47,6 +47,7 @@ enum class AccountState { Offline, Working, Online, + Disabled, Errored, Expired, Gone @@ -81,6 +82,7 @@ struct AccountData { bool legacy = false; bool canMigrateToMSA = false; + QString msaClientID; Katabasis::Token msaToken; Katabasis::Token userToken; Katabasis::Token xboxApiToken; diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index 04470e1c..e404cdda 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -291,6 +291,9 @@ QVariant AccountList::data(const QModelIndex &index, int role) const case AccountState::Expired: { return tr("Expired", "Account status"); } + case AccountState::Disabled: { + return tr("Disabled", "Account status"); + } case AccountState::Gone: { return tr("Gone", "Account status"); } diff --git a/launcher/minecraft/auth/AccountTask.cpp b/launcher/minecraft/auth/AccountTask.cpp index 98d8d94d..321b350f 100644 --- a/launcher/minecraft/auth/AccountTask.cpp +++ b/launcher/minecraft/auth/AccountTask.cpp @@ -43,6 +43,8 @@ QString AccountTask::getStateMessage() const return tr("Authentication task succeeded."); case AccountTaskState::STATE_OFFLINE: return tr("Failed to contact the authentication server."); + case AccountTaskState::STATE_DISABLED: + return tr("Client ID has changed. New session needs to be created."); case AccountTaskState::STATE_FAILED_SOFT: return tr("Encountered an error during authentication."); case AccountTaskState::STATE_FAILED_HARD: @@ -78,6 +80,12 @@ bool AccountTask::changeState(AccountTaskState newState, QString reason) emitFailed(reason); return false; } + case AccountTaskState::STATE_DISABLED: { + m_data->errorString = reason; + m_data->accountState = AccountState::Disabled; + emitFailed(reason); + return false; + } case AccountTaskState::STATE_FAILED_SOFT: { m_data->errorString = reason; m_data->accountState = AccountState::Errored; diff --git a/launcher/minecraft/auth/AccountTask.h b/launcher/minecraft/auth/AccountTask.h index dac3f1b5..c2a5d86c 100644 --- a/launcher/minecraft/auth/AccountTask.h +++ b/launcher/minecraft/auth/AccountTask.h @@ -35,6 +35,7 @@ enum class AccountTaskState STATE_CREATED, STATE_WORKING, STATE_SUCCEEDED, + STATE_DISABLED, //!< MSA Client ID has changed. Tell user to reloginn STATE_FAILED_SOFT, //!< soft failure. authentication went through partially STATE_FAILED_HARD, //!< hard failure. main tokens are invalid STATE_FAILED_GONE, //!< hard failure. main tokens are invalid, and the account no longer exists diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index ffc81ed8..a604cadf 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -176,6 +176,9 @@ void MinecraftAccount::authFailed(QString reason) { switch (m_currentTask->taskState()) { case AccountTaskState::STATE_OFFLINE: + case AccountTaskState::STATE_DISABLED: { + // NOTE: user will need to fix this themselves. + } case AccountTaskState::STATE_FAILED_SOFT: { // NOTE: this doesn't do much. There was an error of some sort. } diff --git a/launcher/minecraft/auth/steps/MSAStep.cpp b/launcher/minecraft/auth/steps/MSAStep.cpp index 779aee43..207d9373 100644 --- a/launcher/minecraft/auth/steps/MSAStep.cpp +++ b/launcher/minecraft/auth/steps/MSAStep.cpp @@ -12,9 +12,10 @@ using OAuth2 = Katabasis::DeviceFlow; using Activity = Katabasis::Activity; MSAStep::MSAStep(AccountData* data, Action action) : AuthStep(data), m_action(action) { + m_clientId = APPLICATION->getMSAClientID(); OAuth2::Options opts; opts.scope = "XboxLive.signin offline_access"; - opts.clientIdentifier = APPLICATION->getMSAClientID(); + opts.clientIdentifier = m_clientId; opts.authorizationUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode"; opts.accessTokenUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"; @@ -48,6 +49,10 @@ void MSAStep::rehydrate() { void MSAStep::perform() { switch(m_action) { case Refresh: { + if (m_data->msaClientID != m_clientId) { + emit hideVerificationUriAndCode(); + emit finished(AccountTaskState::STATE_DISABLED, tr("Microsoft user authentication failed - client identification has changed.")); + } m_oauth2->refresh(); return; } @@ -57,6 +62,7 @@ void MSAStep::perform() { m_oauth2->setExtraRequestParams(extraOpts); *m_data = AccountData(); + m_data->msaClientID = m_clientId; m_oauth2->login(); return; } diff --git a/launcher/minecraft/auth/steps/MSAStep.h b/launcher/minecraft/auth/steps/MSAStep.h index 49ba3542..301e1465 100644 --- a/launcher/minecraft/auth/steps/MSAStep.h +++ b/launcher/minecraft/auth/steps/MSAStep.h @@ -29,4 +29,5 @@ private slots: private: Katabasis::DeviceFlow *m_oauth2 = nullptr; Action m_action; + QString m_clientId; }; diff --git a/launcher/minecraft/legacy/LegacyInstance.cpp b/launcher/minecraft/legacy/LegacyInstance.cpp deleted file mode 100644 index f467ec06..00000000 --- a/launcher/minecraft/legacy/LegacyInstance.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <QFileInfo> -#include <minecraft/launch/LauncherPartLaunch.h> -#include <QDir> -#include <settings/Setting.h> - -#include "LegacyInstance.h" - -#include "minecraft/legacy/LegacyModList.h" -#include "minecraft/WorldList.h" -#include <MMCZip.h> -#include <FileSystem.h> - -LegacyInstance::LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) - : BaseInstance(globalSettings, settings, rootDir) -{ - settings->registerSetting("NeedsRebuild", true); - settings->registerSetting("ShouldUpdate", false); - settings->registerSetting("JarVersion", QString()); - settings->registerSetting("IntendedJarVersion", QString()); - /* - * custom base jar has no default. it is determined in code... see the accessor methods for - *it - * - * for instances that DO NOT have the CustomBaseJar setting (legacy instances), - * [.]minecraft/bin/mcbackup.jar is the default base jar - */ - settings->registerSetting("UseCustomBaseJar", true); - settings->registerSetting("CustomBaseJar", ""); -} - -QString LegacyInstance::mainJarToPreserve() const -{ - bool customJar = m_settings->get("UseCustomBaseJar").toBool(); - if(customJar) - { - auto base = baseJar(); - if(QFile::exists(base)) - { - return base; - } - } - auto runnable = runnableJar(); - if(QFile::exists(runnable)) - { - return runnable; - } - return QString(); -} - - -QString LegacyInstance::baseJar() const -{ - bool customJar = m_settings->get("UseCustomBaseJar").toBool(); - if (customJar) - { - return customBaseJar(); - } - else - return defaultBaseJar(); -} - -QString LegacyInstance::customBaseJar() const -{ - QString value = m_settings->get("CustomBaseJar").toString(); - if (value.isNull() || value.isEmpty()) - { - return defaultCustomBaseJar(); - } - return value; -} - -bool LegacyInstance::shouldUseCustomBaseJar() const -{ - return m_settings->get("UseCustomBaseJar").toBool(); -} - - -Task::Ptr LegacyInstance::createUpdateTask(Net::Mode) -{ - return nullptr; -} - -std::shared_ptr<LegacyModList> LegacyInstance::jarModList() const -{ - if (!jar_mod_list) - { - auto list = new LegacyModList(jarModsDir(), modListFile()); - jar_mod_list.reset(list); - } - jar_mod_list->update(); - return jar_mod_list; -} - -QString LegacyInstance::gameRoot() const -{ - QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft")); - QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft")); - - if (mcDir.exists() && !dotMCDir.exists()) - return mcDir.filePath(); - else - return dotMCDir.filePath(); -} - -QString LegacyInstance::binRoot() const -{ - return FS::PathCombine(gameRoot(), "bin"); -} - -QString LegacyInstance::modsRoot() const { - return FS::PathCombine(gameRoot(), "mods"); -} - - -QString LegacyInstance::jarModsDir() const -{ - return FS::PathCombine(instanceRoot(), "instMods"); -} - -QString LegacyInstance::libDir() const -{ - return FS::PathCombine(gameRoot(), "lib"); -} - -QString LegacyInstance::savesDir() const -{ - return FS::PathCombine(gameRoot(), "saves"); -} - -QString LegacyInstance::coreModsDir() const -{ - return FS::PathCombine(gameRoot(), "coremods"); -} - -QString LegacyInstance::resourceDir() const -{ - return FS::PathCombine(gameRoot(), "resources"); -} -QString LegacyInstance::texturePacksDir() const -{ - return FS::PathCombine(gameRoot(), "texturepacks"); -} - -QString LegacyInstance::runnableJar() const -{ - return FS::PathCombine(binRoot(), "minecraft.jar"); -} - -QString LegacyInstance::modListFile() const -{ - return FS::PathCombine(instanceRoot(), "modlist"); -} - -QString LegacyInstance::instanceConfigFolder() const -{ - return FS::PathCombine(gameRoot(), "config"); -} - -bool LegacyInstance::shouldRebuild() const -{ - return m_settings->get("NeedsRebuild").toBool(); -} - -QString LegacyInstance::currentVersionId() const -{ - return m_settings->get("JarVersion").toString(); -} - -QString LegacyInstance::intendedVersionId() const -{ - return m_settings->get("IntendedJarVersion").toString(); -} - -bool LegacyInstance::shouldUpdate() const -{ - QVariant var = settings()->get("ShouldUpdate"); - if (!var.isValid() || var.toBool() == false) - { - return intendedVersionId() != currentVersionId(); - } - return true; -} - -QString LegacyInstance::defaultBaseJar() const -{ - return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar"; -} - -QString LegacyInstance::defaultCustomBaseJar() const -{ - return FS::PathCombine(binRoot(), "mcbackup.jar"); -} - -std::shared_ptr<WorldList> LegacyInstance::worldList() const -{ - if (!m_world_list) - { - m_world_list.reset(new WorldList(savesDir())); - } - return m_world_list; -} - -QString LegacyInstance::typeName() const -{ - return tr("Legacy"); -} - -QString LegacyInstance::getStatusbarDescription() -{ - return tr("Instance from previous versions."); -} - -QStringList LegacyInstance::verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) -{ - QStringList out; - - auto alltraits = traits(); - if(alltraits.size()) - { - out << "Traits:"; - for (auto trait : alltraits) - { - out << " " + trait; - } - out << ""; - } - - QString windowParams; - if (settings()->get("LaunchMaximized").toBool()) - { - out << "Window size: max (if available)"; - } - else - { - auto width = settings()->get("MinecraftWinWidth").toInt(); - auto height = settings()->get("MinecraftWinHeight").toInt(); - out << "Window size: " + QString::number(width) + " x " + QString::number(height); - } - out << ""; - return out; -} diff --git a/launcher/minecraft/legacy/LegacyInstance.h b/launcher/minecraft/legacy/LegacyInstance.h deleted file mode 100644 index 298543f7..00000000 --- a/launcher/minecraft/legacy/LegacyInstance.h +++ /dev/null @@ -1,142 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "BaseInstance.h" -#include "launch/LaunchTask.h" - -class ModFolderModel; -class LegacyModList; -class WorldList; -class Task; -/* - * WHY: Legacy instances - from MultiMC 3 and 4 - are here only to provide a way to upgrade them to the current format. - */ -class LegacyInstance : public BaseInstance -{ - Q_OBJECT -public: - - explicit LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir); - - virtual void saveNow() override {} - - /// Path to the instance's minecraft.jar - QString runnableJar() const; - - //! Path to the instance's modlist file. - QString modListFile() const; - - ////// Directories ////// - QString libDir() const; - QString savesDir() const; - QString texturePacksDir() const; - QString jarModsDir() const; - QString coreModsDir() const; - QString resourceDir() const; - - QString instanceConfigFolder() const override; - - QString gameRoot() const override; // Path to the instance's minecraft directory. - QString modsRoot() const override; // Path to the instance's minecraft directory. - QString binRoot() const; // Path to the instance's minecraft bin directory. - - /// Get the curent base jar of this instance. By default, it's the - /// versions/$version/$version.jar - QString baseJar() const; - - /// the default base jar of this instance - QString defaultBaseJar() const; - /// the default custom base jar of this instance - QString defaultCustomBaseJar() const; - - // the main jar that we actually want to keep when migrating the instance - QString mainJarToPreserve() const; - - /*! - * Whether or not custom base jar is used - */ - bool shouldUseCustomBaseJar() const; - - /*! - * The value of the custom base jar - */ - QString customBaseJar() const; - - std::shared_ptr<LegacyModList> jarModList() const; - std::shared_ptr<WorldList> worldList() const; - - /*! - * Whether or not the instance's minecraft.jar needs to be rebuilt. - * If this is true, when the instance launches, its jar mods will be - * re-added to a fresh minecraft.jar file. - */ - bool shouldRebuild() const; - - QString currentVersionId() const; - QString intendedVersionId() const; - - QSet<QString> traits() const override - { - return {"legacy-instance", "texturepacks"}; - }; - - virtual bool shouldUpdate() const; - virtual Task::Ptr createUpdateTask(Net::Mode mode) override; - - virtual QString typeName() const override; - - bool canLaunch() const override - { - return false; - } - bool canEdit() const override - { - return true; - } - bool canExport() const override - { - return false; - } - shared_qobject_ptr<LaunchTask> createLaunchTask( - AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override - { - return nullptr; - } - IPathMatcher::Ptr getLogFileMatcher() override - { - return nullptr; - } - QString getLogFileRoot() override - { - return gameRoot(); - } - - QString getStatusbarDescription() override; - QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override; - - QProcessEnvironment createEnvironment() override - { - return QProcessEnvironment(); - } - QMap<QString, QString> getVariables() const override - { - return {}; - } -protected: - mutable std::shared_ptr<LegacyModList> jar_mod_list; - mutable std::shared_ptr<WorldList> m_world_list; -}; diff --git a/launcher/minecraft/legacy/LegacyModList.cpp b/launcher/minecraft/legacy/LegacyModList.cpp deleted file mode 100644 index e9948ab1..00000000 --- a/launcher/minecraft/legacy/LegacyModList.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "LegacyModList.h" -#include <FileSystem.h> -#include <QString> -#include <QDebug> - -LegacyModList::LegacyModList(const QString &dir, const QString &list_file) - : m_dir(dir), m_list_file(list_file) -{ - FS::ensureFolderPathExists(m_dir.absolutePath()); - m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); - m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); -} - - struct OrderItem - { - QString id; - bool enabled = false; - }; - typedef QList<OrderItem> OrderList; - -static void internalSort(QList<LegacyModList::Mod> &what) -{ - auto predicate = [](const LegacyModList::Mod &left, const LegacyModList::Mod &right) - { - return left.fileName().localeAwareCompare(right.fileName()) < 0; - }; - std::sort(what.begin(), what.end(), predicate); -} - -static OrderList readListFile(const QString &m_list_file) -{ - OrderList itemList; - if (m_list_file.isNull() || m_list_file.isEmpty()) - return itemList; - - QFile textFile(m_list_file); - if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text)) - return OrderList(); - - QTextStream textStream; - textStream.setAutoDetectUnicode(true); - textStream.setDevice(&textFile); - while (true) - { - QString line = textStream.readLine(); - if (line.isNull() || line.isEmpty()) - break; - else - { - OrderItem it; - it.enabled = !line.endsWith(".disabled"); - if (!it.enabled) - { - line.chop(9); - } - it.id = line; - itemList.append(it); - } - } - textFile.close(); - return itemList; -} - -bool LegacyModList::update() -{ - if (!m_dir.exists() || !m_dir.isReadable()) - return false; - - QList<Mod> orderedMods; - QList<Mod> newMods; - m_dir.refresh(); - auto folderContents = m_dir.entryInfoList(); - - // first, process the ordered items (if any) - OrderList listOrder = readListFile(m_list_file); - for (auto item : listOrder) - { - QFileInfo infoEnabled(m_dir.filePath(item.id)); - QFileInfo infoDisabled(m_dir.filePath(item.id + ".disabled")); - int idxEnabled = folderContents.indexOf(infoEnabled); - int idxDisabled = folderContents.indexOf(infoDisabled); - bool isEnabled; - // if both enabled and disabled versions are present, it's a special case... - if (idxEnabled >= 0 && idxDisabled >= 0) - { - // we only process the one we actually have in the order file. - // and exactly as we have it. - // THIS IS A CORNER CASE - isEnabled = item.enabled; - } - else - { - // only one is present. - // we pick the one that we found. - // we assume the mod was enabled/disabled by external means - isEnabled = idxEnabled >= 0; - } - int idx = isEnabled ? idxEnabled : idxDisabled; - QFileInfo &info = isEnabled ? infoEnabled : infoDisabled; - // if the file from the index file exists - if (idx != -1) - { - // remove from the actual folder contents list - folderContents.takeAt(idx); - // append the new mod - orderedMods.append(info); - } - } - // if there are any untracked files... append them sorted at the end - if (folderContents.size()) - { - for (auto entry : folderContents) - { - newMods.append(entry); - } - internalSort(newMods); - orderedMods.append(newMods); - } - mods.swap(orderedMods); - return true; -} diff --git a/launcher/minecraft/legacy/LegacyModList.h b/launcher/minecraft/legacy/LegacyModList.h deleted file mode 100644 index fade736e..00000000 --- a/launcher/minecraft/legacy/LegacyModList.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <QList> -#include <QString> -#include <QDir> - -class LegacyModList -{ -public: - - using Mod = QFileInfo; - - LegacyModList(const QString &dir, const QString &list_file = QString()); - - /// Reloads the mod list and returns true if the list changed. - bool update(); - - QDir dir() - { - return m_dir; - } - - const QList<Mod> & allMods() - { - return mods; - } - -protected: - QDir m_dir; - QString m_list_file; - QList<Mod> mods; -}; diff --git a/launcher/minecraft/legacy/LegacyUpgradeTask.cpp b/launcher/minecraft/legacy/LegacyUpgradeTask.cpp deleted file mode 100644 index a4ea60cd..00000000 --- a/launcher/minecraft/legacy/LegacyUpgradeTask.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include "LegacyUpgradeTask.h" -#include "settings/INISettingsObject.h" -#include "FileSystem.h" -#include "NullInstance.h" -#include "pathmatcher/RegexpMatcher.h" -#include <QtConcurrentRun> -#include "LegacyInstance.h" -#include "minecraft/MinecraftInstance.h" -#include "minecraft/PackProfile.h" -#include "LegacyModList.h" -#include "classparser.h" - -LegacyUpgradeTask::LegacyUpgradeTask(InstancePtr origInstance) -{ - m_origInstance = origInstance; -} - -void LegacyUpgradeTask::executeTask() -{ - setStatus(tr("Copying instance %1").arg(m_origInstance->name())); - - FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath); - folderCopy.followSymlinks(true); - - m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy); - connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &LegacyUpgradeTask::copyFinished); - connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &LegacyUpgradeTask::copyAborted); - m_copyFutureWatcher.setFuture(m_copyFuture); -} - -static QString decideVersion(const QString& currentVersion, const QString& intendedVersion) -{ - if(intendedVersion != currentVersion) - { - if(!intendedVersion.isEmpty()) - { - return intendedVersion; - } - else if(!currentVersion.isEmpty()) - { - return currentVersion; - } - } - else - { - if(!intendedVersion.isEmpty()) - { - return intendedVersion; - } - } - return QString(); -} - -void LegacyUpgradeTask::copyFinished() -{ - auto successful = m_copyFuture.result(); - if(!successful) - { - emitFailed(tr("Instance folder copy failed.")); - return; - } - auto legacyInst = std::dynamic_pointer_cast<LegacyInstance>(m_origInstance); - - auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg")); - instanceSettings->registerSetting("InstanceType", "Legacy"); - instanceSettings->set("InstanceType", "OneSix"); - // NOTE: this scope ensures the instance is fully saved before we emitSucceeded - { - MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath); - inst.setName(m_instName); - - QString preferredVersionNumber = decideVersion(legacyInst->currentVersionId(), legacyInst->intendedVersionId()); - if(preferredVersionNumber.isNull()) - { - // try to decide version based on the jar(s?) - preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->baseJar()); - if(preferredVersionNumber.isNull()) - { - preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->runnableJar()); - if(preferredVersionNumber.isNull()) - { - emitFailed(tr("Could not decide Minecraft version.")); - return; - } - } - } - auto components = inst.getPackProfile(); - components->buildingFromScratch(); - components->setComponentVersion("net.minecraft", preferredVersionNumber, true); - - QString jarPath = legacyInst->mainJarToPreserve(); - if(!jarPath.isNull()) - { - qDebug() << "Preserving base jar! : " << jarPath; - // FIXME: handle case when the jar is unreadable? - // TODO: check the hash, if it's the same as the upstream jar, do not do this - components->installCustomJar(jarPath); - } - - auto jarMods = legacyInst->jarModList()->allMods(); - for(auto & jarMod: jarMods) - { - QString modPath = jarMod.absoluteFilePath(); - qDebug() << "jarMod: " << modPath; - components->installJarMods({modPath}); - } - - // remove all the extra garbage we no longer need - auto removeAll = [&](const QString &root, const QStringList &things) - { - for(auto &thing : things) - { - auto removePath = FS::PathCombine(root, thing); - QFileInfo stat(removePath); - if(stat.isDir()) - { - FS::deletePath(removePath); - } - else - { - QFile::remove(removePath); - } - } - }; - QStringList rootRemovables = {"modlist", "version", "instMods"}; - QStringList mcRemovables = {"bin", "MultiMCLauncher.jar", "icon.png"}; - removeAll(inst.instanceRoot(), rootRemovables); - removeAll(inst.gameRoot(), mcRemovables); - } - emitSucceeded(); -} - -void LegacyUpgradeTask::copyAborted() -{ - emitFailed(tr("Instance folder copy has been aborted.")); - return; -} - diff --git a/launcher/minecraft/legacy/LegacyUpgradeTask.h b/launcher/minecraft/legacy/LegacyUpgradeTask.h deleted file mode 100644 index 542e17b8..00000000 --- a/launcher/minecraft/legacy/LegacyUpgradeTask.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "InstanceTask.h" -#include "net/NetJob.h" -#include <QUrl> -#include <QFuture> -#include <QFutureWatcher> -#include "settings/SettingsObject.h" -#include "BaseVersion.h" -#include "BaseInstance.h" - - -class LegacyUpgradeTask : public InstanceTask -{ - Q_OBJECT -public: - explicit LegacyUpgradeTask(InstancePtr origInstance); - -protected: - //! Entry point for tasks. - virtual void executeTask() override; - void copyFinished(); - void copyAborted(); - -private: /* data */ - InstancePtr m_origInstance; - QFuture<bool> m_copyFuture; - QFutureWatcher<bool> m_copyFutureWatcher; -}; diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 8de5fc9f..9dcb3504 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -720,8 +720,6 @@ void PackInstallTask::install() auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg"); auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath); instanceSettings->suspendSave(); - instanceSettings->registerSetting("InstanceType", "Legacy"); - instanceSettings->set("InstanceType", "OneSix"); MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath); auto components = instance.getPackProfile(); diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp index 1d300192..f655a066 100644 --- a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp @@ -122,8 +122,6 @@ void PackInstallTask::install() QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg"); auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath); instanceSettings->suspendSave(); - instanceSettings->registerSetting("InstanceType", "Legacy"); - instanceSettings->set("InstanceType", "OneSix"); MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath); auto components = instance.getPackProfile(); diff --git a/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp b/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp index 03570226..33df6fa4 100644 --- a/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp +++ b/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp @@ -181,8 +181,6 @@ void PackInstallTask::install() auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg"); auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath); instanceSettings->suspendSave(); - instanceSettings->registerSetting("InstanceType", "Legacy"); - instanceSettings->set("InstanceType", "OneSix"); MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath); auto components = instance.getPackProfile(); diff --git a/launcher/modplatform/technic/TechnicPackProcessor.cpp b/launcher/modplatform/technic/TechnicPackProcessor.cpp index c45061ac..156a295a 100644 --- a/launcher/modplatform/technic/TechnicPackProcessor.cpp +++ b/launcher/modplatform/technic/TechnicPackProcessor.cpp @@ -31,8 +31,6 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const QString minecraftPath = FS::PathCombine(stagingPath, ".minecraft"); QString configPath = FS::PathCombine(stagingPath, "instance.cfg"); auto instanceSettings = std::make_shared<INISettingsObject>(configPath); - instanceSettings->registerSetting("InstanceType", "Legacy"); - instanceSettings->set("InstanceType", "OneSix"); MinecraftInstance instance(globalSettings, instanceSettings, stagingPath); instance.setName(instName); diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 32b27afb..ad7227cc 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -588,7 +588,7 @@ public: actionExportInstance = TranslatedAction(MainWindow); actionExportInstance->setObjectName(QStringLiteral("actionExportInstance")); actionExportInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Export Instance")); - // FIXME: missing tooltip + actionExportInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Export the selected instance as a zip file.")); all_actions.append(&actionExportInstance); instanceToolBar->addAction(actionExportInstance); diff --git a/launcher/ui/pages/instance/LegacyUpgradePage.cpp b/launcher/ui/pages/instance/LegacyUpgradePage.cpp deleted file mode 100644 index cb78af02..00000000 --- a/launcher/ui/pages/instance/LegacyUpgradePage.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "LegacyUpgradePage.h" -#include "ui_LegacyUpgradePage.h" - -#include "InstanceList.h" -#include "minecraft/legacy/LegacyInstance.h" -#include "minecraft/legacy/LegacyUpgradeTask.h" -#include "Application.h" - -#include "ui/dialogs/CustomMessageBox.h" -#include "ui/dialogs/ProgressDialog.h" - -LegacyUpgradePage::LegacyUpgradePage(InstancePtr inst, QWidget *parent) - : QWidget(parent), ui(new Ui::LegacyUpgradePage), m_inst(inst) -{ - ui->setupUi(this); -} - -LegacyUpgradePage::~LegacyUpgradePage() -{ - delete ui; -} - -void LegacyUpgradePage::runModalTask(Task *task) -{ - connect(task, &Task::failed, [this](QString reason) - { - CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Warning)->show(); - }); - ProgressDialog loadDialog(this); - loadDialog.setSkipButton(true, tr("Abort")); - if(loadDialog.execWithTask(task) == QDialog::Accepted) - { - m_container->requestClose(); - } -} - -void LegacyUpgradePage::on_upgradeButton_clicked() -{ - QString newName = tr("%1 (Migrated)").arg(m_inst->name()); - auto upgradeTask = new LegacyUpgradeTask(m_inst); - upgradeTask->setName(newName); - upgradeTask->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id())); - upgradeTask->setIcon(m_inst->iconKey()); - unique_qobject_ptr<Task> task(APPLICATION->instances()->wrapInstanceTask(upgradeTask)); - runModalTask(task.get()); -} - -bool LegacyUpgradePage::shouldDisplay() const -{ - return !m_inst->isRunning(); -} diff --git a/launcher/ui/pages/instance/LegacyUpgradePage.h b/launcher/ui/pages/instance/LegacyUpgradePage.h deleted file mode 100644 index 7c51956b..00000000 --- a/launcher/ui/pages/instance/LegacyUpgradePage.h +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <QWidget> - -#include "minecraft/legacy/LegacyInstance.h" -#include "ui/pages/BasePage.h" -#include <Application.h> -#include "tasks/Task.h" - -namespace Ui -{ -class LegacyUpgradePage; -} - -class LegacyUpgradePage : public QWidget, public BasePage -{ - Q_OBJECT - -public: - explicit LegacyUpgradePage(InstancePtr inst, QWidget *parent = 0); - virtual ~LegacyUpgradePage(); - virtual QString displayName() const override - { - return tr("Upgrade"); - } - virtual QIcon icon() const override - { - return APPLICATION->getThemedIcon("checkupdate"); - } - virtual QString id() const override - { - return "upgrade"; - } - virtual QString helpPage() const override - { - return "Legacy-upgrade"; - } - virtual bool shouldDisplay() const override; - -private slots: - void on_upgradeButton_clicked(); - -private: - void runModalTask(Task *task); - -private: - Ui::LegacyUpgradePage *ui; - InstancePtr m_inst; -}; diff --git a/launcher/ui/pages/instance/LegacyUpgradePage.ui b/launcher/ui/pages/instance/LegacyUpgradePage.ui deleted file mode 100644 index b22c03e5..00000000 --- a/launcher/ui/pages/instance/LegacyUpgradePage.ui +++ /dev/null @@ -1,54 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>LegacyUpgradePage</class> - <widget class="QWidget" name="LegacyUpgradePage"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>546</width> - <height>405</height> - </rect> - </property> - <layout class="QVBoxLayout" name="verticalLayout_5"> - <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="QTextBrowser" name="textBrowser"> - <property name="html"> - <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Noto Sans'; font-size:11pt; font-weight:400; font-style:normal;"> -<h1 style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:600;">Upgrade is required</span></h1> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">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.</p> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">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.</p> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Please report any issues on our <a href="https://github.com/PolyMC/PolyMC/issues"><span style=" text-decoration: underline; color:#3584e4;">github issues page</span></a>.</p></body></html></string> - </property> - <property name="openExternalLinks"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QCommandLinkButton" name="upgradeButton"> - <property name="text"> - <string>Upgrade the instance</string> - </property> - </widget> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/packages/nix/polymc/default.nix b/packages/nix/polymc/default.nix index e65a7e34..a9610017 100644 --- a/packages/nix/polymc/default.nix +++ b/packages/nix/polymc/default.nix @@ -79,4 +79,17 @@ mkDerivation rec { --set GAME_LIBRARY_PATH ${gameLibraryPath} \ --prefix PATH : ${lib.makeBinPath [ xorg.xrandr ]} ''; + + meta = with lib; { + homepage = "https://polymc.org/"; + description = "A free, open source launcher for Minecraft"; + longDescription = '' + Allows you to have multiple, separate instances of Minecraft (each with + their own mods, texture packs, saves, etc) and helps you manage them and + their associated options with a simple interface. + ''; + platforms = platforms.unix; + license = licenses.gpl3Plus; + maintainers = with maintainers; [ starcraft66 kloenk ]; + }; } |