diff --git a/.clang-format b/.clang-format index 51ca0e1c1..114bcba50 100644 --- a/.clang-format +++ b/.clang-format @@ -1,16 +1,17 @@ --- -Language: Cpp -BasedOnStyle: Chromium +BasedOnStyle: Chromium IndentWidth: 4 -AlignConsecutiveMacros: false -AlignConsecutiveAssignments: false AllowShortIfStatementsOnASingleLine: false +ColumnLimit: 140 +--- +Language: Cpp +AlignConsecutiveMacros: None +AlignConsecutiveAssignments: None BraceWrapping: - AfterFunction: true + AfterFunction: true SplitEmptyFunction: false SplitEmptyRecord: false SplitEmptyNamespace: false BreakBeforeBraces: Custom BreakConstructorInitializers: BeforeComma -ColumnLimit: 140 Cpp11BracedListStyle: false diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..56166b207 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +# EditorConfig specs and documentation: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# C++ Code Style settings +[*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}] +cpp_generate_documentation_comments = doxygen_slash_star diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 000000000..08cfb56dd --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,32 @@ +name: Backport +on: + pull_request_target: + types: [closed, labeled] + +# WARNING: +# When extending this action, be aware that $GITHUB_TOKEN allows write access to +# the GitHub repository. This means that it should not evaluate user input in a +# way that allows code injection. + +permissions: + contents: read + +jobs: + backport: + permissions: + contents: write # for korthout/backport-action to create branch + pull-requests: write # for korthout/backport-action to create PR to backport + name: Backport Pull Request + if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name)) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Create backport PRs + uses: korthout/backport-action@v1.4.0 + with: + # Config README: https://github.com/korthout/backport-action#backport-action + pull_description: |- + Bot-based backport to `${target_branch}`, triggered by a label in #${pull_number}. + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c2966abe7..d044f4faf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,6 +24,12 @@ on: CACHIX_AUTH_TOKEN: description: Private token for authenticating against Cachix cache required: false + GPG_PRIVATE_KEY: + description: Private key for AppImage signing + required: false + GPG_PRIVATE_KEY_ID: + description: ID for the GPG_PRIVATE_KEY, to select the signing key + required: false jobs: build: @@ -68,7 +74,7 @@ jobs: qt_ver: 6 qt_host: windows qt_arch: '' - qt_version: '6.5.1' + qt_version: '6.5.2' qt_modules: 'qt5compat qtimageformats' qt_tools: '' @@ -80,7 +86,7 @@ jobs: qt_ver: 6 qt_host: windows qt_arch: 'win64_msvc2019_arm64' - qt_version: '6.5.1' + qt_version: '6.5.2' qt_modules: 'qt5compat qtimageformats' qt_tools: '' @@ -90,7 +96,7 @@ jobs: qt_ver: 6 qt_host: mac qt_arch: '' - qt_version: '6.5.0' + qt_version: '6.5.2' qt_modules: 'qt5compat qtimageformats' qt_tools: '' @@ -152,7 +158,7 @@ jobs: - name: Setup ccache if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug' - uses: hendrikmuhs/ccache-action@v1.2.9 + uses: hendrikmuhs/ccache-action@v1.2.10 with: key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }} @@ -249,6 +255,8 @@ jobs: 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" + wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage" + ${{ github.workspace }}/.github/scripts/prepare_JREs.sh sudo apt install libopengl0 @@ -264,23 +272,23 @@ jobs: - name: Configure CMake (macOS) if: runner.os == 'macOS' && matrix.qt_ver == 6 run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja - name: Configure CMake (macOS-Legacy) if: runner.os == 'macOS' && matrix.qt_ver == 5 run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja - name: Configure CMake (Windows MinGW-w64) if: runner.os == 'Windows' && matrix.msystem != '' shell: msys2 {0} run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja - name: Configure CMake (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix) if ("${{ env.CCACHE_VAR }}") { @@ -295,7 +303,7 @@ jobs: - name: Configure CMake (Linux) if: runner.os == 'Linux' run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja ## # BUILD @@ -387,8 +395,8 @@ jobs: cd ${{ env.INSTALL_DIR }} if ("${{ matrix.qt_ver }}" -eq "5") { - Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll - Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll + Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll + Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll } cd ${{ github.workspace }} @@ -425,7 +433,7 @@ jobs: run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - + Get-ChildItem ${{ env.INSTALL_PORTABLE_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_PORTABLE_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows, installer) @@ -466,11 +474,15 @@ jobs: - name: Package AppImage (Linux) if: runner.os == 'Linux' && matrix.qt_ver != 5 shell: bash + env: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} run: | cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr + mv ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.metainfo.xml ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml export "NO_APPSTREAM=1" # we have to skip appstream checking because appstream on ubuntu 20.04 is outdated - export OUTPUT="PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage" + + export OUTPUT="PrismLauncher-Linux-x86_64.AppImage" chmod +x linuxdeploy-*.AppImage @@ -481,8 +493,8 @@ jobs: cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk - cp -r /home/runner/work/PrismLauncher/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines - + cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines + cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ @@ -494,8 +506,33 @@ jobs: LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib" export LD_LIBRARY_PATH + chmod +x AppImageUpdate-x86_64.AppImage + ./AppImageUpdate-x86_64.AppImage --appimage-extract + + mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional + mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins + + cp -r squashfs-root/usr/bin/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin + cp -r squashfs-root/usr/lib/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib + cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional + cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins + + export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-x86_64.AppImage.zsync" + + if [ '${{ secrets.GPG_PRIVATE_KEY_ID }}' != '' ]; then + export SIGN=1 + export SIGN_KEY=${{ secrets.GPG_PRIVATE_KEY_ID }} + mkdir -p ~/.gnupg/ + printf "$GPG_PRIVATE_KEY" | base64 --decode > ~/.gnupg/private.key + gpg --import ~/.gnupg/private.key + else + echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY + fi + ./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.prismlauncher.PrismLauncher.svg + mv "PrismLauncher-Linux-x86_64.AppImage" "PrismLauncher-Linux-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage" + ## # UPLOAD BUILDS ## @@ -562,6 +599,13 @@ jobs: with: name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage + + - name: Upload AppImage Zsync (Linux) + if: runner.os == 'Linux' && matrix.qt_ver != 5 + uses: actions/upload-artifact@v3 + with: + name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync + path: PrismLauncher-Linux-x86_64.AppImage.zsync - name: ccache stats (Windows MinGW-w64) if: runner.os == 'Windows' && matrix.msystem != '' @@ -586,33 +630,3 @@ jobs: with: bundle: "Prism Launcher.flatpak" manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml - - nix: - runs-on: ubuntu-latest - strategy: - matrix: - package: - - prismlauncher - - prismlauncher-qt5 - steps: - - name: Clone repository - if: inputs.build_type == 'Debug' - uses: actions/checkout@v3 - with: - submodules: 'true' - - name: Install nix - if: inputs.build_type == 'Debug' - uses: cachix/install-nix-action@v22 - with: - install_url: https://nixos.org/nix/install - extra_nix_config: | - auto-optimise-store = true - experimental-features = nix-command flakes - - uses: cachix/cachix-action@v12 - if: inputs.build_type == 'Debug' - with: - name: prismlauncher - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - name: Build - if: inputs.build_type == 'Debug' - run: nix build .#${{ matrix.package }} --print-build-logs diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index f19b83986..2a46ff5e7 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -43,7 +43,8 @@ jobs: mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz - mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage + mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage + mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz @@ -78,9 +79,8 @@ jobs: - name: Create release id: create_release uses: softprops/action-gh-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: + token: ${{ secrets.GITHUB_TOKEN }} tag_name: ${{ github.ref }} name: Prism Launcher ${{ env.VERSION }} draft: true @@ -88,7 +88,8 @@ jobs: files: | PrismLauncher-Linux-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz - PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage + PrismLauncher-Linux-x86_64.AppImage + PrismLauncher-Linux-x86_64.AppImage.zsync PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip diff --git a/.github/workflows/update-flake.yml b/.github/workflows/update-flake.yml index ad22120ee..6bad57030 100644 --- a/.github/workflows/update-flake.yml +++ b/.github/workflows/update-flake.yml @@ -25,4 +25,6 @@ jobs: pr-title: "chore(nix): update lockfile" pr-labels: | Linux + packaging simple change + changelog:omit diff --git a/.gitmodules b/.gitmodules index 87703fee5..0f437d277 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "libraries/cmark"] path = libraries/cmark url = https://github.com/commonmark/cmark.git +[submodule "flatpak/shared-modules"] + path = flatpak/shared-modules + url = https://github.com/flathub/shared-modules.git diff --git a/CMakeLists.txt b/CMakeLists.txt index c238086e8..a2853cb5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,13 @@ if(MSVC) # Use /W4 as /Wall includes unnesserey warnings such as added padding to structs set(CMAKE_CXX_FLAGS "/GS /permissive- /W4 ${CMAKE_CXX_FLAGS}") + # /EHs Enables stack unwind semantics for standard C++ exceptions to ensure stackframes are unwound + # and object deconstructors are called when an exception is caught. + # without it memory leaks and a warning is printed + # /EHc tells the compiler to assume that functions declared as extern "C" never throw a C++ exception + # This appears to not always be a defualt compiler option in CMAKE + set(CMAKE_CXX_FLAGS "/EHsc ${CMAKE_CXX_FLAGS}") + # LINK accepts /SUBSYSTEM whics sets if we are a WINDOWS (gui) or a CONSOLE programs # This implicitly selects an entrypoint specific to the subsystem selected # qtmain/QtEntryPointLib provides the correct entrypoint (wWinMain) for gui programs @@ -85,38 +92,39 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0") # set CXXFLAGS for build targets set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}") -option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" on) +option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF) # If this is a Debug build turn on address sanitiser -if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND DEBUG_ADDRESS_SANITIZER) +if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") AND DEBUG_ADDRESS_SANITIZER) message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off") if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") # using clang with clang-cl front end message(STATUS "Address Sanitizer available on Clang MSVC frontend") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-") else() # AppleClang and Clang message(STATUS "Address Sanitizer available on Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") endif() elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # GCC message(STATUS "Address Sanitizer available on GCC") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") link_libraries("asan") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") message(STATUS "Address Sanitizer available on MSVC") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-") else() message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}") endif() endif() + option(ENABLE_LTO "Enable Link Time Optimization" off) if(ENABLE_LTO) @@ -178,7 +186,7 @@ set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}. set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0") # Build platform. -set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.") +set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.") # Channel list URL set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.") @@ -318,6 +326,8 @@ add_subdirectory(program_info) ####################################### Install layout ####################################### +set(Launcher_ENABLE_UPDATER NO) + if(NOT (UNIX AND APPLE)) # Install "portable.txt" if selected component is "portable" install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Portable_File}" DESTINATION "." COMPONENT portable EXCLUDE_FROM_ALL) @@ -342,9 +352,9 @@ if(UNIX AND APPLE) set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns) - set(MACOSX_BUNDLE_COPYRIGHT "© 2022 ${Launcher_Copyright_Mac}") - set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=") - set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml") + set(MACOSX_BUNDLE_COPYRIGHT "© 2022-2023 ${Launcher_Copyright_Mac}") + set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed") + set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed") set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0/Sparkle-2.1.0.tar.xz" CACHE STRING "URL to Sparkle release archive") set(MACOSX_SPARKLE_SHA256 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive") @@ -353,8 +363,12 @@ if(UNIX AND APPLE) # directories to look for dependencies set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${MACOSX_SPARKLE_DIR}) + if(NOT MACOSX_SPARKLE_UPDATE_PUBLIC_KEY STREQUAL "" AND NOT MACOSX_SPARKLE_UPDATE_FEED_URL STREQUAL "") + set(Launcher_ENABLE_UPDATER YES) + endif() + # install as bundle - set(INSTALL_BUNDLE "full") + set(INSTALL_BUNDLE "full" CACHE STRING "Use fixup_bundle to bundle dependencies") # Add the icon install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns) @@ -367,7 +381,7 @@ elseif(UNIX) set(JARS_DEST_DIR "share/${Launcher_Name}") # install as bundle with no dependencies included - set(INSTALL_BUNDLE "nodeps") + set(INSTALL_BUNDLE "nodeps" CACHE STRING "Use fixup_bundle to bundle dependencies") # Set RPATH SET(Launcher_BINARY_RPATH "$ORIGIN/") @@ -401,7 +415,7 @@ elseif(WIN32) set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) # install as bundle - set(INSTALL_BUNDLE "full") + set(INSTALL_BUNDLE "full" CACHE STRING "Use fixup_bundle to bundle dependencies") else() message(FATAL_ERROR "Platform not supported") endif() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4bca126f8..072916772 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,7 @@ In an effort to ensure that the code you contribute is actually compatible with This can be done by appending `-s` to your `git commit` call, or by manually appending the following text to your commit message: -``` +```text Signed-off-by: Author name @@ -27,7 +27,7 @@ Signed-off-by: Author name By signing off your work, you agree to the terms below: -``` +```text Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: @@ -61,3 +61,9 @@ As a bonus, you can also [cryptographically sign your commits][gh-signing-commit [gh-signing-commits]: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits [gh-vigilant-mode]: https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits + +## Backporting to Release Branches + +We use [automated backports](https://github.com/PrismLauncher/PrismLauncher/blob/develop/.github/workflows/backport.yml) to merge specific contributions from develop into `release` branches. + +This is done when pull requests are merged and have labels such as `backport release-7.x` - which should be added along with the milestone for the release. diff --git a/README.md b/README.md index 993f02f5d..641622b5c 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Feel free to create a GitHub issue if you find a bug or want to suggest a new fe - **Our Matrix space:** -[![PrismLauncher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&label=Matrix%20Space&logo=matrix&color=purple)](https://prismlauncher.org/matrix) +[![Prism Launcher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&label=Matrix%20Space&logo=matrix&color=purple)](https://prismlauncher.org/matrix) - **Our Subreddit:** @@ -50,7 +50,7 @@ Feel free to create a GitHub issue if you find a bug or want to suggest a new fe ## Translations -The translation effort for PrismLauncher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at +The translation effort for Prism Launcher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at ## Building @@ -82,14 +82,16 @@ Thanks to the awesome people over at [MacStadium](https://www.macstadium.com/), ## Forking/Redistributing/Custom builds policy -We don't care what you do with your fork/custom build as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy: +You are free to fork, redistribute and provide custom builds as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy: -- Make it clear that your fork is not PrismLauncher and is not endorsed by or affiliated with the PrismLauncher project (). -- Go through [CMakeLists.txt](CMakeLists.txt) and change PrismLauncher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled). +- Make it clear that your fork is not Prism Launcher and is not endorsed by or affiliated with the Prism Launcher project (). +- Go through [CMakeLists.txt](CMakeLists.txt) and change Prism Launcher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled). If you have any questions or want any clarification on the above conditions please make an issue and ask us. -Be aware that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions: +If you are just building Prism Launcher for your distribution, please make sure to set the `Launcher_BUILD_PLATFORM` to a slug representing your distribution. Examples are `archlinux`, `fedora` and `nixpkgs`. + +Note that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions: - [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use) - [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions) diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index 8a412b7ff..d7662a7a4 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -65,7 +65,7 @@ Config::Config() MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@"; MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@"; - if (BUILD_PLATFORM == "macOS" && !MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty()) + if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty()) { UPDATER_ENABLED = true; } diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index 8543d7241..387f494f3 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -36,8 +36,8 @@ */ #pragma once -#include #include +#include /** * \brief The Config class holds all the build-time information passed from the build system. @@ -68,7 +68,7 @@ class Config { bool UPDATER_ENABLED = false; - /// A short string identifying this build's platform. For example, "lin64" or "win32". + /// A short string identifying this build's platform or distribution. QString BUILD_PLATFORM; /// A string containing the build timestamp @@ -145,7 +145,7 @@ class Config { QString AUTH_BASE = "https://authserver.mojang.com/"; QString IMGUR_BASE_URL = "https://api.imgur.com/3/"; QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists - QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists + QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/"; @@ -162,7 +162,7 @@ class Config { QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2"; QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2"; - QStringList MODRINTH_MRPACK_HOSTS{"cdn.modrinth.com", "github.com", "raw.githubusercontent.com", "gitlab.com"}; + QStringList MODRINTH_MRPACK_HOSTS{ "cdn.modrinth.com", "github.com", "raw.githubusercontent.com", "gitlab.com" }; QString FLAME_BASE_URL = "https://api.curseforge.com/v1"; diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake new file mode 100644 index 000000000..635e54289 --- /dev/null +++ b/cmake/CompilerWarnings.cmake @@ -0,0 +1,152 @@ +# +# Function to set compiler warnings with reasonable defaults at the project level. +# Taken from https://github.com/aminya/project_options/blob/main/src/CompilerWarnings.cmake +# under the folowing license: +# +# MIT License +# +# Copyright (c) 2022-2100 Amin Yahyaabadi +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +include_guard() + +function(_set_project_warnings_add_target_link_option TARGET OPTIONS) + target_link_options(${_project_name} INTERFACE ${OPTIONS}) +endfunction() + +# Set the compiler warnings +# +# https://clang.llvm.org/docs/DiagnosticsReference.html +# https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md +function( + set_project_warnings + _project_name + MSVC_WARNINGS + CLANG_WARNINGS + GCC_WARNINGS +) + if("${MSVC_WARNINGS}" STREQUAL "") + set(MSVC_WARNINGS + /W4 # Baseline reasonable warnings + /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data + /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /w14263 # 'function': member function does not override any base class virtual member function + /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not + # be destructed correctly + /w14287 # 'operator': unsigned/negative constant mismatch + /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside + # the for-loop scope + /w14296 # 'operator': expression is always 'boolean_value' + /w14311 # 'variable': pointer truncation from 'type1' to 'type2' + /w14545 # expression before comma evaluates to a function which is missing an argument list + /w14546 # function call before comma missing argument list + /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect + /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'? + /w14555 # expression has no effect; expected expression with side- effect + /w14619 # pragma warning: there is no warning number 'number' + /w14640 # Enable warning on thread un-safe static member initialization + /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior. + /w14905 # wide string literal cast to 'LPSTR' + /w14906 # string literal cast to 'LPWSTR' + /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied + /permissive- # standards conformance mode for MSVC compiler. + ) + endif() + + if("${CLANG_WARNINGS}" STREQUAL "") + set(CLANG_WARNINGS + -Wall + -Wextra # reasonable and standard + -Wextra-semi # Warn about semicolon after in-class function definition. + -Wshadow # warn the user if a variable declaration shadows one from a parent context + -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps + # catch hard to track down memory errors + -Wold-style-cast # warn for c-style casts + -Wcast-align # warn for potential performance problem casts + -Wunused # warn on anything being unused + -Woverloaded-virtual # warn if you overload (not override) a virtual function + -Wpedantic # warn if non-standard C++ is used + -Wconversion # warn on type conversions that may lose data + -Wsign-conversion # warn on sign conversions + -Wnull-dereference # warn if a null dereference is detected + -Wdouble-promotion # warn if float is implicit promoted to double + -Wformat=2 # warn on security issues around functions that format output (ie printf) + -Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation + ) + endif() + + if("${GCC_WARNINGS}" STREQUAL "") + set(GCC_WARNINGS + ${CLANG_WARNINGS} + -Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist + -Wduplicated-cond # warn if if / else chain has duplicated conditions + -Wduplicated-branches # warn if if / else branches have duplicated code + -Wlogical-op # warn about logical operations being used where bitwise were probably wanted + -Wuseless-cast # warn if you perform a cast to the same type + ) + endif() + + if(MSVC) + set(PROJECT_WARNINGS_CXX ${MSVC_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(PROJECT_WARNINGS_CXX ${CLANG_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(PROJECT_WARNINGS_CXX ${GCC_WARNINGS}) + else() + message(AUTHOR_WARNING "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'") + # TODO support Intel compiler + endif() + + # Add C warnings + set(PROJECT_WARNINGS_C "${PROJECT_WARNINGS_CXX}") + list( + REMOVE_ITEM + PROJECT_WARNINGS_C + -Wnon-virtual-dtor + -Wold-style-cast + -Woverloaded-virtual + -Wuseless-cast + -Wextra-semi + ) + + target_compile_options( + ${_project_name} + INTERFACE # C++ warnings + $<$:${PROJECT_WARNINGS_CXX}> + # C warnings + $<$:${PROJECT_WARNINGS_C}> + ) + + # If we are using the compiler as a linker driver pass the warnings to it + # (most useful when using LTO or warnings as errors) + if(CMAKE_CXX_LINK_EXECUTABLE MATCHES "^") + _set_project_warnings_add_target_link_option( + ${_project_name} "$<$:${PROJECT_WARNINGS_CXX}>" + ) + endif() + + if(CMAKE_C_LINK_EXECUTABLE MATCHES "^") + _set_project_warnings_add_target_link_option( + ${_project_name} "$<$:${PROJECT_WARNINGS_C}>" + ) + endif() + + endfunction() diff --git a/cmake/MacOSXBundleInfo.plist.in b/cmake/MacOSXBundleInfo.plist.in index 400e482fe..d36ac3e8f 100644 --- a/cmake/MacOSXBundleInfo.plist.in +++ b/cmake/MacOSXBundleInfo.plist.in @@ -67,5 +67,16 @@ Alternate + CFBundleURLTypes + + + CFBundleURLName + Curseforge + CFBundleURLSchemes + + curseforge + + + diff --git a/flake.lock b/flake.lock index 91a67f087..bfe151758 100644 --- a/flake.lock +++ b/flake.lock @@ -21,11 +21,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1688254665, - "narHash": "sha256-8FHEgBrr7gYNiS/NzCxIO3m4hvtLRW9YY1nYo1ivm3o=", + "lastModified": 1690933134, + "narHash": "sha256-ab989mN63fQZBFrkk4Q8bYxQCktuHmBIBqUG1jl6/FQ=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "267149c58a14d15f7f81b4d737308421de9d7152", + "rev": "59cf3f1447cfc75087e7273b04b31e689a8599fb", "type": "github" }, "original": { @@ -76,11 +76,11 @@ "libnbtplusplus": { "flake": false, "locked": { - "lastModified": 1650031308, - "narHash": "sha256-TvVOjkUobYJD9itQYueELJX3wmecvEdCbJ0FinW2mL4=", + "lastModified": 1690036783, + "narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=", "owner": "PrismLauncher", "repo": "libnbtplusplus", - "rev": "2203af7eeb48c45398139b583615134efd8d407f", + "rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f", "type": "github" }, "original": { @@ -91,11 +91,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1688221086, - "narHash": "sha256-cdW6qUL71cNWhHCpMPOJjlw0wzSRP0pVlRn2vqX/VVg=", + "lastModified": 1691853136, + "narHash": "sha256-wTzDsRV4HN8A2Sl0SVQY0q8ILs90CD43Ha//7gNZE+E=", "owner": "nixos", "repo": "nixpkgs", - "rev": "cd99c2b3c9f160cd004318e0697f90bbd5960825", + "rev": "f0451844bbdf545f696f029d1448de4906c7f753", "type": "github" }, "original": { @@ -108,11 +108,11 @@ "nixpkgs-lib": { "locked": { "dir": "lib", - "lastModified": 1688049487, - "narHash": "sha256-100g4iaKC9MalDjUW9iN6Jl/OocTDtXdeAj7pEGIRh4=", + "lastModified": 1690881714, + "narHash": "sha256-h/nXluEqdiQHs1oSgkOOWF+j8gcJMWhwnZ9PFabN6q0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "4bc72cae107788bf3f24f30db2e2f685c9298dc9", + "rev": "9e1960bc196baf6881340d53dccb203a951745a2", "type": "github" }, "original": { @@ -138,11 +138,11 @@ ] }, "locked": { - "lastModified": 1688386108, - "narHash": "sha256-Vffto9QaVonzYAcPlAzd0soqWYpPpKk60dfNLSIXcFA=", + "lastModified": 1691747570, + "narHash": "sha256-J3fnIwJtHVQ0tK2JMBv4oAmII+1mCdXdpeCxtIsrL2A=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "42587d3414d1747999a5f71e92a83cf6547b62da", + "rev": "c5ac3aa3324bd8aebe8622a3fc92eeb3975d317a", "type": "github" }, "original": { diff --git a/flatpak/libdecor.json b/flatpak/libdecor.json new file mode 100644 index 000000000..589310a35 --- /dev/null +++ b/flatpak/libdecor.json @@ -0,0 +1,22 @@ +{ + "name": "libdecor", + "buildsystem": "meson", + "config-opts": [ + "-Ddemo=false" + ], + "sources": [ + { + "type": "git", + "url": "https://gitlab.freedesktop.org/libdecor/libdecor.git", + "commit": "73260393a97291c887e1074ab7f318e031be0ac6" + }, + { + "type": "patch", + "path": "patches/weird_libdecor.patch" + } + ], + "cleanup": [ + "/include", + "/lib/pkgconfig" + ] +} diff --git a/flatpak/org.prismlauncher.PrismLauncher.yml b/flatpak/org.prismlauncher.PrismLauncher.yml index 0524946f0..46b6da36a 100644 --- a/flatpak/org.prismlauncher.PrismLauncher.yml +++ b/flatpak/org.prismlauncher.PrismLauncher.yml @@ -5,13 +5,6 @@ sdk: org.kde.Sdk sdk-extensions: - org.freedesktop.Sdk.Extension.openjdk17 - org.freedesktop.Sdk.Extension.openjdk8 -add-extensions: - com.valvesoftware.Steam.Utility.gamescope: - version: stable - add-ld-path: lib - no-autodownload: true - autodelete: false - directory: utils/gamescope command: prismlauncher finish-args: @@ -25,22 +18,34 @@ finish-args: - --filesystem=xdg-run/app/com.discordapp.Discord:create # Mod drag&drop - --filesystem=xdg-download:ro + # FTBApp import + - --filesystem=~/.ftba:ro + +cleanup: + - /lib/libGLU* modules: + # Might be needed by some Controller mods (see https://github.com/isXander/Controlify/issues/31) + - shared-modules/libusb/libusb.json + + # Needed for proper Wayland support + - libdecor.json + - name: prismlauncher buildsystem: cmake-ninja + builddir: true config-opts: - -DLauncher_BUILD_PLATFORM=flatpak - - -DCMAKE_BUILD_TYPE=Debug + - -DCMAKE_BUILD_TYPE=RelWithDebInfo - -DLauncher_QT_VERSION_MAJOR=5 build-options: env: JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17 JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac sources: - - type: dir - path: ../ - builddir: true + - type: dir + path: ../ + - name: openjdk buildsystem: simple build-commands: @@ -49,14 +54,45 @@ modules: - mv /app/jre /app/jdk/17 - /usr/lib/sdk/openjdk8/install.sh - mv /app/jre /app/jdk/8 - cleanup: [/jre] + cleanup: + - /jre + + - name: glfw + buildsystem: cmake-ninja + config-opts: + - -DCMAKE_BUILD_TYPE=RelWithDebInfo + - -DBUILD_SHARED_LIBS:BOOL=ON + - -DGLFW_USE_WAYLAND=ON + sources: + - type: git + url: https://github.com/glfw/glfw.git + commit: 3fa2360720eeba1964df3c0ecf4b5df8648a8e52 + - type: patch + path: patches/0003-Don-t-crash-on-calls-to-focus-or-icon.patch + - type: patch + path: patches/0005-Add-warning-about-being-an-unofficial-patch.patch + - type: patch + path: patches/0007-Platform-Prefer-Wayland-over-X11.patch + cleanup: + - /include + - /lib/cmake + - /lib/pkgconfig + - name: xrandr buildsystem: autotools sources: - type: archive - url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.1.tar.xz - sha256: 7bc76daf9d72f8aff885efad04ce06b90488a1a169d118dea8a2b661832e8762 - cleanup: [/share/man, /bin/xkeystone] + url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.2.tar.xz + sha256: c8bee4790d9058bacc4b6246456c58021db58a87ddda1a9d0139bf5f18f1f240 + x-checker-data: + type: anitya + project-id: 14957 + stable-only: true + url-template: https://xorg.freedesktop.org/archive/individual/app/xrandr-$version.tar.xz + cleanup: + - /share/man + - /bin/xkeystone + - name: gamemode buildsystem: meson config-opts: @@ -67,19 +103,56 @@ modules: # post-install is running inside the build dir, we need it from the source though - install -Dm755 ../data/gamemoderun -t /app/bin sources: - - type: git - url: https://github.com/FeralInteractive/gamemode - tag: "1.7" - commit: 4dc99dff76218718763a6b07fc1900fa6d1dafd9 + - type: archive + archive-type: tar-gzip + url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.7 + sha256: 57ce73ba605d1cf12f8d13725006a895182308d93eba0f69f285648449641803 + x-checker-data: + type: json + url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest + version-query: .tag_name + url-query: .tarball_url + timestamp-query: .published_at + cleanup: + - /include + - /lib/pkgconfig + - /lib/libgamemodeauto.a + + - name: glxinfo + buildsystem: meson + config-opts: + - --bindir=/app/mesa-demos + - -Degl=disabled + - -Dglut=disabled + - -Dosmesa=disabled + - -Dvulkan=disabled + - -Dwayland=disabled + post-install: + - mv -v /app/mesa-demos/glxinfo /app/bin + sources: + - type: archive + url: https://archive.mesa3d.org/demos/mesa-demos-9.0.0.tar.xz + sha256: 3046a3d26a7b051af7ebdd257a5f23bfeb160cad6ed952329cdff1e9f1ed496b + x-checker-data: + type: anitya + project-id: 16781 + stable-only: true + url-template: https://archive.mesa3d.org/demos/mesa-demos-$version.tar.xz + cleanup: + - /include + - /mesa-demos + - /share + modules: + - shared-modules/glu/glu-9.json + - name: enhance buildsystem: simple build-commands: - - mkdir -p /app/utils/gamescope - install -Dm755 prime-run /app/bin/prime-run - mv /app/bin/prismlauncher /app/bin/prismrun - install -Dm755 prismlauncher /app/bin/prismlauncher sources: - type: file - path: ../flatpak/prime-run + path: prime-run - type: file - path: ../flatpak/prismlauncher + path: prismlauncher diff --git a/flatpak/patches/0003-Don-t-crash-on-calls-to-focus-or-icon.patch b/flatpak/patches/0003-Don-t-crash-on-calls-to-focus-or-icon.patch new file mode 100644 index 000000000..9130e856c --- /dev/null +++ b/flatpak/patches/0003-Don-t-crash-on-calls-to-focus-or-icon.patch @@ -0,0 +1,24 @@ +diff --git a/src/wl_window.c b/src/wl_window.c +index 52d3b9eb..4ac4eb5d 100644 +--- a/src/wl_window.c ++++ b/src/wl_window.c +@@ -2117,8 +2117,7 @@ void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title) + void _glfwSetWindowIconWayland(_GLFWwindow* window, + int count, const GLFWimage* images) + { +- _glfwInputError(GLFW_FEATURE_UNAVAILABLE, +- "Wayland: The platform does not support setting the window icon"); ++ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the window icon\n"); + } + + void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos) +@@ -2361,8 +2360,7 @@ void _glfwRequestWindowAttentionWayland(_GLFWwindow* window) + + void _glfwFocusWindowWayland(_GLFWwindow* window) + { +- _glfwInputError(GLFW_FEATURE_UNAVAILABLE, +- "Wayland: The platform does not support setting the input focus"); ++ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the input focus\n"); + } + + void _glfwSetWindowMonitorWayland(_GLFWwindow* window, diff --git a/flatpak/patches/0005-Add-warning-about-being-an-unofficial-patch.patch b/flatpak/patches/0005-Add-warning-about-being-an-unofficial-patch.patch new file mode 100644 index 000000000..b031d739f --- /dev/null +++ b/flatpak/patches/0005-Add-warning-about-being-an-unofficial-patch.patch @@ -0,0 +1,17 @@ +diff --git a/src/init.c b/src/init.c +index 06dbb3f2..a7c6da86 100644 +--- a/src/init.c ++++ b/src/init.c +@@ -449,6 +449,12 @@ GLFWAPI int glfwInit(void) + _glfw.initialized = GLFW_TRUE; + + glfwDefaultWindowHints(); ++ ++ fprintf(stderr, "!!! Patched GLFW from https://github.com/Admicos/minecraft-wayland\n" ++ "!!! If any issues with the window, or some issues with rendering, occur, " ++ "first try with the built-in GLFW, and if that solves the issue, report there first.\n" ++ "!!! Use outside Minecraft is untested, and things might break.\n"); ++ + return GLFW_TRUE; + } + diff --git a/flatpak/patches/0007-Platform-Prefer-Wayland-over-X11.patch b/flatpak/patches/0007-Platform-Prefer-Wayland-over-X11.patch new file mode 100644 index 000000000..4eeb81309 --- /dev/null +++ b/flatpak/patches/0007-Platform-Prefer-Wayland-over-X11.patch @@ -0,0 +1,20 @@ +diff --git a/src/platform.c b/src/platform.c +index c5966ae7..3e7442f9 100644 +--- a/src/platform.c ++++ b/src/platform.c +@@ -49,12 +49,12 @@ static const struct + #if defined(_GLFW_COCOA) + { GLFW_PLATFORM_COCOA, _glfwConnectCocoa }, + #endif +-#if defined(_GLFW_X11) +- { GLFW_PLATFORM_X11, _glfwConnectX11 }, +-#endif + #if defined(_GLFW_WAYLAND) + { GLFW_PLATFORM_WAYLAND, _glfwConnectWayland }, + #endif ++#if defined(_GLFW_X11) ++ { GLFW_PLATFORM_X11, _glfwConnectX11 }, ++#endif + }; + + GLFWbool _glfwSelectPlatform(int desiredID, _GLFWplatform* platform) diff --git a/flatpak/patches/weird_libdecor.patch b/flatpak/patches/weird_libdecor.patch new file mode 100644 index 000000000..3a400b820 --- /dev/null +++ b/flatpak/patches/weird_libdecor.patch @@ -0,0 +1,40 @@ +diff --git a/src/libdecor.c b/src/libdecor.c +index a9c1106..1aa38b3 100644 +--- a/src/libdecor.c ++++ b/src/libdecor.c +@@ -1391,22 +1391,32 @@ calculate_priority(const struct libdecor_plugin_description *plugin_description) + static bool + check_symbol_conflicts(const struct libdecor_plugin_description *plugin_description) + { ++ bool ret = true; + char * const *symbol; ++ void* main_prog = dlopen(NULL, RTLD_LAZY); ++ if (!main_prog) { ++ fprintf(stderr, "Plugin \"%s\" couldn't check conflicting symbols: \"%s\".\n", ++ plugin_description->description, dlerror()); ++ return false; ++ } ++ + + symbol = plugin_description->conflicting_symbols; + while (*symbol) { + dlerror(); +- dlsym (RTLD_DEFAULT, *symbol); ++ dlsym (main_prog, *symbol); + if (!dlerror()) { + fprintf(stderr, "Plugin \"%s\" uses conflicting symbol \"%s\".\n", + plugin_description->description, *symbol); +- return false; ++ ret = false; ++ break; + } + + symbol++; + } + +- return true; ++ dlclose(main_prog); ++ return ret; + } + + static struct plugin_loader * diff --git a/flatpak/prismlauncher b/flatpak/prismlauncher index bb8767113..039d890d2 100644 --- a/flatpak/prismlauncher +++ b/flatpak/prismlauncher @@ -5,7 +5,7 @@ for i in {0..9}; do test -S "$XDG_RUNTIME_DIR"/discord-ipc-"$i" || ln -sf {app/com.discordapp.Discord,"$XDG_RUNTIME_DIR"}/discord-ipc-"$i"; done -export PATH="${PATH}${PATH:+:}/app/utils/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin" -export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}${LD_LIBRARY_PATH:+:}/usr/lib/extensions/vulkan/MangoHud/\$LIB/" +export PATH="${PATH}${PATH:+:}/usr/lib/extensions/vulkan/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin" +export VK_LAYER_PATH="/usr/lib/extensions/vulkan/share/vulkan/implicit_layer.d/" exec /app/bin/prismrun "$@" diff --git a/flatpak/shared-modules b/flatpak/shared-modules new file mode 160000 index 000000000..45094ca57 --- /dev/null +++ b/flatpak/shared-modules @@ -0,0 +1 @@ +Subproject commit 45094ca570be383d06df729b6972830ec63bd3df diff --git a/garnix.yaml b/garnix.yaml new file mode 100644 index 000000000..3bf145248 --- /dev/null +++ b/garnix.yaml @@ -0,0 +1,6 @@ +builds: + exclude: [] + include: + - "checks.x86_64-linux.*" + - "devShells.*.*" + - "packages.*.*" diff --git a/launcher/Application.cpp b/launcher/Application.cpp index e6070006c..4da41142c 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -6,9 +6,10 @@ * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Lenny McLennington - * Copyright (C) 2022 Tayou + * Copyright (C) 2022 Tayou * Copyright (C) 2023 TheKodeToad * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * Copyright (C) 2023 seth * * 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 @@ -48,27 +49,27 @@ #include "pathmatcher/MultiMatcher.h" #include "pathmatcher/SimplePrefixMatcher.h" #include "settings/INIFile.h" -#include "ui/MainWindow.h" #include "ui/InstanceWindow.h" +#include "ui/MainWindow.h" #include "ui/dialogs/ProgressDialog.h" #include "ui/instanceview/AccessibleInstanceView.h" #include "ui/pages/BasePageProvider.h" -#include "ui/pages/global/LauncherPage.h" -#include "ui/pages/global/MinecraftPage.h" +#include "ui/pages/global/APIPage.h" +#include "ui/pages/global/AccountListPage.h" +#include "ui/pages/global/CustomCommandsPage.h" +#include "ui/pages/global/ExternalToolsPage.h" #include "ui/pages/global/JavaPage.h" #include "ui/pages/global/LanguagePage.h" +#include "ui/pages/global/LauncherPage.h" +#include "ui/pages/global/MinecraftPage.h" #include "ui/pages/global/ProxyPage.h" -#include "ui/pages/global/ExternalToolsPage.h" -#include "ui/pages/global/AccountListPage.h" -#include "ui/pages/global/APIPage.h" -#include "ui/pages/global/CustomCommandsPage.h" -#include "ui/setupwizard/SetupWizard.h" -#include "ui/setupwizard/LanguageWizardPage.h" #include "ui/setupwizard/JavaWizardPage.h" +#include "ui/setupwizard/LanguageWizardPage.h" #include "ui/setupwizard/PasteWizardPage.h" +#include "ui/setupwizard/SetupWizard.h" #include "ui/setupwizard/ThemeWizardPage.h" #include "ui/dialogs/CustomMessageBox.h" @@ -82,20 +83,20 @@ #include #include -#include #include #include +#include #include #include -#include -#include +#include +#include #include #include +#include #include -#include #include +#include #include -#include #include "InstanceList.h" #include "MTPixmapCache.h" @@ -115,32 +116,27 @@ #include "settings/INISettingsObject.h" #include "settings/Setting.h" -#include "translations/TranslationsModel.h" #include "meta/Index.h" +#include "translations/TranslationsModel.h" -#include #include +#include #include #include #ifdef Q_OS_LINUX #include -#include "gamemode_client.h" #include "MangoHud.h" +#include "gamemode_client.h" #endif -#ifdef Q_OS_MAC +#if defined(Q_OS_MAC) && defined(SPARKLE_ENABLED) #include "updater/MacSparkleUpdater.h" #endif - #if defined Q_OS_WIN32 -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#include +#include "WindowsConsole.h" #endif #define STRINGIFY(x) #x @@ -153,10 +149,10 @@ PixmapCache* PixmapCache::s_instance = nullptr; namespace { /** This is used so that we can output to the log file in addition to the CLI. */ -void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) +void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) { static std::mutex loggerMutex; - const std::lock_guard lock(loggerMutex); // synchronized, QFile logFile is not thread-safe + const std::lock_guard lock(loggerMutex); // synchronized, QFile logFile is not thread-safe QString out = qFormatLogMessage(type, context, msg); out += QChar::LineFeed; @@ -167,31 +163,13 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QSt fflush(stderr); } -} +} // namespace -Application::Application(int &argc, char **argv) : QApplication(argc, argv) +Application::Application(int& argc, char** argv) : QApplication(argc, argv) { #if defined Q_OS_WIN32 - // attach the parent console - if(AttachConsole(ATTACH_PARENT_PROCESS)) - { - // if attach succeeds, reopen and sync all the i/o - if(freopen("CON", "w", stdout)) - { - std::cout.sync_with_stdio(); - } - if(freopen("CON", "w", stderr)) - { - std::cerr.sync_with_stdio(); - } - if(freopen("CON", "r", stdin)) - { - std::cin.sync_with_stdio(); - } - auto out = GetStdHandle (STD_OUTPUT_HANDLE); - DWORD written; - const char * endline = "\n"; - WriteConsole(out, endline, strlen(endline), &written, NULL); + // attach the parent console if stdout not already captured + if (AttachWindowsConsole()) { consoleAttached = true; } #endif @@ -210,15 +188,17 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) QCommandLineParser parser; parser.setApplicationDescription(BuildConfig.LAUNCHER_DISPLAYNAME); - parser.addOptions({ - {{"d", "dir"}, "Use a custom path as application root (use '.' for current directory)", "directory"}, - {{"l", "launch"}, "Launch the specified instance (by instance ID)", "instance"}, - {{"s", "server"}, "Join the specified server on launch (only valid in combination with --launch)", "address"}, - {{"a", "profile"}, "Use the account specified by its profile name (only valid in combination with --launch)", "profile"}, - {"alive", "Write a small '" + liveCheckFile + "' file after the launcher starts"}, - {{"I", "import"}, "Import instance from specified zip (local path or URL)", "file"}, - {"show", "Opens the window for the specified instance (by instance ID)", "show"} - }); + parser.addOptions( + { { { "d", "dir" }, "Use a custom path as application root (use '.' for current directory)", "directory" }, + { { "l", "launch" }, "Launch the specified instance (by instance ID)", "instance" }, + { { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" }, + { { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" }, + { "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" }, + { { "I", "import" }, "Import instance or resource from specified local path or URL", "url" }, + { "show", "Opens the window for the specified instance (by instance ID)", "show" } }); + // Has to be positional for some OS to handle that properly + parser.addPositionalArgument("URL", "Import the resource(s) at the given URL(s) (same as -I / --import)", "[URL...]"); + parser.addHelpOption(); parser.addVersionOption(); @@ -231,19 +211,17 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_instanceIdToShowWindowOf = parser.value("show"); - for (auto zip_path : parser.values("import")){ - m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath())); + for (auto url : parser.values("import")) { + m_urlsToImport.append(normalizeImportUrl(url)); } // treat unspecified positional arguments as import urls - for (auto zip_path : parser.positionalArguments()) { - m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath())); + for (auto url : parser.positionalArguments()) { + m_urlsToImport.append(normalizeImportUrl(url)); } - // error if --launch is missing with --server or --profile - if((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) - { + 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; @@ -255,7 +233,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) { // Root path is used for updates and portable data #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) - QDir foo(FS::PathCombine(binPath, "..")); // typically portable-root or /usr + QDir foo(FS::PathCombine(binPath, "..")); // typically portable-root or /usr m_rootPath = foo.absolutePath(); #elif defined(Q_OS_WIN32) m_rootPath = binPath; @@ -271,16 +249,19 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) QString dataPath; // change folder QString dirParam = parser.value("dir"); - if (!dirParam.isEmpty()) - { + if (!dirParam.isEmpty()) { // the dir param. it makes multimc data path point to whatever the user specified // on command line adjustedBy = "Command line"; dataPath = dirParam; - } - else - { - QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "..")); + } else { + QDir foo; + if (DesktopServices::isSnap()) { + foo = QDir(getenv("SNAP_USER_COMMON")); + } else { + foo = QDir(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "..")); + } + dataPath = foo.absolutePath(); adjustedBy = "Persistent data path"; @@ -293,34 +274,27 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) #endif } - if (!FS::ensureFolderPathExists(dataPath)) - { + if (!FS::ensureFolderPathExists(dataPath)) { showFatalErrorMessage( "The launcher data folder could not be created.", - QString( - "The launcher data folder could not be created.\n" - "\n" - "Make sure you have the right permissions to the launcher data folder and any folder needed to access it.\n" - "(%1)\n" - "\n" - "The launcher cannot continue until you fix this problem." - ).arg(dataPath) - ); + QString("The launcher data folder could not be created.\n" + "\n" + "Make sure you have the right permissions to the launcher data folder and any folder needed to access it.\n" + "(%1)\n" + "\n" + "The launcher cannot continue until you fix this problem.") + .arg(dataPath)); return; } - if (!QDir::setCurrent(dataPath)) - { - showFatalErrorMessage( - "The launcher data folder could not be opened.", - QString( - "The launcher data folder could not be opened.\n" - "\n" - "Make sure you have the right permissions to the launcher data folder.\n" - "(%1)\n" - "\n" - "The launcher cannot continue until you fix this problem." - ).arg(dataPath) - ); + if (!QDir::setCurrent(dataPath)) { + showFatalErrorMessage("The launcher data folder could not be opened.", + QString("The launcher data folder could not be opened.\n" + "\n" + "Make sure you have the right permissions to the launcher data folder.\n" + "(%1)\n" + "\n" + "The launcher cannot continue until you fix this problem.") + .arg(dataPath)); return; } @@ -334,37 +308,31 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // FIXME: you can run the same binaries with multiple data dirs and they won't clash. This could cause issues for updates. m_peerInstance = new LocalPeer(this, appID); connect(m_peerInstance, &LocalPeer::messageReceived, this, &Application::messageReceived); - if(m_peerInstance->isClient()) { + if (m_peerInstance->isClient()) { int timeout = 2000; - if(m_instanceIdToLaunch.isEmpty()) - { + if (m_instanceIdToLaunch.isEmpty()) { ApplicationMessage activate; activate.command = "activate"; m_peerInstance->sendMessage(activate.serialize(), timeout); - if(!m_zipsToImport.isEmpty()) - { - for (auto zip_url : m_zipsToImport) { + if (!m_urlsToImport.isEmpty()) { + for (auto url : m_urlsToImport) { ApplicationMessage import; import.command = "import"; - import.args.insert("path", zip_url.toString()); + import.args.insert("url", url.toString()); m_peerInstance->sendMessage(import.serialize(), timeout); } } - } - else - { + } else { ApplicationMessage launch; launch.command = "launch"; launch.args["id"] = m_instanceIdToLaunch; - if(!m_serverToJoin.isEmpty()) - { + if (!m_serverToJoin.isEmpty()) { launch.args["server"] = m_serverToJoin; } - if(!m_profileToUse.isEmpty()) - { + if (!m_profileToUse.isEmpty()) { launch.args["profile"] = m_profileToUse; } m_peerInstance->sendMessage(launch.serialize(), timeout); @@ -408,44 +376,51 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) qInstallMessageHandler(appDebugOutput); qSetMessagePattern( - "%{time process}" " " - "%{if-debug}D%{endif}" "%{if-info}I%{endif}" "%{if-warning}W%{endif}" "%{if-critical}C%{endif}" "%{if-fatal}F%{endif}" - " " "|" " " - "%{if-category}[%{category}]: %{endif}" - "%{message}"); - + "%{time process}" + " " + "%{if-debug}D%{endif}" + "%{if-info}I%{endif}" + "%{if-warning}W%{endif}" + "%{if-critical}C%{endif}" + "%{if-fatal}F%{endif}" + " " + "|" + " " + "%{if-category}[%{category}]: %{endif}" + "%{message}"); + bool foundLoggingRules = false; - + auto logRulesFile = QStringLiteral("qtlogging.ini"); auto logRulesPath = FS::PathCombine(dataPath, logRulesFile); - - qDebug() << "Testing" << logRulesPath << "..."; + + qDebug() << "Testing" << logRulesPath << "..."; foundLoggingRules = QFile::exists(logRulesPath); // search the dataPath() // seach app data standard path - if(!foundLoggingRules && !isPortable() && dirParam.isEmpty()) { + if (!foundLoggingRules && !isPortable() && dirParam.isEmpty()) { logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile)); - if(!logRulesPath.isEmpty()) { + if (!logRulesPath.isEmpty()) { qDebug() << "Found" << logRulesPath << "..."; foundLoggingRules = true; } } // seach root path - if(!foundLoggingRules) { + if (!foundLoggingRules) { #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) - logRulesPath = FS::PathCombine(m_rootPath, "share", BuildConfig.LAUNCHER_NAME, logRulesFile); + logRulesPath = FS::PathCombine(m_rootPath, "share", BuildConfig.LAUNCHER_NAME, logRulesFile); #else - logRulesPath = FS::PathCombine(m_rootPath, logRulesFile); + logRulesPath = FS::PathCombine(m_rootPath, logRulesFile); #endif qDebug() << "Testing" << logRulesPath << "..."; foundLoggingRules = QFile::exists(logRulesPath); } - - if(foundLoggingRules) { + + if (foundLoggingRules) { // load and set logging rules qDebug() << "Loading logging rules from:" << logRulesPath; - QSettings loggingRules(logRulesPath, QSettings::IniFormat); + QSettings loggingRules(logRulesPath, QSettings::IniFormat); loggingRules.beginGroup("Rules"); QStringList rule_names = loggingRules.childKeys(); QStringList rules; @@ -466,48 +441,44 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) bool migrated = false; if (!migrated) - migrated = handleDataMigration(dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../PolyMC"), "PolyMC", "polymc.cfg"); + migrated = handleDataMigration( + dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../PolyMC"), "PolyMC", + "polymc.cfg"); if (!migrated) - migrated = handleDataMigration(dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../multimc"), "MultiMC", "multimc.cfg"); + migrated = handleDataMigration( + dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../multimc"), "MultiMC", + "multimc.cfg"); } { - qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT; qDebug() << "Version : " << BuildConfig.printableVersionString(); + qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM; qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; - if (adjustedBy.size()) - { + if (adjustedBy.size()) { qDebug() << "Work dir before adjustment : " << origcwdPath; qDebug() << "Work dir after adjustment : " << QDir::currentPath(); qDebug() << "Adjusted by : " << adjustedBy; - } - else - { + } else { qDebug() << "Work dir : " << QDir::currentPath(); } qDebug() << "Binary path : " << binPath; qDebug() << "Application root path : " << m_rootPath; - if(!m_instanceIdToLaunch.isEmpty()) - { + if (!m_instanceIdToLaunch.isEmpty()) { qDebug() << "ID of instance to launch : " << m_instanceIdToLaunch; } - if(!m_serverToJoin.isEmpty()) - { + if (!m_serverToJoin.isEmpty()) { qDebug() << "Address of server to join :" << m_serverToJoin; } qDebug() << "<> Paths set."; } - if(m_liveCheck) - { + if (m_liveCheck) { QFile check(liveCheckFile); - if(check.open(QIODevice::WriteOnly | QIODevice::Truncate)) - { + if (check.open(QIODevice::WriteOnly | QIODevice::Truncate)) { 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 << "!"; @@ -524,7 +495,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings.reset(new INISettingsObject({ BuildConfig.LAUNCHER_CONFIGFILE, "polymc.cfg", "multimc.cfg" }, this)); // Theming - m_settings->registerSetting("IconTheme", QString("pe_colored")); + m_settings->registerSetting("IconTheme", QString()); m_settings->registerSetting("ApplicationTheme", QString()); m_settings->registerSetting("BackgroundCat", QString("kitteh")); @@ -553,7 +524,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) QString resolvedDefaultMonospace = consoleFontInfo.family(); QFont resolvedFont(resolvedDefaultMonospace); qDebug() << "Detected default console font:" << resolvedDefaultMonospace - << ", substitutions:" << resolvedFont.substitutions().join(','); + << ", substitutions:" << resolvedFont.substitutions().join(','); m_settings->registerSetting("ConsoleFont", resolvedDefaultMonospace); m_settings->registerSetting("ConsoleFontSize", defaultSize); @@ -562,7 +533,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // Folders m_settings->registerSetting("InstanceDir", "instances"); - m_settings->registerSetting({"CentralModsDir", "ModsDir"}, "mods"); + m_settings->registerSetting({ "CentralModsDir", "ModsDir" }, "mods"); m_settings->registerSetting("IconsDir", "icons"); m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); m_settings->registerSetting("DownloadsDirWatchRecursive", false); @@ -581,20 +552,20 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("LogPrePostOutput", true); // Window Size - m_settings->registerSetting({"LaunchMaximized", "MCWindowMaximize"}, false); - m_settings->registerSetting({"MinecraftWinWidth", "MCWindowWidth"}, 854); - m_settings->registerSetting({"MinecraftWinHeight", "MCWindowHeight"}, 480); + m_settings->registerSetting({ "LaunchMaximized", "MCWindowMaximize" }, false); + m_settings->registerSetting({ "MinecraftWinWidth", "MCWindowWidth" }, 854); + m_settings->registerSetting({ "MinecraftWinHeight", "MCWindowHeight" }, 480); // Proxy Settings m_settings->registerSetting("ProxyType", "None"); - m_settings->registerSetting({"ProxyAddr", "ProxyHostName"}, "127.0.0.1"); + m_settings->registerSetting({ "ProxyAddr", "ProxyHostName" }, "127.0.0.1"); m_settings->registerSetting("ProxyPort", 8080); - m_settings->registerSetting({"ProxyUser", "ProxyUsername"}, ""); - m_settings->registerSetting({"ProxyPass", "ProxyPassword"}, ""); + m_settings->registerSetting({ "ProxyUser", "ProxyUsername" }, ""); + m_settings->registerSetting({ "ProxyPass", "ProxyPassword" }, ""); // Memory - m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512); - m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, suitableMaxMem()); + m_settings->registerSetting({ "MinMemAlloc", "MinMemoryAlloc" }, 512); + m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, suitableMaxMem()); m_settings->registerSetting("PermGen", 128); // Java Settings @@ -609,6 +580,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("IgnoreJavaCompatibility", false); m_settings->registerSetting("IgnoreJavaWizard", false); + // Mod loader settings + m_settings->registerSetting("DisableQuiltBeacon", false); + // Native library workarounds m_settings->registerSetting("UseNativeOpenAL", false); m_settings->registerSetting("UseNativeGLFW", false); @@ -623,9 +597,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("ShowGlobalGameTime", true); m_settings->registerSetting("RecordGameTime", true); - // Minecraft launch method - m_settings->registerSetting("MCLaunchMethod", "LauncherPart"); - // Minecraft mods m_settings->registerSetting("ModMetadataDisabled", false); @@ -636,8 +607,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("WrapperCommand", ""); // Custom Commands - m_settings->registerSetting({"PreLaunchCommand", "PreLaunchCmd"}, ""); - m_settings->registerSetting({"PostExitCommand", "PostExitCmd"}, ""); + m_settings->registerSetting({ "PreLaunchCommand", "PreLaunchCmd" }, ""); + m_settings->registerSetting({ "PostExitCommand", "PostExitCmd" }, ""); // The cat m_settings->registerSetting("TheCat", false); @@ -676,8 +647,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) QString pastebinURL = m_settings->get("PastebinURL").toString(); bool userHadDefaultPastebin = pastebinURL == "https://0x0.st"; - if (!pastebinURL.isEmpty() && !userHadDefaultPastebin) - { + if (!pastebinURL.isEmpty() && !userHadDefaultPastebin) { m_settings->set("PastebinType", PasteUpload::PasteType::NullPointer); m_settings->set("PastebinCustomAPIBase", pastebinURL); m_settings->reset("PastebinURL"); @@ -686,8 +656,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) bool ok; int pasteType = m_settings->get("PastebinType").toInt(&ok); // If PastebinType is invalid then reset the related settings. - if (!ok || !(PasteUpload::PasteType::First <= pasteType && pasteType <= PasteUpload::PasteType::Last)) - { + if (!ok || !(PasteUpload::PasteType::First <= pasteType && pasteType <= PasteUpload::PasteType::Last)) { m_settings->reset("PastebinType"); m_settings->reset("PastebinCustomAPIBase"); } @@ -699,7 +668,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) QUrl metaUrl(m_settings->get("MetaURLOverride").toString()); // get rid of invalid meta urls - if (!metaUrl.isValid() || metaUrl.scheme() != "http" || metaUrl.scheme() != "https") + if (!metaUrl.isValid() || (metaUrl.scheme() != "http" && metaUrl.scheme() != "https")) m_settings->reset("MetaURLOverride"); } @@ -768,10 +737,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) } // initialize the updater - if(BuildConfig.UPDATER_ENABLED) - { + if (BuildConfig.UPDATER_ENABLED) { qDebug() << "Initializing updater"; -#ifdef Q_OS_MAC +#if defined(Q_OS_MAC) && defined(SPARKLE_ENABLED) m_updater.reset(new MacSparkleUpdater()); #endif qDebug() << "<> Updater started."; @@ -780,23 +748,16 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // Instance icons { auto setting = APPLICATION->settings()->getSetting("IconsDir"); - QStringList instFolders = - { - ":/icons/multimc/32x32/instances/", - ":/icons/multimc/50x50/instances/", - ":/icons/multimc/128x128/instances/", - ":/icons/multimc/scalable/instances/" - }; + QStringList instFolders = { ":/icons/multimc/32x32/instances/", ":/icons/multimc/50x50/instances/", + ":/icons/multimc/128x128/instances/", ":/icons/multimc/scalable/instances/" }; m_icons.reset(new IconList(instFolders, setting->get().toString())); - connect(setting.get(), &Setting::SettingChanged,[&](const Setting &, QVariant value) - { - m_icons->directoryChanged(value.toString()); - }); + connect(setting.get(), &Setting::SettingChanged, + [&](const Setting&, QVariant value) { m_icons->directoryChanged(value.toString()); }); qDebug() << "<> Instance icons intialized."; } // Themes - m_themeManager = std::make_unique(m_mainWindow); + m_themeManager = std::make_unique(); // initialize and load all instances { @@ -805,8 +766,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // and remember that we have to show him a dialog when the gui starts (if it does so) QString instDir = InstDirSetting->get().toString(); qDebug() << "Instance path : " << instDir; - if (FS::checkProblemticPathJava(QDir(instDir))) - { + if (FS::checkProblemticPathJava(QDir(instDir))) { qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!"; } m_instances.reset(new InstanceList(m_settings, instDir, this)); @@ -856,11 +816,10 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // now we have network, download translation updates m_translations->downloadIndex(); - //FIXME: what to do with these? + // FIXME: what to do with these? m_profilers.insert("jprofiler", std::shared_ptr(new JProfilerFactory())); m_profilers.insert("jvisualvm", std::shared_ptr(new JVisualVMFactory())); - for (auto profiler : m_profilers.values()) - { + for (auto profiler : m_profilers.values()) { profiler->registerSettings(m_settings); } @@ -870,87 +829,83 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) } #ifdef Q_OS_MACOS - connect(this, &Application::clickedOnDock, [this]() { - this->showMainWindow(); - }); + connect(this, &Application::clickedOnDock, [this]() { this->showMainWindow(); }); #endif - connect(this, &Application::aboutToQuit, [this](){ - if(m_instances) - { + connect(this, &Application::aboutToQuit, [this]() { + if (m_instances) { // save any remaining instance state m_instances->saveNow(); } - if(logFile) - { + if (logFile) { logFile->flush(); logFile->close(); } }); - applyCurrentlySelectedTheme(true); - updateCapabilities(); - if(createSetupWizard()) - { + if (createSetupWizard()) { return; } + m_themeManager->applyCurrentlySelectedTheme(true); performMainStartupAction(); } bool Application::createSetupWizard() { - bool javaRequired = [&]() - { + bool javaRequired = [&]() { bool ignoreJavaWizard = m_settings->get("IgnoreJavaWizard").toBool(); - if(ignoreJavaWizard) { + if (ignoreJavaWizard) { return false; } QString currentHostName = QHostInfo::localHostName(); QString oldHostName = settings()->get("LastHostname").toString(); - if (currentHostName != oldHostName) - { + if (currentHostName != oldHostName) { settings()->set("LastHostname", currentHostName); return true; } QString currentJavaPath = settings()->get("JavaPath").toString(); QString actualPath = FS::ResolveExecutable(currentJavaPath); - if (actualPath.isNull()) - { + if (actualPath.isNull()) { return true; } return false; }(); bool languageRequired = settings()->get("Language").toString().isEmpty(); bool pasteInterventionRequired = settings()->get("PastebinURL") != ""; - bool themeInterventionRequired = settings()->get("ApplicationTheme") == ""; + bool validWidgets = m_themeManager->isValidApplicationTheme(settings()->get("ApplicationTheme").toString()); + bool validIcons = m_themeManager->isValidIconTheme(settings()->get("IconTheme").toString()); + bool themeInterventionRequired = !validWidgets || !validIcons; bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired; - if(wizardRequired) - { + if (wizardRequired) { + // set default theme after going into theme wizard + if (!validIcons) + settings()->set("IconTheme", QString("pe_colored")); + if (!validWidgets) + settings()->set("ApplicationTheme", QString("system")); + + m_themeManager->applyCurrentlySelectedTheme(true); + m_setupWizard = new SetupWizard(nullptr); - if (languageRequired) - { + if (languageRequired) { m_setupWizard->addPage(new LanguageWizardPage(m_setupWizard)); } - if (javaRequired) - { + if (javaRequired) { m_setupWizard->addPage(new JavaWizardPage(m_setupWizard)); } - if (pasteInterventionRequired) - { + if (pasteInterventionRequired) { m_setupWizard->addPage(new PasteWizardPage(m_setupWizard)); } - if (themeInterventionRequired) - { - settings()->set("ApplicationTheme", QString("system")); // set default theme after going into theme wizard + if (themeInterventionRequired) { m_setupWizard->addPage(new ThemeWizardPage(m_setupWizard)); } + connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished); m_setupWizard->show(); return true; @@ -988,26 +943,22 @@ void Application::setupWizardFinished(int status) void Application::performMainStartupAction() { m_status = Application::Initialized; - if(!m_instanceIdToLaunch.isEmpty()) - { + if (!m_instanceIdToLaunch.isEmpty()) { auto inst = instances()->getInstanceById(m_instanceIdToLaunch); - if(inst) - { + if (inst) { MinecraftServerTargetPtr serverToJoin = nullptr; MinecraftAccountPtr accountToUse = nullptr; qDebug() << "<> Instance" << m_instanceIdToLaunch << "launching"; - if(!m_serverToJoin.isEmpty()) - { + if (!m_serverToJoin.isEmpty()) { // FIXME: validate the server string serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(m_serverToJoin))); qDebug() << " Launching with server" << m_serverToJoin; } - if(!m_profileToUse.isEmpty()) - { + if (!m_profileToUse.isEmpty()) { accountToUse = accounts()->getAccountByProfileName(m_profileToUse); - if(!accountToUse) { + if (!accountToUse) { return; } qDebug() << " Launching with account" << m_profileToUse; @@ -1017,26 +968,22 @@ void Application::performMainStartupAction() return; } } - if(!m_instanceIdToShowWindowOf.isEmpty()) - { + if (!m_instanceIdToShowWindowOf.isEmpty()) { auto inst = instances()->getInstanceById(m_instanceIdToShowWindowOf); - if(inst) - { + if (inst) { qDebug() << "<> Showing window of instance " << m_instanceIdToShowWindowOf; showInstanceWindow(inst); return; } } - if(!m_mainWindow) - { + if (!m_mainWindow) { // normal main window showMainWindow(false); qDebug() << "<> Main window shown."; } - if(!m_zipsToImport.isEmpty()) - { - qDebug() << "<> Importing from zip:" << m_zipsToImport; - m_mainWindow->processURLs( m_zipsToImport ); + if (!m_urlsToImport.isEmpty()) { + qDebug() << "<> Importing from url:" << m_urlsToImport; + m_mainWindow->processURLs(m_urlsToImport); } } @@ -1054,8 +1001,7 @@ Application::~Application() #if defined Q_OS_WIN32 // Detach from Windows console - if(consoleAttached) - { + if (consoleAttached) { fclose(stdout); fclose(stdin); fclose(stderr); @@ -1066,8 +1012,7 @@ Application::~Application() void Application::messageReceived(const QByteArray& message) { - if(status() != Initialized) - { + if (status() != Initialized) { qDebug() << "Received message" << message << "while still initializing. It will be ignored."; return; } @@ -1075,66 +1020,51 @@ void Application::messageReceived(const QByteArray& message) ApplicationMessage received; received.parse(message); - auto & command = received.command; + auto& command = received.command; - if(command == "activate") - { + if (command == "activate") { showMainWindow(); - } - else if(command == "import") - { - QString path = received.args["path"]; - if(path.isEmpty()) - { + } else if (command == "import") { + QString url = received.args["url"]; + if (url.isEmpty()) { qWarning() << "Received" << command << "message without a zip path/URL."; return; } - m_mainWindow->processURLs({ QUrl::fromLocalFile(QFileInfo(path).absoluteFilePath()) }); - } - else if(command == "launch") - { + m_mainWindow->processURLs({ normalizeImportUrl(url) }); + } else if (command == "launch") { QString id = received.args["id"]; QString server = received.args["server"]; QString profile = received.args["profile"]; InstancePtr instance; - if(!id.isEmpty()) { + if (!id.isEmpty()) { instance = instances()->getInstanceById(id); - if(!instance) { + if (!instance) { qWarning() << "Launch command requires an valid instance ID. " << id << "resolves to nothing."; return; } - } - else { + } else { qWarning() << "Launch command called without an instance ID..."; return; } MinecraftServerTargetPtr serverObject = nullptr; - if(!server.isEmpty()) { + if (!server.isEmpty()) { serverObject = std::make_shared(MinecraftServerTarget::parse(server)); } MinecraftAccountPtr accountObject; - if(!profile.isEmpty()) { + if (!profile.isEmpty()) { accountObject = accounts()->getAccountByProfileName(profile); - if(!accountObject) { - qWarning() << "Launch command requires the specified profile to be valid. " << profile << "does not resolve to any account."; + if (!accountObject) { + qWarning() << "Launch command requires the specified profile to be valid. " << profile + << "does not resolve to any account."; return; } } - launch( - instance, - true, - false, - nullptr, - serverObject, - accountObject - ); - } - else - { + launch(instance, true, false, nullptr, serverObject, accountObject); + } else { qWarning() << "Received invalid message" << message; } } @@ -1146,79 +1076,49 @@ std::shared_ptr Application::translations() std::shared_ptr Application::javalist() { - if (!m_javalist) - { + if (!m_javalist) { m_javalist.reset(new JavaInstallList()); } return m_javalist; } -QList Application::getValidApplicationThemes() -{ - return m_themeManager->getValidApplicationThemes(); -} - -void Application::applyCurrentlySelectedTheme(bool initial) -{ - m_themeManager->applyCurrentlySelectedTheme(initial); -} - -void Application::setApplicationTheme(const QString& name) -{ - m_themeManager->setApplicationTheme(name); -} - -void Application::setIconTheme(const QString& name) -{ - m_themeManager->setIconTheme(name); -} - QIcon Application::getThemedIcon(const QString& name) { - if(name == "logo") { + if (name == "logo") { return QIcon(":/" + BuildConfig.LAUNCHER_SVGFILENAME); } return QIcon::fromTheme(name); } -bool Application::openJsonEditor(const QString &filename) +bool Application::openJsonEditor(const QString& filename) { const QString file = QDir::current().absoluteFilePath(filename); - if (m_settings->get("JsonEditor").toString().isEmpty()) - { + if (m_settings->get("JsonEditor").toString().isEmpty()) { return DesktopServices::openUrl(QUrl::fromLocalFile(file)); - } - else - { - //return DesktopServices::openFile(m_settings->get("JsonEditor").toString(), file); - return DesktopServices::run(m_settings->get("JsonEditor").toString(), {file}); + } else { + // return DesktopServices::openFile(m_settings->get("JsonEditor").toString(), file); + return DesktopServices::run(m_settings->get("JsonEditor").toString(), { file }); } } -bool Application::launch( - InstancePtr instance, - bool online, - bool demo, - BaseProfilerFactory *profiler, - MinecraftServerTargetPtr serverToJoin, - MinecraftAccountPtr accountToUse -) { - if(m_updateRunning) - { +bool Application::launch(InstancePtr instance, + bool online, + bool demo, + BaseProfilerFactory* profiler, + MinecraftServerTargetPtr serverToJoin, + MinecraftAccountPtr accountToUse) +{ + if (m_updateRunning) { qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed."; - } - else if(instance->canLaunch()) - { - auto & extras = m_instanceExtras[instance->id()]; - auto & window = extras.window; - if(window) - { - if(!window->saveAll()) - { + } else if (instance->canLaunch()) { + auto& extras = m_instanceExtras[instance->id()]; + auto& window = extras.window; + if (window) { + if (!window->saveAll()) { return false; } } - auto & controller = extras.controller; + auto& controller = extras.controller; controller.reset(new LaunchController()); controller->setInstance(instance); controller->setOnline(online); @@ -1226,30 +1126,21 @@ bool Application::launch( controller->setProfiler(profiler); controller->setServerToJoin(serverToJoin); controller->setAccountToUse(accountToUse); - if(window) - { + if (window) { controller->setParentWidget(window); - } - else if(m_mainWindow) - { + } else if (m_mainWindow) { controller->setParentWidget(m_mainWindow); } connect(controller.get(), &LaunchController::succeeded, this, &Application::controllerSucceeded); connect(controller.get(), &LaunchController::failed, this, &Application::controllerFailed); - connect(controller.get(), &LaunchController::aborted, this, [this] { - controllerFailed(tr("Aborted")); - }); + connect(controller.get(), &LaunchController::aborted, this, [this] { controllerFailed(tr("Aborted")); }); addRunningInstance(); controller->start(); return true; - } - else if (instance->isRunning()) - { + } else if (instance->isRunning()) { showInstanceWindow(instance, "console"); return true; - } - else if (instance->canEdit()) - { + } else if (instance->canEdit()) { showInstanceWindow(instance); return true; } @@ -1258,16 +1149,14 @@ bool Application::launch( bool Application::kill(InstancePtr instance) { - if (!instance->isRunning()) - { + if (!instance->isRunning()) { qWarning() << "Attempted to kill instance" << instance->id() << ", which isn't running."; return false; } - auto & extras = m_instanceExtras[instance->id()]; + auto& extras = m_instanceExtras[instance->id()]; // NOTE: copy of the shared pointer keeps it alive auto controller = extras.controller; - if(controller) - { + if (controller) { return controller->abort(); } return true; @@ -1281,23 +1170,20 @@ void Application::closeCurrentWindow() void Application::addRunningInstance() { - m_runningInstances ++; - if(m_runningInstances == 1) - { + m_runningInstances++; + if (m_runningInstances == 1) { emit updateAllowedChanged(false); } } void Application::subRunningInstance() { - if(m_runningInstances == 0) - { + if (m_runningInstances == 0) { qCritical() << "Something went really wrong and we now have less than 0 running instances... WTF"; return; } - m_runningInstances --; - if(m_runningInstances == 0) - { + m_runningInstances--; + if (m_runningInstances == 0) { emit updateAllowedChanged(true); } } @@ -1317,20 +1203,17 @@ void Application::updateIsRunning(bool running) m_updateRunning = running; } - void Application::controllerSucceeded() { - auto controller = qobject_cast(QObject::sender()); - if(!controller) + auto controller = qobject_cast(QObject::sender()); + if (!controller) return; auto id = controller->id(); - auto & extras = m_instanceExtras[id]; + auto& extras = m_instanceExtras[id]; // on success, do... - if (controller->instance()->settings()->get("AutoCloseConsole").toBool()) - { - if(extras.window) - { + if (controller->instance()->settings()->get("AutoCloseConsole").toBool()) { + if (extras.window) { extras.window->close(); } } @@ -1338,8 +1221,7 @@ void Application::controllerSucceeded() subRunningInstance(); // quit when there are no more windows. - if(shouldExitNow()) - { + if (shouldExitNow()) { m_status = Status::Succeeded; exit(0); } @@ -1348,19 +1230,18 @@ void Application::controllerSucceeded() void Application::controllerFailed(const QString& error) { Q_UNUSED(error); - auto controller = qobject_cast(QObject::sender()); - if(!controller) + auto controller = qobject_cast(QObject::sender()); + if (!controller) return; auto id = controller->id(); - auto & extras = m_instanceExtras[id]; + auto& extras = m_instanceExtras[id]; // on failure, do... nothing extras.controller.reset(); subRunningInstance(); // quit when there are no more windows. - if(shouldExitNow()) - { + if (shouldExitNow()) { m_status = Status::Failed; exit(1); } @@ -1368,7 +1249,7 @@ void Application::controllerFailed(const QString& error) void Application::ShowGlobalSettings(class QWidget* parent, QString open_page) { - if(!m_globalSettingsProvider) { + if (!m_globalSettingsProvider) { return; } emit globalSettingsAboutToOpen(); @@ -1382,24 +1263,18 @@ void Application::ShowGlobalSettings(class QWidget* parent, QString open_page) MainWindow* Application::showMainWindow(bool minimized) { - if(m_mainWindow) - { + if (m_mainWindow) { m_mainWindow->setWindowState(m_mainWindow->windowState() & ~Qt::WindowMinimized); m_mainWindow->raise(); m_mainWindow->activateWindow(); - } - else - { + } else { m_mainWindow = new MainWindow(); m_mainWindow->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowState").toByteArray())); m_mainWindow->restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowGeometry").toByteArray())); - if(minimized) - { + if (minimized) { m_mainWindow->showMinimized(); - } - else - { + } else { m_mainWindow->show(); } @@ -1411,31 +1286,26 @@ MainWindow* Application::showMainWindow(bool minimized) return m_mainWindow; } -InstanceWindow *Application::showInstanceWindow(InstancePtr instance, QString page) +InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString page) { - if(!instance) + if (!instance) return nullptr; auto id = instance->id(); - auto & extras = m_instanceExtras[id]; - auto & window = extras.window; + auto& extras = m_instanceExtras[id]; + auto& window = extras.window; - if(window) - { + if (window) { window->raise(); window->activateWindow(); - } - else - { + } else { window = new InstanceWindow(instance); - m_openWindows ++; + m_openWindows++; connect(window, &InstanceWindow::isClosing, this, &Application::on_windowClose); } - if(!page.isEmpty()) - { + if (!page.isEmpty()) { window->selectPage(page); } - if(extras.controller) - { + if (extras.controller) { extras.controller->setParentWidget(window); } return window; @@ -1444,24 +1314,20 @@ InstanceWindow *Application::showInstanceWindow(InstancePtr instance, QString pa void Application::on_windowClose() { m_openWindows--; - auto instWindow = qobject_cast(QObject::sender()); - if(instWindow) - { - auto & extras = m_instanceExtras[instWindow->instanceId()]; + auto instWindow = qobject_cast(QObject::sender()); + if (instWindow) { + auto& extras = m_instanceExtras[instWindow->instanceId()]; extras.window = nullptr; - if(extras.controller) - { + if (extras.controller) { extras.controller->setParentWidget(m_mainWindow); } } - auto mainWindow = qobject_cast(QObject::sender()); - if(mainWindow) - { + auto mainWindow = qobject_cast(QObject::sender()); + if (mainWindow) { m_mainWindow = nullptr; } // quit when there are no more windows. - if(shouldExitNow()) - { + if (shouldExitNow()) { exit(0); } } @@ -1469,23 +1335,14 @@ void Application::on_windowClose() void Application::updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password) { // Set the application proxy settings. - if (proxyTypeStr == "SOCKS5") - { - QNetworkProxy::setApplicationProxy( - QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password)); - } - else if (proxyTypeStr == "HTTP") - { - QNetworkProxy::setApplicationProxy( - QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password)); - } - else if (proxyTypeStr == "None") - { + if (proxyTypeStr == "SOCKS5") { + QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password)); + } else if (proxyTypeStr == "HTTP") { + QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password)); + } else if (proxyTypeStr == "None") { // If we have no proxy set, set no proxy and return. QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy)); - } - else - { + } else { // If we have "Default" selected, set Qt to use the system proxy settings. QNetworkProxyFactory::setUseSystemConfiguration(true); } @@ -1495,39 +1352,35 @@ void Application::updateProxySettings(QString proxyTypeStr, QString addr, int po m_network->setProxy(proxy); QString proxyDesc; - if (proxy.type() == QNetworkProxy::NoProxy) - { + if (proxy.type() == QNetworkProxy::NoProxy) { qDebug() << "Using no proxy is an option!"; return; } - switch (proxy.type()) - { - case QNetworkProxy::DefaultProxy: - proxyDesc = "Default proxy: "; - break; - case QNetworkProxy::Socks5Proxy: - proxyDesc = "Socks5 proxy: "; - break; - case QNetworkProxy::HttpProxy: - proxyDesc = "HTTP proxy: "; - break; - case QNetworkProxy::HttpCachingProxy: - proxyDesc = "HTTP caching: "; - break; - case QNetworkProxy::FtpCachingProxy: - proxyDesc = "FTP caching: "; - break; - default: - proxyDesc = "DERP proxy: "; - break; + switch (proxy.type()) { + case QNetworkProxy::DefaultProxy: + proxyDesc = "Default proxy: "; + break; + case QNetworkProxy::Socks5Proxy: + proxyDesc = "Socks5 proxy: "; + break; + case QNetworkProxy::HttpProxy: + proxyDesc = "HTTP proxy: "; + break; + case QNetworkProxy::HttpCachingProxy: + proxyDesc = "HTTP caching: "; + break; + case QNetworkProxy::FtpCachingProxy: + proxyDesc = "FTP caching: "; + break; + default: + proxyDesc = "DERP proxy: "; + break; } - proxyDesc += QString("%1:%2") - .arg(proxy.hostName()) - .arg(proxy.port()); + proxyDesc += QString("%1:%2").arg(proxy.hostName()).arg(proxy.port()); qDebug() << proxyDesc; } -shared_qobject_ptr< HttpMetaCache > Application::metacache() +shared_qobject_ptr Application::metacache() { return m_metacache; } @@ -1539,8 +1392,7 @@ shared_qobject_ptr Application::network() shared_qobject_ptr Application::metadataIndex() { - if (!m_metadataIndex) - { + if (!m_metadataIndex) { m_metadataIndex.reset(new Meta::Index()); } return m_metadataIndex; @@ -1571,10 +1423,9 @@ QString Application::getJarPath(QString jarFile) #endif FS::PathCombine(m_rootPath, "jars"), FS::PathCombine(applicationDirPath(), "jars"), - FS::PathCombine(applicationDirPath(), "..", "jars") // from inside build dir, for debuging + FS::PathCombine(applicationDirPath(), "..", "jars") // from inside build dir, for debuging }; - for(QString p : potentialPaths) - { + for (QString p : potentialPaths) { QString jarPath = FS::PathCombine(p, jarFile); if (QFileInfo(jarPath).isFile()) return jarPath; @@ -1639,7 +1490,7 @@ int Application::suitableMaxMem() // If totalRAM < 6GB, use (totalRAM / 1.5), else 4GB if (totalRAM < (4096 * 1.5)) - maxMemoryAlloc = (int) (totalRAM / 1.5); + maxMemoryAlloc = (int)(totalRAM / 1.5); else maxMemoryAlloc = 4096; @@ -1742,3 +1593,13 @@ void Application::triggerUpdateCheck() qDebug() << "Updater not available."; } } + +QUrl Application::normalizeImportUrl(QString const& url) +{ + auto local_file = QFileInfo(url); + if (local_file.exists()) { + return QUrl::fromLocalFile(local_file.absoluteFilePath()); + } else { + return QUrl::fromUserInput(url); + } +} diff --git a/launcher/Application.h b/launcher/Application.h index ced0af17d..7185886d1 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -2,7 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 Tayou + * Copyright (C) 2022 Tayou * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify @@ -38,16 +38,17 @@ #pragma once #include -#include +#include #include #include #include -#include #include +#include #include #include "minecraft/launch/MinecraftServerTarget.h" +#include "ui/themes/CatPack.h" class LaunchController; class LocalPeer; @@ -70,27 +71,22 @@ class TranslationsModel; class ITheme; class MCEditTool; class ThemeManager; +class IconTheme; namespace Meta { - class Index; +class Index; } #if defined(APPLICATION) #undef APPLICATION #endif -#define APPLICATION (static_cast(QCoreApplication::instance())) +#define APPLICATION (static_cast(QCoreApplication::instance())) -class Application : public QApplication -{ +class Application : public QApplication { // friends for the purpose of limiting access to deprecated stuff Q_OBJECT -public: - enum Status { - StartingUp, - Failed, - Succeeded, - Initialized - }; + public: + enum Status { StartingUp, Failed, Succeeded, Initialized }; enum Capability { None = 0, @@ -102,33 +98,21 @@ public: }; Q_DECLARE_FLAGS(Capabilities, Capability) -public: - Application(int &argc, char **argv); + public: + Application(int& argc, char** argv); virtual ~Application(); bool event(QEvent* event) override; - std::shared_ptr settings() const { - return m_settings; - } + std::shared_ptr settings() const { return m_settings; } - qint64 timeSinceStart() const { - return startTime.msecsTo(QDateTime::currentDateTime()); - } + qint64 timeSinceStart() const { return startTime.msecsTo(QDateTime::currentDateTime()); } QIcon getThemedIcon(const QString& name); - void setIconTheme(const QString& name); + ThemeManager* themeManager() { return m_themeManager.get(); } - void applyCurrentlySelectedTheme(bool initial = false); - - QList getValidApplicationThemes(); - - void setApplicationTheme(const QString& name); - - shared_qobject_ptr updater() { - return m_updater; - } + shared_qobject_ptr updater() { return m_updater; } void triggerUpdateCheck(); @@ -136,29 +120,17 @@ public: std::shared_ptr javalist(); - std::shared_ptr instances() const { - return m_instances; - } + std::shared_ptr instances() const { return m_instances; } - std::shared_ptr icons() const { - return m_icons; - } + std::shared_ptr icons() const { return m_icons; } - MCEditTool *mcedit() const { - return m_mcedit.get(); - } + MCEditTool* mcedit() const { return m_mcedit.get(); } - shared_qobject_ptr accounts() const { - return m_accounts; - } + shared_qobject_ptr accounts() const { return m_accounts; } - Status status() const { - return m_status; - } + Status status() const { return m_status; } - const QMap> &profilers() const { - return m_profilers; - } + const QMap>& profilers() const { return m_profilers; } void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password); @@ -183,35 +155,31 @@ public: QString getUserAgentUncached(); /// this is the root of the 'installation'. Used for automatic updates - const QString &root() { - return m_rootPath; - } + const QString& root() { return m_rootPath; } - bool isPortable() { - return m_portable; - } + bool isPortable() { return m_portable; } - const Capabilities capabilities() { - return m_capabilities; - } + const Capabilities capabilities() { return m_capabilities; } /*! * Opens a json file using either a system default editor, or, if not empty, the editor * specified in the settings */ - bool openJsonEditor(const QString &filename); + bool openJsonEditor(const QString& filename); - InstanceWindow *showInstanceWindow(InstancePtr instance, QString page = QString()); - MainWindow *showMainWindow(bool minimized = false); + InstanceWindow* showInstanceWindow(InstancePtr instance, QString page = QString()); + MainWindow* showMainWindow(bool minimized = false); void updateIsRunning(bool running); bool updatesAreAllowed(); - void ShowGlobalSettings(class QWidget * parent, QString open_page = QString()); + void ShowGlobalSettings(class QWidget* parent, QString open_page = QString()); int suitableMaxMem(); -signals: + QUrl normalizeImportUrl(QString const& url); + + signals: void updateAllowedChanged(bool status); void globalSettingsAboutToOpen(); void globalSettingsClosed(); @@ -221,39 +189,37 @@ signals: void clickedOnDock(); #endif -public slots: - bool launch( - InstancePtr instance, - bool online = true, - bool demo = false, - BaseProfilerFactory *profiler = nullptr, - MinecraftServerTargetPtr serverToJoin = nullptr, - MinecraftAccountPtr accountToUse = nullptr - ); + public slots: + bool launch(InstancePtr instance, + bool online = true, + bool demo = false, + BaseProfilerFactory* profiler = nullptr, + MinecraftServerTargetPtr serverToJoin = nullptr, + MinecraftAccountPtr accountToUse = nullptr); bool kill(InstancePtr instance); void closeCurrentWindow(); -private slots: + private slots: void on_windowClose(); - void messageReceived(const QByteArray & message); + void messageReceived(const QByteArray& message); void controllerSucceeded(); - void controllerFailed(const QString & error); + void controllerFailed(const QString& error); void setupWizardFinished(int status); -private: - bool handleDataMigration(const QString & currentData, const QString & oldData, const QString & name, const QString & configFile) const; + private: + bool handleDataMigration(const QString& currentData, const QString& oldData, const QString& name, const QString& configFile) const; bool createSetupWizard(); void performMainStartupAction(); // sets the fatal error message and m_status to Failed. - void showFatalErrorMessage(const QString & title, const QString & content); + void showFatalErrorMessage(const QString& title, const QString& content); -private: + private: void addRunningInstance(); void subRunningInstance(); bool shouldExitNow() const; -private: + private: QDateTime startTime; shared_qobject_ptr m_network; @@ -279,7 +245,7 @@ private: QString m_rootPath; Status m_status = Application::StartingUp; Capabilities m_capabilities; - bool m_portable = false; + bool m_portable = false; #ifdef Q_OS_MACOS Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive; @@ -292,7 +258,7 @@ private: // FIXME: attach to instances instead. struct InstanceXtras { - InstanceWindow * window = nullptr; + InstanceWindow* window = nullptr; shared_qobject_ptr controller; }; std::map m_instanceExtras; @@ -303,18 +269,19 @@ private: bool m_updateRunning = false; // main window, if any - MainWindow * m_mainWindow = nullptr; + MainWindow* m_mainWindow = nullptr; // peer launcher instance connector - used to implement single instance launcher and signalling - LocalPeer * m_peerInstance = nullptr; + LocalPeer* m_peerInstance = nullptr; - SetupWizard * m_setupWizard = nullptr; -public: + SetupWizard* m_setupWizard = nullptr; + + public: QString m_instanceIdToLaunch; QString m_serverToJoin; QString m_profileToUse; bool m_liveCheck = false; - QList m_zipsToImport; + QList m_urlsToImport; QString m_instanceIdToShowWindowOf; std::unique_ptr logFile; }; diff --git a/launcher/ApplicationMessage.cpp b/launcher/ApplicationMessage.cpp index 700e43ced..50ac10ccc 100644 --- a/launcher/ApplicationMessage.cpp +++ b/launcher/ApplicationMessage.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -39,7 +39,8 @@ #include #include "Json.h" -void ApplicationMessage::parse(const QByteArray & input) { +void ApplicationMessage::parse(const QByteArray& input) +{ auto doc = Json::requireDocument(input, "ApplicationMessage"); auto root = Json::requireObject(doc, "ApplicationMessage"); @@ -47,12 +48,13 @@ void ApplicationMessage::parse(const QByteArray & input) { args.clear(); auto parsedArgs = root.value("args").toObject(); - for(auto iter = parsedArgs.constBegin(); iter != parsedArgs.constEnd(); iter++) { + for (auto iter = parsedArgs.constBegin(); iter != parsedArgs.constEnd(); iter++) { args.insert(iter.key(), iter.value().toString()); } } -QByteArray ApplicationMessage::serialize() { +QByteArray ApplicationMessage::serialize() +{ QJsonObject root; root.insert("command", command); QJsonObject outArgs; diff --git a/launcher/ApplicationMessage.h b/launcher/ApplicationMessage.h index d66456ebd..7ad674330 100644 --- a/launcher/ApplicationMessage.h +++ b/launcher/ApplicationMessage.h @@ -1,13 +1,13 @@ #pragma once -#include -#include #include +#include +#include struct ApplicationMessage { QString command; QHash args; QByteArray serialize(); - void parse(const QByteArray & input); + void parse(const QByteArray& input); }; diff --git a/launcher/BaseInstaller.cpp b/launcher/BaseInstaller.cpp index d61c3fe90..1ff86ed40 100644 --- a/launcher/BaseInstaller.cpp +++ b/launcher/BaseInstaller.cpp @@ -18,27 +18,21 @@ #include "BaseInstaller.h" #include "minecraft/MinecraftInstance.h" -BaseInstaller::BaseInstaller() -{ +BaseInstaller::BaseInstaller() {} -} - -bool BaseInstaller::isApplied(MinecraftInstance *on) +bool BaseInstaller::isApplied(MinecraftInstance* on) { return QFile::exists(filename(on->instanceRoot())); } -bool BaseInstaller::add(MinecraftInstance *to) +bool BaseInstaller::add(MinecraftInstance* to) { - if (!patchesDir(to->instanceRoot()).exists()) - { + if (!patchesDir(to->instanceRoot()).exists()) { QDir(to->instanceRoot()).mkdir("patches"); } - if (isApplied(to)) - { - if (!remove(to)) - { + if (isApplied(to)) { + if (!remove(to)) { return false; } } @@ -46,16 +40,16 @@ bool BaseInstaller::add(MinecraftInstance *to) return true; } -bool BaseInstaller::remove(MinecraftInstance *from) +bool BaseInstaller::remove(MinecraftInstance* from) { return QFile::remove(filename(from->instanceRoot())); } -QString BaseInstaller::filename(const QString &root) const +QString BaseInstaller::filename(const QString& root) const { return patchesDir(root).absoluteFilePath(id() + ".json"); } -QDir BaseInstaller::patchesDir(const QString &root) const +QDir BaseInstaller::patchesDir(const QString& root) const { return QDir(root + "/patches/"); } diff --git a/launcher/BaseInstaller.h b/launcher/BaseInstaller.h index a1b80e93f..6244ced7d 100644 --- a/launcher/BaseInstaller.h +++ b/launcher/BaseInstaller.h @@ -26,20 +26,19 @@ class QObject; class Task; class BaseVersion; -class BaseInstaller -{ -public: +class BaseInstaller { + public: BaseInstaller(); virtual ~BaseInstaller(){}; - bool isApplied(MinecraftInstance *on); + bool isApplied(MinecraftInstance* on); - virtual bool add(MinecraftInstance *to); - virtual bool remove(MinecraftInstance *from); + virtual bool add(MinecraftInstance* to); + virtual bool remove(MinecraftInstance* from); - virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersion::Ptr version, QObject *parent) = 0; + virtual Task* createInstallTask(MinecraftInstance* instance, BaseVersion::Ptr version, QObject* parent) = 0; -protected: + protected: virtual QString id() const = 0; - QString filename(const QString &root) const; - QDir patchesDir(const QString &root) const; + QString filename(const QString& root) const; + QDir patchesDir(const QString& root) const; }; diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index a8fce879c..70c6da6b3 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2022 Jamie Mansfield * @@ -36,23 +36,22 @@ #include "BaseInstance.h" -#include -#include #include -#include +#include +#include #include #include +#include #include "settings/INISettingsObject.h" -#include "settings/Setting.h" #include "settings/OverrideSetting.h" +#include "settings/Setting.h" -#include "FileSystem.h" -#include "Commandline.h" #include "BuildConfig.h" +#include "Commandline.h" +#include "FileSystem.h" -BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) - : QObject() +BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir) : QObject() { m_settings = settings; m_global_settings = globalSettings; @@ -79,7 +78,7 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s m_settings->registerSetting("InstanceType", ""); // Custom Commands - auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false); + auto commandSetting = m_settings->registerSetting({ "OverrideCommands", "OverrideLaunchCmd" }, false); m_settings->registerOverride(globalSettings->getSetting("PreLaunchCommand"), commandSetting); m_settings->registerOverride(globalSettings->getSetting("WrapperCommand"), commandSetting); m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"), commandSetting); @@ -148,7 +147,11 @@ QString BaseInstance::getManagedPackVersionName() const return m_settings->get("ManagedPackVersionName").toString(); } -void BaseInstance::setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version) +void BaseInstance::setManagedPack(const QString& type, + const QString& id, + const QString& name, + const QString& versionId, + const QString& version) { m_settings->set("ManagedPack", true); m_settings->set("ManagedPackType", type); @@ -173,8 +176,7 @@ int BaseInstance::getConsoleMaxLines() const auto lineSetting = m_settings->getSetting("ConsoleMaxLines"); bool conversionOk = false; int maxLines = lineSetting->get().toInt(&conversionOk); - if(!conversionOk) - { + if (!conversionOk) { maxLines = lineSetting->defValue().toInt(); qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines; } @@ -220,8 +222,7 @@ bool BaseInstance::isLinkedToInstanceId(const QString& id) const void BaseInstance::iconUpdated(QString key) { - if(iconKey() == key) - { + if (iconKey() == key) { emit propertiesChanged(this); } } @@ -235,8 +236,7 @@ void BaseInstance::invalidate() void BaseInstance::changeStatus(BaseInstance::Status newStatus) { Status status = currentStatus(); - if(status != newStatus) - { + if (status != newStatus) { m_status = newStatus; emit statusChanged(status, newStatus); } @@ -259,23 +259,19 @@ bool BaseInstance::isRunning() const void BaseInstance::setRunning(bool running) { - if(running == m_isRunning) + if (running == m_isRunning) return; m_isRunning = running; - if(!m_settings->get("RecordGameTime").toBool()) - { + if (!m_settings->get("RecordGameTime").toBool()) { emit runningStatusChanged(running); return; } - if(running) - { + if (running) { m_timeStarted = QDateTime::currentDateTime(); - } - else - { + } else { QDateTime timeEnded = QDateTime::currentDateTime(); qint64 current = settings()->get("totalTimePlayed").toLongLong(); @@ -291,8 +287,7 @@ void BaseInstance::setRunning(bool running) int64_t BaseInstance::totalTimePlayed() const { qint64 current = m_settings->get("totalTimePlayed").toLongLong(); - if(m_isRunning) - { + if (m_isRunning) { QDateTime timeNow = QDateTime::currentDateTime(); return current + m_timeStarted.secsTo(timeNow); } @@ -301,8 +296,7 @@ int64_t BaseInstance::totalTimePlayed() const int64_t BaseInstance::lastTimePlayed() const { - if(m_isRunning) - { + if (m_isRunning) { QDateTime timeNow = QDateTime::currentDateTime(); return m_timeStarted.secsTo(timeNow); } @@ -349,14 +343,14 @@ qint64 BaseInstance::lastLaunch() const void BaseInstance::setLastLaunch(qint64 val) { - //FIXME: if no change, do not set. setting involves saving a file. + // FIXME: if no change, do not set. setting involves saving a file. m_settings->set("lastLaunchTime", val); emit propertiesChanged(this); } void BaseInstance::setNotes(QString val) { - //FIXME: if no change, do not set. setting involves saving a file. + // FIXME: if no change, do not set. setting involves saving a file. m_settings->set("notes", val); } @@ -367,7 +361,7 @@ QString BaseInstance::notes() const void BaseInstance::setIconKey(QString val) { - //FIXME: if no change, do not set. setting involves saving a file. + // FIXME: if no change, do not set. setting involves saving a file. m_settings->set("iconKey", val); emit propertiesChanged(this); } @@ -379,7 +373,7 @@ QString BaseInstance::iconKey() const void BaseInstance::setName(QString val) { - //FIXME: if no change, do not set. setting involves saving a file. + // FIXME: if no change, do not set. setting involves saving a file. m_settings->set("name", val); emit propertiesChanged(this); } diff --git a/launcher/BaseInstance.h b/launcher/BaseInstance.h index 83a8064fa..38dc7c4aa 100644 --- a/launcher/BaseInstance.h +++ b/launcher/BaseInstance.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2022 Jamie Mansfield * @@ -37,24 +37,24 @@ #pragma once #include -#include -#include "QObjectPtr.h" #include -#include +#include #include +#include +#include "QObjectPtr.h" #include "settings/SettingsObject.h" -#include "settings/INIFile.h" #include "BaseVersionList.h" -#include "minecraft/auth/MinecraftAccount.h" #include "MessageLevel.h" +#include "minecraft/auth/MinecraftAccount.h" #include "pathmatcher/IPathMatcher.h" +#include "settings/INIFile.h" #include "net/Mode.h" -#include "minecraft/launch/MinecraftServerTarget.h" #include "RuntimeContext.h" +#include "minecraft/launch/MinecraftServerTarget.h" class QDir; class Task; @@ -72,23 +72,21 @@ typedef std::shared_ptr InstancePtr; * To create a new instance type, create a new class inheriting from this class * and implement the pure virtual functions. */ -class BaseInstance : public QObject, public std::enable_shared_from_this -{ +class BaseInstance : public QObject, public std::enable_shared_from_this { Q_OBJECT -protected: + protected: /// no-touchy! - BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir); + BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir); -public: /* types */ - enum class Status - { + public: /* types */ + enum class Status { Present, - Gone // either nuked or invalidated + Gone // either nuked or invalidated }; -public: + public: /// virtual destructor to make sure the destruction is COMPLETE - virtual ~BaseInstance() {}; + virtual ~BaseInstance() {} virtual void saveNow() = 0; @@ -117,10 +115,7 @@ public: QString instanceRoot() const; /// Path to the instance's game root directory. - virtual QString gameRoot() const - { - return instanceRoot(); - } + virtual QString gameRoot() const { return instanceRoot(); } /// Path to the instance's mods directory. virtual QString modsRoot() const = 0; @@ -151,15 +146,12 @@ public: void copyManagedPack(BaseInstance& other); /// guess log level from a line of game log - virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString &line, MessageLevel::Enum level) - { - return level; - }; + virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString& line, MessageLevel::Enum level) { return level; } virtual QStringList extraArguments(); /// Traits. Normally inside the version, depends on instance implementation. - virtual QSet traits() const = 0; + virtual QSet traits() const = 0; /** * Gets the time that the instance was last launched. @@ -189,8 +181,7 @@ public: virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0; /// returns a valid launcher (task container) - virtual shared_qobject_ptr createLaunchTask( - AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0; + virtual shared_qobject_ptr createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0; /// returns the current launch task (if any) shared_qobject_ptr getLaunchTask(); @@ -222,45 +213,30 @@ public: virtual QString typeName() const = 0; void updateRuntimeContext(); - RuntimeContext runtimeContext() const - { - return m_runtimeContext; - } + RuntimeContext runtimeContext() const { return m_runtimeContext; } - bool hasVersionBroken() const - { - return m_hasBrokenVersion; - } + bool hasVersionBroken() const { return m_hasBrokenVersion; } void setVersionBroken(bool value) { - if(m_hasBrokenVersion != value) - { + if (m_hasBrokenVersion != value) { m_hasBrokenVersion = value; emit propertiesChanged(this); } } - bool hasUpdateAvailable() const - { - return m_hasUpdate; - } + bool hasUpdateAvailable() const { return m_hasUpdate; } void setUpdateAvailable(bool value) { - if(m_hasUpdate != value) - { + if (m_hasUpdate != value) { m_hasUpdate = value; emit propertiesChanged(this); } } - bool hasCrashed() const - { - return m_crashed; - } + bool hasCrashed() const { return m_crashed; } void setCrashed(bool value) { - if(m_crashed != value) - { + if (m_crashed != value) { m_crashed = value; emit propertiesChanged(this); } @@ -288,19 +264,19 @@ public: bool removeLinkedInstanceId(const QString& id); bool isLinkedToInstanceId(const QString& id) const; -protected: + protected: void changeStatus(Status newStatus); - SettingsObjectPtr globalSettings() const { return m_global_settings.lock(); }; + SettingsObjectPtr globalSettings() const { return m_global_settings.lock(); } bool isSpecificSettingsLoaded() const { return m_specific_settings_loaded; } void setSpecificSettingsLoaded(bool loaded) { m_specific_settings_loaded = loaded; } -signals: + signals: /*! * \brief Signal emitted when properties relevant to the instance view change */ - void propertiesChanged(BaseInstance *inst); + void propertiesChanged(BaseInstance* inst); void launchTaskChanged(shared_qobject_ptr); @@ -308,10 +284,10 @@ signals: void statusChanged(Status from, Status to); -protected slots: + protected slots: void iconUpdated(QString key); -protected: /* data */ + protected: /* data */ QString m_rootDir; SettingsObjectPtr m_settings; // InstanceFlags m_flags; @@ -320,7 +296,7 @@ protected: /* data */ QDateTime m_timeStarted; RuntimeContext m_runtimeContext; -private: /* data */ + private: /* data */ Status m_status = Status::Present; bool m_crashed = false; bool m_hasUpdate = false; @@ -328,9 +304,8 @@ private: /* data */ SettingsObjectWeakPtr m_global_settings; bool m_specific_settings_loaded = false; - }; Q_DECLARE_METATYPE(shared_qobject_ptr) -//Q_DECLARE_METATYPE(BaseInstance::InstanceFlag) -//Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags) +// Q_DECLARE_METATYPE(BaseInstance::InstanceFlag) +// Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags) diff --git a/launcher/BaseVersion.h b/launcher/BaseVersion.h index ca0e45027..2837ff3a9 100644 --- a/launcher/BaseVersion.h +++ b/launcher/BaseVersion.h @@ -15,16 +15,15 @@ #pragma once -#include -#include #include +#include +#include /*! * An abstract base class for versions. */ -class BaseVersion -{ -public: +class BaseVersion { + public: using Ptr = std::shared_ptr; virtual ~BaseVersion() {} /*! @@ -44,15 +43,8 @@ public: * the kind of version this is (Stable, Beta, Snapshot, whatever) */ virtual QString typeString() const = 0; - - virtual bool operator<(BaseVersion &a) - { - return name() < a.name(); - }; - virtual bool operator>(BaseVersion &a) - { - return name() > a.name(); - }; + virtual bool operator<(BaseVersion& a) { return name() < a.name(); } + virtual bool operator>(BaseVersion& a) { return name() > a.name(); } }; Q_DECLARE_METATYPE(BaseVersion::Ptr) diff --git a/launcher/BaseVersionList.cpp b/launcher/BaseVersionList.cpp index dc95e7ea5..e11560d5e 100644 --- a/launcher/BaseVersionList.cpp +++ b/launcher/BaseVersionList.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -36,14 +36,11 @@ #include "BaseVersionList.h" #include "BaseVersion.h" -BaseVersionList::BaseVersionList(QObject *parent) : QAbstractListModel(parent) -{ -} +BaseVersionList::BaseVersionList(QObject* parent) : QAbstractListModel(parent) {} -BaseVersion::Ptr BaseVersionList::findVersion(const QString &descriptor) +BaseVersion::Ptr BaseVersionList::findVersion(const QString& descriptor) { - for (int i = 0; i < count(); i++) - { + for (int i = 0; i < count(); i++) { if (at(i)->descriptor() == descriptor) return at(i); } @@ -58,7 +55,7 @@ BaseVersion::Ptr BaseVersionList::getRecommended() const return at(0); } -QVariant BaseVersionList::data(const QModelIndex &index, int role) const +QVariant BaseVersionList::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); @@ -68,37 +65,36 @@ QVariant BaseVersionList::data(const QModelIndex &index, int role) const BaseVersion::Ptr version = at(index.row()); - switch (role) - { - case VersionPointerRole: - return QVariant::fromValue(version); + switch (role) { + case VersionPointerRole: + return QVariant::fromValue(version); - case VersionRole: - return version->name(); + case VersionRole: + return version->name(); - case VersionIdRole: - return version->descriptor(); + case VersionIdRole: + return version->descriptor(); - case TypeRole: - return version->typeString(); + case TypeRole: + return version->typeString(); - default: - return QVariant(); + default: + return QVariant(); } } BaseVersionList::RoleList BaseVersionList::providesRoles() const { - return {VersionPointerRole, VersionRole, VersionIdRole, TypeRole}; + return { VersionPointerRole, VersionRole, VersionIdRole, TypeRole }; } -int BaseVersionList::rowCount(const QModelIndex &parent) const +int BaseVersionList::rowCount(const QModelIndex& parent) const { // Return count return parent.isValid() ? 0 : count(); } -int BaseVersionList::columnCount(const QModelIndex &parent) const +int BaseVersionList::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : 1; } diff --git a/launcher/BaseVersionList.h b/launcher/BaseVersionList.h index 31f29022a..fe1550c21 100644 --- a/launcher/BaseVersionList.h +++ b/launcher/BaseVersionList.h @@ -15,13 +15,13 @@ #pragma once +#include #include #include -#include #include "BaseVersion.h" -#include "tasks/Task.h" #include "QObjectPtr.h" +#include "tasks/Task.h" /*! * \brief Class that each instance type's version list derives from. @@ -35,12 +35,10 @@ * all have a default implementation, but they can be overridden by plugins to * change the behavior of the list. */ -class BaseVersionList : public QAbstractListModel -{ +class BaseVersionList : public QAbstractListModel { Q_OBJECT -public: - enum ModelRoles - { + public: + enum ModelRoles { VersionPointerRole = Qt::UserRole, VersionRole, VersionIdRole, @@ -55,7 +53,7 @@ public: }; typedef QList RoleList; - explicit BaseVersionList(QObject *parent = 0); + explicit BaseVersionList(QObject* parent = 0); /*! * \brief Gets a task that will reload the version list. @@ -66,7 +64,7 @@ public: virtual Task::Ptr getLoadTask() = 0; //! Checks whether or not the list is loaded. If this returns false, the list should be - //loaded. + // loaded. virtual bool isLoaded() = 0; //! Gets the version at the given index. @@ -76,9 +74,9 @@ public: virtual int count() const = 0; //////// List Model Functions //////// - QVariant data(const QModelIndex &index, int role) const override; - int rowCount(const QModelIndex &parent) const override; - int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex& index, int role) const override; + int rowCount(const QModelIndex& parent) const override; + int columnCount(const QModelIndex& parent) const override; QHash roleNames() const override; //! which roles are provided by this version list? @@ -90,7 +88,7 @@ public: * \return A const pointer to the version with the given descriptor. NULL if * one doesn't exist. */ - virtual BaseVersion::Ptr findVersion(const QString &descriptor); + virtual BaseVersion::Ptr findVersion(const QString& descriptor); /*! * \brief Gets the recommended version from this list @@ -103,8 +101,7 @@ public: */ virtual void sortVersions() = 0; -protected -slots: + protected slots: /*! * Updates this list with the given list of versions. * This is done by copying each version in the given list and inserting it diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index af3bc28e3..18e0acab1 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -136,6 +136,15 @@ set(NET_SOURCES net/Validator.h net/Upload.cpp net/Upload.h + net/HeaderProxy.h + net/RawHeaderProxy.h + net/ApiHeaderProxy.h + net/ApiDownload.h + net/ApiDownload.cpp + net/ApiUpload.cpp + net/ApiUpload.h + net/NetRequest.cpp + net/NetRequest.h ) # Game launch logic @@ -262,8 +271,6 @@ set(MINECRAFT_SOURCES minecraft/launch/CreateGameFolders.h minecraft/launch/ModMinecraftJar.cpp minecraft/launch/ModMinecraftJar.h - minecraft/launch/DirectJavaLaunch.cpp - minecraft/launch/DirectJavaLaunch.h minecraft/launch/ExtractNatives.cpp minecraft/launch/ExtractNatives.h minecraft/launch/LauncherPartLaunch.cpp @@ -487,6 +494,9 @@ set(API_SOURCES modplatform/helpers/HashUtils.cpp modplatform/helpers/OverrideUtils.h modplatform/helpers/OverrideUtils.cpp + + modplatform/helpers/ExportToModList.h + modplatform/helpers/ExportToModList.cpp ) set(FTB_SOURCES @@ -498,6 +508,11 @@ set(FTB_SOURCES modplatform/legacy_ftb/PrivatePackManager.cpp modplatform/legacy_ftb/PackHelpers.h + + modplatform/import_ftb/PackInstallTask.h + modplatform/import_ftb/PackInstallTask.cpp + modplatform/import_ftb/PackHelpers.h + modplatform/import_ftb/PackHelpers.cpp ) set(FLAME_SOURCES @@ -560,6 +575,9 @@ set(ATLAUNCHER_SOURCES ) set(LINKEXE_SOURCES + WindowsConsole.cpp + WindowsConsole.h + filelink/FileLink.h filelink/FileLink.cpp FileSystem.h @@ -665,7 +683,7 @@ set(LOGIC_SOURCES ${ATLAUNCHER_SOURCES} ) -if(APPLE) +if(APPLE AND Launcher_ENABLE_UPDATER) set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES}) endif() @@ -756,8 +774,12 @@ SET(LAUNCHER_SOURCES ui/themes/ITheme.h ui/themes/SystemTheme.cpp ui/themes/SystemTheme.h + ui/themes/IconTheme.cpp + ui/themes/IconTheme.h ui/themes/ThemeManager.cpp ui/themes/ThemeManager.h + ui/themes/CatPack.cpp + ui/themes/CatPack.h # Processes LaunchController.h @@ -867,6 +889,11 @@ SET(LAUNCHER_SOURCES ui/pages/modplatform/legacy_ftb/ListModel.h ui/pages/modplatform/legacy_ftb/ListModel.cpp + ui/pages/modplatform/import_ftb/ImportFTBPage.cpp + ui/pages/modplatform/import_ftb/ImportFTBPage.h + ui/pages/modplatform/import_ftb/ListModel.h + ui/pages/modplatform/import_ftb/ListModel.cpp + ui/pages/modplatform/flame/FlameModel.cpp ui/pages/modplatform/flame/FlameModel.h ui/pages/modplatform/flame/FlamePage.cpp @@ -911,6 +938,8 @@ SET(LAUNCHER_SOURCES ui/dialogs/ExportInstanceDialog.h ui/dialogs/ExportPackDialog.cpp ui/dialogs/ExportPackDialog.h + ui/dialogs/ExportToModListDialog.cpp + ui/dialogs/ExportToModListDialog.h ui/dialogs/IconPickerDialog.cpp ui/dialogs/IconPickerDialog.h ui/dialogs/ImportResourceDialog.cpp @@ -947,6 +976,8 @@ SET(LAUNCHER_SOURCES ui/dialogs/ChooseProviderDialog.cpp ui/dialogs/ModUpdateDialog.cpp ui/dialogs/ModUpdateDialog.h + ui/dialogs/InstallLoaderDialog.cpp + ui/dialogs/InstallLoaderDialog.h # GUI - widgets ui/widgets/Common.cpp @@ -1011,6 +1042,14 @@ SET(LAUNCHER_SOURCES ui/instanceview/VisualGroup.h ) +if(WIN32) + set(LAUNCHER_SOURCES + WindowsConsole.cpp + WindowsConsole.h + ${LAUNCHER_SOURCES} + ) +endif() + qt_wrap_ui(LAUNCHER_UI ui/MainWindow.ui ui/setupwizard/PasteWizardPage.ui @@ -1039,6 +1078,7 @@ qt_wrap_ui(LAUNCHER_UI ui/pages/modplatform/ResourcePage.ui ui/pages/modplatform/flame/FlamePage.ui ui/pages/modplatform/legacy_ftb/Page.ui + ui/pages/modplatform/import_ftb/ImportFTBPage.ui ui/pages/modplatform/ImportPage.ui ui/pages/modplatform/modrinth/ModrinthPage.ui ui/pages/modplatform/technic/TechnicPage.ui @@ -1058,6 +1098,7 @@ qt_wrap_ui(LAUNCHER_UI ui/dialogs/SkinUploadDialog.ui ui/dialogs/ExportInstanceDialog.ui ui/dialogs/ExportPackDialog.ui + ui/dialogs/ExportToModListDialog.ui ui/dialogs/IconPickerDialog.ui ui/dialogs/ImportResourceDialog.ui ui/dialogs/MSALoginDialog.ui @@ -1092,8 +1133,15 @@ if(WIN32) set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC}) endif() +include(CompilerWarnings) + # Add executable add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES}) +set_project_warnings(Launcher_logic + "${Launcher_MSVC_WARNINGS}" + "${Launcher_CLANG_WARNINGS}" + "${Launcher_GCC_WARNINGS}") +target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION) target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(Launcher_logic systeminfo @@ -1133,17 +1181,23 @@ if(APPLE) set(CMAKE_MACOSX_RPATH 1) set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/") - file(DOWNLOAD ${MACOSX_SPARKLE_DOWNLOAD_URL} ${CMAKE_BINARY_DIR}/Sparkle.tar.xz EXPECTED_HASH SHA256=${MACOSX_SPARKLE_SHA256}) - file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle) + if(Launcher_ENABLE_UPDATER) + file(DOWNLOAD ${MACOSX_SPARKLE_DOWNLOAD_URL} ${CMAKE_BINARY_DIR}/Sparkle.tar.xz EXPECTED_HASH SHA256=${MACOSX_SPARKLE_SHA256}) + file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle) + + find_library(SPARKLE_FRAMEWORK Sparkle "${CMAKE_BINARY_DIR}/frameworks/Sparkle") + add_compile_definitions(SPARKLE_ENABLED) + endif() - find_library(SPARKLE_FRAMEWORK Sparkle "${CMAKE_BINARY_DIR}/frameworks/Sparkle") target_link_libraries(Launcher_logic "-framework AppKit" "-framework Carbon" "-framework Foundation" "-framework ApplicationServices" ) - target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK}) + if(Launcher_ENABLE_UPDATER) + target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK}) + endif() endif() target_link_libraries(Launcher_logic) @@ -1172,6 +1226,11 @@ install(TARGETS ${Launcher_Name} if(WIN32) add_library(filelink_logic STATIC ${LINKEXE_SOURCES}) + set_project_warnings(filelink_logic + "${Launcher_MSVC_WARNINGS}" + "${Launcher_CLANG_WARNINGS}" + "${Launcher_GCC_WARNINGS}") + target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(filelink_logic systeminfo @@ -1205,7 +1264,7 @@ if(WIN32) ) endif() -if (UNIX AND APPLE) +if (UNIX AND APPLE AND Launcher_ENABLE_UPDATER) # Add Sparkle updater # It has to be copied here instead of just allowing fixup_bundle to install it, otherwise essential parts of # the framework aren't installed diff --git a/launcher/Commandline.cpp b/launcher/Commandline.cpp index 6d97918de..8489fb745 100644 --- a/launcher/Commandline.cpp +++ b/launcher/Commandline.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -41,8 +41,7 @@ * @file libutil/src/cmdutils.cpp */ -namespace Commandline -{ +namespace Commandline { // commandline splitter QStringList splitArgs(QString args) @@ -51,19 +50,15 @@ QStringList splitArgs(QString args) QString current; bool escape = false; QChar inquotes; - for (int i = 0; i < args.length(); i++) - { + for (int i = 0; i < args.length(); i++) { QChar cchar = args.at(i); // \ escaped - if (escape) - { + if (escape) { current += cchar; escape = false; // in "quotes" - } - else if (!inquotes.isNull()) - { + } else if (!inquotes.isNull()) { if (cchar == '\\') escape = true; else if (cchar == inquotes) @@ -71,18 +66,13 @@ QStringList splitArgs(QString args) else current += cchar; // otherwise - } - else - { - if (cchar == ' ') - { - if (!current.isEmpty()) - { + } else { + if (cchar == ' ') { + if (!current.isEmpty()) { argv << current; current.clear(); } - } - else if (cchar == '"' || cchar == '\'') + } else if (cchar == '"' || cchar == '\'') inquotes = cchar; else current += cchar; @@ -92,4 +82,4 @@ QStringList splitArgs(QString args) argv << current; return argv; } -} +} // namespace Commandline diff --git a/launcher/Commandline.h b/launcher/Commandline.h index 8bd791808..77c557de2 100644 --- a/launcher/Commandline.h +++ b/launcher/Commandline.h @@ -25,8 +25,7 @@ * @brief commandline parsing and processing utilities */ -namespace Commandline -{ +namespace Commandline { /** * @brief split a string into argv items like a shell would do @@ -34,4 +33,4 @@ namespace Commandline * @return a QStringList containing all arguments */ QStringList splitArgs(QString args); -} +} // namespace Commandline diff --git a/launcher/DefaultVariable.h b/launcher/DefaultVariable.h index 5c069bd38..b082091c7 100644 --- a/launcher/DefaultVariable.h +++ b/launcher/DefaultVariable.h @@ -1,33 +1,21 @@ #pragma once template -class DefaultVariable -{ -public: - DefaultVariable(const T & value) - { - defaultValue = value; - } - DefaultVariable & operator =(const T & value) +class DefaultVariable { + public: + DefaultVariable(const T& value) { defaultValue = value; } + DefaultVariable& operator=(const T& value) { currentValue = value; is_default = currentValue == defaultValue; is_explicit = true; return *this; } - operator const T &() const - { - return is_default ? defaultValue : currentValue; - } - bool isDefault() const - { - return is_default; - } - bool isExplicit() const - { - return is_explicit; - } -private: + operator const T&() const { return is_default ? defaultValue : currentValue; } + bool isDefault() const { return is_default; } + bool isExplicit() const { return is_explicit; } + + private: T currentValue; T defaultValue; bool is_default = true; diff --git a/launcher/DesktopServices.cpp b/launcher/DesktopServices.cpp index 2984a1b4f..004e5e085 100644 --- a/launcher/DesktopServices.cpp +++ b/launcher/DesktopServices.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 dada513 * * This program is free software: you can redistribute it and/or modify @@ -33,40 +33,37 @@ * limitations under the License. */ #include "DesktopServices.h" -#include -#include -#include #include +#include +#include +#include /** * This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing. */ #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) -#include #include #include #include +#include template -bool IndirectOpen(T callable, qint64 *pid_forked = nullptr) +bool IndirectOpen(T callable, qint64* pid_forked = nullptr) { auto pid = fork(); - if(pid_forked) - { - if(pid > 0) + if (pid_forked) { + if (pid > 0) *pid_forked = pid; else *pid_forked = 0; } - if(pid == -1) - { + if (pid == -1) { qWarning() << "IndirectOpen failed to fork: " << errno; return false; } // child - do the stuff - if(pid == 0) - { + if (pid == 0) { // unset all this garbage so it doesn't get passed to the child process qunsetenv("LD_PRELOAD"); qunsetenv("LD_LIBRARY_PATH"); @@ -82,19 +79,14 @@ bool IndirectOpen(T callable, qint64 *pid_forked = nullptr) // die. now. do not clean up anything, it would just hang forever. _exit(status ? 0 : 1); - } - else - { - //parent - assume it worked. + } else { + // parent - assume it worked. int status; - while (waitpid(pid, &status, 0)) - { - if(WIFEXITED(status)) - { + while (waitpid(pid, &status, 0)) { + if (WIFEXITED(status)) { return WEXITSTATUS(status) == 0; } - if(WIFSIGNALED(status)) - { + if (WIFSIGNALED(status)) { return false; } } @@ -104,26 +96,19 @@ bool IndirectOpen(T callable, qint64 *pid_forked = nullptr) #endif namespace DesktopServices { -bool openDirectory(const QString &path, bool ensureExists) +bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists) { qDebug() << "Opening directory" << path; QDir parentPath; QDir dir(path); - if (!dir.exists()) - { + if (ensureExists && !dir.exists()) { parentPath.mkpath(dir.absolutePath()); } - auto f = [&]() - { - return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); - }; + auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); }; #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) - if(!isFlatpak()) - { + if (!isSandbox()) { return IndirectOpen(f); - } - else - { + } else { return f(); } #else @@ -131,20 +116,14 @@ bool openDirectory(const QString &path, bool ensureExists) #endif } -bool openFile(const QString &path) +bool openFile(const QString& path) { qDebug() << "Opening file" << path; - auto f = [&]() - { - return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); - }; + auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); }; #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) - if(!isFlatpak()) - { + if (!isSandbox()) { return IndirectOpen(f); - } - else - { + } else { return f(); } #else @@ -152,41 +131,29 @@ bool openFile(const QString &path) #endif } -bool openFile(const QString &application, const QString &path, const QString &workingDirectory, qint64 *pid) +bool openFile(const QString& application, const QString& path, const QString& workingDirectory, qint64* pid) { qDebug() << "Opening file" << path << "using" << application; #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave - if(!isFlatpak()) - { - return IndirectOpen([&]() - { - return QProcess::startDetached(application, QStringList() << path, workingDirectory); - }, pid); - } - else - { - return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid); + if (!isSandbox()) { + return IndirectOpen([&]() { return QProcess::startDetached(application, QStringList() << path, workingDirectory); }, pid); + } else { + return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid); } #else return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid); #endif } -bool run(const QString &application, const QStringList &args, const QString &workingDirectory, qint64 *pid) +bool run(const QString& application, const QStringList& args, const QString& workingDirectory, qint64* pid) { qDebug() << "Running" << application << "with args" << args.join(' '); #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) - if(!isFlatpak()) - { - // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave - return IndirectOpen([&]() - { - return QProcess::startDetached(application, args, workingDirectory); - }, pid); - } - else - { + if (!isSandbox()) { + // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave + return IndirectOpen([&]() { return QProcess::startDetached(application, args, workingDirectory); }, pid); + } else { return QProcess::startDetached(application, args, workingDirectory, pid); } #else @@ -194,20 +161,14 @@ bool run(const QString &application, const QStringList &args, const QString &wor #endif } -bool openUrl(const QUrl &url) +bool openUrl(const QUrl& url) { qDebug() << "Opening URL" << url.toString(); - auto f = [&]() - { - return QDesktopServices::openUrl(url); - }; + auto f = [&]() { return QDesktopServices::openUrl(url); }; #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) - if(!isFlatpak()) - { + if (!isSandbox()) { return IndirectOpen(f); - } - else - { + } else { return f(); } #else @@ -224,4 +185,18 @@ bool isFlatpak() #endif } +bool isSnap() +{ +#ifdef Q_OS_LINUX + return getenv("SNAP"); +#else + return false; +#endif } + +bool isSandbox() +{ + return isSnap() || isFlatpak(); +} + +} // namespace DesktopServices diff --git a/launcher/DesktopServices.h b/launcher/DesktopServices.h index 21c9cae0b..151db5557 100644 --- a/launcher/DesktopServices.h +++ b/launcher/DesktopServices.h @@ -1,38 +1,50 @@ #pragma once -#include #include +#include /** * This wraps around QDesktopServices and adds workarounds where needed * Use this instead of QDesktopServices! */ -namespace DesktopServices -{ - /** - * Open a file in whatever application is applicable - */ - bool openFile(const QString &path); +namespace DesktopServices { +/** + * Open a file in whatever application is applicable + */ +bool openFile(const QString& path); - /** - * Open a file in the specified application - */ - bool openFile(const QString &application, const QString &path, const QString & workingDirectory = QString(), qint64 *pid = 0); +/** + * Open a file in the specified application + */ +bool openFile(const QString& application, const QString& path, const QString& workingDirectory = QString(), qint64* pid = 0); - /** - * Run an application - */ - bool run(const QString &application,const QStringList &args, const QString & workingDirectory = QString(), qint64 *pid = 0); +/** + * Run an application + */ +bool run(const QString& application, const QStringList& args, const QString& workingDirectory = QString(), qint64* pid = 0); - /** - * Open a directory - */ - bool openDirectory(const QString &path, bool ensureExists = false); +/** + * Open a directory + */ +bool openDirectory(const QString& path, bool ensureExists = false); - /** - * Open the URL, most likely in a browser. Maybe. - */ - bool openUrl(const QUrl &url); +/** + * Open the URL, most likely in a browser. Maybe. + */ +bool openUrl(const QUrl& url); - bool isFlatpak(); -} +/** + * Determine whether the launcher is running in a Flatpak environment + */ +bool isFlatpak(); + +/** + * Determine whether the launcher is running in a Snap environment + */ +bool isSnap(); + +/** + * Determine whether the launcher is running in a sandboxed (Flatpak or Snap) environment + */ +bool isSandbox(); +} // namespace DesktopServices diff --git a/launcher/Exception.h b/launcher/Exception.h index fe0b86b5f..ef1e4e0d8 100644 --- a/launcher/Exception.h +++ b/launcher/Exception.h @@ -2,31 +2,18 @@ #pragma once -#include #include +#include #include -class Exception : public std::exception -{ -public: - Exception(const QString &message) : std::exception(), m_message(message) - { - qCritical() << "Exception:" << message; - } - Exception(const Exception &other) - : std::exception(), m_message(other.cause()) - { - } +class Exception : public std::exception { + public: + Exception(const QString& message) : std::exception(), m_message(message) { qCritical() << "Exception:" << message; } + Exception(const Exception& other) : std::exception(), m_message(other.cause()) {} virtual ~Exception() noexcept {} - const char *what() const noexcept - { - return m_message.toLatin1().constData(); - } - QString cause() const - { - return m_message; - } + const char* what() const noexcept { return m_message.toLatin1().constData(); } + QString cause() const { return m_message; } -private: + private: QString m_message; }; diff --git a/launcher/ExponentialSeries.h b/launcher/ExponentialSeries.h index a9487f0a3..b1fca4359 100644 --- a/launcher/ExponentialSeries.h +++ b/launcher/ExponentialSeries.h @@ -4,31 +4,24 @@ template inline void clamp(T& current, T min, T max) { - if (current < min) - { + if (current < min) { current = min; - } - else if(current > max) - { + } else if (current > max) { current = max; } } // List of numbers from min to max. Next is exponent times bigger than previous. -class ExponentialSeries -{ -public: +class ExponentialSeries { + public: ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2) { m_current = m_min = min; m_max = max; m_exponent = exponent; } - void reset() - { - m_current = m_min; - } + void reset() { m_current = m_min; } unsigned operator()() { unsigned retval = m_current; diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 4538702f2..defb2cb9e 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -779,7 +779,8 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri } #if defined(Q_OS_MACOS) // Create the Application - QDir applicationDirectory = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + "/" + BuildConfig.LAUNCHER_NAME + " Instances/"; + QDir applicationDirectory = + QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + "/" + BuildConfig.LAUNCHER_NAME + " Instances/"; if (!applicationDirectory.mkpath(".")) { qWarning() << "Couldn't create application directory"; @@ -843,7 +844,9 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri " CFBundleIconFile\n" " Icon.icns\n" " CFBundleName\n" - " " << name << "\n" // Name of the application + " " + << name + << "\n" // Name of the application " CFBundlePackageType\n" " APPL\n" " CFBundleShortVersionString\n" diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index f8a82baef..bfed576c1 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -43,10 +43,10 @@ #include #include -#include #include #include #include +#include #include namespace FS { @@ -112,8 +112,8 @@ class copy : public QObject { bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } - int totalCopied() { return m_copied; } - int totalFailed() { return m_failedPaths.length(); } + qsizetype totalCopied() { return m_copied; } + qsizetype totalFailed() { return m_failedPaths.length(); } QStringList failed() { return m_failedPaths; } signals: @@ -130,7 +130,7 @@ class copy : public QObject { bool m_whitelist = false; QDir m_src; QDir m_dst; - int m_copied; + qsizetype m_copied; QStringList m_failedPaths; }; @@ -365,25 +365,24 @@ enum class FilesystemType { * QMap is ordered * */ -static const QMap s_filesystem_type_names = { - {FilesystemType::FAT, { "FAT" }}, - {FilesystemType::NTFS, { "NTFS" }}, - {FilesystemType::REFS, { "REFS" }}, - {FilesystemType::EXT_2_OLD, { "EXT_2_OLD", "EXT2_OLD" }}, - {FilesystemType::EXT_2_3_4, { "EXT2/3/4", "EXT_2_3_4", "EXT2", "EXT3", "EXT4" }}, - {FilesystemType::EXT, { "EXT" }}, - {FilesystemType::XFS, { "XFS" }}, - {FilesystemType::BTRFS, { "BTRFS" }}, - {FilesystemType::NFS, { "NFS" }}, - {FilesystemType::ZFS, { "ZFS" }}, - {FilesystemType::APFS, { "APFS" }}, - {FilesystemType::HFS, { "HFS" }}, - {FilesystemType::HFSPLUS, { "HFSPLUS" }}, - {FilesystemType::HFSX, { "HFSX" }}, - {FilesystemType::FUSEBLK, { "FUSEBLK" }}, - {FilesystemType::F2FS, { "F2FS" }}, - {FilesystemType::UNKNOWN, { "UNKNOWN" }} -}; +static const QMap s_filesystem_type_names = { { FilesystemType::FAT, { "FAT" } }, + { FilesystemType::NTFS, { "NTFS" } }, + { FilesystemType::REFS, { "REFS" } }, + { FilesystemType::EXT_2_OLD, { "EXT_2_OLD", "EXT2_OLD" } }, + { FilesystemType::EXT_2_3_4, + { "EXT2/3/4", "EXT_2_3_4", "EXT2", "EXT3", "EXT4" } }, + { FilesystemType::EXT, { "EXT" } }, + { FilesystemType::XFS, { "XFS" } }, + { FilesystemType::BTRFS, { "BTRFS" } }, + { FilesystemType::NFS, { "NFS" } }, + { FilesystemType::ZFS, { "ZFS" } }, + { FilesystemType::APFS, { "APFS" } }, + { FilesystemType::HFS, { "HFS" } }, + { FilesystemType::HFSPLUS, { "HFSPLUS" } }, + { FilesystemType::HFSX, { "HFSX" } }, + { FilesystemType::FUSEBLK, { "FUSEBLK" } }, + { FilesystemType::F2FS, { "F2FS" } }, + { FilesystemType::UNKNOWN, { "UNKNOWN" } } }; /** * @brief Get the string name of Filesystem enum object @@ -475,8 +474,8 @@ class clone : public QObject { bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } - int totalCloned() { return m_cloned; } - int totalFailed() { return m_failedClones.length(); } + qsizetype totalCloned() { return m_cloned; } + qsizetype totalFailed() { return m_failedClones.length(); } QList> failed() { return m_failedClones; } @@ -492,7 +491,7 @@ class clone : public QObject { bool m_whitelist = false; QDir m_src; QDir m_dst; - int m_cloned; + qsizetype m_cloned; QList> m_failedClones; }; diff --git a/launcher/Filter.cpp b/launcher/Filter.cpp index c65ca0ceb..fc1c42344 100644 --- a/launcher/Filter.cpp +++ b/launcher/Filter.cpp @@ -1,28 +1,33 @@ #include "Filter.h" -Filter::~Filter(){} +Filter::~Filter() {} -ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern){} -ContainsFilter::~ContainsFilter(){} +ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern) {} +ContainsFilter::~ContainsFilter() {} bool ContainsFilter::accepts(const QString& value) { return value.contains(pattern); } -ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern){} -ExactFilter::~ExactFilter(){} +ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern) {} +ExactFilter::~ExactFilter() {} bool ExactFilter::accepts(const QString& value) { return value == pattern; } -RegexpFilter::RegexpFilter(const QString& regexp, bool invert) - :invert(invert) +ExactIfPresentFilter::ExactIfPresentFilter(const QString& pattern) : pattern(pattern) {} +bool ExactIfPresentFilter::accepts(const QString& value) +{ + return value.isEmpty() || value == pattern; +} + +RegexpFilter::RegexpFilter(const QString& regexp, bool invert) : invert(invert) { pattern.setPattern(regexp); pattern.optimize(); } -RegexpFilter::~RegexpFilter(){} +RegexpFilter::~RegexpFilter() {} bool RegexpFilter::accepts(const QString& value) { auto match = pattern.match(value); diff --git a/launcher/Filter.h b/launcher/Filter.h index b55067acb..089c844d4 100644 --- a/launcher/Filter.h +++ b/launcher/Filter.h @@ -1,42 +1,51 @@ #pragma once -#include #include +#include -class Filter -{ -public: +class Filter { + public: virtual ~Filter(); - virtual bool accepts(const QString & value) = 0; + virtual bool accepts(const QString& value) = 0; }; -class ContainsFilter: public Filter -{ -public: - ContainsFilter(const QString &pattern); +class ContainsFilter : public Filter { + public: + ContainsFilter(const QString& pattern); virtual ~ContainsFilter(); - bool accepts(const QString & value) override; -private: + bool accepts(const QString& value) override; + + private: QString pattern; }; -class ExactFilter: public Filter -{ -public: - ExactFilter(const QString &pattern); +class ExactFilter : public Filter { + public: + ExactFilter(const QString& pattern); virtual ~ExactFilter(); - bool accepts(const QString & value) override; -private: + bool accepts(const QString& value) override; + + private: QString pattern; }; -class RegexpFilter: public Filter -{ -public: - RegexpFilter(const QString ®exp, bool invert); +class ExactIfPresentFilter : public Filter { + public: + ExactIfPresentFilter(const QString& pattern); + ~ExactIfPresentFilter() override = default; + bool accepts(const QString& value) override; + + private: + QString pattern; +}; + +class RegexpFilter : public Filter { + public: + RegexpFilter(const QString& regexp, bool invert); virtual ~RegexpFilter(); - bool accepts(const QString & value) override; -private: + bool accepts(const QString& value) override; + + private: QRegularExpression pattern; bool invert = false; }; diff --git a/launcher/GZip.cpp b/launcher/GZip.cpp index e36dc8a49..1c2539e08 100644 --- a/launcher/GZip.cpp +++ b/launcher/GZip.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -37,10 +37,9 @@ #include #include -bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes) +bool GZip::unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes) { - if (compressedBytes.size() == 0) - { + if (compressedBytes.size() == 0) { uncompressedBytes = compressedBytes; return true; } @@ -51,42 +50,37 @@ bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedByte z_stream strm; memset(&strm, 0, sizeof(strm)); - strm.next_in = (Bytef *)compressedBytes.data(); + strm.next_in = (Bytef*)compressedBytes.data(); strm.avail_in = compressedBytes.size(); bool done = false; - if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) - { + if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) { return false; } int err = Z_OK; - while (!done) - { + while (!done) { // If our output buffer is too small - if (strm.total_out >= uncompLength) - { + if (strm.total_out >= uncompLength) { uncompressedBytes.resize(uncompLength * 2); uncompLength *= 2; } - strm.next_out = reinterpret_cast((uncompressedBytes.data() + strm.total_out)); + strm.next_out = reinterpret_cast((uncompressedBytes.data() + strm.total_out)); strm.avail_out = uncompLength - strm.total_out; // Inflate another chunk. err = inflate(&strm, Z_SYNC_FLUSH); if (err == Z_STREAM_END) done = true; - else if (err != Z_OK) - { + else if (err != Z_OK) { break; } } - if (inflateEnd(&strm) != Z_OK || !done) - { + if (inflateEnd(&strm) != Z_OK || !done) { return false; } @@ -94,10 +88,9 @@ bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedByte return true; } -bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes) +bool GZip::zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes) { - if (uncompressedBytes.size() == 0) - { + if (uncompressedBytes.size() == 0) { compressedBytes = uncompressedBytes; return true; } @@ -109,8 +102,7 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes) z_stream zs; memset(&zs, 0, sizeof(zs)); - if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK) - { + if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK) { return false; } @@ -122,11 +114,9 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes) unsigned offset = 0; unsigned temp = 0; - do - { + do { auto remaining = compressedBytes.size() - offset; - if(remaining < 1) - { + if (remaining < 1) { compressedBytes.resize(compressedBytes.size() * 2); } zs.next_out = reinterpret_cast((compressedBytes.data() + offset)); @@ -137,13 +127,11 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes) compressedBytes.resize(offset); - if (deflateEnd(&zs) != Z_OK) - { + if (deflateEnd(&zs) != Z_OK) { return false; } - if (ret != Z_STREAM_END) - { + if (ret != Z_STREAM_END) { return false; } return true; diff --git a/launcher/GZip.h b/launcher/GZip.h index 7d4b1c332..0bdb70407 100644 --- a/launcher/GZip.h +++ b/launcher/GZip.h @@ -1,10 +1,8 @@ #pragma once #include -class GZip -{ -public: - static bool unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes); - static bool zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes); +class GZip { + public: + static bool unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes); + static bool zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes); }; - diff --git a/launcher/InstanceCopyPrefs.cpp b/launcher/InstanceCopyPrefs.cpp index 0650002b8..63c200cc4 100644 --- a/launcher/InstanceCopyPrefs.cpp +++ b/launcher/InstanceCopyPrefs.cpp @@ -6,17 +6,10 @@ bool InstanceCopyPrefs::allTrue() const { - return copySaves && - keepPlaytime && - copyGameOptions && - copyResourcePacks && - copyShaderPacks && - copyServers && - copyMods && - copyScreenshots; + return copySaves && keepPlaytime && copyGameOptions && copyResourcePacks && copyShaderPacks && copyServers && copyMods && + copyScreenshots; } - // Returns a single RegEx string of the selected folders/files to filter out (ex: ".minecraft/saves|.minecraft/server.dat") QString InstanceCopyPrefs::getSelectedFiltersAsRegex() const { @@ -26,25 +19,30 @@ QString InstanceCopyPrefs::getSelectedFiltersAsRegex(const QStringList& addition { QStringList filters; - if(!copySaves) + if (!copySaves) filters << "saves"; - if(!copyGameOptions) + if (!copyGameOptions) filters << "options.txt"; - if(!copyResourcePacks) - filters << "resourcepacks" << "texturepacks"; + if (!copyResourcePacks) + filters << "resourcepacks" + << "texturepacks"; - if(!copyShaderPacks) + if (!copyShaderPacks) filters << "shaderpacks"; - if(!copyServers) - filters << "servers.dat" << "servers.dat_old" << "server-resource-packs"; + if (!copyServers) + filters << "servers.dat" + << "servers.dat_old" + << "server-resource-packs"; - if(!copyMods) - filters << "coremods" << "mods" << "config"; + if (!copyMods) + filters << "coremods" + << "mods" + << "config"; - if(!copyScreenshots) + if (!copyScreenshots) filters << "screenshots"; for (auto filter : additionalFilters) { diff --git a/launcher/InstanceCopyPrefs.h b/launcher/InstanceCopyPrefs.h index c7bde0682..61c51b3b7 100644 --- a/launcher/InstanceCopyPrefs.h +++ b/launcher/InstanceCopyPrefs.h @@ -40,7 +40,7 @@ struct InstanceCopyPrefs { void enableDontLinkSaves(bool b); void enableUseClone(bool b); - protected: // data + protected: // data bool copySaves = true; bool keepPlaytime = true; bool copyGameOptions = true; diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp index 57a3143a1..8abf30640 100644 --- a/launcher/InstanceCopyTask.cpp +++ b/launcher/InstanceCopyTask.cpp @@ -156,8 +156,9 @@ void InstanceCopyTask::copyFinished() allowed_symlinks.append(m_origInstance->gameRoot().toUtf8()); allowed_symlinks.append("\n"); if (allowed_symlinks_file.isSymLink()) - FS::deletePath(allowed_symlinks_file - .filePath()); // we dont want to modify the original. also make sure the resulting file is not itself a link. + FS::deletePath( + allowed_symlinks_file + .filePath()); // we dont want to modify the original. also make sure the resulting file is not itself a link. FS::write(allowed_symlinks_file.filePath(), allowed_symlinks); } diff --git a/launcher/InstanceCopyTask.h b/launcher/InstanceCopyTask.h index aea9d99a1..357c6df0b 100644 --- a/launcher/InstanceCopyTask.h +++ b/launcher/InstanceCopyTask.h @@ -11,19 +11,18 @@ #include "settings/SettingsObject.h" #include "tasks/Task.h" -class InstanceCopyTask : public InstanceTask -{ +class InstanceCopyTask : public InstanceTask { Q_OBJECT -public: + public: explicit InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs); -protected: + protected: //! Entry point for tasks. virtual void executeTask() override; void copyFinished(); void copyAborted(); -private: + private: /* data */ InstancePtr m_origInstance; QFuture m_copyFuture; diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 352848f02..bc139e4f1 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2022 flowln * @@ -45,11 +45,14 @@ #include "icons/IconList.h" #include "icons/IconUtils.h" -#include "modplatform/technic/TechnicPackProcessor.h" -#include "modplatform/modrinth/ModrinthInstanceCreationTask.h" #include "modplatform/flame/FlameInstanceCreationTask.h" +#include "modplatform/modrinth/ModrinthInstanceCreationTask.h" +#include "modplatform/technic/TechnicPackProcessor.h" #include "settings/INISettingsObject.h" +#include "tasks/Task.h" + +#include "net/ApiDownload.h" #include #include @@ -88,25 +91,27 @@ void InstanceImportTask::executeTask() setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString())); m_downloadRequired = true; - const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path()); - - auto entry = APPLICATION->metacache()->resolveEntry("general", path); - entry->setStale(true); - m_archivePath = entry->getFullPath(); - - m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); - m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); - - connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); - connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged); - connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propogateStepProgress); - connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed); - connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted); - - m_filesNetJob->start(); + downloadFromUrl(); } } +void InstanceImportTask::downloadFromUrl() +{ + const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path(); + auto entry = APPLICATION->metacache()->resolveEntry("general", path); + entry->setStale(true); + m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); + m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry)); + m_archivePath = entry->getFullPath(); + + connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); + connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged); + connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress); + connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed); + connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted); + m_filesNetJob->start(); +} + void InstanceImportTask::downloadSucceeded() { processZipPack(); @@ -138,8 +143,7 @@ void InstanceImportTask::processZipPack() // open the zip and find relevant files in it m_packZip.reset(new QuaZip(m_archivePath)); - if (!m_packZip->open(QuaZip::mdUnzip)) - { + if (!m_packZip->open(QuaZip::mdUnzip)) { emitFailed(tr("Unable to open supplied modpack zip file.")); return; } @@ -153,44 +157,40 @@ void InstanceImportTask::processZipPack() // NOTE: Prioritize modpack platforms that aren't searched for recursively. // Especially Flame has a very common filename for its manifest, which may appear inside overrides for example - if(modrinthFound) - { + if (modrinthFound) { // process as Modrinth pack qDebug() << "Modrinth:" << modrinthFound; m_modpackType = ModpackType::Modrinth; - } - else if (technicFound) - { + } else if (technicFound) { // process as Technic pack qDebug() << "Technic:" << technicFound; extractDir.mkpath(".minecraft"); extractDir.cd(".minecraft"); m_modpackType = ModpackType::Technic; - } - else - { - QStringList paths_to_ignore { "overrides/" }; + } else { + QStringList paths_to_ignore{ "overrides/" }; if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) { // process as MultiMC instance/pack qDebug() << "MultiMC:" << mmcRoot; root = mmcRoot; m_modpackType = ModpackType::MultiMC; - } else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore); !flameRoot.isNull()) { + } else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore); + !flameRoot.isNull()) { // process as Flame pack qDebug() << "Flame:" << flameRoot; root = flameRoot; m_modpackType = ModpackType::Flame; } } - if(m_modpackType == ModpackType::Unknown) - { + if (m_modpackType == ModpackType::Unknown) { emitFailed(tr("Archive does not contain a recognized modpack type.")); return; } // make sure we extract just the pack - m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath()); + m_extractFuture = + QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath()); connect(&m_extractFutureWatcher, &QFutureWatcher::finished, this, &InstanceImportTask::extractFinished); m_extractFutureWatcher.setFuture(m_extractFuture); } @@ -210,37 +210,28 @@ void InstanceImportTask::extractFinished() qDebug() << "Fixing permissions for extracted pack files..."; QDirIterator it(extractDir, QDirIterator::Subdirectories); - while (it.hasNext()) - { + while (it.hasNext()) { auto filepath = it.next(); QFileInfo file(filepath); auto permissions = QFile::permissions(filepath); auto origPermissions = permissions; - if(file.isDir()) - { + if (file.isDir()) { // Folder +rwx for current user permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser; - } - else - { + } else { // File +rw for current user permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser; } - if(origPermissions != permissions) - { - if(!QFile::setPermissions(filepath, permissions)) - { + if (origPermissions != permissions) { + if (!QFile::setPermissions(filepath, permissions)) { logWarning(tr("Could not fix permissions for %1").arg(filepath)); - } - else - { + } else { qDebug() << "Fixed" << filepath; } } } - switch(m_modpackType) - { + switch (m_modpackType) { case ModpackType::MultiMC: processMultiMC(); return; @@ -276,7 +267,8 @@ void InstanceImportTask::processFlame() if (original_instance_id_it != m_extra_info.constEnd()) original_instance_id = original_instance_id_it.value(); - inst_creation_task = makeShared(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); + inst_creation_task = + makeShared(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); } else { // FIXME: Find a way to get IDs in directly imported ZIPs inst_creation_task = makeShared(m_stagingPath, m_globalSettings, m_parent, QString(), QString()); @@ -286,14 +278,14 @@ void InstanceImportTask::processFlame() inst_creation_task->setIcon(m_instIcon); inst_creation_task->setGroup(m_instGroup); inst_creation_task->setConfirmUpdate(shouldConfirmUpdate()); - + connect(inst_creation_task.get(), &Task::succeeded, this, [this, inst_creation_task] { setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID()); emitSucceeded(); }); connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed); connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress); - connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress); + connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress); connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus); connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails); @@ -362,7 +354,8 @@ void InstanceImportTask::processModrinth() if (original_instance_id_it != m_extra_info.constEnd()) original_instance_id = original_instance_id_it.value(); - inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); + inst_creation_task = + new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); } else { QString pack_id; if (!m_sourceUrl.isEmpty()) { @@ -378,14 +371,14 @@ void InstanceImportTask::processModrinth() inst_creation_task->setIcon(m_instIcon); inst_creation_task->setGroup(m_instGroup); inst_creation_task->setConfirmUpdate(shouldConfirmUpdate()); - + connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] { setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID()); emitSucceeded(); }); connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed); connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress); - connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress); + connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress); connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus); connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails); connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater); diff --git a/launcher/InstanceImportTask.h b/launcher/InstanceImportTask.h index 7fda439fc..ca3d30ad6 100644 --- a/launcher/InstanceImportTask.h +++ b/launcher/InstanceImportTask.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,54 +35,49 @@ #pragma once -#include "InstanceTask.h" -#include "net/NetJob.h" -#include #include #include -#include "settings/SettingsObject.h" +#include +#include "InstanceTask.h" #include "QObjectPtr.h" #include "modplatform/flame/PackManifest.h" +#include "net/NetJob.h" +#include "settings/SettingsObject.h" #include class QuaZip; -namespace Flame -{ - class FileResolvingTask; +namespace Flame { +class FileResolvingTask; } -class InstanceImportTask : public InstanceTask -{ +class InstanceImportTask : public InstanceTask { Q_OBJECT -public: + public: explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap&& extra_info = {}); bool abort() override; - const QVector &getBlockedFiles() const - { - return m_blockedMods; - } + const QVector& getBlockedFiles() const { return m_blockedMods; } -protected: + protected: //! Entry point for tasks. virtual void executeTask() override; -private: + private: void processZipPack(); void processMultiMC(); void processTechnic(); void processFlame(); void processModrinth(); -private slots: + private slots: void downloadSucceeded(); void downloadFailed(QString reason); void downloadProgressChanged(qint64 current, qint64 total); void downloadAborted(); void extractFinished(); -private: /* data */ + private: /* data */ NetJob::Ptr m_filesNetJob; shared_qobject_ptr m_modIdResolver; QUrl m_sourceUrl; @@ -92,7 +87,7 @@ private: /* data */ QFuture> m_extractFuture; QFutureWatcher> m_extractFutureWatcher; QVector m_blockedMods; - enum class ModpackType{ + enum class ModpackType { Unknown, MultiMC, Technic, @@ -104,6 +99,7 @@ private: /* data */ // the source URL / the resource it points to alone. QMap m_extra_info; - //FIXME: nuke + // FIXME: nuke QWidget* m_parent; + void downloadFromUrl(); }; diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index b4c520cd9..856eee816 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -41,9 +41,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -96,7 +96,11 @@ Qt::DropActions InstanceList::supportedDropActions() const return Qt::MoveAction; } -bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const +bool InstanceList::canDropMimeData(const QMimeData* data, + [[maybe_unused]] Qt::DropAction action, + [[maybe_unused]] int row, + [[maybe_unused]] int column, + [[maybe_unused]] const QModelIndex& parent) const { if (data && data->hasFormat("application/x-instanceid")) { return true; @@ -104,7 +108,11 @@ bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action, return false; } -bool InstanceList::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) +bool InstanceList::dropMimeData(const QMimeData* data, + [[maybe_unused]] Qt::DropAction action, + [[maybe_unused]] int row, + [[maybe_unused]] int column, + [[maybe_unused]] const QModelIndex& parent) { if (data && data->hasFormat("application/x-instanceid")) { return true; @@ -129,7 +137,7 @@ QMimeData* InstanceList::mimeData(const QModelIndexList& indexes) const return mimeData; } -QStringList InstanceList::getLinkedInstancesById(const QString &id) const +QStringList InstanceList::getLinkedInstancesById(const QString& id) const { QStringList linkedInstances; for (auto inst : m_instances) { @@ -158,42 +166,34 @@ QVariant InstanceList::data(const QModelIndex& index, int role) const if (!index.isValid()) { return QVariant(); } - BaseInstance *pdata = static_cast(index.internalPointer()); - switch (role) - { - case InstancePointerRole: - { - QVariant v = QVariant::fromValue((void *)pdata); - return v; - } - case InstanceIDRole: - { - return pdata->id(); - } - case Qt::EditRole: - case Qt::DisplayRole: - { - return pdata->name(); - } - case Qt::AccessibleTextRole: - { - return tr("%1 Instance").arg(pdata->name()); - } - case Qt::ToolTipRole: - { - return pdata->instanceRoot(); - } - case Qt::DecorationRole: - { - return pdata->iconKey(); - } - // HACK: see InstanceView.h in gui! - case GroupRole: - { - return getInstanceGroup(pdata->id()); - } - default: - break; + BaseInstance* pdata = static_cast(index.internalPointer()); + switch (role) { + case InstancePointerRole: { + QVariant v = QVariant::fromValue((void*)pdata); + return v; + } + case InstanceIDRole: { + return pdata->id(); + } + case Qt::EditRole: + case Qt::DisplayRole: { + return pdata->name(); + } + case Qt::AccessibleTextRole: { + return tr("%1 Instance").arg(pdata->name()); + } + case Qt::ToolTipRole: { + return pdata->instanceRoot(); + } + case Qt::DecorationRole: { + return pdata->iconKey(); + } + // HACK: see InstanceView.h in gui! + case GroupRole: { + return getInstanceGroup(pdata->id()); + } + default: + break; } return QVariant(); } @@ -320,16 +320,18 @@ bool InstanceList::trashInstance(const InstanceId& id) } qDebug() << "Instance" << id << "has been trashed by the launcher."; - m_trashHistory.push({id, inst->instanceRoot(), trashedLoc, cachedGroupId}); - + m_trashHistory.push({ id, inst->instanceRoot(), trashedLoc, cachedGroupId }); + return true; } -bool InstanceList::trashedSomething() { +bool InstanceList::trashedSomething() +{ return !m_trashHistory.empty(); } -void InstanceList::undoTrashInstance() { +void InstanceList::undoTrashInstance() +{ if (m_trashHistory.empty()) { qWarning() << "Nothing to recover from trash."; return; @@ -558,7 +560,7 @@ InstancePtr InstanceList::getInstanceByManagedName(const QString& managed_name) return {}; } -QModelIndex InstanceList::getInstanceIndexById(const QString &id) const +QModelIndex InstanceList::getInstanceIndexById(const QString& id) const { return index(getInstIndex(getInstanceById(id).get())); } @@ -597,13 +599,11 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id) QString inst_type = instanceSettings->get("InstanceType").toString(); - // NOTE: Some PolyMC versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a OneSix instance - if (inst_type == "OneSix" || inst_type.isEmpty()) - { + // NOTE: Some PolyMC versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a OneSix + // instance + if (inst_type == "OneSix" || inst_type.isEmpty()) { inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot)); - } - else - { + } else { inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot)); } qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot(); @@ -759,7 +759,7 @@ void InstanceList::instanceDirContentsChanged(const QString& path) emit instancesChanged(); } -void InstanceList::on_InstFolderChanged(const Setting& setting, QVariant value) +void InstanceList::on_InstFolderChanged([[maybe_unused]] const Setting& setting, QVariant value) { QString newInstDir = QDir(value.toString()).canonicalPath(); if (newInstDir != m_instDir) { @@ -787,20 +787,25 @@ class InstanceStaging : public Task { Q_OBJECT const unsigned minBackoff = 1; const unsigned maxBackoff = 16; + public: InstanceStaging(InstanceList* parent, InstanceTask* child, QString stagingPath, InstanceName const& instanceName, QString groupName) - : m_parent(parent), backoff(minBackoff, maxBackoff), m_stagingPath(std::move(stagingPath)), m_instance_name(std::move(instanceName)), m_groupName(std::move(groupName)) + : m_parent(parent) + , backoff(minBackoff, maxBackoff) + , m_stagingPath(std::move(stagingPath)) + , m_instance_name(std::move(instanceName)) + , m_groupName(std::move(groupName)) { m_child.reset(child); - connect(child, &Task::succeeded, this, &InstanceStaging::childSucceded); + connect(child, &Task::succeeded, this, &InstanceStaging::childSucceeded); connect(child, &Task::failed, this, &InstanceStaging::childFailed); connect(child, &Task::aborted, this, &InstanceStaging::childAborted); connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable); connect(child, &Task::status, this, &InstanceStaging::setStatus); connect(child, &Task::details, this, &InstanceStaging::setDetails); connect(child, &Task::progress, this, &InstanceStaging::setProgress); - connect(child, &Task::stepProgress, this, &InstanceStaging::propogateStepProgress); - connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded); + connect(child, &Task::stepProgress, this, &InstanceStaging::propagateStepProgress); + connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceeded); } virtual ~InstanceStaging(){}; @@ -815,21 +820,17 @@ class InstanceStaging : public Task { return Task::abort(); } - bool canAbort() const override - { - return (m_child && m_child->canAbort()); - } + bool canAbort() const override { return (m_child && m_child->canAbort()); } protected: virtual void executeTask() override { m_child->start(); } QStringList warnings() const override { return m_child->warnings(); } private slots: - void childSucceded() + void childSucceeded() { unsigned sleepTime = backoff(); - if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get())) - { + if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get())) { emitSucceeded(); return; } @@ -847,13 +848,10 @@ class InstanceStaging : public Task { emitFailed(reason); } - void childAborted() - { - emitAborted(); - } + void childAborted() { emitAborted(); } -private: - InstanceList * m_parent; + private: + InstanceList* m_parent; /* * WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows. * Basically, it starts messing things up while the launcher is extracting/creating instances @@ -892,7 +890,10 @@ QString InstanceList::getStagedInstancePath() return path; } -bool InstanceList::commitStagedInstance(const QString& path, InstanceName const& instanceName, const QString& groupName, InstanceTask const& commiting) +bool InstanceList::commitStagedInstance(const QString& path, + InstanceName const& instanceName, + const QString& groupName, + InstanceTask const& commiting) { QDir dir; QString instID; diff --git a/launcher/InstanceList.h b/launcher/InstanceList.h index 48bede07d..ee4578ffd 100644 --- a/launcher/InstanceList.h +++ b/launcher/InstanceList.h @@ -15,12 +15,12 @@ #pragma once -#include #include -#include #include -#include +#include #include +#include +#include #include "BaseInstance.h" @@ -32,21 +32,9 @@ using InstanceId = QString; using GroupId = QString; using InstanceLocator = std::pair; -enum class InstCreateError -{ - NoCreateError = 0, - NoSuchVersion, - UnknownCreateError, - InstExists, - CantCreateDir -}; +enum class InstCreateError { NoCreateError = 0, NoSuchVersion, UnknownCreateError, InstExists, CantCreateDir }; -enum class GroupsState -{ - NotLoaded, - Steady, - Dirty -}; +enum class GroupsState { NotLoaded, Steady, Dirty }; struct TrashHistoryItem { QString id; @@ -55,48 +43,36 @@ struct TrashHistoryItem { QString groupName; }; -class InstanceList : public QAbstractListModel -{ +class InstanceList : public QAbstractListModel { Q_OBJECT -public: - explicit InstanceList(SettingsObjectPtr settings, const QString & instDir, QObject *parent = 0); + public: + explicit InstanceList(SettingsObjectPtr settings, const QString& instDir, QObject* parent = 0); virtual ~InstanceList(); -public: - QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role) const override; - Qt::ItemFlags flags(const QModelIndex &index) const override; + public: + QModelIndex index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role) const override; + Qt::ItemFlags flags(const QModelIndex& index) const override; - bool setData(const QModelIndex & index, const QVariant & value, int role) override; + bool setData(const QModelIndex& index, const QVariant& value, int role) override; - enum AdditionalRoles - { + enum AdditionalRoles { GroupRole = Qt::UserRole, - InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance - InstanceIDRole = 0x34B1CB49 ///< Return id if the instance + InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance + InstanceIDRole = 0x34B1CB49 ///< Return id if the instance }; /*! * \brief Error codes returned by functions in the InstanceList class. * NoError Indicates that no error occurred. * UnknownError indicates that an unspecified error occurred. */ - enum InstListError - { - NoError = 0, - UnknownError - }; + enum InstListError { NoError = 0, UnknownError }; - InstancePtr at(int i) const - { - return m_instances.at(i); - } + InstancePtr at(int i) const { return m_instances.at(i); } - int count() const - { - return m_instances.count(); - } + int count() const { return m_instances.count(); } InstListError loadList(); void saveNow(); @@ -105,21 +81,21 @@ public: InstancePtr getInstanceById(QString id) const; /* O(n) */ InstancePtr getInstanceByManagedName(const QString& managed_name) const; - QModelIndex getInstanceIndexById(const QString &id) const; + QModelIndex getInstanceIndexById(const QString& id) const; QStringList getGroups(); - bool isGroupCollapsed(const QString &groupName); + bool isGroupCollapsed(const QString& groupName); - GroupId getInstanceGroup(const InstanceId & id) const; - void setInstanceGroup(const InstanceId & id, const GroupId& name); + GroupId getInstanceGroup(const InstanceId& id) const; + void setInstanceGroup(const InstanceId& id, const GroupId& name); - void deleteGroup(const GroupId & name); - bool trashInstance(const InstanceId &id); + void deleteGroup(const GroupId& name); + bool trashInstance(const InstanceId& id); bool trashedSomething(); void undoTrashInstance(); - void deleteInstance(const InstanceId & id); + void deleteInstance(const InstanceId& id); // Wrap an instance creation task in some more task machinery and make it ready to be used - Task * wrapInstanceTask(InstanceTask * task); + Task* wrapInstanceTask(InstanceTask* task); /** * Create a new empty staging area for instance creation and @return a path/key top commit it later. @@ -139,7 +115,7 @@ public: * Destroy a previously created staging area given by @keyPath - used when creation fails. * Used by instance manipulation tasks. */ - bool destroyStagingPath(const QString & keyPath); + bool destroyStagingPath(const QString& keyPath); int getTotalPlayTime(); @@ -147,42 +123,42 @@ public: Qt::DropActions supportedDropActions() const override; - bool canDropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) const override; + bool canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const override; - bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) override; + bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; QStringList mimeTypes() const override; - QMimeData *mimeData(const QModelIndexList &indexes) const override; + QMimeData* mimeData(const QModelIndexList& indexes) const override; - QStringList getLinkedInstancesById(const QString &id) const; + QStringList getLinkedInstancesById(const QString& id) const; -signals: + signals: void dataIsInvalid(); void instancesChanged(); void instanceSelectRequest(QString instanceId); void groupsChanged(QSet groups); -public slots: - void on_InstFolderChanged(const Setting &setting, QVariant value); - void on_GroupStateChanged(const QString &group, bool collapsed); + public slots: + void on_InstFolderChanged(const Setting& setting, QVariant value); + void on_GroupStateChanged(const QString& group, bool collapsed); -private slots: - void propertiesChanged(BaseInstance *inst); + private slots: + void propertiesChanged(BaseInstance* inst); void providerUpdated(); - void instanceDirContentsChanged(const QString &path); + void instanceDirContentsChanged(const QString& path); -private: - int getInstIndex(BaseInstance *inst) const; + private: + int getInstIndex(BaseInstance* inst) const; void updateTotalPlayTime(); void suspendWatch(); void resumeWatch(); - void add(const QList &list); + void add(const QList& list); void loadGroupList(); void saveGroupList(); QList discoverInstances(); InstancePtr loadInstance(const InstanceId& id); -private: + private: int m_watchLevel = 0; int totalPlayTime = 0; bool m_dirty = false; @@ -191,7 +167,7 @@ private: SettingsObjectPtr m_globalSettings; QString m_instDir; - QFileSystemWatcher * m_watcher; + QFileSystemWatcher* m_watcher; // FIXME: this is so inefficient that looking at it is almost painful. QSet m_collapsedGroups; QMap m_instanceGroupIndex; diff --git a/launcher/InstancePageProvider.h b/launcher/InstancePageProvider.h index b4b6e739d..66d2b6750 100644 --- a/launcher/InstancePageProvider.h +++ b/launcher/InstancePageProvider.h @@ -1,36 +1,31 @@ #pragma once -#include "minecraft/MinecraftInstance.h" #include +#include "minecraft/MinecraftInstance.h" #include "ui/pages/BasePage.h" #include "ui/pages/BasePageProvider.h" +#include "ui/pages/instance/InstanceSettingsPage.h" #include "ui/pages/instance/LogPage.h" -#include "ui/pages/instance/VersionPage.h" #include "ui/pages/instance/ManagedPackPage.h" #include "ui/pages/instance/ModFolderPage.h" -#include "ui/pages/instance/ResourcePackPage.h" -#include "ui/pages/instance/TexturePackPage.h" -#include "ui/pages/instance/ShaderPackPage.h" #include "ui/pages/instance/NotesPage.h" -#include "ui/pages/instance/ScreenshotsPage.h" -#include "ui/pages/instance/InstanceSettingsPage.h" #include "ui/pages/instance/OtherLogsPage.h" -#include "ui/pages/instance/WorldListPage.h" +#include "ui/pages/instance/ResourcePackPage.h" +#include "ui/pages/instance/ScreenshotsPage.h" #include "ui/pages/instance/ServersPage.h" -#include "ui/pages/instance/GameOptionsPage.h" +#include "ui/pages/instance/ShaderPackPage.h" +#include "ui/pages/instance/TexturePackPage.h" +#include "ui/pages/instance/VersionPage.h" +#include "ui/pages/instance/WorldListPage.h" -class InstancePageProvider : public QObject, public BasePageProvider -{ +class InstancePageProvider : protected QObject, public BasePageProvider { Q_OBJECT -public: - explicit InstancePageProvider(InstancePtr parent) - { - inst = parent; - } + public: + explicit InstancePageProvider(InstancePtr parent) { inst = parent; } - virtual ~InstancePageProvider() {}; - virtual QList getPages() override + virtual ~InstancePageProvider(){}; + virtual QList getPages() override { - QList values; + QList values; values.append(new LogPage(inst)); std::shared_ptr onesix = std::dynamic_pointer_cast(inst); values.append(new VersionPage(onesix.get())); @@ -50,18 +45,14 @@ public: values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots"))); values.append(new InstanceSettingsPage(onesix.get())); auto logMatcher = inst->getLogFileMatcher(); - if(logMatcher) - { + if (logMatcher) { values.append(new OtherLogsPage(inst->getLogFileRoot(), logMatcher)); } return values; } - virtual QString dialogTitle() override - { - return tr("Edit Instance (%1)").arg(inst->name()); - } -protected: + virtual QString dialogTitle() override { return tr("Edit Instance (%1)").arg(inst->name()); } + + protected: InstancePtr inst; }; - diff --git a/launcher/InstanceTask.cpp b/launcher/InstanceTask.cpp index b16a40ba6..5b2398268 100644 --- a/launcher/InstanceTask.cpp +++ b/launcher/InstanceTask.cpp @@ -18,13 +18,14 @@ InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& ol return InstanceNameChange::ShouldKeep; } -ShouldUpdate askIfShouldUpdate(QWidget *parent, QString original_version_name) +ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name) { auto info = CustomMessageBox::selectable( parent, QObject::tr("Similar modpack was found!"), - QObject::tr("One or more of your instances are from this same modpack%1. Do you want to create a " - "separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before " - "updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).") + QObject::tr( + "One or more of your instances are from this same modpack%1. Do you want to create a " + "separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before " + "updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).") .arg(original_version_name), QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort); info->setButtonText(QMessageBox::Ok, QObject::tr("Update existing instance")); @@ -38,7 +39,6 @@ ShouldUpdate askIfShouldUpdate(QWidget *parent, QString original_version_name) if (info->clickedButton() == info->button(QMessageBox::Abort)) return ShouldUpdate::SkipUpdating; return ShouldUpdate::Cancel; - } QString InstanceName::name() const diff --git a/launcher/JavaCommon.cpp b/launcher/JavaCommon.cpp index e29e22709..e16ac9255 100644 --- a/launcher/JavaCommon.cpp +++ b/launcher/JavaCommon.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -39,43 +39,39 @@ #include -bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent) +bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget* parent) { - if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegularExpression("-Xm[sx]")) - || jvmargs.contains("-XX-MaxHeapSize") || jvmargs.contains("-XX:InitialHeapSize")) - { + if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegularExpression("-Xm[sx]")) || jvmargs.contains("-XX-MaxHeapSize") || + jvmargs.contains("-XX:InitialHeapSize")) { auto warnStr = QObject::tr( - "You tried to manually set a JVM memory option (using \"-XX:PermSize\", \"-XX-MaxHeapSize\", \"-XX:InitialHeapSize\", \"-Xmx\" or \"-Xms\").\n" + "You tried to manually set a JVM memory option (using \"-XX:PermSize\", \"-XX-MaxHeapSize\", \"-XX:InitialHeapSize\", \"-Xmx\" " + "or \"-Xms\").\n" "There are dedicated boxes for these in the settings (Java tab, in the Memory group at the top).\n" "This message will be displayed until you remove them from the JVM arguments."); - CustomMessageBox::selectable( - parent, QObject::tr("JVM arguments warning"), - warnStr, - QMessageBox::Warning)->exec(); + CustomMessageBox::selectable(parent, QObject::tr("JVM arguments warning"), warnStr, QMessageBox::Warning)->exec(); return false; } // block lunacy with passing required version to the JVM if (jvmargs.contains(QRegularExpression("-version:.*"))) { auto warnStr = QObject::tr( - "You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be allowed.\n" + "You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be " + "allowed.\n" "This message will be displayed until you remove this from the JVM arguments."); - CustomMessageBox::selectable( - parent, QObject::tr("JVM arguments warning"), - warnStr, - QMessageBox::Warning)->exec(); + CustomMessageBox::selectable(parent, QObject::tr("JVM arguments warning"), warnStr, QMessageBox::Warning)->exec(); return false; } return true; } -void JavaCommon::javaWasOk(QWidget *parent, JavaCheckResult result) +void JavaCommon::javaWasOk(QWidget* parent, const JavaCheckResult& result) { QString text; - text += QObject::tr("Java test succeeded!
Platform reported: %1
Java version " - "reported: %2
Java vendor " - "reported: %3
").arg(result.realPlatform, result.javaVersion.toString(), result.javaVendor); - if (result.errorLog.size()) - { + text += QObject::tr( + "Java test succeeded!
Platform reported: %1
Java version " + "reported: %2
Java vendor " + "reported: %3
") + .arg(result.realPlatform, result.javaVersion.toString(), result.javaVendor); + if (result.errorLog.size()) { auto htmlError = result.errorLog; htmlError.replace('\n', "
"); text += QObject::tr("
Warnings:
%1").arg(htmlError); @@ -83,7 +79,7 @@ void JavaCommon::javaWasOk(QWidget *parent, JavaCheckResult result) CustomMessageBox::selectable(parent, QObject::tr("Java test success"), text, QMessageBox::Information)->show(); } -void JavaCommon::javaArgsWereBad(QWidget *parent, JavaCheckResult result) +void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaCheckResult& result) { auto htmlError = result.errorLog; QString text; @@ -93,7 +89,7 @@ void JavaCommon::javaArgsWereBad(QWidget *parent, JavaCheckResult result) CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show(); } -void JavaCommon::javaBinaryWasBad(QWidget *parent, JavaCheckResult result) +void JavaCommon::javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result) { QString text; text += QObject::tr( @@ -102,7 +98,7 @@ void JavaCommon::javaBinaryWasBad(QWidget *parent, JavaCheckResult result) CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show(); } -void JavaCommon::javaCheckNotFound(QWidget *parent) +void JavaCommon::javaCheckNotFound(QWidget* parent) { QString text; text += QObject::tr("Java checker library could not be found. Please check your installation."); @@ -111,8 +107,7 @@ void JavaCommon::javaCheckNotFound(QWidget *parent) void JavaCommon::TestCheck::run() { - if (!JavaCommon::checkJVMArgs(m_args, m_parent)) - { + if (!JavaCommon::checkJVMArgs(m_args, m_parent)) { emit finished(); return; } @@ -129,8 +124,7 @@ void JavaCommon::TestCheck::run() void JavaCommon::TestCheck::checkFinished(JavaCheckResult result) { - if (result.validity != JavaCheckResult::Validity::Valid) - { + if (result.validity != JavaCheckResult::Validity::Valid) { javaBinaryWasBad(m_parent, result); emit finished(); return; @@ -141,8 +135,7 @@ void JavaCommon::TestCheck::checkFinished(JavaCheckResult result) checker->m_args = m_args; checker->m_minMem = m_minMem; checker->m_maxMem = m_maxMem; - if (result.javaVersion.requiresPermGen()) - { + if (result.javaVersion.requiresPermGen()) { checker->m_permGen = m_permGen; } checker->performCheck(); @@ -150,8 +143,7 @@ void JavaCommon::TestCheck::checkFinished(JavaCheckResult result) void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result) { - if (result.validity == JavaCheckResult::Validity::Valid) - { + if (result.validity == JavaCheckResult::Validity::Valid) { javaWasOk(m_parent, result); emit finished(); return; @@ -159,4 +151,3 @@ void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result) javaArgsWereBad(m_parent, result); emit finished(); } - diff --git a/launcher/JavaCommon.h b/launcher/JavaCommon.h index 59cb7a67d..c96f7a985 100644 --- a/launcher/JavaCommon.h +++ b/launcher/JavaCommon.h @@ -6,45 +6,42 @@ class QWidget; /** * Common UI bits for the java pages to use. */ -namespace JavaCommon -{ - bool checkJVMArgs(QString args, QWidget *parent); +namespace JavaCommon { +bool checkJVMArgs(QString args, QWidget* parent); - // Show a dialog saying that the Java binary was usable - void javaWasOk(QWidget *parent, JavaCheckResult result); - // Show a dialog saying that the Java binary was not usable because of bad options - void javaArgsWereBad(QWidget *parent, JavaCheckResult result); - // Show a dialog saying that the Java binary was not usable - void javaBinaryWasBad(QWidget *parent, JavaCheckResult result); - // Show a dialog if we couldn't find Java Checker - void javaCheckNotFound(QWidget *parent); +// Show a dialog saying that the Java binary was usable +void javaWasOk(QWidget* parent, const JavaCheckResult& result); +// Show a dialog saying that the Java binary was not usable because of bad options +void javaArgsWereBad(QWidget* parent, const JavaCheckResult& result); +// Show a dialog saying that the Java binary was not usable +void javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result); +// Show a dialog if we couldn't find Java Checker +void javaCheckNotFound(QWidget* parent); - class TestCheck : public QObject - { - Q_OBJECT - public: - TestCheck(QWidget *parent, QString path, QString args, int minMem, int maxMem, int permGen) - :m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen) - { - } - virtual ~TestCheck() {}; +class TestCheck : public QObject { + Q_OBJECT + public: + TestCheck(QWidget* parent, QString path, QString args, int minMem, int maxMem, int permGen) + : m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen) + {} + virtual ~TestCheck(){}; - void run(); + void run(); - signals: - void finished(); + signals: + void finished(); - private slots: - void checkFinished(JavaCheckResult result); - void checkFinishedWithArgs(JavaCheckResult result); + private slots: + void checkFinished(JavaCheckResult result); + void checkFinishedWithArgs(JavaCheckResult result); - private: - std::shared_ptr checker; - QWidget *m_parent = nullptr; - QString m_path; - QString m_args; - int m_minMem = 0; - int m_maxMem = 0; - int m_permGen = 64; - }; -} + private: + std::shared_ptr checker; + QWidget* m_parent = nullptr; + QString m_path; + QString m_args; + int m_minMem = 0; + int m_maxMem = 0; + int m_permGen = 64; +}; +} // namespace JavaCommon diff --git a/launcher/Json.cpp b/launcher/Json.cpp index 06b3d3bd2..f397f89c5 100644 --- a/launcher/Json.cpp +++ b/launcher/Json.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -37,257 +37,246 @@ #include -#include "FileSystem.h" #include +#include "FileSystem.h" -namespace Json -{ -void write(const QJsonDocument &doc, const QString &filename) +namespace Json { +void write(const QJsonDocument& doc, const QString& filename) { FS::write(filename, doc.toJson()); } -void write(const QJsonObject &object, const QString &filename) +void write(const QJsonObject& object, const QString& filename) { write(QJsonDocument(object), filename); } -void write(const QJsonArray &array, const QString &filename) +void write(const QJsonArray& array, const QString& filename) { write(QJsonDocument(array), filename); } -QByteArray toText(const QJsonObject &obj) +QByteArray toText(const QJsonObject& obj) { return QJsonDocument(obj).toJson(QJsonDocument::Compact); } -QByteArray toText(const QJsonArray &array) +QByteArray toText(const QJsonArray& array) { return QJsonDocument(array).toJson(QJsonDocument::Compact); } -static bool isBinaryJson(const QByteArray &data) +static bool isBinaryJson(const QByteArray& data) { decltype(QJsonDocument::BinaryFormatTag) tag = QJsonDocument::BinaryFormatTag; return memcmp(data.constData(), &tag, sizeof(QJsonDocument::BinaryFormatTag)) == 0; } -QJsonDocument requireDocument(const QByteArray &data, const QString &what) +QJsonDocument requireDocument(const QByteArray& data, const QString& what) { - if (isBinaryJson(data)) - { + if (isBinaryJson(data)) { // FIXME: Is this needed? throw JsonException(what + ": Invalid JSON. Binary JSON unsupported"); - } - else - { + } else { QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(data, &error); - if (error.error != QJsonParseError::NoError) - { + if (error.error != QJsonParseError::NoError) { throw JsonException(what + ": Error parsing JSON: " + error.errorString()); } return doc; } } -QJsonDocument requireDocument(const QString &filename, const QString &what) +QJsonDocument requireDocument(const QString& filename, const QString& what) { return requireDocument(FS::read(filename), what); } -QJsonObject requireObject(const QJsonDocument &doc, const QString &what) +QJsonObject requireObject(const QJsonDocument& doc, const QString& what) { - if (!doc.isObject()) - { + if (!doc.isObject()) { throw JsonException(what + " is not an object"); } return doc.object(); } -QJsonArray requireArray(const QJsonDocument &doc, const QString &what) +QJsonArray requireArray(const QJsonDocument& doc, const QString& what) { - if (!doc.isArray()) - { + if (!doc.isArray()) { throw JsonException(what + " is not an array"); } return doc.array(); } -void writeString(QJsonObject &to, const QString &key, const QString &value) +void writeString(QJsonObject& to, const QString& key, const QString& value) { - if (!value.isEmpty()) - { + if (!value.isEmpty()) { to.insert(key, value); } } -void writeStringList(QJsonObject &to, const QString &key, const QStringList &values) +void writeStringList(QJsonObject& to, const QString& key, const QStringList& values) { - if (!values.isEmpty()) - { + if (!values.isEmpty()) { QJsonArray array; - for(auto value: values) - { + for (auto value : values) { array.append(value); } to.insert(key, array); } } -template<> -QJsonValue toJson(const QUrl &url) +template <> +QJsonValue toJson(const QUrl& url) { return QJsonValue(url.toString(QUrl::FullyEncoded)); } -template<> -QJsonValue toJson(const QByteArray &data) +template <> +QJsonValue toJson(const QByteArray& data) { return QJsonValue(QString::fromLatin1(data.toHex())); } -template<> -QJsonValue toJson(const QDateTime &datetime) +template <> +QJsonValue toJson(const QDateTime& datetime) { return QJsonValue(datetime.toString(Qt::ISODate)); } -template<> -QJsonValue toJson(const QDir &dir) +template <> +QJsonValue toJson(const QDir& dir) { return QDir::current().relativeFilePath(dir.absolutePath()); } -template<> -QJsonValue toJson(const QUuid &uuid) +template <> +QJsonValue toJson(const QUuid& uuid) { return uuid.toString(); } -template<> -QJsonValue toJson(const QVariant &variant) +template <> +QJsonValue toJson(const QVariant& variant) { return QJsonValue::fromVariant(variant); } - -template<> QByteArray requireIsType(const QJsonValue &value, const QString &what) +template <> +QByteArray requireIsType(const QJsonValue& value, const QString& what) { const QString string = ensureIsType(value, what); // ensure that the string can be safely cast to Latin1 - if (string != QString::fromLatin1(string.toLatin1())) - { + if (string != QString::fromLatin1(string.toLatin1())) { throw JsonException(what + " is not encodable as Latin1"); } return QByteArray::fromHex(string.toLatin1()); } -template<> QJsonArray requireIsType(const QJsonValue &value, const QString &what) +template <> +QJsonArray requireIsType(const QJsonValue& value, const QString& what) { - if (!value.isArray()) - { + if (!value.isArray()) { throw JsonException(what + " is not an array"); } return value.toArray(); } - -template<> QString requireIsType(const QJsonValue &value, const QString &what) +template <> +QString requireIsType(const QJsonValue& value, const QString& what) { - if (!value.isString()) - { + if (!value.isString()) { throw JsonException(what + " is not a string"); } return value.toString(); } -template<> bool requireIsType(const QJsonValue &value, const QString &what) +template <> +bool requireIsType(const QJsonValue& value, const QString& what) { - if (!value.isBool()) - { + if (!value.isBool()) { throw JsonException(what + " is not a bool"); } return value.toBool(); } -template<> double requireIsType(const QJsonValue &value, const QString &what) +template <> +double requireIsType(const QJsonValue& value, const QString& what) { - if (!value.isDouble()) - { + if (!value.isDouble()) { throw JsonException(what + " is not a double"); } return value.toDouble(); } -template<> int requireIsType(const QJsonValue &value, const QString &what) +template <> +int requireIsType(const QJsonValue& value, const QString& what) { const double doubl = requireIsType(value, what); - if (fmod(doubl, 1) != 0) - { + if (fmod(doubl, 1) != 0) { throw JsonException(what + " is not an integer"); } return int(doubl); } -template<> QDateTime requireIsType(const QJsonValue &value, const QString &what) +template <> +QDateTime requireIsType(const QJsonValue& value, const QString& what) { const QString string = requireIsType(value, what); const QDateTime datetime = QDateTime::fromString(string, Qt::ISODate); - if (!datetime.isValid()) - { + if (!datetime.isValid()) { throw JsonException(what + " is not a ISO formatted date/time value"); } return datetime; } -template<> QUrl requireIsType(const QJsonValue &value, const QString &what) +template <> +QUrl requireIsType(const QJsonValue& value, const QString& what) { const QString string = ensureIsType(value, what); - if (string.isEmpty()) - { + if (string.isEmpty()) { return QUrl(); } const QUrl url = QUrl(string, QUrl::StrictMode); - if (!url.isValid()) - { + if (!url.isValid()) { throw JsonException(what + " is not a correctly formatted URL"); } return url; } -template<> QDir requireIsType(const QJsonValue &value, const QString &what) +template <> +QDir requireIsType(const QJsonValue& value, const QString& what) { const QString string = requireIsType(value, what); // FIXME: does not handle invalid characters! return QDir::current().absoluteFilePath(string); } -template<> QUuid requireIsType(const QJsonValue &value, const QString &what) +template <> +QUuid requireIsType(const QJsonValue& value, const QString& what) { const QString string = requireIsType(value, what); const QUuid uuid = QUuid(string); - if (uuid.toString() != string) // converts back => valid + if (uuid.toString() != string) // converts back => valid { throw JsonException(what + " is not a valid UUID"); } return uuid; } -template<> QJsonObject requireIsType(const QJsonValue &value, const QString &what) +template <> +QJsonObject requireIsType(const QJsonValue& value, const QString& what) { - if (!value.isObject()) - { + if (!value.isObject()) { throw JsonException(what + " is not an object"); } return value.toObject(); } -template<> QVariant requireIsType(const QJsonValue &value, const QString &what) +template <> +QVariant requireIsType(const QJsonValue& value, const QString& what) { - if (value.isNull() || value.isUndefined()) - { + if (value.isNull() || value.isUndefined()) { throw JsonException(what + " is null or undefined"); } return value.toVariant(); } -template<> QJsonValue requireIsType(const QJsonValue &value, const QString &what) +template <> +QJsonValue requireIsType(const QJsonValue& value, const QString& what) { - if (value.isNull() || value.isUndefined()) - { + if (value.isNull() || value.isUndefined()) { throw JsonException(what + " is null or undefined"); } return value; } -} +} // namespace Json diff --git a/launcher/Json.h b/launcher/Json.h index b11a356cc..28891f398 100644 --- a/launcher/Json.h +++ b/launcher/Json.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,74 +35,71 @@ #pragma once -#include -#include -#include #include -#include #include +#include +#include +#include +#include #include #include #include #include "Exception.h" -namespace Json -{ -class JsonException : public ::Exception -{ -public: - JsonException(const QString &message) : Exception(message) {} +namespace Json { +class JsonException : public ::Exception { + public: + JsonException(const QString& message) : Exception(message) {} }; /// @throw FileSystemException -void write(const QJsonDocument &doc, const QString &filename); +void write(const QJsonDocument& doc, const QString& filename); /// @throw FileSystemException -void write(const QJsonObject &object, const QString &filename); +void write(const QJsonObject& object, const QString& filename); /// @throw FileSystemException -void write(const QJsonArray &array, const QString &filename); +void write(const QJsonArray& array, const QString& filename); -QByteArray toText(const QJsonObject &obj); -QByteArray toText(const QJsonArray &array); +QByteArray toText(const QJsonObject& obj); +QByteArray toText(const QJsonArray& array); /// @throw JsonException -QJsonDocument requireDocument(const QByteArray &data, const QString &what = "Document"); +QJsonDocument requireDocument(const QByteArray& data, const QString& what = "Document"); /// @throw JsonException -QJsonDocument requireDocument(const QString &filename, const QString &what = "Document"); +QJsonDocument requireDocument(const QString& filename, const QString& what = "Document"); /// @throw JsonException -QJsonObject requireObject(const QJsonDocument &doc, const QString &what = "Document"); +QJsonObject requireObject(const QJsonDocument& doc, const QString& what = "Document"); /// @throw JsonException -QJsonArray requireArray(const QJsonDocument &doc, const QString &what = "Document"); +QJsonArray requireArray(const QJsonDocument& doc, const QString& what = "Document"); /////////////////// WRITING //////////////////// -void writeString(QJsonObject & to, const QString &key, const QString &value); -void writeStringList(QJsonObject & to, const QString &key, const QStringList &values); +void writeString(QJsonObject& to, const QString& key, const QString& value); +void writeStringList(QJsonObject& to, const QString& key, const QStringList& values); -template -QJsonValue toJson(const T &t) +template +QJsonValue toJson(const T& t) { return QJsonValue(t); } -template<> -QJsonValue toJson(const QUrl &url); -template<> -QJsonValue toJson(const QByteArray &data); -template<> -QJsonValue toJson(const QDateTime &datetime); -template<> -QJsonValue toJson(const QDir &dir); -template<> -QJsonValue toJson(const QUuid &uuid); -template<> -QJsonValue toJson(const QVariant &variant); +template <> +QJsonValue toJson(const QUrl& url); +template <> +QJsonValue toJson(const QByteArray& data); +template <> +QJsonValue toJson(const QDateTime& datetime); +template <> +QJsonValue toJson(const QDir& dir); +template <> +QJsonValue toJson(const QUuid& uuid); +template <> +QJsonValue toJson(const QVariant& variant); -template -QJsonArray toJsonArray(const QList &container) +template +QJsonArray toJsonArray(const QList& container) { QJsonArray array; - for (const T item : container) - { + for (const T item : container) { array.append(toJson(item)); } return array; @@ -112,106 +109,110 @@ QJsonArray toJsonArray(const QList &container) /// @throw JsonException template -T requireIsType(const QJsonValue &value, const QString &what = "Value"); +T requireIsType(const QJsonValue& value, const QString& what = "Value"); /// @throw JsonException -template<> double requireIsType(const QJsonValue &value, const QString &what); +template <> +double requireIsType(const QJsonValue& value, const QString& what); /// @throw JsonException -template<> bool requireIsType(const QJsonValue &value, const QString &what); +template <> +bool requireIsType(const QJsonValue& value, const QString& what); /// @throw JsonException -template<> int requireIsType(const QJsonValue &value, const QString &what); +template <> +int requireIsType(const QJsonValue& value, const QString& what); /// @throw JsonException -template<> QJsonObject requireIsType(const QJsonValue &value, const QString &what); +template <> +QJsonObject requireIsType(const QJsonValue& value, const QString& what); /// @throw JsonException -template<> QJsonArray requireIsType(const QJsonValue &value, const QString &what); +template <> +QJsonArray requireIsType(const QJsonValue& value, const QString& what); /// @throw JsonException -template<> QJsonValue requireIsType(const QJsonValue &value, const QString &what); +template <> +QJsonValue requireIsType(const QJsonValue& value, const QString& what); /// @throw JsonException -template<> QByteArray requireIsType(const QJsonValue &value, const QString &what); +template <> +QByteArray requireIsType(const QJsonValue& value, const QString& what); /// @throw JsonException -template<> QDateTime requireIsType(const QJsonValue &value, const QString &what); +template <> +QDateTime requireIsType(const QJsonValue& value, const QString& what); /// @throw JsonException -template<> QVariant requireIsType(const QJsonValue &value, const QString &what); +template <> +QVariant requireIsType(const QJsonValue& value, const QString& what); /// @throw JsonException -template<> QString requireIsType(const QJsonValue &value, const QString &what); +template <> +QString requireIsType(const QJsonValue& value, const QString& what); /// @throw JsonException -template<> QUuid requireIsType(const QJsonValue &value, const QString &what); +template <> +QUuid requireIsType(const QJsonValue& value, const QString& what); /// @throw JsonException -template<> QDir requireIsType(const QJsonValue &value, const QString &what); +template <> +QDir requireIsType(const QJsonValue& value, const QString& what); /// @throw JsonException -template<> QUrl requireIsType(const QJsonValue &value, const QString &what); +template <> +QUrl requireIsType(const QJsonValue& value, const QString& what); // the following functions are higher level functions, that make use of the above functions for // type conversion template -T ensureIsType(const QJsonValue &value, const T default_ = T(), const QString &what = "Value") +T ensureIsType(const QJsonValue& value, const T default_ = T(), const QString& what = "Value") { - if (value.isUndefined() || value.isNull()) - { + if (value.isUndefined() || value.isNull()) { return default_; } - try - { + try { return requireIsType(value, what); - } - catch (const JsonException &) - { + } catch (const JsonException&) { return default_; } } /// @throw JsonException template -T requireIsType(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") +T requireIsType(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__") { const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); - if (!parent.contains(key)) - { + if (!parent.contains(key)) { throw JsonException(localWhat + "s parent does not contain " + localWhat); } return requireIsType(parent.value(key), localWhat); } template -T ensureIsType(const QJsonObject &parent, const QString &key, const T default_ = T(), const QString &what = "__placeholder__") +T ensureIsType(const QJsonObject& parent, const QString& key, const T default_ = T(), const QString& what = "__placeholder__") { const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); - if (!parent.contains(key)) - { + if (!parent.contains(key)) { return default_; } return ensureIsType(parent.value(key), default_, localWhat); } template -QVector requireIsArrayOf(const QJsonDocument &doc) +QVector requireIsArrayOf(const QJsonDocument& doc) { const QJsonArray array = requireArray(doc); QVector out; - for (const QJsonValue val : array) - { + for (const QJsonValue val : array) { out.append(requireIsType(val, "Document")); } return out; } template -QVector ensureIsArrayOf(const QJsonValue &value, const QString &what = "Value") +QVector ensureIsArrayOf(const QJsonValue& value, const QString& what = "Value") { const QJsonArray array = ensureIsType(value, QJsonArray(), what); QVector out; - for (const QJsonValue val : array) - { + for (const QJsonValue val : array) { out.append(requireIsType(val, what)); } return out; } template -QVector ensureIsArrayOf(const QJsonValue &value, const QVector default_, const QString &what = "Value") +QVector ensureIsArrayOf(const QJsonValue& value, const QVector default_, const QString& what = "Value") { - if (value.isUndefined()) - { + if (value.isUndefined()) { return default_; } return ensureIsArrayOf(value, what); @@ -219,45 +220,46 @@ QVector ensureIsArrayOf(const QJsonValue &value, const QVector default_, c /// @throw JsonException template -QVector requireIsArrayOf(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") +QVector requireIsArrayOf(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__") { const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); - if (!parent.contains(key)) - { + if (!parent.contains(key)) { throw JsonException(localWhat + "s parent does not contain " + localWhat); } return ensureIsArrayOf(parent.value(key), localWhat); } template -QVector ensureIsArrayOf(const QJsonObject &parent, const QString &key, - const QVector &default_ = QVector(), const QString &what = "__placeholder__") +QVector ensureIsArrayOf(const QJsonObject& parent, + const QString& key, + const QVector& default_ = QVector(), + const QString& what = "__placeholder__") { const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); - if (!parent.contains(key)) - { + if (!parent.contains(key)) { return default_; } return ensureIsArrayOf(parent.value(key), default_, localWhat); } // this macro part could be replaced by variadic functions that just pass on their arguments, but that wouldn't work well with IDE helpers -#define JSON_HELPERFUNCTIONS(NAME, TYPE) \ - inline TYPE require##NAME(const QJsonValue &value, const QString &what = "Value") \ - { \ - return requireIsType(value, what); \ - } \ - inline TYPE ensure##NAME(const QJsonValue &value, const TYPE default_ = TYPE(), const QString &what = "Value") \ - { \ - return ensureIsType(value, default_, what); \ - } \ - inline TYPE require##NAME(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") \ - { \ - return requireIsType(parent, key, what); \ - } \ - inline TYPE ensure##NAME(const QJsonObject &parent, const QString &key, const TYPE default_ = TYPE(), const QString &what = "__placeholder") \ - { \ - return ensureIsType(parent, key, default_, what); \ +#define JSON_HELPERFUNCTIONS(NAME, TYPE) \ + inline TYPE require##NAME(const QJsonValue& value, const QString& what = "Value") \ + { \ + return requireIsType(value, what); \ + } \ + inline TYPE ensure##NAME(const QJsonValue& value, const TYPE default_ = TYPE(), const QString& what = "Value") \ + { \ + return ensureIsType(value, default_, what); \ + } \ + inline TYPE require##NAME(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__") \ + { \ + return requireIsType(parent, key, what); \ + } \ + inline TYPE ensure##NAME(const QJsonObject& parent, const QString& key, const TYPE default_ = TYPE(), \ + const QString& what = "__placeholder") \ + { \ + return ensureIsType(parent, key, default_, what); \ } JSON_HELPERFUNCTIONS(Array, QJsonArray) @@ -276,5 +278,5 @@ JSON_HELPERFUNCTIONS(Variant, QVariant) #undef JSON_HELPERFUNCTIONS -} +} // namespace Json using JSONValidationError = Json::JsonException; diff --git a/launcher/KonamiCode.cpp b/launcher/KonamiCode.cpp index 46a2a0b2e..f9ec3b89d 100644 --- a/launcher/KonamiCode.cpp +++ b/launcher/KonamiCode.cpp @@ -1,42 +1,26 @@ #include "KonamiCode.h" -#include #include +#include namespace { -const std::array konamiCode = -{ - { - Qt::Key_Up, Qt::Key_Up, - Qt::Key_Down, Qt::Key_Down, - Qt::Key_Left, Qt::Key_Right, - Qt::Key_Left, Qt::Key_Right, - Qt::Key_B, Qt::Key_A - } -}; -} - -KonamiCode::KonamiCode(QObject* parent) : QObject(parent) -{ +const std::array konamiCode = { { Qt::Key_Up, Qt::Key_Up, Qt::Key_Down, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, + Qt::Key_Left, Qt::Key_Right, Qt::Key_B, Qt::Key_A } }; } +KonamiCode::KonamiCode(QObject* parent) : QObject(parent) {} void KonamiCode::input(QEvent* event) { - if( event->type() == QEvent::KeyPress ) - { - QKeyEvent *keyEvent = static_cast( event ); + if (event->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast(event); auto key = Qt::Key(keyEvent->key()); - if(key == konamiCode[m_progress]) - { - m_progress ++; - } - else - { + if (key == konamiCode[m_progress]) { + m_progress++; + } else { m_progress = 0; } - if(m_progress == static_cast(konamiCode.size())) - { + if (m_progress == static_cast(konamiCode.size())) { m_progress = 0; emit triggered(); } diff --git a/launcher/KonamiCode.h b/launcher/KonamiCode.h index 3d320ae7f..5c21e2ff3 100644 --- a/launcher/KonamiCode.h +++ b/launcher/KonamiCode.h @@ -2,16 +2,15 @@ #include -class KonamiCode : public QObject -{ +class KonamiCode : public QObject { Q_OBJECT -public: - KonamiCode(QObject *parent = 0); - void input(QEvent *event); + public: + KonamiCode(QObject* parent = 0); + void input(QEvent* event); -signals: + signals: void triggered(); -private: + private: int m_progress = 0; }; diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 5d84b3bf6..380489e06 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -34,44 +34,41 @@ */ #include "LaunchController.h" -#include "minecraft/auth/AccountList.h" #include "Application.h" +#include "minecraft/auth/AccountList.h" -#include "ui/MainWindow.h" #include "ui/InstanceWindow.h" +#include "ui/MainWindow.h" #include "ui/dialogs/CustomMessageBox.h" -#include "ui/dialogs/ProfileSelectDialog.h" -#include "ui/dialogs/ProgressDialog.h" #include "ui/dialogs/EditAccountDialog.h" +#include "ui/dialogs/ProfileSelectDialog.h" #include "ui/dialogs/ProfileSetupDialog.h" +#include "ui/dialogs/ProgressDialog.h" -#include -#include -#include -#include -#include #include +#include +#include +#include +#include #include +#include #include "BuildConfig.h" #include "JavaCommon.h" -#include "tasks/Task.h" -#include "minecraft/auth/AccountTask.h" #include "launch/steps/TextPrint.h" +#include "minecraft/auth/AccountTask.h" +#include "tasks/Task.h" -LaunchController::LaunchController(QObject *parent) : Task(parent) -{ -} +LaunchController::LaunchController(QObject* parent) : Task(parent) {} void LaunchController::executeTask() { - if (!m_instance) - { + if (!m_instance) { emitFailed(tr("No instance specified!")); return; } - if(!JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget)) { + if (!JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget)) { emitFailed(tr("Invalid Java arguments specified. Please fix this first.")); return; } @@ -81,32 +78,25 @@ void LaunchController::executeTask() void LaunchController::decideAccount() { - if(m_accountToUse) { + if (m_accountToUse) { return; } // Find an account to use. auto accounts = APPLICATION->accounts(); - if (accounts->count() <= 0) - { + if (accounts->count() <= 0) { // Tell the user they need to log in at least one account in order to play. - auto reply = CustomMessageBox::selectable( - m_parentWidget, - tr("No Accounts"), - tr("In order to play Minecraft, you must have at least one Microsoft or Mojang " - "account logged in. Mojang accounts can only be used offline. " - "Would you like to open the account manager to add an account now?"), - QMessageBox::Information, - QMessageBox::Yes | QMessageBox::No - )->exec(); + auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"), + tr("In order to play Minecraft, you must have at least one Microsoft or Mojang " + "account logged in. Mojang accounts can only be used offline. " + "Would you like to open the account manager to add an account now?"), + QMessageBox::Information, QMessageBox::Yes | QMessageBox::No) + ->exec(); - if (reply == QMessageBox::Yes) - { + if (reply == QMessageBox::Yes) { // Open the account manager. APPLICATION->ShowGlobalSettings(m_parentWidget, "accounts"); - } - else if (reply == QMessageBox::No) - { + } else if (reply == QMessageBox::No) { // Do not open "profile select" dialog. return; } @@ -121,14 +111,10 @@ void LaunchController::decideAccount() m_accountToUse = accounts->at(instanceAccountIndex); } - if (!m_accountToUse) - { + if (!m_accountToUse) { // If no default account is set, ask the user which one to use. - ProfileSelectDialog selectDialog( - tr("Which account would you like to use?"), - ProfileSelectDialog::GlobalDefaultCheckbox, - m_parentWidget - ); + ProfileSelectDialog selectDialog(tr("Which account would you like to use?"), ProfileSelectDialog::GlobalDefaultCheckbox, + m_parentWidget); selectDialog.exec(); @@ -142,13 +128,12 @@ void LaunchController::decideAccount() } } - -void LaunchController::login() { +void LaunchController::login() +{ decideAccount(); // if no account is selected, we bail - if (!m_accountToUse) - { + if (!m_accountToUse) { emitFailed(tr("No account selected for launch.")); return; } @@ -157,15 +142,11 @@ void LaunchController::login() { bool tryagain = true; unsigned int tries = 0; - while (tryagain) - { + while (tryagain) { if (tries > 0 && tries % 3 == 0) { - auto result = QMessageBox::question( - m_parentWidget, - tr("Continue launch?"), - tr("It looks like we couldn't launch after %1 tries. Do you want to continue trying?") - .arg(tries) - ); + auto result = + QMessageBox::question(m_parentWidget, tr("Continue launch?"), + tr("It looks like we couldn't launch after %1 tries. Do you want to continue trying?").arg(tries)); if (result == QMessageBox::No) { emitAborted(); @@ -179,60 +160,48 @@ void LaunchController::login() { m_accountToUse->fillSession(m_session); // Launch immediately in true offline mode - if(m_accountToUse->isOffline()) { + if (m_accountToUse->isOffline()) { launchInstance(); return; } - switch(m_accountToUse->accountState()) { + switch (m_accountToUse->accountState()) { case AccountState::Offline: { m_session->wants_online = false; } /* fallthrough */ case AccountState::Online: { - if(!m_session->wants_online) { + if (!m_session->wants_online) { // we ask the user for a player name bool ok = false; QString message = tr("Choose your offline mode player name."); - if(m_session->demo) { + if (m_session->demo) { message = tr("Choose your demo mode player name."); } QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString(); QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName; - QString name = QInputDialog::getText( - m_parentWidget, - tr("Player name"), - message, - QLineEdit::Normal, - usedname, - &ok - ); - if (!ok) - { + QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok); + if (!ok) { tryagain = false; break; } - if (name.length()) - { + if (name.length()) { usedname = name; APPLICATION->settings()->set("LastOfflinePlayerName", usedname); } m_session->MakeOffline(usedname); // offline flavored game from here :3 } - if(m_accountToUse->ownsMinecraft()) { - if(!m_accountToUse->hasProfile()) { + if (m_accountToUse->ownsMinecraft()) { + if (!m_accountToUse->hasProfile()) { // Now handle setting up a profile name here... ProfileSetupDialog dialog(m_accountToUse, m_parentWidget); - if (dialog.exec() == QDialog::Accepted) - { + if (dialog.exec() == QDialog::Accepted) { tryagain = true; continue; - } - else - { + } else { emitFailed(tr("Received undetermined session status during login.")); return; } @@ -240,24 +209,24 @@ void LaunchController::login() { // we own Minecraft, there is a profile, it's all ready to go! launchInstance(); return; - } - else { + } else { // play demo ? QMessageBox box(m_parentWidget); box.setWindowTitle(tr("Play demo?")); - box.setText(tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play the demo?")); + box.setText( + tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play " + "the demo?")); box.setIcon(QMessageBox::Warning); auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole); auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole); box.setDefaultButton(cancelButton); box.exec(); - if(box.clickedButton() == demoButton) { + if (box.clickedButton() == demoButton) { // play demo here m_session->MakeDemo(); launchInstance(); - } - else { + } else { emitFailed(tr("Launch cancelled - account does not own Minecraft.")); } } @@ -272,8 +241,7 @@ void LaunchController::login() { case AccountState::Working: { // refresh is in progress, we need to wait for it to finish to proceed. ProgressDialog progDialog(m_parentWidget); - if (m_online) - { + if (m_online) { progDialog.setSkipButton(true, tr("Play Offline")); } auto task = m_accountToUse->currentTask(); @@ -288,37 +256,24 @@ void LaunchController::login() { */ case AccountState::Expired: { auto errorString = tr("The account has expired and needs to be logged into manually again."); - QMessageBox::warning( - m_parentWidget, - tr("Account refresh failed"), - errorString, - QMessageBox::StandardButton::Ok, - QMessageBox::StandardButton::Ok - ); + QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), errorString, QMessageBox::StandardButton::Ok, + QMessageBox::StandardButton::Ok); 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 - ); + 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( - m_parentWidget, - tr("Account gone"), - errorString, - QMessageBox::StandardButton::Ok, - QMessageBox::StandardButton::Ok - ); + 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(m_parentWidget, tr("Account gone"), errorString, QMessageBox::StandardButton::Ok, + QMessageBox::StandardButton::Ok); emitFailed(errorString); return; } @@ -332,48 +287,45 @@ void LaunchController::launchInstance() Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL"); Q_ASSERT_X(m_session.get() != nullptr, "launchInstance", "session is NULL"); - if(!m_instance->reloadSettings()) - { + if (!m_instance->reloadSettings()) { QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't load the instance profile.")); emitFailed(tr("Couldn't load the instance profile.")); return; } m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin); - if (!m_launcher) - { + if (!m_launcher) { emitFailed(tr("Couldn't instantiate a launcher.")); return; } - auto console = qobject_cast(m_parentWidget); + auto console = qobject_cast(m_parentWidget); auto showConsole = m_instance->settings()->get("ShowConsole").toBool(); - if(!console && showConsole) - { + if (!console && showConsole) { APPLICATION->showInstanceWindow(m_instance); } connect(m_launcher.get(), &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch); connect(m_launcher.get(), &LaunchTask::succeeded, this, &LaunchController::onSucceeded); - connect(m_launcher.get(), &LaunchTask::failed, this, &LaunchController::onFailed); + connect(m_launcher.get(), &LaunchTask::failed, this, &LaunchController::onFailed); connect(m_launcher.get(), &LaunchTask::requestProgress, this, &LaunchController::onProgressRequested); // Prepend Online and Auth Status QString online_mode; - if(m_session->wants_online) { + if (m_session->wants_online) { online_mode = "online"; // Prepend Server Status - QStringList servers = {"authserver.mojang.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com"}; + QStringList servers = { "authserver.mojang.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" }; QString resolved_servers = ""; QHostInfo host_info; - for(QString server : servers) { + for (QString server : servers) { host_info = QHostInfo::fromName(server); resolved_servers = resolved_servers + server + " resolves to:\n ["; - if(!host_info.addresses().isEmpty()) { - for(QHostAddress address : host_info.addresses()) { + if (!host_info.addresses().isEmpty()) { + for (QHostAddress address : host_info.addresses()) { resolved_servers = resolved_servers + address.toString(); - if(!host_info.addresses().endsWith(address)) { + if (!host_info.addresses().endsWith(address)) { resolved_servers = resolved_servers + ", "; } } @@ -387,37 +339,40 @@ void LaunchController::launchInstance() online_mode = m_demo ? "demo" : "offline"; } - m_launcher->prependStep(makeShared(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher)); + m_launcher->prependStep( + makeShared(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher)); // Prepend Version - m_launcher->prependStep(makeShared(m_launcher.get(), BuildConfig.LAUNCHER_DISPLAYNAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher)); + { + auto versionString = QString("%1 version: %2 (%3)") + .arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString(), BuildConfig.BUILD_PLATFORM); + m_launcher->prependStep(makeShared(m_launcher.get(), versionString + "\n\n", MessageLevel::Launcher)); + } m_launcher->start(); } void LaunchController::readyForLaunch() { - if (!m_profiler) - { + if (!m_profiler) { m_launcher->proceed(); return; } QString error; - if (!m_profiler->check(&error)) - { + if (!m_profiler->check(&error)) { m_launcher->abort(); QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't start profiler: %1").arg(error)); emitFailed("Profiler startup failed!"); return; } - BaseProfiler *profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this); + BaseProfiler* profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this); - connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString & message) - { + connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString& message) { QMessageBox msg; msg.setText(tr("The game launch is delayed until you press the " - "button. This is the right time to setup the profiler, as the " - "profiler server is running now.\n\n%1").arg(message)); + "button. This is the right time to setup the profiler, as the " + "profiler server is running now.\n\n%1") + .arg(message)); msg.setWindowTitle(tr("Waiting.")); msg.setIcon(QMessageBox::Information); msg.addButton(tr("Launch"), QMessageBox::AcceptRole); @@ -425,8 +380,7 @@ void LaunchController::readyForLaunch() msg.exec(); m_launcher->proceed(); }); - connect(profilerInstance, &BaseProfiler::abortLaunch, [this](const QString & message) - { + connect(profilerInstance, &BaseProfiler::abortLaunch, [this](const QString& message) { QMessageBox msg; msg.setText(tr("Couldn't start the profiler: %1").arg(message)); msg.setWindowTitle(tr("Error")); @@ -447,8 +401,7 @@ void LaunchController::onSucceeded() void LaunchController::onFailed(QString reason) { - if(m_instance->settings()->get("ShowConsoleOnError").toBool()) - { + if (m_instance->settings()->get("ShowConsoleOnError").toBool()) { APPLICATION->showInstanceWindow(m_instance, "console"); } emitFailed(reason); @@ -464,21 +417,18 @@ void LaunchController::onProgressRequested(Task* task) bool LaunchController::abort() { - if(!m_launcher) - { + if (!m_launcher) { return true; } - if(!m_launcher->canAbort()) - { + if (!m_launcher->canAbort()) { return false; } - auto response = CustomMessageBox::selectable( - m_parentWidget, tr("Kill Minecraft?"), - tr("This can cause the instance to get corrupted and should only be used if Minecraft " - "is frozen for some reason"), - QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec(); - if (response == QMessageBox::Yes) - { + auto response = CustomMessageBox::selectable(m_parentWidget, tr("Kill Minecraft?"), + tr("This can cause the instance to get corrupted and should only be used if Minecraft " + "is frozen for some reason"), + QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) + ->exec(); + if (response == QMessageBox::Yes) { return m_launcher->abort(); } return false; diff --git a/launcher/LaunchController.h b/launcher/LaunchController.h index af6c98d18..f1c88afb7 100644 --- a/launcher/LaunchController.h +++ b/launcher/LaunchController.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -34,81 +34,61 @@ */ #pragma once -#include #include #include +#include -#include "minecraft/launch/MinecraftServerTarget.h" #include "minecraft/auth/MinecraftAccount.h" +#include "minecraft/launch/MinecraftServerTarget.h" class InstanceWindow; -class LaunchController: public Task -{ +class LaunchController : public Task { Q_OBJECT -public: + public: void executeTask() override; - LaunchController(QObject * parent = nullptr); + LaunchController(QObject* parent = nullptr); virtual ~LaunchController(){}; - void setInstance(InstancePtr instance) { - m_instance = instance; - } + void setInstance(InstancePtr instance) { m_instance = instance; } - InstancePtr instance() { - return m_instance; - } + InstancePtr instance() { return m_instance; } - void setOnline(bool online) { - m_online = online; - } + void setOnline(bool online) { m_online = online; } - void setDemo(bool demo) { - m_demo = demo; - } + void setDemo(bool demo) { m_demo = demo; } - void setProfiler(BaseProfilerFactory *profiler) { - m_profiler = profiler; - } + void setProfiler(BaseProfilerFactory* profiler) { m_profiler = profiler; } - void setParentWidget(QWidget * widget) { - m_parentWidget = widget; - } + void setParentWidget(QWidget* widget) { m_parentWidget = widget; } - void setServerToJoin(MinecraftServerTargetPtr serverToJoin) { - m_serverToJoin = std::move(serverToJoin); - } + void setServerToJoin(MinecraftServerTargetPtr serverToJoin) { m_serverToJoin = std::move(serverToJoin); } - void setAccountToUse(MinecraftAccountPtr accountToUse) { - m_accountToUse = std::move(accountToUse); - } + void setAccountToUse(MinecraftAccountPtr accountToUse) { m_accountToUse = std::move(accountToUse); } - QString id() - { - return m_instance->id(); - } + QString id() { return m_instance->id(); } bool abort() override; -private: + private: void login(); void launchInstance(); void decideAccount(); -private slots: + private slots: void readyForLaunch(); void onSucceeded(); void onFailed(QString reason); - void onProgressRequested(Task *task); + void onProgressRequested(Task* task); -private: - BaseProfilerFactory *m_profiler = nullptr; + private: + BaseProfilerFactory* m_profiler = nullptr; bool m_online = true; bool m_demo = false; InstancePtr m_instance; - QWidget * m_parentWidget = nullptr; - InstanceWindow *m_console = nullptr; + QWidget* m_parentWidget = nullptr; + InstanceWindow* m_console = nullptr; MinecraftAccountPtr m_accountToUse = nullptr; AuthSessionPtr m_session; shared_qobject_ptr m_launcher; diff --git a/launcher/LoggedProcess.cpp b/launcher/LoggedProcess.cpp index d70f6d005..fadd64e68 100644 --- a/launcher/LoggedProcess.cpp +++ b/launcher/LoggedProcess.cpp @@ -2,7 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022,2023 Sefa Eyeoglu - * Copyright (c) 2023 flowln + * Copyright (c) 2023 flowln * * 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 @@ -39,7 +39,7 @@ #include #include "MessageLevel.h" -LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent) +LoggedProcess::LoggedProcess(QObject* parent) : QProcess(parent) { // QProcess has a strange interface... let's map a lot of those into a few. connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut); @@ -51,8 +51,7 @@ LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent) LoggedProcess::~LoggedProcess() { - if(m_is_detachable) - { + if (m_is_detachable) { setProcessState(QProcess::NotRunning); } } @@ -66,14 +65,9 @@ QStringList LoggedProcess::reprocess(const QByteArray& data, QTextDecoder& decod m_leftover_line = ""; } -#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) - auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, QString::SkipEmptyParts); -#else - auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, Qt::SkipEmptyParts); -#endif + auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed); - if (!str.endsWith(QChar::LineFeed)) - m_leftover_line = lines.takeLast(); + m_leftover_line = lines.takeLast(); return lines; } @@ -95,39 +89,31 @@ void LoggedProcess::on_exit(int exit_code, QProcess::ExitStatus status) m_exit_code = exit_code; // based on state, send signals - if (!m_is_aborting) - { - if (status == QProcess::NormalExit) - { + if (!m_is_aborting) { + if (status == QProcess::NormalExit) { //: Message displayed on instance exit - emit log({tr("Process exited with code %1.").arg(exit_code)}, MessageLevel::Launcher); + emit log({ tr("Process exited with code %1.").arg(exit_code) }, MessageLevel::Launcher); changeState(LoggedProcess::Finished); - } - else - { + } else { //: Message displayed on instance crashed - if(exit_code == -1) - emit log({tr("Process crashed.")}, MessageLevel::Launcher); + if (exit_code == -1) + emit log({ tr("Process crashed.") }, MessageLevel::Launcher); else - emit log({tr("Process crashed with exitcode %1.").arg(exit_code)}, MessageLevel::Launcher); + emit log({ tr("Process crashed with exitcode %1.").arg(exit_code) }, MessageLevel::Launcher); changeState(LoggedProcess::Crashed); } - } - else - { + } else { //: Message displayed after the instance exits due to kill request - emit log({tr("Process was killed by user.")}, MessageLevel::Error); + emit log({ tr("Process was killed by user.") }, MessageLevel::Error); changeState(LoggedProcess::Aborted); } } void LoggedProcess::on_error(QProcess::ProcessError error) { - switch(error) - { - case QProcess::FailedToStart: - { - emit log({tr("The process failed to start.")}, MessageLevel::Fatal); + switch (error) { + case QProcess::FailedToStart: { + emit log({ tr("The process failed to start.") }, MessageLevel::Fatal); changeState(LoggedProcess::FailedToStart); break; } @@ -154,7 +140,7 @@ int LoggedProcess::exitCode() const void LoggedProcess::changeState(LoggedProcess::State state) { - if(state == m_state) + if (state == m_state) return; m_state = state; emit stateChanged(m_state); @@ -167,24 +153,19 @@ LoggedProcess::State LoggedProcess::state() const void LoggedProcess::on_stateChange(QProcess::ProcessState state) { - switch(state) - { + switch (state) { case QProcess::NotRunning: - break; // let's not - there are too many that handle this already. - case QProcess::Starting: - { - if(m_state != LoggedProcess::NotRunning) - { - qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Starting; + break; // let's not - there are too many that handle this already. + case QProcess::Starting: { + if (m_state != LoggedProcess::NotRunning) { + qWarning() << "Wrong state change for process from state" << m_state << "to" << (int)LoggedProcess::Starting; } changeState(LoggedProcess::Starting); return; } - case QProcess::Running: - { - if(m_state != LoggedProcess::Starting) - { - qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Running; + case QProcess::Running: { + if (m_state != LoggedProcess::Starting) { + qWarning() << "Wrong state change for process from state" << m_state << "to" << (int)LoggedProcess::Running; } changeState(LoggedProcess::Running); return; diff --git a/launcher/LoggedProcess.h b/launcher/LoggedProcess.h index af3ed79f4..46bdaa830 100644 --- a/launcher/LoggedProcess.h +++ b/launcher/LoggedProcess.h @@ -43,22 +43,12 @@ * This is a basic process. * It has line-based logging support and hides some of the nasty bits. */ -class LoggedProcess : public QProcess -{ -Q_OBJECT -public: - enum State - { - NotRunning, - Starting, - FailedToStart, - Running, - Finished, - Crashed, - Aborted - }; +class LoggedProcess : public QProcess { + Q_OBJECT + public: + enum State { NotRunning, Starting, FailedToStart, Running, Finished, Crashed, Aborted }; -public: + public: explicit LoggedProcess(QObject* parent = 0); virtual ~LoggedProcess(); @@ -67,30 +57,29 @@ public: void setDetachable(bool detachable); -signals: + signals: void log(QStringList lines, MessageLevel::Enum level); void stateChanged(LoggedProcess::State state); -public slots: + public slots: /** * @brief kill the process - equivalent to kill -9 */ void kill(); - -private slots: + private slots: void on_stdErr(); void on_stdOut(); void on_exit(int exit_code, QProcess::ExitStatus status); void on_error(QProcess::ProcessError error); void on_stateChange(QProcess::ProcessState); -private: + private: void changeState(LoggedProcess::State state); QStringList reprocess(const QByteArray& data, QTextDecoder& decoder); -private: + private: QTextDecoder m_err_decoder = QTextDecoder(QTextCodec::codecForLocale()); QTextDecoder m_out_decoder = QTextDecoder(QTextCodec::codecForLocale()); QString m_leftover_line; diff --git a/launcher/MMCTime.cpp b/launcher/MMCTime.cpp index 1702ec066..3972dbd53 100644 --- a/launcher/MMCTime.cpp +++ b/launcher/MMCTime.cpp @@ -17,30 +17,29 @@ #include -#include #include +#include #include -QString Time::prettifyDuration(int64_t duration) { - int seconds = (int) (duration % 60); +QString Time::prettifyDuration(int64_t duration) +{ + int seconds = (int)(duration % 60); duration /= 60; - int minutes = (int) (duration % 60); + int minutes = (int)(duration % 60); duration /= 60; - int hours = (int) (duration % 24); - int days = (int) (duration / 24); - if((hours == 0)&&(days == 0)) - { + int hours = (int)(duration % 24); + int days = (int)(duration / 24); + if ((hours == 0) && (days == 0)) { return QObject::tr("%1min %2s").arg(minutes).arg(seconds); } - if (days == 0) - { + if (days == 0) { return QObject::tr("%1h %2min").arg(hours).arg(minutes); } return QObject::tr("%1d %2h %3min").arg(days).arg(hours).arg(minutes); } -QString Time::humanReadableDuration(double duration, int precision) { - +QString Time::humanReadableDuration(double duration, int precision) +{ using days = std::chrono::duration>; QString outStr; @@ -48,10 +47,10 @@ QString Time::humanReadableDuration(double duration, int precision) { bool neg = false; if (duration < 0) { - neg = true; // flag - duration *= -1; // invert + neg = true; // flag + duration *= -1; // invert } - + auto std_duration = std::chrono::duration(duration); auto d = std::chrono::duration_cast(std_duration); std_duration -= d; @@ -78,22 +77,22 @@ QString Time::humanReadableDuration(double duration, int precision) { if (hc) { if (dc) os << " "; - os << qSetFieldWidth(2) << hc << QObject::tr("h"); // hours + os << qSetFieldWidth(2) << hc << QObject::tr("h"); // hours } if (mc) { if (dc || hc) os << " "; - os << qSetFieldWidth(2) << mc << QObject::tr("m"); // minutes + os << qSetFieldWidth(2) << mc << QObject::tr("m"); // minutes } if (dc || hc || mc || sc) { if (dc || hc || mc) os << " "; - os << qSetFieldWidth(2) << sc << QObject::tr("s"); // seconds + os << qSetFieldWidth(2) << sc << QObject::tr("s"); // seconds } if ((msc && (precision > 0)) || !(dc || hc || mc || sc)) { if (dc || hc || mc || sc) os << " "; - os << qSetFieldWidth(0) << qSetRealNumberPrecision(precision) << msc << QObject::tr("ms"); // miliseconds + os << qSetFieldWidth(0) << qSetRealNumberPrecision(precision) << msc << QObject::tr("ms"); // miliseconds } os.flush(); diff --git a/launcher/MMCTime.h b/launcher/MMCTime.h index 6a5780b44..b7d34b5d8 100644 --- a/launcher/MMCTime.h +++ b/launcher/MMCTime.h @@ -25,10 +25,10 @@ QString prettifyDuration(int64_t duration); /** * @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms`. * miliseconds are only included if `precision` is greater than 0. - * + * * @param duration a number of seconds as floating point * @param precision number of decmial points to display on fractons of a second, defualts to 0. - * @return QString + * @return QString */ QString humanReadableDuration(double duration, int precision = 0); -} +} // namespace Time diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 1a336375b..acd6bf7e4 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 Trial97 * * 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 @@ -33,56 +34,50 @@ * limitations under the License. */ +#include "MMCZip.h" #include #include #include -#include "MMCZip.h" #include "FileSystem.h" #include #include +#include +namespace MMCZip { // ours -bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet &contained, const FilterFunction filter) +bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet& contained, const FilterFunction filter) { QuaZip modZip(from.filePath()); modZip.open(QuaZip::mdUnzip); QuaZipFile fileInsideMod(&modZip); QuaZipFile zipOutFile(into); - for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile()) - { + for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile()) { QString filename = modZip.getCurrentFileName(); - if (filter && !filter(filename)) - { - qDebug() << "Skipping file " << filename << " from " - << from.fileName() << " - filtered"; + if (filter && !filter(filename)) { + qDebug() << "Skipping file " << filename << " from " << from.fileName() << " - filtered"; continue; } - if (contained.contains(filename)) - { - qDebug() << "Skipping already contained file " << filename << " from " - << from.fileName(); + if (contained.contains(filename)) { + qDebug() << "Skipping already contained file " << filename << " from " << from.fileName(); continue; } contained.insert(filename); - if (!fileInsideMod.open(QIODevice::ReadOnly)) - { + if (!fileInsideMod.open(QIODevice::ReadOnly)) { qCritical() << "Failed to open " << filename << " from " << from.fileName(); return false; } QuaZipNewInfo info_out(fileInsideMod.getActualFileName()); - if (!zipOutFile.open(QIODevice::WriteOnly, info_out)) - { + if (!zipOutFile.open(QIODevice::WriteOnly, info_out)) { qCritical() << "Failed to open " << filename << " in the jar"; fileInsideMod.close(); return false; } - if (!JlCompress::copyData(fileInsideMod, zipOutFile)) - { + if (!JlCompress::copyData(fileInsideMod, zipOutFile)) { zipOutFile.close(); fileInsideMod.close(); qCritical() << "Failed to copy data of " << filename << " into the jar"; @@ -94,10 +89,11 @@ bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet &containe return true; } -bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, bool followSymlinks) +bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool followSymlinks) { QDir directory(dir); - if (!directory.exists()) return false; + if (!directory.exists()) + return false; for (auto e : files) { auto filePath = directory.relativeFilePath(e.absoluteFilePath()); @@ -109,17 +105,18 @@ bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, boo srcPath = e.canonicalFilePath(); } } - if( !JlCompress::compressFile(zip, srcPath, filePath)) return false; + if (!JlCompress::compressFile(zip, srcPath, filePath)) + return false; } return true; } -bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks) +bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks) { QuaZip zip(fileCompressed); QDir().mkpath(QFileInfo(fileCompressed).absolutePath()); - if(!zip.open(QuaZip::mdCreate)) { + if (!zip.open(QuaZip::mdCreate)) { QFile::remove(fileCompressed); return false; } @@ -127,7 +124,7 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList auto result = compressDirFiles(&zip, dir, files, followSymlinks); zip.close(); - if(zip.getZipError()!=0) { + if (zip.getZipError() != 0) { QFile::remove(fileCompressed); return false; } @@ -136,11 +133,10 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList } // ours -bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods) +bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods) { QuaZip zipOut(targetJarPath); - if (!zipOut.open(QuaZip::mdCreate)) - { + if (!zipOut.open(QuaZip::mdCreate)) { QFile::remove(targetJarPath); qCritical() << "Failed to open the minecraft.jar for modding"; return false; @@ -151,37 +147,29 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const // Modify the jar // This needs to be done in reverse-order to ensure we respect the loading order of components - for (auto i = mods.crbegin(); i != mods.crend(); i++) - { + for (auto i = mods.crbegin(); i != mods.crend(); i++) { const auto* mod = *i; // do not merge disabled mods. if (!mod->enabled()) continue; - if (mod->type() == ResourceType::ZIPFILE) - { - if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) - { + if (mod->type() == ResourceType::ZIPFILE) { + if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) { zipOut.close(); QFile::remove(targetJarPath); qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; return false; } - } - else if (mod->type() == ResourceType::SINGLEFILE) - { + } else if (mod->type() == ResourceType::SINGLEFILE) { // FIXME: buggy - does not work with addedFiles auto filename = mod->fileinfo(); - if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) - { + if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) { zipOut.close(); QFile::remove(targetJarPath); qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; return false; } addedFiles.insert(filename.fileName()); - } - else if (mod->type() == ResourceType::FOLDER) - { + } else if (mod->type() == ResourceType::FOLDER) { // untested, but seems to be unused / not possible to reach // FIXME: buggy - does not work with addedFiles auto filename = mod->fileinfo(); @@ -190,25 +178,21 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const dir.cdUp(); QString parent_dir = dir.absolutePath(); auto files = QFileInfoList(); - MMCZip::collectFileListRecursively(what_to_zip, nullptr, &files, nullptr); + 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)) - { + if (!compressDirFiles(&zipOut, parent_dir, files)) { zipOut.close(); QFile::remove(targetJarPath); qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; return false; } - qDebug() << "Adding folder " << filename.fileName() << " from " - << filename.absoluteFilePath(); - } - else - { + qDebug() << "Adding folder " << filename.fileName() << " from " << filename.absoluteFilePath(); + } else { // Make sure we do not continue launching when something is missing or undefined... zipOut.close(); QFile::remove(targetJarPath); @@ -217,8 +201,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const } } - if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key){return !key.contains("META-INF");})) - { + if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key) { return !key.contains("META-INF"); })) { zipOut.close(); QFile::remove(targetJarPath); qCritical() << "Failed to insert minecraft.jar contents."; @@ -227,8 +210,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const // Recompress the jar zipOut.close(); - if (zipOut.getZipError() != 0) - { + if (zipOut.getZipError() != 0) { QFile::remove(targetJarPath); qCritical() << "Failed to finalize minecraft.jar!"; return false; @@ -237,7 +219,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const } // ours -QString MMCZip::findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root) +QString findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root) { QuaZipDir rootDir(zip, root); for (auto&& fileName : rootDir.entryList(QDir::Files)) { @@ -261,27 +243,23 @@ QString MMCZip::findFolderOfFileInZip(QuaZip* zip, const QString& what, const QS } // ours -bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root) +bool findFilesInZip(QuaZip* zip, const QString& what, QStringList& result, const QString& root) { QuaZipDir rootDir(zip, root); - for(auto fileName: rootDir.entryList(QDir::Files)) - { - if(fileName == what) - { + for (auto fileName : rootDir.entryList(QDir::Files)) { + if (fileName == what) { result.append(root); return true; } } - for(auto fileName: rootDir.entryList(QDir::Dirs)) - { + for (auto fileName : rootDir.entryList(QDir::Dirs)) { findFilesInZip(zip, what, result, root + fileName); } return !result.isEmpty(); } - // ours -std::optional MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target) +std::optional extractSubDir(QuaZip* zip, const QString& subdir, const QString& target) { auto target_top_dir = QUrl::fromLocalFile(target); @@ -289,16 +267,13 @@ std::optional MMCZip::extractSubDir(QuaZip *zip, const QString & su qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target; auto numEntries = zip->getEntriesCount(); - if(numEntries < 0) { + if (numEntries < 0) { qWarning() << "Failed to enumerate files in archive"; return std::nullopt; - } - else if(numEntries == 0) { + } else if (numEntries == 0) { qDebug() << "Extracting empty archives seems odd..."; return extracted; - } - else if (!zip->goToFirstFile()) - { + } else if (!zip->goToFirstFile()) { qWarning() << "Failed to seek to first file in zip"; return std::nullopt; } @@ -334,7 +309,8 @@ std::optional MMCZip::extractSubDir(QuaZip *zip, const QString & su } if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) { - qWarning() << "Extracting" << relative_file_name << "was cancelled, because it was effectively outside of the target path" << target; + qWarning() << "Extracting" << relative_file_name << "was cancelled, because it was effectively outside of the target path" + << target; return std::nullopt; } @@ -345,7 +321,8 @@ std::optional MMCZip::extractSubDir(QuaZip *zip, const QString & su } extracted.append(target_file_path); - QFile::setPermissions(target_file_path, QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser); + QFile::setPermissions(target_file_path, + QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser); qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path; } while (zip->goToNextFile()); @@ -354,66 +331,66 @@ std::optional MMCZip::extractSubDir(QuaZip *zip, const QString & su } // ours -bool MMCZip::extractRelFile(QuaZip *zip, const QString &file, const QString &target) +bool extractRelFile(QuaZip* zip, const QString& file, const QString& target) { return JlCompress::extractFile(zip, file, target); } // ours -std::optional MMCZip::extractDir(QString fileCompressed, QString dir) +std::optional extractDir(QString fileCompressed, QString dir) { QuaZip zip(fileCompressed); - if (!zip.open(QuaZip::mdUnzip)) - { + if (!zip.open(QuaZip::mdUnzip)) { // check if this is a minimum size empty zip file... QFileInfo fileInfo(fileCompressed); - if(fileInfo.size() == 22) { + if (fileInfo.size() == 22) { return QStringList(); } - qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();; + qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError(); + ; return std::nullopt; } - return MMCZip::extractSubDir(&zip, "", dir); + return extractSubDir(&zip, "", dir); } // ours -std::optional MMCZip::extractDir(QString fileCompressed, QString subdir, QString dir) +std::optional extractDir(QString fileCompressed, QString subdir, QString dir) { QuaZip zip(fileCompressed); - if (!zip.open(QuaZip::mdUnzip)) - { + if (!zip.open(QuaZip::mdUnzip)) { // check if this is a minimum size empty zip file... QFileInfo fileInfo(fileCompressed); - if(fileInfo.size() == 22) { + if (fileInfo.size() == 22) { return QStringList(); } - qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();; + qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError(); + ; return std::nullopt; } - return MMCZip::extractSubDir(&zip, subdir, dir); + return extractSubDir(&zip, subdir, dir); } // ours -bool MMCZip::extractFile(QString fileCompressed, QString file, QString target) +bool extractFile(QString fileCompressed, QString file, QString target) { QuaZip zip(fileCompressed); - if (!zip.open(QuaZip::mdUnzip)) - { + if (!zip.open(QuaZip::mdUnzip)) { // check if this is a minimum size empty zip file... QFileInfo fileInfo(fileCompressed); - if(fileInfo.size() == 22) { + if (fileInfo.size() == 22) { return true; } qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError(); return false; } - return MMCZip::extractRelFile(&zip, file, target); + return extractRelFile(&zip, file, target); } -bool MMCZip::collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList *files, - MMCZip::FilterFunction excludeFilter) { +bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter) +{ QDir rootDirectory(rootDir); - if (!rootDirectory.exists()) return false; + if (!rootDirectory.exists()) + return false; QDir directory; if (subDir == nullptr) @@ -421,25 +398,107 @@ bool MMCZip::collectFileListRecursively(const QString& rootDir, const QString& s else directory = QDir(subDir); - if (!directory.exists()) return false; // shouldn't ever happen + 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) { + 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) { + for (const auto& e : entries) { QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath()); if (excludeFilter && excludeFilter(relativeFilePath)) { qDebug() << "Skipping file " << relativeFilePath; continue; } - files->append(e); // we want the original paths for MMCZip::compressDirFiles + files->append(e); // we want the original paths for compressDirFiles } return true; } + +void ExportToZipTask::executeTask() +{ + setStatus("Adding files..."); + setProgress(0, m_files.length()); + m_build_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return exportZip(); }); + connect(&m_build_zip_watcher, &QFutureWatcher::finished, this, &ExportToZipTask::finish); + m_build_zip_watcher.setFuture(m_build_zip_future); +} + +auto ExportToZipTask::exportZip() -> ZipResult +{ + if (!m_dir.exists()) { + return ZipResult(tr("Folder doesn't exist")); + } + if (!m_output.isOpen() && !m_output.open(QuaZip::mdCreate)) { + return ZipResult(tr("Could not create file")); + } + + for (auto fileName : m_extra_files.keys()) { + if (m_build_zip_future.isCanceled()) + return ZipResult(); + QuaZipFile indexFile(&m_output); + if (!indexFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileName))) { + return ZipResult(tr("Could not create:") + fileName); + } + indexFile.write(m_extra_files[fileName]); + } + + for (const QFileInfo& file : m_files) { + if (m_build_zip_future.isCanceled()) + return ZipResult(); + + auto absolute = file.absoluteFilePath(); + auto relative = m_dir.relativeFilePath(absolute); + setStatus("Compresing: " + relative); + setProgress(m_progress + 1, m_progressTotal); + if (m_follow_symlinks) { + if (file.isSymLink()) + absolute = file.symLinkTarget(); + else + absolute = file.canonicalFilePath(); + } + + if (!m_exclude_files.contains(relative) && !JlCompress::compressFile(&m_output, absolute, m_destination_prefix + relative)) { + return ZipResult(tr("Could not read and compress %1").arg(relative)); + } + } + + m_output.close(); + if (m_output.getZipError() != 0) { + return ZipResult(tr("A zip error occurred")); + } + return ZipResult(); +} + +void ExportToZipTask::finish() +{ + if (m_build_zip_future.isCanceled()) { + QFile::remove(m_output_path); + emitAborted(); + } else if (auto result = m_build_zip_future.result(); result.has_value()) { + QFile::remove(m_output_path); + emitFailed(result.value()); + } else { + emitSucceeded(); + } +} + +bool ExportToZipTask::abort() +{ + if (m_build_zip_future.isRunning()) { + m_build_zip_future.cancel(); + // NOTE: Here we don't do `emitAborted()` because it will be done when `m_build_zip_future` actually cancels, which may not occur + // immediately. + return true; + } + return false; +} + +} // namespace MMCZip \ No newline at end of file diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index 2a78f830f..bc527ad1b 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 Trial97 * * 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 @@ -35,110 +36,157 @@ #pragma once -#include -#include -#include -#include "minecraft/mod/Mod.h" -#include - +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include "minecraft/mod/Mod.h" +#include "tasks/Task.h" -namespace MMCZip -{ - using FilterFunction = std::function; +namespace MMCZip { +using FilterFunction = std::function; - /** - * Merge two zip files, using a filter function - */ - bool mergeZipFiles(QuaZip *into, QFileInfo from, QSet &contained, - const FilterFunction filter = nullptr); +/** + * Merge two zip files, using a filter function + */ +bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet& contained, 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 - * \param followSymlinks should follow symlinks when compressing file data - * \return true for success or false for failure - */ - bool compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, bool followSymlinks = false); +/** + * 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 + * \param followSymlinks should follow symlinks when compressing file data + * \return true for success or false for failure + */ +bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool followSymlinks = false); - /** - * 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 - * \param followSymlinks should follow symlinks when compressing file data - * \return true for success or false for failure - */ - bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false); +/** + * 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 + * \param followSymlinks should follow symlinks when compressing file data + * \return true for success or false for failure + */ +bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false); - /** - * take a source jar, add mods to it, resulting in target jar - */ - bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods); +/** + * take a source jar, add mods to it, resulting in target jar + */ +bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods); - /** - * Find a single file in archive by file name (not path) - * - * \param ignore_paths paths to skip when recursing the search - * - * \return the path prefix where the file is - */ - QString findFolderOfFileInZip(QuaZip * zip, const QString & what, const QStringList& ignore_paths = {}, const QString &root = QString("")); +/** + * Find a single file in archive by file name (not path) + * + * \param ignore_paths paths to skip when recursing the search + * + * \return the path prefix where the file is + */ +QString findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths = {}, const QString& root = QString("")); - /** - * Find a multiple files of the same name in archive by file name - * If a file is found in a path, no deeper paths are searched - * - * \return true if anything was found - */ - bool findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString()); +/** + * Find a multiple files of the same name in archive by file name + * If a file is found in a path, no deeper paths are searched + * + * \return true if anything was found + */ +bool findFilesInZip(QuaZip* zip, const QString& what, QStringList& result, const QString& root = QString()); - /** - * Extract a subdirectory from an archive - */ - std::optional extractSubDir(QuaZip *zip, const QString & subdir, const QString &target); +/** + * Extract a subdirectory from an archive + */ +std::optional extractSubDir(QuaZip* zip, const QString& subdir, const QString& target); - bool extractRelFile(QuaZip *zip, const QString & file, const QString &target); +bool extractRelFile(QuaZip* zip, const QString& file, const QString& target); - /** - * Extract a whole archive. - * - * \param fileCompressed The name of the archive. - * \param dir The directory to extract to, the current directory if left empty. - * \return The list of the full paths of the files extracted, empty on failure. - */ - std::optional extractDir(QString fileCompressed, QString dir); +/** + * Extract a whole archive. + * + * \param fileCompressed The name of the archive. + * \param dir The directory to extract to, the current directory if left empty. + * \return The list of the full paths of the files extracted, empty on failure. + */ +std::optional extractDir(QString fileCompressed, QString dir); - /** - * Extract a subdirectory from an archive - * - * \param fileCompressed The name of the archive. - * \param subdir The directory within the archive to extract - * \param dir The directory to extract to, the current directory if left empty. - * \return The list of the full paths of the files extracted, empty on failure. - */ - std::optional extractDir(QString fileCompressed, QString subdir, QString dir); +/** + * Extract a subdirectory from an archive + * + * \param fileCompressed The name of the archive. + * \param subdir The directory within the archive to extract + * \param dir The directory to extract to, the current directory if left empty. + * \return The list of the full paths of the files extracted, empty on failure. + */ +std::optional extractDir(QString fileCompressed, QString subdir, QString dir); - /** - * Extract a single file from an archive into a directory - * - * \param fileCompressed The name of the archive. - * \param file The file within the archive to extract - * \param dir The directory to extract to, the current directory if left empty. - * \return true for success or false for failure - */ - bool extractFile(QString fileCompressed, QString file, QString dir); +/** + * Extract a single file from an archive into a directory + * + * \param fileCompressed The name of the archive. + * \param file The file within the archive to extract + * \param dir The directory to extract to, the current directory if left empty. + * \return true for success or false for failure + */ +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); -} +/** + * 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); + +class ExportToZipTask : public Task { + public: + ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) + : m_output_path(outputPath) + , m_output(outputPath) + , m_dir(dir) + , m_files(files) + , m_destination_prefix(destinationPrefix) + , m_follow_symlinks(followSymlinks) + { + setAbortable(true); + }; + ExportToZipTask(QString outputPath, QString dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) + : ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks){}; + + virtual ~ExportToZipTask() = default; + + void setExcludeFiles(QStringList excludeFiles) { m_exclude_files = excludeFiles; } + void addExtraFile(QString fileName, QByteArray data) { m_extra_files.insert(fileName, data); } + + typedef std::optional ZipResult; + + protected: + virtual void executeTask() override; + bool abort() override; + + ZipResult exportZip(); + void finish(); + + private: + QString m_output_path; + QuaZip m_output; + QDir m_dir; + QFileInfoList m_files; + QString m_destination_prefix; + bool m_follow_symlinks; + QStringList m_exclude_files; + QHash m_extra_files; + + QFuture m_build_zip_future; + QFutureWatcher m_build_zip_watcher; +}; +} // namespace MMCZip diff --git a/launcher/MTPixmapCache.h b/launcher/MTPixmapCache.h index 65cbe032a..1a3e52160 100644 --- a/launcher/MTPixmapCache.h +++ b/launcher/MTPixmapCache.h @@ -1,10 +1,10 @@ #pragma once #include +#include #include #include #include -#include #define GET_TYPE() \ Qt::ConnectionType type; \ @@ -94,8 +94,8 @@ class PixmapCache final : public QObject { return true; } - /** - * Mark that a cache miss occurred because of a eviction if too many of these occur too fast the cache size is increased + /** + * Mark that a cache miss occurred because of a eviction if too many of these occur too fast the cache size is increased * @return if the cache size was increased */ bool _markCacheMissByEviciton() diff --git a/launcher/MangoHud.cpp b/launcher/MangoHud.cpp index 90e48e298..5758da3aa 100644 --- a/launcher/MangoHud.cpp +++ b/launcher/MangoHud.cpp @@ -16,15 +16,15 @@ * along with this program. If not, see . */ -#include #include #include +#include #include #include -#include "MangoHud.h" #include "FileSystem.h" #include "Json.h" +#include "MangoHud.h" namespace MangoHud { diff --git a/launcher/Markdown.h b/launcher/Markdown.h index 6b261e60c..f91a016bd 100644 --- a/launcher/Markdown.h +++ b/launcher/Markdown.h @@ -18,7 +18,7 @@ #pragma once -#include #include +#include QString markdownToHTML(const QString& markdown); \ No newline at end of file diff --git a/launcher/MessageLevel.cpp b/launcher/MessageLevel.cpp index 135ef4632..116e70c4b 100644 --- a/launcher/MessageLevel.cpp +++ b/launcher/MessageLevel.cpp @@ -22,12 +22,11 @@ MessageLevel::Enum MessageLevel::getLevel(const QString& levelName) return MessageLevel::Unknown; } -MessageLevel::Enum MessageLevel::fromLine(QString &line) +MessageLevel::Enum MessageLevel::fromLine(QString& line) { // Level prefix int endmark = line.indexOf("]!"); - if (line.startsWith("!![") && endmark != -1) - { + if (line.startsWith("!![") && endmark != -1) { auto level = MessageLevel::getLevel(line.left(endmark).mid(3)); line = line.mid(endmark + 2); return level; diff --git a/launcher/MessageLevel.h b/launcher/MessageLevel.h index 227ad25d8..fd12583f2 100644 --- a/launcher/MessageLevel.h +++ b/launcher/MessageLevel.h @@ -6,23 +6,21 @@ * @brief the MessageLevel Enum * defines what level a log message is */ -namespace MessageLevel -{ -enum Enum -{ - Unknown, /**< No idea what this is or where it came from */ - StdOut, /**< Undetermined stderr messages */ - StdErr, /**< Undetermined stdout messages */ +namespace MessageLevel { +enum Enum { + Unknown, /**< No idea what this is or where it came from */ + StdOut, /**< Undetermined stderr messages */ + StdErr, /**< Undetermined stdout messages */ Launcher, /**< Launcher Messages */ - Debug, /**< Debug Messages */ - Info, /**< Info Messages */ - Message, /**< Standard Messages */ - Warning, /**< Warnings */ - Error, /**< Errors */ - Fatal, /**< Fatal Errors */ + Debug, /**< Debug Messages */ + Info, /**< Info Messages */ + Message, /**< Standard Messages */ + Warning, /**< Warnings */ + Error, /**< Errors */ + Fatal, /**< Fatal Errors */ }; -MessageLevel::Enum getLevel(const QString &levelName); +MessageLevel::Enum getLevel(const QString& levelName); /* Get message level from a line. Line is modified if it was successful. */ -MessageLevel::Enum fromLine(QString &line); -} +MessageLevel::Enum fromLine(QString& line); +} // namespace MessageLevel diff --git a/launcher/NullInstance.h b/launcher/NullInstance.h index 53edfa0b8..ff658c431 100644 --- a/launcher/NullInstance.h +++ b/launcher/NullInstance.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -37,88 +37,38 @@ #include "BaseInstance.h" #include "launch/LaunchTask.h" -class NullInstance: public BaseInstance -{ +class NullInstance : public BaseInstance { Q_OBJECT -public: + public: NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir) - :BaseInstance(globalSettings, settings, rootDir) + : BaseInstance(globalSettings, settings, rootDir) { setVersionBroken(true); } - virtual ~NullInstance() {}; - void saveNow() override - { - } - void loadSpecificSettings() override - { - setSpecificSettingsLoaded(true); - } - QString getStatusbarDescription() override - { - return tr("Unknown instance type"); - }; - QSet< QString > traits() const override - { - return {}; - }; - QString instanceConfigFolder() const override - { - return instanceRoot(); - }; - shared_qobject_ptr createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override - { - return nullptr; - } - shared_qobject_ptr< Task > createUpdateTask(Net::Mode mode) override - { - return nullptr; - } - QProcessEnvironment createEnvironment() override - { - return QProcessEnvironment(); - } - QProcessEnvironment createLaunchEnvironment() override - { - return QProcessEnvironment(); - } - QMap getVariables() override - { - return QMap(); - } - IPathMatcher::Ptr getLogFileMatcher() override - { - return nullptr; - } - QString getLogFileRoot() override - { - return instanceRoot(); - } - QString typeName() const override - { - return "Null"; - } - bool canExport() const override - { - return false; - } - bool canEdit() const override - { - return false; - } - bool canLaunch() const override - { - return false; - } + virtual ~NullInstance(){}; + void saveNow() override {} + void loadSpecificSettings() override { setSpecificSettingsLoaded(true); } + QString getStatusbarDescription() override { return tr("Unknown instance type"); }; + QSet traits() const override { return {}; }; + QString instanceConfigFolder() const override { return instanceRoot(); }; + shared_qobject_ptr createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override { return nullptr; } + shared_qobject_ptr createUpdateTask([[maybe_unused]] Net::Mode mode) override { return nullptr; } + QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); } + QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); } + QMap getVariables() override { return QMap(); } + IPathMatcher::Ptr getLogFileMatcher() override { return nullptr; } + QString getLogFileRoot() override { return instanceRoot(); } + QString typeName() const override { return "Null"; } + bool canExport() const override { return false; } + bool canEdit() const override { return false; } + bool canLaunch() const override { return false; } QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override { QStringList out; out << "Null instance - placeholder."; return out; } - QString modsRoot() const override { - return QString(); - } + QString modsRoot() const override { return QString(); } void updateRuntimeContext() { // NOOP diff --git a/launcher/ProblemProvider.h b/launcher/ProblemProvider.h index cd4745fa1..9d1b0004e 100644 --- a/launcher/ProblemProvider.h +++ b/launcher/ProblemProvider.h @@ -1,47 +1,35 @@ #pragma once -enum class ProblemSeverity -{ - None, - Warning, - Error -}; +#include +#include -struct PatchProblem -{ +enum class ProblemSeverity { None, Warning, Error }; + +struct PatchProblem { ProblemSeverity m_severity; QString m_description; }; -class ProblemProvider -{ -public: - virtual ~ProblemProvider() {}; +class ProblemProvider { + public: + virtual ~ProblemProvider() {} virtual const QList getProblems() const = 0; virtual ProblemSeverity getProblemSeverity() const = 0; }; -class ProblemContainer : public ProblemProvider -{ -public: - const QList getProblems() const override +class ProblemContainer : public ProblemProvider { + public: + const QList getProblems() const override { return m_problems; } + ProblemSeverity getProblemSeverity() const override { return m_problemSeverity; } + virtual void addProblem(ProblemSeverity severity, const QString& description) { - return m_problems; - } - ProblemSeverity getProblemSeverity() const override - { - return m_problemSeverity; - } - virtual void addProblem(ProblemSeverity severity, const QString &description) - { - if(severity > m_problemSeverity) - { + if (severity > m_problemSeverity) { m_problemSeverity = severity; } - m_problems.append({severity, description}); + m_problems.append({ severity, description }); } -private: + private: QList m_problems; ProblemSeverity m_problemSeverity = ProblemSeverity::None; }; diff --git a/launcher/QVariantUtils.h b/launcher/QVariantUtils.h index 7e422c3e8..91f2ad29c 100644 --- a/launcher/QVariantUtils.h +++ b/launcher/QVariantUtils.h @@ -36,35 +36,34 @@ #pragma once - -#include #include +#include namespace QVariantUtils { -template -inline QList toList(QVariant src) { +template +inline QList toList(QVariant src) +{ QVariantList variantList = src.toList(); QList list_t; list_t.reserve(variantList.size()); - for (const QVariant& v : variantList) - { + for (const QVariant& v : variantList) { list_t.append(v.value()); } - return list_t; + return list_t; } -template -inline QVariant fromList(QList val) { +template +inline QVariant fromList(QList val) +{ QVariantList variantList; variantList.reserve(val.size()); - for (const T& v : val) - { + for (const T& v : val) { variantList.append(v); } return variantList; } -} \ No newline at end of file +} // namespace QVariantUtils \ No newline at end of file diff --git a/launcher/RWStorage.h b/launcher/RWStorage.h index 3028388e2..5764d7958 100644 --- a/launcher/RWStorage.h +++ b/launcher/RWStorage.h @@ -1,13 +1,12 @@ #pragma once -#include -#include #include +#include #include +#include template -class RWStorage -{ -public: +class RWStorage { + public: void add(K key, V value) { QWriteLocker l(&lock); @@ -17,21 +16,19 @@ public: V get(K key) { QReadLocker l(&lock); - if(cache.contains(key)) - { + if (cache.contains(key)) { return cache[key]; - } - else return V(); + } else + return V(); } bool get(K key, V& value) { QReadLocker l(&lock); - if(cache.contains(key)) - { + if (cache.contains(key)) { value = cache[key]; return true; - } - else return false; + } else + return false; } bool has(K key) { @@ -41,15 +38,14 @@ public: bool stale(K key) { QReadLocker l(&lock); - if(!cache.contains(key)) + if (!cache.contains(key)) return true; return stale_entries.contains(key); } void setStale(K key) { QWriteLocker l(&lock); - if(cache.contains(key)) - { + if (cache.contains(key)) { stale_entries.insert(key); } } @@ -59,7 +55,8 @@ public: cache.clear(); stale_entries.clear(); } -private: + + private: QReadWriteLock lock; QMap cache; QSet stale_entries; diff --git a/launcher/RecursiveFileSystemWatcher.cpp b/launcher/RecursiveFileSystemWatcher.cpp index b7417cdfe..8b28a03f1 100644 --- a/launcher/RecursiveFileSystemWatcher.cpp +++ b/launcher/RecursiveFileSystemWatcher.cpp @@ -1,25 +1,21 @@ #include "RecursiveFileSystemWatcher.h" -#include #include +#include -RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject *parent) - : QObject(parent), m_watcher(new QFileSystemWatcher(this)) +RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject* parent) : QObject(parent), m_watcher(new QFileSystemWatcher(this)) { - connect(m_watcher, &QFileSystemWatcher::fileChanged, this, - &RecursiveFileSystemWatcher::fileChange); - connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, - &RecursiveFileSystemWatcher::directoryChange); + connect(m_watcher, &QFileSystemWatcher::fileChanged, this, &RecursiveFileSystemWatcher::fileChange); + connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &RecursiveFileSystemWatcher::directoryChange); } -void RecursiveFileSystemWatcher::setRootDir(const QDir &root) +void RecursiveFileSystemWatcher::setRootDir(const QDir& root) { bool wasEnabled = m_isEnabled; disable(); m_root = root; setFiles(scanRecursive(m_root)); - if (wasEnabled) - { + if (wasEnabled) { enable(); } } @@ -28,16 +24,14 @@ void RecursiveFileSystemWatcher::setWatchFiles(const bool watchFiles) bool wasEnabled = m_isEnabled; disable(); m_watchFiles = watchFiles; - if (wasEnabled) - { + if (wasEnabled) { enable(); } } void RecursiveFileSystemWatcher::enable() { - if (m_isEnabled) - { + if (m_isEnabled) { return; } Q_ASSERT(m_root != QDir::root()); @@ -46,8 +40,7 @@ void RecursiveFileSystemWatcher::enable() } void RecursiveFileSystemWatcher::disable() { - if (!m_isEnabled) - { + if (!m_isEnabled) { return; } m_isEnabled = false; @@ -55,57 +48,49 @@ void RecursiveFileSystemWatcher::disable() m_watcher->removePaths(m_watcher->directories()); } -void RecursiveFileSystemWatcher::setFiles(const QStringList &files) +void RecursiveFileSystemWatcher::setFiles(const QStringList& files) { - if (files != m_files) - { + if (files != m_files) { m_files = files; emit filesChanged(); } } -void RecursiveFileSystemWatcher::addFilesToWatcherRecursive(const QDir &dir) +void RecursiveFileSystemWatcher::addFilesToWatcherRecursive(const QDir& dir) { m_watcher->addPath(dir.absolutePath()); - for (const QString &directory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) - { + for (const QString& directory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { addFilesToWatcherRecursive(dir.absoluteFilePath(directory)); } - if (m_watchFiles) - { - for (const QFileInfo &info : dir.entryInfoList(QDir::Files)) - { + if (m_watchFiles) { + for (const QFileInfo& info : dir.entryInfoList(QDir::Files)) { m_watcher->addPath(info.absoluteFilePath()); } } } -QStringList RecursiveFileSystemWatcher::scanRecursive(const QDir &directory) +QStringList RecursiveFileSystemWatcher::scanRecursive(const QDir& directory) { QStringList ret; - if(!m_matcher) - { + if (!m_matcher) { return {}; } - for (const QString &dir : directory.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden)) - { + for (const QString& dir : directory.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden)) { ret.append(scanRecursive(directory.absoluteFilePath(dir))); } - for (const QString &file : directory.entryList(QDir::Files | QDir::Hidden)) - { + for (const QString& file : directory.entryList(QDir::Files | QDir::Hidden)) { auto relPath = m_root.relativeFilePath(directory.absoluteFilePath(file)); - if (m_matcher->matches(relPath)) - { + if (m_matcher->matches(relPath)) { ret.append(relPath); } } return ret; } -void RecursiveFileSystemWatcher::fileChange(const QString &path) +void RecursiveFileSystemWatcher::fileChange(const QString& path) { emit fileChanged(path); } -void RecursiveFileSystemWatcher::directoryChange(const QString &path) +void RecursiveFileSystemWatcher::directoryChange([[maybe_unused]] const QString& path) { setFiles(scanRecursive(m_root)); } diff --git a/launcher/RecursiveFileSystemWatcher.h b/launcher/RecursiveFileSystemWatcher.h index cc837d603..ec3ed804e 100644 --- a/launcher/RecursiveFileSystemWatcher.h +++ b/launcher/RecursiveFileSystemWatcher.h @@ -1,61 +1,48 @@ #pragma once -#include #include +#include #include "pathmatcher/IPathMatcher.h" -class RecursiveFileSystemWatcher : public QObject -{ +class RecursiveFileSystemWatcher : public QObject { Q_OBJECT -public: - RecursiveFileSystemWatcher(QObject *parent); + public: + RecursiveFileSystemWatcher(QObject* parent); - void setRootDir(const QDir &root); - QDir rootDir() const - { - return m_root; - } + void setRootDir(const QDir& root); + QDir rootDir() const { return m_root; } // WARNING: setting this to true may be bad for performance void setWatchFiles(const bool watchFiles); - bool watchFiles() const - { - return m_watchFiles; - } + bool watchFiles() const { return m_watchFiles; } - void setMatcher(IPathMatcher::Ptr matcher) - { - m_matcher = matcher; - } + void setMatcher(IPathMatcher::Ptr matcher) { m_matcher = matcher; } - QStringList files() const - { - return m_files; - } + QStringList files() const { return m_files; } -signals: + signals: void filesChanged(); - void fileChanged(const QString &path); + void fileChanged(const QString& path); -public slots: + public slots: void enable(); void disable(); -private: + private: QDir m_root; bool m_watchFiles = false; bool m_isEnabled = false; IPathMatcher::Ptr m_matcher; - QFileSystemWatcher *m_watcher; + QFileSystemWatcher* m_watcher; QStringList m_files; - void setFiles(const QStringList &files); + void setFiles(const QStringList& files); - void addFilesToWatcherRecursive(const QDir &dir); - QStringList scanRecursive(const QDir &dir); + void addFilesToWatcherRecursive(const QDir& dir); + QStringList scanRecursive(const QDir& dir); -private slots: - void fileChange(const QString &path); - void directoryChange(const QString &path); + private slots: + void fileChange(const QString& path); + void directoryChange(const QString& path); }; diff --git a/launcher/ResourceDownloadTask.cpp b/launcher/ResourceDownloadTask.cpp index 06c03c779..a02151ca1 100644 --- a/launcher/ResourceDownloadTask.cpp +++ b/launcher/ResourceDownloadTask.cpp @@ -24,6 +24,8 @@ #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourceFolderModel.h" +#include "net/ApiDownload.h" + ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion version, const std::shared_ptr packs, @@ -51,10 +53,10 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, } } - m_filesNetJob->addNetAction(Net::Download::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename()))); + m_filesNetJob->addNetAction(Net::ApiDownload::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename()))); connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged); - connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propogateStepProgress); + connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propagateStepProgress); connect(m_filesNetJob.get(), &NetJob::failed, this, &ResourceDownloadTask::downloadFailed); addTask(m_filesNetJob); diff --git a/launcher/RuntimeContext.h b/launcher/RuntimeContext.h index 70e7d0d16..c57140d28 100644 --- a/launcher/RuntimeContext.h +++ b/launcher/RuntimeContext.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/SeparatorPrefixTree.h b/launcher/SeparatorPrefixTree.h index 7a841cb76..df9dfe579 100644 --- a/launcher/SeparatorPrefixTree.h +++ b/launcher/SeparatorPrefixTree.h @@ -1,44 +1,32 @@ #pragma once -#include #include +#include #include template -class SeparatorPrefixTree -{ -public: - SeparatorPrefixTree(QStringList paths) - { - insert(paths); - } +class SeparatorPrefixTree { + public: + SeparatorPrefixTree(QStringList paths) { insert(paths); } - SeparatorPrefixTree(bool contained = false) - { - m_contained = contained; - } + SeparatorPrefixTree(bool contained = false) { m_contained = contained; } void insert(QStringList paths) { - for(auto &path: paths) - { + for (auto& path : paths) { insert(path); } } /// insert an exact path into the tree - SeparatorPrefixTree & insert(QString path) + SeparatorPrefixTree& insert(QString path) { auto sepIndex = path.indexOf(Tseparator); - if(sepIndex == -1) - { + if (sepIndex == -1) { children[path] = SeparatorPrefixTree(true); return children[path]; - } - else - { + } else { auto prefix = path.left(sepIndex); - if(!children.contains(prefix)) - { + if (!children.contains(prefix)) { children[prefix] = SeparatorPrefixTree(false); } return children[prefix].insert(path.mid(sepIndex + 1)); @@ -56,26 +44,20 @@ public: bool covers(QString path) const { // if we found some valid node, it's good enough. the tree covers the path - if(m_contained) - { + if (m_contained) { return true; } auto sepIndex = path.indexOf(Tseparator); - if(sepIndex == -1) - { + if (sepIndex == -1) { auto found = children.find(path); - if(found == children.end()) - { + if (found == children.end()) { return false; } return (*found).covers(QString()); - } - else - { + } else { auto prefix = path.left(sepIndex); auto found = children.find(prefix); - if(found == children.end()) - { + if (found == children.end()) { return false; } return (*found).covers(path.mid(sepIndex + 1)); @@ -86,41 +68,33 @@ public: QString cover(QString path) const { // if we found some valid node, it's good enough. the tree covers the path - if(m_contained) - { + if (m_contained) { return QString(""); } auto sepIndex = path.indexOf(Tseparator); - if(sepIndex == -1) - { + if (sepIndex == -1) { auto found = children.find(path); - if(found == children.end()) - { + if (found == children.end()) { return QString(); } auto nested = (*found).cover(QString()); - if(nested.isNull()) - { + if (nested.isNull()) { return nested; } - if(nested.isEmpty()) + if (nested.isEmpty()) return path; return path + Tseparator + nested; - } - else - { + } else { auto prefix = path.left(sepIndex); auto found = children.find(prefix); - if(found == children.end()) - { + if (found == children.end()) { return QString(); } auto nested = (*found).cover(path.mid(sepIndex + 1)); - if(nested.isNull()) - { + if (nested.isNull()) { return nested; } - if(nested.isEmpty()) + if (nested.isEmpty()) return prefix; return prefix + Tseparator + nested; } @@ -130,21 +104,16 @@ public: bool exists(QString path) const { auto sepIndex = path.indexOf(Tseparator); - if(sepIndex == -1) - { + if (sepIndex == -1) { auto found = children.find(path); - if(found == children.end()) - { + if (found == children.end()) { return false; } return true; - } - else - { + } else { auto prefix = path.left(sepIndex); auto found = children.find(prefix); - if(found == children.end()) - { + if (found == children.end()) { return false; } return (*found).exists(path.mid(sepIndex + 1)); @@ -152,24 +121,19 @@ public: } /// find a node in the tree by name - const SeparatorPrefixTree * find(QString path) const + const SeparatorPrefixTree* find(QString path) const { auto sepIndex = path.indexOf(Tseparator); - if(sepIndex == -1) - { + if (sepIndex == -1) { auto found = children.find(path); - if(found == children.end()) - { + if (found == children.end()) { return nullptr; } return &(*found); - } - else - { + } else { auto prefix = path.left(sepIndex); auto found = children.find(prefix); - if(found == children.end()) - { + if (found == children.end()) { return nullptr; } return (*found).find(path.mid(sepIndex + 1)); @@ -177,70 +141,48 @@ public: } /// is this a leaf node? - bool leaf() const - { - return children.isEmpty(); - } + bool leaf() const { return children.isEmpty(); } /// is this node actually contained in the tree, or is it purely structural? - bool contained() const - { - return m_contained; - } + bool contained() const { return m_contained; } /// Remove a path from the tree - bool remove(QString path) - { - return removeInternal(path) != Failed; - } + bool remove(QString path) { return removeInternal(path) != Failed; } /// Clear all children of this node tree node - void clear() - { - children.clear(); - } + void clear() { children.clear(); } QStringList toStringList() const { QStringList collected; // collecting these is more expensive. auto iter = children.begin(); - while(iter != children.end()) - { + while (iter != children.end()) { QStringList list = iter.value().toStringList(); - for(int i = 0; i < list.size(); i++) - { + for (int i = 0; i < list.size(); i++) { list[i] = iter.key() + Tseparator + list[i]; } collected.append(list); - if((*iter).m_contained) - { + if ((*iter).m_contained) { collected.append(iter.key()); } iter++; } return collected; } -private: - enum Removal - { - Failed, - Succeeded, - HasChildren - }; + + private: + enum Removal { Failed, Succeeded, HasChildren }; Removal removeInternal(QString path = QString()) { - if(path.isEmpty()) - { - if(!m_contained) - { + if (path.isEmpty()) { + if (!m_contained) { // remove all children - we are removing a prefix clear(); return Succeeded; } m_contained = false; - if(children.size()) - { + if (children.size()) { return HasChildren; } return Succeeded; @@ -248,42 +190,32 @@ private: Removal remStatus = Failed; QString childToRemove; auto sepIndex = path.indexOf(Tseparator); - if(sepIndex == -1) - { + if (sepIndex == -1) { childToRemove = path; auto found = children.find(childToRemove); - if(found == children.end()) - { + if (found == children.end()) { return Failed; } remStatus = (*found).removeInternal(); - } - else - { + } else { childToRemove = path.left(sepIndex); auto found = children.find(childToRemove); - if(found == children.end()) - { + if (found == children.end()) { return Failed; } remStatus = (*found).removeInternal(path.mid(sepIndex + 1)); } - switch (remStatus) - { + switch (remStatus) { case Failed: - case HasChildren: - { + case HasChildren: { return remStatus; } - case Succeeded: - { + case Succeeded: { children.remove(childToRemove); - if(m_contained) - { + if (m_contained) { return HasChildren; } - if(children.size()) - { + if (children.size()) { return HasChildren; } return Succeeded; @@ -292,7 +224,7 @@ private: return Failed; } -private: - QMap> children; + private: + QMap> children; bool m_contained = false; }; diff --git a/launcher/SkinUtils.cpp b/launcher/SkinUtils.cpp index 1fe0c896d..989114ad5 100644 --- a/launcher/SkinUtils.cpp +++ b/launcher/SkinUtils.cpp @@ -14,17 +14,16 @@ */ #include "SkinUtils.h" -#include "net/HttpMetaCache.h" #include "Application.h" +#include "net/HttpMetaCache.h" #include -#include +#include #include #include -#include +#include -namespace SkinUtils -{ +namespace SkinUtils { /* * Given a username, return a pixmap of the cached skin (if it exists), QPixmap() otherwise */ @@ -32,12 +31,15 @@ QPixmap getFaceFromCache(QString username, int height, int width) { QFile fskin(APPLICATION->metacache()->resolveEntry("skins", username + ".png")->getFullPath()); - if (fskin.exists()) - { + if (fskin.exists()) { QPixmap skinTexture(fskin.fileName()); - if(!skinTexture.isNull()) - { + if (!skinTexture.isNull()) { QPixmap skin = QPixmap(8, 8); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + skin.fill(QColorConstants::Transparent); +#else + skin.fill(QColor(0, 0, 0, 0)); +#endif QPainter painter(&skin); painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8)); painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8)); @@ -47,4 +49,4 @@ QPixmap getFaceFromCache(QString username, int height, int width) return QPixmap(); } -} +} // namespace SkinUtils diff --git a/launcher/SkinUtils.h b/launcher/SkinUtils.h index c1f437ab0..11bc8bc6f 100644 --- a/launcher/SkinUtils.h +++ b/launcher/SkinUtils.h @@ -17,7 +17,6 @@ #include -namespace SkinUtils -{ +namespace SkinUtils { QPixmap getFaceFromCache(QString id, int height = 64, int width = 64); } diff --git a/launcher/StringUtils.h b/launcher/StringUtils.h index f90a6ac75..eac0d5a7d 100644 --- a/launcher/StringUtils.h +++ b/launcher/StringUtils.h @@ -68,15 +68,14 @@ inline QString fromStdString(string s) int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs); /** - * @brief Truncate a url while keeping its readability py placing the `...` in the middle of the path + * @brief Truncate a url while keeping its readability py placing the `...` in the middle of the path * @param url Url to truncate * @param max_len max lenght of url in charaters * @param hard_limit if truncating the path can't get the url short enough, truncate it normaly. */ -QString truncateUrlHumanFriendly(QUrl &url, int max_len, bool hard_limit = false); +QString truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_limit = false); QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1); - QString getRandomAlphaNumeric(); } // namespace StringUtils diff --git a/launcher/Usable.h b/launcher/Usable.h index a3e880f38..b0ecd4018 100644 --- a/launcher/Usable.h +++ b/launcher/Usable.h @@ -12,28 +12,20 @@ class Usable; * * @see UseLock */ -class Usable -{ +class Usable { friend class UseLock; -public: - std::size_t useCount() const - { - return m_useCount; - } - bool isInUse() const - { - return m_useCount > 0; - } -protected: - virtual void decrementUses() - { - m_useCount--; - } - virtual void incrementUses() - { - m_useCount++; - } -private: + + public: + virtual ~Usable() {} + + std::size_t useCount() const { return m_useCount; } + bool isInUse() const { return m_useCount > 0; } + + protected: + virtual void decrementUses() { m_useCount--; } + virtual void incrementUses() { m_useCount++; } + + private: std::size_t m_useCount = 0; }; @@ -42,19 +34,15 @@ private: * * @see Usable */ -class UseLock -{ -public: - UseLock(shared_qobject_ptr usable) - : m_usable(usable) +class UseLock { + public: + UseLock(shared_qobject_ptr usable) : m_usable(usable) { // this doesn't use shared pointer use count, because that wouldn't be correct. this count is separate. m_usable->incrementUses(); } - ~UseLock() - { - m_usable->decrementUses(); - } -private: + ~UseLock() { m_usable->decrementUses(); } + + private: shared_qobject_ptr m_usable; }; diff --git a/launcher/Version.cpp b/launcher/Version.cpp index e4311f314..511aa9c35 100644 --- a/launcher/Version.cpp +++ b/launcher/Version.cpp @@ -117,12 +117,14 @@ QDebug operator<<(QDebug debug, const Version& v) bool first = true; for (auto s : v.m_sections) { - if (!first) debug.nospace() << ", "; + if (!first) + debug.nospace() << ", "; debug.nospace() << s.m_fullString; first = false; } - - debug.nospace() << " ]" << " }"; + + debug.nospace() << " ]" + << " }"; return debug; } diff --git a/launcher/Version.h b/launcher/Version.h index 659f8e54e..92bff9ba9 100644 --- a/launcher/Version.h +++ b/launcher/Version.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 flowln * Copyright (C) 2022 Sefa Eyeoglu * @@ -48,12 +48,12 @@ class Version { Version(QString str); Version() = default; - bool operator<(const Version &other) const; - bool operator<=(const Version &other) const; - bool operator>(const Version &other) const; - bool operator>=(const Version &other) const; - bool operator==(const Version &other) const; - bool operator!=(const Version &other) const; + bool operator<(const Version& other) const; + bool operator<=(const Version& other) const; + bool operator>(const Version& other) const; + bool operator>=(const Version& other) const; + bool operator==(const Version& other) const; + bool operator!=(const Version& other) const; QString toString() const { return m_string; } @@ -63,7 +63,7 @@ class Version { struct Section { explicit Section(QString fullString) : m_fullString(std::move(fullString)) { - int cutoff = m_fullString.size(); + qsizetype cutoff = m_fullString.size(); for (int i = 0; i < m_fullString.size(); i++) { if (!m_fullString[i].isDigit()) { cutoff = i; @@ -72,7 +72,7 @@ class Version { } #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - auto numPart = QStringView{m_fullString}.left(cutoff); + auto numPart = QStringView{ m_fullString }.left(cutoff); #else auto numPart = m_fullString.leftRef(cutoff); #endif @@ -83,7 +83,7 @@ class Version { } #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - auto stringPart = QStringView{m_fullString}.mid(cutoff); + auto stringPart = QStringView{ m_fullString }.mid(cutoff); #else auto stringPart = m_fullString.midRef(cutoff); #endif @@ -103,8 +103,14 @@ class Version { QString m_fullString; - [[nodiscard]] inline bool isAppendix() const { return m_stringPart.startsWith('+'); } - [[nodiscard]] inline bool isPreRelease() const { return m_stringPart.startsWith('-') && m_stringPart.length() > 1; } + [[nodiscard]] inline bool isAppendix() const + { + return m_stringPart.startsWith('+'); + } + [[nodiscard]] inline bool isPreRelease() const + { + return m_stringPart.startsWith('-') && m_stringPart.length() > 1; + } inline bool operator==(const Section& other) const { @@ -121,7 +127,7 @@ class Version { } inline bool operator<(const Section& other) const - { + { static auto unequal_is_less = [](Section const& non_null) -> bool { if (non_null.m_stringPart.isEmpty()) return non_null.m_numPart == 0; @@ -154,7 +160,7 @@ class Version { { return !(*this == other); } - inline bool operator>(const Section &other) const + inline bool operator>(const Section& other) const { return !(*this < other || *this == other); } @@ -166,5 +172,3 @@ class Version { void parse(); }; - - diff --git a/launcher/VersionProxyModel.cpp b/launcher/VersionProxyModel.cpp index 63a43465c..0ab9ae2c3 100644 --- a/launcher/VersionProxyModel.cpp +++ b/launcher/VersionProxyModel.cpp @@ -35,53 +35,48 @@ */ #include "VersionProxyModel.h" -#include "Application.h" -#include -#include #include #include +#include +#include +#include "Application.h" -class VersionFilterModel : public QSortFilterProxyModel -{ +class VersionFilterModel : public QSortFilterProxyModel { Q_OBJECT -public: - VersionFilterModel(VersionProxyModel *parent) : QSortFilterProxyModel(parent) + public: + VersionFilterModel(VersionProxyModel* parent) : QSortFilterProxyModel(parent) { m_parent = parent; setSortRole(BaseVersionList::SortRole); sort(0, Qt::DescendingOrder); } - bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const + bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const { - const auto &filters = m_parent->filters(); - const QString &search = m_parent->search(); + const auto& filters = m_parent->filters(); + const QString& search = m_parent->search(); const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); if (!search.isEmpty() && !sourceModel()->data(idx, BaseVersionList::VersionRole).toString().contains(search, Qt::CaseInsensitive)) return false; - for (auto it = filters.begin(); it != filters.end(); ++it) - { + for (auto it = filters.begin(); it != filters.end(); ++it) { auto data = sourceModel()->data(idx, it.key()); auto match = data.toString(); - if(!it.value()->accepts(match)) - { + if (!it.value()->accepts(match)) { return false; } } return true; } - void filterChanged() - { - invalidateFilter(); - } -private: - VersionProxyModel *m_parent; + void filterChanged() { invalidateFilter(); } + + private: + VersionProxyModel* m_parent; }; -VersionProxyModel::VersionProxyModel(QObject *parent) : QAbstractProxyModel(parent) +VersionProxyModel::VersionProxyModel(QObject* parent) : QAbstractProxyModel(parent) { filterModel = new VersionFilterModel(this); connect(filterModel, &QAbstractItemModel::dataChanged, this, &VersionProxyModel::sourceDataChanged); @@ -104,19 +99,17 @@ VersionProxyModel::VersionProxyModel(QObject *parent) : QAbstractProxyModel(pare QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, int role) const { - if(section < 0 || section >= m_columns.size()) + if (section < 0 || section >= m_columns.size()) return QVariant(); - if(orientation != Qt::Horizontal) + if (orientation != Qt::Horizontal) return QVariant(); auto column = m_columns[section]; - if(role == Qt::DisplayRole) - { - switch(column) - { + if (role == Qt::DisplayRole) { + switch (column) { case Name: return tr("Version"); case ParentVersion: - return tr("Minecraft"); //FIXME: this should come from metadata + return tr("Minecraft"); // FIXME: this should come from metadata case Branch: return tr("Branch"); case Type: @@ -128,15 +121,12 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, case Time: return tr("Released"); } - } - else if(role == Qt::ToolTipRole) - { - switch(column) - { + } else if (role == Qt::ToolTipRole) { + switch (column) { case Name: return tr("The name of the version."); case ParentVersion: - return tr("Minecraft version"); //FIXME: this should come from metadata + return tr("Minecraft version"); // FIXME: this should come from metadata case Branch: return tr("The version's branch"); case Type: @@ -152,25 +142,19 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, return QVariant(); } -QVariant VersionProxyModel::data(const QModelIndex &index, int role) const +QVariant VersionProxyModel::data(const QModelIndex& index, int role) const { - if(!index.isValid()) - { + if (!index.isValid()) { return QVariant(); } auto column = m_columns[index.column()]; auto parentIndex = mapToSource(index); - switch(role) - { - case Qt::DisplayRole: - { - switch(column) - { - case Name: - { + switch (role) { + case Qt::DisplayRole: { + switch (column) { + case Name: { QString version = sourceModel()->data(parentIndex, BaseVersionList::VersionRole).toString(); - if(version == m_currentVersion) - { + if (version == m_currentVersion) { return tr("%1 (installed)").arg(version); } return version; @@ -191,18 +175,14 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const return QVariant(); } } - case Qt::ToolTipRole: - { - if(column == Name && hasRecommended) - { + case Qt::ToolTipRole: { + if (column == Name && hasRecommended) { auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole); - if(value.toBool()) - { + if (value.toBool()) { return tr("Recommended"); - } else if(hasLatest) { + } else if (hasLatest) { auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole); - if(value.toBool()) - { + if (value.toBool()) { return tr("Latest"); } } @@ -210,32 +190,23 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole); } } - case Qt::DecorationRole: - { - switch(column) - { - case Name: - { - if(hasRecommended) - { - auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole); - if(value.toBool()) - { + case Qt::DecorationRole: { + switch (column) { + case Name: { + if (hasRecommended) { + auto recommenced = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole); + if (recommenced.toBool()) { return APPLICATION->getThemedIcon("star"); - } - else if(hasLatest) - { - auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole); - if(value.toBool()) - { + } else if (hasLatest) { + auto latest = sourceModel()->data(parentIndex, BaseVersionList::LatestRole); + if (latest.toBool()) { return APPLICATION->getThemedIcon("bug"); } } QPixmap pixmap; QPixmapCache::find("placeholder", &pixmap); - if(!pixmap) - { - QPixmap px(16,16); + if (!pixmap) { + QPixmap px(16, 16); px.fill(Qt::transparent); QPixmapCache::insert("placeholder", px); return px; @@ -243,16 +214,13 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const return pixmap; } } - default: - { + default: { return QVariant(); } } } - default: - { - if(roles.contains((BaseVersionList::ModelRoles)role)) - { + default: { + if (roles.contains((BaseVersionList::ModelRoles)role)) { return sourceModel()->data(parentIndex, role); } return QVariant(); @@ -260,61 +228,56 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const } } -QModelIndex VersionProxyModel::parent(const QModelIndex &child) const +QModelIndex VersionProxyModel::parent([[maybe_unused]] const QModelIndex& child) const { return QModelIndex(); } -QModelIndex VersionProxyModel::mapFromSource(const QModelIndex &sourceIndex) const +QModelIndex VersionProxyModel::mapFromSource(const QModelIndex& sourceIndex) const { - if(sourceIndex.isValid()) - { + if (sourceIndex.isValid()) { return index(sourceIndex.row(), 0); } return QModelIndex(); } -QModelIndex VersionProxyModel::mapToSource(const QModelIndex &proxyIndex) const +QModelIndex VersionProxyModel::mapToSource(const QModelIndex& proxyIndex) const { - if(proxyIndex.isValid()) - { + if (proxyIndex.isValid()) { return sourceModel()->index(proxyIndex.row(), 0); } return QModelIndex(); } -QModelIndex VersionProxyModel::index(int row, int column, const QModelIndex &parent) const +QModelIndex VersionProxyModel::index(int row, int column, const QModelIndex& parent) const { // no trees here... shoo - if(parent.isValid()) - { + if (parent.isValid()) { return QModelIndex(); } - if(row < 0 || row >= sourceModel()->rowCount()) + if (row < 0 || row >= sourceModel()->rowCount()) return QModelIndex(); - if(column < 0 || column >= columnCount()) + if (column < 0 || column >= columnCount()) return QModelIndex(); return QAbstractItemModel::createIndex(row, column); } -int VersionProxyModel::columnCount(const QModelIndex &parent) const +int VersionProxyModel::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_columns.size(); } -int VersionProxyModel::rowCount(const QModelIndex &parent) const +int VersionProxyModel::rowCount(const QModelIndex& parent) const { - if(sourceModel()) - { + if (sourceModel()) { return sourceModel()->rowCount(parent); } return 0; } -void VersionProxyModel::sourceDataChanged(const QModelIndex &source_top_left, - const QModelIndex &source_bottom_right) +void VersionProxyModel::sourceDataChanged(const QModelIndex& source_top_left, const QModelIndex& source_bottom_right) { - if(source_top_left.parent() != source_bottom_right.parent()) + if (source_top_left.parent() != source_bottom_right.parent()) return; // whole row is getting changed @@ -323,22 +286,20 @@ void VersionProxyModel::sourceDataChanged(const QModelIndex &source_top_left, emit dataChanged(topLeft, bottomRight); } -void VersionProxyModel::setSourceModel(QAbstractItemModel *replacingRaw) +void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw) { - auto replacing = dynamic_cast(replacingRaw); + auto replacing = dynamic_cast(replacingRaw); beginResetModel(); m_columns.clear(); - if(!replacing) - { + if (!replacing) { roles.clear(); filterModel->setSourceModel(replacing); return; } roles = replacing->providesRoles(); - if(roles.contains(BaseVersionList::VersionRole)) - { + if (roles.contains(BaseVersionList::VersionRole)) { m_columns.push_back(Name); } /* @@ -347,32 +308,25 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel *replacingRaw) m_columns.push_back(ParentVersion); } */ - if(roles.contains(BaseVersionList::ArchitectureRole)) - { + if (roles.contains(BaseVersionList::ArchitectureRole)) { m_columns.push_back(Architecture); } - if(roles.contains(BaseVersionList::PathRole)) - { + if (roles.contains(BaseVersionList::PathRole)) { m_columns.push_back(Path); } - if(roles.contains(Meta::VersionList::TimeRole)) - { + if (roles.contains(Meta::VersionList::TimeRole)) { m_columns.push_back(Time); } - if(roles.contains(BaseVersionList::BranchRole)) - { + if (roles.contains(BaseVersionList::BranchRole)) { m_columns.push_back(Branch); } - if(roles.contains(BaseVersionList::TypeRole)) - { + if (roles.contains(BaseVersionList::TypeRole)) { m_columns.push_back(Type); } - if(roles.contains(BaseVersionList::RecommendedRole)) - { + if (roles.contains(BaseVersionList::RecommendedRole)) { hasRecommended = true; } - if(roles.contains(BaseVersionList::LatestRole)) - { + if (roles.contains(BaseVersionList::LatestRole)) { hasLatest = true; } filterModel->setSourceModel(replacing); @@ -382,16 +336,13 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel *replacingRaw) QModelIndex VersionProxyModel::getRecommended() const { - if(!roles.contains(BaseVersionList::RecommendedRole)) - { + if (!roles.contains(BaseVersionList::RecommendedRole)) { return index(0, 0); } int recommended = 0; - for (int i = 0; i < rowCount(); i++) - { + for (int i = 0; i < rowCount(); i++) { auto value = sourceModel()->data(mapToSource(index(i, 0)), BaseVersionList::RecommendedRole); - if (value.toBool()) - { + if (value.toBool()) { recommended = i; } } @@ -401,16 +352,13 @@ QModelIndex VersionProxyModel::getRecommended() const QModelIndex VersionProxyModel::getVersion(const QString& version) const { int found = -1; - for (int i = 0; i < rowCount(); i++) - { + for (int i = 0; i < rowCount(); i++) { auto value = sourceModel()->data(mapToSource(index(i, 0)), BaseVersionList::VersionRole); - if (value.toString() == version) - { + if (value.toString() == version) { found = i; } } - if(found == -1) - { + if (found == -1) { return QModelIndex(); } return index(found, 0); @@ -423,23 +371,24 @@ void VersionProxyModel::clearFilters() filterModel->filterChanged(); } -void VersionProxyModel::setFilter(const BaseVersionList::ModelRoles column, Filter * f) +void VersionProxyModel::setFilter(const BaseVersionList::ModelRoles column, Filter* f) { m_filters[column].reset(f); filterModel->filterChanged(); } -void VersionProxyModel::setSearch(const QString &search) { +void VersionProxyModel::setSearch(const QString& search) +{ m_search = search; filterModel->filterChanged(); } -const VersionProxyModel::FilterMap &VersionProxyModel::filters() const +const VersionProxyModel::FilterMap& VersionProxyModel::filters() const { return m_filters; } -const QString &VersionProxyModel::search() const +const QString& VersionProxyModel::search() const { return m_search; } @@ -459,7 +408,9 @@ void VersionProxyModel::sourceRowsAboutToBeInserted(const QModelIndex& parent, i beginInsertRows(parent, first, last); } -void VersionProxyModel::sourceRowsInserted(const QModelIndex& parent, int first, int last) +void VersionProxyModel::sourceRowsInserted([[maybe_unused]] const QModelIndex& parent, + [[maybe_unused]] int first, + [[maybe_unused]] int last) { endInsertRows(); } @@ -469,12 +420,12 @@ void VersionProxyModel::sourceRowsAboutToBeRemoved(const QModelIndex& parent, in beginRemoveRows(parent, first, last); } -void VersionProxyModel::sourceRowsRemoved(const QModelIndex& parent, int first, int last) +void VersionProxyModel::sourceRowsRemoved([[maybe_unused]] const QModelIndex& parent, [[maybe_unused]] int first, [[maybe_unused]] int last) { endRemoveRows(); } -void VersionProxyModel::setCurrentVersion(const QString &version) +void VersionProxyModel::setCurrentVersion(const QString& version) { m_currentVersion = version; } diff --git a/launcher/VersionProxyModel.h b/launcher/VersionProxyModel.h index 6434376c6..c7d5fd94e 100644 --- a/launcher/VersionProxyModel.h +++ b/launcher/VersionProxyModel.h @@ -6,64 +6,53 @@ class VersionFilterModel; -class VersionProxyModel: public QAbstractProxyModel -{ +class VersionProxyModel : public QAbstractProxyModel { Q_OBJECT -public: - - enum Column - { - Name, - ParentVersion, - Branch, - Type, - Architecture, - Path, - Time - }; + public: + enum Column { Name, ParentVersion, Branch, Type, Architecture, Path, Time }; typedef QHash> FilterMap; -public: - VersionProxyModel ( QObject* parent = 0 ); - virtual ~VersionProxyModel() {}; + public: + VersionProxyModel(QObject* parent = 0); + virtual ~VersionProxyModel(){}; - virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; - virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; - virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; - virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + virtual QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override; + virtual QModelIndex mapToSource(const QModelIndex& proxyIndex) const override; + virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - virtual QModelIndex parent(const QModelIndex &child) const override; - virtual void setSourceModel(QAbstractItemModel *sourceModel) override; + virtual QModelIndex parent(const QModelIndex& child) const override; + virtual void setSourceModel(QAbstractItemModel* sourceModel) override; - const FilterMap &filters() const; - const QString &search() const; - void setFilter(const BaseVersionList::ModelRoles column, Filter * filter); - void setSearch(const QString &search); + const FilterMap& filters() const; + const QString& search() const; + void setFilter(const BaseVersionList::ModelRoles column, Filter* filter); + void setSearch(const QString& search); void clearFilters(); QModelIndex getRecommended() const; - QModelIndex getVersion(const QString & version) const; - void setCurrentVersion(const QString &version); -private slots: + QModelIndex getVersion(const QString& version) const; + void setCurrentVersion(const QString& version); + private slots: - void sourceDataChanged(const QModelIndex &source_top_left,const QModelIndex &source_bottom_right); + void sourceDataChanged(const QModelIndex& source_top_left, const QModelIndex& source_bottom_right); void sourceAboutToBeReset(); void sourceReset(); - void sourceRowsAboutToBeInserted(const QModelIndex &parent, int first, int last); - void sourceRowsInserted(const QModelIndex &parent, int first, int last); + void sourceRowsAboutToBeInserted(const QModelIndex& parent, int first, int last); + void sourceRowsInserted(const QModelIndex& parent, int first, int last); - void sourceRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last); - void sourceRowsRemoved(const QModelIndex &parent, int first, int last); + void sourceRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last); + void sourceRowsRemoved(const QModelIndex& parent, int first, int last); -private: + private: QList m_columns; FilterMap m_filters; QString m_search; BaseVersionList::RoleList roles; - VersionFilterModel * filterModel; + VersionFilterModel* filterModel; bool hasRecommended = false; bool hasLatest = false; QString m_currentVersion; diff --git a/launcher/WatchLock.h b/launcher/WatchLock.h index 3e08b4137..ad74f251e 100644 --- a/launcher/WatchLock.h +++ b/launcher/WatchLock.h @@ -1,20 +1,15 @@ #pragma once -#include #include +#include -struct WatchLock -{ - WatchLock(QFileSystemWatcher * watcher, const QString& directory) - : m_watcher(watcher), m_directory(directory) +struct WatchLock { + WatchLock(QFileSystemWatcher* watcher, const QString& directory) : m_watcher(watcher), m_directory(directory) { m_watcher->removePath(m_directory); } - ~WatchLock() - { - m_watcher->addPath(m_directory); - } - QFileSystemWatcher * m_watcher; + ~WatchLock() { m_watcher->addPath(m_directory); } + QFileSystemWatcher* m_watcher; QString m_directory; }; diff --git a/launcher/WindowsConsole.cpp b/launcher/WindowsConsole.cpp new file mode 100644 index 000000000..83cad5afa --- /dev/null +++ b/launcher/WindowsConsole.cpp @@ -0,0 +1,128 @@ +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * 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 . + * + */ + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#include +#include +#include + +void RedirectHandle(DWORD handle, FILE* stream, const char* mode) +{ + HANDLE stdHandle = GetStdHandle(handle); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, mode); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stream)); + if (dup2Result == 0) { + setvbuf(stream, NULL, _IONBF, 0); + } + } + } + } +} + +// taken from https://stackoverflow.com/a/25927081 +// getting a proper output to console with redirection support on windows is apparently hell +void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr) +{ + // Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been + // observed that the file number of our standard handle file objects can be assigned internally to a value of -2 + // when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our + // call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value + // before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to + // use the "nul" device, which will place them into a valid state, after which we can redirect them to our target + // using the "_dup2" function. + if (bindStdIn) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "r", stdin); + } + if (bindStdOut) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "w", stdout); + } + if (bindStdErr) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "w", stderr); + } + + // Redirect unbuffered stdin from the current standard input handle + if (bindStdIn) { + RedirectHandle(STD_INPUT_HANDLE, stdin, "r"); + } + + // Redirect unbuffered stdout to the current standard output handle + if (bindStdOut) { + RedirectHandle(STD_OUTPUT_HANDLE, stdout, "w"); + } + + // Redirect unbuffered stderr to the current standard error handle + if (bindStdErr) { + RedirectHandle(STD_ERROR_HANDLE, stderr, "w"); + } + + // Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the + // standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In + // versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything + // has been read from or written to the targets or not. + if (bindStdIn) { + std::wcin.clear(); + std::cin.clear(); + } + if (bindStdOut) { + std::wcout.clear(); + std::cout.clear(); + } + if (bindStdErr) { + std::wcerr.clear(); + std::cerr.clear(); + } +} + +bool AttachWindowsConsole() +{ + auto stdinType = GetFileType(GetStdHandle(STD_INPUT_HANDLE)); + auto stdoutType = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)); + auto stderrType = GetFileType(GetStdHandle(STD_ERROR_HANDLE)); + + bool bindStdIn = false; + bool bindStdOut = false; + bool bindStdErr = false; + + if (stdinType == FILE_TYPE_CHAR || stdinType == FILE_TYPE_UNKNOWN) { + bindStdIn = true; + } + if (stdoutType == FILE_TYPE_CHAR || stdoutType == FILE_TYPE_UNKNOWN) { + bindStdOut = true; + } + if (stderrType == FILE_TYPE_CHAR || stderrType == FILE_TYPE_UNKNOWN) { + bindStdErr = true; + } + + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + BindCrtHandlesToStdHandles(bindStdIn, bindStdOut, bindStdErr); + return true; + } + + return false; +} diff --git a/launcher/WindowsConsole.h b/launcher/WindowsConsole.h new file mode 100644 index 000000000..ab53864b4 --- /dev/null +++ b/launcher/WindowsConsole.h @@ -0,0 +1,25 @@ +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * 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 . + * + */ + +#pragma once + +void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr); +bool AttachWindowsConsole(); diff --git a/launcher/filelink/FileLink.cpp b/launcher/filelink/FileLink.cpp index c9599b820..1ea0e382f 100644 --- a/launcher/filelink/FileLink.cpp +++ b/launcher/filelink/FileLink.cpp @@ -37,11 +37,7 @@ #include #if defined Q_OS_WIN32 -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#include +#include "WindowsConsole.h" #endif // Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header @@ -67,21 +63,7 @@ FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv), { #if defined Q_OS_WIN32 // attach the parent console - if (AttachConsole(ATTACH_PARENT_PROCESS)) { - // if attach succeeds, reopen and sync all the i/o - if (freopen("CON", "w", stdout)) { - std::cout.sync_with_stdio(); - } - if (freopen("CON", "w", stderr)) { - std::cerr.sync_with_stdio(); - } - if (freopen("CON", "r", stdin)) { - std::cin.sync_with_stdio(); - } - auto out = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD written; - const char* endline = "\n"; - WriteConsole(out, endline, strlen(endline), &written, NULL); + if (AttachWindowsConsole()) { consoleAttached = true; } #endif @@ -188,7 +170,7 @@ void FileLinkApp::runLink() FS::LinkResult result = { src_path, dst_path, QString::fromStdString(os_err.message()), os_err.value() }; m_path_results.append(result); } else { - FS::LinkResult result = { src_path, dst_path }; + FS::LinkResult result = { src_path, dst_path, "", 0 }; m_path_results.append(result); } } @@ -248,7 +230,7 @@ void FileLinkApp::readPathPairs() in >> numLinks; qDebug() << "numLinks" << numLinks; - for (int i = 0; i < numLinks; i++) { + for (quint32 i = 0; i < numLinks; i++) { FS::LinkPair pair; in >> pair.src; in >> pair.dst; @@ -271,7 +253,6 @@ FileLinkApp::~FileLinkApp() fclose(stdout); fclose(stdin); fclose(stderr); - FreeConsole(); } #endif } diff --git a/launcher/icons/IconList.cpp b/launcher/icons/IconList.cpp index 13174f6e8..5576b9745 100644 --- a/launcher/icons/IconList.cpp +++ b/launcher/icons/IconList.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 Trial97 * * 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 @@ -35,32 +36,30 @@ #include "IconList.h" #include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include "icons/IconUtils.h" #define MAX_SIZE 1024 -IconList::IconList(const QStringList &builtinPaths, QString path, QObject *parent) : QAbstractListModel(parent) +IconList::IconList(const QStringList& builtinPaths, QString path, QObject* parent) : QAbstractListModel(parent) { QSet builtinNames; // add builtin icons - for(auto & builtinPath: builtinPaths) - { + for (auto& builtinPath : builtinPaths) { QDir instance_icons(builtinPath); auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name); - for (auto file_info : file_info_list) - { + for (auto file_info : file_info_list) { builtinNames.insert(file_info.completeBaseName()); } } - for(auto & builtinName : builtinNames) - { + for (auto& builtinName : builtinNames) { addThemeIcon(builtinName); } @@ -78,31 +77,27 @@ IconList::IconList(const QStringList &builtinPaths, QString path, QObject *paren void IconList::sortIconList() { qDebug() << "Sorting icon list..."; - std::sort(icons.begin(), icons.end(), [](const MMCIcon& a, const MMCIcon& b) { - return a.m_key.localeAwareCompare(b.m_key) < 0; - }); + std::sort(icons.begin(), icons.end(), [](const MMCIcon& a, const MMCIcon& b) { return a.m_key.localeAwareCompare(b.m_key) < 0; }); reindex(); } -void IconList::directoryChanged(const QString &path) +void IconList::directoryChanged(const QString& path) { - QDir new_dir (path); - if(m_dir.absolutePath() != new_dir.absolutePath()) - { + QDir new_dir(path); + if (m_dir.absolutePath() != new_dir.absolutePath()) { m_dir.setPath(path); m_dir.refresh(); - if(is_watching) + if (is_watching) stopWatching(); startWatching(); } - if(!m_dir.exists()) - if(!FS::ensureFolderPathExists(m_dir.absolutePath())) + if (!m_dir.exists()) + if (!FS::ensureFolderPathExists(m_dir.absolutePath())) return; m_dir.refresh(); auto new_list = m_dir.entryList(QDir::Files, QDir::Name); - for (auto it = new_list.begin(); it != new_list.end(); it++) - { - QString &foo = (*it); + for (auto it = new_list.begin(); it != new_list.end(); it++) { + QString& foo = (*it); foo = m_dir.filePath(foo); } #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) @@ -111,8 +106,7 @@ void IconList::directoryChanged(const QString &path) auto new_set = new_list.toSet(); #endif QList current_list; - for (auto &it : icons) - { + for (auto& it : icons) { if (!it.has(IconType::FileBased)) continue; current_list.push_back(it.m_images[IconType::FileBased].filename); @@ -129,38 +123,33 @@ void IconList::directoryChanged(const QString &path) QSet to_add = new_set; to_add -= current_set; - for (auto remove : to_remove) - { + for (auto remove : to_remove) { qDebug() << "Removing " << remove; QFileInfo rmfile(remove); QString key = rmfile.completeBaseName(); QString suffix = rmfile.suffix(); // The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well - if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif") + if (!IconUtils::isIconSuffix(suffix)) key = rmfile.fileName(); int idx = getIconIndex(key); if (idx == -1) continue; icons[idx].remove(IconType::FileBased); - if (icons[idx].type() == IconType::ToBeDeleted) - { + if (icons[idx].type() == IconType::ToBeDeleted) { beginRemoveRows(QModelIndex(), idx, idx); icons.remove(idx); reindex(); endRemoveRows(); - } - else - { + } else { dataChanged(index(idx), index(idx)); } m_watcher->removePath(remove); emit iconUpdated(key); } - for (auto add : to_add) - { + for (auto add : to_add) { qDebug() << "Adding " << add; QFileInfo addfile(add); @@ -168,11 +157,10 @@ void IconList::directoryChanged(const QString &path) QString suffix = addfile.suffix(); // The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well - if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif") + if (!IconUtils::isIconSuffix(suffix)) key = addfile.fileName(); - if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased)) - { + if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased)) { m_watcher->addPath(add); emit iconUpdated(key); } @@ -181,7 +169,7 @@ void IconList::directoryChanged(const QString &path) sortIconList(); } -void IconList::fileChanged(const QString &path) +void IconList::fileChanged(const QString& path) { qDebug() << "Checking " << path; QFileInfo checkfile(path); @@ -200,9 +188,9 @@ void IconList::fileChanged(const QString &path) emit iconUpdated(key); } -void IconList::SettingChanged(const Setting &setting, QVariant value) +void IconList::SettingChanged(const Setting& setting, QVariant value) { - if(setting.id() != "IconsDir") + if (setting.id() != "IconsDir") return; directoryChanged(value.toString()); @@ -213,12 +201,9 @@ void IconList::startWatching() auto abs_path = m_dir.absolutePath(); FS::ensureFolderPathExists(abs_path); is_watching = m_watcher->addPath(abs_path); - if (is_watching) - { + if (is_watching) { qDebug() << "Started watching " << abs_path; - } - else - { + } else { qDebug() << "Failed to start watching " << abs_path; } } @@ -241,7 +226,11 @@ Qt::DropActions IconList::supportedDropActions() const return Qt::CopyAction; } -bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[maybe_unused]] int row, [[maybe_unused]] int column, [[maybe_unused]] const QModelIndex &parent) +bool IconList::dropMimeData(const QMimeData* data, + Qt::DropAction action, + [[maybe_unused]] int row, + [[maybe_unused]] int column, + [[maybe_unused]] const QModelIndex& parent) { if (action == Qt::IgnoreAction) return true; @@ -250,12 +239,10 @@ bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[mayb return false; // files dropped from outside? - if (data->hasUrls()) - { + if (data->hasUrls()) { auto urls = data->urls(); QStringList iconFiles; - for (auto url : urls) - { + for (auto url : urls) { // only local files may be dropped... if (!url.isLocalFile()) continue; @@ -267,16 +254,13 @@ bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[mayb return false; } -Qt::ItemFlags IconList::flags(const QModelIndex &index) const +Qt::ItemFlags IconList::flags(const QModelIndex& index) const { Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); - if (index.isValid()) - return Qt::ItemIsDropEnabled | defaultFlags; - else - return Qt::ItemIsDropEnabled | defaultFlags; + return Qt::ItemIsDropEnabled | defaultFlags; } -QVariant IconList::data(const QModelIndex &index, int role) const +QVariant IconList::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); @@ -286,64 +270,49 @@ QVariant IconList::data(const QModelIndex &index, int role) const if (row < 0 || row >= icons.size()) return QVariant(); - switch (role) - { - case Qt::DecorationRole: - return icons[row].icon(); - case Qt::DisplayRole: - return icons[row].name(); - case Qt::UserRole: - return icons[row].m_key; - default: - return QVariant(); + switch (role) { + case Qt::DecorationRole: + return icons[row].icon(); + case Qt::DisplayRole: + return icons[row].name(); + case Qt::UserRole: + return icons[row].m_key; + default: + return QVariant(); } } -int IconList::rowCount(const QModelIndex &parent) const +int IconList::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : icons.size(); } -void IconList::installIcons(const QStringList &iconFiles) +void IconList::installIcons(const QStringList& iconFiles) { for (QString file : iconFiles) - { - QFileInfo fileinfo(file); - if (!fileinfo.isReadable() || !fileinfo.isFile()) - continue; - QString target = FS::PathCombine(getDirectory(), fileinfo.fileName()); - - QString suffix = fileinfo.suffix(); - if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif") - continue; - - if (!QFile::copy(file, target)) - continue; - } + installIcon(file, {}); } -void IconList::installIcon(const QString &file, const QString &name) +void IconList::installIcon(const QString& file, const QString& name) { QFileInfo fileinfo(file); - if(!fileinfo.isReadable() || !fileinfo.isFile()) + if (!fileinfo.isReadable() || !fileinfo.isFile()) return; - QString target = FS::PathCombine(getDirectory(), name); + if (!IconUtils::isIconSuffix(fileinfo.suffix())) + return; + QString target = FS::PathCombine(getDirectory(), name.isEmpty() ? fileinfo.fileName() : name); QFile::copy(file, target); } -bool IconList::iconFileExists(const QString &key) const +bool IconList::iconFileExists(const QString& key) const { auto iconEntry = icon(key); - if(!iconEntry) - { - return false; - } - return iconEntry->has(IconType::FileBased); + return iconEntry && iconEntry->has(IconType::FileBased); } -const MMCIcon *IconList::icon(const QString &key) const +const MMCIcon* IconList::icon(const QString& key) const { int iconIdx = getIconIndex(key); if (iconIdx == -1) @@ -351,100 +320,84 @@ const MMCIcon *IconList::icon(const QString &key) const return &icons[iconIdx]; } -bool IconList::deleteIcon(const QString &key) +bool IconList::deleteIcon(const QString& key) { - if (!iconFileExists(key)) - return false; - - return QFile::remove(icon(key)->getFilePath()); + return iconFileExists(key) && QFile::remove(icon(key)->getFilePath()); } -bool IconList::trashIcon(const QString &key) +bool IconList::trashIcon(const QString& key) { - if (!iconFileExists(key)) - return false; - - return FS::trash(icon(key)->getFilePath(), nullptr); + return iconFileExists(key) && FS::trash(icon(key)->getFilePath(), nullptr); } bool IconList::addThemeIcon(const QString& key) { auto iter = name_index.find(key); - if (iter != name_index.end()) - { - auto &oldOne = icons[*iter]; + if (iter != name_index.end()) { + auto& oldOne = icons[*iter]; oldOne.replace(Builtin, key); dataChanged(index(*iter), index(*iter)); return true; } - else + // add a new icon + beginInsertRows(QModelIndex(), icons.size(), icons.size()); { - // add a new icon - beginInsertRows(QModelIndex(), icons.size(), icons.size()); - { - MMCIcon mmc_icon; - mmc_icon.m_name = key; - mmc_icon.m_key = key; - mmc_icon.replace(Builtin, key); - icons.push_back(mmc_icon); - name_index[key] = icons.size() - 1; - } - endInsertRows(); - return true; + MMCIcon mmc_icon; + mmc_icon.m_name = key; + mmc_icon.m_key = key; + mmc_icon.replace(Builtin, key); + icons.push_back(mmc_icon); + name_index[key] = icons.size() - 1; } + endInsertRows(); + return true; } -bool IconList::addIcon(const QString &key, const QString &name, const QString &path, const IconType type) +bool IconList::addIcon(const QString& key, const QString& name, const QString& path, const IconType type) { // replace the icon even? is the input valid? QIcon icon(path); if (icon.isNull()) return false; auto iter = name_index.find(key); - if (iter != name_index.end()) - { - auto &oldOne = icons[*iter]; + if (iter != name_index.end()) { + auto& oldOne = icons[*iter]; oldOne.replace(type, icon, path); dataChanged(index(*iter), index(*iter)); return true; } - else + // add a new icon + beginInsertRows(QModelIndex(), icons.size(), icons.size()); { - // add a new icon - beginInsertRows(QModelIndex(), icons.size(), icons.size()); - { - MMCIcon mmc_icon; - mmc_icon.m_name = name; - mmc_icon.m_key = key; - mmc_icon.replace(type, icon, path); - icons.push_back(mmc_icon); - name_index[key] = icons.size() - 1; - } - endInsertRows(); - return true; + MMCIcon mmc_icon; + mmc_icon.m_name = name; + mmc_icon.m_key = key; + mmc_icon.replace(type, icon, path); + icons.push_back(mmc_icon); + name_index[key] = icons.size() - 1; } + endInsertRows(); + return true; } -void IconList::saveIcon(const QString &key, const QString &path, const char * format) const +void IconList::saveIcon(const QString& key, const QString& path, const char* format) const { auto icon = getIcon(key); auto pixmap = icon.pixmap(128, 128); pixmap.save(path, format); } - void IconList::reindex() { name_index.clear(); int i = 0; - for (auto &iter : icons) - { + for (auto& iter : icons) { name_index[iter.m_key] = i; i++; } } -QIcon IconList::getIcon(const QString &key) const +QIcon IconList::getIcon(const QString& key) const { int icon_index = getIconIndex(key); @@ -459,7 +412,7 @@ QIcon IconList::getIcon(const QString &key) const return QIcon(); } -int IconList::getIconIndex(const QString &key) const +int IconList::getIconIndex(const QString& key) const { auto iter = name_index.find(key == "default" ? "grass" : key); if (iter != name_index.end()) @@ -472,5 +425,3 @@ QString IconList::getDirectory() const { return m_dir.absolutePath(); } - -//#include "IconList.moc" diff --git a/launcher/icons/IconList.h b/launcher/icons/IconList.h index 97141e4ae..8afd05574 100644 --- a/launcher/icons/IconList.h +++ b/launcher/icons/IconList.h @@ -1,24 +1,43 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - #pragma once -#include #include -#include #include +#include +#include #include #include @@ -29,58 +48,58 @@ class QFileSystemWatcher; -class IconList : public QAbstractListModel -{ +class IconList : public QAbstractListModel { Q_OBJECT -public: - explicit IconList(const QStringList &builtinPaths, QString path, QObject *parent = 0); - virtual ~IconList() {}; + public: + explicit IconList(const QStringList& builtinPaths, QString path, QObject* parent = 0); + virtual ~IconList(){}; - QIcon getIcon(const QString &key) const; - int getIconIndex(const QString &key) const; + QIcon getIcon(const QString& key) const; + int getIconIndex(const QString& key) const; QString getDirectory() const; - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; virtual QStringList mimeTypes() const override; virtual Qt::DropActions supportedDropActions() const override; - virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; - virtual Qt::ItemFlags flags(const QModelIndex &index) const override; + virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; - bool addThemeIcon(const QString &key); - bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type); - void saveIcon(const QString &key, const QString &path, const char * format) const; - bool deleteIcon(const QString &key); - bool trashIcon(const QString &key); - bool iconFileExists(const QString &key) const; + bool addThemeIcon(const QString& key); + bool addIcon(const QString& key, const QString& name, const QString& path, const IconType type); + void saveIcon(const QString& key, const QString& path, const char* format) const; + bool deleteIcon(const QString& key); + bool trashIcon(const QString& key); + bool iconFileExists(const QString& key) const; - void installIcons(const QStringList &iconFiles); - void installIcon(const QString &file, const QString &name); + void installIcons(const QStringList& iconFiles); + void installIcon(const QString& file, const QString& name); - const MMCIcon * icon(const QString &key) const; + const MMCIcon* icon(const QString& key) const; void startWatching(); void stopWatching(); -signals: + signals: void iconUpdated(QString key); -private: + private: // hide copy constructor - IconList(const IconList &) = delete; + IconList(const IconList&) = delete; // hide assign op - IconList &operator=(const IconList &) = delete; + IconList& operator=(const IconList&) = delete; void reindex(); void sortIconList(); -public slots: - void directoryChanged(const QString &path); + public slots: + void directoryChanged(const QString& path); -protected slots: - void fileChanged(const QString &path); - void SettingChanged(const Setting & setting, QVariant value); -private: + protected slots: + void fileChanged(const QString& path); + void SettingChanged(const Setting& setting, QVariant value); + + private: shared_qobject_ptr m_watcher; bool is_watching; QMap name_index; diff --git a/launcher/icons/IconUtils.cpp b/launcher/icons/IconUtils.cpp index bf530c16f..99c38f47a 100644 --- a/launcher/icons/IconUtils.cpp +++ b/launcher/icons/IconUtils.cpp @@ -1,25 +1,51 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #include "IconUtils.h" -#include "FileSystem.h" #include - -#include +#include "FileSystem.h" namespace { -std::array validIconExtensions = {{ - "svg", - "png", - "ico", - "gif", - "jpg", - "jpeg" -}}; +static const QStringList validIconExtensions = { { "svg", "png", "ico", "gif", "jpg", "jpeg" } }; } -namespace IconUtils{ +namespace IconUtils { -QString findBestIconIn(const QString &folder, const QString & iconKey) { - int best_found = validIconExtensions.size(); +QString findBestIconIn(const QString& folder, const QString& iconKey) +{ QString best_filename; QDirIterator it(folder, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::NoIteratorFlags); @@ -27,36 +53,20 @@ QString findBestIconIn(const QString &folder, const QString & iconKey) { it.next(); auto fileInfo = it.fileInfo(); - if(fileInfo.completeBaseName() != iconKey) - continue; - - auto extension = fileInfo.suffix(); - - for(int i = 0; i < best_found; i++) { - if(extension == validIconExtensions[i]) { - best_found = i; - qDebug() << i << " : " << fileInfo.fileName(); - best_filename = fileInfo.fileName(); - } - } + if (fileInfo.completeBaseName() == iconKey && isIconSuffix(fileInfo.suffix())) + return fileInfo.absoluteFilePath(); } - return FS::PathCombine(folder, best_filename); + return {}; } -QString getIconFilter() { - QString out; - QTextStream stream(&out); - stream << '('; - for(size_t i = 0; i < validIconExtensions.size() - 1; i++) { - if(i > 0) { - stream << " "; - } - stream << "*." << validIconExtensions[i]; - } - stream << " *." << validIconExtensions[validIconExtensions.size() - 1]; - stream << ')'; - return out; +QString getIconFilter() +{ + return "(*." + validIconExtensions.join(" *.") + ")"; } +bool isIconSuffix(QString suffix) +{ + return validIconExtensions.contains(suffix); } +} // namespace IconUtils diff --git a/launcher/icons/IconUtils.h b/launcher/icons/IconUtils.h index be93d9143..90cdfe5ab 100644 --- a/launcher/icons/IconUtils.h +++ b/launcher/icons/IconUtils.h @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #pragma once #include @@ -5,9 +40,10 @@ namespace IconUtils { // Given a folder and an icon key, find 'best' of the icons with the given key in there and return its path -QString findBestIconIn(const QString &folder, const QString & iconKey); +QString findBestIconIn(const QString& folder, const QString& iconKey); // Get icon file type filter for file browser dialogs QString getIconFilter(); -} +bool isIconSuffix(QString suffix); +} // namespace IconUtils diff --git a/launcher/icons/MMCIcon.cpp b/launcher/icons/MMCIcon.cpp index 436ef75ff..991b470c0 100644 --- a/launcher/icons/MMCIcon.cpp +++ b/launcher/icons/MMCIcon.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 Trial97 * * 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 @@ -37,23 +38,21 @@ #include #include -IconType operator--(IconType &t, int) +IconType operator--(IconType& t, int) { IconType temp = t; - switch (t) - { - case IconType::Builtin: - t = IconType::ToBeDeleted; - break; - case IconType::Transient: - t = IconType::Builtin; - break; - case IconType::FileBased: - t = IconType::Transient; - break; - default: - { - } + switch (t) { + case IconType::Builtin: + t = IconType::ToBeDeleted; + break; + case IconType::Transient: + t = IconType::Builtin; + break; + case IconType::FileBased: + t = IconType::Transient; + break; + default: + break; } return temp; } @@ -79,8 +78,8 @@ QIcon MMCIcon::icon() const { if (m_current_type == IconType::ToBeDeleted) return QIcon(); - auto & icon = m_images[m_current_type].icon; - if(!icon.isNull()) + auto& icon = m_images[m_current_type].icon; + if (!icon.isNull()) return icon; // FIXME: inject this. return QIcon::fromTheme(m_images[m_current_type].key); @@ -90,10 +89,8 @@ void MMCIcon::remove(IconType rm_type) { m_images[rm_type].filename = QString(); m_images[rm_type].icon = QIcon(); - for (auto iter = rm_type; iter != IconType::ToBeDeleted; iter--) - { - if (m_images[iter].present()) - { + for (auto iter = rm_type; iter != IconType::ToBeDeleted; iter--) { + if (m_images[iter].present()) { m_current_type = iter; return; } @@ -103,8 +100,7 @@ void MMCIcon::remove(IconType rm_type) void MMCIcon::replace(IconType new_type, QIcon icon, QString path) { - if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted) - { + if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted) { m_current_type = new_type; } m_images[new_type].icon = icon; @@ -114,8 +110,7 @@ void MMCIcon::replace(IconType new_type, QIcon icon, QString path) void MMCIcon::replace(IconType new_type, const QString& key) { - if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted) - { + if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted) { m_current_type = new_type; } m_images[new_type].icon = QIcon(); @@ -125,13 +120,12 @@ void MMCIcon::replace(IconType new_type, const QString& key) QString MMCIcon::getFilePath() const { - if(m_current_type == IconType::ToBeDeleted){ + if (m_current_type == IconType::ToBeDeleted) { return QString(); } return m_images[m_current_type].filename; } - bool MMCIcon::isBuiltIn() const { return m_current_type == IconType::Builtin; diff --git a/launcher/icons/MMCIcon.h b/launcher/icons/MMCIcon.h index 13d99318a..a6e305668 100644 --- a/launcher/icons/MMCIcon.h +++ b/launcher/icons/MMCIcon.h @@ -1,45 +1,52 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - #pragma once -#include #include #include +#include -enum IconType : unsigned -{ - Builtin, - Transient, - FileBased, - ICONS_TOTAL, - ToBeDeleted -}; +enum IconType : unsigned { Builtin, Transient, FileBased, ICONS_TOTAL, ToBeDeleted }; -struct MMCImage -{ +struct MMCImage { QIcon icon; QString key; QString filename; - bool present() const - { - return !icon.isNull() || !key.isEmpty(); - } + bool present() const { return !icon.isNull() || !key.isEmpty(); } }; -struct MMCIcon -{ +struct MMCIcon { QString m_key; QString m_name; MMCImage m_images[ICONS_TOTAL]; @@ -51,7 +58,7 @@ struct MMCIcon QIcon icon() const; void remove(IconType rm_type); void replace(IconType new_type, QIcon icon, QString path = QString()); - void replace(IconType new_type, const QString &key); + void replace(IconType new_type, const QString& key); bool isBuiltIn() const; QString getFilePath() const; }; diff --git a/launcher/java/JavaChecker.cpp b/launcher/java/JavaChecker.cpp index e4a686c27..20caba189 100644 --- a/launcher/java/JavaChecker.cpp +++ b/launcher/java/JavaChecker.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,26 +35,23 @@ #include "JavaChecker.h" -#include -#include -#include #include +#include +#include +#include -#include "JavaUtils.h" -#include "FileSystem.h" -#include "Commandline.h" #include "Application.h" +#include "Commandline.h" +#include "FileSystem.h" +#include "JavaUtils.h" -JavaChecker::JavaChecker(QObject *parent) : QObject(parent) -{ -} +JavaChecker::JavaChecker(QObject* parent) : QObject(parent) {} void JavaChecker::performCheck() { QString checkerJar = JavaUtils::getJavaCheckPath(); - if (checkerJar.isEmpty()) - { + if (checkerJar.isEmpty()) { qDebug() << "Java checker library could not be found. Please check your installation."; return; } @@ -62,25 +59,21 @@ void JavaChecker::performCheck() QStringList args; process.reset(new QProcess()); - if(m_args.size()) - { + if (m_args.size()) { auto extraArgs = Commandline::splitArgs(m_args); args.append(extraArgs); } - if(m_minMem != 0) - { + if (m_minMem != 0) { args << QString("-Xms%1m").arg(m_minMem); } - if(m_maxMem != 0) - { + if (m_maxMem != 0) { args << QString("-Xmx%1m").arg(m_maxMem); } - if(m_permGen != 64) - { + if (m_permGen != 64) { args << QString("-XX:PermSize=%1m").arg(m_permGen); } - args.append({"-jar", checkerJar}); + args.append({ "-jar", checkerJar }); process->setArguments(args); process->setProgram(m_path); process->setProcessChannelMode(QProcess::SeparateChannels); @@ -130,8 +123,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) qWarning() << "STDERR" << m_stderr; qDebug() << "Java checker finished with status" << status << "exit code" << exitcode; - if (status == QProcess::CrashExit || exitcode == 1) - { + if (status == QProcess::CrashExit || exitcode == 1) { result.validity = JavaCheckResult::Validity::Errored; emit checkFinished(result); return; @@ -146,8 +138,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) #else QStringList lines = m_stdout.split("\n", QString::SkipEmptyParts); #endif - for(QString line : lines) - { + for (QString line : lines) { line = line.trimmed(); // NOTE: workaround for GH-4125, where garbage is getting printed into stdout on bedrock linux if (line.contains("/bedrock/strata")) { @@ -159,18 +150,14 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) #else auto parts = line.split('=', QString::SkipEmptyParts); #endif - if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty()) - { + if (parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty()) { continue; - } - else - { + } else { results.insert(parts[0], parts[1]); } } - if(!results.contains("os.arch") || !results.contains("java.version") || !results.contains("java.vendor") || !success) - { + if (!results.contains("os.arch") || !results.contains("java.version") || !results.contains("java.vendor") || !success) { result.validity = JavaCheckResult::Validity::ReturnedInvalidData; emit checkFinished(result); return; @@ -181,7 +168,6 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) auto java_vendor = results["java.vendor"]; bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64"; - result.validity = JavaCheckResult::Validity::Valid; result.is_64bit = is_64; result.mojangPlatform = is_64 ? "64" : "32"; @@ -194,8 +180,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) void JavaChecker::error(QProcess::ProcessError err) { - if(err == QProcess::FailedToStart) - { + if (err == QProcess::FailedToStart) { qDebug() << "Java checker has failed to start."; qDebug() << "Process environment:"; qDebug() << process->environment(); @@ -216,8 +201,7 @@ void JavaChecker::error(QProcess::ProcessError err) void JavaChecker::timeout() { // NO MERCY. NO ABUSE. - if(process) - { + if (process) { qDebug() << "Java checker has been killed by timeout."; process->kill(); } diff --git a/launcher/java/JavaChecker.h b/launcher/java/JavaChecker.h index 122861cf7..743f49417 100644 --- a/launcher/java/JavaChecker.h +++ b/launcher/java/JavaChecker.h @@ -9,8 +9,7 @@ class JavaChecker; -struct JavaCheckResult -{ +struct JavaCheckResult { QString path; QString mojangPlatform; QString realPlatform; @@ -20,21 +19,15 @@ struct JavaCheckResult QString errorLog; bool is_64bit = false; int id; - enum class Validity - { - Errored, - ReturnedInvalidData, - Valid - } validity = Validity::Errored; + enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored; }; typedef shared_qobject_ptr QProcessPtr; typedef shared_qobject_ptr JavaCheckerPtr; -class JavaChecker : public QObject -{ +class JavaChecker : public QObject { Q_OBJECT -public: - explicit JavaChecker(QObject *parent = 0); + public: + explicit JavaChecker(QObject* parent = 0); void performCheck(); QString m_path; @@ -44,15 +37,15 @@ public: int m_maxMem = 0; int m_permGen = 64; -signals: + signals: void checkFinished(JavaCheckResult result); -private: + + private: QProcessPtr process; QTimer killTimer; QString m_stdout; QString m_stderr; -public -slots: + public slots: void timeout(); void finished(int exitcode, QProcess::ExitStatus); void error(QProcess::ProcessError); diff --git a/launcher/java/JavaCheckerJob.cpp b/launcher/java/JavaCheckerJob.cpp index 48274974d..870e2a09a 100644 --- a/launcher/java/JavaCheckerJob.cpp +++ b/launcher/java/JavaCheckerJob.cpp @@ -20,14 +20,12 @@ void JavaCheckerJob::partFinished(JavaCheckResult result) { num_finished++; - qDebug() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/" - << javacheckers.size(); + qDebug() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/" << javacheckers.size(); setProgress(num_finished, javacheckers.size()); javaresults.replace(result.id, result); - if (num_finished == javacheckers.size()) - { + if (num_finished == javacheckers.size()) { emitSucceeded(); } } @@ -35,8 +33,7 @@ void JavaCheckerJob::partFinished(JavaCheckResult result) void JavaCheckerJob::executeTask() { qDebug() << m_job_name.toLocal8Bit() << " started."; - for (auto iter : javacheckers) - { + for (auto iter : javacheckers) { javaresults.append(JavaCheckResult()); connect(iter.get(), &JavaChecker::checkFinished, this, &JavaCheckerJob::partFinished); iter->performCheck(); diff --git a/launcher/java/JavaCheckerJob.h b/launcher/java/JavaCheckerJob.h index c09864207..009687917 100644 --- a/launcher/java/JavaCheckerJob.h +++ b/launcher/java/JavaCheckerJob.h @@ -23,37 +23,32 @@ class JavaCheckerJob; typedef shared_qobject_ptr JavaCheckerJobPtr; // FIXME: this just seems horribly redundant -class JavaCheckerJob : public Task -{ +class JavaCheckerJob : public Task { Q_OBJECT -public: - explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name) {}; - virtual ~JavaCheckerJob() {}; + public: + explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name){}; + virtual ~JavaCheckerJob(){}; bool addJavaCheckerAction(JavaCheckerPtr base) { javacheckers.append(base); // if this is already running, the action needs to be started right away! - if (isRunning()) - { + if (isRunning()) { setProgress(num_finished, javacheckers.size()); connect(base.get(), &JavaChecker::checkFinished, this, &JavaCheckerJob::partFinished); base->performCheck(); } return true; } - QList getResults() - { - return javaresults; - } + QList getResults() { return javaresults; } -private slots: + private slots: void partFinished(JavaCheckResult result); -protected: + protected: virtual void executeTask() override; -private: + private: QString m_job_name; QList javacheckers; QList javaresults; diff --git a/launcher/java/JavaInstall.cpp b/launcher/java/JavaInstall.cpp index d5932bcb9..cfa471402 100644 --- a/launcher/java/JavaInstall.cpp +++ b/launcher/java/JavaInstall.cpp @@ -1,29 +1,64 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include "JavaInstall.h" +#include "BaseVersion.h" #include "StringUtils.h" -bool JavaInstall::operator<(const JavaInstall &rhs) +bool JavaInstall::operator<(const JavaInstall& rhs) { auto archCompare = StringUtils::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive); - if(archCompare != 0) + if (archCompare != 0) return archCompare < 0; - if(id < rhs.id) - { + if (id < rhs.id) { return true; } - if(id > rhs.id) - { + if (id > rhs.id) { return false; } return StringUtils::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0; } -bool JavaInstall::operator==(const JavaInstall &rhs) +bool JavaInstall::operator==(const JavaInstall& rhs) { return arch == rhs.arch && id == rhs.id && path == rhs.path; } -bool JavaInstall::operator>(const JavaInstall &rhs) +bool JavaInstall::operator>(const JavaInstall& rhs) { return (!operator<(rhs)) && (!operator==(rhs)); } + +bool JavaInstall::operator<(BaseVersion& a) +{ + try { + return operator<(dynamic_cast(a)); + } catch (const std::bad_cast& e) { + return BaseVersion::operator<(a); + } +} + +bool JavaInstall::operator>(BaseVersion& a) +{ + try { + return operator>(dynamic_cast(a)); + } catch (const std::bad_cast& e) { + return BaseVersion::operator>(a); + } +} diff --git a/launcher/java/JavaInstall.h b/launcher/java/JavaInstall.h index 64be40d19..30815b5a8 100644 --- a/launcher/java/JavaInstall.h +++ b/launcher/java/JavaInstall.h @@ -1,33 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #pragma once #include "BaseVersion.h" #include "JavaVersion.h" -struct JavaInstall : public BaseVersion -{ - JavaInstall(){} - JavaInstall(QString id, QString arch, QString path) - : id(id), arch(arch), path(path) - { - } - virtual QString descriptor() - { - return id.toString(); - } +struct JavaInstall : public BaseVersion { + JavaInstall() {} + JavaInstall(QString id, QString arch, QString path) : id(id), arch(arch), path(path) {} + virtual QString descriptor() { return id.toString(); } - virtual QString name() - { - return id.toString(); - } + virtual QString name() { return id.toString(); } - virtual QString typeString() const - { - return arch; - } + virtual QString typeString() const { return arch; } - bool operator<(const JavaInstall & rhs); - bool operator==(const JavaInstall & rhs); - bool operator>(const JavaInstall & rhs); + virtual bool operator<(BaseVersion& a) override; + virtual bool operator>(BaseVersion& a) override; + bool operator<(const JavaInstall& rhs); + bool operator==(const JavaInstall& rhs); + bool operator>(const JavaInstall& rhs); JavaVersion id; QString arch; diff --git a/launcher/java/JavaInstallList.cpp b/launcher/java/JavaInstallList.cpp index 3407fdf72..d8be4963f 100644 --- a/launcher/java/JavaInstallList.cpp +++ b/launcher/java/JavaInstallList.cpp @@ -39,14 +39,12 @@ #include -#include "java/JavaInstallList.h" #include "java/JavaCheckerJob.h" +#include "java/JavaInstallList.h" #include "java/JavaUtils.h" #include "minecraft/VersionFilterData.h" -JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent) -{ -} +JavaInstallList::JavaInstallList(QObject* parent) : BaseVersionList(parent) {} Task::Ptr JavaInstallList::getLoadTask() { @@ -56,8 +54,7 @@ Task::Ptr JavaInstallList::getLoadTask() Task::Ptr JavaInstallList::getCurrentTask() { - if(m_status == Status::InProgress) - { + if (m_status == Status::InProgress) { return m_loadTask; } return nullptr; @@ -65,8 +62,7 @@ Task::Ptr JavaInstallList::getCurrentTask() void JavaInstallList::load() { - if(m_status != Status::InProgress) - { + if (m_status != Status::InProgress) { m_status = Status::InProgress; m_loadTask.reset(new JavaListLoadTask(this)); m_loadTask->start(); @@ -88,7 +84,7 @@ int JavaInstallList::count() const return m_vlist.count(); } -QVariant JavaInstallList::data(const QModelIndex &index, int role) const +QVariant JavaInstallList::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); @@ -97,8 +93,7 @@ QVariant JavaInstallList::data(const QModelIndex &index, int role) const return QVariant(); auto version = std::dynamic_pointer_cast(m_vlist[index.row()]); - switch (role) - { + switch (role) { case SortRole: return -index.row(); case VersionPointerRole: @@ -120,17 +115,15 @@ QVariant JavaInstallList::data(const QModelIndex &index, int role) const BaseVersionList::RoleList JavaInstallList::providesRoles() const { - return {VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, ArchitectureRole}; + return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, ArchitectureRole }; } - void JavaInstallList::updateListData(QList versions) { beginResetModel(); m_vlist = versions; sortVersions(); - if(m_vlist.size()) - { + if (m_vlist.size()) { auto best = std::dynamic_pointer_cast(m_vlist[0]); best->recommended = true; } @@ -153,15 +146,13 @@ void JavaInstallList::sortVersions() endResetModel(); } -JavaListLoadTask::JavaListLoadTask(JavaInstallList *vlist) : Task() +JavaListLoadTask::JavaListLoadTask(JavaInstallList* vlist) : Task() { m_list = vlist; m_currentRecommended = NULL; } -JavaListLoadTask::~JavaListLoadTask() -{ -} +JavaListLoadTask::~JavaListLoadTask() {} void JavaListLoadTask::executeTask() { @@ -176,8 +167,7 @@ void JavaListLoadTask::executeTask() qDebug() << "Probing the following Java paths: "; int id = 0; - for(QString candidate : candidate_paths) - { + for (QString candidate : candidate_paths) { qDebug() << " " << candidate; auto candidate_checker = new JavaChecker(); @@ -197,10 +187,8 @@ void JavaListLoadTask::javaCheckerFinished() auto results = m_job->getResults(); qDebug() << "Found the following valid Java installations:"; - for(JavaCheckResult result : results) - { - if(result.validity == JavaCheckResult::Validity::Valid) - { + for (JavaCheckResult result : results) { + if (result.validity == JavaCheckResult::Validity::Valid) { JavaInstallPtr javaVersion(new JavaInstall()); javaVersion->id = result.javaVersion; @@ -213,13 +201,11 @@ void JavaListLoadTask::javaCheckerFinished() } QList javas_bvp; - for (auto java : candidates) - { - //qDebug() << java->id << java->arch << " at " << java->path; + for (auto java : candidates) { + // qDebug() << java->id << java->arch << " at " << java->path; BaseVersion::Ptr bp_java = std::dynamic_pointer_cast(java); - if (bp_java) - { + if (bp_java) { javas_bvp.append(java); } } diff --git a/launcher/java/JavaInstallList.h b/launcher/java/JavaInstallList.h index 733dc7e1c..1eebadf23 100644 --- a/launcher/java/JavaInstallList.h +++ b/launcher/java/JavaInstallList.h @@ -15,8 +15,8 @@ #pragma once -#include #include +#include #include "BaseVersionList.h" #include "tasks/Task.h" @@ -28,17 +28,12 @@ class JavaListLoadTask; -class JavaInstallList : public BaseVersionList -{ +class JavaInstallList : public BaseVersionList { Q_OBJECT - enum class Status - { - NotDone, - InProgress, - Done - }; -public: - explicit JavaInstallList(QObject *parent = 0); + enum class Status { NotDone, InProgress, Done }; + + public: + explicit JavaInstallList(QObject* parent = 0); Task::Ptr getLoadTask() override; bool isLoaded() override; @@ -46,36 +41,35 @@ public: int count() const override; void sortVersions() override; - QVariant data(const QModelIndex &index, int role) const override; + QVariant data(const QModelIndex& index, int role) const override; RoleList providesRoles() const override; -public slots: + public slots: void updateListData(QList versions) override; -protected: + protected: void load(); Task::Ptr getCurrentTask(); -protected: + protected: Status m_status = Status::NotDone; shared_qobject_ptr m_loadTask; QList m_vlist; }; -class JavaListLoadTask : public Task -{ +class JavaListLoadTask : public Task { Q_OBJECT -public: - explicit JavaListLoadTask(JavaInstallList *vlist); + public: + explicit JavaListLoadTask(JavaInstallList* vlist); virtual ~JavaListLoadTask(); void executeTask() override; -public slots: + public slots: void javaCheckerFinished(); -protected: + protected: shared_qobject_ptr m_job; - JavaInstallList *m_list; - JavaInstall *m_currentRecommended; + JavaInstallList* m_list; + JavaInstall* m_currentRecommended; }; diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index e55663aab..3512c3079 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -33,24 +33,21 @@ * limitations under the License. */ -#include -#include #include +#include #include #include #include -#include "java/JavaUtils.h" -#include "java/JavaInstallList.h" -#include "FileSystem.h" #include "Application.h" +#include "FileSystem.h" +#include "java/JavaInstallList.h" +#include "java/JavaUtils.h" #define IBUS "@im=ibus" -JavaUtils::JavaUtils() -{ -} +JavaUtils::JavaUtils() {} QString stripVariableEntries(QString name, QString target, QString remove) { @@ -65,8 +62,7 @@ QString stripVariableEntries(QString name, QString target, QString remove) for (QString item : toRemove) { bool removed = targetItems.removeOne(item); if (!removed) - qWarning() << "Entry" << item - << "could not be stripped from variable" << name; + qWarning() << "Entry" << item << "could not be stripped from variable" << name; } return targetItems.join(delimiter); } @@ -77,20 +73,10 @@ QProcessEnvironment CleanEnviroment() QProcessEnvironment rawenv = QProcessEnvironment::systemEnvironment(); QProcessEnvironment env; - QStringList ignored = - { - "JAVA_ARGS", - "CLASSPATH", - "CONFIGPATH", - "JAVA_HOME", - "JRE_HOME", - "_JAVA_OPTIONS", - "JAVA_OPTIONS", - "JAVA_TOOL_OPTIONS" - }; + QStringList ignored = { "JAVA_ARGS", "CLASSPATH", "CONFIGPATH", "JAVA_HOME", + "JRE_HOME", "_JAVA_OPTIONS", "JAVA_OPTIONS", "JAVA_TOOL_OPTIONS" }; - QStringList stripped = - { + QStringList stripped = { #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) "LD_LIBRARY_PATH", "LD_PRELOAD", @@ -98,12 +84,10 @@ QProcessEnvironment CleanEnviroment() "QT_PLUGIN_PATH", "QT_FONTPATH" }; - for(auto key: rawenv.keys()) - { + for (auto key : rawenv.keys()) { auto value = rawenv.value(key); // filter out dangerous java crap - if(ignored.contains(key)) - { + if (ignored.contains(key)) { qDebug() << "Env: ignoring" << key << value; continue; } @@ -111,13 +95,11 @@ QProcessEnvironment CleanEnviroment() // These are used to strip the original variables // If there is "LD_LIBRARY_PATH" and "LAUNCHER_LD_LIBRARY_PATH", we want to // remove all values in "LAUNCHER_LD_LIBRARY_PATH" from "LD_LIBRARY_PATH" - if(key.startsWith("LAUNCHER_")) - { + if (key.startsWith("LAUNCHER_")) { qDebug() << "Env: ignoring" << key << value; continue; } - if(stripped.contains(key)) - { + if (stripped.contains(key)) { QString newValue = stripVariableEntries(key, value, rawenv.value("LAUNCHER_" + key)); qDebug() << "Env: stripped" << key << value << "to" << newValue; @@ -125,8 +107,7 @@ QProcessEnvironment CleanEnviroment() #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) // Strip IBus // IBus is a Linux IME framework. For some reason, it breaks MC? - if (key == "XMODIFIERS" && value.contains(IBUS)) - { + if (key == "XMODIFIERS" && value.contains(IBUS)) { QString save = value; value.replace(IBUS, ""); qDebug() << "Env: stripped" << IBUS << "from" << save << ":" << value; @@ -137,8 +118,7 @@ QProcessEnvironment CleanEnviroment() } #ifdef Q_OS_LINUX // HACK: Workaround for QTBUG-42500 - if(!env.contains("LD_LIBRARY_PATH")) - { + if (!env.contains("LD_LIBRARY_PATH")) { env.insert("LD_LIBRARY_PATH", ""); } #endif @@ -177,7 +157,7 @@ QStringList addJavasFromEnv(QList javas) auto env = qEnvironmentVariable("PRISMLAUNCHER_JAVA_PATHS"); // FIXME: use launcher name from buildconfig #if defined(Q_OS_WIN32) QList javaPaths = env.replace("\\", "/").split(QLatin1String(";")); - + auto envPath = qEnvironmentVariable("PATH"); QList javaPathsfromPath = envPath.replace("\\", "/").split(QLatin1String(";")); for (QString string : javaPathsfromPath) { @@ -186,8 +166,7 @@ QStringList addJavasFromEnv(QList javas) #else QList javaPaths = env.split(QLatin1String(":")); #endif - for(QString i : javaPaths) - { + for (QString i : javaPaths) { javas.append(i); }; return javas; @@ -205,9 +184,8 @@ QList JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString archType = "32"; HKEY jreKey; - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyName.toStdWString().c_str(), 0, - KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == ERROR_SUCCESS) - { + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyName.toStdWString().c_str(), 0, KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == + ERROR_SUCCESS) { // Read the current type version from the registry. // This will be used to find any key that contains the JavaHome value. @@ -215,46 +193,36 @@ QList JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString DWORD subKeyNameSize, numSubKeys, retCode; // Get the number of subkeys - RegQueryInfoKeyW(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, - NULL, NULL); + RegQueryInfoKeyW(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); // Iterate until RegEnumKeyEx fails - if (numSubKeys > 0) - { - for (DWORD i = 0; i < numSubKeys; i++) - { + if (numSubKeys > 0) { + for (DWORD i = 0; i < numSubKeys; i++) { subKeyNameSize = 255; - retCode = RegEnumKeyExW(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL, - NULL); + retCode = RegEnumKeyExW(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL, NULL); QString newSubkeyName = QString::fromWCharArray(subKeyName); - if (retCode == ERROR_SUCCESS) - { + if (retCode == ERROR_SUCCESS) { // Now open the registry key for the version that we just got. QString newKeyName = keyName + "\\" + newSubkeyName + subkeySuffix; HKEY newKey; - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, newKeyName.toStdWString().c_str(), 0, - KEY_READ | KEY_WOW64_64KEY, &newKey) == ERROR_SUCCESS) - { + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, newKeyName.toStdWString().c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &newKey) == + ERROR_SUCCESS) { // Read the JavaHome value to find where Java is installed. DWORD valueSz = 0; - if (RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, NULL, - &valueSz) == ERROR_SUCCESS) - { - WCHAR *value = new WCHAR[valueSz]; - RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, (BYTE *)value, - &valueSz); + if (RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, NULL, &valueSz) == ERROR_SUCCESS) { + WCHAR* value = new WCHAR[valueSz]; + RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, (BYTE*)value, &valueSz); QString newValue = QString::fromWCharArray(value); - delete [] value; + delete[] value; // Now, we construct the version object and add it to the list. JavaInstallPtr javaVersion(new JavaInstall()); javaVersion->id = newSubkeyName; javaVersion->arch = archType; - javaVersion->path = - QDir(FS::PathCombine(newValue, "bin")).absoluteFilePath("javaw.exe"); + javaVersion->path = QDir(FS::PathCombine(newValue, "bin")).absoluteFilePath("javaw.exe"); javas.append(javaVersion); } @@ -275,66 +243,56 @@ QList JavaUtils::FindJavaPaths() QList java_candidates; // Oracle - QList JRE64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome"); - QList JDK64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome"); - QList JRE32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome"); - QList JDK32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome"); + QList JRE64s = + this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome"); + QList JDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome"); + QList JRE32s = + this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome"); + QList JDK32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome"); // Oracle for Java 9 and newer - QList NEWJRE64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome"); - QList NEWJDK64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome"); - QList NEWJRE32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome"); - QList NEWJDK32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome"); + QList NEWJRE64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome"); + QList NEWJDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome"); + QList NEWJRE32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome"); + QList NEWJDK32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome"); // AdoptOpenJDK - QList ADOPTOPENJRE32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI"); - QList ADOPTOPENJRE64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI"); - QList ADOPTOPENJDK32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI"); - QList ADOPTOPENJDK64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI"); + QList ADOPTOPENJRE32s = + this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTOPENJRE64s = + this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTOPENJDK32s = + this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI"); + QList ADOPTOPENJDK64s = + this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI"); // Eclipse Foundation - QList FOUNDATIONJDK32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI"); - QList FOUNDATIONJDK64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI"); + QList FOUNDATIONJDK32s = + this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI"); + QList FOUNDATIONJDK64s = + this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI"); // Eclipse Adoptium - QList ADOPTIUMJRE32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI"); - QList ADOPTIUMJRE64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI"); - QList ADOPTIUMJDK32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); - QList ADOPTIUMJDK64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); + QList ADOPTIUMJRE32s = + this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTIUMJRE64s = + this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTIUMJDK32s = + this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); + QList ADOPTIUMJDK64s = + this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); // Microsoft - QList MICROSOFTJDK64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI"); + QList MICROSOFTJDK64s = + this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI"); // Azul Zulu - QList ZULU64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath"); - QList ZULU32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath"); + QList ZULU64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath"); + QList ZULU32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath"); // BellSoft Liberica - QList LIBERICA64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath"); - QList LIBERICA32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath"); + QList LIBERICA64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath"); + QList LIBERICA32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath"); // List x64 before x86 java_candidates.append(JRE64s); @@ -371,10 +329,8 @@ QList JavaUtils::FindJavaPaths() java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path)); QList candidates; - for(JavaInstallPtr java_candidate : java_candidates) - { - if(!candidates.contains(java_candidate->path)) - { + for (JavaInstallPtr java_candidate : java_candidates) { + if (!candidates.contains(java_candidate->path)) { candidates.append(java_candidate->path); } } @@ -394,13 +350,13 @@ QList JavaUtils::FindJavaPaths() javas.append("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java"); QDir libraryJVMDir("/Library/Java/JavaVirtualMachines/"); QStringList libraryJVMJavas = libraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); - foreach (const QString &java, libraryJVMJavas) { + foreach (const QString& java, libraryJVMJavas) { javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/jre/bin/java"); } QDir systemLibraryJVMDir("/System/Library/Java/JavaVirtualMachines/"); QStringList systemLibraryJVMJavas = systemLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); - foreach (const QString &java, systemLibraryJVMJavas) { + foreach (const QString& java, systemLibraryJVMJavas) { javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); } @@ -414,14 +370,12 @@ QList JavaUtils::FindJavaPaths() { QList javas; javas.append(this->GetDefaultJava()->path); - auto scanJavaDir = [&](const QString & dirPath) - { + auto scanJavaDir = [&](const QString& dirPath) { QDir dir(dirPath); - if(!dir.exists()) + if (!dir.exists()) return; auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); - for(auto & entry: entries) - { + for (auto& entry : entries) { QString prefix; prefix = entry.canonicalFilePath(); javas.append(FS::PathCombine(prefix, "jre/bin/java")); @@ -430,8 +384,7 @@ QList JavaUtils::FindJavaPaths() }; // java installed in a snap is installed in the standard directory, but underneath $SNAP auto snap = qEnvironmentVariable("SNAP"); - auto scanJavaDirs = [&](const QString & dirPath) - { + auto scanJavaDirs = [&](const QString& dirPath) { scanJavaDir(dirPath); if (!snap.isNull()) { scanJavaDir(snap + dirPath); diff --git a/launcher/java/JavaUtils.h b/launcher/java/JavaUtils.h index 9b69b516e..616179706 100644 --- a/launcher/java/JavaUtils.h +++ b/launcher/java/JavaUtils.h @@ -27,10 +27,9 @@ QString stripVariableEntries(QString name, QString target, QString remove); QProcessEnvironment CleanEnviroment(); -class JavaUtils : public QObject -{ +class JavaUtils : public QObject { Q_OBJECT -public: + public: JavaUtils(); JavaInstallPtr MakeJavaPtr(QString path, QString id = "unknown", QString arch = "unknown"); diff --git a/launcher/java/JavaVersion.cpp b/launcher/java/JavaVersion.cpp index 0e4fc1d3c..f9ac47824 100644 --- a/launcher/java/JavaVersion.cpp +++ b/launcher/java/JavaVersion.cpp @@ -5,27 +5,22 @@ #include #include -JavaVersion & JavaVersion::operator=(const QString & javaVersionString) +JavaVersion& JavaVersion::operator=(const QString& javaVersionString) { m_string = javaVersionString; - auto getCapturedInteger = [](const QRegularExpressionMatch & match, const QString &what) -> int - { + auto getCapturedInteger = [](const QRegularExpressionMatch& match, const QString& what) -> int { auto str = match.captured(what); - if(str.isEmpty()) - { + if (str.isEmpty()) { return 0; } return str.toInt(); }; QRegularExpression pattern; - if(javaVersionString.startsWith("1.")) - { - pattern = QRegularExpression ("1[.](?[0-9]+)([.](?[0-9]+))?(_(?[0-9]+)?)?(-(?[a-zA-Z0-9]+))?"); - } - else - { + if (javaVersionString.startsWith("1.")) { + pattern = QRegularExpression("1[.](?[0-9]+)([.](?[0-9]+))?(_(?[0-9]+)?)?(-(?[a-zA-Z0-9]+))?"); + } else { pattern = QRegularExpression("(?[0-9]+)([.](?[0-9]+))?([.](?[0-9]+))?(-(?[a-zA-Z0-9]+))?"); } @@ -38,85 +33,77 @@ JavaVersion & JavaVersion::operator=(const QString & javaVersionString) return *this; } -JavaVersion::JavaVersion(const QString &rhs) +JavaVersion::JavaVersion(const QString& rhs) { operator=(rhs); } -QString JavaVersion::toString() +QString JavaVersion::toString() const { return m_string; } bool JavaVersion::requiresPermGen() { - if(m_parseable) - { + if (m_parseable) { return m_major < 8; } return true; } -bool JavaVersion::operator<(const JavaVersion &rhs) +bool JavaVersion::operator<(const JavaVersion& rhs) { - if(m_parseable && rhs.m_parseable) - { + if (m_parseable && rhs.m_parseable) { auto major = m_major; auto rmajor = rhs.m_major; // HACK: discourage using java 9 - if(major > 8) + if (major > 8) major = -major; - if(rmajor > 8) + if (rmajor > 8) rmajor = -rmajor; - if(major < rmajor) + if (major < rmajor) return true; - if(major > rmajor) + if (major > rmajor) return false; - if(m_minor < rhs.m_minor) + if (m_minor < rhs.m_minor) return true; - if(m_minor > rhs.m_minor) + if (m_minor > rhs.m_minor) return false; - if(m_security < rhs.m_security) + if (m_security < rhs.m_security) return true; - if(m_security > rhs.m_security) + if (m_security > rhs.m_security) return false; // everything else being equal, consider prerelease status bool thisPre = !m_prerelease.isEmpty(); bool rhsPre = !rhs.m_prerelease.isEmpty(); - if(thisPre && !rhsPre) - { + if (thisPre && !rhsPre) { // this is a prerelease and the other one isn't -> lesser return true; - } - else if(!thisPre && rhsPre) - { + } else if (!thisPre && rhsPre) { // this isn't a prerelease and the other one is -> greater return false; - } - else if(thisPre && rhsPre) - { + } else if (thisPre && rhsPre) { // both are prereleases - use natural compare... return StringUtils::naturalCompare(m_prerelease, rhs.m_prerelease, Qt::CaseSensitive) < 0; } // neither is prerelease, so they are the same -> this cannot be less than rhs return false; - } - else return StringUtils::naturalCompare(m_string, rhs.m_string, Qt::CaseSensitive) < 0; + } else + return StringUtils::naturalCompare(m_string, rhs.m_string, Qt::CaseSensitive) < 0; } -bool JavaVersion::operator==(const JavaVersion &rhs) +bool JavaVersion::operator==(const JavaVersion& rhs) { - if(m_parseable && rhs.m_parseable) - { + if (m_parseable && rhs.m_parseable) { return m_major == rhs.m_major && m_minor == rhs.m_minor && m_security == rhs.m_security && m_prerelease == rhs.m_prerelease; } return m_string == rhs.m_string; } -bool JavaVersion::operator>(const JavaVersion &rhs) +bool JavaVersion::operator>(const JavaVersion& rhs) { return (!operator<(rhs)) && (!operator==(rhs)); } diff --git a/launcher/java/JavaVersion.h b/launcher/java/JavaVersion.h index 9bbf06425..7e66269cb 100644 --- a/launcher/java/JavaVersion.h +++ b/launcher/java/JavaVersion.h @@ -4,42 +4,34 @@ // NOTE: apparently the GNU C library pollutes the global namespace with these... undef them. #ifdef major - #undef major +#undef major #endif #ifdef minor - #undef minor +#undef minor #endif -class JavaVersion -{ +class JavaVersion { friend class JavaVersionTest; -public: - JavaVersion() {}; - JavaVersion(const QString & rhs); - JavaVersion & operator=(const QString & rhs); + public: + JavaVersion() {} + JavaVersion(const QString& rhs); - bool operator<(const JavaVersion & rhs); - bool operator==(const JavaVersion & rhs); - bool operator>(const JavaVersion & rhs); + JavaVersion& operator=(const QString& rhs); + + bool operator<(const JavaVersion& rhs); + bool operator==(const JavaVersion& rhs); + bool operator>(const JavaVersion& rhs); bool requiresPermGen(); - QString toString(); + QString toString() const; - int major() - { - return m_major; - } - int minor() - { - return m_minor; - } - int security() - { - return m_security; - } -private: + int major() { return m_major; } + int minor() { return m_minor; } + int security() { return m_security; } + + private: QString m_string; int m_major = 0; int m_minor = 0; diff --git a/launcher/launch/LaunchStep.cpp b/launcher/launch/LaunchStep.cpp index d6bb6e885..ebc534617 100644 --- a/launcher/launch/LaunchStep.cpp +++ b/launcher/launch/LaunchStep.cpp @@ -16,7 +16,7 @@ #include "LaunchStep.h" #include "LaunchTask.h" -void LaunchStep::bind(LaunchTask *parent) +void LaunchStep::bind(LaunchTask* parent) { m_parent = parent; connect(this, &LaunchStep::readyForLaunch, parent, &LaunchTask::onReadyForLaunch); diff --git a/launcher/launch/LaunchStep.h b/launcher/launch/LaunchStep.h index 3939f9604..b1bec2b4a 100644 --- a/launcher/launch/LaunchStep.h +++ b/launcher/launch/LaunchStep.h @@ -15,36 +15,32 @@ #pragma once -#include "tasks/Task.h" #include "MessageLevel.h" +#include "tasks/Task.h" #include class LaunchTask; -class LaunchStep: public Task -{ +class LaunchStep : public Task { Q_OBJECT -public: /* methods */ - explicit LaunchStep(LaunchTask *parent):Task(nullptr), m_parent(parent) - { - bind(parent); - }; - virtual ~LaunchStep() {}; + public: /* methods */ + explicit LaunchStep(LaunchTask* parent) : Task(nullptr), m_parent(parent) { bind(parent); }; + virtual ~LaunchStep(){}; -private: /* methods */ - void bind(LaunchTask *parent); + private: /* methods */ + void bind(LaunchTask* parent); -signals: + signals: void logLines(QStringList lines, MessageLevel::Enum level); void logLine(QString line, MessageLevel::Enum level); void readyForLaunch(); void progressReportingRequest(); -public slots: - virtual void proceed() {}; + public slots: + virtual void proceed(){}; // called in the opposite order than the Task launch(), used to clean up or otherwise undo things after the launch ends - virtual void finalize() {}; + virtual void finalize(){}; -protected: /* data */ - LaunchTask *m_parent; + protected: /* data */ + LaunchTask* m_parent; }; diff --git a/launcher/launch/LaunchTask.cpp b/launcher/launch/LaunchTask.cpp index 9e1794b34..06a32bd28 100644 --- a/launcher/launch/LaunchTask.cpp +++ b/launcher/launch/LaunchTask.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -36,16 +36,16 @@ */ #include "launch/LaunchTask.h" -#include "MessageLevel.h" -#include "java/JavaChecker.h" -#include "tasks/Task.h" +#include +#include #include #include #include #include -#include #include -#include +#include "MessageLevel.h" +#include "java/JavaChecker.h" +#include "tasks/Task.h" void LaunchTask::init() { @@ -59,9 +59,7 @@ shared_qobject_ptr LaunchTask::create(InstancePtr inst) return proc; } -LaunchTask::LaunchTask(InstancePtr instance): m_instance(instance) -{ -} +LaunchTask::LaunchTask(InstancePtr instance) : m_instance(instance) {} void LaunchTask::appendStep(shared_qobject_ptr step) { @@ -76,8 +74,7 @@ void LaunchTask::prependStep(shared_qobject_ptr step) void LaunchTask::executeTask() { m_instance->setCrashed(false); - if(!m_steps.size()) - { + if (!m_steps.size()) { state = LaunchTask::Finished; emitSucceeded(); } @@ -94,46 +91,35 @@ void LaunchTask::onReadyForLaunch() void LaunchTask::onStepFinished() { // initial -> just start the first step - if(currentStep == -1) - { - currentStep ++; + if (currentStep == -1) { + currentStep++; m_steps[currentStep]->start(); return; } auto step = m_steps[currentStep]; - if(step->wasSuccessful()) - { + if (step->wasSuccessful()) { // end? - if(currentStep == m_steps.size() - 1) - { + if (currentStep == m_steps.size() - 1) { finalizeSteps(true, QString()); - } - else - { - currentStep ++; + } else { + currentStep++; step = m_steps[currentStep]; step->start(); } - } - else - { + } else { finalizeSteps(false, step->failReason()); } } void LaunchTask::finalizeSteps(bool successful, const QString& error) { - for(auto step = currentStep; step >= 0; step--) - { + for (auto step = currentStep; step >= 0; step--) { m_steps[step]->finalize(); } - if(successful) - { + if (successful) { emitSucceeded(); - } - else - { + } else { emitFailed(error); } } @@ -152,8 +138,7 @@ void LaunchTask::setCensorFilter(QMap filter) QString LaunchTask::censorPrivateInfo(QString in) { auto iter = m_censorFilter.begin(); - while (iter != m_censorFilter.end()) - { + while (iter != m_censorFilter.end()) { in.replace(iter.key(), iter.value()); iter++; } @@ -162,8 +147,7 @@ QString LaunchTask::censorPrivateInfo(QString in) void LaunchTask::proceed() { - if(state != LaunchTask::Waiting) - { + if (state != LaunchTask::Waiting) { return; } m_steps[currentStep]->proceed(); @@ -171,8 +155,7 @@ void LaunchTask::proceed() bool LaunchTask::canAbort() const { - switch(state) - { + switch (state) { case LaunchTask::Aborted: case LaunchTask::Failed: case LaunchTask::Finished: @@ -180,8 +163,7 @@ bool LaunchTask::canAbort() const case LaunchTask::NotStarted: return true; case LaunchTask::Running: - case LaunchTask::Waiting: - { + case LaunchTask::Waiting: { auto step = m_steps[currentStep]; return step->canAbort(); } @@ -191,28 +173,23 @@ bool LaunchTask::canAbort() const bool LaunchTask::abort() { - switch(state) - { + switch (state) { case LaunchTask::Aborted: case LaunchTask::Failed: case LaunchTask::Finished: return true; - case LaunchTask::NotStarted: - { + case LaunchTask::NotStarted: { state = LaunchTask::Aborted; emitFailed("Aborted"); return true; } case LaunchTask::Running: - case LaunchTask::Waiting: - { + case LaunchTask::Waiting: { auto step = m_steps[currentStep]; - if(!step->canAbort()) - { + if (!step->canAbort()) { return false; } - if(step->abort()) - { + if (step->abort()) { state = LaunchTask::Aborted; return true; } @@ -225,23 +202,22 @@ bool LaunchTask::abort() shared_qobject_ptr LaunchTask::getLogModel() { - if(!m_logModel) - { + if (!m_logModel) { m_logModel.reset(new LogModel()); m_logModel->setMaxLines(m_instance->getConsoleMaxLines()); m_logModel->setStopOnOverflow(m_instance->shouldStopOnConsoleOverflow()); // FIXME: should this really be here? m_logModel->setOverflowMessage(tr("Stopped watching the game log because the log length surpassed %1 lines.\n" - "You may have to fix your mods because the game is still logging to files and" - " likely wasting harddrive space at an alarming rate!").arg(m_logModel->getMaxLines())); + "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())); } return m_logModel; } -void LaunchTask::onLogLines(const QStringList &lines, MessageLevel::Enum defaultLevel) +void LaunchTask::onLogLines(const QStringList& lines, MessageLevel::Enum defaultLevel) { - for (auto & line: lines) - { + for (auto& line : lines) { onLogLine(line, defaultLevel); } } @@ -250,21 +226,19 @@ void LaunchTask::onLogLine(QString line, MessageLevel::Enum level) { // if the launcher part set a log level, use it auto innerLevel = MessageLevel::fromLine(line); - if(innerLevel != MessageLevel::Unknown) - { + if (innerLevel != MessageLevel::Unknown) { level = innerLevel; } // If the level is still undetermined, guess level - if (level == MessageLevel::StdErr || level == MessageLevel::StdOut || level == MessageLevel::Unknown) - { + if (level == MessageLevel::StdErr || level == MessageLevel::StdOut || level == MessageLevel::Unknown) { level = m_instance->guessLevel(line, level); } // censor private user info line = censorPrivateInfo(line); - auto &model = *getLogModel(); + auto& model = *getLogModel(); model.append(level, line); } @@ -281,22 +255,20 @@ void LaunchTask::emitFailed(QString reason) Task::emitFailed(reason); } -void LaunchTask::substituteVariables(QStringList &args) const +void LaunchTask::substituteVariables(QStringList& args) const { auto env = m_instance->createEnvironment(); - for (auto key : env.keys()) - { + for (auto key : env.keys()) { args.replaceInStrings("$" + key, env.value(key)); } } -void LaunchTask::substituteVariables(QString &cmd) const +void LaunchTask::substituteVariables(QString& cmd) const { auto env = m_instance->createEnvironment(); - for (auto key : env.keys()) - { + for (auto key : env.keys()) { cmd.replace("$" + key, env.value(key)); } } diff --git a/launcher/launch/LaunchTask.h b/launcher/launch/LaunchTask.h index 9c72b23f0..e79c43557 100644 --- a/launcher/launch/LaunchTask.h +++ b/launcher/launch/LaunchTask.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -36,54 +36,36 @@ */ #pragma once -#include #include -#include "LogModel.h" +#include #include "BaseInstance.h" -#include "MessageLevel.h" -#include "LoggedProcess.h" #include "LaunchStep.h" +#include "LogModel.h" +#include "LoggedProcess.h" +#include "MessageLevel.h" -class LaunchTask: public Task -{ +class LaunchTask : public Task { Q_OBJECT -protected: + protected: explicit LaunchTask(InstancePtr instance); void init(); -public: - enum State - { - NotStarted, - Running, - Waiting, - Failed, - Aborted, - Finished - }; + public: + enum State { NotStarted, Running, Waiting, Failed, Aborted, Finished }; -public: /* methods */ + public: /* methods */ static shared_qobject_ptr create(InstancePtr inst); - virtual ~LaunchTask() {}; + virtual ~LaunchTask(){}; void appendStep(shared_qobject_ptr step); void prependStep(shared_qobject_ptr step); void setCensorFilter(QMap filter); - InstancePtr instance() - { - return m_instance; - } + InstancePtr instance() { return m_instance; } - void setPid(qint64 pid) - { - m_pid = pid; - } + void setPid(qint64 pid) { m_pid = pid; } - qint64 pid() - { - return m_pid; - } + qint64 pid() { return m_pid; } /** * @brief prepare the process for launch (for multi-stage launch) @@ -104,39 +86,39 @@ public: /* methods */ shared_qobject_ptr getLogModel(); -public: - void substituteVariables(QStringList &args) const; - void substituteVariables(QString &cmd) const; + public: + void substituteVariables(QStringList& args) const; + void substituteVariables(QString& cmd) const; QString censorPrivateInfo(QString in); -protected: /* methods */ + protected: /* methods */ virtual void emitFailed(QString reason) override; virtual void emitSucceeded() override; -signals: + signals: /** * @brief emitted when the launch preparations are done */ void readyForLaunch(); - void requestProgress(Task *task); + void requestProgress(Task* task); void requestLogging(); -public slots: + public slots: void onLogLines(const QStringList& lines, MessageLevel::Enum defaultLevel = MessageLevel::Launcher); void onLogLine(QString line, MessageLevel::Enum defaultLevel = MessageLevel::Launcher); void onReadyForLaunch(); void onStepFinished(); void onProgressReportingRequested(); -private: /*methods */ - void finalizeSteps(bool successful, const QString & error); + private: /*methods */ + void finalizeSteps(bool successful, const QString& error); -protected: /* data */ + protected: /* data */ InstancePtr m_instance; shared_qobject_ptr m_logModel; - QList > m_steps; + QList> m_steps; QMap m_censorFilter; int currentStep = -1; State state = NotStarted; diff --git a/launcher/launch/LogModel.cpp b/launcher/launch/LogModel.cpp index 92f9487a9..23a33ae18 100644 --- a/launcher/launch/LogModel.cpp +++ b/launcher/launch/LogModel.cpp @@ -1,11 +1,11 @@ #include "LogModel.h" -LogModel::LogModel(QObject *parent):QAbstractListModel(parent) +LogModel::LogModel(QObject* parent) : QAbstractListModel(parent) { m_content.resize(m_maxLines); } -int LogModel::rowCount(const QModelIndex &parent) const +int LogModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; @@ -13,19 +13,17 @@ int LogModel::rowCount(const QModelIndex &parent) const return m_numLines; } -QVariant LogModel::data(const QModelIndex &index, int role) const +QVariant LogModel::data(const QModelIndex& index, int role) const { if (index.row() < 0 || index.row() >= m_numLines) return QVariant(); auto row = index.row(); auto realRow = (row + m_firstLine) % m_maxLines; - if (role == Qt::DisplayRole || role == Qt::EditRole) - { + if (role == Qt::DisplayRole || role == Qt::EditRole) { return m_content[realRow].line; } - if(role == LevelRole) - { + if (role == LevelRole) { return m_content[realRow].level; } @@ -34,31 +32,26 @@ QVariant LogModel::data(const QModelIndex &index, int role) const void LogModel::append(MessageLevel::Enum level, QString line) { - if(m_suspended) - { + if (m_suspended) { return; } int lineNum = (m_firstLine + m_numLines) % m_maxLines; // overflow - if(m_numLines == m_maxLines) - { - if(m_stopOnOverflow) - { + if (m_numLines == m_maxLines) { + if (m_stopOnOverflow) { // nothing more to do, the buffer is full return; } beginRemoveRows(QModelIndex(), 0, 0); m_firstLine = (m_firstLine + 1) % m_maxLines; - m_numLines --; + m_numLines--; endRemoveRows(); - } - else if (m_numLines == m_maxLines - 1 && m_stopOnOverflow) - { + } else if (m_numLines == m_maxLines - 1 && m_stopOnOverflow) { level = MessageLevel::Fatal; line = m_overflowMessage; } beginInsertRows(QModelIndex(), m_numLines, m_numLines); - m_numLines ++; + m_numLines++; m_content[lineNum].level = level; m_content[lineNum].line = line; endInsertRows(); @@ -86,9 +79,8 @@ QString LogModel::toPlainText() { QString out; out.reserve(m_numLines * 80); - for(int i = 0; i < m_numLines; i++) - { - QString & line = m_content[(m_firstLine + i) % m_maxLines].line; + for (int i = 0; i < m_numLines; i++) { + QString& line = m_content[(m_firstLine + i) % m_maxLines].line; out.append(line + '\n'); } out.squeeze(); @@ -98,13 +90,11 @@ QString LogModel::toPlainText() void LogModel::setMaxLines(int maxLines) { // no-op - if(maxLines == m_maxLines) - { + if (maxLines == m_maxLines) { return; } // if it all still fits in the buffer, just resize it - if(m_firstLine + m_numLines < m_maxLines) - { + if (m_firstLine + m_numLines < m_maxLines) { m_maxLines = maxLines; m_content.resize(maxLines); return; @@ -112,22 +102,17 @@ void LogModel::setMaxLines(int maxLines) // otherwise, we need to reorganize the data because it crosses the wrap boundary QVector newContent; newContent.resize(maxLines); - if(m_numLines <= maxLines) - { + if (m_numLines <= maxLines) { // if it all fits in the new buffer, just copy it over - for(int i = 0; i < m_numLines; i++) - { + for (int i = 0; i < m_numLines; i++) { newContent[i] = m_content[(m_firstLine + i) % m_maxLines]; } m_content.swap(newContent); - } - else - { + } else { // if it doesn't fit, part of the data needs to be thrown away (the oldest log messages) int lead = m_numLines - maxLines; beginRemoveRows(QModelIndex(), 0, lead - 1); - for(int i = 0; i < maxLines; i++) - { + for (int i = 0; i < maxLines; i++) { newContent[i] = m_content[(m_firstLine + lead + i) % m_maxLines]; } m_numLines = m_maxLines; @@ -155,8 +140,7 @@ void LogModel::setOverflowMessage(const QString& overflowMessage) void LogModel::setLineWrap(bool state) { - if(m_lineWrap != state) - { + if (m_lineWrap != state) { m_lineWrap = state; } } diff --git a/launcher/launch/LogModel.h b/launcher/launch/LogModel.h index 6aabc8236..ba2e4054a 100644 --- a/launcher/launch/LogModel.h +++ b/launcher/launch/LogModel.h @@ -4,14 +4,13 @@ #include #include "MessageLevel.h" -class LogModel : public QAbstractListModel -{ +class LogModel : public QAbstractListModel { Q_OBJECT -public: - explicit LogModel(QObject *parent = 0); + public: + explicit LogModel(QObject* parent = 0); - int rowCount(const QModelIndex &parent = QModelIndex()) const; - QVariant data(const QModelIndex &index, int role) const; + int rowCount(const QModelIndex& parent = QModelIndex()) const; + QVariant data(const QModelIndex& index, int role) const; void append(MessageLevel::Enum, QString line); void clear(); @@ -24,25 +23,21 @@ public: int getMaxLines(); void setMaxLines(int maxLines); void setStopOnOverflow(bool stop); - void setOverflowMessage(const QString & overflowMessage); + void setOverflowMessage(const QString& overflowMessage); void setLineWrap(bool state); bool wrapLines() const; - enum Roles - { - LevelRole = Qt::UserRole - }; + enum Roles { LevelRole = Qt::UserRole }; -private /* types */: - struct entry - { + private /* types */: + struct entry { MessageLevel::Enum level; QString line; }; -private: /* data */ - QVector m_content; + private: /* data */ + QVector m_content; int m_maxLines = 1000; // first line in the circular buffer int m_firstLine = 0; @@ -53,6 +48,6 @@ private: /* data */ bool m_suspended = false; bool m_lineWrap = true; -private: + private: Q_DISABLE_COPY(LogModel) }; diff --git a/launcher/launch/steps/CheckJava.cpp b/launcher/launch/steps/CheckJava.cpp index 7d697ba96..81337a88e 100644 --- a/launcher/launch/steps/CheckJava.cpp +++ b/launcher/launch/steps/CheckJava.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -34,12 +34,12 @@ */ #include "CheckJava.h" -#include "java/JavaUtils.h" -#include #include -#include -#include +#include #include +#include +#include +#include "java/JavaUtils.h" void CheckJava::executeTask() { @@ -49,32 +49,26 @@ void CheckJava::executeTask() bool perInstance = settings->get("OverrideJava").toBool() || settings->get("OverrideJavaLocation").toBool(); auto realJavaPath = QStandardPaths::findExecutable(m_javaPath); - if (realJavaPath.isEmpty()) - { - if (perInstance) - { - emit logLine( - QString("The java binary \"%1\" couldn't be found. Please fix the java path " - "override in the instance's settings or disable it.").arg(m_javaPath), - MessageLevel::Warning); - } - else - { + if (realJavaPath.isEmpty()) { + if (perInstance) { + emit logLine(QString("The java binary \"%1\" couldn't be found. Please fix the java path " + "override in the instance's settings or disable it.") + .arg(m_javaPath), + MessageLevel::Warning); + } else { emit logLine(QString("The java binary \"%1\" couldn't be found. Please set up java in " - "the settings.").arg(m_javaPath), + "the settings.") + .arg(m_javaPath), MessageLevel::Warning); } emitFailed(QString("Java path is not valid.")); return; - } - else - { + } else { emit logLine("Java path is:\n" + m_javaPath + "\n\n", MessageLevel::Launcher); } - if (JavaUtils::getJavaCheckPath().isEmpty()) - { - const char *reason = QT_TR_NOOP("Java checker library could not be found. Please check your installation."); + if (JavaUtils::getJavaCheckPath().isEmpty()) { + const char* reason = QT_TR_NOOP("Java checker library could not be found. Please check your installation."); emit logLine(tr(reason), MessageLevel::Fatal); emitFailed(tr(reason)); return; @@ -94,19 +88,15 @@ void CheckJava::executeTask() m_javaSignature = hash.result().toHex(); // if timestamps are not the same, or something is missing, check! - if (m_javaSignature != storedSignature || storedVersion.size() == 0 - || storedArchitecture.size() == 0 || storedRealArchitecture.size() == 0 - || storedVendor.size() == 0) - { + if (m_javaSignature != storedSignature || storedVersion.size() == 0 || storedArchitecture.size() == 0 || + storedRealArchitecture.size() == 0 || storedVendor.size() == 0) { m_JavaChecker.reset(new JavaChecker); emit logLine(QString("Checking Java version..."), MessageLevel::Launcher); connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished); m_JavaChecker->m_path = realJavaPath; m_JavaChecker->performCheck(); return; - } - else - { + } else { auto verString = instance->settings()->get("JavaVersion").toString(); auto archString = instance->settings()->get("JavaArchitecture").toString(); auto realArchString = settings->get("JavaRealArchitecture").toString(); @@ -118,10 +108,8 @@ void CheckJava::executeTask() void CheckJava::checkJavaFinished(JavaCheckResult result) { - switch (result.validity) - { - case JavaCheckResult::Validity::Errored: - { + switch (result.validity) { + case JavaCheckResult::Validity::Errored: { // 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); @@ -129,16 +117,14 @@ void CheckJava::checkJavaFinished(JavaCheckResult result) emitFailed(QString("Could not start java!")); return; } - case JavaCheckResult::Validity::ReturnedInvalidData: - { + case JavaCheckResult::Validity::ReturnedInvalidData: { emit logLine(QString("Java checker returned some invalid data we don't understand:"), MessageLevel::Error); emit logLines(result.outLog.split('\n'), MessageLevel::Warning); emit logLine("\nMinecraft might not start properly.", MessageLevel::Launcher); emitSucceeded(); return; } - case JavaCheckResult::Validity::Valid: - { + case JavaCheckResult::Validity::Valid: { auto instance = m_parent->instance(); printJavaInfo(result.javaVersion.toString(), result.mojangPlatform, result.realPlatform, result.javaVendor); instance->settings()->set("JavaVersion", result.javaVersion.toString()); @@ -152,8 +138,9 @@ void CheckJava::checkJavaFinished(JavaCheckResult result) } } -void CheckJava::printJavaInfo(const QString& version, const QString& architecture, const QString& realArchitecture, const QString & vendor) +void CheckJava::printJavaInfo(const QString& version, const QString& architecture, const QString& realArchitecture, const QString& vendor) { - emit logLine(QString("Java is version %1, using %2 (%3) architecture, from %4.\n\n") - .arg(version, architecture, realArchitecture, vendor), MessageLevel::Launcher); + emit logLine( + QString("Java is version %1, using %2 (%3) architecture, from %4.\n\n").arg(version, architecture, realArchitecture, vendor), + MessageLevel::Launcher); } diff --git a/launcher/launch/steps/CheckJava.h b/launcher/launch/steps/CheckJava.h index bbf06b7c7..4436e2a55 100644 --- a/launcher/launch/steps/CheckJava.h +++ b/launcher/launch/steps/CheckJava.h @@ -15,30 +15,26 @@ #pragma once -#include #include #include +#include -class CheckJava: public LaunchStep -{ +class CheckJava : public LaunchStep { Q_OBJECT -public: - explicit CheckJava(LaunchTask *parent) :LaunchStep(parent){}; - virtual ~CheckJava() {}; + public: + explicit CheckJava(LaunchTask* parent) : LaunchStep(parent){}; + virtual ~CheckJava(){}; virtual void executeTask(); - virtual bool canAbort() const - { - return false; - } -private slots: + virtual bool canAbort() const { return false; } + private slots: void checkJavaFinished(JavaCheckResult result); -private: - void printJavaInfo(const QString & version, const QString & architecture, const QString & realArchitecture, const QString & vendor); + private: + void printJavaInfo(const QString& version, const QString& architecture, const QString& realArchitecture, const QString& vendor); void printSystemInfo(bool javaIsKnown, bool javaIs64bit); -private: + private: QString m_javaPath; QString m_javaSignature; JavaCheckerPtr m_JavaChecker; diff --git a/launcher/launch/steps/LookupServerAddress.cpp b/launcher/launch/steps/LookupServerAddress.cpp index c7b8cea47..9bdac203b 100644 --- a/launcher/launch/steps/LookupServerAddress.cpp +++ b/launcher/launch/steps/LookupServerAddress.cpp @@ -13,20 +13,18 @@ * limitations under the License. */ - #include "LookupServerAddress.h" #include -LookupServerAddress::LookupServerAddress(LaunchTask *parent) : - LaunchStep(parent), m_dnsLookup(new QDnsLookup(this)) +LookupServerAddress::LookupServerAddress(LaunchTask* parent) : LaunchStep(parent), m_dnsLookup(new QDnsLookup(this)) { connect(m_dnsLookup, &QDnsLookup::finished, this, &LookupServerAddress::on_dnsLookupFinished); m_dnsLookup->setType(QDnsLookup::SRV); } -void LookupServerAddress::setLookupAddress(const QString &lookupAddress) +void LookupServerAddress::setLookupAddress(const QString& lookupAddress) { m_lookupAddress = lookupAddress; m_dnsLookup->setName(QString("_minecraft._tcp.%1").arg(lookupAddress)); @@ -51,41 +49,40 @@ void LookupServerAddress::executeTask() void LookupServerAddress::on_dnsLookupFinished() { - if (isFinished()) - { + if (isFinished()) { // Aborted return; } - if (m_dnsLookup->error() != QDnsLookup::NoError) - { + if (m_dnsLookup->error() != QDnsLookup::NoError) { emit logLine(QString("Failed to resolve server address (this is NOT an error!) %1: %2\n") - .arg(m_dnsLookup->name(), m_dnsLookup->errorString()), MessageLevel::Launcher); - resolve(m_lookupAddress, 25565); // Technically the task failed, however, we don't abort the launch - // and leave it up to minecraft to fail (or maybe not) when connecting + .arg(m_dnsLookup->name(), m_dnsLookup->errorString()), + MessageLevel::Launcher); + resolve(m_lookupAddress, 25565); // Technically the task failed, however, we don't abort the launch + // and leave it up to minecraft to fail (or maybe not) when connecting return; } const auto records = m_dnsLookup->serviceRecords(); - if (records.empty()) - { - emit logLine( - QString("Failed to resolve server address %1: the DNS lookup succeeded, but no records were returned.\n") - .arg(m_dnsLookup->name()), MessageLevel::Warning); - resolve(m_lookupAddress, 25565); // Technically the task failed, however, we don't abort the launch - // and leave it up to minecraft to fail (or maybe not) when connecting + if (records.empty()) { + emit logLine(QString("Failed to resolve server address %1: the DNS lookup succeeded, but no records were returned.\n") + .arg(m_dnsLookup->name()), + MessageLevel::Warning); + resolve(m_lookupAddress, 25565); // Technically the task failed, however, we don't abort the launch + // and leave it up to minecraft to fail (or maybe not) when connecting return; } - const auto &firstRecord = records.at(0); + const auto& firstRecord = records.at(0); quint16 port = firstRecord.port(); - emit logLine(QString("Resolved server address %1 to %2 with port %3\n").arg( - m_dnsLookup->name(), firstRecord.target(), QString::number(port)),MessageLevel::Launcher); + emit logLine( + QString("Resolved server address %1 to %2 with port %3\n").arg(m_dnsLookup->name(), firstRecord.target(), QString::number(port)), + MessageLevel::Launcher); resolve(firstRecord.target(), port); } -void LookupServerAddress::resolve(const QString &address, quint16 port) +void LookupServerAddress::resolve(const QString& address, quint16 port) { m_output->address = address; m_output->port = port; diff --git a/launcher/launch/steps/LookupServerAddress.h b/launcher/launch/steps/LookupServerAddress.h index 5a5c3de17..abd92a5e8 100644 --- a/launcher/launch/steps/LookupServerAddress.h +++ b/launcher/launch/steps/LookupServerAddress.h @@ -15,35 +15,32 @@ #pragma once -#include #include +#include #include #include "minecraft/launch/MinecraftServerTarget.h" -class LookupServerAddress: public LaunchStep { -Q_OBJECT -public: - explicit LookupServerAddress(LaunchTask *parent); - virtual ~LookupServerAddress() {}; +class LookupServerAddress : public LaunchStep { + Q_OBJECT + public: + explicit LookupServerAddress(LaunchTask* parent); + virtual ~LookupServerAddress(){}; virtual void executeTask(); virtual bool abort(); - virtual bool canAbort() const - { - return true; - } + virtual bool canAbort() const { return true; } - void setLookupAddress(const QString &lookupAddress); + void setLookupAddress(const QString& lookupAddress); void setOutputAddressPtr(MinecraftServerTargetPtr output); -private slots: + private slots: void on_dnsLookupFinished(); -private: - void resolve(const QString &address, quint16 port); + private: + void resolve(const QString& address, quint16 port); - QDnsLookup *m_dnsLookup; + QDnsLookup* m_dnsLookup; QString m_lookupAddress; MinecraftServerTargetPtr m_output; }; diff --git a/launcher/launch/steps/PostLaunchCommand.cpp b/launcher/launch/steps/PostLaunchCommand.cpp index ccf07f220..725101224 100644 --- a/launcher/launch/steps/PostLaunchCommand.cpp +++ b/launcher/launch/steps/PostLaunchCommand.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -36,7 +36,7 @@ #include "PostLaunchCommand.h" #include -PostLaunchCommand::PostLaunchCommand(LaunchTask *parent) : LaunchStep(parent) +PostLaunchCommand::PostLaunchCommand(LaunchTask* parent) : LaunchStep(parent) { auto instance = m_parent->instance(); m_command = instance->getPostExitCommand(); @@ -47,7 +47,7 @@ PostLaunchCommand::PostLaunchCommand(LaunchTask *parent) : LaunchStep(parent) void PostLaunchCommand::executeTask() { - //FIXME: where to put this? + // FIXME: where to put this? #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) auto args = QProcess::splitCommand(m_command); m_parent->substituteVariables(args); @@ -65,31 +65,22 @@ void PostLaunchCommand::executeTask() void PostLaunchCommand::on_state(LoggedProcess::State state) { - auto getError = [&]() - { - return tr("Post-Launch command failed with code %1.\n\n").arg(m_process.exitCode()); - }; - switch(state) - { + auto getError = [&]() { return tr("Post-Launch command failed with code %1.\n\n").arg(m_process.exitCode()); }; + switch (state) { case LoggedProcess::Aborted: case LoggedProcess::Crashed: - case LoggedProcess::FailedToStart: - { + case LoggedProcess::FailedToStart: { auto error = getError(); emit logLine(error, MessageLevel::Fatal); emitFailed(error); return; } - case LoggedProcess::Finished: - { - if(m_process.exitCode() != 0) - { + case LoggedProcess::Finished: { + if (m_process.exitCode() != 0) { auto error = getError(); emit logLine(error, MessageLevel::Fatal); emitFailed(error); - } - else - { + } else { emit logLine(tr("Post-Launch command ran successfully.\n\n"), MessageLevel::Launcher); emitSucceeded(); } @@ -99,7 +90,7 @@ void PostLaunchCommand::on_state(LoggedProcess::State state) } } -void PostLaunchCommand::setWorkingDirectory(const QString &wd) +void PostLaunchCommand::setWorkingDirectory(const QString& wd) { m_process.setWorkingDirectory(wd); } @@ -107,8 +98,7 @@ void PostLaunchCommand::setWorkingDirectory(const QString &wd) bool PostLaunchCommand::abort() { auto state = m_process.state(); - if (state == LoggedProcess::Running || state == LoggedProcess::Starting) - { + if (state == LoggedProcess::Running || state == LoggedProcess::Starting) { m_process.kill(); } return true; diff --git a/launcher/launch/steps/PostLaunchCommand.h b/launcher/launch/steps/PostLaunchCommand.h index ab4c494f5..578433b86 100644 --- a/launcher/launch/steps/PostLaunchCommand.h +++ b/launcher/launch/steps/PostLaunchCommand.h @@ -15,27 +15,23 @@ #pragma once -#include #include +#include -class PostLaunchCommand: public LaunchStep -{ +class PostLaunchCommand : public LaunchStep { Q_OBJECT -public: - explicit PostLaunchCommand(LaunchTask *parent); - virtual ~PostLaunchCommand() {}; + public: + explicit PostLaunchCommand(LaunchTask* parent); + virtual ~PostLaunchCommand(){}; virtual void executeTask(); virtual bool abort(); - virtual bool canAbort() const - { - return true; - } - void setWorkingDirectory(const QString &wd); -private slots: + virtual bool canAbort() const { return true; } + void setWorkingDirectory(const QString& wd); + private slots: void on_state(LoggedProcess::State state); -private: + private: LoggedProcess m_process; QString m_command; }; diff --git a/launcher/launch/steps/PreLaunchCommand.cpp b/launcher/launch/steps/PreLaunchCommand.cpp index 0b0392cbb..6d071a66e 100644 --- a/launcher/launch/steps/PreLaunchCommand.cpp +++ b/launcher/launch/steps/PreLaunchCommand.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -36,7 +36,7 @@ #include "PreLaunchCommand.h" #include -PreLaunchCommand::PreLaunchCommand(LaunchTask *parent) : LaunchStep(parent) +PreLaunchCommand::PreLaunchCommand(LaunchTask* parent) : LaunchStep(parent) { auto instance = m_parent->instance(); m_command = instance->getPreLaunchCommand(); @@ -47,7 +47,7 @@ PreLaunchCommand::PreLaunchCommand(LaunchTask *parent) : LaunchStep(parent) void PreLaunchCommand::executeTask() { - //FIXME: where to put this? + // FIXME: where to put this? #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) auto args = QProcess::splitCommand(m_command); m_parent->substituteVariables(args); @@ -65,31 +65,22 @@ void PreLaunchCommand::executeTask() void PreLaunchCommand::on_state(LoggedProcess::State state) { - auto getError = [&]() - { - return tr("Pre-Launch command failed with code %1.\n\n").arg(m_process.exitCode()); - }; - switch(state) - { + auto getError = [&]() { return tr("Pre-Launch command failed with code %1.\n\n").arg(m_process.exitCode()); }; + switch (state) { case LoggedProcess::Aborted: case LoggedProcess::Crashed: - case LoggedProcess::FailedToStart: - { + case LoggedProcess::FailedToStart: { auto error = getError(); emit logLine(error, MessageLevel::Fatal); emitFailed(error); return; } - case LoggedProcess::Finished: - { - if(m_process.exitCode() != 0) - { + case LoggedProcess::Finished: { + if (m_process.exitCode() != 0) { auto error = getError(); emit logLine(error, MessageLevel::Fatal); emitFailed(error); - } - else - { + } else { emit logLine(tr("Pre-Launch command ran successfully.\n\n"), MessageLevel::Launcher); emitSucceeded(); } @@ -99,7 +90,7 @@ void PreLaunchCommand::on_state(LoggedProcess::State state) } } -void PreLaunchCommand::setWorkingDirectory(const QString &wd) +void PreLaunchCommand::setWorkingDirectory(const QString& wd) { m_process.setWorkingDirectory(wd); } @@ -107,8 +98,7 @@ void PreLaunchCommand::setWorkingDirectory(const QString &wd) bool PreLaunchCommand::abort() { auto state = m_process.state(); - if (state == LoggedProcess::Running || state == LoggedProcess::Starting) - { + if (state == LoggedProcess::Running || state == LoggedProcess::Starting) { m_process.kill(); } return true; diff --git a/launcher/launch/steps/PreLaunchCommand.h b/launcher/launch/steps/PreLaunchCommand.h index dc069f71b..10568ea34 100644 --- a/launcher/launch/steps/PreLaunchCommand.h +++ b/launcher/launch/steps/PreLaunchCommand.h @@ -15,27 +15,23 @@ #pragma once -#include "launch/LaunchStep.h" #include "LoggedProcess.h" +#include "launch/LaunchStep.h" -class PreLaunchCommand: public LaunchStep -{ +class PreLaunchCommand : public LaunchStep { Q_OBJECT -public: - explicit PreLaunchCommand(LaunchTask *parent); - virtual ~PreLaunchCommand() {}; + public: + explicit PreLaunchCommand(LaunchTask* parent); + virtual ~PreLaunchCommand(){}; virtual void executeTask(); virtual bool abort(); - virtual bool canAbort() const - { - return true; - } - void setWorkingDirectory(const QString &wd); -private slots: + virtual bool canAbort() const { return true; } + void setWorkingDirectory(const QString& wd); + private slots: void on_state(LoggedProcess::State state); -private: + private: LoggedProcess m_process; QString m_command; }; diff --git a/launcher/launch/steps/QuitAfterGameStop.cpp b/launcher/launch/steps/QuitAfterGameStop.cpp index f9eced993..389560461 100644 --- a/launcher/launch/steps/QuitAfterGameStop.cpp +++ b/launcher/launch/steps/QuitAfterGameStop.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 dada513 * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/launch/steps/QuitAfterGameStop.h b/launcher/launch/steps/QuitAfterGameStop.h index 1ce14da95..9326b2a8c 100644 --- a/launcher/launch/steps/QuitAfterGameStop.h +++ b/launcher/launch/steps/QuitAfterGameStop.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 dada513 * * This program is free software: you can redistribute it and/or modify @@ -20,16 +20,12 @@ #include -class QuitAfterGameStop: public LaunchStep -{ +class QuitAfterGameStop : public LaunchStep { Q_OBJECT -public: - explicit QuitAfterGameStop(LaunchTask *parent) :LaunchStep(parent){}; - virtual ~QuitAfterGameStop() {}; + public: + explicit QuitAfterGameStop(LaunchTask* parent) : LaunchStep(parent){}; + virtual ~QuitAfterGameStop(){}; virtual void executeTask(); - virtual bool canAbort() const - { - return false; - } + virtual bool canAbort() const { return false; } }; diff --git a/launcher/launch/steps/TextPrint.cpp b/launcher/launch/steps/TextPrint.cpp index 0c1f320c3..0dec35b79 100644 --- a/launcher/launch/steps/TextPrint.cpp +++ b/launcher/launch/steps/TextPrint.cpp @@ -1,11 +1,11 @@ #include "TextPrint.h" -TextPrint::TextPrint(LaunchTask * parent, const QStringList &lines, MessageLevel::Enum level) : LaunchStep(parent) +TextPrint::TextPrint(LaunchTask* parent, const QStringList& lines, MessageLevel::Enum level) : LaunchStep(parent) { m_lines = lines; m_level = level; } -TextPrint::TextPrint(LaunchTask *parent, const QString &line, MessageLevel::Enum level) : LaunchStep(parent) +TextPrint::TextPrint(LaunchTask* parent, const QString& line, MessageLevel::Enum level) : LaunchStep(parent) { m_lines.append(line); m_level = level; diff --git a/launcher/launch/steps/TextPrint.h b/launcher/launch/steps/TextPrint.h index 36fa7f9a9..bd6c28567 100644 --- a/launcher/launch/steps/TextPrint.h +++ b/launcher/launch/steps/TextPrint.h @@ -15,27 +15,26 @@ #pragma once -#include #include #include +#include /* * FIXME: maybe do not export */ -class TextPrint: public LaunchStep -{ +class TextPrint : public LaunchStep { Q_OBJECT -public: - explicit TextPrint(LaunchTask *parent, const QStringList &lines, MessageLevel::Enum level); - explicit TextPrint(LaunchTask *parent, const QString &line, MessageLevel::Enum level); + public: + explicit TextPrint(LaunchTask* parent, const QStringList& lines, MessageLevel::Enum level); + explicit TextPrint(LaunchTask* parent, const QString& line, MessageLevel::Enum level); virtual ~TextPrint(){}; virtual void executeTask(); virtual bool canAbort() const; virtual bool abort(); -private: + private: QStringList m_lines; MessageLevel::Enum m_level; }; diff --git a/launcher/launch/steps/Update.cpp b/launcher/launch/steps/Update.cpp index 77c8a18ea..f23c0bb4b 100644 --- a/launcher/launch/steps/Update.cpp +++ b/launcher/launch/steps/Update.cpp @@ -18,17 +18,15 @@ void Update::executeTask() { - if(m_aborted) - { + if (m_aborted) { emitFailed(tr("Task aborted.")); return; } m_updateTask.reset(m_parent->instance()->createUpdateTask(m_mode)); - if(m_updateTask) - { + if (m_updateTask) { connect(m_updateTask.get(), &Task::finished, this, &Update::updateFinished); connect(m_updateTask.get(), &Task::progress, this, &Update::setProgress); - connect(m_updateTask.get(), &Task::stepProgress, this, &Update::propogateStepProgress); + connect(m_updateTask.get(), &Task::stepProgress, this, &Update::propagateStepProgress); connect(m_updateTask.get(), &Task::status, this, &Update::setStatus); connect(m_updateTask.get(), &Task::details, this, &Update::setDetails); emit progressReportingRequest(); @@ -44,13 +42,10 @@ void Update::proceed() void Update::updateFinished() { - if(m_updateTask->wasSuccessful()) - { + if (m_updateTask->wasSuccessful()) { m_updateTask.reset(); emitSucceeded(); - } - else - { + } else { QString reason = tr("Instance update failed because: %1\n\n").arg(m_updateTask->failReason()); m_updateTask.reset(); emit logLine(reason, MessageLevel::Fatal); @@ -60,21 +55,17 @@ void Update::updateFinished() bool Update::canAbort() const { - if(m_updateTask) - { + if (m_updateTask) { return m_updateTask->canAbort(); } return true; } - bool Update::abort() { m_aborted = true; - if(m_updateTask) - { - if(m_updateTask->canAbort()) - { + if (m_updateTask) { + if (m_updateTask->canAbort()) { return m_updateTask->abort(); } } diff --git a/launcher/launch/steps/Update.h b/launcher/launch/steps/Update.h index ce40611e0..9262cdbe4 100644 --- a/launcher/launch/steps/Update.h +++ b/launcher/launch/steps/Update.h @@ -15,30 +15,29 @@ #pragma once -#include -#include #include +#include #include +#include #include // FIXME: stupid. should be defined by the instance type? or even completely abstracted away... -class Update: public LaunchStep -{ +class Update : public LaunchStep { Q_OBJECT -public: - explicit Update(LaunchTask *parent, Net::Mode mode):LaunchStep(parent), m_mode(mode) {}; - virtual ~Update() {}; + public: + explicit Update(LaunchTask* parent, Net::Mode mode) : LaunchStep(parent), m_mode(mode){}; + virtual ~Update(){}; void executeTask() override; bool canAbort() const override; void proceed() override; -public slots: + public slots: bool abort() override; -private slots: + private slots: void updateFinished(); -private: + private: Task::Ptr m_updateTask; bool m_aborted = false; Net::Mode m_mode = Net::Mode::Offline; diff --git a/launcher/main.cpp b/launcher/main.cpp index b63f8bfd0..35f545f52 100644 --- a/launcher/main.cpp +++ b/launcher/main.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -40,15 +40,14 @@ // #define BREAK_RETURN #ifdef BREAK_INFINITE_LOOP -#include #include +#include #endif -int main(int argc, char *argv[]) +int main(int argc, char* argv[]) { #ifdef BREAK_INFINITE_LOOP - while(true) - { + while (true) { std::this_thread::sleep_for(std::chrono::milliseconds(250)); } #endif @@ -67,33 +66,31 @@ int main(int argc, char *argv[]) // initialize Qt Application app(argc, argv); - switch (app.status()) - { - case Application::StartingUp: - case Application::Initialized: - { - Q_INIT_RESOURCE(multimc); - Q_INIT_RESOURCE(backgrounds); - Q_INIT_RESOURCE(documents); - Q_INIT_RESOURCE(prismlauncher); + switch (app.status()) { + case Application::StartingUp: + case Application::Initialized: { + Q_INIT_RESOURCE(multimc); + Q_INIT_RESOURCE(backgrounds); + Q_INIT_RESOURCE(documents); + Q_INIT_RESOURCE(prismlauncher); - Q_INIT_RESOURCE(pe_dark); - Q_INIT_RESOURCE(pe_light); - Q_INIT_RESOURCE(pe_blue); - Q_INIT_RESOURCE(pe_colored); - Q_INIT_RESOURCE(breeze_dark); - Q_INIT_RESOURCE(breeze_light); - Q_INIT_RESOURCE(OSX); - Q_INIT_RESOURCE(iOS); - Q_INIT_RESOURCE(flat); - Q_INIT_RESOURCE(flat_white); - return app.exec(); - } - case Application::Failed: - return 1; - case Application::Succeeded: - return 0; - default: - return -1; + Q_INIT_RESOURCE(pe_dark); + Q_INIT_RESOURCE(pe_light); + Q_INIT_RESOURCE(pe_blue); + Q_INIT_RESOURCE(pe_colored); + Q_INIT_RESOURCE(breeze_dark); + Q_INIT_RESOURCE(breeze_light); + Q_INIT_RESOURCE(OSX); + Q_INIT_RESOURCE(iOS); + Q_INIT_RESOURCE(flat); + Q_INIT_RESOURCE(flat_white); + return app.exec(); + } + case Application::Failed: + return 1; + case Application::Succeeded: + return 0; + default: + return -1; } } diff --git a/launcher/meta/BaseEntity.cpp b/launcher/meta/BaseEntity.cpp index 97815eba8..5f9804e48 100644 --- a/launcher/meta/BaseEntity.cpp +++ b/launcher/meta/BaseEntity.cpp @@ -15,74 +15,55 @@ #include "BaseEntity.h" -#include "net/Download.h" +#include "Json.h" +#include "net/ApiDownload.h" #include "net/HttpMetaCache.h" #include "net/NetJob.h" -#include "Json.h" -#include "BuildConfig.h" #include "Application.h" +#include "BuildConfig.h" -class ParsingValidator : public Net::Validator -{ -public: /* con/des */ - ParsingValidator(Meta::BaseEntity *entity) : m_entity(entity) - { - }; - virtual ~ParsingValidator() - { - }; +class ParsingValidator : public Net::Validator { + public: /* con/des */ + ParsingValidator(Meta::BaseEntity* entity) : m_entity(entity){}; + virtual ~ParsingValidator(){}; -public: /* methods */ - bool init(QNetworkRequest &) override + public: /* methods */ + bool init(QNetworkRequest&) override { return true; } + bool write(QByteArray& data) override { + this->m_data.append(data); return true; } - bool write(QByteArray & data) override - { - this->data.append(data); - return true; - } - bool abort() override - { - return true; - } - bool validate(QNetworkReply &) override + bool abort() override { return true; } + bool validate(QNetworkReply&) override { auto fname = m_entity->localFilename(); - try - { - auto doc = Json::requireDocument(data, fname); + try { + auto doc = Json::requireDocument(m_data, fname); auto obj = Json::requireObject(doc, fname); m_entity->parse(obj); return true; - } - catch (const Exception &e) - { + } catch (const Exception& e) { qWarning() << "Unable to parse response:" << e.cause(); return false; } } -private: /* data */ - QByteArray data; - Meta::BaseEntity *m_entity; + private: /* data */ + QByteArray m_data; + Meta::BaseEntity* m_entity; }; -Meta::BaseEntity::~BaseEntity() -{ -} +Meta::BaseEntity::~BaseEntity() {} QUrl Meta::BaseEntity::url() const { auto s = APPLICATION->settings(); QString metaOverride = s->get("MetaURLOverride").toString(); - if(metaOverride.isEmpty()) - { + if (metaOverride.isEmpty()) { return QUrl(BuildConfig.META_URL).resolved(localFilename()); - } - else - { + } else { return QUrl(metaOverride).resolved(localFilename()); } } @@ -90,20 +71,16 @@ QUrl Meta::BaseEntity::url() const bool Meta::BaseEntity::loadLocalFile() { const QString fname = QDir("meta").absoluteFilePath(localFilename()); - if (!QFile::exists(fname)) - { + if (!QFile::exists(fname)) { return false; } // TODO: check if the file has the expected checksum - try - { + try { auto doc = Json::requireDocument(fname, fname); auto obj = Json::requireObject(doc, fname); parse(obj); return true; - } - catch (const Exception &e) - { + } catch (const Exception& e) { qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause()); // just make sure it's gone and we never consider it again. QFile::remove(fname); @@ -114,23 +91,20 @@ bool Meta::BaseEntity::loadLocalFile() void Meta::BaseEntity::load(Net::Mode loadType) { // load local file if nothing is loaded yet - if(!isLoaded()) - { - if(loadLocalFile()) - { + if (!isLoaded()) { + if (loadLocalFile()) { m_loadStatus = LoadStatus::Local; } } // if we need remote update, run the update task - if(loadType == Net::Mode::Offline || !shouldStartRemoteUpdate()) - { + if (loadType == Net::Mode::Offline || !shouldStartRemoteUpdate()) { return; } m_updateTask.reset(new NetJob(QObject::tr("Download of meta file %1").arg(localFilename()), APPLICATION->network())); auto url = this->url(); auto entry = APPLICATION->metacache()->resolveEntry("meta", localFilename()); entry->setStale(true); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); /* * The validator parses the file and loads it into the object. * If that fails, the file is not written to storage. @@ -138,14 +112,12 @@ void Meta::BaseEntity::load(Net::Mode loadType) dl->addValidator(new ParsingValidator(this)); m_updateTask->addNetAction(dl); m_updateStatus = UpdateStatus::InProgress; - QObject::connect(m_updateTask.get(), &NetJob::succeeded, [&]() - { + QObject::connect(m_updateTask.get(), &NetJob::succeeded, [&]() { m_loadStatus = LoadStatus::Remote; m_updateStatus = UpdateStatus::Succeeded; m_updateTask.reset(); }); - QObject::connect(m_updateTask.get(), &NetJob::failed, [&]() - { + QObject::connect(m_updateTask.get(), &NetJob::failed, [&]() { m_updateStatus = UpdateStatus::Failed; m_updateTask.reset(); }); @@ -165,8 +137,7 @@ bool Meta::BaseEntity::shouldStartRemoteUpdate() const Task::Ptr Meta::BaseEntity::getCurrentTask() { - if(m_updateStatus == UpdateStatus::InProgress) - { + if (m_updateStatus == UpdateStatus::InProgress) { return m_updateTask; } return nullptr; diff --git a/launcher/meta/BaseEntity.h b/launcher/meta/BaseEntity.h index 75fa384ad..1336a5217 100644 --- a/launcher/meta/BaseEntity.h +++ b/launcher/meta/BaseEntity.h @@ -22,30 +22,17 @@ #include "net/Mode.h" #include "net/NetJob.h" -namespace Meta -{ -class BaseEntity -{ -public: /* types */ +namespace Meta { +class BaseEntity { + public: /* types */ using Ptr = std::shared_ptr; - enum class LoadStatus - { - NotLoaded, - Local, - Remote - }; - enum class UpdateStatus - { - NotDone, - InProgress, - Failed, - Succeeded - }; + enum class LoadStatus { NotLoaded, Local, Remote }; + enum class UpdateStatus { NotDone, InProgress, Failed, Succeeded }; -public: + public: virtual ~BaseEntity(); - virtual void parse(const QJsonObject &obj) = 0; + virtual void parse(const QJsonObject& obj) = 0; virtual QString localFilename() const = 0; virtual QUrl url() const; @@ -56,12 +43,12 @@ public: void load(Net::Mode loadType); Task::Ptr getCurrentTask(); -protected: /* methods */ + protected: /* methods */ bool loadLocalFile(); -private: + private: LoadStatus m_loadStatus = LoadStatus::NotLoaded; UpdateStatus m_updateStatus = UpdateStatus::NotDone; NetJob::Ptr m_updateTask; }; -} +} // namespace Meta diff --git a/launcher/meta/Index.cpp b/launcher/meta/Index.cpp index 4dccccca8..657019f8a 100644 --- a/launcher/meta/Index.cpp +++ b/launcher/meta/Index.cpp @@ -15,84 +15,75 @@ #include "Index.h" -#include "VersionList.h" #include "JsonFormat.h" +#include "VersionList.h" -namespace Meta +namespace Meta { +Index::Index(QObject* parent) : QAbstractListModel(parent) {} +Index::Index(const QVector& lists, QObject* parent) : QAbstractListModel(parent), m_lists(lists) { -Index::Index(QObject *parent) - : QAbstractListModel(parent) -{ -} -Index::Index(const QVector &lists, QObject *parent) - : QAbstractListModel(parent), m_lists(lists) -{ - for (int i = 0; i < m_lists.size(); ++i) - { + for (int i = 0; i < m_lists.size(); ++i) { m_uids.insert(m_lists.at(i)->uid(), m_lists.at(i)); connectVersionList(i, m_lists.at(i)); } } -QVariant Index::data(const QModelIndex &index, int role) const +QVariant Index::data(const QModelIndex& index, int role) const { - if (index.parent().isValid() || index.row() < 0 || index.row() >= m_lists.size()) - { + if (index.parent().isValid() || index.row() < 0 || index.row() >= m_lists.size()) { return QVariant(); } VersionList::Ptr list = m_lists.at(index.row()); - switch (role) - { - case Qt::DisplayRole: - if (index.column() == 0) { - return list->humanReadable(); - } else { - break; - } - case UidRole: return list->uid(); - case NameRole: return list->name(); - case ListPtrRole: return QVariant::fromValue(list); + switch (role) { + case Qt::DisplayRole: + if (index.column() == 0) { + return list->humanReadable(); + } else { + break; + } + case UidRole: + return list->uid(); + case NameRole: + return list->name(); + case ListPtrRole: + return QVariant::fromValue(list); } return QVariant(); } -int Index::rowCount(const QModelIndex &parent) const +int Index::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_lists.size(); } -int Index::columnCount(const QModelIndex &parent) const +int Index::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : 1; } QVariant Index::headerData(int section, Qt::Orientation orientation, int role) const { - if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0) - { + if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0) { return tr("Name"); - } - else - { + } else { return QVariant(); } } -bool Index::hasUid(const QString &uid) const +bool Index::hasUid(const QString& uid) const { return m_uids.contains(uid); } -VersionList::Ptr Index::get(const QString &uid) +VersionList::Ptr Index::get(const QString& uid) { VersionList::Ptr out = m_uids.value(uid, nullptr); - if(!out) - { + if (!out) { out = std::make_shared(uid); m_uids[uid] = out; } return out; } -Version::Ptr Index::get(const QString &uid, const QString &version) +Version::Ptr Index::get(const QString& uid, const QString& version) { auto list = get(uid); return list->getVersion(version); @@ -103,31 +94,23 @@ void Index::parse(const QJsonObject& obj) parseIndex(obj, this); } -void Index::merge(const std::shared_ptr &other) +void Index::merge(const std::shared_ptr& other) { const QVector lists = std::dynamic_pointer_cast(other)->m_lists; // initial load, no need to merge - if (m_lists.isEmpty()) - { + if (m_lists.isEmpty()) { beginResetModel(); m_lists = lists; - for (int i = 0; i < lists.size(); ++i) - { + for (int i = 0; i < lists.size(); ++i) { m_uids.insert(lists.at(i)->uid(), lists.at(i)); connectVersionList(i, lists.at(i)); } endResetModel(); - } - else - { - for (const VersionList::Ptr &list : lists) - { - if (m_uids.contains(list->uid())) - { + } else { + for (const VersionList::Ptr& list : lists) { + if (m_uids.contains(list->uid())) { m_uids[list->uid()]->mergeFromIndex(list); - } - else - { + } else { beginInsertRows(QModelIndex(), m_lists.size(), m_lists.size()); connectVersionList(m_lists.size(), list); m_lists.append(list); @@ -138,11 +121,9 @@ void Index::merge(const std::shared_ptr &other) } } -void Index::connectVersionList(const int row, const VersionList::Ptr &list) +void Index::connectVersionList(const int row, const VersionList::Ptr& list) { - connect(list.get(), &VersionList::nameChanged, this, [this, row]() - { - emit dataChanged(index(row), index(row), QVector() << Qt::DisplayRole); - }); -} + connect(list.get(), &VersionList::nameChanged, this, + [this, row]() { emit dataChanged(index(row), index(row), QVector() << Qt::DisplayRole); }); } +} // namespace Meta diff --git a/launcher/meta/Index.h b/launcher/meta/Index.h index 06ea09dcf..41fdfcea9 100644 --- a/launcher/meta/Index.h +++ b/launcher/meta/Index.h @@ -23,46 +23,38 @@ class Task; -namespace Meta -{ +namespace Meta { -class Index : public QAbstractListModel, public BaseEntity -{ +class Index : public QAbstractListModel, public BaseEntity { Q_OBJECT -public: - explicit Index(QObject *parent = nullptr); - explicit Index(const QVector &lists, QObject *parent = nullptr); + public: + explicit Index(QObject* parent = nullptr); + explicit Index(const QVector& lists, QObject* parent = nullptr); - enum - { - UidRole = Qt::UserRole, - NameRole, - ListPtrRole - }; + enum { UidRole = Qt::UserRole, NameRole, ListPtrRole }; - QVariant data(const QModelIndex &index, int role) const override; - int rowCount(const QModelIndex &parent) const override; - int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex& index, int role) const override; + int rowCount(const QModelIndex& parent) const override; + int columnCount(const QModelIndex& parent) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QString localFilename() const override { return "index.json"; } // queries - VersionList::Ptr get(const QString &uid); - Version::Ptr get(const QString &uid, const QString &version); - bool hasUid(const QString &uid) const; + VersionList::Ptr get(const QString& uid); + Version::Ptr get(const QString& uid, const QString& version); + bool hasUid(const QString& uid) const; QVector lists() const { return m_lists; } -public: // for usage by parsers only - void merge(const std::shared_ptr &other); - void parse(const QJsonObject &obj) override; + public: // for usage by parsers only + void merge(const std::shared_ptr& other); + void parse(const QJsonObject& obj) override; -private: + private: QVector m_lists; QHash m_uids; - void connectVersionList(const int row, const VersionList::Ptr &list); + void connectVersionList(const int row, const VersionList::Ptr& list); }; -} - +} // namespace Meta diff --git a/launcher/meta/JsonFormat.cpp b/launcher/meta/JsonFormat.cpp index cb2d06ea0..6c993f720 100644 --- a/launcher/meta/JsonFormat.cpp +++ b/launcher/meta/JsonFormat.cpp @@ -16,8 +16,8 @@ #include "JsonFormat.h" // FIXME: remove this from here... somehow -#include "minecraft/OneSixVersionFormat.h" #include "Json.h" +#include "minecraft/OneSixVersionFormat.h" #include "Index.h" #include "Version.h" @@ -25,8 +25,7 @@ using namespace Json; -namespace Meta -{ +namespace Meta { MetadataVersion currentFormatVersion() { @@ -34,13 +33,12 @@ MetadataVersion currentFormatVersion() } // Index -static std::shared_ptr parseIndexInternal(const QJsonObject &obj) +static std::shared_ptr parseIndexInternal(const QJsonObject& obj) { const QVector objects = requireIsArrayOf(obj, "packages"); QVector lists; lists.reserve(objects.size()); - std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject &obj) - { + std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject& obj) { VersionList::Ptr list = std::make_shared(requireString(obj, "uid")); list->setName(ensureString(obj, "name", QString())); return list; @@ -49,7 +47,7 @@ static std::shared_ptr parseIndexInternal(const QJsonObject &obj) } // Version -static Version::Ptr parseCommonVersion(const QString &uid, const QJsonObject &obj) +static Version::Ptr parseCommonVersion(const QString& uid, const QJsonObject& obj) { Version::Ptr version = std::make_shared(uid, requireString(obj, "version")); version->setTime(QDateTime::fromString(requireString(obj, "releaseTime"), Qt::ISODate).toMSecsSinceEpoch() / 1000); @@ -63,26 +61,24 @@ static Version::Ptr parseCommonVersion(const QString &uid, const QJsonObject &ob return version; } -static Version::Ptr parseVersionInternal(const QJsonObject &obj) +static Version::Ptr parseVersionInternal(const QJsonObject& obj) { Version::Ptr version = parseCommonVersion(requireString(obj, "uid"), obj); - version->setData(OneSixVersionFormat::versionFileFromJson(QJsonDocument(obj), - QString("%1/%2.json").arg(version->uid(), version->version()), - obj.contains("order"))); + version->setData(OneSixVersionFormat::versionFileFromJson( + QJsonDocument(obj), QString("%1/%2.json").arg(version->uid(), version->version()), obj.contains("order"))); return version; } // Version list / package -static VersionList::Ptr parseVersionListInternal(const QJsonObject &obj) +static VersionList::Ptr parseVersionListInternal(const QJsonObject& obj) { const QString uid = requireString(obj, "uid"); const QVector versionsRaw = requireIsArrayOf(obj, "versions"); QVector versions; versions.reserve(versionsRaw.size()); - std::transform(versionsRaw.begin(), versionsRaw.end(), std::back_inserter(versions), [uid](const QJsonObject &vObj) - { + std::transform(versionsRaw.begin(), versionsRaw.end(), std::back_inserter(versions), [uid](const QJsonObject& vObj) { auto version = parseCommonVersion(uid, vObj); version->setProvidesRecommendations(); return version; @@ -94,23 +90,18 @@ static VersionList::Ptr parseVersionListInternal(const QJsonObject &obj) return list; } - -MetadataVersion parseFormatVersion(const QJsonObject &obj, bool required) +MetadataVersion parseFormatVersion(const QJsonObject& obj, bool required) { - if (!obj.contains("formatVersion")) - { - if(required) - { + if (!obj.contains("formatVersion")) { + if (required) { return MetadataVersion::Invalid; } return MetadataVersion::InitialRelease; } - if (!obj.value("formatVersion").isDouble()) - { + if (!obj.value("formatVersion").isDouble()) { return MetadataVersion::Invalid; } - switch(obj.value("formatVersion").toInt()) - { + switch (obj.value("formatVersion").toInt()) { case 0: case 1: return MetadataVersion::InitialRelease; @@ -121,49 +112,45 @@ MetadataVersion parseFormatVersion(const QJsonObject &obj, bool required) void serializeFormatVersion(QJsonObject& obj, Meta::MetadataVersion version) { - if(version == MetadataVersion::Invalid) - { + if (version == MetadataVersion::Invalid) { return; } obj.insert("formatVersion", int(version)); } -void parseIndex(const QJsonObject &obj, Index *ptr) +void parseIndex(const QJsonObject& obj, Index* ptr) { const MetadataVersion version = parseFormatVersion(obj); - switch (version) - { - case MetadataVersion::InitialRelease: - ptr->merge(parseIndexInternal(obj)); - break; - case MetadataVersion::Invalid: - throw ParseException(QObject::tr("Unknown format version!")); + switch (version) { + case MetadataVersion::InitialRelease: + ptr->merge(parseIndexInternal(obj)); + break; + case MetadataVersion::Invalid: + throw ParseException(QObject::tr("Unknown format version!")); } } -void parseVersionList(const QJsonObject &obj, VersionList *ptr) +void parseVersionList(const QJsonObject& obj, VersionList* ptr) { const MetadataVersion version = parseFormatVersion(obj); - switch (version) - { - case MetadataVersion::InitialRelease: - ptr->merge(parseVersionListInternal(obj)); - break; - case MetadataVersion::Invalid: - throw ParseException(QObject::tr("Unknown format version!")); + switch (version) { + case MetadataVersion::InitialRelease: + ptr->merge(parseVersionListInternal(obj)); + break; + case MetadataVersion::Invalid: + throw ParseException(QObject::tr("Unknown format version!")); } } -void parseVersion(const QJsonObject &obj, Version *ptr) +void parseVersion(const QJsonObject& obj, Version* ptr) { const MetadataVersion version = parseFormatVersion(obj); - switch (version) - { - case MetadataVersion::InitialRelease: - ptr->merge(parseVersionInternal(obj)); - break; - case MetadataVersion::Invalid: - throw ParseException(QObject::tr("Unknown format version!")); + switch (version) { + case MetadataVersion::InitialRelease: + ptr->merge(parseVersionInternal(obj)); + break; + case MetadataVersion::Invalid: + throw ParseException(QObject::tr("Unknown format version!")); } } @@ -172,40 +159,34 @@ void parseVersion(const QJsonObject &obj, Version *ptr) {"uid":"foo", "equals":"version"} ] */ -void parseRequires(const QJsonObject& obj, RequireSet* ptr, const char * keyName) +void parseRequires(const QJsonObject& obj, RequireSet* ptr, const char* keyName) { - if(obj.contains(keyName)) - { + if (obj.contains(keyName)) { auto reqArray = requireArray(obj, keyName); auto iter = reqArray.begin(); - while(iter != reqArray.end()) - { + while (iter != reqArray.end()) { auto reqObject = requireObject(*iter); auto uid = requireString(reqObject, "uid"); auto equals = ensureString(reqObject, "equals", QString()); auto suggests = ensureString(reqObject, "suggests", QString()); - ptr->insert({uid, equals, suggests}); + ptr->insert({ uid, equals, suggests }); iter++; } } } -void serializeRequires(QJsonObject& obj, RequireSet* ptr, const char * keyName) +void serializeRequires(QJsonObject& obj, RequireSet* ptr, const char* keyName) { - if(!ptr || ptr->empty()) - { + if (!ptr || ptr->empty()) { return; } QJsonArray arrOut; - for(auto &iter: *ptr) - { + for (auto& iter : *ptr) { QJsonObject reqOut; reqOut.insert("uid", iter.uid); - if(!iter.equalsVersion.isEmpty()) - { + if (!iter.equalsVersion.isEmpty()) { reqOut.insert("equals", iter.equalsVersion); } - if(!iter.suggests.isEmpty()) - { + if (!iter.suggests.isEmpty()) { reqOut.insert("suggests", iter.suggests); } arrOut.append(reqOut); @@ -213,5 +194,4 @@ void serializeRequires(QJsonObject& obj, RequireSet* ptr, const char * keyName) obj.insert(keyName, arrOut); } -} - +} // namespace Meta diff --git a/launcher/meta/JsonFormat.h b/launcher/meta/JsonFormat.h index 63128a4e6..d474bcc39 100644 --- a/launcher/meta/JsonFormat.h +++ b/launcher/meta/JsonFormat.h @@ -18,43 +18,25 @@ #include #include +#include #include "Exception.h" #include "meta/BaseEntity.h" -#include -namespace Meta -{ +namespace Meta { class Index; class Version; class VersionList; -enum class MetadataVersion -{ - Invalid = -1, - InitialRelease = 1 -}; +enum class MetadataVersion { Invalid = -1, InitialRelease = 1 }; -class ParseException : public Exception -{ -public: +class ParseException : public Exception { + public: using Exception::Exception; }; -struct Require -{ - bool operator==(const Require & rhs) const - { - return uid == rhs.uid; - } - bool operator<(const Require & rhs) const - { - return uid < rhs.uid; - } - bool deepEquals(const Require & rhs) const - { - return uid == rhs.uid - && equalsVersion == rhs.equalsVersion - && suggests == rhs.suggests; - } +struct Require { + bool operator==(const Require& rhs) const { return uid == rhs.uid; } + bool operator<(const Require& rhs) const { return uid < rhs.uid; } + bool deepEquals(const Require& rhs) const { return uid == rhs.uid && equalsVersion == rhs.equalsVersion && suggests == rhs.suggests; } QString uid; QString equalsVersion; QString suggests; @@ -62,17 +44,17 @@ struct Require using RequireSet = std::set; -void parseIndex(const QJsonObject &obj, Index *ptr); -void parseVersion(const QJsonObject &obj, Version *ptr); -void parseVersionList(const QJsonObject &obj, VersionList *ptr); +void parseIndex(const QJsonObject& obj, Index* ptr); +void parseVersion(const QJsonObject& obj, Version* ptr); +void parseVersionList(const QJsonObject& obj, VersionList* ptr); -MetadataVersion parseFormatVersion(const QJsonObject &obj, bool required = true); -void serializeFormatVersion(QJsonObject &obj, MetadataVersion version); +MetadataVersion parseFormatVersion(const QJsonObject& obj, bool required = true); +void serializeFormatVersion(QJsonObject& obj, MetadataVersion version); // FIXME: this has a different shape than the others...FIX IT!? -void parseRequires(const QJsonObject &obj, RequireSet * ptr, const char * keyName = "requires"); -void serializeRequires(QJsonObject & objOut, RequireSet* ptr, const char * keyName = "requires"); +void parseRequires(const QJsonObject& obj, RequireSet* ptr, const char* keyName = "requires"); +void serializeRequires(QJsonObject& objOut, RequireSet* ptr, const char* keyName = "requires"); MetadataVersion currentFormatVersion(); -} +} // namespace Meta Q_DECLARE_METATYPE(std::set) diff --git a/launcher/meta/Version.cpp b/launcher/meta/Version.cpp index 0718a4204..655a20b93 100644 --- a/launcher/meta/Version.cpp +++ b/launcher/meta/Version.cpp @@ -20,14 +20,9 @@ #include "JsonFormat.h" #include "minecraft/PackProfile.h" -Meta::Version::Version(const QString &uid, const QString &version) - : BaseVersion(), m_uid(uid), m_version(version) -{ -} +Meta::Version::Version(const QString& uid, const QString& version) : BaseVersion(), m_uid(uid), m_version(version) {} -Meta::Version::~Version() -{ -} +Meta::Version::~Version() {} QString Meta::Version::descriptor() { @@ -35,7 +30,7 @@ QString Meta::Version::descriptor() } QString Meta::Version::name() { - if(m_data) + if (m_data) return m_data->name; return m_uid; } @@ -56,40 +51,32 @@ void Meta::Version::parse(const QJsonObject& obj) void Meta::Version::mergeFromList(const Meta::Version::Ptr& other) { - if(other->m_providesRecommendations) - { - if(m_recommended != other->m_recommended) - { + if (other->m_providesRecommendations) { + if (m_recommended != other->m_recommended) { setRecommended(other->m_recommended); } } - if (m_type != other->m_type) - { + if (m_type != other->m_type) { setType(other->m_type); } - if (m_time != other->m_time) - { + if (m_time != other->m_time) { setTime(other->m_time); } - if (m_requires != other->m_requires) - { + if (m_requires != other->m_requires) { m_requires = other->m_requires; } - if (m_conflicts != other->m_conflicts) - { + if (m_conflicts != other->m_conflicts) { m_conflicts = other->m_conflicts; } - if(m_volatile != other->m_volatile) - { + if (m_volatile != other->m_volatile) { setVolatile(other->m_volatile); } } -void Meta::Version::merge(const Version::Ptr &other) +void Meta::Version::merge(const Version::Ptr& other) { mergeFromList(other); - if(other->m_data) - { + if (other->m_data) { setData(other->m_data); } } @@ -104,7 +91,7 @@ QString Meta::Version::localFilename() const return { const_cast(this)->descriptor() }; } -void Meta::Version::setType(const QString &type) +void Meta::Version::setType(const QString& type) { m_type = type; emit typeChanged(); @@ -116,7 +103,7 @@ void Meta::Version::setTime(const qint64 time) emit timeChanged(); } -void Meta::Version::setRequires(const Meta::RequireSet &reqs, const Meta::RequireSet &conflicts) +void Meta::Version::setRequires(const Meta::RequireSet& reqs, const Meta::RequireSet& conflicts) { m_requires = reqs; m_conflicts = conflicts; @@ -128,8 +115,7 @@ void Meta::Version::setVolatile(bool volatile_) m_volatile = volatile_; } - -void Meta::Version::setData(const VersionFilePtr &data) +void Meta::Version::setData(const VersionFilePtr& data) { m_data = data; } diff --git a/launcher/meta/Version.h b/launcher/meta/Version.h index 59a96a68b..07dcafb01 100644 --- a/launcher/meta/Version.h +++ b/launcher/meta/Version.h @@ -15,8 +15,8 @@ #pragma once -#include "BaseVersion.h" #include "../Version.h" +#include "BaseVersion.h" #include #include @@ -29,80 +29,54 @@ #include "JsonFormat.h" -namespace Meta -{ +namespace Meta { -class Version : public QObject, public BaseVersion, public BaseEntity -{ +class Version : public QObject, public BaseVersion, public BaseEntity { Q_OBJECT -public: + public: using Ptr = std::shared_ptr; - explicit Version(const QString &uid, const QString &version); + explicit Version(const QString& uid, const QString& version); virtual ~Version(); QString descriptor() override; QString name() override; QString typeString() const override; - QString uid() const - { - return m_uid; - } - QString version() const - { - return m_version; - } - QString type() const - { - return m_type; - } + QString uid() const { return m_uid; } + QString version() const { return m_version; } + QString type() const { return m_type; } QDateTime time() const; - qint64 rawTime() const - { - return m_time; - } - const Meta::RequireSet &requiredSet() const - { - return m_requires; - } - VersionFilePtr data() const - { - return m_data; - } - bool isRecommended() const - { - return m_recommended; - } - bool isLoaded() const - { - return m_data != nullptr; - } + qint64 rawTime() const { return m_time; } + const Meta::RequireSet& requiredSet() const { return m_requires; } + VersionFilePtr data() const { return m_data; } + bool isRecommended() const { return m_recommended; } + bool isLoaded() const { return m_data != nullptr; } - void merge(const Version::Ptr &other); - void mergeFromList(const Version::Ptr &other); - void parse(const QJsonObject &obj) override; + void merge(const Version::Ptr& other); + void mergeFromList(const Version::Ptr& other); + void parse(const QJsonObject& obj) override; QString localFilename() const override; [[nodiscard]] ::Version toComparableVersion() const; -public: // for usage by format parsers only - void setType(const QString &type); + public: // for usage by format parsers only + void setType(const QString& type); void setTime(const qint64 time); - void setRequires(const Meta::RequireSet &reqs, const Meta::RequireSet &conflicts); + void setRequires(const Meta::RequireSet& reqs, const Meta::RequireSet& conflicts); void setVolatile(bool volatile_); void setRecommended(bool recommended); void setProvidesRecommendations(); - void setData(const VersionFilePtr &data); + void setData(const VersionFilePtr& data); -signals: + signals: void typeChanged(); void timeChanged(); void requiresChanged(); -private: + private: bool m_providesRecommendations = false; bool m_recommended = false; QString m_name; @@ -115,6 +89,6 @@ private: bool m_volatile = false; VersionFilePtr m_data; }; -} +} // namespace Meta Q_DECLARE_METATYPE(Meta::Version::Ptr) diff --git a/launcher/meta/VersionList.cpp b/launcher/meta/VersionList.cpp index 9f4482784..7b7ae1fa3 100644 --- a/launcher/meta/VersionList.cpp +++ b/launcher/meta/VersionList.cpp @@ -17,14 +17,11 @@ #include -#include "Version.h" #include "JsonFormat.h" #include "Version.h" -namespace Meta -{ -VersionList::VersionList(const QString &uid, QObject *parent) - : BaseVersionList(parent), m_uid(uid) +namespace Meta { +VersionList::VersionList(const QString& uid, QObject* parent) : BaseVersionList(parent), m_uid(uid) { setObjectName("Version list: " + uid); } @@ -52,61 +49,60 @@ int VersionList::count() const void VersionList::sortVersions() { beginResetModel(); - std::sort(m_versions.begin(), m_versions.end(), [](const Version::Ptr &a, const Version::Ptr &b) - { - return *a.get() < *b.get(); - }); + std::sort(m_versions.begin(), m_versions.end(), [](const Version::Ptr& a, const Version::Ptr& b) { return *a.get() < *b.get(); }); endResetModel(); } -QVariant VersionList::data(const QModelIndex &index, int role) const +QVariant VersionList::data(const QModelIndex& index, int role) const { - if (!index.isValid() || index.row() < 0 || index.row() >= m_versions.size() || index.parent().isValid()) - { + if (!index.isValid() || index.row() < 0 || index.row() >= m_versions.size() || index.parent().isValid()) { return QVariant(); } Version::Ptr version = m_versions.at(index.row()); - switch (role) - { - case VersionPointerRole: return QVariant::fromValue(std::dynamic_pointer_cast(version)); - case VersionRole: - case VersionIdRole: - return version->version(); - case ParentVersionRole: - { - // FIXME: HACK: this should be generic and be replaced by something else. Anything that is a hard 'equals' dep is a 'parent uid'. - auto & reqs = version->requiredSet(); - auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Require & req) - { - return req.uid == "net.minecraft"; - }); - if (iter != reqs.end()) - { - return (*iter).equalsVersion; + switch (role) { + case VersionPointerRole: + return QVariant::fromValue(std::dynamic_pointer_cast(version)); + case VersionRole: + case VersionIdRole: + return version->version(); + case ParentVersionRole: { + // FIXME: HACK: this should be generic and be replaced by something else. Anything that is a hard 'equals' dep is a 'parent + // uid'. + auto& reqs = version->requiredSet(); + auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Require& req) { return req.uid == "net.minecraft"; }); + if (iter != reqs.end()) { + return (*iter).equalsVersion; + } + return QVariant(); } - return QVariant(); - } - case TypeRole: return version->type(); + case TypeRole: + return version->type(); - case UidRole: return version->uid(); - case TimeRole: return version->time(); - case RequiresRole: return QVariant::fromValue(version->requiredSet()); - case SortRole: return version->rawTime(); - case VersionPtrRole: return QVariant::fromValue(version); - case RecommendedRole: return version->isRecommended(); - // FIXME: this should be determined in whatever view/proxy is used... - // case LatestRole: return version == getLatestStable(); - default: return QVariant(); + case UidRole: + return version->uid(); + case TimeRole: + return version->time(); + case RequiresRole: + return QVariant::fromValue(version->requiredSet()); + case SortRole: + return version->rawTime(); + case VersionPtrRole: + return QVariant::fromValue(version); + case RecommendedRole: + return version->isRecommended(); + // FIXME: this should be determined in whatever view/proxy is used... + // case LatestRole: return version == getLatestStable(); + default: + return QVariant(); } } BaseVersionList::RoleList VersionList::providesRoles() const { - return {VersionPointerRole, VersionRole, VersionIdRole, ParentVersionRole, - TypeRole, UidRole, TimeRole, RequiresRole, SortRole, - RecommendedRole, LatestRole, VersionPtrRole}; + return { VersionPointerRole, VersionRole, VersionIdRole, ParentVersionRole, TypeRole, UidRole, + TimeRole, RequiresRole, SortRole, RecommendedRole, LatestRole, VersionPtrRole }; } QHash VersionList::roleNames() const @@ -129,11 +125,10 @@ QString VersionList::humanReadable() const return m_name.isEmpty() ? m_uid : m_name; } -Version::Ptr VersionList::getVersion(const QString &version) +Version::Ptr VersionList::getVersion(const QString& version) { Version::Ptr out = m_lookup.value(version, nullptr); - if(!out) - { + if (!out) { out = std::make_shared(m_uid, version); m_lookup[version] = out; } @@ -142,33 +137,31 @@ Version::Ptr VersionList::getVersion(const QString &version) bool VersionList::hasVersion(QString version) const { - auto ver = std::find_if(m_versions.constBegin(), m_versions.constEnd(), - [&](Meta::Version::Ptr const& a){ return a->version() == version; }); + auto ver = + std::find_if(m_versions.constBegin(), m_versions.constEnd(), [&](Meta::Version::Ptr const& a) { return a->version() == version; }); return (ver != m_versions.constEnd()); } -void VersionList::setName(const QString &name) +void VersionList::setName(const QString& name) { m_name = name; emit nameChanged(name); } -void VersionList::setVersions(const QVector &versions) +void VersionList::setVersions(const QVector& versions) { beginResetModel(); m_versions = versions; - std::sort(m_versions.begin(), m_versions.end(), [](const Version::Ptr &a, const Version::Ptr &b) - { - return a->rawTime() > b->rawTime(); - }); - for (int i = 0; i < m_versions.size(); ++i) - { + std::sort(m_versions.begin(), m_versions.end(), + [](const Version::Ptr& a, const Version::Ptr& b) { return a->rawTime() > b->rawTime(); }); + for (int i = 0; i < m_versions.size(); ++i) { m_lookup.insert(m_versions.at(i)->version(), m_versions.at(i)); setupAddedVersion(i, m_versions.at(i)); } // FIXME: this is dumb, we have 'recommended' as part of the metadata already... - auto recommendedIt = std::find_if(m_versions.constBegin(), m_versions.constEnd(), [](const Version::Ptr &ptr) { return ptr->type() == "release"; }); + auto recommendedIt = + std::find_if(m_versions.constBegin(), m_versions.constEnd(), [](const Version::Ptr& ptr) { return ptr->type() == "release"; }); m_recommended = recommendedIt == m_versions.constEnd() ? nullptr : *recommendedIt; endResetModel(); } @@ -179,14 +172,13 @@ void VersionList::parse(const QJsonObject& obj) } // FIXME: this is dumb, we have 'recommended' as part of the metadata already... -static const Meta::Version::Ptr &getBetterVersion(const Meta::Version::Ptr &a, const Meta::Version::Ptr &b) +static const Meta::Version::Ptr& getBetterVersion(const Meta::Version::Ptr& a, const Meta::Version::Ptr& b) { - if(!a) + if (!a) return b; - if(!b) + if (!b) return a; - if(a->type() == b->type()) - { + if (a->type() == b->type()) { // newer of same type wins return (a->rawTime() > b->rawTime() ? a : b); } @@ -194,37 +186,30 @@ static const Meta::Version::Ptr &getBetterVersion(const Meta::Version::Ptr &a, c return (a->type() == "release" ? a : b); } -void VersionList::mergeFromIndex(const VersionList::Ptr &other) +void VersionList::mergeFromIndex(const VersionList::Ptr& other) { - if (m_name != other->m_name) - { + if (m_name != other->m_name) { setName(other->m_name); } } -void VersionList::merge(const VersionList::Ptr &other) +void VersionList::merge(const VersionList::Ptr& other) { - if (m_name != other->m_name) - { + if (m_name != other->m_name) { setName(other->m_name); } // TODO: do not reset the whole model. maybe? beginResetModel(); m_versions.clear(); - if(other->m_versions.isEmpty()) - { + if (other->m_versions.isEmpty()) { qWarning() << "Empty list loaded ..."; } - for (const Version::Ptr &version : other->m_versions) - { + for (const Version::Ptr& version : other->m_versions) { // we already have the version. merge the contents - if (m_lookup.contains(version->version())) - { + if (m_lookup.contains(version->version())) { m_lookup.value(version->version())->mergeFromList(version); - } - else - { + } else { m_lookup.insert(version->uid(), version); } // connect it. @@ -235,13 +220,16 @@ void VersionList::merge(const VersionList::Ptr &other) endResetModel(); } -void VersionList::setupAddedVersion(const int row, const Version::Ptr &version) +void VersionList::setupAddedVersion(const int row, const Version::Ptr& version) { // FIXME: do not disconnect from everythin, disconnect only the lambdas here version->disconnect(); - connect(version.get(), &Version::requiresChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector() << RequiresRole); }); - connect(version.get(), &Version::timeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector() << TimeRole << SortRole); }); - connect(version.get(), &Version::typeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector() << TypeRole); }); + connect(version.get(), &Version::requiresChanged, this, + [this, row]() { emit dataChanged(index(row), index(row), QVector() << RequiresRole); }); + connect(version.get(), &Version::timeChanged, this, + [this, row]() { emit dataChanged(index(row), index(row), QVector() << TimeRole << SortRole); }); + connect(version.get(), &Version::typeChanged, this, + [this, row]() { emit dataChanged(index(row), index(row), QVector() << TypeRole); }); } BaseVersion::Ptr VersionList::getRecommended() const @@ -249,4 +237,4 @@ BaseVersion::Ptr VersionList::getRecommended() const return m_recommended; } -} +} // namespace Meta diff --git a/launcher/meta/VersionList.h b/launcher/meta/VersionList.h index a4d5603d9..5e587f204 100644 --- a/launcher/meta/VersionList.h +++ b/launcher/meta/VersionList.h @@ -15,33 +15,25 @@ #pragma once -#include "BaseEntity.h" -#include "BaseVersionList.h" #include #include +#include "BaseEntity.h" +#include "BaseVersionList.h" #include "meta/Version.h" -namespace Meta -{ +namespace Meta { -class VersionList : public BaseVersionList, public BaseEntity -{ +class VersionList : public BaseVersionList, public BaseEntity { Q_OBJECT Q_PROPERTY(QString uid READ uid CONSTANT) Q_PROPERTY(QString name READ name NOTIFY nameChanged) -public: - explicit VersionList(const QString &uid, QObject *parent = nullptr); + public: + explicit VersionList(const QString& uid, QObject* parent = nullptr); using Ptr = std::shared_ptr; - enum Roles - { - UidRole = Qt::UserRole + 100, - TimeRole, - RequiresRole, - VersionPtrRole - }; + enum Roles { UidRole = Qt::UserRole + 100, TimeRole, RequiresRole, VersionPtrRole }; Task::Ptr getLoadTask() override; bool isLoaded() override; @@ -51,46 +43,35 @@ public: BaseVersion::Ptr getRecommended() const override; - QVariant data(const QModelIndex &index, int role) const override; + QVariant data(const QModelIndex& index, int role) const override; RoleList providesRoles() const override; QHash roleNames() const override; QString localFilename() const override; - QString uid() const - { - return m_uid; - } - QString name() const - { - return m_name; - } + QString uid() const { return m_uid; } + QString name() const { return m_name; } QString humanReadable() const; - Version::Ptr getVersion(const QString &version); + Version::Ptr getVersion(const QString& version); bool hasVersion(QString version) const; - QVector versions() const - { - return m_versions; - } + QVector versions() const { return m_versions; } -public: // for usage only by parsers - void setName(const QString &name); - void setVersions(const QVector &versions); - void merge(const VersionList::Ptr &other); - void mergeFromIndex(const VersionList::Ptr &other); - void parse(const QJsonObject &obj) override; + public: // for usage only by parsers + void setName(const QString& name); + void setVersions(const QVector& versions); + void merge(const VersionList::Ptr& other); + void mergeFromIndex(const VersionList::Ptr& other); + void parse(const QJsonObject& obj) override; -signals: - void nameChanged(const QString &name); + signals: + void nameChanged(const QString& name); -protected slots: - void updateListData(QList) override - { - } + protected slots: + void updateListData(QList) override {} -private: + private: QVector m_versions; QHash m_lookup; QString m_uid; @@ -98,7 +79,7 @@ private: Version::Ptr m_recommended; - void setupAddedVersion(const int row, const Version::Ptr &version); + void setupAddedVersion(const int row, const Version::Ptr& version); }; -} +} // namespace Meta Q_DECLARE_METATYPE(Meta::VersionList::Ptr) diff --git a/launcher/minecraft/Agent.h b/launcher/minecraft/Agent.h index 374e6e94e..8958521e5 100644 --- a/launcher/minecraft/Agent.h +++ b/launcher/minecraft/Agent.h @@ -9,28 +9,21 @@ class Agent; typedef std::shared_ptr AgentPtr; class Agent { -public: - Agent(LibraryPtr library, const QString &argument) + public: + Agent(LibraryPtr library, const QString& argument) { m_library = library; m_argument = argument; } -public: /* methods */ - - LibraryPtr library() { - return m_library; - } - QString argument() { - return m_argument; - } - -protected: /* data */ + public: /* methods */ + LibraryPtr library() { return m_library; } + QString argument() { return m_argument; } + protected: /* data */ /// The library pointing to the jar this Java agent is contained within LibraryPtr m_library; /// The argument to the Java agent, passed after an = if present QString m_argument; - }; diff --git a/launcher/minecraft/AssetsUtils.cpp b/launcher/minecraft/AssetsUtils.cpp index 16fdfdb1c..4ef007075 100644 --- a/launcher/minecraft/AssetsUtils.cpp +++ b/launcher/minecraft/AssetsUtils.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify @@ -33,21 +33,21 @@ * limitations under the License. */ -#include +#include +#include #include #include -#include -#include +#include #include #include +#include #include -#include #include "AssetsUtils.h" -#include "FileSystem.h" -#include "net/Download.h" -#include "net/ChecksumValidator.h" #include "BuildConfig.h" +#include "FileSystem.h" +#include "net/ApiDownload.h" +#include "net/ChecksumValidator.h" #include "Application.h" @@ -56,37 +56,32 @@ QSet collectPathsFromDir(QString dirPath) { QFileInfo dirInfo(dirPath); - if (!dirInfo.exists()) - { + if (!dirInfo.exists()) { return {}; } QSet out; QDirIterator iter(dirPath, QDirIterator::Subdirectories); - while (iter.hasNext()) - { + while (iter.hasNext()) { QString value = iter.next(); QFileInfo info(value); - if(info.isFile()) - { + if (info.isFile()) { out.insert(value); qDebug() << value; } } return out; } -} +} // namespace - -namespace AssetsUtils -{ +namespace AssetsUtils { /* * Returns true on success, with index populated * index is undefined otherwise */ -bool loadAssetsIndexJson(const QString &assetsId, const QString &path, AssetsIndex& index) +bool loadAssetsIndexJson(const QString& assetsId, const QString& path, AssetsIndex& index) { /* { @@ -105,8 +100,7 @@ bool loadAssetsIndexJson(const QString &assetsId, const QString &path, AssetsInd // Try to open the file and fail if we can't. // TODO: We should probably report this error to the user. - if (!file.open(QIODevice::ReadOnly)) - { + if (!file.open(QIODevice::ReadOnly)) { qCritical() << "Failed to read assets index file" << path; return false; } @@ -120,16 +114,14 @@ bool loadAssetsIndexJson(const QString &assetsId, const QString &path, AssetsInd QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError); // Fail if the JSON is invalid. - if (parseError.error != QJsonParseError::NoError) - { - qCritical() << "Failed to parse assets index file:" << parseError.errorString() - << "at offset " << QString::number(parseError.offset); + if (parseError.error != QJsonParseError::NoError) { + qCritical() << "Failed to parse assets index file:" << parseError.errorString() << "at offset " + << QString::number(parseError.offset); return false; } // Make sure the root is an object. - if (!jsonDoc.isObject()) - { + if (!jsonDoc.isObject()) { qCritical() << "Invalid assets index JSON: Root should be an array."; return false; } @@ -137,22 +129,19 @@ bool loadAssetsIndexJson(const QString &assetsId, const QString &path, AssetsInd QJsonObject root = jsonDoc.object(); QJsonValue isVirtual = root.value("virtual"); - if (!isVirtual.isUndefined()) - { + if (!isVirtual.isUndefined()) { index.isVirtual = isVirtual.toBool(false); } QJsonValue mapToResources = root.value("map_to_resources"); - if (!mapToResources.isUndefined()) - { + if (!mapToResources.isUndefined()) { index.mapToResources = mapToResources.toBool(false); } QJsonValue objects = root.value("objects"); QVariantMap map = objects.toVariant().toMap(); - for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) - { + for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) { // qDebug() << iter.key(); QVariant variant = iter.value(); @@ -160,19 +149,14 @@ bool loadAssetsIndexJson(const QString &assetsId, const QString &path, AssetsInd AssetObject object; - for (QVariantMap::const_iterator nested_iter = nested_objects.begin(); - nested_iter != nested_objects.end(); ++nested_iter) - { + for (QVariantMap::const_iterator nested_iter = nested_objects.begin(); nested_iter != nested_objects.end(); ++nested_iter) { // qDebug() << nested_iter.key() << nested_iter.value().toString(); QString key = nested_iter.key(); QVariant value = nested_iter.value(); - if (key == "hash") - { + if (key == "hash") { object.hash = value.toString(); - } - else if (key == "size") - { + } else if (key == "size") { object.size = value.toDouble(); } } @@ -184,7 +168,7 @@ bool loadAssetsIndexJson(const QString &assetsId, const QString &path, AssetsInd } // FIXME: ugly code duplication -QDir getAssetsDir(const QString &assetsId, const QString &resourcesFolder) +QDir getAssetsDir(const QString& assetsId, const QString& resourcesFolder) { QDir assetsDir = QDir("assets/"); QDir indexDir = QDir(FS::PathCombine(assetsDir.path(), "indexes")); @@ -195,26 +179,21 @@ QDir getAssetsDir(const QString &assetsId, const QString &resourcesFolder) QFile indexFile(indexPath); QDir virtualRoot(FS::PathCombine(virtualDir.path(), assetsId)); - if (!indexFile.exists()) - { + if (!indexFile.exists()) { qCritical() << "No assets index file" << indexPath << "; can't determine assets path!"; return virtualRoot; } AssetsIndex index; - if(!AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, index)) - { + if (!AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, index)) { qCritical() << "Failed to load asset index file" << indexPath << "; can't determine assets path!"; return virtualRoot; } QString targetPath; - if(index.isVirtual) - { + if (index.isVirtual) { return virtualRoot; - } - else if(index.mapToResources) - { + } else if (index.mapToResources) { return QDir(resourcesFolder); } return virtualRoot; @@ -232,8 +211,7 @@ bool reconstructAssets(QString assetsId, QString resourcesFolder) QFile indexFile(indexPath); QDir virtualRoot(FS::PathCombine(virtualDir.path(), assetsId)); - if (!indexFile.exists()) - { + if (!indexFile.exists()) { qCritical() << "No assets index file" << indexPath << "; can't reconstruct assets!"; return false; } @@ -241,31 +219,25 @@ bool reconstructAssets(QString assetsId, QString resourcesFolder) qDebug() << "reconstructAssets" << assetsDir.path() << indexDir.path() << objectDir.path() << virtualDir.path() << virtualRoot.path(); AssetsIndex index; - if(!AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, index)) - { + if (!AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, index)) { qCritical() << "Failed to load asset index file" << indexPath << "; can't reconstruct assets!"; return false; } QString targetPath; bool removeLeftovers = false; - if(index.isVirtual) - { + if (index.isVirtual) { targetPath = virtualRoot.path(); removeLeftovers = true; qDebug() << "Reconstructing virtual assets folder at" << targetPath; - } - else if(index.mapToResources) - { + } else if (index.mapToResources) { targetPath = resourcesFolder; qDebug() << "Reconstructing resources folder at" << targetPath; } - if (!targetPath.isNull()) - { + if (!targetPath.isNull()) { auto presentFiles = collectPathsFromDir(targetPath); - for (QString map : index.objects.keys()) - { + for (QString map : index.objects.keys()) { AssetObject asset_object = index.objects.value(map); QString target_path = FS::PathCombine(targetPath, map); QFile target(target_path); @@ -279,8 +251,7 @@ bool reconstructAssets(QString assetsId, QString resourcesFolder) presentFiles.remove(target_path); - if (!target.exists()) - { + if (!target.exists()) { QFileInfo info(target_path); QDir target_dir = info.dir(); @@ -293,10 +264,8 @@ bool reconstructAssets(QString assetsId, QString resourcesFolder) } // TODO: Write last used time to virtualRoot/.lastused - if(removeLeftovers) - { - for(auto & file: presentFiles) - { + if (removeLeftovers) { + for (auto& file : presentFiles) { qDebug() << "Would remove" << file; } } @@ -304,16 +273,14 @@ bool reconstructAssets(QString assetsId, QString resourcesFolder) return true; } -} +} // namespace AssetsUtils NetAction::Ptr AssetObject::getDownloadAction() { QFileInfo objectFile(getLocalPath()); - if ((!objectFile.isFile()) || (objectFile.size() != size)) - { - auto objectDL = Net::Download::makeFile(getUrl(), objectFile.filePath()); - if(hash.size()) - { + if ((!objectFile.isFile()) || (objectFile.size() != size)) { + auto objectDL = Net::ApiDownload::makeFile(getUrl(), objectFile.filePath()); + if (hash.size()) { auto rawHash = QByteArray::fromHex(hash.toLatin1()); objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash)); } @@ -341,15 +308,13 @@ QString AssetObject::getRelPath() NetJob::Ptr AssetsIndex::getDownloadJob() { auto job = makeShared(QObject::tr("Assets for %1").arg(id), APPLICATION->network()); - for (auto &object : objects.values()) - { + for (auto& object : objects.values()) { auto dl = object.getDownloadAction(); - if(dl) - { + if (dl) { job->addNetAction(dl); } } - if(job->size()) + if (job->size()) return job; return nullptr; } diff --git a/launcher/minecraft/AssetsUtils.h b/launcher/minecraft/AssetsUtils.h index 3dbf19ed6..87956e57a 100644 --- a/launcher/minecraft/AssetsUtils.h +++ b/launcher/minecraft/AssetsUtils.h @@ -15,13 +15,12 @@ #pragma once -#include #include +#include #include "net/NetAction.h" #include "net/NetJob.h" -struct AssetObject -{ +struct AssetObject { QString getRelPath(); QUrl getUrl(); QString getLocalPath(); @@ -31,8 +30,7 @@ struct AssetObject qint64 size; }; -struct AssetsIndex -{ +struct AssetsIndex { NetJob::Ptr getDownloadJob(); QString id; @@ -42,12 +40,11 @@ struct AssetsIndex }; /// FIXME: this is absolutely horrendous. REDO!!!! -namespace AssetsUtils -{ -bool loadAssetsIndexJson(const QString &id, const QString &file, AssetsIndex& index); +namespace AssetsUtils { +bool loadAssetsIndexJson(const QString& id, const QString& file, AssetsIndex& index); -QDir getAssetsDir(const QString &assetsId, const QString &resourcesFolder); +QDir getAssetsDir(const QString& assetsId, const QString& resourcesFolder); /// Reconstruct a virtual assets folder for the given assets ID and return the folder bool reconstructAssets(QString assetsId, QString resourcesFolder); -} +} // namespace AssetsUtils diff --git a/launcher/minecraft/Component.cpp b/launcher/minecraft/Component.cpp index ff81fcbb8..79ea7a06d 100644 --- a/launcher/minecraft/Component.cpp +++ b/launcher/minecraft/Component.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -33,22 +33,22 @@ * limitations under the License. */ -#include -#include #include "Component.h" +#include +#include #include -#include "meta/Version.h" -#include "VersionFile.h" -#include "minecraft/PackProfile.h" +#include "Application.h" #include "FileSystem.h" #include "OneSixVersionFormat.h" -#include "Application.h" +#include "VersionFile.h" +#include "meta/Version.h" +#include "minecraft/PackProfile.h" #include -Component::Component(PackProfile * parent, const QString& uid) +Component::Component(PackProfile* parent, const QString& uid) { assert(parent); m_parent = parent; @@ -56,7 +56,7 @@ Component::Component(PackProfile * parent, const QString& uid) m_uid = uid; } -Component::Component(PackProfile * parent, std::shared_ptr version) +Component::Component(PackProfile* parent, std::shared_ptr version) { assert(parent); m_parent = parent; @@ -68,7 +68,7 @@ Component::Component(PackProfile * parent, std::shared_ptr versio m_loaded = version->isLoaded(); } -Component::Component(PackProfile * parent, const QString& uid, std::shared_ptr file) +Component::Component(PackProfile* parent, const QString& uid, std::shared_ptr file) { assert(parent); m_parent = parent; @@ -88,33 +88,25 @@ std::shared_ptr Component::getMeta() void Component::applyTo(LaunchProfile* profile) { // do not apply disabled components - if(!isEnabled()) - { + if (!isEnabled()) { return; } auto vfile = getVersionFile(); - if(vfile) - { + if (vfile) { vfile->applyTo(profile, m_parent->runtimeContext()); - } - else - { + } else { profile->applyProblemSeverity(getProblemSeverity()); } } std::shared_ptr Component::getVersionFile() const { - if(m_metaVersion) - { - if(!m_metaVersion->isLoaded()) - { + if (m_metaVersion) { + if (!m_metaVersion->isLoaded()) { m_metaVersion->load(Net::Mode::Online); } return m_metaVersion->data(); - } - else - { + } else { return m_file; } } @@ -122,8 +114,7 @@ std::shared_ptr Component::getVersionFile() const std::shared_ptr Component::getVersionList() const { // FIXME: what if the metadata index isn't loaded yet? - if(APPLICATION->metadataIndex()->hasUid(m_uid)) - { + if (APPLICATION->metadataIndex()->hasUid(m_uid)) { return APPLICATION->metadataIndex()->get(m_uid); } return nullptr; @@ -131,12 +122,11 @@ std::shared_ptr Component::getVersionList() const int Component::getOrder() { - if(m_orderOverride) + if (m_orderOverride) return m_order; auto vfile = getVersionFile(); - if(vfile) - { + if (vfile) { return vfile->order; } return 0; @@ -166,13 +156,11 @@ QString Component::getFilename() } QDateTime Component::getReleaseDateTime() { - if(m_metaVersion) - { + if (m_metaVersion) { return m_metaVersion->time(); } auto vfile = getVersionFile(); - if(vfile) - { + if (vfile) { return vfile->releaseTime; } // FIXME: fake @@ -192,12 +180,10 @@ bool Component::canBeDisabled() bool Component::setEnabled(bool state) { bool intendedDisabled = !state; - if (!canBeDisabled()) - { + if (!canBeDisabled()) { intendedDisabled = false; } - if(intendedDisabled != m_disabled) - { + if (intendedDisabled != m_disabled) { m_disabled = intendedDisabled; emit dataChanged(); return true; @@ -212,10 +198,8 @@ bool Component::isCustom() bool Component::isCustomizable() { - if(m_metaVersion) - { - if(getVersionFile()) - { + if (m_metaVersion) { + if (getVersionFile()) { return true; } } @@ -227,10 +211,8 @@ bool Component::isRemovable() } bool Component::isRevertible() { - if (isCustom()) - { - if(APPLICATION->metadataIndex()->hasUid(m_uid)) - { + if (isCustom()) { + if (APPLICATION->metadataIndex()->hasUid(m_uid)) { return true; } } @@ -244,10 +226,8 @@ bool Component::isMoveable() bool Component::isVersionChangeable() { auto list = getVersionList(); - if(list) - { - if(!list->isLoaded()) - { + if (list) { + if (!list->isLoaded()) { list->load(Net::Mode::Online); } return list->count() != 0; @@ -257,8 +237,7 @@ bool Component::isVersionChangeable() void Component::setImportant(bool state) { - if(m_important != state) - { + if (m_important != state) { m_important = state; emit dataChanged(); } @@ -267,8 +246,7 @@ void Component::setImportant(bool state) ProblemSeverity Component::getProblemSeverity() const { auto file = getVersionFile(); - if(file) - { + if (file) { return file->getProblemSeverity(); } return ProblemSeverity::Error; @@ -277,49 +255,38 @@ ProblemSeverity Component::getProblemSeverity() const const QList Component::getProblems() const { auto file = getVersionFile(); - if(file) - { + if (file) { return file->getProblems(); } - return {{ProblemSeverity::Error, QObject::tr("Patch is not loaded yet.")}}; + return { { ProblemSeverity::Error, QObject::tr("Patch is not loaded yet.") } }; } void Component::setVersion(const QString& version) { - if(version == m_version) - { + if (version == m_version) { return; } m_version = version; - if(m_loaded) - { + if (m_loaded) { // we are loaded and potentially have state to invalidate - if(m_file) - { + if (m_file) { // we have a file... explicit version has been changed and there is nothing else to do. - } - else - { + } else { // we don't have a file, therefore we are loaded with metadata m_cachedVersion = version; // see if the meta version is loaded auto metaVersion = APPLICATION->metadataIndex()->get(m_uid, version); - if(metaVersion->isLoaded()) - { + if (metaVersion->isLoaded()) { // if yes, we can continue with that. m_metaVersion = metaVersion; - } - else - { + } else { // if not, we need loading m_metaVersion.reset(); m_loaded = false; } updateCachedData(); } - } - else - { + } else { // not loaded... assume it will be sorted out later by the update task } emit dataChanged(); @@ -327,41 +294,33 @@ void Component::setVersion(const QString& version) bool Component::customize() { - if(isCustom()) - { + if (isCustom()) { return false; } auto filename = getFilename(); - if(!FS::ensureFilePathExists(filename)) - { + if (!FS::ensureFilePathExists(filename)) { return false; } // FIXME: get rid of this try-catch. - try - { + try { QSaveFile jsonFile(filename); - if(!jsonFile.open(QIODevice::WriteOnly)) - { + if (!jsonFile.open(QIODevice::WriteOnly)) { return false; } auto vfile = getVersionFile(); - if(!vfile) - { + if (!vfile) { return false; } auto document = OneSixVersionFormat::versionFileToJson(vfile); jsonFile.write(document.toJson()); - if(!jsonFile.commit()) - { + if (!jsonFile.commit()) { return false; } m_file = vfile; m_metaVersion.reset(); emit dataChanged(); - } - catch (const Exception &error) - { + } catch (const Exception& error) { qWarning() << "Version could not be loaded:" << error.cause(); } return true; @@ -369,31 +328,25 @@ bool Component::customize() bool Component::revert() { - if(!isCustom()) - { + if (!isCustom()) { // already not custom return true; } auto filename = getFilename(); bool result = true; // just kill the file and reload - if(QFile::exists(filename)) - { + if (QFile::exists(filename)) { result = QFile::remove(filename); } - if(result) - { + if (result) { // file gone... m_file.reset(); // check local cache for metadata... auto version = APPLICATION->metadataIndex()->get(m_uid, m_version); - if(version->isLoaded()) - { + if (version->isLoaded()) { m_metaVersion = version; - } - else - { + } else { m_metaVersion.reset(); m_loaded = false; } @@ -407,23 +360,19 @@ bool Component::revert() * By default, only uids are compared for set operations. * This compares all fields of the Require structs in the sets. */ -static bool deepCompare(const std::set & a, const std::set & b) +static bool deepCompare(const std::set& a, const std::set& b) { // NOTE: this needs to be rewritten if the type of Meta::RequireSet changes - if(a.size() != b.size()) - { + if (a.size() != b.size()) { return false; } - for(const auto & reqA :a) - { - const auto &iter2 = b.find(reqA); - if(iter2 == b.cend()) - { + for (const auto& reqA : a) { + const auto& iter2 = b.find(reqA); + if (iter2 == b.cend()) { return false; } - const auto & reqB = *iter2; - if(!reqA.deepEquals(reqB)) - { + const auto& reqB = *iter2; + if (!reqA.deepEquals(reqB)) { return false; } } @@ -433,41 +382,32 @@ static bool deepCompare(const std::set & a, const std::setname) - { + if (m_cachedName != file->name) { m_cachedName = file->name; changed = true; } - if(m_cachedVersion != file->version) - { + if (m_cachedVersion != file->version) { m_cachedVersion = file->version; changed = true; } - if(m_cachedVolatile != file->m_volatile) - { + if (m_cachedVolatile != file->m_volatile) { m_cachedVolatile = file->m_volatile; changed = true; } - if(!deepCompare(m_cachedRequires, file->m_requires)) - { + if (!deepCompare(m_cachedRequires, file->m_requires)) { m_cachedRequires = file->m_requires; changed = true; } - if(!deepCompare(m_cachedConflicts, file->conflicts)) - { + if (!deepCompare(m_cachedConflicts, file->conflicts)) { m_cachedConflicts = file->conflicts; changed = true; } - if(changed) - { + if (changed) { emit dataChanged(); } - } - else - { + } else { // in case we removed all the metadata m_cachedRequires.clear(); m_cachedConflicts.clear(); diff --git a/launcher/minecraft/Component.h b/launcher/minecraft/Component.h index ef7c99470..3474a22e0 100644 --- a/launcher/minecraft/Component.h +++ b/launcher/minecraft/Component.h @@ -1,37 +1,36 @@ #pragma once -#include -#include -#include #include -#include "meta/JsonFormat.h" +#include +#include +#include #include "ProblemProvider.h" #include "QObjectPtr.h" +#include "meta/JsonFormat.h" class PackProfile; class LaunchProfile; -namespace Meta -{ - class Version; - class VersionList; -} +namespace Meta { +class Version; +class VersionList; +} // namespace Meta class VersionFile; -class Component : public QObject, public ProblemProvider -{ -Q_OBJECT -public: - Component(PackProfile * parent, const QString &uid); +class Component : public QObject, public ProblemProvider { + Q_OBJECT + public: + Component(PackProfile* parent, const QString& uid); // DEPRECATED: remove these constructors? - Component(PackProfile * parent, std::shared_ptr version); - Component(PackProfile * parent, const QString & uid, std::shared_ptr file); + Component(PackProfile* parent, std::shared_ptr version); + Component(PackProfile* parent, const QString& uid, std::shared_ptr file); - virtual ~Component(){}; - void applyTo(LaunchProfile *profile); + virtual ~Component() {} + + void applyTo(LaunchProfile* profile); bool isEnabled(); - bool setEnabled (bool state); + bool setEnabled(bool state); bool canBeDisabled(); bool isMoveable(); @@ -56,23 +55,22 @@ public: std::shared_ptr getVersionFile() const; std::shared_ptr getVersionList() const; - void setImportant (bool state); - + void setImportant(bool state); const QList getProblems() const override; ProblemSeverity getProblemSeverity() const override; - void setVersion(const QString & version); + void setVersion(const QString& version); bool customize(); bool revert(); void updateCachedData(); -signals: + signals: void dataChanged(); -public: /* data */ - PackProfile * m_parent; + public: /* data */ + PackProfile* m_parent; // BEGIN: persistent component list properties /// ID of the component diff --git a/launcher/minecraft/ComponentUpdateTask.cpp b/launcher/minecraft/ComponentUpdateTask.cpp index d55bc17f2..0b85b81aa 100644 --- a/launcher/minecraft/ComponentUpdateTask.cpp +++ b/launcher/minecraft/ComponentUpdateTask.cpp @@ -1,16 +1,16 @@ #include "ComponentUpdateTask.h" -#include "PackProfile_p.h" -#include "PackProfile.h" #include "Component.h" -#include "meta/Index.h" -#include "meta/VersionList.h" -#include "meta/Version.h" #include "ComponentUpdateTask_p.h" -#include "cassert" -#include "Version.h" -#include "net/Mode.h" #include "OneSixVersionFormat.h" +#include "PackProfile.h" +#include "PackProfile_p.h" +#include "Version.h" +#include "cassert" +#include "meta/Index.h" +#include "meta/Version.h" +#include "meta/VersionList.h" +#include "net/Mode.h" #include "Application.h" @@ -32,8 +32,7 @@ * If the component list changes, start over. */ -ComponentUpdateTask::ComponentUpdateTask(Mode mode, Net::Mode netmode, PackProfile* list, QObject* parent) - : Task(parent) +ComponentUpdateTask::ComponentUpdateTask(Mode mode, Net::Mode netmode, PackProfile* list, QObject* parent) : Task(parent) { d.reset(new ComponentUpdateTaskData); d->m_list = list; @@ -41,9 +40,7 @@ ComponentUpdateTask::ComponentUpdateTask(Mode mode, Net::Mode netmode, PackProfi d->netmode = netmode; } -ComponentUpdateTask::~ComponentUpdateTask() -{ -} +ComponentUpdateTask::~ComponentUpdateTask() {} void ComponentUpdateTask::executeTask() { @@ -51,19 +48,12 @@ void ComponentUpdateTask::executeTask() loadComponents(); } -namespace -{ -enum class LoadResult -{ - LoadedLocal, - RequiresRemote, - Failed -}; +namespace { +enum class LoadResult { LoadedLocal, RequiresRemote, Failed }; LoadResult composeLoadResult(LoadResult a, LoadResult b) { - if (a < b) - { + if (a < b) { return b; } return a; @@ -71,28 +61,24 @@ LoadResult composeLoadResult(LoadResult a, LoadResult b) static LoadResult loadComponent(ComponentPtr component, Task::Ptr& loadTask, Net::Mode netmode) { - if(component->m_loaded) - { + if (component->m_loaded) { qDebug() << component->getName() << "is already loaded"; return LoadResult::LoadedLocal; } LoadResult result = LoadResult::Failed; auto customPatchFilename = component->getFilename(); - if(QFile::exists(customPatchFilename)) - { + if (QFile::exists(customPatchFilename)) { // if local file exists... // check for uid problems inside... bool fileChanged = false; auto file = ProfileUtils::parseJsonFile(QFileInfo(customPatchFilename), false); - if(file->uid != component->m_uid) - { + if (file->uid != component->m_uid) { file->uid = component->m_uid; fileChanged = true; } - if(fileChanged) - { + if (fileChanged) { // FIXME: @QUALITY do not ignore return value ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), customPatchFilename); } @@ -100,21 +86,16 @@ static LoadResult loadComponent(ComponentPtr component, Task::Ptr& loadTask, Net component->m_file = file; component->m_loaded = true; result = LoadResult::LoadedLocal; - } - else - { + } else { auto metaVersion = APPLICATION->metadataIndex()->get(component->m_uid, component->m_version); component->m_metaVersion = metaVersion; - if(metaVersion->isLoaded()) - { + if (metaVersion->isLoaded()) { component->m_loaded = true; result = LoadResult::LoadedLocal; - } - else - { + } else { metaVersion->load(netmode); loadTask = metaVersion->getCurrentTask(); - if(loadTask) + if (loadTask) result = LoadResult::RequiresRemote; else if (metaVersion->isLoaded()) result = LoadResult::LoadedLocal; @@ -155,21 +136,19 @@ static LoadResult loadPackProfile(ComponentPtr component, Task::Ptr& loadTask, N static LoadResult loadIndex(Task::Ptr& loadTask, Net::Mode netmode) { // FIXME: DECIDE. do we want to run the update task anyway? - if(APPLICATION->metadataIndex()->isLoaded()) - { + if (APPLICATION->metadataIndex()->isLoaded()) { qDebug() << "Index is already loaded"; return LoadResult::LoadedLocal; } APPLICATION->metadataIndex()->load(netmode); loadTask = APPLICATION->metadataIndex()->getCurrentTask(); - if(loadTask) - { + if (loadTask) { return LoadResult::RequiresRemote; } // FIXME: this is assuming the load succeeded... did it really? return LoadResult::LoadedLocal; } -} +} // namespace void ComponentUpdateTask::loadComponents() { @@ -183,34 +162,24 @@ void ComponentUpdateTask::loadComponents() Task::Ptr indexLoadTask; auto singleResult = loadIndex(indexLoadTask, d->netmode); result = composeLoadResult(result, singleResult); - if(indexLoadTask) - { + if (indexLoadTask) { qDebug() << "Remote loading is being run for metadata index"; RemoteLoadStatus status; status.type = RemoteLoadStatus::Type::Index; d->remoteLoadStatusList.append(status); - connect(indexLoadTask.get(), &Task::succeeded, [=]() - { - remoteLoadSucceeded(taskIndex); - }); - connect(indexLoadTask.get(), &Task::failed, [=](const QString & error) - { - remoteLoadFailed(taskIndex, error); - }); - connect(indexLoadTask.get(), &Task::aborted, [=]() - { - remoteLoadFailed(taskIndex, tr("Aborted")); - }); + connect(indexLoadTask.get(), &Task::succeeded, [=]() { remoteLoadSucceeded(taskIndex); }); + connect(indexLoadTask.get(), &Task::failed, [=](const QString& error) { remoteLoadFailed(taskIndex, error); }); + connect(indexLoadTask.get(), &Task::aborted, [=]() { remoteLoadFailed(taskIndex, tr("Aborted")); }); taskIndex++; } } // load all the components OR their lists... - for (auto component: d->m_list->d->components) - { + for (auto component : d->m_list->d->components) { Task::Ptr loadTask; LoadResult singleResult; RemoteLoadStatus::Type loadType; - // FIXME: to do this right, we need to load the lists and decide on which versions to use during dependency resolution. For now, ignore all that... + // FIXME: to do this right, we need to load the lists and decide on which versions to use during dependency resolution. For now, + // ignore all that... #if 0 switch(d->mode) { @@ -231,26 +200,15 @@ void ComponentUpdateTask::loadComponents() singleResult = loadComponent(component, loadTask, d->netmode); loadType = RemoteLoadStatus::Type::Version; #endif - if(singleResult == LoadResult::LoadedLocal) - { + if (singleResult == LoadResult::LoadedLocal) { component->updateCachedData(); } result = composeLoadResult(result, singleResult); - if (loadTask) - { + if (loadTask) { qDebug() << "Remote loading is being run for" << component->getName(); - connect(loadTask.get(), &Task::succeeded, [=]() - { - remoteLoadSucceeded(taskIndex); - }); - connect(loadTask.get(), &Task::failed, [=](const QString & error) - { - remoteLoadFailed(taskIndex, error); - }); - connect(loadTask.get(), &Task::aborted, [=]() - { - remoteLoadFailed(taskIndex, tr("Aborted")); - }); + connect(loadTask.get(), &Task::succeeded, [=]() { remoteLoadSucceeded(taskIndex); }); + connect(loadTask.get(), &Task::failed, [=](const QString& error) { remoteLoadFailed(taskIndex, error); }); + connect(loadTask.get(), &Task::aborted, [=]() { remoteLoadFailed(taskIndex, tr("Aborted")); }); RemoteLoadStatus status; status.type = loadType; status.PackProfileIndex = componentIndex; @@ -260,95 +218,73 @@ void ComponentUpdateTask::loadComponents() componentIndex++; } d->remoteTasksInProgress = taskIndex; - switch(result) - { - case LoadResult::LoadedLocal: - { + switch (result) { + case LoadResult::LoadedLocal: { // Everything got loaded. Advance to dependency resolution. resolveDependencies(d->mode == Mode::Launch || d->netmode == Net::Mode::Offline); break; } - case LoadResult::RequiresRemote: - { + case LoadResult::RequiresRemote: { // we wait for signals. break; } - case LoadResult::Failed: - { + case LoadResult::Failed: { emitFailed(tr("Some component metadata load tasks failed.")); break; } } } -namespace -{ - struct RequireEx : public Meta::Require - { - size_t indexOfFirstDependee = 0; - }; - struct RequireCompositionResult - { - bool ok; - RequireEx outcome; - }; - using RequireExSet = std::set; -} +namespace { +struct RequireEx : public Meta::Require { + size_t indexOfFirstDependee = 0; +}; +struct RequireCompositionResult { + bool ok; + RequireEx outcome; +}; +using RequireExSet = std::set; +} // namespace -static RequireCompositionResult composeRequirement(const RequireEx & a, const RequireEx & b) +static RequireCompositionResult composeRequirement(const RequireEx& a, const RequireEx& b) { assert(a.uid == b.uid); RequireEx out; out.uid = a.uid; out.indexOfFirstDependee = std::min(a.indexOfFirstDependee, b.indexOfFirstDependee); - if(a.equalsVersion.isEmpty()) - { + if (a.equalsVersion.isEmpty()) { out.equalsVersion = b.equalsVersion; - } - else if (b.equalsVersion.isEmpty()) - { + } else if (b.equalsVersion.isEmpty()) { out.equalsVersion = a.equalsVersion; - } - else if (a.equalsVersion == b.equalsVersion) - { + } else if (a.equalsVersion == b.equalsVersion) { out.equalsVersion = a.equalsVersion; - } - else - { + } else { // FIXME: mark error as explicit version conflict - return {false, out}; + return { false, out }; } - if(a.suggests.isEmpty()) - { + if (a.suggests.isEmpty()) { out.suggests = b.suggests; - } - else if (b.suggests.isEmpty()) - { + } else if (b.suggests.isEmpty()) { out.suggests = a.suggests; - } - else - { + } else { Version aVer(a.suggests); Version bVer(b.suggests); out.suggests = (aVer < bVer ? b.suggests : a.suggests); } - return {true, out}; + return { true, out }; } // gather the requirements from all components, finding any obvious conflicts -static bool gatherRequirementsFromComponents(const ComponentContainer & input, RequireExSet & output) +static bool gatherRequirementsFromComponents(const ComponentContainer& input, RequireExSet& output) { bool succeeded = true; size_t componentNum = 0; - for(auto component: input) - { - auto &componentRequires = component->m_cachedRequires; - for(const auto & componentRequire: componentRequires) - { - auto found = std::find_if(output.cbegin(), output.cend(), [componentRequire](const Meta::Require & req){ - return req.uid == componentRequire.uid; - }); + for (auto component : input) { + auto& componentRequires = component->m_cachedRequires; + for (const auto& componentRequire : componentRequires) { + auto found = std::find_if(output.cbegin(), output.cend(), + [componentRequire](const Meta::Require& req) { return req.uid == componentRequire.uid; }); RequireEx componenRequireEx; componenRequireEx.uid = componentRequire.uid; @@ -356,29 +292,18 @@ static bool gatherRequirementsFromComponents(const ComponentContainer & input, R componenRequireEx.equalsVersion = componentRequire.equalsVersion; componenRequireEx.indexOfFirstDependee = componentNum; - if(found != output.cend()) - { + if (found != output.cend()) { // found... process it further auto result = composeRequirement(componenRequireEx, *found); - if(result.ok) - { + if (result.ok) { output.erase(componenRequireEx); output.insert(result.outcome); - } - else - { - qCritical() - << "Conflicting requirements:" - << componentRequire.uid - << "versions:" - << componentRequire.equalsVersion - << ";" - << (*found).equalsVersion; + } else { + qCritical() << "Conflicting requirements:" << componentRequire.uid << "versions:" << componentRequire.equalsVersion + << ";" << (*found).equalsVersion; } succeeded &= result.ok; - } - else - { + } else { // not found, accumulate output.insert(componenRequireEx); } @@ -389,19 +314,17 @@ static bool gatherRequirementsFromComponents(const ComponentContainer & input, R } /// Get list of uids that can be trivially removed because nothing is depending on them anymore (and they are installed as deps) -static void getTrivialRemovals(const ComponentContainer & components, const RequireExSet & reqs, QStringList &toRemove) +static void getTrivialRemovals(const ComponentContainer& components, const RequireExSet& reqs, QStringList& toRemove) { - for(const auto & component: components) - { - if(!component->m_dependencyOnly) + for (const auto& component : components) { + if (!component->m_dependencyOnly) continue; - if(!component->m_cachedVolatile) + if (!component->m_cachedVolatile) continue; RequireEx reqNeedle; reqNeedle.uid = component->m_uid; const auto iter = reqs.find(reqNeedle); - if(iter == reqs.cend()) - { + if (iter == reqs.cend()) { toRemove.append(component->m_uid); } } @@ -415,60 +338,40 @@ static void getTrivialRemovals(const ComponentContainer & components, const Requ * toAdd - set of requirements than mean adding a new component * toChange - set of requirements that mean changing version of an existing component */ -static bool getTrivialComponentChanges(const ComponentIndex & index, const RequireExSet & input, RequireExSet & toAdd, RequireExSet & toChange) +static bool getTrivialComponentChanges(const ComponentIndex& index, const RequireExSet& input, RequireExSet& toAdd, RequireExSet& toChange) { - enum class Decision - { - Undetermined, - Met, - Missing, - VersionNotSame, - LockedVersionNotSame - } decision = Decision::Undetermined; + enum class Decision { Undetermined, Met, Missing, VersionNotSame, LockedVersionNotSame } decision = Decision::Undetermined; QString reqStr; bool succeeded = true; // list the composed requirements and say if they are met or unmet - for(auto & req: input) - { - do - { - if(req.equalsVersion.isEmpty()) - { + for (auto& req : input) { + do { + if (req.equalsVersion.isEmpty()) { reqStr = QString("Req: %1").arg(req.uid); - if(index.contains(req.uid)) - { + if (index.contains(req.uid)) { decision = Decision::Met; - } - else - { + } else { toAdd.insert(req); decision = Decision::Missing; } break; - } - else - { + } else { reqStr = QString("Req: %1 == %2").arg(req.uid, req.equalsVersion); - const auto & compIter = index.find(req.uid); - if(compIter == index.cend()) - { + const auto& compIter = index.find(req.uid); + if (compIter == index.cend()) { toAdd.insert(req); decision = Decision::Missing; break; } - auto & comp = (*compIter); - if(comp->getVersion() != req.equalsVersion) - { - if(comp->isCustom()) { + auto& comp = (*compIter); + if (comp->getVersion() != req.equalsVersion) { + if (comp->isCustom()) { decision = Decision::LockedVersionNotSame; } else { - if(comp->m_dependencyOnly) - { + if (comp->m_dependencyOnly) { decision = Decision::VersionNotSame; - } - else - { + } else { decision = Decision::LockedVersionNotSame; } } @@ -476,9 +379,8 @@ static bool getTrivialComponentChanges(const ComponentIndex & index, const Requi } decision = Decision::Met; } - } while(false); - switch(decision) - { + } while (false); + switch (decision) { case Decision::Undetermined: qCritical() << "No decision for" << reqStr; succeeded = false; @@ -520,26 +422,22 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly) * * NOTE: this is a placeholder and should eventually be replaced with something 'serious' */ - auto & components = d->m_list->d->components; - auto & componentIndex = d->m_list->d->componentIndex; + auto& components = d->m_list->d->components; + auto& componentIndex = d->m_list->d->componentIndex; RequireExSet allRequires; QStringList toRemove; - do - { + do { allRequires.clear(); toRemove.clear(); - if(!gatherRequirementsFromComponents(components, allRequires)) - { + if (!gatherRequirementsFromComponents(components, allRequires)) { emitFailed(tr("Conflicting requirements detected during dependency checking!")); return; } getTrivialRemovals(components, allRequires, toRemove); - if(!toRemove.isEmpty()) - { + if (!toRemove.isEmpty()) { qDebug() << "Removing obsolete components..."; - for(auto & remove : toRemove) - { + for (auto& remove : toRemove) { qDebug() << "Removing" << remove; d->m_list->remove(remove); } @@ -548,69 +446,50 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly) RequireExSet toAdd; RequireExSet toChange; bool succeeded = getTrivialComponentChanges(componentIndex, allRequires, toAdd, toChange); - if(!succeeded) - { + if (!succeeded) { emitFailed(tr("Instance has conflicting dependencies.")); return; } - if(checkOnly) - { - if(toAdd.size() || toChange.size()) - { + if (checkOnly) { + if (toAdd.size() || toChange.size()) { emitFailed(tr("Instance has unresolved dependencies while loading/checking for launch.")); - } - else - { + } else { emitSucceeded(); } return; } bool recursionNeeded = false; - if(toAdd.size()) - { + if (toAdd.size()) { // add stuff... - for(auto &add: toAdd) - { + for (auto& add : toAdd) { auto component = makeShared(d->m_list, add.uid); - if(!add.equalsVersion.isEmpty()) - { + if (!add.equalsVersion.isEmpty()) { // exact version qDebug() << "Adding" << add.uid << "version" << add.equalsVersion << "at position" << add.indexOfFirstDependee; component->m_version = add.equalsVersion; - } - else - { + } else { // version needs to be decided qDebug() << "Adding" << add.uid << "at position" << add.indexOfFirstDependee; -// ############################################################################################################ -// HACK HACK HACK HACK FIXME: this is a placeholder for deciding what version to use. For now, it is hardcoded. - if(!add.suggests.isEmpty()) - { + // ############################################################################################################ + // HACK HACK HACK HACK FIXME: this is a placeholder for deciding what version to use. For now, it is hardcoded. + if (!add.suggests.isEmpty()) { component->m_version = add.suggests; - } - else - { - if(add.uid == "org.lwjgl") - { + } else { + if (add.uid == "org.lwjgl") { component->m_version = "2.9.1"; - } - else if (add.uid == "org.lwjgl3") - { + } else if (add.uid == "org.lwjgl3") { component->m_version = "3.1.2"; - } - else if (add.uid == "net.fabricmc.intermediary" || add.uid == "org.quiltmc.hashed") - { - auto minecraft = std::find_if(components.begin(), components.end(), [](ComponentPtr & cmp){ - return cmp->getID() == "net.minecraft"; - }); - if(minecraft != components.end()) { + } else if (add.uid == "net.fabricmc.intermediary" || add.uid == "org.quiltmc.hashed") { + auto minecraft = std::find_if(components.begin(), components.end(), + [](ComponentPtr& cmp) { return cmp->getID() == "net.minecraft"; }); + if (minecraft != components.end()) { component->m_version = (*minecraft)->getVersion(); } } } -// HACK HACK HACK HACK FIXME: this is a placeholder for deciding what version to use. For now, it is hardcoded. -// ############################################################################################################ + // HACK HACK HACK HACK FIXME: this is a placeholder for deciding what version to use. For now, it is hardcoded. + // ############################################################################################################ } component->m_dependencyOnly = true; // FIXME: this should not work directly with the component list @@ -619,11 +498,9 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly) } recursionNeeded = true; } - if(toChange.size()) - { + if (toChange.size()) { // change a version of something that exists - for(auto &change: toChange) - { + for (auto& change : toChange) { // FIXME: this should not work directly with the component list qDebug() << "Setting version of " << change.uid << "to" << change.equalsVersion; auto component = componentIndex[change.uid]; @@ -632,31 +509,26 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly) recursionNeeded = true; } - if(recursionNeeded) - { + if (recursionNeeded) { loadComponents(); - } - else - { + } else { emitSucceeded(); } } void ComponentUpdateTask::remoteLoadSucceeded(size_t taskIndex) { - auto &taskSlot = d->remoteLoadStatusList[taskIndex]; - if(taskSlot.finished) - { + auto& taskSlot = d->remoteLoadStatusList[taskIndex]; + if (taskSlot.finished) { qWarning() << "Got multiple results from remote load task" << taskIndex; return; } qDebug() << "Remote task" << taskIndex << "succeeded"; taskSlot.succeeded = false; taskSlot.finished = true; - d->remoteTasksInProgress --; + d->remoteTasksInProgress--; // update the cached data of the component from the downloaded version file. - if (taskSlot.type == RemoteLoadStatus::Type::Version) - { + if (taskSlot.type == RemoteLoadStatus::Type::Version) { auto component = d->m_list->getComponent(taskSlot.PackProfileIndex); component->m_loaded = true; component->updateCachedData(); @@ -664,12 +536,10 @@ void ComponentUpdateTask::remoteLoadSucceeded(size_t taskIndex) checkIfAllFinished(); } - void ComponentUpdateTask::remoteLoadFailed(size_t taskIndex, const QString& msg) { - auto &taskSlot = d->remoteLoadStatusList[taskIndex]; - if(taskSlot.finished) - { + auto& taskSlot = d->remoteLoadStatusList[taskIndex]; + if (taskSlot.finished) { qWarning() << "Got multiple results from remote load task" << taskIndex; return; } @@ -678,31 +548,25 @@ void ComponentUpdateTask::remoteLoadFailed(size_t taskIndex, const QString& msg) taskSlot.succeeded = false; taskSlot.finished = true; taskSlot.error = msg; - d->remoteTasksInProgress --; + d->remoteTasksInProgress--; checkIfAllFinished(); } void ComponentUpdateTask::checkIfAllFinished() { - if(d->remoteTasksInProgress) - { + if (d->remoteTasksInProgress) { // not yet... return; } - if(d->remoteLoadSuccessful) - { + if (d->remoteLoadSuccessful) { // nothing bad happened... clear the temp load status and proceed with looking at dependencies d->remoteLoadStatusList.clear(); resolveDependencies(d->mode == Mode::Launch); - } - else - { + } else { // remote load failed... report error and bail QStringList allErrorsList; - for(auto & item: d->remoteLoadStatusList) - { - if(!item.succeeded) - { + for (auto& item : d->remoteLoadStatusList) { + if (!item.succeeded) { allErrorsList.append(item.error); } } diff --git a/launcher/minecraft/ComponentUpdateTask.h b/launcher/minecraft/ComponentUpdateTask.h index 4274cabb5..2f396a049 100644 --- a/launcher/minecraft/ComponentUpdateTask.h +++ b/launcher/minecraft/ComponentUpdateTask.h @@ -1,37 +1,32 @@ #pragma once -#include "tasks/Task.h" #include "net/Mode.h" +#include "tasks/Task.h" #include class PackProfile; struct ComponentUpdateTaskData; -class ComponentUpdateTask : public Task -{ +class ComponentUpdateTask : public Task { Q_OBJECT -public: - enum class Mode - { - Launch, - Resolution - }; + public: + enum class Mode { Launch, Resolution }; -public: - explicit ComponentUpdateTask(Mode mode, Net::Mode netmode, PackProfile * list, QObject *parent = 0); + public: + explicit ComponentUpdateTask(Mode mode, Net::Mode netmode, PackProfile* list, QObject* parent = 0); virtual ~ComponentUpdateTask(); -protected: + protected: void executeTask(); -private: + private: void loadComponents(); void resolveDependencies(bool checkOnly); void remoteLoadSucceeded(size_t index); - void remoteLoadFailed(size_t index, const QString &msg); + void remoteLoadFailed(size_t index, const QString& msg); void checkIfAllFinished(); -private: + private: std::unique_ptr d; }; diff --git a/launcher/minecraft/ComponentUpdateTask_p.h b/launcher/minecraft/ComponentUpdateTask_p.h index 5b02431b2..00e8f2fbe 100644 --- a/launcher/minecraft/ComponentUpdateTask_p.h +++ b/launcher/minecraft/ComponentUpdateTask_p.h @@ -1,29 +1,22 @@ #pragma once -#include -#include #include +#include +#include #include "net/Mode.h" class PackProfile; -struct RemoteLoadStatus -{ - enum class Type - { - Index, - List, - Version - } type = Type::Version; +struct RemoteLoadStatus { + enum class Type { Index, List, Version } type = Type::Version; size_t PackProfileIndex = 0; bool finished = false; bool succeeded = false; QString error; }; -struct ComponentUpdateTaskData -{ - PackProfile * m_list = nullptr; +struct ComponentUpdateTaskData { + PackProfile* m_list = nullptr; QList remoteLoadStatusList; bool remoteLoadSuccessful = true; size_t remoteTasksInProgress = 0; diff --git a/launcher/minecraft/GradleSpecifier.h b/launcher/minecraft/GradleSpecifier.h index 27514ab9d..22db7d641 100644 --- a/launcher/minecraft/GradleSpecifier.h +++ b/launcher/minecraft/GradleSpecifier.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,22 +35,15 @@ #pragma once +#include #include #include -#include #include "DefaultVariable.h" -struct GradleSpecifier -{ - GradleSpecifier() - { - m_valid = false; - } - GradleSpecifier(QString value) - { - operator=(value); - } - GradleSpecifier & operator =(const QString & value) +struct GradleSpecifier { + GradleSpecifier() { m_valid = false; } + GradleSpecifier(QString value) { operator=(value); } + GradleSpecifier& operator=(const QString& value) { /* org.gradle.test.classifiers : service : 1.0 : jdk15 @ jar @@ -61,10 +54,13 @@ struct GradleSpecifier 4 "jdk15" 5 "jar" */ - QRegularExpression matcher(QRegularExpression::anchoredPattern("([^:@]+):([^:@]+):([^:@]+)" "(?::([^:@]+))?" "(?:@([^:@]+))?")); + QRegularExpression matcher( + QRegularExpression::anchoredPattern("([^:@]+):([^:@]+):([^:@]+)" + "(?::([^:@]+))?" + "(?:@([^:@]+))?")); QRegularExpressionMatch match = matcher.match(value); m_valid = match.hasMatch(); - if(!m_valid) { + if (!m_valid) { m_invalidValue = value; return *this; } @@ -73,53 +69,46 @@ struct GradleSpecifier m_artifactId = match.captured(2); m_version = match.captured(3); m_classifier = match.captured(4); - if(match.lastCapturedIndex() >= 5) - { + if (match.lastCapturedIndex() >= 5) { m_extension = match.captured(5); } return *this; } QString serialize() const { - if(!m_valid) { + if (!m_valid) { return m_invalidValue; } QString retval = m_groupId + ":" + m_artifactId + ":" + m_version; - if(!m_classifier.isEmpty()) - { + if (!m_classifier.isEmpty()) { retval += ":" + m_classifier; } - if(m_extension.isExplicit()) - { + if (m_extension.isExplicit()) { retval += "@" + m_extension; } return retval; } QString getFileName() const { - if(!m_valid) { + if (!m_valid) { return QString(); } QString filename = m_artifactId + '-' + m_version; - if(!m_classifier.isEmpty()) - { + if (!m_classifier.isEmpty()) { filename += "-" + m_classifier; } filename += "." + m_extension; return filename; } - QString toPath(const QString & filenameOverride = QString()) const + QString toPath(const QString& filenameOverride = QString()) const { - if(!m_valid) { + if (!m_valid) { return QString(); } QString filename; - if(filenameOverride.isEmpty()) - { + if (filenameOverride.isEmpty()) { filename = getFileName(); - } - else - { + } else { filename = filenameOverride; } QString path = m_groupId; @@ -127,57 +116,34 @@ struct GradleSpecifier path += '/' + m_artifactId + '/' + m_version + '/' + filename; return path; } - inline bool valid() const - { - return m_valid; - } - inline QString version() const - { - return m_version; - } - inline QString groupId() const - { - return m_groupId; - } - inline QString artifactId() const - { - return m_artifactId; - } - inline void setClassifier(const QString & classifier) - { - m_classifier = classifier; - } - inline QString classifier() const - { - return m_classifier; - } - inline QString extension() const - { - return m_extension; - } - inline QString artifactPrefix() const - { - return m_groupId + ":" + m_artifactId; - } - bool matchName(const GradleSpecifier & other) const + inline bool valid() const { return m_valid; } + inline QString version() const { return m_version; } + inline QString groupId() const { return m_groupId; } + inline QString artifactId() const { return m_artifactId; } + inline void setClassifier(const QString& classifier) { m_classifier = classifier; } + inline QString classifier() const { return m_classifier; } + inline QString extension() const { return m_extension; } + inline QString artifactPrefix() const { return m_groupId + ":" + m_artifactId; } + bool matchName(const GradleSpecifier& other) const { return other.artifactId() == artifactId() && other.groupId() == groupId() && other.classifier() == classifier(); } - bool operator==(const GradleSpecifier & other) const + bool operator==(const GradleSpecifier& other) const { - if(m_groupId != other.m_groupId) + if (m_groupId != other.m_groupId) return false; - if(m_artifactId != other.m_artifactId) + if (m_artifactId != other.m_artifactId) return false; - if(m_version != other.m_version) + if (m_version != other.m_version) return false; - if(m_classifier != other.m_classifier) + if (m_classifier != other.m_classifier) return false; - if(m_extension != other.m_extension) + if (m_extension != other.m_extension) return false; return true; } -private: + + private: QString m_invalidValue; QString m_groupId; QString m_artifactId; diff --git a/launcher/minecraft/LaunchProfile.cpp b/launcher/minecraft/LaunchProfile.cpp index 9a6cea11a..cf819b411 100644 --- a/launcher/minecraft/LaunchProfile.cpp +++ b/launcher/minecraft/LaunchProfile.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -55,9 +55,9 @@ void LaunchProfile::clear() m_problemSeverity = ProblemSeverity::None; } -static void applyString(const QString & from, QString & to) +static void applyString(const QString& from, QString& to) { - if(from.isEmpty()) + if (from.isEmpty()) return; to = from; } @@ -94,8 +94,7 @@ void LaunchProfile::applyMinecraftVersionType(const QString& type) void LaunchProfile::applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets) { - if(assets) - { + if (assets) { m_minecraftAssets = assets; } } @@ -109,10 +108,8 @@ void LaunchProfile::applyTweakers(const QStringList& tweakers) { // if the applied tweakers override an existing one, skip it. this effectively moves it later in the sequence QStringList newTweakers; - for(auto & tweaker: m_tweakers) - { - if (tweakers.contains(tweaker)) - { + for (auto& tweaker : m_tweakers) { + if (tweakers.contains(tweaker)) { continue; } newTweakers.append(tweaker); @@ -127,13 +124,11 @@ void LaunchProfile::applyJarMods(const QList& jarMods) this->m_jarMods.append(jarMods); } -static int findLibraryByName(QList *haystack, const GradleSpecifier &needle) +static int findLibraryByName(QList* haystack, const GradleSpecifier& needle) { int retval = -1; - for (int i = 0; i < haystack->size(); ++i) - { - if (haystack->at(i)->rawName().matchName(needle)) - { + for (int i = 0; i < haystack->size(); ++i) { + if (haystack->at(i)->rawName().matchName(needle)) { // only one is allowed. if (retval != -1) return -1; @@ -145,24 +140,21 @@ static int findLibraryByName(QList *haystack, const GradleSpecifier void LaunchProfile::applyMods(const QList& mods) { - QList * list = &m_mods; - for(auto & mod: mods) - { + QList* list = &m_mods; + for (auto& mod : mods) { auto modCopy = Library::limitedCopy(mod); // find the mod by name. const int index = findLibraryByName(list, mod->rawName()); // mod not found? just add it. - if (index < 0) - { + if (index < 0) { list->append(modCopy); return; } auto existingLibrary = list->at(index); // if we are higher it means we should update - if (Version(mod->version()) > Version(existingLibrary->version())) - { + if (Version(mod->version()) > Version(existingLibrary->version())) { list->replace(index, modCopy); } } @@ -173,16 +165,14 @@ void LaunchProfile::applyCompatibleJavaMajors(QList& javaMajor) m_compatibleJavaMajors.append(javaMajor); } -void LaunchProfile::applyLibrary(LibraryPtr library, const RuntimeContext & runtimeContext) +void LaunchProfile::applyLibrary(LibraryPtr library, const RuntimeContext& runtimeContext) { - if(!library->isActive(runtimeContext)) - { + if (!library->isActive(runtimeContext)) { return; } - QList * list = &m_libraries; - if(library->isNative()) - { + QList* list = &m_libraries; + if (library->isNative()) { list = &m_nativeLibraries; } @@ -191,29 +181,25 @@ void LaunchProfile::applyLibrary(LibraryPtr library, const RuntimeContext & runt // find the library by name. const int index = findLibraryByName(list, library->rawName()); // library not found? just add it. - if (index < 0) - { + if (index < 0) { list->append(libraryCopy); return; } auto existingLibrary = list->at(index); // if we are higher it means we should update - if (Version(library->version()) > Version(existingLibrary->version())) - { + if (Version(library->version()) > Version(existingLibrary->version())) { list->replace(index, libraryCopy); } } -void LaunchProfile::applyMavenFile(LibraryPtr mavenFile, const RuntimeContext & runtimeContext) +void LaunchProfile::applyMavenFile(LibraryPtr mavenFile, const RuntimeContext& runtimeContext) { - if(!mavenFile->isActive(runtimeContext)) - { + if (!mavenFile->isActive(runtimeContext)) { return; } - if(mavenFile->isNative()) - { + if (mavenFile->isNative()) { return; } @@ -221,16 +207,14 @@ void LaunchProfile::applyMavenFile(LibraryPtr mavenFile, const RuntimeContext & m_mavenFiles.append(Library::limitedCopy(mavenFile)); } -void LaunchProfile::applyAgent(AgentPtr agent, const RuntimeContext & runtimeContext) +void LaunchProfile::applyAgent(AgentPtr agent, const RuntimeContext& runtimeContext) { auto lib = agent->library(); - if(!lib->isActive(runtimeContext)) - { + if (!lib->isActive(runtimeContext)) { return; } - if(lib->isNative()) - { + if (lib->isNative()) { return; } @@ -244,16 +228,14 @@ const LibraryPtr LaunchProfile::getMainJar() const void LaunchProfile::applyMainJar(LibraryPtr jar) { - if(jar) - { + if (jar) { m_mainJar = jar; } } void LaunchProfile::applyProblemSeverity(ProblemSeverity severity) { - if (m_problemSeverity < severity) - { + if (m_problemSeverity < severity) { m_problemSeverity = severity; } } @@ -279,12 +261,12 @@ QString LaunchProfile::getMainClass() const return m_mainClass; } -const QSet &LaunchProfile::getTraits() const +const QSet& LaunchProfile::getTraits() const { return m_traits; } -const QStringList & LaunchProfile::getTweakers() const +const QStringList& LaunchProfile::getTweakers() const { return m_tweakers; } @@ -306,8 +288,7 @@ QString LaunchProfile::getMinecraftVersionType() const std::shared_ptr LaunchProfile::getMinecraftAssets() const { - if(!m_minecraftAssets) - { + if (!m_minecraftAssets) { return std::make_shared("legacy"); } return m_minecraftAssets; @@ -318,80 +299,69 @@ QString LaunchProfile::getMinecraftArguments() const return m_minecraftArguments; } -const QStringList & LaunchProfile::getAddnJvmArguments() const +const QStringList& LaunchProfile::getAddnJvmArguments() const { return m_addnJvmArguments; } -const QList & LaunchProfile::getJarMods() const +const QList& LaunchProfile::getJarMods() const { return m_jarMods; } -const QList & LaunchProfile::getLibraries() const +const QList& LaunchProfile::getLibraries() const { return m_libraries; } -const QList & LaunchProfile::getNativeLibraries() const +const QList& LaunchProfile::getNativeLibraries() const { return m_nativeLibraries; } -const QList & LaunchProfile::getMavenFiles() const +const QList& LaunchProfile::getMavenFiles() const { return m_mavenFiles; } -const QList & LaunchProfile::getAgents() const +const QList& LaunchProfile::getAgents() const { return m_agents; } -const QList & LaunchProfile::getCompatibleJavaMajors() const +const QList& LaunchProfile::getCompatibleJavaMajors() const { return m_compatibleJavaMajors; } -void LaunchProfile::getLibraryFiles( - const RuntimeContext & runtimeContext, - QStringList& jars, - QStringList& nativeJars, - const QString& overridePath, - const QString& tempPath -) const +void LaunchProfile::getLibraryFiles(const RuntimeContext& runtimeContext, + QStringList& jars, + QStringList& nativeJars, + const QString& overridePath, + const QString& tempPath) const { QStringList native32, native64; jars.clear(); nativeJars.clear(); - for (auto lib : getLibraries()) - { + for (auto lib : getLibraries()) { lib->getApplicableFiles(runtimeContext, jars, nativeJars, native32, native64, overridePath); } // NOTE: order is important here, add main jar last to the lists - if(m_mainJar) - { + if (m_mainJar) { // FIXME: HACK!! jar modding is weird and unsystematic! - if(m_jarMods.size()) - { + if (m_jarMods.size()) { QDir tempDir(tempPath); jars.append(tempDir.absoluteFilePath("minecraft.jar")); - } - else - { + } else { m_mainJar->getApplicableFiles(runtimeContext, jars, nativeJars, native32, native64, overridePath); } } - for (auto lib : getNativeLibraries()) - { + for (auto lib : getNativeLibraries()) { lib->getApplicableFiles(runtimeContext, jars, nativeJars, native32, native64, overridePath); } - if(runtimeContext.javaArchitecture == "32") - { + if (runtimeContext.javaArchitecture == "32") { nativeJars.append(native32); - } - else if(runtimeContext.javaArchitecture == "64") - { + } else if (runtimeContext.javaArchitecture == "64") { nativeJars.append(native64); } } diff --git a/launcher/minecraft/LaunchProfile.h b/launcher/minecraft/LaunchProfile.h index 49c1217d3..12b312383 100644 --- a/launcher/minecraft/LaunchProfile.h +++ b/launcher/minecraft/LaunchProfile.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -34,17 +34,16 @@ */ #pragma once -#include -#include "Library.h" -#include "Agent.h" #include +#include +#include "Agent.h" +#include "Library.h" -class LaunchProfile: public ProblemProvider -{ -public: - virtual ~LaunchProfile() {}; +class LaunchProfile : public ProblemProvider { + public: + virtual ~LaunchProfile() {} -public: /* application of profile variables from patches */ + public: /* application of profile variables from patches */ void applyMinecraftVersion(const QString& id); void applyMainClass(const QString& mainClass); void applyAppletClass(const QString& appletClass); @@ -52,48 +51,46 @@ public: /* application of profile variables from patches */ void applyAddnJvmArguments(const QStringList& minecraftArguments); void applyMinecraftVersionType(const QString& type); void applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets); - void applyTraits(const QSet &traits); - void applyTweakers(const QStringList &tweakers); - void applyJarMods(const QList &jarMods); - void applyMods(const QList &jarMods); - void applyLibrary(LibraryPtr library, const RuntimeContext & runtimeContext); - void applyMavenFile(LibraryPtr library, const RuntimeContext & runtimeContext); - void applyAgent(AgentPtr agent, const RuntimeContext & runtimeContext); + void applyTraits(const QSet& traits); + void applyTweakers(const QStringList& tweakers); + void applyJarMods(const QList& jarMods); + void applyMods(const QList& jarMods); + void applyLibrary(LibraryPtr library, const RuntimeContext& runtimeContext); + void applyMavenFile(LibraryPtr library, const RuntimeContext& runtimeContext); + void applyAgent(AgentPtr agent, const RuntimeContext& runtimeContext); void applyCompatibleJavaMajors(QList& javaMajor); void applyMainJar(LibraryPtr jar); void applyProblemSeverity(ProblemSeverity severity); /// clear the profile void clear(); -public: /* getters for profile variables */ + public: /* getters for profile variables */ QString getMinecraftVersion() const; QString getMainClass() const; QString getAppletClass() const; QString getMinecraftVersionType() const; MojangAssetIndexInfo::Ptr getMinecraftAssets() const; QString getMinecraftArguments() const; - const QStringList & getAddnJvmArguments() const; - const QSet & getTraits() const; - const QStringList & getTweakers() const; - const QList & getJarMods() const; - const QList & getLibraries() const; - const QList & getNativeLibraries() const; - const QList & getMavenFiles() const; - const QList & getAgents() const; - const QList & getCompatibleJavaMajors() const; + const QStringList& getAddnJvmArguments() const; + const QSet& getTraits() const; + const QStringList& getTweakers() const; + const QList& getJarMods() const; + const QList& getLibraries() const; + const QList& getNativeLibraries() const; + const QList& getMavenFiles() const; + const QList& getAgents() const; + const QList& getCompatibleJavaMajors() const; const LibraryPtr getMainJar() const; - void getLibraryFiles( - const RuntimeContext & runtimeContext, - QStringList & jars, - QStringList & nativeJars, - const QString & overridePath, - const QString & tempPath - ) const; - bool hasTrait(const QString & trait) const; + void getLibraryFiles(const RuntimeContext& runtimeContext, + QStringList& jars, + QStringList& nativeJars, + const QString& overridePath, + const QString& tempPath) const; + bool hasTrait(const QString& trait) const; ProblemSeverity getProblemSeverity() const override; const QList getProblems() const override; -private: + private: /// the version of Minecraft - jar to use QString m_minecraftVersion; @@ -154,5 +151,4 @@ private: QList m_compatibleJavaMajors; ProblemSeverity m_problemSeverity = ProblemSeverity::None; - }; diff --git a/launcher/minecraft/Library.cpp b/launcher/minecraft/Library.cpp index cb2b5254d..0e8ddf03d 100644 --- a/launcher/minecraft/Library.cpp +++ b/launcher/minecraft/Library.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -36,228 +36,180 @@ #include "Library.h" #include "MinecraftInstance.h" -#include -#include -#include #include +#include +#include +#include - -void Library::getApplicableFiles(const RuntimeContext & runtimeContext, QStringList& jar, QStringList& native, QStringList& native32, - QStringList& native64, const QString &overridePath) const +void Library::getApplicableFiles(const RuntimeContext& runtimeContext, + QStringList& jar, + QStringList& native, + QStringList& native32, + QStringList& native64, + const QString& overridePath) const { bool local = isLocal(); - auto actualPath = [&](QString relPath) - { + auto actualPath = [&](QString relPath) { QFileInfo out(FS::PathCombine(storagePrefix(), relPath)); - if(local && !overridePath.isEmpty()) - { + if (local && !overridePath.isEmpty()) { QString fileName = out.fileName(); return QFileInfo(FS::PathCombine(overridePath, fileName)).absoluteFilePath(); } return out.absoluteFilePath(); }; QString raw_storage = storageSuffix(runtimeContext); - if(isNative()) - { - if (raw_storage.contains("${arch}")) - { + if (isNative()) { + if (raw_storage.contains("${arch}")) { auto nat32Storage = raw_storage; nat32Storage.replace("${arch}", "32"); auto nat64Storage = raw_storage; nat64Storage.replace("${arch}", "64"); native32 += actualPath(nat32Storage); native64 += actualPath(nat64Storage); - } - else - { + } else { native += actualPath(raw_storage); } - } - else - { + } else { jar += actualPath(raw_storage); } } -QList Library::getDownloads( - const RuntimeContext & runtimeContext, - class HttpMetaCache* cache, - QStringList& failedLocalFiles, - const QString & overridePath -) const +QList Library::getDownloads(const RuntimeContext& runtimeContext, + class HttpMetaCache* cache, + QStringList& failedLocalFiles, + const QString& overridePath) const { QList out; bool stale = isAlwaysStale(); bool local = isLocal(); - auto check_local_file = [&](QString storage) - { + auto check_local_file = [&](QString storage) { QFileInfo fileinfo(storage); QString fileName = fileinfo.fileName(); auto fullPath = FS::PathCombine(overridePath, fileName); QFileInfo localFileInfo(fullPath); - if(!localFileInfo.exists()) - { + if (!localFileInfo.exists()) { failedLocalFiles.append(localFileInfo.filePath()); return false; } return true; }; - auto add_download = [&](QString storage, QString url, QString sha1) - { - if(local) - { + auto add_download = [&](QString storage, QString url, QString sha1) { + if (local) { return check_local_file(storage); } auto entry = cache->resolveEntry("libraries", storage); - if(stale) - { + if (stale) { entry->setStale(true); } if (!entry->isStale()) return true; Net::Download::Options options; - if(stale) - { + if (stale) { options |= Net::Download::Option::AcceptLocalFiles; } // Don't add a time limit for the libraries cache entry validity options |= Net::Download::Option::MakeEternal; - if(sha1.size()) - { + if (sha1.size()) { auto rawSha1 = QByteArray::fromHex(sha1.toLatin1()); - auto dl = Net::Download::makeCached(url, entry, options); + auto dl = Net::ApiDownload::makeCached(url, entry, options); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); qDebug() << "Checksummed Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url; out.append(dl); - } - else - { - out.append(Net::Download::makeCached(url, entry, options)); + } else { + out.append(Net::ApiDownload::makeCached(url, entry, options)); qDebug() << "Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url; } return true; }; QString raw_storage = storageSuffix(runtimeContext); - if(m_mojangDownloads) - { - if(isNative()) - { + if (m_mojangDownloads) { + if (isNative()) { auto nativeClassifier = getCompatibleNative(runtimeContext); - if(!nativeClassifier.isNull()) - { - if(nativeClassifier.contains("${arch}")) - { + if (!nativeClassifier.isNull()) { + if (nativeClassifier.contains("${arch}")) { auto nat32Classifier = nativeClassifier; nat32Classifier.replace("${arch}", "32"); auto nat64Classifier = nativeClassifier; nat64Classifier.replace("${arch}", "64"); auto nat32info = m_mojangDownloads->getDownloadInfo(nat32Classifier); - if(nat32info) - { + if (nat32info) { auto cooked_storage = raw_storage; cooked_storage.replace("${arch}", "32"); add_download(cooked_storage, nat32info->url, nat32info->sha1); } auto nat64info = m_mojangDownloads->getDownloadInfo(nat64Classifier); - if(nat64info) - { + if (nat64info) { auto cooked_storage = raw_storage; cooked_storage.replace("${arch}", "64"); add_download(cooked_storage, nat64info->url, nat64info->sha1); } - } - else - { + } else { auto info = m_mojangDownloads->getDownloadInfo(nativeClassifier); - if(info) - { + if (info) { add_download(raw_storage, info->url, info->sha1); } } - } - else - { + } else { qDebug() << "Ignoring native library" << m_name.serialize() << "because it has no classifier for current OS"; } - } - else - { - if(m_mojangDownloads->artifact) - { + } else { + if (m_mojangDownloads->artifact) { auto artifact = m_mojangDownloads->artifact; add_download(raw_storage, artifact->url, artifact->sha1); - } - else - { + } else { qDebug() << "Ignoring java library" << m_name.serialize() << "because it has no artifact"; } } - } - else - { - auto raw_dl = [&]() - { - if (!m_absoluteURL.isEmpty()) - { + } else { + auto raw_dl = [&]() { + if (!m_absoluteURL.isEmpty()) { return m_absoluteURL; } - if (m_repositoryURL.isEmpty()) - { + if (m_repositoryURL.isEmpty()) { return BuildConfig.LIBRARY_BASE + raw_storage; } - if(m_repositoryURL.endsWith('/')) - { + if (m_repositoryURL.endsWith('/')) { return m_repositoryURL + raw_storage; - } - else - { + } else { return m_repositoryURL + QChar('/') + raw_storage; } }(); - if (raw_storage.contains("${arch}")) - { + if (raw_storage.contains("${arch}")) { QString cooked_storage = raw_storage; QString cooked_dl = raw_dl; add_download(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32"), QString()); cooked_storage = raw_storage; cooked_dl = raw_dl; add_download(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64"), QString()); - } - else - { + } else { add_download(raw_storage, raw_dl, QString()); } } return out; } -bool Library::isActive(const RuntimeContext & runtimeContext) const +bool Library::isActive(const RuntimeContext& runtimeContext) const { bool result = true; - if (m_rules.empty()) - { + if (m_rules.empty()) { result = true; - } - else - { + } else { RuleAction ruleResult = Disallow; - for (auto rule : m_rules) - { + for (auto rule : m_rules) { RuleAction temp = rule->apply(this, runtimeContext); if (temp != Defer) ruleResult = temp; } result = result && (ruleResult == Allow); } - if (isNative()) - { + if (isNative()) { result = result && !getCompatibleNative(runtimeContext).isNull(); } return result; @@ -273,7 +225,8 @@ bool Library::isAlwaysStale() const return m_hint == "always-stale"; } -QString Library::getCompatibleNative(const RuntimeContext & runtimeContext) const { +QString Library::getCompatibleNative(const RuntimeContext& runtimeContext) const +{ // try to match precise classifier "[os]-[arch]" auto entry = m_nativeClassifiers.constFind(runtimeContext.getClassifier()); // try to match imprecise classifier on legacy architectures "[os]" @@ -298,63 +251,53 @@ QString Library::defaultStoragePrefix() QString Library::storagePrefix() const { - if(m_storagePrefix.isEmpty()) - { + if (m_storagePrefix.isEmpty()) { return defaultStoragePrefix(); } return m_storagePrefix; } -QString Library::filename(const RuntimeContext & runtimeContext) const +QString Library::filename(const RuntimeContext& runtimeContext) const { - if(!m_filename.isEmpty()) - { + if (!m_filename.isEmpty()) { return m_filename; } // non-native? use only the gradle specifier - if (!isNative()) - { + if (!isNative()) { return m_name.getFileName(); } // otherwise native, override classifiers. Mojang HACK! GradleSpecifier nativeSpec = m_name; QString nativeClassifier = getCompatibleNative(runtimeContext); - if (!nativeClassifier.isNull()) - { + if (!nativeClassifier.isNull()) { nativeSpec.setClassifier(nativeClassifier); - } - else - { + } else { nativeSpec.setClassifier("INVALID"); } return nativeSpec.getFileName(); } -QString Library::displayName(const RuntimeContext & runtimeContext) const +QString Library::displayName(const RuntimeContext& runtimeContext) const { - if(!m_displayname.isEmpty()) + if (!m_displayname.isEmpty()) return m_displayname; return filename(runtimeContext); } -QString Library::storageSuffix(const RuntimeContext & runtimeContext) const +QString Library::storageSuffix(const RuntimeContext& runtimeContext) const { // non-native? use only the gradle specifier - if (!isNative()) - { + if (!isNative()) { return m_name.toPath(m_filename); } // otherwise native, override classifiers. Mojang HACK! GradleSpecifier nativeSpec = m_name; QString nativeClassifier = getCompatibleNative(runtimeContext); - if (!nativeClassifier.isNull()) - { + if (!nativeClassifier.isNull()) { nativeSpec.setClassifier(nativeClassifier); - } - else - { + } else { nativeSpec.setClassifier("INVALID"); } return nativeSpec.toPath(m_filename); diff --git a/launcher/minecraft/Library.h b/launcher/minecraft/Library.h index 26dbf962d..f8816a97b 100644 --- a/launcher/minecraft/Library.h +++ b/launcher/minecraft/Library.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -34,20 +34,19 @@ */ #pragma once -#include #include -#include +#include #include +#include +#include #include #include -#include -#include #include #include -#include "Rule.h" #include "GradleSpecifier.h" #include "MojangDownloadInfo.h" +#include "Rule.h" #include "RuntimeContext.h" class Library; @@ -55,19 +54,14 @@ class MinecraftInstance; typedef std::shared_ptr LibraryPtr; -class Library -{ +class Library { friend class OneSixVersionFormat; friend class MojangVersionFormat; friend class LibraryTest; -public: - Library() - { - } - Library(const QString &name) - { - m_name = name; - } + + public: + Library() {} + Library(const QString& name) { m_name = name; } /// limited copy without some data. TODO: why? static LibraryPtr limitedCopy(LibraryPtr base) { @@ -85,98 +79,60 @@ public: return newlib; } -public: /* methods */ + public: /* methods */ /// Returns the raw name field - const GradleSpecifier & rawName() const - { - return m_name; - } + const GradleSpecifier& rawName() const { return m_name; } - void setRawName(const GradleSpecifier & spec) - { - m_name = spec; - } + void setRawName(const GradleSpecifier& spec) { m_name = spec; } - void setClassifier(const QString & spec) - { - m_name.setClassifier(spec); - } + void setClassifier(const QString& spec) { m_name.setClassifier(spec); } /// returns the full group and artifact prefix - QString artifactPrefix() const - { - return m_name.artifactPrefix(); - } + QString artifactPrefix() const { return m_name.artifactPrefix(); } /// get the artifact ID - QString artifactId() const - { - return m_name.artifactId(); - } + QString artifactId() const { return m_name.artifactId(); } /// get the artifact version - QString version() const - { - return m_name.version(); - } + QString version() const { return m_name.version(); } /// Returns true if the library is native - bool isNative() const - { - return m_nativeClassifiers.size() != 0; - } + bool isNative() const { return m_nativeClassifiers.size() != 0; } void setStoragePrefix(QString prefix = QString()); /// Set the url base for downloads - void setRepositoryURL(const QString &base_url) - { - m_repositoryURL = base_url; - } + void setRepositoryURL(const QString& base_url) { m_repositoryURL = base_url; } - void getApplicableFiles(const RuntimeContext & runtimeContext, QStringList & jar, QStringList & native, - QStringList & native32, QStringList & native64, const QString & overridePath) const; + void getApplicableFiles(const RuntimeContext& runtimeContext, + QStringList& jar, + QStringList& native, + QStringList& native32, + QStringList& native64, + const QString& overridePath) const; - void setAbsoluteUrl(const QString &absolute_url) - { - m_absoluteURL = absolute_url; - } + void setAbsoluteUrl(const QString& absolute_url) { m_absoluteURL = absolute_url; } - void setFilename(const QString &filename) - { - m_filename = filename; - } + void setFilename(const QString& filename) { m_filename = filename; } /// Get the file name of the library - QString filename(const RuntimeContext & runtimeContext) const; + QString filename(const RuntimeContext& runtimeContext) const; // DEPRECATED: set a display name, used by jar mods only - void setDisplayName(const QString & displayName) - { - m_displayname = displayName; - } + void setDisplayName(const QString& displayName) { m_displayname = displayName; } /// Get the file name of the library - QString displayName(const RuntimeContext & runtimeContext) const; + QString displayName(const RuntimeContext& runtimeContext) const; - void setMojangDownloadInfo(MojangLibraryDownloadInfo::Ptr info) - { - m_mojangDownloads = info; - } + void setMojangDownloadInfo(MojangLibraryDownloadInfo::Ptr info) { m_mojangDownloads = info; } - void setHint(const QString &hint) - { - m_hint = hint; - } + void setHint(const QString& hint) { m_hint = hint; } /// Set the load rules - void setRules(QList> rules) - { - m_rules = rules; - } + void setRules(QList> rules) { m_rules = rules; } /// Returns true if the library should be loaded (or extracted, in case of natives) - bool isActive(const RuntimeContext & runtimeContext) const; + bool isActive(const RuntimeContext& runtimeContext) const; /// Returns true if the library is contained in an instance and false if it is shared bool isLocal() const; @@ -188,12 +144,14 @@ public: /* methods */ bool isForge() const; // Get a list of downloads for this library - QList getDownloads(const RuntimeContext & runtimeContext, class HttpMetaCache * cache, - QStringList & failedLocalFiles, const QString & overridePath) const; + QList getDownloads(const RuntimeContext& runtimeContext, + class HttpMetaCache* cache, + QStringList& failedLocalFiles, + const QString& overridePath) const; - QString getCompatibleNative(const RuntimeContext & runtimeContext) const; + QString getCompatibleNative(const RuntimeContext& runtimeContext) const; -private: /* methods */ + private: /* methods */ /// the default storage prefix used by Prism Launcher static QString defaultStoragePrefix(); @@ -201,14 +159,11 @@ private: /* methods */ QString storagePrefix() const; /// Get the relative file path where the library should be saved - QString storageSuffix(const RuntimeContext & runtimeContext) const; + QString storageSuffix(const RuntimeContext& runtimeContext) const; - QString hint() const - { - return m_hint; - } + QString hint() const { return m_hint; } -protected: /* data */ + protected: /* data */ /// the basic gradle dependency specifier. GradleSpecifier m_name; @@ -253,4 +208,3 @@ 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 4867cc7a3..305bff67b 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -4,6 +4,7 @@ * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Jamie Mansfield * Copyright (C) 2022 TheKodeToad + * Copyright (c) 2023 seth * * 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 @@ -36,33 +37,32 @@ */ #include "MinecraftInstance.h" +#include "Application.h" #include "BuildConfig.h" #include "minecraft/launch/CreateGameFolders.h" #include "minecraft/launch/ExtractNatives.h" #include "minecraft/launch/PrintInstanceInfo.h" #include "settings/Setting.h" #include "settings/SettingsObject.h" -#include "Application.h" -#include "pathmatcher/RegexpMatcher.h" -#include "pathmatcher/MultiMatcher.h" #include "FileSystem.h" -#include "java/JavaVersion.h" #include "MMCTime.h" +#include "java/JavaVersion.h" +#include "pathmatcher/MultiMatcher.h" +#include "pathmatcher/RegexpMatcher.h" #include "launch/LaunchTask.h" +#include "launch/steps/CheckJava.h" #include "launch/steps/LookupServerAddress.h" #include "launch/steps/PostLaunchCommand.h" -#include "launch/steps/Update.h" #include "launch/steps/PreLaunchCommand.h" -#include "launch/steps/TextPrint.h" -#include "launch/steps/CheckJava.h" #include "launch/steps/QuitAfterGameStop.h" +#include "launch/steps/TextPrint.h" +#include "launch/steps/Update.h" -#include "minecraft/launch/LauncherPartLaunch.h" -#include "minecraft/launch/DirectJavaLaunch.h" -#include "minecraft/launch/ModMinecraftJar.h" #include "minecraft/launch/ClaimAccount.h" +#include "minecraft/launch/LauncherPartLaunch.h" +#include "minecraft/launch/ModMinecraftJar.h" #include "minecraft/launch/ReconstructAssets.h" #include "minecraft/launch/ScanModFolders.h" #include "minecraft/launch/VerifyJavaInstall.h" @@ -81,10 +81,10 @@ #include "WorldList.h" -#include "PackProfile.h" #include "AssetsUtils.h" -#include "MinecraftUpdate.h" #include "MinecraftLoadAndCheck.h" +#include "MinecraftUpdate.h" +#include "PackProfile.h" #include "minecraft/gameoptions/GameOptions.h" #include "minecraft/update/FoldersTask.h" @@ -96,14 +96,10 @@ // all of this because keeping things compatible with deprecated old settings // if either of the settings {a, b} is true, this also resolves to true -class OrSetting : public Setting -{ +class OrSetting : public Setting { Q_OBJECT -public: - OrSetting(QString id, std::shared_ptr a, std::shared_ptr b) - :Setting({id}, false), m_a(a), m_b(b) - { - } + public: + OrSetting(QString id, std::shared_ptr a, std::shared_ptr b) : Setting({ id }, false), m_a(a), m_b(b) {} virtual QVariant get() const { bool a = m_a->get().toBool(); @@ -112,12 +108,13 @@ public: } virtual void reset() {} virtual void set(QVariant value) {} -private: + + private: std::shared_ptr m_a; std::shared_ptr m_b; }; -MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) +MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir) : BaseInstance(globalSettings, settings, rootDir) { m_components.reset(new PackProfile(this)); @@ -166,10 +163,6 @@ void MinecraftInstance::loadSpecificSettings() m_settings->registerOverride(global_settings->getSetting("MaxMemAlloc"), memorySetting); m_settings->registerOverride(global_settings->getSetting("PermGen"), memorySetting); - // Minecraft launch method - auto launchMethodOverride = m_settings->registerSetting("OverrideMCLaunchMethod", false); - m_settings->registerOverride(global_settings->getSetting("MCLaunchMethod"), launchMethodOverride); - // Native library workarounds auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false); m_settings->registerOverride(global_settings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride); @@ -186,6 +179,10 @@ void MinecraftInstance::loadSpecificSettings() m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride); m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride); + // Mod loader specific options + auto modLoaderSettings = m_settings->registerSetting("OverrideModLoaderSettings", false); + m_settings->registerOverride(global_settings->getSetting("DisableQuiltBeacon"), modLoaderSettings); + m_settings->set("InstanceType", "OneSix"); } @@ -222,14 +219,12 @@ std::shared_ptr MinecraftInstance::getPackProfile() const QSet MinecraftInstance::traits() const { auto components = getPackProfile(); - if (!components) - { - return {"version-incomplete"}; + if (!components) { + return { "version-incomplete" }; } auto profile = components->getProfile(); - if (!profile) - { - return {"version-incomplete"}; + if (!profile) { + return { "version-incomplete" }; } return profile->getTraits(); } @@ -264,7 +259,7 @@ QString MinecraftInstance::getLocalLibraryPath() const bool MinecraftInstance::supportsDemo() const { - Version instance_ver { getPackProfile()->getComponentVersion("net.minecraft") }; + Version instance_ver{ getPackProfile()->getComponentVersion("net.minecraft") }; // Demo mode was introduced in 1.3.1: https://minecraft.fandom.com/wiki/Demo_mode#History // FIXME: Due to Version constraints atm, this can't handle well non-release versions return instance_ver >= Version("1.3.1"); @@ -375,21 +370,24 @@ QStringList MinecraftInstance::extraArguments() if (!version) return list; auto jarMods = getJarMods(); - if (!jarMods.isEmpty()) - { - list.append({"-Dfml.ignoreInvalidMinecraftCertificates=true", - "-Dfml.ignorePatchDiscrepancies=true"}); + if (!jarMods.isEmpty()) { + list.append({ "-Dfml.ignoreInvalidMinecraftCertificates=true", "-Dfml.ignorePatchDiscrepancies=true" }); } auto addn = m_components->getProfile()->getAddnJvmArguments(); if (!addn.isEmpty()) { list.append(addn); } auto agents = m_components->getProfile()->getAgents(); - for (auto agent : agents) - { + for (auto agent : agents) { QStringList jar, temp1, temp2, temp3; agent->library()->getApplicableFiles(runtimeContext(), jar, temp1, temp2, temp3, getLocalLibraryPath()); - list.append("-javaagent:"+jar[0]+(agent->argument().isEmpty() ? "" : "="+agent->argument())); + list.append("-javaagent:" + jar[0] + (agent->argument().isEmpty() ? "" : "=" + agent->argument())); + } + + { + const auto loaders = version->getModLoaders(); + if (loaders.has_value() && loaders.value() & ResourceAPI::Quilt && settings()->get("DisableQuiltBeacon").toBool()) + list.append("-Dloader.disable_beacon=true"); } return list; } @@ -410,38 +408,33 @@ QStringList MinecraftInstance::javaArguments() // HACK: fix issues on macOS with 1.13 snapshots // NOTE: Oracle Java option. if there are alternate jvm implementations, this would be the place to customize this for them #ifdef Q_OS_MAC - if(traits_.contains("FirstThreadOnMacOS")) - { + if (traits_.contains("FirstThreadOnMacOS")) { args << QString("-XstartOnFirstThread"); } #endif // HACK: Stupid hack for Intel drivers. See: https://mojang.atlassian.net/browse/MCL-767 #ifdef Q_OS_WIN32 - args << QString("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_" - "minecraft.exe.heapdump"); + args << QString( + "-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_" + "minecraft.exe.heapdump"); #endif int min = settings()->get("MinMemAlloc").toInt(); int max = settings()->get("MaxMemAlloc").toInt(); - if(min < max) - { + if (min < max) { args << QString("-Xms%1m").arg(min); args << QString("-Xmx%1m").arg(max); - } - else - { + } else { args << QString("-Xms%1m").arg(max); args << QString("-Xmx%1m").arg(min); } // No PermGen in newer java. JavaVersion javaVersion = getJavaVersion(); - if(javaVersion.requiresPermGen()) - { + if (javaVersion.requiresPermGen()) { auto permgen = settings()->get("PermGen").toInt(); - if (permgen != 64) - { + if (permgen != 64) { args << QString("-XX:PermSize=%1m").arg(permgen); } } @@ -481,8 +474,7 @@ QProcessEnvironment MinecraftInstance::createEnvironment() // export some infos auto variables = getVariables(); - for (auto it = variables.begin(); it != variables.end(); ++it) - { + for (auto it = variables.begin(); it != variables.end(); ++it) { env.insert(it.key(), it.value()); } return env; @@ -494,15 +486,12 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment() QProcessEnvironment env = createEnvironment(); #ifdef Q_OS_LINUX - if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud) - { - + if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud) { auto preloadList = env.value("LD_PRELOAD").split(QLatin1String(":")); auto libPaths = env.value("LD_LIBRARY_PATH").split(QLatin1String(":")); auto mangoHudLibString = MangoHud::getLibraryString(); - if (!mangoHudLibString.isEmpty()) - { + if (!mangoHudLibString.isEmpty()) { QFileInfo mangoHudLib(mangoHudLibString); // dlsym variant is only needed for OpenGL and not included in the vulkan layer @@ -515,8 +504,7 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment() env.insert("MANGOHUD", "1"); } - if (settings()->get("UseDiscreteGpu").toBool()) - { + if (settings()->get("UseDiscreteGpu").toBool()) { // Open Source Drivers env.insert("DRI_PRIME", "1"); // Proprietary Nvidia Drivers @@ -537,14 +525,12 @@ static QString replaceTokensIn(QString text, QMap with) QStringList list; QRegularExpressionMatchIterator i = token_regexp.globalMatch(text); int lastCapturedEnd = 0; - while (i.hasNext()) - { + while (i.hasNext()) { QRegularExpressionMatch match = i.next(); result.append(text.mid(lastCapturedEnd, match.capturedStart())); QString key = match.captured(1); auto iter = with.find(key); - if (iter != with.end()) - { + if (iter != with.end()) { result.append(*iter); } lastCapturedEnd = match.capturedEnd(); @@ -553,25 +539,22 @@ static QString replaceTokensIn(QString text, QMap with) return result; } -QStringList MinecraftInstance::processMinecraftArgs( - AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) const +QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) const { auto profile = m_components->getProfile(); QString args_pattern = profile->getMinecraftArguments(); - for (auto tweaker : profile->getTweakers()) - { + for (auto tweaker : profile->getTweakers()) { args_pattern += " --tweakClass " + tweaker; } - if (serverToJoin && !serverToJoin->address.isEmpty()) - { + if (serverToJoin && !serverToJoin->address.isEmpty()) { args_pattern += " --server " + serverToJoin->address; args_pattern += " --port " + QString::number(serverToJoin->port); } QMap token_mapping; // yggdrasil! - if(session) { + if (session) { // token_mapping["auth_username"] = session->username; token_mapping["auth_session"] = session->session; token_mapping["auth_access_token"] = session->access_token; @@ -579,7 +562,7 @@ QStringList MinecraftInstance::processMinecraftArgs( token_mapping["auth_uuid"] = session->uuid; token_mapping["user_properties"] = session->serializeUserProperties(); token_mapping["user_type"] = session->user_type; - if(session->demo) { + if (session->demo) { args_pattern += " --demo"; } } @@ -603,8 +586,7 @@ QStringList MinecraftInstance::processMinecraftArgs( #else QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts); #endif - for (int i = 0; i < parts.length(); i++) - { + for (int i = 0; i < parts.length(); i++) { parts[i] = replaceTokensIn(parts[i], token_mapping); } return parts; @@ -617,32 +599,26 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS if (!m_components) return QString(); auto profile = m_components->getProfile(); - if(!profile) + if (!profile) return QString(); auto mainClass = getMainClass(); - if (!mainClass.isEmpty()) - { + if (!mainClass.isEmpty()) { launchScript += "mainClass " + mainClass + "\n"; } auto appletClass = profile->getAppletClass(); - if (!appletClass.isEmpty()) - { + if (!appletClass.isEmpty()) { launchScript += "appletClass " + appletClass + "\n"; } - if (serverToJoin && !serverToJoin->address.isEmpty()) - { + if (serverToJoin && !serverToJoin->address.isEmpty()) { launchScript += "serverAddress " + serverToJoin->address + "\n"; launchScript += "serverPort " + QString::number(serverToJoin->port) + "\n"; } // generic minecraft params - for (auto param : processMinecraftArgs( - session, - nullptr /* When using a launch script, the server parameters are handled by it*/ - )) - { + for (auto param : processMinecraftArgs(session, nullptr /* When using a launch script, the server parameters are handled by it*/ + )) { launchScript += "param " + param + "\n"; } @@ -652,22 +628,19 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS if (settings()->get("LaunchMaximized").toBool()) windowParams = "max"; else - windowParams = QString("%1x%2") - .arg(settings()->get("MinecraftWinWidth").toInt()) - .arg(settings()->get("MinecraftWinHeight").toInt()); + windowParams = + QString("%1x%2").arg(settings()->get("MinecraftWinWidth").toInt()).arg(settings()->get("MinecraftWinHeight").toInt()); launchScript += "windowTitle " + windowTitle() + "\n"; launchScript += "windowParams " + windowParams + "\n"; } // legacy auth - if(session) - { + if (session) { launchScript += "userName " + session->player_name + "\n"; launchScript += "sessionId " + session->session + "\n"; } - for (auto trait : profile->getTraits()) - { + for (auto trait : profile->getTraits()) { launchScript += "traits " + trait + "\n"; } @@ -680,17 +653,17 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) { QStringList out; - out << "Main Class:" << " " + getMainClass() << ""; - out << "Native path:" << " " + getNativePath() << ""; + out << "Main Class:" + << " " + getMainClass() << ""; + out << "Native path:" + << " " + getNativePath() << ""; auto profile = m_components->getProfile(); auto alltraits = traits(); - if(alltraits.size()) - { + if (alltraits.size()) { out << "Traits:"; - for (auto trait : alltraits) - { + for (auto trait : alltraits) { out << "traits " + trait; } out << ""; @@ -699,8 +672,7 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr auto settings = this->settings(); bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool(); bool nativeGLFW = settings->get("UseNativeGLFW").toBool(); - if (nativeOpenAL || nativeGLFW) - { + if (nativeOpenAL || nativeGLFW) { if (nativeOpenAL) out << "Using system OpenAL."; if (nativeGLFW) @@ -713,34 +685,27 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr out << "Libraries:"; QStringList jars, nativeJars; profile->getLibraryFiles(runtimeContext(), jars, nativeJars, getLocalLibraryPath(), binRoot()); - auto printLibFile = [&](const QString & path) - { + auto printLibFile = [&](const QString& path) { QFileInfo info(path); - if(info.exists()) - { + if (info.exists()) { out << " " + path; - } - else - { + } else { out << " " + path + " (missing)"; } }; - for(auto file: jars) - { + for (auto file : jars) { printLibFile(file); } out << ""; out << "Native libraries:"; - for(auto file: nativeJars) - { + for (auto file : nativeJars) { printLibFile(file); } out << ""; } - auto printModList = [&](const QString & label, ModFolderModel & model) { - if(model.size()) - { + auto printModList = [&](const QString& label, ModFolderModel& model) { + if (model.size()) { out << QString("%1:").arg(label); auto modList = model.allMods(); std::sort(modList.begin(), modList.end(), [](auto a, auto b) { @@ -748,21 +713,17 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr auto bName = b->fileinfo().completeBaseName(); return aName.localeAwareCompare(bName) < 0; }); - for(auto mod: modList) - { - if(mod->type() == ResourceType::FOLDER) - { + for (auto mod : modList) { + if (mod->type() == ResourceType::FOLDER) { out << u8" [🖿] " + mod->fileinfo().completeBaseName() + " (folder)"; continue; } - if(mod->enabled()) { + if (mod->enabled()) { out << u8" [✔] " + mod->fileinfo().completeBaseName(); - } - else { + } else { out << u8" [✘] " + mod->fileinfo().completeBaseName() + " (disabled)"; } - } out << ""; } @@ -771,20 +732,15 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr printModList("Mods", *(loaderModList().get())); printModList("Core Mods", *(coreModList().get())); - auto & jarMods = profile->getJarMods(); - if(jarMods.size()) - { + auto& jarMods = profile->getJarMods(); + if (jarMods.size()) { out << "Jar Mods:"; - for(auto & jarmod: jarMods) - { + for (auto& jarmod : jarMods) { auto displayname = jarmod->displayName(runtimeContext()); auto realname = jarmod->filename(runtimeContext()); - if(displayname != realname) - { + if (displayname != realname) { out << " " + displayname + " (" + realname + ")"; - } - else - { + } else { out << " " + realname; } } @@ -797,12 +753,9 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr out << ""; QString windowParams; - if (settings->get("LaunchMaximized").toBool()) - { + if (settings->get("LaunchMaximized").toBool()) { out << "Window size: max (if available)"; - } - else - { + } else { auto width = settings->get("MinecraftWinWidth").toInt(); auto height = settings->get("MinecraftWinHeight").toInt(); out << "Window size: " + QString::number(width) + " x " + QString::number(height); @@ -815,27 +768,23 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr QMap MinecraftInstance::createCensorFilterFromSession(AuthSessionPtr session) { - if(!session) - { + if (!session) { return QMap(); } - auto & sessionRef = *session.get(); + auto& sessionRef = *session.get(); QMap filter; - auto addToFilter = [&filter](QString key, QString value) - { - if(key.trimmed().size()) - { + auto addToFilter = [&filter](QString key, QString value) { + if (key.trimmed().size()) { filter[key] = value; } }; - if (sessionRef.session != "-") - { + if (sessionRef.session != "-") { addToFilter(sessionRef.session, tr("")); } - if (sessionRef.access_token != "offline") { + if (sessionRef.access_token != "0") { addToFilter(sessionRef.access_token, tr("")); } - if(sessionRef.client_token.size()) { + if (sessionRef.client_token.size()) { addToFilter(sessionRef.client_token, tr("")); } addToFilter(sessionRef.uuid, tr("")); @@ -843,31 +792,28 @@ QMap MinecraftInstance::createCensorFilterFromSession(AuthSess return filter; } -MessageLevel::Enum MinecraftInstance::guessLevel(const QString &line, MessageLevel::Enum level) +MessageLevel::Enum MinecraftInstance::guessLevel(const QString& line, MessageLevel::Enum level) { QRegularExpression re("\\[(?[0-9:]+)\\] \\[[^/]+/(?[^\\]]+)\\]"); auto match = re.match(line); - if(match.hasMatch()) - { + if (match.hasMatch()) { // New style logs from log4j QString timestamp = match.captured("timestamp"); QString levelStr = match.captured("level"); - if(levelStr == "INFO") + if (levelStr == "INFO") level = MessageLevel::Message; - if(levelStr == "WARN") + if (levelStr == "WARN") level = MessageLevel::Warning; - if(levelStr == "ERROR") + if (levelStr == "ERROR") level = MessageLevel::Error; - if(levelStr == "FATAL") + if (levelStr == "FATAL") level = MessageLevel::Fatal; - if(levelStr == "TRACE" || levelStr == "DEBUG") + if (levelStr == "TRACE" || levelStr == "DEBUG") level = MessageLevel::Debug; - } - else - { + } else { // Old style forge logs - if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || - line.contains("[FINER]") || line.contains("[FINEST]")) + if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || line.contains("[FINER]") || + line.contains("[FINEST]")) level = MessageLevel::Message; if (line.contains("[SEVERE]") || line.contains("[STDERR]")) level = MessageLevel::Error; @@ -878,14 +824,12 @@ MessageLevel::Enum MinecraftInstance::guessLevel(const QString &line, MessageLev } if (line.contains("overwriting existing")) return MessageLevel::Fatal; - //NOTE: this diverges from the real regexp. no unicode, the first section is + instead of * + // NOTE: this diverges from the real regexp. no unicode, the first section is + instead of * static const QString javaSymbol = "([a-zA-Z_$][a-zA-Z\\d_$]*\\.)+[a-zA-Z_$][a-zA-Z\\d_$]*"; - if (line.contains("Exception in thread") - || line.contains(QRegularExpression("\\s+at " + javaSymbol)) - || line.contains(QRegularExpression("Caused by: " + javaSymbol)) - || line.contains(QRegularExpression("([a-zA-Z_$][a-zA-Z\\d_$]*\\.)+[a-zA-Z_$]?[a-zA-Z\\d_$]*(Exception|Error|Throwable)")) - || line.contains(QRegularExpression("... \\d+ more$")) - ) + if (line.contains("Exception in thread") || line.contains(QRegularExpression("\\s+at " + javaSymbol)) || + line.contains(QRegularExpression("Caused by: " + javaSymbol)) || + line.contains(QRegularExpression("([a-zA-Z_$][a-zA-Z\\d_$]*\\.)+[a-zA-Z_$]?[a-zA-Z\\d_$]*(Exception|Error|Throwable)")) || + line.contains(QRegularExpression("... \\d+ more$"))) return MessageLevel::Error; return level; } @@ -908,14 +852,12 @@ QString MinecraftInstance::getLogFileRoot() QString MinecraftInstance::getStatusbarDescription() { QStringList traits; - if (hasVersionBroken()) - { + if (hasVersionBroken()) { traits.append(tr("broken")); } QString mcVersion = m_components->getComponentVersion("net.minecraft"); - if (mcVersion.isEmpty()) - { + if (mcVersion.isEmpty()) { // Load component info if needed m_components->reload(Net::Mode::Offline); mcVersion = m_components->getComponentVersion("net.minecraft"); @@ -923,8 +865,7 @@ QString MinecraftInstance::getStatusbarDescription() QString description; description.append(tr("Minecraft %1").arg(mcVersion)); - if(m_settings->get("ShowGameTime").toBool()) - { + if (m_settings->get("ShowGameTime").toBool()) { if (lastTimePlayed() > 0) { QDateTime lastLaunchTime = QDateTime::fromMSecsSinceEpoch(lastLaunch()); description.append(tr(", last played on %1 for %2") @@ -936,8 +877,7 @@ QString MinecraftInstance::getStatusbarDescription() description.append(tr(", total played for %1").arg(Time::prettifyDuration(totalTimePlayed()))); } } - if(hasCrashed()) - { + if (hasCrashed()) { description.append(tr(", has crashed.")); } return description; @@ -946,14 +886,11 @@ QString MinecraftInstance::getStatusbarDescription() Task::Ptr MinecraftInstance::createUpdateTask(Net::Mode mode) { updateRuntimeContext(); - switch (mode) - { - case Net::Mode::Offline: - { + switch (mode) { + case Net::Mode::Offline: { return Task::Ptr(new MinecraftLoadAndCheck(this)); } - case Net::Mode::Online: - { + case Net::Mode::Online: { return Task::Ptr(new MinecraftUpdate(this)); } } @@ -979,28 +916,17 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt process->appendStep(makeShared(pptr)); } - // check launch method - QStringList validMethods = {"LauncherPart", "DirectJava"}; - QString method = launchMethod(); - if(!validMethods.contains(method)) - { - process->appendStep(makeShared(pptr, "Selected launch method \"" + method + "\" is not valid.\n", MessageLevel::Fatal)); - return process; - } - // create the .minecraft folder and server-resource-packs (workaround for Minecraft bug MCL-3732) { process->appendStep(makeShared(pptr)); } - if (!serverToJoin && settings()->get("JoinServerOnLaunch").toBool()) - { + if (!serverToJoin && settings()->get("JoinServerOnLaunch").toBool()) { QString fullAddress = settings()->get("JoinServerOnLaunchAddress").toString(); serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(fullAddress))); } - if(serverToJoin && serverToJoin->port == 25565) - { + if (serverToJoin && serverToJoin->port == 25565) { // Resolve server address to join on launch auto step = makeShared(pptr); step->setLookupAddress(serverToJoin->address); @@ -1009,23 +935,19 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt } // run pre-launch command if that's needed - if(getPreLaunchCommand().size()) - { + if (getPreLaunchCommand().size()) { auto step = makeShared(pptr); step->setWorkingDirectory(gameRoot()); process->appendStep(step); } // if we aren't in offline mode,. - if(session->status != AuthSession::PlayableOffline) - { - if(!session->demo) { + if (session->status != AuthSession::PlayableOffline) { + if (!session->demo) { process->appendStep(makeShared(pptr, session)); } process->appendStep(makeShared(pptr, Net::Mode::Online)); - } - else - { + } else { process->appendStep(makeShared(pptr, Net::Mode::Offline)); } @@ -1061,38 +983,23 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt { // actually launch the game - auto method = launchMethod(); - if(method == "LauncherPart") - { - auto step = makeShared(pptr); - step->setWorkingDirectory(gameRoot()); - step->setAuthSession(session); - step->setServerToJoin(serverToJoin); - process->appendStep(step); - } - else if (method == "DirectJava") - { - auto step = makeShared(pptr); - step->setWorkingDirectory(gameRoot()); - step->setAuthSession(session); - step->setServerToJoin(serverToJoin); - process->appendStep(step); - } + auto step = makeShared(pptr); + step->setWorkingDirectory(gameRoot()); + step->setAuthSession(session); + step->setServerToJoin(serverToJoin); + process->appendStep(step); } // run post-exit command if that's needed - if(getPostExitCommand().size()) - { + if (getPostExitCommand().size()) { auto step = makeShared(pptr); step->setWorkingDirectory(gameRoot()); process->appendStep(step); } - if (session) - { + if (session) { process->setCensorFilter(createCensorFilterFromSession(session)); } - if(m_settings->get("QuitAfterGameStop").toBool()) - { + if (m_settings->get("QuitAfterGameStop").toBool()) { process->appendStep(makeShared(pptr)); } m_launchProcess = process; @@ -1100,11 +1007,6 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt return m_launchProcess; } -QString MinecraftInstance::launchMethod() -{ - return settings()->get("MCLaunchMethod").toString(); -} - JavaVersion MinecraftInstance::getJavaVersion() { return JavaVersion(settings()->get("JavaVersion").toString()); @@ -1139,8 +1041,7 @@ std::shared_ptr MinecraftInstance::nilModList() std::shared_ptr MinecraftInstance::resourcePackList() { - if (!m_resource_pack_list) - { + if (!m_resource_pack_list) { m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir(), this)); } return m_resource_pack_list; @@ -1148,8 +1049,7 @@ std::shared_ptr MinecraftInstance::resourcePackList() std::shared_ptr MinecraftInstance::texturePackList() { - if (!m_texture_pack_list) - { + if (!m_texture_pack_list) { m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir(), this)); } return m_texture_pack_list; @@ -1157,8 +1057,7 @@ std::shared_ptr MinecraftInstance::texturePackList() std::shared_ptr MinecraftInstance::shaderPackList() { - if (!m_shader_pack_list) - { + if (!m_shader_pack_list) { m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir(), this)); } return m_shader_pack_list; @@ -1166,8 +1065,7 @@ std::shared_ptr MinecraftInstance::shaderPackList() std::shared_ptr MinecraftInstance::worldList() { - if (!m_world_list) - { + if (!m_world_list) { m_world_list.reset(new WorldList(worldDir(), this)); } return m_world_list; @@ -1175,8 +1073,7 @@ std::shared_ptr MinecraftInstance::worldList() std::shared_ptr MinecraftInstance::gameOptionsModel() { - if (!m_game_options) - { + if (!m_game_options) { m_game_options.reset(new GameOptions(FS::PathCombine(gameRoot(), "options.txt"))); } return m_game_options; @@ -1186,8 +1083,7 @@ QList MinecraftInstance::getJarMods() const { auto profile = m_components->getProfile(); QList mods; - for (auto jarmod : profile->getJarMods()) - { + for (auto jarmod : profile->getJarMods()) { QStringList jar, temp1, temp2, temp3; jarmod->getApplicableFiles(runtimeContext(), jar, temp1, temp2, temp3, jarmodsPath().absolutePath()); // QString filePath = jarmodsPath().absoluteFilePath(jarmod->filename(currentSystem)); @@ -1196,5 +1092,4 @@ QList MinecraftInstance::getJarMods() const return mods; } - #include "MinecraftInstance.moc" diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 068b30082..c331cc6f5 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -35,12 +35,12 @@ */ #pragma once -#include "BaseInstance.h" #include -#include "minecraft/mod/Mod.h" -#include #include +#include +#include "BaseInstance.h" #include "minecraft/launch/MinecraftServerTarget.h" +#include "minecraft/mod/Mod.h" class ModFolderModel; class ResourceFolderModel; @@ -52,12 +52,11 @@ class GameOptions; class LaunchStep; class PackProfile; -class MinecraftInstance: public BaseInstance -{ +class MinecraftInstance : public BaseInstance { Q_OBJECT -public: - MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir); - virtual ~MinecraftInstance() {}; + public: + MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir); + virtual ~MinecraftInstance(){}; virtual void saveNow() override; void loadSpecificSettings() override; @@ -67,15 +66,9 @@ public: // FIXME: remove QSet traits() const override; - bool canEdit() const override - { - return true; - } + bool canEdit() const override { return true; } - bool canExport() const override - { - return true; - } + bool canExport() const override { return true; } ////// Directories and files ////// QString jarModsDir() const; @@ -143,7 +136,7 @@ public: QProcessEnvironment createLaunchEnvironment() override; /// guess log level from a line of minecraft log - MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level) override; + MessageLevel::Enum guessLevel(const QString& line, MessageLevel::Enum level) override; IPathMatcher::Ptr getLogFileMatcher() override; @@ -163,12 +156,10 @@ public: virtual JavaVersion getJavaVersion(); -protected: + protected: QMap createCensorFilterFromSession(AuthSessionPtr session); - QStringList validLaunchMethods(); - QString launchMethod(); -protected: // data + protected: // data std::shared_ptr m_components; mutable std::shared_ptr m_loader_mod_list; mutable std::shared_ptr m_core_mod_list; diff --git a/launcher/minecraft/MinecraftLoadAndCheck.cpp b/launcher/minecraft/MinecraftLoadAndCheck.cpp index 1c3f6fb71..818e90cfc 100644 --- a/launcher/minecraft/MinecraftLoadAndCheck.cpp +++ b/launcher/minecraft/MinecraftLoadAndCheck.cpp @@ -2,9 +2,7 @@ #include "MinecraftInstance.h" #include "PackProfile.h" -MinecraftLoadAndCheck::MinecraftLoadAndCheck(MinecraftInstance *inst, QObject *parent) : Task(parent), m_inst(inst) -{ -} +MinecraftLoadAndCheck::MinecraftLoadAndCheck(MinecraftInstance* inst, QObject* parent) : Task(parent), m_inst(inst) {} void MinecraftLoadAndCheck::executeTask() { @@ -13,23 +11,21 @@ void MinecraftLoadAndCheck::executeTask() components->reload(Net::Mode::Offline); m_task = components->getCurrentTask(); - if(!m_task) - { + if (!m_task) { emitSucceeded(); return; } connect(m_task.get(), &Task::succeeded, this, &MinecraftLoadAndCheck::subtaskSucceeded); connect(m_task.get(), &Task::failed, this, &MinecraftLoadAndCheck::subtaskFailed); - connect(m_task.get(), &Task::aborted, this, [this]{ subtaskFailed(tr("Aborted")); }); + connect(m_task.get(), &Task::aborted, this, [this] { subtaskFailed(tr("Aborted")); }); connect(m_task.get(), &Task::progress, this, &MinecraftLoadAndCheck::progress); - connect(m_task.get(), &Task::stepProgress, this, &MinecraftLoadAndCheck::propogateStepProgress); + connect(m_task.get(), &Task::stepProgress, this, &MinecraftLoadAndCheck::propagateStepProgress); connect(m_task.get(), &Task::status, this, &MinecraftLoadAndCheck::setStatus); } void MinecraftLoadAndCheck::subtaskSucceeded() { - if(isFinished()) - { + if (isFinished()) { qCritical() << "MinecraftUpdate: Subtask" << sender() << "succeeded, but work was already done!"; return; } @@ -38,8 +34,7 @@ void MinecraftLoadAndCheck::subtaskSucceeded() void MinecraftLoadAndCheck::subtaskFailed(QString error) { - if(isFinished()) - { + if (isFinished()) { qCritical() << "MinecraftUpdate: Subtask" << sender() << "failed, but work was already done!"; return; } diff --git a/launcher/minecraft/MinecraftLoadAndCheck.h b/launcher/minecraft/MinecraftLoadAndCheck.h index d9af3ace2..9556c1d6a 100644 --- a/launcher/minecraft/MinecraftLoadAndCheck.h +++ b/launcher/minecraft/MinecraftLoadAndCheck.h @@ -15,34 +15,32 @@ #pragma once -#include #include +#include #include -#include "tasks/Task.h" #include +#include "tasks/Task.h" #include "QObjectPtr.h" class MinecraftVersion; class MinecraftInstance; -class MinecraftLoadAndCheck : public Task -{ +class MinecraftLoadAndCheck : public Task { Q_OBJECT -public: - explicit MinecraftLoadAndCheck(MinecraftInstance *inst, QObject *parent = 0); - virtual ~MinecraftLoadAndCheck() {}; + public: + explicit MinecraftLoadAndCheck(MinecraftInstance* inst, QObject* parent = 0); + virtual ~MinecraftLoadAndCheck(){}; void executeTask() override; -private slots: + private slots: void subtaskSucceeded(); void subtaskFailed(QString error); -private: - MinecraftInstance *m_inst = nullptr; + private: + MinecraftInstance* m_inst = nullptr; Task::Ptr m_task; QString m_preFailure; QString m_fail_reason; }; - diff --git a/launcher/minecraft/MinecraftUpdate.cpp b/launcher/minecraft/MinecraftUpdate.cpp index 35430bb0f..c009317a6 100644 --- a/launcher/minecraft/MinecraftUpdate.cpp +++ b/launcher/minecraft/MinecraftUpdate.cpp @@ -16,27 +16,25 @@ #include "MinecraftUpdate.h" #include "MinecraftInstance.h" +#include #include #include #include -#include -#include "BaseInstance.h" -#include "minecraft/PackProfile.h" -#include "minecraft/Library.h" #include +#include "BaseInstance.h" +#include "minecraft/Library.h" +#include "minecraft/PackProfile.h" +#include "update/AssetUpdateTask.h" +#include "update/FMLLibrariesTask.h" #include "update/FoldersTask.h" #include "update/LibrariesTask.h" -#include "update/FMLLibrariesTask.h" -#include "update/AssetUpdateTask.h" #include #include -MinecraftUpdate::MinecraftUpdate(MinecraftInstance *inst, QObject *parent) : Task(parent), m_inst(inst) -{ -} +MinecraftUpdate::MinecraftUpdate(MinecraftInstance* inst, QObject* parent) : Task(parent), m_inst(inst) {} void MinecraftUpdate::executeTask() { @@ -51,8 +49,7 @@ void MinecraftUpdate::executeTask() auto components = m_inst->getPackProfile(); components->reload(Net::Mode::Online); auto task = components->getCurrentTask(); - if(task) - { + if (task) { m_tasks.append(task); } } @@ -72,8 +69,7 @@ void MinecraftUpdate::executeTask() m_tasks.append(makeShared(m_inst)); } - if(!m_preFailure.isEmpty()) - { + if (!m_preFailure.isEmpty()) { emitFailed(m_preFailure); return; } @@ -82,37 +78,32 @@ void MinecraftUpdate::executeTask() void MinecraftUpdate::next() { - if(m_abort) - { + if (m_abort) { emitFailed(tr("Aborted by user.")); return; } - if(m_failed_out_of_order) - { + if (m_failed_out_of_order) { emitFailed(m_fail_reason); return; } - m_currentTask ++; - if(m_currentTask > 0) - { + m_currentTask++; + if (m_currentTask > 0) { auto task = m_tasks[m_currentTask - 1]; disconnect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded); disconnect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed); disconnect(task.get(), &Task::aborted, this, &Task::abort); disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress); - disconnect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propogateStepProgress); + disconnect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propagateStepProgress); disconnect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus); disconnect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails); } - if(m_currentTask == m_tasks.size()) - { + if (m_currentTask == m_tasks.size()) { emitSucceeded(); return; } auto task = m_tasks[m_currentTask]; // if the task is already finished by the time we look at it, skip it - if(task->isFinished()) - { + if (task->isFinished()) { qCritical() << "MinecraftUpdate: Skipping finished subtask" << m_currentTask << ":" << task.get(); next(); } @@ -120,27 +111,24 @@ void MinecraftUpdate::next() connect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed); connect(task.get(), &Task::aborted, this, &Task::abort); connect(task.get(), &Task::progress, this, &MinecraftUpdate::progress); - connect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propogateStepProgress); + connect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propagateStepProgress); connect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus); connect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails); // if the task is already running, do not start it again - if(!task->isRunning()) - { + if (!task->isRunning()) { task->start(); } } void MinecraftUpdate::subtaskSucceeded() { - if(isFinished()) - { + if (isFinished()) { qCritical() << "MinecraftUpdate: Subtask" << sender() << "succeeded, but work was already done!"; return; } auto senderTask = QObject::sender(); auto currentTask = m_tasks[m_currentTask].get(); - if(senderTask != currentTask) - { + if (senderTask != currentTask) { qDebug() << "MinecraftUpdate: Subtask" << sender() << "succeeded out of order."; return; } @@ -149,15 +137,13 @@ void MinecraftUpdate::subtaskSucceeded() void MinecraftUpdate::subtaskFailed(QString error) { - if(isFinished()) - { + if (isFinished()) { qCritical() << "MinecraftUpdate: Subtask" << sender() << "failed, but work was already done!"; return; } auto senderTask = QObject::sender(); auto currentTask = m_tasks[m_currentTask].get(); - if(senderTask != currentTask) - { + if (senderTask != currentTask) { qDebug() << "MinecraftUpdate: Subtask" << sender() << "failed out of order."; m_failed_out_of_order = true; m_fail_reason = error; @@ -166,15 +152,12 @@ void MinecraftUpdate::subtaskFailed(QString error) emitFailed(error); } - bool MinecraftUpdate::abort() { - if(!m_abort) - { + if (!m_abort) { m_abort = true; auto task = m_tasks[m_currentTask]; - if(task->canAbort()) - { + if (task->canAbort()) { return task->abort(); } } diff --git a/launcher/minecraft/MinecraftUpdate.h b/launcher/minecraft/MinecraftUpdate.h index c9cf8624b..9c41d7f56 100644 --- a/launcher/minecraft/MinecraftUpdate.h +++ b/launcher/minecraft/MinecraftUpdate.h @@ -15,41 +15,39 @@ #pragma once -#include #include +#include #include +#include +#include "minecraft/VersionFilterData.h" #include "net/NetJob.h" #include "tasks/Task.h" -#include "minecraft/VersionFilterData.h" -#include class MinecraftVersion; class MinecraftInstance; // FIXME: This looks very similar to a SequentialTask. Maybe we can reduce code duplications? :^) -class MinecraftUpdate : public Task -{ +class MinecraftUpdate : public Task { Q_OBJECT -public: - explicit MinecraftUpdate(MinecraftInstance *inst, QObject *parent = 0); - virtual ~MinecraftUpdate() {}; + public: + explicit MinecraftUpdate(MinecraftInstance* inst, QObject* parent = 0); + virtual ~MinecraftUpdate(){}; void executeTask() override; bool canAbort() const override; -private -slots: + private slots: bool abort() override; void subtaskSucceeded(); void subtaskFailed(QString error); -private: + private: void next(); -private: - MinecraftInstance *m_inst = nullptr; + private: + MinecraftInstance* m_inst = nullptr; QList m_tasks; QString m_preFailure; int m_currentTask = -1; diff --git a/launcher/minecraft/MojangDownloadInfo.h b/launcher/minecraft/MojangDownloadInfo.h index 13e27e15e..855dbe005 100644 --- a/launcher/minecraft/MojangDownloadInfo.h +++ b/launcher/minecraft/MojangDownloadInfo.h @@ -1,10 +1,9 @@ #pragma once -#include #include +#include #include -struct MojangDownloadInfo -{ +struct MojangDownloadInfo { // types typedef std::shared_ptr Ptr; @@ -19,24 +18,20 @@ struct MojangDownloadInfo int size; }; - - -struct MojangLibraryDownloadInfo -{ - MojangLibraryDownloadInfo(MojangDownloadInfo::Ptr artifact): artifact(artifact) {}; - MojangLibraryDownloadInfo() {}; +struct MojangLibraryDownloadInfo { + MojangLibraryDownloadInfo(MojangDownloadInfo::Ptr artifact_) : artifact(artifact_) {} + MojangLibraryDownloadInfo() {} // types typedef std::shared_ptr Ptr; // methods - MojangDownloadInfo *getDownloadInfo(QString classifier) + MojangDownloadInfo* getDownloadInfo(QString classifier) { - if (classifier.isNull()) - { + if (classifier.isNull()) { return artifact.get(); } - + return classifiers[classifier].get(); } @@ -45,32 +40,25 @@ struct MojangLibraryDownloadInfo QMap classifiers; }; - - -struct MojangAssetIndexInfo : public MojangDownloadInfo -{ +struct MojangAssetIndexInfo : public MojangDownloadInfo { // types typedef std::shared_ptr Ptr; // methods - MojangAssetIndexInfo() - { - } + MojangAssetIndexInfo() {} - MojangAssetIndexInfo(QString id) + MojangAssetIndexInfo(QString id_) { - this->id = id; + this->id = id_; // HACK: ignore assets from other version files than Minecraft // workaround for stupid assets issue caused by amazon: // https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/ - if(id == "legacy") - { + if (id_ == "legacy") { url = "https://piston-meta.mojang.com/mc/assets/legacy/c0fd82e8ce9fbc93119e40d96d5a4e62cfa3f729/legacy.json"; } // HACK - else - { - url = "https://s3.amazonaws.com/Minecraft.Download/indexes/" + id + ".json"; + else { + url = "https://s3.amazonaws.com/Minecraft.Download/indexes/" + id_ + ".json"; } known = false; } diff --git a/launcher/minecraft/MojangVersionFormat.cpp b/launcher/minecraft/MojangVersionFormat.cpp index 623dcdfa6..6ee10ec15 100644 --- a/launcher/minecraft/MojangVersionFormat.cpp +++ b/launcher/minecraft/MojangVersionFormat.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -34,34 +34,32 @@ */ #include "MojangVersionFormat.h" -#include "OneSixVersionFormat.h" #include "MojangDownloadInfo.h" +#include "OneSixVersionFormat.h" #include "Json.h" using namespace Json; -#include "ParseUtils.h" #include +#include "ParseUtils.h" static const int CURRENT_MINIMUM_LAUNCHER_VERSION = 18; -static MojangAssetIndexInfo::Ptr assetIndexFromJson (const QJsonObject &obj); -static MojangDownloadInfo::Ptr downloadInfoFromJson (const QJsonObject &obj); -static MojangLibraryDownloadInfo::Ptr libDownloadInfoFromJson (const QJsonObject &libObj); -static QJsonObject assetIndexToJson (MojangAssetIndexInfo::Ptr assetidxinfo); -static QJsonObject libDownloadInfoToJson (MojangLibraryDownloadInfo::Ptr libinfo); -static QJsonObject downloadInfoToJson (MojangDownloadInfo::Ptr info); +static MojangAssetIndexInfo::Ptr assetIndexFromJson(const QJsonObject& obj); +static MojangDownloadInfo::Ptr downloadInfoFromJson(const QJsonObject& obj); +static MojangLibraryDownloadInfo::Ptr libDownloadInfoFromJson(const QJsonObject& libObj); +static QJsonObject assetIndexToJson(MojangAssetIndexInfo::Ptr assetidxinfo); +static QJsonObject libDownloadInfoToJson(MojangLibraryDownloadInfo::Ptr libinfo); +static QJsonObject downloadInfoToJson(MojangDownloadInfo::Ptr info); -namespace Bits +namespace Bits { +static void readString(const QJsonObject& root, const QString& key, QString& variable) { -static void readString(const QJsonObject &root, const QString &key, QString &variable) -{ - if (root.contains(key)) - { + if (root.contains(key)) { variable = requireString(root.value(key)); } } -static void readDownloadInfo(MojangDownloadInfo::Ptr out, const QJsonObject &obj) +static void readDownloadInfo(MojangDownloadInfo::Ptr out, const QJsonObject& obj) { // optional, not used readString(obj, "path", out->path); @@ -71,22 +69,22 @@ static void readDownloadInfo(MojangDownloadInfo::Ptr out, const QJsonObject &obj out->size = requireInteger(obj, "size"); } -static void readAssetIndex(MojangAssetIndexInfo::Ptr out, const QJsonObject &obj) +static void readAssetIndex(MojangAssetIndexInfo::Ptr out, const QJsonObject& obj) { out->totalSize = requireInteger(obj, "totalSize"); out->id = requireString(obj, "id"); // out->known = true; } -} +} // namespace Bits -MojangDownloadInfo::Ptr downloadInfoFromJson(const QJsonObject &obj) +MojangDownloadInfo::Ptr downloadInfoFromJson(const QJsonObject& obj) { auto out = std::make_shared(); Bits::readDownloadInfo(out, obj); return out; } -MojangAssetIndexInfo::Ptr assetIndexFromJson(const QJsonObject &obj) +MojangAssetIndexInfo::Ptr assetIndexFromJson(const QJsonObject& obj) { auto out = std::make_shared(); Bits::readDownloadInfo(out, obj); @@ -97,8 +95,7 @@ MojangAssetIndexInfo::Ptr assetIndexFromJson(const QJsonObject &obj) QJsonObject downloadInfoToJson(MojangDownloadInfo::Ptr info) { QJsonObject out; - if(!info->path.isNull()) - { + if (!info->path.isNull()) { out.insert("path", info->path); } out.insert("sha1", info->sha1); @@ -107,19 +104,16 @@ QJsonObject downloadInfoToJson(MojangDownloadInfo::Ptr info) return out; } -MojangLibraryDownloadInfo::Ptr libDownloadInfoFromJson(const QJsonObject &libObj) +MojangLibraryDownloadInfo::Ptr libDownloadInfoFromJson(const QJsonObject& libObj) { auto out = std::make_shared(); auto dlObj = requireObject(libObj.value("downloads")); - if(dlObj.contains("artifact")) - { + if (dlObj.contains("artifact")) { out->artifact = downloadInfoFromJson(requireObject(dlObj, "artifact")); } - if(dlObj.contains("classifiers")) - { + if (dlObj.contains("classifiers")) { auto classifiersObj = requireObject(dlObj, "classifiers"); - for(auto iter = classifiersObj.begin(); iter != classifiersObj.end(); iter++) - { + for (auto iter = classifiersObj.begin(); iter != classifiersObj.end(); iter++) { auto classifier = iter.key(); auto classifierObj = requireObject(iter.value()); out->classifiers[classifier] = downloadInfoFromJson(classifierObj); @@ -131,15 +125,12 @@ MojangLibraryDownloadInfo::Ptr libDownloadInfoFromJson(const QJsonObject &libObj QJsonObject libDownloadInfoToJson(MojangLibraryDownloadInfo::Ptr libinfo) { QJsonObject out; - if(libinfo->artifact) - { + if (libinfo->artifact) { out.insert("artifact", downloadInfoToJson(libinfo->artifact)); } - if(!libinfo->classifiers.isEmpty()) - { + if (!libinfo->classifiers.isEmpty()) { QJsonObject classifiersOut; - for(auto iter = libinfo->classifiers.begin(); iter != libinfo->classifiers.end(); iter++) - { + for (auto iter = libinfo->classifiers.begin(); iter != libinfo->classifiers.end(); iter++) { classifiersOut.insert(iter.key(), downloadInfoToJson(iter.value())); } out.insert("classifiers", classifiersOut); @@ -150,8 +141,7 @@ QJsonObject libDownloadInfoToJson(MojangLibraryDownloadInfo::Ptr libinfo) QJsonObject assetIndexToJson(MojangAssetIndexInfo::Ptr info) { QJsonObject out; - if(!info->path.isNull()) - { + if (!info->path.isNull()) { out.insert("path", info->path); } out.insert("sha1", info->sha1); @@ -162,76 +152,57 @@ QJsonObject assetIndexToJson(MojangAssetIndexInfo::Ptr info) return out; } -void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFile *out) +void MojangVersionFormat::readVersionProperties(const QJsonObject& in, VersionFile* out) { Bits::readString(in, "id", out->minecraftVersion); Bits::readString(in, "mainClass", out->mainClass); Bits::readString(in, "minecraftArguments", out->minecraftArguments); - if(out->minecraftArguments.isEmpty()) - { + if (out->minecraftArguments.isEmpty()) { QString processArguments; Bits::readString(in, "processArguments", processArguments); QString toCompare = processArguments.toLower(); - if (toCompare == "legacy") - { + if (toCompare == "legacy") { out->minecraftArguments = " ${auth_player_name} ${auth_session}"; - } - else if (toCompare == "username_session") - { + } else if (toCompare == "username_session") { out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}"; - } - else if (toCompare == "username_session_version") - { + } else if (toCompare == "username_session_version") { out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; - } - else if (!toCompare.isEmpty()) - { + } else if (!toCompare.isEmpty()) { out->addProblem(ProblemSeverity::Error, QObject::tr("processArguments is set to unknown value '%1'").arg(processArguments)); } } Bits::readString(in, "type", out->type); Bits::readString(in, "assets", out->assets); - if(in.contains("assetIndex")) - { + if (in.contains("assetIndex")) { out->mojangAssetIndex = assetIndexFromJson(requireObject(in, "assetIndex")); - } - else if (!out->assets.isNull()) - { + } else if (!out->assets.isNull()) { out->mojangAssetIndex = std::make_shared(out->assets); } out->releaseTime = timeFromS3Time(in.value("releaseTime").toString("")); out->updateTime = timeFromS3Time(in.value("time").toString("")); - if (in.contains("minimumLauncherVersion")) - { + if (in.contains("minimumLauncherVersion")) { out->minimumLauncherVersion = requireInteger(in.value("minimumLauncherVersion")); - if (out->minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION) - { - out->addProblem( - ProblemSeverity::Warning, - QObject::tr("The 'minimumLauncherVersion' value of this version (%1) is higher than supported by %3 (%2). It might not work properly!") - .arg(out->minimumLauncherVersion) - .arg(CURRENT_MINIMUM_LAUNCHER_VERSION) - .arg(BuildConfig.LAUNCHER_DISPLAYNAME) - ); + if (out->minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION) { + out->addProblem(ProblemSeverity::Warning, QObject::tr("The 'minimumLauncherVersion' value of this version (%1) is higher than " + "supported by %3 (%2). It might not work properly!") + .arg(out->minimumLauncherVersion) + .arg(CURRENT_MINIMUM_LAUNCHER_VERSION) + .arg(BuildConfig.LAUNCHER_DISPLAYNAME)); } } - if (in.contains("compatibleJavaMajors")) - { - for (auto compatible : requireArray(in.value("compatibleJavaMajors"))) - { + if (in.contains("compatibleJavaMajors")) { + for (auto compatible : requireArray(in.value("compatibleJavaMajors"))) { out->compatibleJavaMajors.append(requireInteger(compatible)); } } - if(in.contains("downloads")) - { + if (in.contains("downloads")) { auto downloadsObj = requireObject(in, "downloads"); - for(auto iter = downloadsObj.begin(); iter != downloadsObj.end(); iter++) - { + for (auto iter = downloadsObj.begin(); iter != downloadsObj.end(); iter++) { auto classifier = iter.key(); auto classifierObj = requireObject(iter.value()); out->mojangDownloads[classifier] = downloadInfoFromJson(classifierObj); @@ -239,15 +210,13 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFi } } -VersionFilePtr MojangVersionFormat::versionFileFromJson(const QJsonDocument &doc, const QString &filename) +VersionFilePtr MojangVersionFormat::versionFileFromJson(const QJsonDocument& doc, const QString& filename) { VersionFilePtr out(new VersionFile()); - if (doc.isEmpty() || doc.isNull()) - { + if (doc.isEmpty() || doc.isNull()) { throw JSONValidationError(filename + " is empty or null"); } - if (!doc.isObject()) - { + if (!doc.isObject()) { throw JSONValidationError(filename + " is not an object"); } @@ -260,11 +229,8 @@ VersionFilePtr MojangVersionFormat::versionFileFromJson(const QJsonDocument &doc out->version = out->minecraftVersion; // out->filename = filename; - - if (root.contains("libraries")) - { - for (auto libVal : requireArray(root.value("libraries"))) - { + if (root.contains("libraries")) { + for (auto libVal : requireArray(root.value("libraries"))) { auto libObj = requireObject(libVal); auto lib = MojangVersionFormat::libraryFromJson(*out, libObj, filename); @@ -280,52 +246,42 @@ void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObj writeString(out, "mainClass", in->mainClass); writeString(out, "minecraftArguments", in->minecraftArguments); writeString(out, "type", in->type); - if(!in->releaseTime.isNull()) - { + if (!in->releaseTime.isNull()) { writeString(out, "releaseTime", timeToS3Time(in->releaseTime)); } - if(!in->updateTime.isNull()) - { + if (!in->updateTime.isNull()) { writeString(out, "time", timeToS3Time(in->updateTime)); } - if(in->minimumLauncherVersion != -1) - { + if (in->minimumLauncherVersion != -1) { out.insert("minimumLauncherVersion", in->minimumLauncherVersion); } writeString(out, "assets", in->assets); - if(in->mojangAssetIndex && in->mojangAssetIndex->known) - { + if (in->mojangAssetIndex && in->mojangAssetIndex->known) { out.insert("assetIndex", assetIndexToJson(in->mojangAssetIndex)); } - if(!in->mojangDownloads.isEmpty()) - { + if (!in->mojangDownloads.isEmpty()) { QJsonObject downloadsOut; - for(auto iter = in->mojangDownloads.begin(); iter != in->mojangDownloads.end(); iter++) - { + for (auto iter = in->mojangDownloads.begin(); iter != in->mojangDownloads.end(); iter++) { downloadsOut.insert(iter.key(), downloadInfoToJson(iter.value())); } out.insert("downloads", downloadsOut); } - if(!in->compatibleJavaMajors.isEmpty()) - { + if (!in->compatibleJavaMajors.isEmpty()) { QJsonArray compatibleJavaMajorsOut; - for(auto compatibleJavaMajor : in->compatibleJavaMajors) - { + for (auto compatibleJavaMajor : in->compatibleJavaMajors) { compatibleJavaMajorsOut.append(compatibleJavaMajor); } out.insert("compatibleJavaMajors", compatibleJavaMajorsOut); } } -QJsonDocument MojangVersionFormat::versionFileToJson(const VersionFilePtr &patch) +QJsonDocument MojangVersionFormat::versionFileToJson(const VersionFilePtr& patch) { QJsonObject root; writeVersionProperties(patch.get(), root); - if (!patch->libraries.isEmpty()) - { + if (!patch->libraries.isEmpty()) { QJsonArray array; - for (auto value: patch->libraries) - { + for (auto value : patch->libraries) { array.append(MojangVersionFormat::libraryToJson(value.get())); } root.insert("libraries", array); @@ -339,96 +295,80 @@ QJsonDocument MojangVersionFormat::versionFileToJson(const VersionFilePtr &patch } } -LibraryPtr MojangVersionFormat::libraryFromJson(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename) +LibraryPtr MojangVersionFormat::libraryFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename) { LibraryPtr out(new Library()); - if (!libObj.contains("name")) - { + if (!libObj.contains("name")) { throw JSONValidationError(filename + "contains a library that doesn't have a 'name' field"); } auto rawName = libObj.value("name").toString(); out->m_name = rawName; - if(!out->m_name.valid()) { + if (!out->m_name.valid()) { problems.addProblem(ProblemSeverity::Error, QObject::tr("Library %1 name is broken and cannot be processed.").arg(rawName)); } Bits::readString(libObj, "url", out->m_repositoryURL); - if (libObj.contains("extract")) - { + if (libObj.contains("extract")) { out->m_hasExcludes = true; auto extractObj = requireObject(libObj.value("extract")); - for (auto excludeVal : requireArray(extractObj.value("exclude"))) - { + for (auto excludeVal : requireArray(extractObj.value("exclude"))) { out->m_extractExcludes.append(requireString(excludeVal)); } } - if (libObj.contains("natives")) - { + if (libObj.contains("natives")) { QJsonObject nativesObj = requireObject(libObj.value("natives")); - for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) - { - if (!it.value().isString()) - { + for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) { + if (!it.value().isString()) { qWarning() << filename << "contains an invalid native (skipping)"; } // FIXME: Skip unknown platforms out->m_nativeClassifiers[it.key()] = it.value().toString(); } } - if (libObj.contains("rules")) - { + if (libObj.contains("rules")) { out->applyRules = true; out->m_rules = rulesFromJsonV4(libObj); } - if (libObj.contains("downloads")) - { + if (libObj.contains("downloads")) { out->m_mojangDownloads = libDownloadInfoFromJson(libObj); } return out; } -QJsonObject MojangVersionFormat::libraryToJson(Library *library) +QJsonObject MojangVersionFormat::libraryToJson(Library* library) { QJsonObject libRoot; libRoot.insert("name", library->m_name.serialize()); - if (!library->m_repositoryURL.isEmpty()) - { + if (!library->m_repositoryURL.isEmpty()) { libRoot.insert("url", library->m_repositoryURL); } - if (library->isNative()) - { + if (library->isNative()) { QJsonObject nativeList; auto iter = library->m_nativeClassifiers.begin(); - while (iter != library->m_nativeClassifiers.end()) - { + while (iter != library->m_nativeClassifiers.end()) { nativeList.insert(iter.key(), iter.value()); iter++; } libRoot.insert("natives", nativeList); - if (!library->m_extractExcludes.isEmpty()) - { + if (!library->m_extractExcludes.isEmpty()) { QJsonArray excludes; QJsonObject extract; - for (auto exclude : library->m_extractExcludes) - { + for (auto exclude : library->m_extractExcludes) { excludes.append(exclude); } extract.insert("exclude", excludes); libRoot.insert("extract", extract); } } - if (!library->m_rules.isEmpty()) - { + if (!library->m_rules.isEmpty()) { QJsonArray allRules; - for (auto &rule : library->m_rules) - { + for (auto& rule : library->m_rules) { QJsonObject ruleObj = rule->toJson(); allRules.append(ruleObj); } libRoot.insert("rules", allRules); } - if(library->m_mojangDownloads) - { + if (library->m_mojangDownloads) { auto downloadsObj = libDownloadInfoToJson(library->m_mojangDownloads); libRoot.insert("downloads", downloadsObj); } diff --git a/launcher/minecraft/MojangVersionFormat.h b/launcher/minecraft/MojangVersionFormat.h index d38f0a2ff..88d8a206d 100644 --- a/launcher/minecraft/MojangVersionFormat.h +++ b/launcher/minecraft/MojangVersionFormat.h @@ -1,24 +1,25 @@ #pragma once -#include -#include -#include #include +#include +#include +#include -class MojangVersionFormat -{ -friend class OneSixVersionFormat; -protected: +class MojangVersionFormat { + friend class OneSixVersionFormat; + + protected: // does not include libraries static void readVersionProperties(const QJsonObject& in, VersionFile* out); // does not include libraries static void writeVersionProperties(const VersionFile* in, QJsonObject& out); -public: + + public: // version files / profile patches - static VersionFilePtr versionFileFromJson(const QJsonDocument &doc, const QString &filename); - static QJsonDocument versionFileToJson(const VersionFilePtr &patch); + static VersionFilePtr versionFileFromJson(const QJsonDocument& doc, const QString& filename); + static QJsonDocument versionFileToJson(const VersionFilePtr& patch); // libraries - static LibraryPtr libraryFromJson(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename); - static QJsonObject libraryToJson(Library *library); + static LibraryPtr libraryFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename); + static QJsonObject libraryToJson(Library* library); }; diff --git a/launcher/minecraft/OneSixVersionFormat.cpp b/launcher/minecraft/OneSixVersionFormat.cpp index b586198bf..306c95a6a 100644 --- a/launcher/minecraft/OneSixVersionFormat.cpp +++ b/launcher/minecraft/OneSixVersionFormat.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,23 +35,22 @@ #include "OneSixVersionFormat.h" #include +#include #include "minecraft/Agent.h" #include "minecraft/ParseUtils.h" -#include #include using namespace Json; -static void readString(const QJsonObject &root, const QString &key, QString &variable) +static void readString(const QJsonObject& root, const QString& key, QString& variable) { - if (root.contains(key)) - { + if (root.contains(key)) { variable = requireString(root.value(key)); } } -LibraryPtr OneSixVersionFormat::libraryFromJson(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename) +LibraryPtr OneSixVersionFormat::libraryFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename) { LibraryPtr out = MojangVersionFormat::libraryFromJson(problems, libObj, filename); readString(libObj, "MMC-hint", out->m_hint); @@ -62,7 +61,7 @@ LibraryPtr OneSixVersionFormat::libraryFromJson(ProblemContainer & problems, con return out; } -QJsonObject OneSixVersionFormat::libraryToJson(Library *library) +QJsonObject OneSixVersionFormat::libraryToJson(Library* library) { QJsonObject libRoot = MojangVersionFormat::libraryToJson(library); if (!library->m_absoluteURL.isEmpty()) @@ -76,37 +75,30 @@ QJsonObject OneSixVersionFormat::libraryToJson(Library *library) return libRoot; } -VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc, const QString &filename, const bool requireOrder) +VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument& doc, const QString& filename, const bool requireOrder) { VersionFilePtr out(new VersionFile()); - if (doc.isEmpty() || doc.isNull()) - { + if (doc.isEmpty() || doc.isNull()) { throw JSONValidationError(filename + " is empty or null"); } - if (!doc.isObject()) - { + if (!doc.isObject()) { throw JSONValidationError(filename + " is not an object"); } QJsonObject root = doc.object(); Meta::MetadataVersion formatVersion = Meta::parseFormatVersion(root, false); - switch(formatVersion) - { + switch (formatVersion) { case Meta::MetadataVersion::InitialRelease: break; case Meta::MetadataVersion::Invalid: throw JSONValidationError(filename + " does not contain a recognizable version of the metadata format."); } - if (requireOrder) - { - if (root.contains("order")) - { + if (requireOrder) { + if (root.contains("order")) { out->order = requireInteger(root.value("order")); - } - else - { + } else { // FIXME: evaluate if we don't want to throw exceptions here instead qCritical() << filename << "doesn't contain an order field"; } @@ -114,22 +106,18 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc out->name = root.value("name").toString(); - if(root.contains("uid")) - { + if (root.contains("uid")) { out->uid = root.value("uid").toString(); - } - else - { + } else { out->uid = root.value("fileId").toString(); } - const QRegularExpression valid_uid_regex{ QRegularExpression::anchoredPattern(QStringLiteral(R"([a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]+)*)")) }; + const QRegularExpression valid_uid_regex{ QRegularExpression::anchoredPattern( + QStringLiteral(R"([a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]+)*)")) }; if (!valid_uid_regex.match(out->uid).hasMatch()) { qCritical() << "The component's 'uid' contains illegal characters! UID:" << out->uid; - out->addProblem( - ProblemSeverity::Error, - QObject::tr("The component's 'uid' contains illegal characters! This can cause security issues.") - ); + out->addProblem(ProblemSeverity::Error, + QObject::tr("The component's 'uid' contains illegal characters! This can cause security issues.")); } out->version = root.value("version").toString(); @@ -139,46 +127,35 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc // added for legacy Minecraft window embedding, TODO: remove readString(root, "appletClass", out->appletClass); - if (root.contains("+tweakers")) - { - for (auto tweakerVal : requireArray(root.value("+tweakers"))) - { + if (root.contains("+tweakers")) { + for (auto tweakerVal : requireArray(root.value("+tweakers"))) { out->addTweakers.append(requireString(tweakerVal)); } } - if (root.contains("+traits")) - { - for (auto tweakerVal : requireArray(root.value("+traits"))) - { + if (root.contains("+traits")) { + for (auto tweakerVal : requireArray(root.value("+traits"))) { out->traits.insert(requireString(tweakerVal)); } } - if (root.contains("+jvmArgs")) - { - for (auto arg : requireArray(root.value("+jvmArgs"))) - { + if (root.contains("+jvmArgs")) { + for (auto arg : requireArray(root.value("+jvmArgs"))) { out->addnJvmArguments.append(requireString(arg)); } } - - if (root.contains("jarMods")) - { - for (auto libVal : requireArray(root.value("jarMods"))) - { + if (root.contains("jarMods")) { + for (auto libVal : requireArray(root.value("jarMods"))) { QJsonObject libObj = requireObject(libVal); // parse the jarmod auto lib = OneSixVersionFormat::jarModFromJson(*out, libObj, filename); // and add to jar mods out->jarMods.append(lib); } - } - else if (root.contains("+jarMods")) // DEPRECATED: old style '+jarMods' are only here for backwards compatibility + } else if (root.contains("+jarMods")) // DEPRECATED: old style '+jarMods' are only here for backwards compatibility { - for (auto libVal : requireArray(root.value("+jarMods"))) - { + for (auto libVal : requireArray(root.value("+jarMods"))) { QJsonObject libObj = requireObject(libVal); // parse the jarmod auto lib = OneSixVersionFormat::plusJarModFromJson(*out, libObj, filename, out->name); @@ -187,10 +164,8 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc } } - if (root.contains("mods")) - { - for (auto libVal : requireArray(root.value("mods"))) - { + if (root.contains("mods")) { + for (auto libVal : requireArray(root.value("mods"))) { QJsonObject libObj = requireObject(libVal); // parse the jarmod auto lib = OneSixVersionFormat::modFromJson(*out, libObj, filename); @@ -199,10 +174,8 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc } } - auto readLibs = [&](const char * which, QList & outList) - { - for (auto libVal : requireArray(root.value(which))) - { + auto readLibs = [&](const char* which, QList& outList) { + for (auto libVal : requireArray(root.value(which))) { QJsonObject libObj = requireObject(libVal); // parse the library auto lib = libraryFromJson(*out, libObj, filename); @@ -211,29 +184,23 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc }; bool hasPlusLibs = root.contains("+libraries"); bool hasLibs = root.contains("libraries"); - if (hasPlusLibs && hasLibs) - { + if (hasPlusLibs && hasLibs) { out->addProblem(ProblemSeverity::Warning, QObject::tr("Version file has both '+libraries' and 'libraries'. This is no longer supported.")); readLibs("libraries", out->libraries); readLibs("+libraries", out->libraries); - } - else if (hasLibs) - { + } else if (hasLibs) { readLibs("libraries", out->libraries); - } - else if(hasPlusLibs) - { + } else if (hasPlusLibs) { readLibs("+libraries", out->libraries); } - if(root.contains("mavenFiles")) { + if (root.contains("mavenFiles")) { readLibs("mavenFiles", out->mavenFiles); } - if(root.contains("+agents")) { - for (auto agentVal : requireArray(root.value("+agents"))) - { + if (root.contains("+agents")) { + for (auto agentVal : requireArray(root.value("+agents"))) { QJsonObject agentObj = requireObject(agentVal); auto lib = libraryFromJson(*out, agentObj, filename); @@ -246,83 +213,68 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc } // if we have mainJar, just use it - if(root.contains("mainJar")) - { + if (root.contains("mainJar")) { QJsonObject libObj = requireObject(root, "mainJar"); out->mainJar = libraryFromJson(*out, libObj, filename); } // else reconstruct it from downloads and id ... if that's available - else if(!out->minecraftVersion.isEmpty()) - { + else if (!out->minecraftVersion.isEmpty()) { auto lib = std::make_shared(); lib->setRawName(GradleSpecifier(QString("com.mojang:minecraft:%1:client").arg(out->minecraftVersion))); // we have a reliable client download, use it. - if(out->mojangDownloads.contains("client")) - { + if (out->mojangDownloads.contains("client")) { auto LibDLInfo = std::make_shared(); LibDLInfo->artifact = out->mojangDownloads["client"]; lib->setMojangDownloadInfo(LibDLInfo); } // we got nothing... - else - { + else { out->addProblem( ProblemSeverity::Error, - QObject::tr("URL for the main jar could not be determined - Mojang removed the server that we used as fallback.") - ); + QObject::tr("URL for the main jar could not be determined - Mojang removed the server that we used as fallback.")); } out->mainJar = lib; } - if (root.contains("requires")) - { + if (root.contains("requires")) { Meta::parseRequires(root, &out->m_requires); } QString dependsOnMinecraftVersion = root.value("mcVersion").toString(); - if(!dependsOnMinecraftVersion.isEmpty()) - { + if (!dependsOnMinecraftVersion.isEmpty()) { Meta::Require mcReq; mcReq.uid = "net.minecraft"; mcReq.equalsVersion = dependsOnMinecraftVersion; - if (out->m_requires.count(mcReq) == 0) - { + if (out->m_requires.count(mcReq) == 0) { out->m_requires.insert(mcReq); } } - if (root.contains("conflicts")) - { + if (root.contains("conflicts")) { Meta::parseRequires(root, &out->conflicts); } - if (root.contains("volatile")) - { + if (root.contains("volatile")) { out->m_volatile = requireBoolean(root, "volatile"); } /* removed features that shouldn't be used */ - if (root.contains("tweakers")) - { + if (root.contains("tweakers")) { out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element 'tweakers'")); } - if (root.contains("-libraries")) - { + if (root.contains("-libraries")) { out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-libraries'")); } - if (root.contains("-tweakers")) - { + if (root.contains("-tweakers")) { out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-tweakers'")); } - if (root.contains("-minecraftArguments")) - { + if (root.contains("-minecraftArguments")) { out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-minecraftArguments'")); } - if (root.contains("+minecraftArguments")) - { + if (root.contains("+minecraftArguments")) { out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '+minecraftArguments'")); } return out; } -QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch) +QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr& patch) { QJsonObject root; writeString(root, "name", patch->name); @@ -335,19 +287,16 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch MojangVersionFormat::writeVersionProperties(patch.get(), root); - if(patch->mainJar) - { + if (patch->mainJar) { root.insert("mainJar", libraryToJson(patch->mainJar.get())); } writeString(root, "appletClass", patch->appletClass); writeStringList(root, "+tweakers", patch->addTweakers); writeStringList(root, "+traits", patch->traits.values()); writeStringList(root, "+jvmArgs", patch->addnJvmArguments); - if (!patch->agents.isEmpty()) - { + if (!patch->agents.isEmpty()) { QJsonArray array; - for (auto value: patch->agents) - { + for (auto value : patch->agents) { QJsonObject agentOut = OneSixVersionFormat::libraryToJson(value->library().get()); if (!value->argument().isEmpty()) agentOut.insert("argument", value->argument()); @@ -356,52 +305,41 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch } root.insert("+agents", array); } - if (!patch->libraries.isEmpty()) - { + if (!patch->libraries.isEmpty()) { QJsonArray array; - for (auto value: patch->libraries) - { + for (auto value : patch->libraries) { array.append(OneSixVersionFormat::libraryToJson(value.get())); } root.insert("libraries", array); } - if (!patch->mavenFiles.isEmpty()) - { + if (!patch->mavenFiles.isEmpty()) { QJsonArray array; - for (auto value: patch->mavenFiles) - { + for (auto value : patch->mavenFiles) { array.append(OneSixVersionFormat::libraryToJson(value.get())); } root.insert("mavenFiles", array); } - if (!patch->jarMods.isEmpty()) - { + if (!patch->jarMods.isEmpty()) { QJsonArray array; - for (auto value: patch->jarMods) - { + for (auto value : patch->jarMods) { array.append(OneSixVersionFormat::jarModtoJson(value.get())); } root.insert("jarMods", array); } - if (!patch->mods.isEmpty()) - { + if (!patch->mods.isEmpty()) { QJsonArray array; - for (auto value: patch->jarMods) - { + for (auto value : patch->jarMods) { array.append(OneSixVersionFormat::modtoJson(value.get())); } root.insert("mods", array); } - if(!patch->m_requires.empty()) - { + if (!patch->m_requires.empty()) { Meta::serializeRequires(root, &patch->m_requires, "requires"); } - if(!patch->conflicts.empty()) - { + if (!patch->conflicts.empty()) { Meta::serializeRequires(root, &patch->conflicts, "conflicts"); } - if(patch->m_volatile) - { + if (patch->m_volatile) { root.insert("volatile", true); } // write the contents to a json document. @@ -412,17 +350,14 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch } } -LibraryPtr OneSixVersionFormat::plusJarModFromJson( - ProblemContainer & problems, - const QJsonObject &libObj, - const QString &filename, - const QString &originalName -) { +LibraryPtr OneSixVersionFormat::plusJarModFromJson([[maybe_unused]] ProblemContainer& problems, + const QJsonObject& libObj, + const QString& filename, + const QString& originalName) +{ LibraryPtr out(new Library()); - if (!libObj.contains("name")) - { - throw JSONValidationError(filename + - "contains a jarmod that doesn't have a 'name' field"); + if (!libObj.contains("name")) { + throw JSONValidationError(filename + "contains a jarmod that doesn't have a 'name' field"); } // just make up something unique on the spot for the library name. @@ -439,36 +374,32 @@ LibraryPtr OneSixVersionFormat::plusJarModFromJson( // read the original name if present - some versions did not set it // it is the original jar mod filename before it got renamed at the point of addition auto displayName = libObj.value("originalName").toString(); - if(displayName.isEmpty()) - { + if (displayName.isEmpty()) { auto fixed = originalName; fixed.remove(" (jar mod)"); out->setDisplayName(fixed); - } - else - { + } else { out->setDisplayName(displayName); } return out; } -LibraryPtr OneSixVersionFormat::jarModFromJson(ProblemContainer & problems, const QJsonObject& libObj, const QString& filename) +LibraryPtr OneSixVersionFormat::jarModFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename) { return libraryFromJson(problems, libObj, filename); } - -QJsonObject OneSixVersionFormat::jarModtoJson(Library *jarmod) +QJsonObject OneSixVersionFormat::jarModtoJson(Library* jarmod) { return libraryToJson(jarmod); } -LibraryPtr OneSixVersionFormat::modFromJson(ProblemContainer & problems, const QJsonObject& libObj, const QString& filename) +LibraryPtr OneSixVersionFormat::modFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename) { - return libraryFromJson(problems, libObj, filename); + return libraryFromJson(problems, libObj, filename); } -QJsonObject OneSixVersionFormat::modtoJson(Library *jarmod) +QJsonObject OneSixVersionFormat::modtoJson(Library* jarmod) { return libraryToJson(jarmod); } diff --git a/launcher/minecraft/OneSixVersionFormat.h b/launcher/minecraft/OneSixVersionFormat.h index 1a091d880..9bdc4a4a3 100644 --- a/launcher/minecraft/OneSixVersionFormat.h +++ b/launcher/minecraft/OneSixVersionFormat.h @@ -1,30 +1,32 @@ #pragma once -#include -#include -#include -#include #include +#include +#include +#include +#include -class OneSixVersionFormat -{ -public: +class OneSixVersionFormat { + public: // version files / profile patches - static VersionFilePtr versionFileFromJson(const QJsonDocument &doc, const QString &filename, const bool requireOrder); - static QJsonDocument versionFileToJson(const VersionFilePtr &patch); + static VersionFilePtr versionFileFromJson(const QJsonDocument& doc, const QString& filename, const bool requireOrder); + static QJsonDocument versionFileToJson(const VersionFilePtr& patch); // libraries - static LibraryPtr libraryFromJson(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename); - static QJsonObject libraryToJson(Library *library); + static LibraryPtr libraryFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename); + static QJsonObject libraryToJson(Library* library); // DEPRECATED: old 'plus' jar mods generated by the application - static LibraryPtr plusJarModFromJson(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename, const QString &originalName); + static LibraryPtr plusJarModFromJson(ProblemContainer& problems, + const QJsonObject& libObj, + const QString& filename, + const QString& originalName); // new jar mods derived from libraries - static LibraryPtr jarModFromJson(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename); - static QJsonObject jarModtoJson(Library * jarmod); + static LibraryPtr jarModFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename); + static QJsonObject jarModtoJson(Library* jarmod); // mods, also derived from libraries - static LibraryPtr modFromJson(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename); - static QJsonObject modtoJson(Library * jarmod); + static LibraryPtr modFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename); + static QJsonObject modtoJson(Library* jarmod); }; diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index e8fd21572..cf8270cd2 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -37,40 +37,37 @@ * limitations under the License. */ -#include -#include #include -#include -#include -#include +#include #include +#include +#include +#include +#include #include -#include #include +#include #include "Exception.h" -#include "minecraft/OneSixVersionFormat.h" #include "FileSystem.h" -#include "minecraft/MinecraftInstance.h" -#include "minecraft/ProfileUtils.h" #include "Json.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/OneSixVersionFormat.h" +#include "minecraft/ProfileUtils.h" +#include "ComponentUpdateTask.h" #include "PackProfile.h" #include "PackProfile_p.h" -#include "ComponentUpdateTask.h" #include "Application.h" #include "modplatform/ResourceAPI.h" -static const QMap modloaderMapping{ - {"net.minecraftforge", ResourceAPI::Forge}, - {"net.fabricmc.fabric-loader", ResourceAPI::Fabric}, - {"org.quiltmc.quilt-loader", ResourceAPI::Quilt}, - {"com.mumfrey.liteloader", ResourceAPI::LiteLoader} -}; +static const QMap modloaderMapping{ { "net.minecraftforge", ResourceAPI::Forge }, + { "net.fabricmc.fabric-loader", ResourceAPI::Fabric }, + { "org.quiltmc.quilt-loader", ResourceAPI::Quilt }, + { "com.mumfrey.liteloader", ResourceAPI::LiteLoader } }; -PackProfile::PackProfile(MinecraftInstance * instance) - : QAbstractListModel() +PackProfile::PackProfile(MinecraftInstance* instance) : QAbstractListModel() { d.reset(new PackProfileData); d->m_instance = instance; @@ -95,42 +92,35 @@ static QJsonObject componentToJsonV1(ComponentPtr component) QJsonObject obj; // critical obj.insert("uid", component->m_uid); - if(!component->m_version.isEmpty()) - { + if (!component->m_version.isEmpty()) { obj.insert("version", component->m_version); } - if(component->m_dependencyOnly) - { + if (component->m_dependencyOnly) { obj.insert("dependencyOnly", true); } - if(component->m_important) - { + if (component->m_important) { obj.insert("important", true); } - if(component->m_disabled) - { + if (component->m_disabled) { obj.insert("disabled", true); } // cached - if(!component->m_cachedVersion.isEmpty()) - { + if (!component->m_cachedVersion.isEmpty()) { obj.insert("cachedVersion", component->m_cachedVersion); } - if(!component->m_cachedName.isEmpty()) - { + if (!component->m_cachedName.isEmpty()) { obj.insert("cachedName", component->m_cachedName); } Meta::serializeRequires(obj, &component->m_cachedRequires, "cachedRequires"); Meta::serializeRequires(obj, &component->m_cachedConflicts, "cachedConflicts"); - if(component->m_cachedVolatile) - { + if (component->m_cachedVolatile) { obj.insert("cachedVolatile", true); } return obj; } -static ComponentPtr componentFromJsonV1(PackProfile * parent, const QString & componentJsonPattern, const QJsonObject &obj) +static ComponentPtr componentFromJsonV1(PackProfile* parent, const QString& componentJsonPattern, const QJsonObject& obj) { // critical auto uid = Json::requireString(obj.value("uid")); @@ -153,51 +143,44 @@ static ComponentPtr componentFromJsonV1(PackProfile * parent, const QString & co } // Save the given component container data to a file -static bool savePackProfile(const QString & filename, const ComponentContainer & container) +static bool savePackProfile(const QString& filename, const ComponentContainer& container) { QJsonObject obj; obj.insert("formatVersion", currentComponentsFileVersion); QJsonArray orderArray; - for(auto component: container) - { + for (auto component : container) { orderArray.append(componentToJsonV1(component)); } obj.insert("components", orderArray); QSaveFile outFile(filename); - if (!outFile.open(QFile::WriteOnly)) - { - qCritical() << "Couldn't open" << outFile.fileName() - << "for writing:" << outFile.errorString(); + if (!outFile.open(QFile::WriteOnly)) { + qCritical() << "Couldn't open" << outFile.fileName() << "for writing:" << outFile.errorString(); return false; } auto data = QJsonDocument(obj).toJson(QJsonDocument::Indented); - if(outFile.write(data) != data.size()) - { - qCritical() << "Couldn't write all the data into" << outFile.fileName() - << "because:" << outFile.errorString(); + if (outFile.write(data) != data.size()) { + qCritical() << "Couldn't write all the data into" << outFile.fileName() << "because:" << outFile.errorString(); return false; } - if(!outFile.commit()) - { - qCritical() << "Couldn't save" << outFile.fileName() - << "because:" << outFile.errorString(); + if (!outFile.commit()) { + qCritical() << "Couldn't save" << outFile.fileName() << "because:" << outFile.errorString(); } return true; } // Read the given file into component containers -static bool loadPackProfile(PackProfile * parent, const QString & filename, const QString & componentJsonPattern, ComponentContainer & container) +static bool loadPackProfile(PackProfile* parent, + const QString& filename, + const QString& componentJsonPattern, + ComponentContainer& container) { QFile componentsFile(filename); - if (!componentsFile.exists()) - { + if (!componentsFile.exists()) { qWarning() << "Components file doesn't exist. This should never happen."; return false; } - if (!componentsFile.open(QFile::ReadOnly)) - { - qCritical() << "Couldn't open" << componentsFile.fileName() - << " for reading:" << componentsFile.errorString(); + if (!componentsFile.open(QFile::ReadOnly)) { + qCritical() << "Couldn't open" << componentsFile.fileName() << " for reading:" << componentsFile.errorString(); qWarning() << "Ignoring overriden order"; return false; } @@ -205,33 +188,26 @@ static bool loadPackProfile(PackProfile * parent, const QString & filename, cons // and it's valid JSON QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(componentsFile.readAll(), &error); - if (error.error != QJsonParseError::NoError) - { + if (error.error != QJsonParseError::NoError) { qCritical() << "Couldn't parse" << componentsFile.fileName() << ":" << error.errorString(); qWarning() << "Ignoring overriden order"; return false; } // and then read it and process it if all above is true. - try - { + try { auto obj = Json::requireObject(doc); // check order file version. auto version = Json::requireInteger(obj.value("formatVersion")); - if (version != currentComponentsFileVersion) - { - throw JSONValidationError(QObject::tr("Invalid component file version, expected %1") - .arg(currentComponentsFileVersion)); + if (version != currentComponentsFileVersion) { + throw JSONValidationError(QObject::tr("Invalid component file version, expected %1").arg(currentComponentsFileVersion)); } auto orderArray = Json::requireArray(obj.value("components")); - for(auto item: orderArray) - { - auto obj = Json::requireObject(item, "Component must be an object."); - container.append(componentFromJsonV1(parent, componentJsonPattern, obj)); + for (auto item : orderArray) { + auto comp_obj = Json::requireObject(item, "Component must be an object."); + container.append(componentFromJsonV1(parent, componentJsonPattern, comp_obj)); } - } - catch (const JSONValidationError &err) - { + } catch ([[maybe_unused]] const JSONValidationError& err) { qCritical() << "Couldn't parse" << componentsFile.fileName() << ": bad file format"; container.clear(); return false; @@ -245,8 +221,7 @@ static bool loadPackProfile(PackProfile * parent, const QString & filename, cons void PackProfile::saveNow() { - if(saveIsScheduled()) - { + if (saveIsScheduled()) { d->m_saveTimer.stop(); save_internal(); } @@ -265,13 +240,11 @@ void PackProfile::buildingFromScratch() void PackProfile::scheduleSave() { - if(!d->loaded) - { + if (!d->loaded) { qDebug() << "Component list should never save if it didn't successfully load, instance:" << d->m_instance->name(); return; } - if(!d->dirty) - { + if (!d->dirty) { d->dirty = true; qDebug() << "Component list save is scheduled for" << d->m_instance->name(); } @@ -312,26 +285,20 @@ bool PackProfile::load() // load the new component list and swap it with the current one... ComponentContainer newComponents; - if(!loadPackProfile(this, filename, patchesPattern(), newComponents)) - { + if (!loadPackProfile(this, filename, patchesPattern(), newComponents)) { qCritical() << "Failed to load the component config for instance" << d->m_instance->name(); return false; - } - else - { + } else { // FIXME: actually use fine-grained updates, not this... beginResetModel(); // disconnect all the old components - for(auto component: d->components) - { + for (auto component : d->components) { disconnect(component.get(), &Component::dataChanged, this, &PackProfile::componentDataChanged); } d->components.clear(); d->componentIndex.clear(); - for(auto component: newComponents) - { - if(d->componentIndex.contains(component->m_uid)) - { + for (auto component : newComponents) { + if (d->componentIndex.contains(component->m_uid)) { qWarning() << "Ignoring duplicate component entry" << component->m_uid; continue; } @@ -348,8 +315,7 @@ bool PackProfile::load() void PackProfile::reload(Net::Mode netmode) { // Do not reload when the update/resolve task is running. It is in control. - if(d->m_updateTask) - { + if (d->m_updateTask) { return; } @@ -359,8 +325,7 @@ void PackProfile::reload(Net::Mode netmode) // FIXME: differentiate when a reapply is required by propagating state from components invalidateLaunchProfile(); - if(load()) - { + if (load()) { resolve(netmode); } } @@ -376,11 +341,10 @@ void PackProfile::resolve(Net::Mode netmode) d->m_updateTask.reset(updateTask); connect(updateTask, &ComponentUpdateTask::succeeded, this, &PackProfile::updateSucceeded); connect(updateTask, &ComponentUpdateTask::failed, this, &PackProfile::updateFailed); - connect(updateTask, &ComponentUpdateTask::aborted, this, [this]{ updateFailed(tr("Aborted")); }); + connect(updateTask, &ComponentUpdateTask::aborted, this, [this] { updateFailed(tr("Aborted")); }); d->m_updateTask->start(); } - void PackProfile::updateSucceeded() { qDebug() << "Component list update/resolve task succeeded for" << d->m_instance->name(); @@ -405,17 +369,15 @@ void PackProfile::appendComponent(ComponentPtr component) void PackProfile::insertComponent(size_t index, ComponentPtr component) { auto id = component->getID(); - if(id.isEmpty()) - { + if (id.isEmpty()) { qWarning() << "Attempt to add a component with empty ID!"; return; } - if(d->componentIndex.contains(id)) - { + if (d->componentIndex.contains(id)) { qWarning() << "Attempt to add a component that is already present!"; return; } - beginInsertRows(QModelIndex(), index, index); + beginInsertRows(QModelIndex(), static_cast(index), static_cast(index)); d->components.insert(index, component); d->componentIndex[id] = component; endInsertRows(); @@ -425,41 +387,36 @@ void PackProfile::insertComponent(size_t index, ComponentPtr component) void PackProfile::componentDataChanged() { - auto objPtr = qobject_cast(sender()); - if(!objPtr) - { - qWarning() << "PackProfile got dataChenged signal from a non-Component!"; + auto objPtr = qobject_cast(sender()); + if (!objPtr) { + qWarning() << "PackProfile got dataChanged signal from a non-Component!"; return; } - if(objPtr->getID() == "net.minecraft") { + if (objPtr->getID() == "net.minecraft") { emit minecraftChanged(); } // figure out which one is it... in a seriously dumb way. int index = 0; - for (auto component: d->components) - { - if(component.get() == objPtr) - { + for (auto component : d->components) { + if (component.get() == objPtr) { emit dataChanged(createIndex(index, 0), createIndex(index, columnCount(QModelIndex()) - 1)); scheduleSave(); return; } index++; } - qWarning() << "PackProfile got dataChenged signal from a Component which does not belong to it!"; + qWarning() << "PackProfile got dataChanged signal from a Component which does not belong to it!"; } bool PackProfile::remove(const int index) { auto patch = getComponent(index); - if (!patch->isRemovable()) - { + if (!patch->isRemovable()) { qWarning() << "Patch" << patch->getID() << "is non-removable"; return false; } - if(!removeComponent_internal(patch)) - { + if (!removeComponent_internal(patch)) { qCritical() << "Patch" << patch->getID() << "could not be removed"; return false; } @@ -476,10 +433,8 @@ bool PackProfile::remove(const int index) bool PackProfile::remove(const QString id) { int i = 0; - for (auto patch : d->components) - { - if (patch->getID() == id) - { + for (auto patch : d->components) { + if (patch->getID() == id) { return remove(i); } i++; @@ -490,13 +445,11 @@ bool PackProfile::remove(const QString id) bool PackProfile::customize(int index) { auto patch = getComponent(index); - if (!patch->isCustomizable()) - { + if (!patch->isCustomizable()) { qDebug() << "Patch" << patch->getID() << "is not customizable"; return false; } - if(!patch->customize()) - { + if (!patch->customize()) { qCritical() << "Patch" << patch->getID() << "could not be customized"; return false; } @@ -508,13 +461,11 @@ bool PackProfile::customize(int index) bool PackProfile::revertToBase(int index) { auto patch = getComponent(index); - if (!patch->isRevertible()) - { + if (!patch->isRevertible()) { qDebug() << "Patch" << patch->getID() << "is not revertible"; return false; } - if(!patch->revert()) - { + if (!patch->revert()) { qCritical() << "Patch" << patch->getID() << "could not be reverted"; return false; } @@ -523,26 +474,24 @@ bool PackProfile::revertToBase(int index) return true; } -ComponentPtr PackProfile::getComponent(const QString &id) +ComponentPtr PackProfile::getComponent(const QString& id) { auto iter = d->componentIndex.find(id); - if (iter == d->componentIndex.end()) - { + if (iter == d->componentIndex.end()) { return nullptr; } return (*iter); } -ComponentPtr PackProfile::getComponent(int index) +ComponentPtr PackProfile::getComponent(size_t index) { - if(index < 0 || index >= d->components.size()) - { + if (index >= static_cast(d->components.size())) { return nullptr; } return d->components[index]; } -QVariant PackProfile::data(const QModelIndex &index, int role) const +QVariant PackProfile::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); @@ -555,79 +504,58 @@ QVariant PackProfile::data(const QModelIndex &index, int role) const auto patch = d->components.at(row); - switch (role) - { - case Qt::CheckStateRole: - { - switch (column) - { - case NameColumn: { - return patch->isEnabled() ? Qt::Checked : Qt::Unchecked; - } - default: - return QVariant(); - } - } - case Qt::DisplayRole: - { - switch (column) - { - case NameColumn: - return patch->getName(); - case VersionColumn: - { - if(patch->isCustom()) - { - return QString("%1 (Custom)").arg(patch->getVersion()); - } - else - { - return patch->getVersion(); - } - } - default: - return QVariant(); - } - } - case Qt::DecorationRole: - { - switch(column) - { - case NameColumn: - { - auto severity = patch->getProblemSeverity(); - switch (severity) - { - case ProblemSeverity::Warning: - return "warning"; - case ProblemSeverity::Error: - return "error"; + switch (role) { + case Qt::CheckStateRole: { + switch (column) { + case NameColumn: { + return patch->isEnabled() ? Qt::Checked : Qt::Unchecked; + } default: return QVariant(); } } - default: - { + case Qt::DisplayRole: { + switch (column) { + case NameColumn: + return patch->getName(); + case VersionColumn: { + if (patch->isCustom()) { + return QString("%1 (Custom)").arg(patch->getVersion()); + } else { + return patch->getVersion(); + } + } + default: + return QVariant(); + } + } + case Qt::DecorationRole: { + if (column == NameColumn) { + auto severity = patch->getProblemSeverity(); + switch (severity) { + case ProblemSeverity::Warning: + return "warning"; + case ProblemSeverity::Error: + return "error"; + default: + return QVariant(); + } + } return QVariant(); } - } - } } return QVariant(); } -bool PackProfile::setData(const QModelIndex& index, const QVariant& value, int role) +bool PackProfile::setData(const QModelIndex& index, [[maybe_unused]] const QVariant& value, int role) { - if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index.parent())) - { + if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index.parent())) { return false; } - if (role == Qt::CheckStateRole) - { + if (role == Qt::CheckStateRole) { auto component = d->components[index.row()]; - if (component->setEnabled(!component->isEnabled())) - { + if (component->setEnabled(!component->isEnabled())) { return true; } } @@ -636,18 +564,15 @@ bool PackProfile::setData(const QModelIndex& index, const QVariant& value, int r QVariant PackProfile::headerData(int section, Qt::Orientation orientation, int role) const { - if (orientation == Qt::Horizontal) - { - if (role == Qt::DisplayRole) - { - switch (section) - { - case NameColumn: - return tr("Name"); - case VersionColumn: - return tr("Version"); - default: - return QVariant(); + if (orientation == Qt::Horizontal) { + if (role == Qt::DisplayRole) { + switch (section) { + case NameColumn: + return tr("Name"); + case VersionColumn: + return tr("Version"); + default: + return QVariant(); } } } @@ -655,7 +580,7 @@ QVariant PackProfile::headerData(int section, Qt::Orientation orientation, int r } // FIXME: zero precision mess -Qt::ItemFlags PackProfile::flags(const QModelIndex &index) const +Qt::ItemFlags PackProfile::flags(const QModelIndex& index) const { if (!index.isValid()) { return Qt::NoItemFlags; @@ -671,19 +596,18 @@ Qt::ItemFlags PackProfile::flags(const QModelIndex &index) const auto patch = d->components.at(row); // TODO: this will need fine-tuning later... - if(patch->canBeDisabled() && !d->interactionDisabled) - { + if (patch->canBeDisabled() && !d->interactionDisabled) { outFlags |= Qt::ItemIsUserCheckable; } return outFlags; } -int PackProfile::rowCount(const QModelIndex &parent) const +int PackProfile::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : d->components.size(); } -int PackProfile::columnCount(const QModelIndex &parent) const +int PackProfile::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : NUM_COLUMNS; } @@ -691,12 +615,9 @@ int PackProfile::columnCount(const QModelIndex &parent) const void PackProfile::move(const int index, const MoveDirection direction) { int theirIndex; - if (direction == MoveUp) - { + if (direction == MoveUp) { theirIndex = index - 1; - } - else - { + } else { theirIndex = index + 1; } @@ -713,8 +634,7 @@ void PackProfile::move(const int index, const MoveDirection direction) auto from = getComponent(index); auto to = getComponent(theirIndex); - if (!from || !to || !to->isMoveable() || !from->isMoveable()) - { + if (!from || !to || !to->isMoveable() || !from->isMoveable()) { return; } beginMoveRows(QModelIndex(), index, index, QModelIndex(), togap); @@ -782,8 +702,7 @@ void PackProfile::installAgents(QStringList selectedFiles) bool PackProfile::installEmpty(const QString& uid, const QString& name) { QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches"); - if(!FS::ensureFolderPathExists(patchDir)) - { + if (!FS::ensureFolderPathExists(patchDir)) { return false; } auto f = std::make_shared(); @@ -792,10 +711,8 @@ bool PackProfile::installEmpty(const QString& uid, const QString& name) f->version = "1"; QString patchFileName = FS::PathCombine(patchDir, uid + ".json"); QFile file(patchFileName); - if (!file.open(QFile::WriteOnly)) - { - qCritical() << "Error opening" << file.fileName() - << "for reading:" << file.errorString(); + if (!file.open(QFile::WriteOnly)) { + qCritical() << "Error opening" << file.fileName() << "for reading:" << file.errorString(); return false; } file.write(OneSixVersionFormat::versionFileToJson(f).toJson()); @@ -812,31 +729,25 @@ bool PackProfile::removeComponent_internal(ComponentPtr patch) bool ok = true; // first, remove the patch file. this ensures it's not used anymore auto fileName = patch->getFilename(); - if(fileName.size()) - { + if (fileName.size()) { QFile patchFile(fileName); - if(patchFile.exists() && !patchFile.remove()) - { + if (patchFile.exists() && !patchFile.remove()) { qCritical() << "File" << fileName << "could not be removed because:" << patchFile.errorString(); return false; } } // FIXME: we need a generic way of removing local resources, not just jar mods... - auto preRemoveJarMod = [&](LibraryPtr jarMod) -> bool - { - if (!jarMod->isLocal()) - { + auto preRemoveJarMod = [&](LibraryPtr jarMod) -> bool { + if (!jarMod->isLocal()) { return true; } QStringList jar, temp1, temp2, temp3; jarMod->getApplicableFiles(d->m_instance->runtimeContext(), jar, temp1, temp2, temp3, d->m_instance->jarmodsPath().absolutePath()); - QFileInfo finfo (jar[0]); - if(finfo.exists()) - { + QFileInfo finfo(jar[0]); + if (finfo.exists()) { QFile jarModFile(jar[0]); - if(!jarModFile.remove()) - { + if (!jarModFile.remove()) { qCritical() << "File" << jar[0] << "could not be removed because:" << jarModFile.errorString(); return false; } @@ -846,11 +757,9 @@ bool PackProfile::removeComponent_internal(ComponentPtr patch) }; auto vFile = patch->getVersionFile(); - if(vFile) - { - auto &jarMods = vFile->jarMods; - for(auto &jarmod: jarMods) - { + if (vFile) { + auto& jarMods = vFile->jarMods; + for (auto& jarmod : jarMods) { ok &= preRemoveJarMod(jarmod); } } @@ -860,18 +769,15 @@ bool PackProfile::removeComponent_internal(ComponentPtr patch) bool PackProfile::installJarMods_internal(QStringList filepaths) { QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches"); - if(!FS::ensureFolderPathExists(patchDir)) - { + if (!FS::ensureFolderPathExists(patchDir)) { return false; } - if (!FS::ensureFolderPathExists(d->m_instance->jarModsDir())) - { + if (!FS::ensureFolderPathExists(d->m_instance->jarModsDir())) { return false; } - for(auto filepath:filepaths) - { + for (auto filepath : filepaths) { QFileInfo sourceInfo(filepath); QString id = QUuid::createUuid().toString(QUuid::WithoutBraces); QString target_filename = id + ".jar"; @@ -882,8 +788,7 @@ bool PackProfile::installJarMods_internal(QStringList filepaths) QFileInfo targetInfo(finalPath); Q_ASSERT(!targetInfo.exists()); - if (!QFile::copy(sourceInfo.absoluteFilePath(),QFileInfo(finalPath).absoluteFilePath())) - { + if (!QFile::copy(sourceInfo.absoluteFilePath(), QFileInfo(finalPath).absoluteFilePath())) { return false; } @@ -899,10 +804,8 @@ bool PackProfile::installJarMods_internal(QStringList filepaths) QString patchFileName = FS::PathCombine(patchDir, target_id + ".json"); QFile file(patchFileName); - if (!file.open(QFile::WriteOnly)) - { - qCritical() << "Error opening" << file.fileName() - << "for reading:" << file.errorString(); + if (!file.open(QFile::WriteOnly)) { + qCritical() << "Error opening" << file.fileName() << "for reading:" << file.errorString(); return false; } file.write(OneSixVersionFormat::versionFileToJson(f).toJson()); @@ -918,14 +821,12 @@ bool PackProfile::installJarMods_internal(QStringList filepaths) bool PackProfile::installCustomJar_internal(QString filepath) { QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches"); - if(!FS::ensureFolderPathExists(patchDir)) - { + if (!FS::ensureFolderPathExists(patchDir)) { return false; } QString libDir = d->m_instance->getLocalLibraryPath(); - if (!FS::ensureFolderPathExists(libDir)) - { + if (!FS::ensureFolderPathExists(libDir)) { return false; } @@ -937,15 +838,12 @@ bool PackProfile::installCustomJar_internal(QString filepath) QString finalPath = FS::PathCombine(libDir, target_filename); QFileInfo jarInfo(finalPath); - if (jarInfo.exists()) - { - if(!QFile::remove(finalPath)) - { + if (jarInfo.exists()) { + if (!QFile::remove(finalPath)) { return false; } } - if (!QFile::copy(filepath, finalPath)) - { + if (!QFile::copy(filepath, finalPath)) { return false; } @@ -960,10 +858,8 @@ bool PackProfile::installCustomJar_internal(QString filepath) QString patchFileName = FS::PathCombine(patchDir, target_id + ".json"); QFile file(patchFileName); - if (!file.open(QFile::WriteOnly)) - { - qCritical() << "Error opening" << file.fileName() - << "for reading:" << file.errorString(); + if (!file.open(QFile::WriteOnly)) { + qCritical() << "Error opening" << file.fileName() << "for reading:" << file.errorString(); return false; } file.write(OneSixVersionFormat::versionFileToJson(f).toJson()); @@ -1036,20 +932,15 @@ bool PackProfile::installAgents_internal(QStringList filepaths) std::shared_ptr PackProfile::getProfile() const { - if(!d->m_profile) - { - try - { + if (!d->m_profile) { + try { auto profile = std::make_shared(); - for(auto file: d->components) - { + for (auto file : d->components) { qDebug() << "Applying" << file->getID() << (file->getProblemSeverity() == ProblemSeverity::Error ? "ERROR" : "GOOD"); file->applyTo(profile.get()); } d->m_profile = profile; - } - catch (const Exception &error) - { + } catch (const Exception& error) { qWarning() << "Couldn't apply profile patches because: " << error.cause(); } } @@ -1059,20 +950,16 @@ std::shared_ptr PackProfile::getProfile() const bool PackProfile::setComponentVersion(const QString& uid, const QString& version, bool important) { auto iter = d->componentIndex.find(uid); - if(iter != d->componentIndex.end()) - { + if (iter != d->componentIndex.end()) { ComponentPtr component = *iter; // set existing - if(component->revert()) - { + if (component->revert()) { component->setVersion(version); component->setImportant(important); return true; } return false; - } - else - { + } else { // add new auto component = makeShared(this, uid); component->m_version = version; @@ -1085,8 +972,7 @@ bool PackProfile::setComponentVersion(const QString& uid, const QString& version QString PackProfile::getComponentVersion(const QString& uid) const { const auto iter = d->componentIndex.find(uid); - if (iter != d->componentIndex.end()) - { + if (iter != d->componentIndex.end()) { return (*iter)->getVersion(); } return QString(); @@ -1094,10 +980,10 @@ QString PackProfile::getComponentVersion(const QString& uid) const void PackProfile::disableInteraction(bool disable) { - if(d->interactionDisabled != disable) { + if (d->interactionDisabled != disable) { d->interactionDisabled = disable; auto size = d->components.size(); - if(size) { + if (size) { emit dataChanged(index(0), index(size - 1)); } } diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h index d144d875c..ce44fa588 100644 --- a/launcher/minecraft/PackProfile.h +++ b/launcher/minecraft/PackProfile.h @@ -41,44 +41,39 @@ #include -#include #include +#include #include -#include "Library.h" -#include "LaunchProfile.h" -#include "Component.h" -#include "ProfileUtils.h" #include "BaseVersion.h" +#include "Component.h" +#include "LaunchProfile.h" +#include "Library.h" #include "MojangDownloadInfo.h" -#include "net/Mode.h" +#include "ProfileUtils.h" #include "modplatform/ResourceAPI.h" +#include "net/Mode.h" class MinecraftInstance; struct PackProfileData; class ComponentUpdateTask; -class PackProfile : public QAbstractListModel -{ +class PackProfile : public QAbstractListModel { Q_OBJECT friend ComponentUpdateTask; -public: - enum Columns - { - NameColumn = 0, - VersionColumn, - NUM_COLUMNS - }; - explicit PackProfile(MinecraftInstance * instance); + public: + enum Columns { NameColumn = 0, VersionColumn, NUM_COLUMNS }; + + explicit PackProfile(MinecraftInstance* instance); virtual ~PackProfile(); - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; - virtual int columnCount(const QModelIndex &parent) const override; - virtual Qt::ItemFlags flags(const QModelIndex &index) const override; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + virtual int columnCount(const QModelIndex& parent) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; /// call this to explicitly mark the component list as loaded - this is used to build a new component list from scratch. void buildingFromScratch(); @@ -121,15 +116,15 @@ public: std::shared_ptr getProfile() const; // NOTE: used ONLY by MinecraftInstance to provide legacy version mappings from instance config - void setOldConfigVersion(const QString &uid, const QString &version); + void setOldConfigVersion(const QString& uid, const QString& version); - QString getComponentVersion(const QString &uid) const; + QString getComponentVersion(const QString& uid) const; - bool setComponentVersion(const QString &uid, const QString &version, bool important = false); + bool setComponentVersion(const QString& uid, const QString& version, bool important = false); - bool installEmpty(const QString &uid, const QString &name); + bool installEmpty(const QString& uid, const QString& name); - QString patchFilePathForUid(const QString &uid) const; + QString patchFilePathForUid(const QString& uid) const; /// if there is a save scheduled, do it now. void saveNow(); @@ -137,15 +132,15 @@ public: /// helper method, returns RuntimeContext of instance RuntimeContext runtimeContext(); -signals: + signals: void minecraftChanged(); -public: + public: /// get the profile component by id - ComponentPtr getComponent(const QString &id); + ComponentPtr getComponent(const QString& id); /// get the profile component by index - ComponentPtr getComponent(int index); + ComponentPtr getComponent(size_t index); /// Add the component to the internal list of patches // todo(merged): is this the best approach @@ -153,7 +148,7 @@ public: std::optional getModLoaders(); -private: + private: void scheduleSave(); bool saveIsScheduled() const; @@ -166,21 +161,20 @@ private: QString componentsFilePath() const; QString patchesPattern() const; -private slots: + private slots: void save_internal(); void updateSucceeded(); - void updateFailed(const QString & error); + void updateFailed(const QString& error); void componentDataChanged(); void disableInteraction(bool disable); -private: + private: bool load(); bool installJarMods_internal(QStringList filepaths); bool installCustomJar_internal(QString filepath); bool installAgents_internal(QStringList filepaths); bool removeComponent_internal(ComponentPtr patch); -private: /* data */ - + private: /* data */ std::unique_ptr d; }; diff --git a/launcher/minecraft/PackProfile_p.h b/launcher/minecraft/PackProfile_p.h index 715e04606..0cd4fb839 100644 --- a/launcher/minecraft/PackProfile_p.h +++ b/launcher/minecraft/PackProfile_p.h @@ -1,19 +1,18 @@ #pragma once -#include "Component.h" -#include -#include #include #include +#include +#include +#include "Component.h" class MinecraftInstance; using ComponentContainer = QList; using ComponentIndex = QMap; -struct PackProfileData -{ +struct PackProfileData { // the instance this belongs to - MinecraftInstance *m_instance; + MinecraftInstance* m_instance; // the launch profile (volatile, temporary thing created on demand) std::shared_ptr m_profile; @@ -27,4 +26,3 @@ struct PackProfileData bool loaded = false; bool interactionDisabled = true; }; - diff --git a/launcher/minecraft/ParseUtils.cpp b/launcher/minecraft/ParseUtils.cpp index c9640e776..fabc57a65 100644 --- a/launcher/minecraft/ParseUtils.cpp +++ b/launcher/minecraft/ParseUtils.cpp @@ -1,7 +1,7 @@ -#include -#include #include "ParseUtils.h" +#include #include +#include #include QDateTime timeFromS3Time(QString str) @@ -22,7 +22,7 @@ QString timeToS3Time(QDateTime time) int offsetMinutes = offsetAbs % 3600; offsetAbs -= offsetMinutes; offsetMinutes /= 60; - + int offsetHours = offsetAbs / 3600; QString raw = time.toString("yyyy-MM-ddTHH:mm:ss"); diff --git a/launcher/minecraft/ParseUtils.h b/launcher/minecraft/ParseUtils.h index aad82748d..c81d8f8bb 100644 --- a/launcher/minecraft/ParseUtils.h +++ b/launcher/minecraft/ParseUtils.h @@ -1,6 +1,6 @@ #pragma once -#include #include +#include /// take the timestamp used by S3 and turn it into QDateTime QDateTime timeFromS3Time(QString str); diff --git a/launcher/minecraft/ProfileUtils.cpp b/launcher/minecraft/ProfileUtils.cpp index 03f8c1989..d56ed14bd 100644 --- a/launcher/minecraft/ProfileUtils.cpp +++ b/launcher/minecraft/ProfileUtils.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -34,33 +34,29 @@ */ #include "ProfileUtils.h" -#include "minecraft/VersionFilterData.h" -#include "minecraft/OneSixVersionFormat.h" -#include "Json.h" #include +#include "Json.h" +#include "minecraft/OneSixVersionFormat.h" +#include "minecraft/VersionFilterData.h" -#include #include +#include #include #include -namespace ProfileUtils -{ +namespace ProfileUtils { static const int currentOrderFileVersion = 1; -bool readOverrideOrders(QString path, PatchOrder &order) +bool readOverrideOrders(QString path, PatchOrder& order) { QFile orderFile(path); - if (!orderFile.exists()) - { + if (!orderFile.exists()) { qWarning() << "Order file doesn't exist. Ignoring."; return false; } - if (!orderFile.open(QFile::ReadOnly)) - { - qCritical() << "Couldn't open" << orderFile.fileName() - << " for reading:" << orderFile.errorString(); + if (!orderFile.open(QFile::ReadOnly)) { + qCritical() << "Couldn't open" << orderFile.fileName() << " for reading:" << orderFile.errorString(); qWarning() << "Ignoring overriden order"; return false; } @@ -68,32 +64,25 @@ bool readOverrideOrders(QString path, PatchOrder &order) // and it's valid JSON QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll(), &error); - if (error.error != QJsonParseError::NoError) - { + if (error.error != QJsonParseError::NoError) { qCritical() << "Couldn't parse" << orderFile.fileName() << ":" << error.errorString(); qWarning() << "Ignoring overriden order"; return false; } // and then read it and process it if all above is true. - try - { + try { auto obj = Json::requireObject(doc); // check order file version. auto version = Json::requireInteger(obj.value("version")); - if (version != currentOrderFileVersion) - { - throw JSONValidationError(QObject::tr("Invalid order file version, expected %1") - .arg(currentOrderFileVersion)); + if (version != currentOrderFileVersion) { + throw JSONValidationError(QObject::tr("Invalid order file version, expected %1").arg(currentOrderFileVersion)); } auto orderArray = Json::requireArray(obj.value("order")); - for(auto item: orderArray) - { + for (auto item : orderArray) { order.append(Json::requireString(item)); } - } - catch (const JSONValidationError &err) - { + } catch ([[maybe_unused]] const JSONValidationError& err) { qCritical() << "Couldn't parse" << orderFile.fileName() << ": bad file format"; qWarning() << "Ignoring overriden order"; order.clear(); @@ -111,23 +100,19 @@ static VersionFilePtr createErrorVersionFile(QString fileId, QString filepath, Q return outError; } -static VersionFilePtr guardedParseJson(const QJsonDocument & doc,const QString &fileId,const QString &filepath,const bool &requireOrder) +static VersionFilePtr guardedParseJson(const QJsonDocument& doc, const QString& fileId, const QString& filepath, const bool& requireOrder) { - try - { + try { return OneSixVersionFormat::versionFileFromJson(doc, filepath, requireOrder); - } - catch (const Exception &e) - { + } catch (const Exception& e) { return createErrorVersionFile(fileId, filepath, e.cause()); } } -VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder) +VersionFilePtr parseJsonFile(const QFileInfo& fileInfo, const bool requireOrder) { QFile file(fileInfo.absoluteFilePath()); - if (!file.open(QFile::ReadOnly)) - { + if (!file.open(QFile::ReadOnly)) { auto errorStr = QObject::tr("Unable to open the version file %1: %2.").arg(fileInfo.fileName(), file.errorString()); return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr); } @@ -135,14 +120,11 @@ VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder) auto data = file.readAll(); QJsonDocument doc = QJsonDocument::fromJson(data, &error); file.close(); - if (error.error != QJsonParseError::NoError) - { + if (error.error != QJsonParseError::NoError) { int line = 1; int column = 0; - for(int i = 0; i < error.offset; i++) - { - if(data[i] == '\n') - { + for (int i = 0; i < error.offset; i++) { + if (data[i] == '\n') { line++; column = 0; continue; @@ -150,26 +132,25 @@ VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder) column++; } auto errorStr = QObject::tr("Unable to process the version file %1: %2 at line %3 column %4.") - .arg(fileInfo.fileName(), error.errorString()) - .arg(line).arg(column); + .arg(fileInfo.fileName(), error.errorString()) + .arg(line) + .arg(column); return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr); } return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), requireOrder); } -bool saveJsonFile(const QJsonDocument doc, const QString & filename) +bool saveJsonFile(const QJsonDocument doc, const QString& filename) { auto data = doc.toJson(); QSaveFile jsonFile(filename); - if(!jsonFile.open(QIODevice::WriteOnly)) - { + if (!jsonFile.open(QIODevice::WriteOnly)) { jsonFile.cancelWriting(); qWarning() << "Couldn't open" << filename << "for writing"; return false; } jsonFile.write(data); - if(!jsonFile.commit()) - { + if (!jsonFile.commit()) { qWarning() << "Couldn't save" << filename; return false; } @@ -178,13 +159,10 @@ bool saveJsonFile(const QJsonDocument doc, const QString & filename) void removeLwjglFromPatch(VersionFilePtr patch) { - auto filter = [](QList& libs) - { + auto filter = [](QList& libs) { QList filteredLibs; - for (auto lib : libs) - { - if (!g_VersionFilterData.lwjglWhitelist.contains(lib->artifactPrefix())) - { + for (auto lib : libs) { + if (!g_VersionFilterData.lwjglWhitelist.contains(lib->artifactPrefix())) { filteredLibs.append(lib); } } @@ -192,4 +170,4 @@ void removeLwjglFromPatch(VersionFilePtr patch) }; filter(patch->libraries); } -} +} // namespace ProfileUtils diff --git a/launcher/minecraft/ProfileUtils.h b/launcher/minecraft/ProfileUtils.h index 5b938784c..98a7ff739 100644 --- a/launcher/minecraft/ProfileUtils.h +++ b/launcher/minecraft/ProfileUtils.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -37,24 +37,22 @@ #include "Library.h" #include "VersionFile.h" -namespace ProfileUtils -{ +namespace ProfileUtils { typedef QStringList PatchOrder; /// Read and parse a OneSix format order file -bool readOverrideOrders(QString path, PatchOrder &order); +bool readOverrideOrders(QString path, PatchOrder& order); /// Write a OneSix format order file -bool writeOverrideOrders(QString path, const PatchOrder &order); - +bool writeOverrideOrders(QString path, const PatchOrder& order); /// Parse a version file in JSON format -VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder); +VersionFilePtr parseJsonFile(const QFileInfo& fileInfo, const bool requireOrder); /// Save a JSON file (in any format) -bool saveJsonFile(const QJsonDocument doc, const QString & filename); +bool saveJsonFile(const QJsonDocument doc, const QString& filename); /// Remove LWJGL from a patch file. This is applied to all Mojang-like profile files. void removeLwjglFromPatch(VersionFilePtr patch); -} +} // namespace ProfileUtils diff --git a/launcher/minecraft/Rule.cpp b/launcher/minecraft/Rule.cpp index ff3d75f2e..d80aab84d 100644 --- a/launcher/minecraft/Rule.cpp +++ b/launcher/minecraft/Rule.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -33,8 +33,8 @@ * limitations under the License. */ -#include #include +#include #include "Rule.h" @@ -47,7 +47,7 @@ RuleAction RuleAction_fromString(QString name) return Defer; } -QList> rulesFromJsonV4(const QJsonObject &objectWithRules) +QList> rulesFromJsonV4(const QJsonObject& objectWithRules) { QList> rules; auto rulesVal = objectWithRules.value("rules"); @@ -55,8 +55,7 @@ QList> rulesFromJsonV4(const QJsonObject &objectWithRules) return rules; QJsonArray ruleList = rulesVal.toArray(); - for (auto ruleVal : ruleList) - { + for (auto ruleVal : ruleList) { std::shared_ptr rule; if (!ruleVal.isObject()) continue; @@ -69,8 +68,7 @@ QList> rulesFromJsonV4(const QJsonObject &objectWithRules) continue; auto osVal = ruleObj.value("os"); - if (!osVal.isObject()) - { + if (!osVal.isObject()) { // add a new implicit action rule rules.append(ImplicitRule::create(action)); continue; @@ -102,12 +100,10 @@ QJsonObject OsRule::toJson() QJsonObject osObj; { osObj.insert("name", m_system); - if(!m_version_regexp.isEmpty()) - { + if (!m_version_regexp.isEmpty()) { osObj.insert("version", m_version_regexp); } } ruleObj.insert("os", osObj); return ruleObj; } - diff --git a/launcher/minecraft/Rule.h b/launcher/minecraft/Rule.h index 846e8e428..c6cdbc43f 100644 --- a/launcher/minecraft/Rule.h +++ b/launcher/minecraft/Rule.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,37 +35,29 @@ #pragma once -#include -#include #include +#include +#include #include #include "RuntimeContext.h" class Library; class Rule; -enum RuleAction -{ - Allow, - Disallow, - Defer -}; +enum RuleAction { Allow, Disallow, Defer }; -QList> rulesFromJsonV4(const QJsonObject &objectWithRules); +QList> rulesFromJsonV4(const QJsonObject& objectWithRules); -class Rule -{ -protected: +class Rule { + protected: RuleAction m_result; - virtual bool applies(const Library *parent, const RuntimeContext & runtimeContext) = 0; + virtual bool applies(const Library* parent, const RuntimeContext& runtimeContext) = 0; -public: - Rule(RuleAction result) : m_result(result) - { - } - virtual ~Rule() {}; + public: + Rule(RuleAction result) : m_result(result) {} + virtual ~Rule() {} virtual QJsonObject toJson() = 0; - RuleAction apply(const Library *parent, const RuntimeContext & runtimeContext) + RuleAction apply(const Library* parent, const RuntimeContext& runtimeContext) { if (applies(parent, runtimeContext)) return m_result; @@ -74,48 +66,31 @@ public: } }; -class OsRule : public Rule -{ -private: +class OsRule : public Rule { + private: // the OS QString m_system; // the OS version regexp QString m_version_regexp; -protected: - virtual bool applies(const Library *, const RuntimeContext & runtimeContext) - { - return runtimeContext.classifierMatches(m_system); - } - OsRule(RuleAction result, QString system, QString version_regexp) - : Rule(result), m_system(system), m_version_regexp(version_regexp) - { - } + protected: + virtual bool applies(const Library*, const RuntimeContext& runtimeContext) { return runtimeContext.classifierMatches(m_system); } + OsRule(RuleAction result, QString system, QString version_regexp) : Rule(result), m_system(system), m_version_regexp(version_regexp) {} -public: + public: virtual QJsonObject toJson(); - static std::shared_ptr create(RuleAction result, QString system, - QString version_regexp) + static std::shared_ptr create(RuleAction result, QString system, QString version_regexp) { return std::shared_ptr(new OsRule(result, system, version_regexp)); } }; -class ImplicitRule : public Rule -{ -protected: - virtual bool applies(const Library *, [[maybe_unused]] const RuntimeContext & runtimeContext) - { - return true; - } - ImplicitRule(RuleAction result) : Rule(result) - { - } +class ImplicitRule : public Rule { + protected: + virtual bool applies(const Library*, [[maybe_unused]] const RuntimeContext& runtimeContext) { return true; } + ImplicitRule(RuleAction result) : Rule(result) {} -public: + public: virtual QJsonObject toJson(); - static std::shared_ptr create(RuleAction result) - { - return std::shared_ptr(new ImplicitRule(result)); - } + static std::shared_ptr create(RuleAction result) { return std::shared_ptr(new ImplicitRule(result)); } }; diff --git a/launcher/minecraft/VanillaInstanceCreationTask.cpp b/launcher/minecraft/VanillaInstanceCreationTask.cpp index 0bb92e876..ccbd8c677 100644 --- a/launcher/minecraft/VanillaInstanceCreationTask.cpp +++ b/launcher/minecraft/VanillaInstanceCreationTask.cpp @@ -8,7 +8,11 @@ #include "settings/INISettingsObject.h" VanillaCreationTask::VanillaCreationTask(BaseVersion::Ptr version, QString loader, BaseVersion::Ptr loader_version) - : InstanceCreationTask(), m_version(std::move(version)), m_using_loader(true), m_loader(std::move(loader)), m_loader_version(std::move(loader_version)) + : InstanceCreationTask() + , m_version(std::move(version)) + , m_using_loader(true) + , m_loader(std::move(loader)) + , m_loader_version(std::move(loader_version)) {} bool VanillaCreationTask::createInstance() @@ -22,7 +26,7 @@ bool VanillaCreationTask::createInstance() auto components = inst.getPackProfile(); components->buildingFromScratch(); components->setComponentVersion("net.minecraft", m_version->descriptor(), true); - if(m_using_loader) + if (m_using_loader) components->setComponentVersion(m_loader, m_loader_version->descriptor()); inst.setName(name()); diff --git a/launcher/minecraft/VersionFile.cpp b/launcher/minecraft/VersionFile.cpp index 76f41600f..6632bb8bf 100644 --- a/launcher/minecraft/VersionFile.cpp +++ b/launcher/minecraft/VersionFile.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Jamie Mansfield * @@ -39,23 +39,22 @@ #include -#include "minecraft/VersionFile.h" +#include "ParseUtils.h" #include "minecraft/Library.h" #include "minecraft/PackProfile.h" -#include "ParseUtils.h" +#include "minecraft/VersionFile.h" #include -static bool isMinecraftVersion(const QString &uid) +static bool isMinecraftVersion(const QString& uid) { return uid == "net.minecraft"; } -void VersionFile::applyTo(LaunchProfile *profile, const RuntimeContext & runtimeContext) +void VersionFile::applyTo(LaunchProfile* profile, const RuntimeContext& runtimeContext) { // Only real Minecraft can set those. Don't let anything override them. - if (isMinecraftVersion(uid)) - { + if (isMinecraftVersion(uid)) { profile->applyMinecraftVersion(version); profile->applyMinecraftVersionType(type); // HACK: ignore assets from other version files than Minecraft @@ -75,16 +74,13 @@ void VersionFile::applyTo(LaunchProfile *profile, const RuntimeContext & runtime profile->applyTraits(traits); profile->applyCompatibleJavaMajors(compatibleJavaMajors); - for (auto library : libraries) - { + for (auto library : libraries) { profile->applyLibrary(library, runtimeContext); } - for (auto mavenFile : mavenFiles) - { + for (auto mavenFile : mavenFiles) { profile->applyMavenFile(mavenFile, runtimeContext); } - for (auto agent : agents) - { + for (auto agent : agents) { profile->applyAgent(agent, runtimeContext); } profile->applyProblemSeverity(getProblemSeverity()); diff --git a/launcher/minecraft/VersionFile.h b/launcher/minecraft/VersionFile.h index 8e9dd1670..280e35ee3 100644 --- a/launcher/minecraft/VersionFile.h +++ b/launcher/minecraft/VersionFile.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,17 +35,17 @@ #pragma once -#include -#include #include #include +#include +#include -#include -#include "minecraft/Rule.h" -#include "ProblemProvider.h" -#include "Library.h" -#include "Agent.h" #include +#include +#include "Agent.h" +#include "Library.h" +#include "ProblemProvider.h" +#include "minecraft/Rule.h" class PackProfile; class VersionFile; @@ -54,14 +54,14 @@ struct MojangDownloadInfo; struct MojangAssetIndexInfo; using VersionFilePtr = std::shared_ptr; -class VersionFile : public ProblemContainer -{ +class VersionFile : public ProblemContainer { friend class MojangVersionFormat; friend class OneSixVersionFormat; -public: /* methods */ - void applyTo(LaunchProfile* profile, const RuntimeContext & runtimeContext); -public: /* data */ + public: /* methods */ + void applyTo(LaunchProfile* profile, const RuntimeContext& runtimeContext); + + public: /* data */ /// Prism Launcher: order hint for this version file if no explicit order is set int order = 0; @@ -149,11 +149,10 @@ public: /* data */ /// is volatile -- may be removed as soon as it is no longer needed by something else bool m_volatile = false; -public: + public: // Mojang: DEPRECATED list of 'downloads' - client jar, server jar, windows server exe, maybe more. - QMap > mojangDownloads; + QMap> mojangDownloads; // Mojang: extended asset index download information std::shared_ptr mojangAssetIndex; }; - diff --git a/launcher/minecraft/VersionFilterData.cpp b/launcher/minecraft/VersionFilterData.cpp index c286d2662..3924e598f 100644 --- a/launcher/minecraft/VersionFilterData.cpp +++ b/launcher/minecraft/VersionFilterData.cpp @@ -6,19 +6,17 @@ VersionFilterData g_VersionFilterData = VersionFilterData(); VersionFilterData::VersionFilterData() { // 1.3.* - auto libs13 = - QList{{"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b"}, - {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f"}, - {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82"}}; + auto libs13 = QList{ { "argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b" }, + { "guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f" }, + { "asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82" } }; fmlLibsMapping["1.3.2"] = libs13; // 1.4.* - auto libs14 = QList{ - {"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b"}, - {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f"}, - {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82"}, - {"bcprov-jdk15on-147.jar", "b6f5d9926b0afbde9f4dbe3db88c5247be7794bb"}}; + auto libs14 = QList{ { "argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b" }, + { "guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f" }, + { "asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82" }, + { "bcprov-jdk15on-147.jar", "b6f5d9926b0afbde9f4dbe3db88c5247be7794bb" } }; fmlLibsMapping["1.4"] = libs14; fmlLibsMapping["1.4.1"] = libs14; @@ -30,43 +28,38 @@ VersionFilterData::VersionFilterData() fmlLibsMapping["1.4.7"] = libs14; // 1.5 - fmlLibsMapping["1.5"] = QList{ - {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51"}, - {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a"}, - {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58"}, - {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65"}, - {"deobfuscation_data_1.5.zip", "5f7c142d53776f16304c0bbe10542014abad6af8"}, - {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85"}}; + fmlLibsMapping["1.5"] = QList{ { "argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51" }, + { "guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a" }, + { "asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58" }, + { "bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65" }, + { "deobfuscation_data_1.5.zip", "5f7c142d53776f16304c0bbe10542014abad6af8" }, + { "scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85" } }; // 1.5.1 - fmlLibsMapping["1.5.1"] = QList{ - {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51"}, - {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a"}, - {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58"}, - {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65"}, - {"deobfuscation_data_1.5.1.zip", "22e221a0d89516c1f721d6cab056a7e37471d0a6"}, - {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85"}}; + fmlLibsMapping["1.5.1"] = QList{ { "argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51" }, + { "guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a" }, + { "asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58" }, + { "bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65" }, + { "deobfuscation_data_1.5.1.zip", "22e221a0d89516c1f721d6cab056a7e37471d0a6" }, + { "scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85" } }; // 1.5.2 - fmlLibsMapping["1.5.2"] = QList{ - {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51"}, - {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a"}, - {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58"}, - {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65"}, - {"deobfuscation_data_1.5.2.zip", "446e55cd986582c70fcf12cb27bc00114c5adfd9"}, - {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85"}}; + fmlLibsMapping["1.5.2"] = QList{ { "argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51" }, + { "guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a" }, + { "asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58" }, + { "bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65" }, + { "deobfuscation_data_1.5.2.zip", "446e55cd986582c70fcf12cb27bc00114c5adfd9" }, + { "scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85" } }; // don't use installers for those. - forgeInstallerBlacklist = QSet({"1.5.2"}); + forgeInstallerBlacklist = QSet({ "1.5.2" }); // FIXME: remove, used for deciding when core mods should display legacyCutoffDate = timeFromS3Time("2013-06-25T15:08:56+02:00"); - lwjglWhitelist = - QSet{"net.java.jinput:jinput", "net.java.jinput:jinput-platform", - "net.java.jutils:jutils", "org.lwjgl.lwjgl:lwjgl", - "org.lwjgl.lwjgl:lwjgl_util", "org.lwjgl.lwjgl:lwjgl-platform"}; + lwjglWhitelist = QSet{ "net.java.jinput:jinput", "net.java.jinput:jinput-platform", "net.java.jutils:jutils", + "org.lwjgl.lwjgl:lwjgl", "org.lwjgl.lwjgl:lwjgl_util", "org.lwjgl.lwjgl:lwjgl-platform" }; - java8BeginsDate = timeFromS3Time("2017-03-30T09:32:19+00:00"); + java8BeginsDate = timeFromS3Time("2017-03-30T09:32:19+00:00"); java16BeginsDate = timeFromS3Time("2021-05-12T11:19:15+00:00"); java17BeginsDate = timeFromS3Time("2021-11-16T17:04:48+00:00"); } diff --git a/launcher/minecraft/VersionFilterData.h b/launcher/minecraft/VersionFilterData.h index 13445a517..bcd329b6c 100644 --- a/launcher/minecraft/VersionFilterData.h +++ b/launcher/minecraft/VersionFilterData.h @@ -1,17 +1,15 @@ #pragma once -#include -#include -#include #include +#include +#include +#include -struct FMLlib -{ +struct FMLlib { QString filename; QString checksum; }; -struct VersionFilterData -{ +struct VersionFilterData { VersionFilterData(); // mapping between minecraft versions and FML libraries required QMap> fmlLibsMapping; diff --git a/launcher/minecraft/World.cpp b/launcher/minecraft/World.cpp index 54fb94346..1a680ac56 100644 --- a/launcher/minecraft/World.cpp +++ b/launcher/minecraft/World.cpp @@ -34,23 +34,23 @@ * limitations under the License. */ -#include -#include -#include -#include -#include #include "World.h" +#include +#include +#include +#include +#include -#include "GZip.h" -#include #include -#include +#include #include -#include -#include #include -#include #include +#include +#include +#include +#include +#include "GZip.h" #include @@ -58,16 +58,15 @@ #include "FileSystem.h" -using std::optional; using std::nullopt; +using std::optional; -GameType::GameType(std::optional original): - original(original) +GameType::GameType(std::optional original) : original(original) { - if(!original) { + if (!original) { return; } - switch(*original) { + switch (*original) { case 0: type = GameType::Survival; break; @@ -87,8 +86,7 @@ GameType::GameType(std::optional original): QString GameType::toTranslatedString() const { - switch (type) - { + switch (type) { case GameType::Survival: return QCoreApplication::translate("GameType", "Survival"); case GameType::Creative: @@ -100,7 +98,7 @@ QString GameType::toTranslatedString() const default: break; } - if(original) { + if (original) { return QCoreApplication::translate("GameType", "Unknown (%1)").arg(*original); } return QCoreApplication::translate("GameType", "Undefined"); @@ -108,8 +106,7 @@ QString GameType::toTranslatedString() const QString GameType::toLogString() const { - switch (type) - { + switch (type) { case GameType::Survival: return "Survival"; case GameType::Creative: @@ -121,108 +118,94 @@ QString GameType::toLogString() const default: break; } - if(original) { + if (original) { return QString("Unknown (%1)").arg(*original); } return "Undefined"; } -std::unique_ptr parseLevelDat(QByteArray data) +std::unique_ptr parseLevelDat(QByteArray data) { QByteArray output; - if(!GZip::unzip(data, output)) - { + if (!GZip::unzip(data, output)) { return nullptr; } std::istringstream foo(std::string(output.constData(), output.size())); try { auto pair = nbt::io::read_compound(foo); - if(pair.first != "") + if (pair.first != "") return nullptr; - if(pair.second == nullptr) + if (pair.second == nullptr) return nullptr; return std::move(pair.second); - } - catch (const nbt::io::input_error &e) - { + } catch (const nbt::io::input_error& e) { qWarning() << "Unable to parse level.dat:" << e.what(); return nullptr; } } -QByteArray serializeLevelDat(nbt::tag_compound * levelInfo) +QByteArray serializeLevelDat(nbt::tag_compound* levelInfo) { std::ostringstream s; nbt::io::write_tag("", *levelInfo, s); - QByteArray val( s.str().data(), (int) s.str().size() ); + QByteArray val(s.str().data(), (int)s.str().size()); return val; } -QString getLevelDatFromFS(const QFileInfo &file) +QString getLevelDatFromFS(const QFileInfo& file) { QDir worldDir(file.filePath()); - if(!file.isDir() || !worldDir.exists("level.dat")) - { + if (!file.isDir() || !worldDir.exists("level.dat")) { return QString(); } return worldDir.absoluteFilePath("level.dat"); } -QByteArray getLevelDatDataFromFS(const QFileInfo &file) +QByteArray getLevelDatDataFromFS(const QFileInfo& file) { auto fullFilePath = getLevelDatFromFS(file); - if(fullFilePath.isNull()) - { + if (fullFilePath.isNull()) { return QByteArray(); } QFile f(fullFilePath); - if(!f.open(QIODevice::ReadOnly)) - { + if (!f.open(QIODevice::ReadOnly)) { return QByteArray(); } return f.readAll(); } -bool putLevelDatDataToFS(const QFileInfo &file, QByteArray & data) +bool putLevelDatDataToFS(const QFileInfo& file, QByteArray& data) { - auto fullFilePath = getLevelDatFromFS(file); - if(fullFilePath.isNull()) - { + auto fullFilePath = getLevelDatFromFS(file); + if (fullFilePath.isNull()) { return false; } QSaveFile f(fullFilePath); - if(!f.open(QIODevice::WriteOnly)) - { + if (!f.open(QIODevice::WriteOnly)) { return false; } QByteArray compressed; - if(!GZip::zip(data, compressed)) - { + if (!GZip::zip(data, compressed)) { return false; } - if(f.write(compressed) != compressed.size()) - { + if (f.write(compressed) != compressed.size()) { f.cancelWriting(); return false; } return f.commit(); } -int64_t calculateWorldSize(const QFileInfo &file) +int64_t calculateWorldSize(const QFileInfo& file) { - if (file.isFile() && file.suffix() == "zip") - { + if (file.isFile() && file.suffix() == "zip") { return file.size(); - } - else if(file.isDir()) - { + } else if (file.isDir()) { QDirIterator it(file.absoluteFilePath(), QDir::Files, QDirIterator::Subdirectories); int64_t total = 0; - while (it.hasNext()) - { + while (it.hasNext()) { total += it.fileInfo().size(); it.next(); } @@ -231,25 +214,22 @@ int64_t calculateWorldSize(const QFileInfo &file) return -1; } -World::World(const QFileInfo &file) +World::World(const QFileInfo& file) { repath(file); } -void World::repath(const QFileInfo &file) +void World::repath(const QFileInfo& file) { m_containerFile = file; m_folderName = file.fileName(); m_size = calculateWorldSize(file); - if(file.isFile() && file.suffix() == "zip") - { + if (file.isFile() && file.suffix() == "zip") { m_iconFile = QString(); readFromZip(file); - } - else if(file.isDir()) - { + } else if (file.isDir()) { QFileInfo assumedIconPath(file.absoluteFilePath() + "/icon.png"); - if(assumedIconPath.exists()) { + if (assumedIconPath.exists()) { m_iconFile = assumedIconPath.absoluteFilePath(); } readFromFS(file); @@ -258,21 +238,20 @@ void World::repath(const QFileInfo &file) bool World::resetIcon() { - if(m_iconFile.isNull()) { + if (m_iconFile.isNull()) { return false; } - if(QFile(m_iconFile).remove()) { + if (QFile(m_iconFile).remove()) { m_iconFile = QString(); return true; } return false; } -void World::readFromFS(const QFileInfo &file) +void World::readFromFS(const QFileInfo& file) { auto bytes = getLevelDatDataFromFS(file); - if(bytes.isEmpty()) - { + if (bytes.isEmpty()) { is_valid = false; return; } @@ -280,104 +259,88 @@ void World::readFromFS(const QFileInfo &file) levelDatTime = file.lastModified(); } -void World::readFromZip(const QFileInfo &file) +void World::readFromZip(const QFileInfo& file) { QuaZip zip(file.absoluteFilePath()); is_valid = zip.open(QuaZip::mdUnzip); - if (!is_valid) - { + if (!is_valid) { return; } auto location = MMCZip::findFolderOfFileInZip(&zip, "level.dat"); is_valid = !location.isEmpty(); - if (!is_valid) - { + if (!is_valid) { return; } m_containerOffsetPath = location; QuaZipFile zippedFile(&zip); // read the install profile is_valid = zip.setCurrentFile(location + "level.dat"); - if (!is_valid) - { + if (!is_valid) { return; } is_valid = zippedFile.open(QIODevice::ReadOnly); QuaZipFileInfo64 levelDatInfo; zippedFile.getFileInfo(&levelDatInfo); auto modTime = levelDatInfo.getNTFSmTime(); - if(!modTime.isValid()) - { + if (!modTime.isValid()) { modTime = levelDatInfo.dateTime; } levelDatTime = modTime; - if (!is_valid) - { + if (!is_valid) { return; } loadFromLevelDat(zippedFile.readAll()); zippedFile.close(); } -bool World::install(const QString &to, const QString &name) +bool World::install(const QString& to, const QString& name) { auto finalPath = FS::PathCombine(to, FS::DirNameFromString(m_actualName, to)); - if(!FS::ensureFolderPathExists(finalPath)) - { + if (!FS::ensureFolderPathExists(finalPath)) { return false; } bool ok = false; - if(m_containerFile.isFile()) - { + if (m_containerFile.isFile()) { QuaZip zip(m_containerFile.absoluteFilePath()); - if (!zip.open(QuaZip::mdUnzip)) - { + if (!zip.open(QuaZip::mdUnzip)) { return false; } ok = !MMCZip::extractSubDir(&zip, m_containerOffsetPath, finalPath); - } - else if(m_containerFile.isDir()) - { + } else if (m_containerFile.isDir()) { QString from = m_containerFile.filePath(); ok = FS::copy(from, finalPath)(); } - if(ok && !name.isEmpty() && m_actualName != name) - { + if (ok && !name.isEmpty() && m_actualName != name) { QFileInfo finalPathInfo(finalPath); World newWorld(finalPathInfo); - if(newWorld.isValid()) - { + if (newWorld.isValid()) { newWorld.rename(name); } } return ok; } -bool World::rename(const QString &newName) +bool World::rename(const QString& newName) { - if(m_containerFile.isFile()) - { + if (m_containerFile.isFile()) { return false; } auto data = getLevelDatDataFromFS(m_containerFile); - if(data.isEmpty()) - { + if (data.isEmpty()) { return false; } auto worldData = parseLevelDat(data); - if(!worldData) - { + if (!worldData) { return false; } - auto &val = worldData->at("Data"); - if(val.get_type() != nbt::tag_type::Compound) - { + auto& val = worldData->at("Data"); + if (val.get_type() != nbt::tag_type::Compound) { return false; } - auto &dataCompound = val.as(); + auto& dataCompound = val.as(); dataCompound.put("LevelName", nbt::value_initializer(newName.toUtf8().data())); data = serializeLevelDat(worldData.get()); @@ -396,112 +359,93 @@ bool World::rename(const QString &newName) namespace { -optional read_string (nbt::value& parent, const char * name) +optional read_string(nbt::value& parent, const char* name) { - try - { - auto &namedValue = parent.at(name); - if(namedValue.get_type() != nbt::tag_type::String) - { + try { + auto& namedValue = parent.at(name); + if (namedValue.get_type() != nbt::tag_type::String) { return nullopt; } - auto & tag_str = namedValue.as(); + auto& tag_str = namedValue.as(); return QString::fromStdString(tag_str.get()); - } - catch (const std::out_of_range &e) - { + } catch ([[maybe_unused]] const std::out_of_range& e) { // fallback for old world formats qWarning() << "String NBT tag" << name << "could not be found."; return nullopt; - } - catch (const std::bad_cast &e) - { + } catch ([[maybe_unused]] const std::bad_cast& e) { // type mismatch qWarning() << "NBT tag" << name << "could not be converted to string."; return nullopt; } } -optional read_long (nbt::value& parent, const char * name) +optional read_long(nbt::value& parent, const char* name) { - try - { - auto &namedValue = parent.at(name); - if(namedValue.get_type() != nbt::tag_type::Long) - { + try { + auto& namedValue = parent.at(name); + if (namedValue.get_type() != nbt::tag_type::Long) { return nullopt; } - auto & tag_str = namedValue.as(); + auto& tag_str = namedValue.as(); return tag_str.get(); - } - catch (const std::out_of_range &e) - { + } catch ([[maybe_unused]] const std::out_of_range& e) { // fallback for old world formats qWarning() << "Long NBT tag" << name << "could not be found."; return nullopt; - } - catch (const std::bad_cast &e) - { + } catch ([[maybe_unused]] const std::bad_cast& e) { // type mismatch qWarning() << "NBT tag" << name << "could not be converted to long."; return nullopt; } } -optional read_int (nbt::value& parent, const char * name) +optional read_int(nbt::value& parent, const char* name) { - try - { - auto &namedValue = parent.at(name); - if(namedValue.get_type() != nbt::tag_type::Int) - { + try { + auto& namedValue = parent.at(name); + if (namedValue.get_type() != nbt::tag_type::Int) { return nullopt; } - auto & tag_str = namedValue.as(); + auto& tag_str = namedValue.as(); return tag_str.get(); - } - catch (const std::out_of_range &e) - { + } catch ([[maybe_unused]] const std::out_of_range& e) { // fallback for old world formats qWarning() << "Int NBT tag" << name << "could not be found."; return nullopt; - } - catch (const std::bad_cast &e) - { + } catch ([[maybe_unused]] const std::bad_cast& e) { // type mismatch qWarning() << "NBT tag" << name << "could not be converted to int."; return nullopt; } } -GameType read_gametype(nbt::value& parent, const char * name) { +GameType read_gametype(nbt::value& parent, const char* name) +{ return GameType(read_int(parent, name)); } -} +} // namespace void World::loadFromLevelDat(QByteArray data) { auto levelData = parseLevelDat(data); - if(!levelData) - { + if (!levelData) { is_valid = false; return; } - nbt::value * valPtr = nullptr; + nbt::value* valPtr = nullptr; try { valPtr = &levelData->at("Data"); - } - catch (const std::out_of_range &e) { + } catch (const std::out_of_range& e) { qWarning() << "Unable to read NBT tags from " << m_folderName << ":" << e.what(); is_valid = false; return; } - nbt::value &val = *valPtr; + nbt::value& val = *valPtr; is_valid = val.get_type() == nbt::tag_type::Compound; - if(!is_valid) + if (!is_valid) return; auto name = read_string(val, "LevelName"); @@ -514,31 +458,30 @@ void World::loadFromLevelDat(QByteArray data) optional randomSeed; try { - auto &WorldGen_val = val.at("WorldGenSettings"); + auto& WorldGen_val = val.at("WorldGenSettings"); randomSeed = read_long(WorldGen_val, "seed"); + } catch (const std::out_of_range&) { } - catch (const std::out_of_range &) {} - if(!randomSeed) { + if (!randomSeed) { randomSeed = read_long(val, "RandomSeed"); } m_randomSeed = randomSeed ? *randomSeed : 0; qDebug() << "World Name:" << m_actualName; qDebug() << "Last Played:" << m_lastPlayed.toString(); - if(randomSeed) { + if (randomSeed) { qDebug() << "Seed:" << *randomSeed; } qDebug() << "Size:" << m_size; qDebug() << "GameType:" << m_gameType.toLogString(); } -bool World::replace(World &with) +bool World::replace(World& with) { if (!destroy()) return false; bool success = FS::copy(with.m_containerFile.filePath(), m_containerFile.path())(); - if (success) - { + if (success) { m_folderName = with.m_folderName; m_containerFile.refresh(); } @@ -547,25 +490,23 @@ bool World::replace(World &with) bool World::destroy() { - if(!is_valid) return false; + if (!is_valid) + return false; if (FS::trash(m_containerFile.filePath())) return true; - if (m_containerFile.isDir()) - { + if (m_containerFile.isDir()) { QDir d(m_containerFile.filePath()); return d.removeRecursively(); - } - else if(m_containerFile.isFile()) - { + } else if (m_containerFile.isFile()) { QFile file(m_containerFile.absoluteFilePath()); return file.remove(); } return true; } -bool World::operator==(const World &other) const +bool World::operator==(const World& other) const { return is_valid == other.is_valid && folderName() == other.folderName(); } @@ -585,8 +526,7 @@ bool World::isSymLinkUnder(const QString& instPath) const bool World::isMoreThanOneHardLink() const { - if (m_containerFile.isDir()) - { + if (m_containerFile.isDir()) { return FS::hardLinkCount(QDir(m_containerFile.absoluteFilePath()).filePath("level.dat")) > 1; } return FS::hardLinkCount(m_containerFile.absoluteFilePath()) > 1; diff --git a/launcher/minecraft/World.h b/launcher/minecraft/World.h index 10328cce8..4303dc553 100644 --- a/launcher/minecraft/World.h +++ b/launcher/minecraft/World.h @@ -14,95 +14,57 @@ */ #pragma once -#include #include +#include #include struct GameType { GameType() = default; - GameType (std::optional original); + GameType(std::optional original); QString toTranslatedString() const; QString toLogString() const; - enum - { - Unknown = -1, - Survival = 0, - Creative, - Adventure, - Spectator - } type = Unknown; + enum { Unknown = -1, Survival, Creative, Adventure, Spectator } type = Unknown; std::optional original; }; -class World -{ -public: - World(const QFileInfo &file); - QString folderName() const - { - return m_folderName; - } - QString name() const - { - return m_actualName; - } - QString iconFile() const - { - return m_iconFile; - } - int64_t bytes() const - { - return m_size; - } - QDateTime lastPlayed() const - { - return m_lastPlayed; - } - GameType gameType() const - { - return m_gameType; - } - int64_t seed() const - { - return m_randomSeed; - } - bool isValid() const - { - return is_valid; - } - bool isOnFS() const - { - return m_containerFile.isDir(); - } - QFileInfo container() const - { - return m_containerFile; - } +class World { + public: + World(const QFileInfo& file); + QString folderName() const { return m_folderName; } + QString name() const { return m_actualName; } + QString iconFile() const { return m_iconFile; } + int64_t bytes() const { return m_size; } + QDateTime lastPlayed() const { return m_lastPlayed; } + GameType gameType() const { return m_gameType; } + int64_t seed() const { return m_randomSeed; } + bool isValid() const { return is_valid; } + bool isOnFS() const { return m_containerFile.isDir(); } + QFileInfo container() const { return m_containerFile; } // delete all the files of this world bool destroy(); // replace this world with a copy of the other - bool replace(World &with); + bool replace(World& with); // change the world's filesystem path (used by world lists for *MAGIC* purposes) - void repath(const QFileInfo &file); + void repath(const QFileInfo& file); // remove the icon file, if any bool resetIcon(); - bool rename(const QString &to); - bool install(const QString &to, const QString &name= QString()); + bool rename(const QString& to); + bool install(const QString& to, const QString& name = QString()); // WEAK compare operator - used for replacing worlds - bool operator==(const World &other) const; + bool operator==(const World& other) const; - [[nodiscard]] auto isSymLink() const -> bool{ return m_containerFile.isSymLink(); } + [[nodiscard]] auto isSymLink() const -> bool { return m_containerFile.isSymLink(); } /** * @brief Take a instance path, checks if the file pointed to by the resource is a symlink or under a symlink in that instance - * + * * @param instPath path to an instance directory - * @return true - * @return false + * @return true + * @return false */ [[nodiscard]] bool isSymLinkUnder(const QString& instPath) const; @@ -110,13 +72,12 @@ public: QString canonicalFilePath() const { return m_containerFile.canonicalFilePath(); } -private: - void readFromZip(const QFileInfo &file); - void readFromFS(const QFileInfo &file); + private: + void readFromZip(const QFileInfo& file); + void readFromFS(const QFileInfo& file); void loadFromLevelDat(QByteArray data); -protected: - + protected: QFileInfo m_containerFile; QString m_containerOffsetPath; QString m_folderName; diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp index 0feee2999..812b13c71 100644 --- a/launcher/minecraft/WorldList.cpp +++ b/launcher/minecraft/WorldList.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,18 +35,17 @@ #include "WorldList.h" -#include "Application.h" #include -#include +#include +#include #include +#include #include #include -#include -#include -#include +#include +#include "Application.h" -WorldList::WorldList(const QString &dir, BaseInstance* instance) - : QAbstractListModel(), m_instance(instance), m_dir(dir) +WorldList::WorldList(const QString& dir, BaseInstance* instance) : QAbstractListModel(), m_instance(instance), m_dir(dir) { FS::ensureFolderPathExists(m_dir.absolutePath()); m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); @@ -58,35 +57,27 @@ WorldList::WorldList(const QString &dir, BaseInstance* instance) void WorldList::startWatching() { - if(is_watching) - { + if (is_watching) { return; } update(); is_watching = m_watcher->addPath(m_dir.absolutePath()); - if (is_watching) - { + if (is_watching) { qDebug() << "Started watching " << m_dir.absolutePath(); - } - else - { + } else { qDebug() << "Failed to start watching " << m_dir.absolutePath(); } } void WorldList::stopWatching() { - if(!is_watching) - { + if (!is_watching) { return; } is_watching = !m_watcher->removePath(m_dir.absolutePath()); - if (!is_watching) - { + if (!is_watching) { qDebug() << "Stopped watching " << m_dir.absolutePath(); - } - else - { + } else { qDebug() << "Failed to stop watching " << m_dir.absolutePath(); } } @@ -100,14 +91,12 @@ bool WorldList::update() m_dir.refresh(); auto folderContents = m_dir.entryInfoList(); // if there are any untracked files... - for (QFileInfo entry : folderContents) - { - if(!entry.isDir()) + for (QFileInfo entry : folderContents) { + if (!entry.isDir()) continue; World w(entry); - if(w.isValid()) - { + if (w.isValid()) { newWorlds.append(w); } } @@ -127,7 +116,8 @@ bool WorldList::isValid() return m_dir.exists() && m_dir.isReadable(); } -QString WorldList::instDirPath() const { +QString WorldList::instDirPath() const +{ return QFileInfo(m_instance->instanceRoot()).absoluteFilePath(); } @@ -135,9 +125,8 @@ bool WorldList::deleteWorld(int index) { if (index >= worlds.size() || index < 0) return false; - World &m = worlds[index]; - if (m.destroy()) - { + World& m = worlds[index]; + if (m.destroy()) { beginRemoveRows(QModelIndex(), index, index); worlds.removeAt(index); endRemoveRows(); @@ -149,9 +138,8 @@ bool WorldList::deleteWorld(int index) bool WorldList::deleteWorlds(int first, int last) { - for (int i = first; i <= last; i++) - { - World &m = worlds[i]; + for (int i = first; i <= last; i++) { + World& m = worlds[i]; m.destroy(); } beginRemoveRows(QModelIndex(), first, last); @@ -165,21 +153,20 @@ bool WorldList::resetIcon(int row) { if (row >= worlds.size() || row < 0) return false; - World &m = worlds[row]; - if(m.resetIcon()) { - emit dataChanged(index(row), index(row), {WorldList::IconFileRole}); + World& m = worlds[row]; + if (m.resetIcon()) { + emit dataChanged(index(row), index(row), { WorldList::IconFileRole }); return true; } return false; } - -int WorldList::columnCount(const QModelIndex &parent) const +int WorldList::columnCount(const QModelIndex& parent) const { - return parent.isValid()? 0 : 5; + return parent.isValid() ? 0 : 5; } -QVariant WorldList::data(const QModelIndex &index, int role) const +QVariant WorldList::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); @@ -192,135 +179,121 @@ QVariant WorldList::data(const QModelIndex &index, int role) const QLocale locale; - auto & world = worlds[row]; - switch (role) - { - case Qt::DisplayRole: - switch (column) - { - case NameColumn: + auto& world = worlds[row]; + switch (role) { + case Qt::DisplayRole: + switch (column) { + case NameColumn: + return world.name(); + + case GameModeColumn: + return world.gameType().toTranslatedString(); + + case LastPlayedColumn: + return world.lastPlayed(); + + case SizeColumn: + return locale.formattedDataSize(world.bytes()); + + case InfoColumn: + if (world.isSymLinkUnder(instDirPath())) { + return tr("This world is symbolically linked from elsewhere."); + } + if (world.isMoreThanOneHardLink()) { + return tr("\nThis world is hard linked elsewhere."); + } + return ""; + default: + return QVariant(); + } + + case Qt::UserRole: + switch (column) { + case SizeColumn: + return QVariant::fromValue(world.bytes()); + + default: + return data(index, Qt::DisplayRole); + } + + case Qt::ToolTipRole: { + if (column == InfoColumn) { + if (world.isSymLinkUnder(instDirPath())) { + return tr("Warning: This world is symbolically linked from elsewhere. Editing it will also change the original." + "\nCanonical Path: %1") + .arg(world.canonicalFilePath()); + } + if (world.isMoreThanOneHardLink()) { + return tr("Warning: This world is hard linked elsewhere. Editing it will also change the original."); + } + } + return world.folderName(); + } + case ObjectRole: { + return QVariant::fromValue((void*)&world); + } + case FolderRole: { + return QDir::toNativeSeparators(dir().absoluteFilePath(world.folderName())); + } + case SeedRole: { + return QVariant::fromValue(world.seed()); + } + case NameRole: { return world.name(); - - case GameModeColumn: - return world.gameType().toTranslatedString(); - - case LastPlayedColumn: + } + case LastPlayedRole: { return world.lastPlayed(); - - case SizeColumn: - return locale.formattedDataSize(world.bytes()); - - case InfoColumn: - if (world.isSymLinkUnder(instDirPath())) { - return tr("This world is symbolically linked from elsewhere."); - } - if (world.isMoreThanOneHardLink()) { - return tr("\nThis world is hard linked elsewhere."); - } - return ""; + } + case SizeRole: { + return QVariant::fromValue(world.bytes()); + } + case IconFileRole: { + return world.iconFile(); + } default: return QVariant(); - } - - case Qt::UserRole: - switch (column) - { - case SizeColumn: - return QVariant::fromValue(world.bytes()); - - default: - return data(index, Qt::DisplayRole); - } - - case Qt::ToolTipRole: - { - if (column == InfoColumn) { - if (world.isSymLinkUnder(instDirPath())) { - return tr("Warning: This world is symbolically linked from elsewhere. Editing it will also change the original." - "\nCanonical Path: %1").arg(world.canonicalFilePath()); - } - if (world.isMoreThanOneHardLink()) { - return tr("Warning: This world is hard linked elsewhere. Editing it will also change the original."); - } - } - return world.folderName(); - } - case ObjectRole: - { - return QVariant::fromValue((void *)&world); - } - case FolderRole: - { - return QDir::toNativeSeparators(dir().absoluteFilePath(world.folderName())); - } - case SeedRole: - { - return QVariant::fromValue(world.seed()); - } - case NameRole: - { - return world.name(); - } - case LastPlayedRole: - { - return world.lastPlayed(); - } - case SizeRole: - { - return QVariant::fromValue(world.bytes()); - } - case IconFileRole: - { - return world.iconFile(); - } - default: - return QVariant(); } } -QVariant WorldList::headerData(int section, Qt::Orientation orientation, int role) const +QVariant WorldList::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const { - switch (role) - { - case Qt::DisplayRole: - switch (section) - { - case NameColumn: - return tr("Name"); - case GameModeColumn: - return tr("Game Mode"); - case LastPlayedColumn: - return tr("Last Played"); - case SizeColumn: - //: World size on disk - return tr("Size"); - case InfoColumn: - //: special warnings? - return tr("Info"); - default: - return QVariant(); - } + switch (role) { + case Qt::DisplayRole: + switch (section) { + case NameColumn: + return tr("Name"); + case GameModeColumn: + return tr("Game Mode"); + case LastPlayedColumn: + return tr("Last Played"); + case SizeColumn: + //: World size on disk + return tr("Size"); + case InfoColumn: + //: special warnings? + return tr("Info"); + default: + return QVariant(); + } - case Qt::ToolTipRole: - switch (section) - { - case NameColumn: - return tr("The name of the world."); - case GameModeColumn: - return tr("Game mode of the world."); - case LastPlayedColumn: - return tr("Date and time the world was last played."); - case SizeColumn: - return tr("Size of the world on disk."); - case InfoColumn: - return tr("Information and warnings about the world."); + case Qt::ToolTipRole: + switch (section) { + case NameColumn: + return tr("The name of the world."); + case GameModeColumn: + return tr("Game mode of the world."); + case LastPlayedColumn: + return tr("Date and time the world was last played."); + case SizeColumn: + return tr("Size of the world on disk."); + case InfoColumn: + return tr("Information and warnings about the world."); + default: + return QVariant(); + } default: return QVariant(); - } - default: - return QVariant(); } - return QVariant(); } QStringList WorldList::mimeTypes() const @@ -330,32 +303,23 @@ QStringList WorldList::mimeTypes() const return types; } -class WorldMimeData : public QMimeData -{ -Q_OBJECT +class WorldMimeData : public QMimeData { + Q_OBJECT -public: - WorldMimeData(QList worlds) - { - m_worlds = worlds; + public: + WorldMimeData(QList worlds) { m_worlds = worlds; } + QStringList formats() const { return QMimeData::formats() << "text/uri-list"; } - } - QStringList formats() const - { - return QMimeData::formats() << "text/uri-list"; - } - -protected: + protected: #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - QVariant retrieveData(const QString &mimetype, QMetaType type) const + QVariant retrieveData(const QString& mimetype, QMetaType type) const #else - QVariant retrieveData(const QString &mimetype, QVariant::Type type) const + QVariant retrieveData(const QString& mimetype, QVariant::Type type) const #endif { QList urls; - for(auto &world: m_worlds) - { - if(!world.isValid() || !world.isOnFS()) + for (auto& world : m_worlds) { + if (!world.isValid() || !world.isOnFS()) continue; QString worldPath = world.container().absoluteFilePath(); qDebug() << worldPath; @@ -364,38 +328,36 @@ protected: const_cast(this)->setUrls(urls); return QMimeData::retrieveData(mimetype, type); } -private: + + private: QList m_worlds; }; -QMimeData *WorldList::mimeData(const QModelIndexList &indexes) const +QMimeData* WorldList::mimeData(const QModelIndexList& indexes) const { if (indexes.size() == 0) return new QMimeData(); - QList worlds; - for(auto idx : indexes) - { - if(idx.column() != 0) + QList worlds_; + for (auto idx : indexes) { + if (idx.column() != 0) continue; int row = idx.row(); if (row < 0 || row >= this->worlds.size()) continue; - worlds.append(this->worlds[row]); + worlds_.append(this->worlds[row]); } - if(!worlds.size()) - { + if (!worlds_.size()) { return new QMimeData(); } - return new WorldMimeData(worlds); + return new WorldMimeData(worlds_); } -Qt::ItemFlags WorldList::flags(const QModelIndex &index) const +Qt::ItemFlags WorldList::flags(const QModelIndex& index) const { Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); if (index.isValid()) - return Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | - defaultFlags; + return Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; else return Qt::ItemIsDropEnabled | defaultFlags; } @@ -416,15 +378,17 @@ void WorldList::installWorld(QFileInfo filename) { qDebug() << "installing: " << filename.absoluteFilePath(); World w(filename); - if(!w.isValid()) - { + if (!w.isValid()) { return; } w.install(m_dir.absolutePath()); } -bool WorldList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[maybe_unused]] int row, [[maybe_unused]] int column, - [[maybe_unused]] const QModelIndex &parent) +bool WorldList::dropMimeData(const QMimeData* data, + Qt::DropAction action, + [[maybe_unused]] int row, + [[maybe_unused]] int column, + [[maybe_unused]] const QModelIndex& parent) { if (action == Qt::IgnoreAction) return true; @@ -432,14 +396,12 @@ bool WorldList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[may if (!data || !(action & supportedDropActions())) return false; // files dropped from outside? - if (data->hasUrls()) - { + if (data->hasUrls()) { bool was_watching = is_watching; if (was_watching) stopWatching(); auto urls = data->urls(); - for (auto url : urls) - { + for (auto url : urls) { // only local files may be dropped... if (!url.isLocalFile()) continue; @@ -447,8 +409,7 @@ bool WorldList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[may QFileInfo worldInfo(filename); - if(!m_dir.entryInfoList().contains(worldInfo)) - { + if (!m_dir.entryInfoList().contains(worldInfo)) { installWorld(worldInfo); } } diff --git a/launcher/minecraft/WorldList.h b/launcher/minecraft/WorldList.h index 96b64193f..bea24bb9a 100644 --- a/launcher/minecraft/WorldList.h +++ b/launcher/minecraft/WorldList.h @@ -15,65 +15,34 @@ #pragma once -#include -#include -#include #include +#include +#include #include -#include "minecraft/World.h" +#include #include "BaseInstance.h" +#include "minecraft/World.h" class QFileSystemWatcher; -class WorldList : public QAbstractListModel -{ +class WorldList : public QAbstractListModel { Q_OBJECT -public: - enum Columns - { - NameColumn, - GameModeColumn, - LastPlayedColumn, - SizeColumn, - InfoColumn - }; + public: + enum Columns { NameColumn, GameModeColumn, LastPlayedColumn, SizeColumn, InfoColumn }; - enum Roles - { - ObjectRole = Qt::UserRole + 1, - FolderRole, - SeedRole, - NameRole, - GameModeRole, - LastPlayedRole, - SizeRole, - IconFileRole - }; + enum Roles { ObjectRole = Qt::UserRole + 1, FolderRole, SeedRole, NameRole, GameModeRole, LastPlayedRole, SizeRole, IconFileRole }; - WorldList(const QString &dir, BaseInstance* instance); + WorldList(const QString& dir, BaseInstance* instance); - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const - { - return parent.isValid() ? 0 : static_cast(size()); - }; - virtual QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; - virtual int columnCount(const QModelIndex &parent) const; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const { return parent.isValid() ? 0 : static_cast(size()); }; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual int columnCount(const QModelIndex& parent) const; - size_t size() const - { - return worlds.size(); - }; - bool empty() const - { - return size() == 0; - } - World &operator[](size_t index) - { - return worlds[index]; - } + size_t size() const { return worlds.size(); }; + bool empty() const { return size() == 0; } + World& operator[](size_t index) { return worlds[index]; } /// Reloads the mod list and returns true if the list changed. virtual bool update(); @@ -91,13 +60,13 @@ public: virtual bool deleteWorlds(int first, int last); /// flags, mostly to support drag&drop - virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual Qt::ItemFlags flags(const QModelIndex& index) const; /// get data for drag action - virtual QMimeData *mimeData(const QModelIndexList &indexes) const; + virtual QMimeData* mimeData(const QModelIndexList& indexes) const; /// get the supported mime types virtual QStringList mimeTypes() const; /// process data from drop action - virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent); /// what drag actions do we support? virtual Qt::DropActions supportedDragActions() const; @@ -109,27 +78,21 @@ public: virtual bool isValid(); - QDir dir() const - { - return m_dir; - } + QDir dir() const { return m_dir; } QString instDirPath() const; - const QList &allWorlds() const - { - return worlds; - } + const QList& allWorlds() const { return worlds; } -private slots: + private slots: void directoryChanged(QString path); -signals: + signals: void changed(); -protected: + protected: BaseInstance* m_instance; - QFileSystemWatcher *m_watcher; + QFileSystemWatcher* m_watcher; bool is_watching; QDir m_dir; QList worlds; diff --git a/launcher/minecraft/auth/AccountData.cpp b/launcher/minecraft/auth/AccountData.cpp index 44f7e2563..474bf7c6e 100644 --- a/launcher/minecraft/auth/AccountData.cpp +++ b/launcher/minecraft/auth/AccountData.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -34,87 +34,90 @@ */ #include "AccountData.h" +#include +#include #include #include -#include -#include -#include #include +#include namespace { -void tokenToJSONV3(QJsonObject &parent, Katabasis::Token t, const char * tokenName) { - if(!t.persistent) { +void tokenToJSONV3(QJsonObject& parent, Katabasis::Token t, const char* tokenName) +{ + if (!t.persistent) { return; } QJsonObject out; - if(t.issueInstant.isValid()) { + if (t.issueInstant.isValid()) { out["iat"] = QJsonValue(t.issueInstant.toMSecsSinceEpoch() / 1000); } - if(t.notAfter.isValid()) { + if (t.notAfter.isValid()) { out["exp"] = QJsonValue(t.notAfter.toMSecsSinceEpoch() / 1000); } bool save = false; - if(!t.token.isEmpty()) { + if (!t.token.isEmpty()) { out["token"] = QJsonValue(t.token); save = true; } - if(!t.refresh_token.isEmpty()) { + if (!t.refresh_token.isEmpty()) { out["refresh_token"] = QJsonValue(t.refresh_token); save = true; } - if(t.extra.size()) { + if (t.extra.size()) { out["extra"] = QJsonObject::fromVariantMap(t.extra); save = true; } - if(save) { + if (save) { parent[tokenName] = out; } } -Katabasis::Token tokenFromJSONV3(const QJsonObject &parent, const char * tokenName) { +Katabasis::Token tokenFromJSONV3(const QJsonObject& parent, const char* tokenName) +{ Katabasis::Token out; auto tokenObject = parent.value(tokenName).toObject(); - if(tokenObject.isEmpty()) { + if (tokenObject.isEmpty()) { return out; } auto issueInstant = tokenObject.value("iat"); - if(issueInstant.isDouble()) { - out.issueInstant = QDateTime::fromMSecsSinceEpoch(((int64_t) issueInstant.toDouble()) * 1000); + if (issueInstant.isDouble()) { + out.issueInstant = QDateTime::fromMSecsSinceEpoch(((int64_t)issueInstant.toDouble()) * 1000); } auto notAfter = tokenObject.value("exp"); - if(notAfter.isDouble()) { - out.notAfter = QDateTime::fromMSecsSinceEpoch(((int64_t) notAfter.toDouble()) * 1000); + if (notAfter.isDouble()) { + out.notAfter = QDateTime::fromMSecsSinceEpoch(((int64_t)notAfter.toDouble()) * 1000); } auto token = tokenObject.value("token"); - if(token.isString()) { + if (token.isString()) { out.token = token.toString(); out.validity = Katabasis::Validity::Assumed; } auto refresh_token = tokenObject.value("refresh_token"); - if(refresh_token.isString()) { + if (refresh_token.isString()) { out.refresh_token = refresh_token.toString(); } auto extra = tokenObject.value("extra"); - if(extra.isObject()) { + if (extra.isObject()) { out.extra = extra.toObject().toVariantMap(); } return out; } -void profileToJSONV3(QJsonObject &parent, MinecraftProfile p, const char * tokenName) { - if(p.id.isEmpty()) { +void profileToJSONV3(QJsonObject& parent, MinecraftProfile p, const char* tokenName) +{ + if (p.id.isEmpty()) { return; } QJsonObject out; out["id"] = QJsonValue(p.id); out["name"] = QJsonValue(p.name); - if(!p.currentCape.isEmpty()) { + if (!p.currentCape.isEmpty()) { out["cape"] = p.currentCape; } @@ -123,19 +126,19 @@ void profileToJSONV3(QJsonObject &parent, MinecraftProfile p, const char * token skinObj["id"] = p.skin.id; skinObj["url"] = p.skin.url; skinObj["variant"] = p.skin.variant; - if(p.skin.data.size()) { + if (p.skin.data.size()) { skinObj["data"] = QString::fromLatin1(p.skin.data.toBase64()); } out["skin"] = skinObj; } QJsonArray capesArray; - for(auto & cape: p.capes) { + for (auto& cape : p.capes) { QJsonObject capeObj; capeObj["id"] = cape.id; capeObj["url"] = cape.url; capeObj["alias"] = cape.alias; - if(cape.data.size()) { + if (cape.data.size()) { capeObj["data"] = QString::fromLatin1(cape.data.toBase64()); } capesArray.push_back(capeObj); @@ -144,16 +147,17 @@ void profileToJSONV3(QJsonObject &parent, MinecraftProfile p, const char * token parent[tokenName] = out; } -MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * tokenName) { +MinecraftProfile profileFromJSONV3(const QJsonObject& parent, const char* tokenName) +{ MinecraftProfile out; auto tokenObject = parent.value(tokenName).toObject(); - if(tokenObject.isEmpty()) { + if (tokenObject.isEmpty()) { return out; } { auto idV = tokenObject.value("id"); auto nameV = tokenObject.value("name"); - if(!idV.isString() || !nameV.isString()) { + if (!idV.isString() || !nameV.isString()) { qWarning() << "mandatory profile attributes are missing or of unexpected type"; return MinecraftProfile(); } @@ -163,7 +167,7 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token { auto skinV = tokenObject.value("skin"); - if(!skinV.isObject()) { + if (!skinV.isObject()) { qWarning() << "skin is missing"; return MinecraftProfile(); } @@ -171,7 +175,7 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token auto idV = skinObj.value("id"); auto urlV = skinObj.value("url"); auto variantV = skinObj.value("variant"); - if(!idV.isString() || !urlV.isString() || !variantV.isString()) { + if (!idV.isString() || !urlV.isString() || !variantV.isString()) { qWarning() << "mandatory skin attributes are missing or of unexpected type"; return MinecraftProfile(); } @@ -181,11 +185,10 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token // data for skin is optional auto dataV = skinObj.value("data"); - if(dataV.isString()) { + if (dataV.isString()) { // TODO: validate base64 out.skin.data = QByteArray::fromBase64(dataV.toString().toLatin1()); - } - else if (!dataV.isUndefined()) { + } else if (!dataV.isUndefined()) { qWarning() << "skin data is something unexpected"; return MinecraftProfile(); } @@ -193,13 +196,13 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token { auto capesV = tokenObject.value("capes"); - if(!capesV.isArray()) { + if (!capesV.isArray()) { qWarning() << "capes is not an array!"; return MinecraftProfile(); } auto capesArray = capesV.toArray(); - for(auto capeV: capesArray) { - if(!capeV.isObject()) { + for (auto capeV : capesArray) { + if (!capeV.isObject()) { qWarning() << "cape is not an object!"; return MinecraftProfile(); } @@ -207,7 +210,7 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token auto idV = capeObj.value("id"); auto urlV = capeObj.value("url"); auto aliasV = capeObj.value("alias"); - if(!idV.isString() || !urlV.isString() || !aliasV.isString()) { + if (!idV.isString() || !urlV.isString() || !aliasV.isString()) { qWarning() << "mandatory skin attributes are missing or of unexpected type"; return MinecraftProfile(); } @@ -218,11 +221,10 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token // data for cape is optional. auto dataV = capeObj.value("data"); - if(dataV.isString()) { + if (dataV.isString()) { // TODO: validate base64 cape.data = QByteArray::fromBase64(dataV.toString().toLatin1()); - } - else if (!dataV.isUndefined()) { + } else if (!dataV.isUndefined()) { qWarning() << "cape data is something unexpected"; return MinecraftProfile(); } @@ -232,9 +234,9 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token // current cape { auto capeV = tokenObject.value("cape"); - if(capeV.isString()) { + if (capeV.isString()) { auto currentCape = capeV.toString(); - if(out.capes.contains(currentCape)) { + if (out.capes.contains(currentCape)) { out.currentCape = currentCape; } } @@ -243,8 +245,9 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token return out; } -void entitlementToJSONV3(QJsonObject &parent, MinecraftEntitlement p) { - if(p.validity == Katabasis::Validity::None) { +void entitlementToJSONV3(QJsonObject& parent, MinecraftEntitlement p) +{ + if (p.validity == Katabasis::Validity::None) { return; } QJsonObject out; @@ -253,15 +256,16 @@ void entitlementToJSONV3(QJsonObject &parent, MinecraftEntitlement p) { parent["entitlement"] = out; } -bool entitlementFromJSONV3(const QJsonObject &parent, MinecraftEntitlement & out) { +bool entitlementFromJSONV3(const QJsonObject& parent, MinecraftEntitlement& out) +{ auto entitlementObject = parent.value("entitlement").toObject(); - if(entitlementObject.isEmpty()) { + if (entitlementObject.isEmpty()) { return false; } { auto ownsMinecraftV = entitlementObject.value("ownsMinecraft"); auto canPlayMinecraftV = entitlementObject.value("canPlayMinecraft"); - if(!ownsMinecraftV.isBool() || !canPlayMinecraftV.isBool()) { + if (!ownsMinecraftV.isBool() || !canPlayMinecraftV.isBool()) { qWarning() << "mandatory attributes are missing or of unexpected type"; return false; } @@ -272,12 +276,12 @@ bool entitlementFromJSONV3(const QJsonObject &parent, MinecraftEntitlement & out return true; } -} +} // namespace -bool AccountData::resumeStateFromV2(QJsonObject data) { +bool AccountData::resumeStateFromV2(QJsonObject data) +{ // The JSON object must at least have a username for it to be valid. - if (!data.value("username").isString()) - { + if (!data.value("username").isString()) { qCritical() << "Can't load Mojang account info from JSON object. Username field is missing or of the wrong type."; return false; } @@ -287,14 +291,12 @@ bool AccountData::resumeStateFromV2(QJsonObject data) { QString accessToken = data.value("accessToken").toString(""); QJsonArray profileArray = data.value("profiles").toArray(); - if (profileArray.size() < 1) - { + if (profileArray.size() < 1) { qCritical() << "Can't load Mojang account with username \"" << userName << "\". No profiles found."; return false; } - struct AccountProfile - { + struct AccountProfile { QString id; QString name; bool legacy; @@ -304,24 +306,22 @@ bool AccountData::resumeStateFromV2(QJsonObject data) { int currentProfileIndex = 0; int index = -1; QString currentProfile = data.value("activeProfile").toString(""); - for (QJsonValue profileVal : profileArray) - { + for (QJsonValue profileVal : profileArray) { index++; QJsonObject profileObject = profileVal.toObject(); QString id = profileObject.value("id").toString(""); QString name = profileObject.value("name").toString(""); - bool legacy = profileObject.value("legacy").toBool(false); - if (id.isEmpty() || name.isEmpty()) - { + bool legacy_ = profileObject.value("legacy").toBool(false); + if (id.isEmpty() || name.isEmpty()) { qWarning() << "Unable to load a profile" << name << "because it was missing an ID or a name."; continue; } - if(id == currentProfile) { + if (id == currentProfile) { currentProfileIndex = index; } - profiles.append({id, name, legacy}); + profiles.append({ id, name, legacy_ }); } - auto & profile = profiles[currentProfileIndex]; + auto& profile = profiles[currentProfileIndex]; type = AccountType::Mojang; legacy = profile.legacy; @@ -339,14 +339,15 @@ bool AccountData::resumeStateFromV2(QJsonObject data) { return true; } -bool AccountData::resumeStateFromV3(QJsonObject data) { +bool AccountData::resumeStateFromV3(QJsonObject data) +{ auto typeV = data.value("type"); - if(!typeV.isString()) { + if (!typeV.isString()) { qWarning() << "Failed to parse account data: type is missing."; return false; } auto typeS = typeV.toString(); - if(typeS == "MSA") { + if (typeS == "MSA") { type = AccountType::MSA; } else if (typeS == "Mojang") { type = AccountType::Mojang; @@ -357,16 +358,16 @@ bool AccountData::resumeStateFromV3(QJsonObject data) { return false; } - if(type == AccountType::Mojang) { + if (type == AccountType::Mojang) { legacy = data.value("legacy").toBool(false); canMigrateToMSA = data.value("canMigrateToMSA").toBool(false); } - if(type == AccountType::MSA) { + 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 + } // 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"); @@ -374,9 +375,13 @@ bool AccountData::resumeStateFromV3(QJsonObject data) { } yggdrasilToken = tokenFromJSONV3(data, "ygg"); + // versions before 7.2 used "offline" as the offline token + if (yggdrasilToken.token == "offline") + yggdrasilToken.token = "0"; + minecraftProfile = profileFromJSONV3(data, "profile"); - if(!entitlementFromJSONV3(data, minecraftEntitlement)) { - if(minecraftProfile.validity != Katabasis::Validity::None) { + if (!entitlementFromJSONV3(data, minecraftEntitlement)) { + if (minecraftProfile.validity != Katabasis::Validity::None) { minecraftEntitlement.canPlayMinecraft = true; minecraftEntitlement.ownsMinecraft = true; minecraftEntitlement.validity = Katabasis::Validity::Assumed; @@ -387,26 +392,25 @@ bool AccountData::resumeStateFromV3(QJsonObject data) { return true; } -QJsonObject AccountData::saveState() const { +QJsonObject AccountData::saveState() const +{ QJsonObject output; - if(type == AccountType::Mojang) { + if (type == AccountType::Mojang) { output["type"] = "Mojang"; - if(legacy) { + if (legacy) { output["legacy"] = true; } - if(canMigrateToMSA) { + if (canMigrateToMSA) { output["canMigrateToMSA"] = true; } - } - else if (type == AccountType::MSA) { + } 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"); tokenToJSONV3(output, mojangservicesToken, "xrp-mc"); - } - else if (type == AccountType::Offline) { + } else if (type == AccountType::Offline) { output["type"] = "Offline"; } @@ -416,60 +420,68 @@ QJsonObject AccountData::saveState() const { return output; } -QString AccountData::userName() const { - if(type == AccountType::MSA) { +QString AccountData::userName() const +{ + if (type == AccountType::MSA) { return QString(); } return yggdrasilToken.extra["userName"].toString(); } -QString AccountData::accessToken() const { +QString AccountData::accessToken() const +{ return yggdrasilToken.token; } -QString AccountData::clientToken() const { - if(type != AccountType::Mojang) { +QString AccountData::clientToken() const +{ + if (type != AccountType::Mojang) { return QString(); } return yggdrasilToken.extra["clientToken"].toString(); } -void AccountData::setClientToken(QString clientToken) { - if(type != AccountType::Mojang) { +void AccountData::setClientToken(QString clientToken) +{ + if (type != AccountType::Mojang) { return; } yggdrasilToken.extra["clientToken"] = clientToken; } -void AccountData::generateClientTokenIfMissing() { - if(yggdrasilToken.extra.contains("clientToken")) { +void AccountData::generateClientTokenIfMissing() +{ + if (yggdrasilToken.extra.contains("clientToken")) { return; } invalidateClientToken(); } -void AccountData::invalidateClientToken() { - if(type != AccountType::Mojang) { +void AccountData::invalidateClientToken() +{ + if (type != AccountType::Mojang) { return; } yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{-}]")); } -QString AccountData::profileId() const { +QString AccountData::profileId() const +{ return minecraftProfile.id; } -QString AccountData::profileName() const { - if(minecraftProfile.name.size() == 0) { +QString AccountData::profileName() const +{ + if (minecraftProfile.name.size() == 0) { return QObject::tr("No profile (%1)").arg(accountDisplayString()); - } - else { + } else { return minecraftProfile.name; } } -QString AccountData::accountDisplayString() const { - switch(type) { +QString AccountData::accountDisplayString() const +{ + switch (type) { case AccountType::Mojang: { return userName(); } @@ -477,7 +489,7 @@ QString AccountData::accountDisplayString() const { return QObject::tr(""); } case AccountType::MSA: { - if(xboxApiToken.extra.contains("gtg")) { + if (xboxApiToken.extra.contains("gtg")) { return xboxApiToken.extra["gtg"].toString(); } return "Xbox profile missing"; @@ -488,6 +500,7 @@ QString AccountData::accountDisplayString() const { } } -QString AccountData::lastError() const { +QString AccountData::lastError() const +{ return errorString; } diff --git a/launcher/minecraft/auth/AccountData.h b/launcher/minecraft/auth/AccountData.h index 092e1691d..9b626c34e 100644 --- a/launcher/minecraft/auth/AccountData.h +++ b/launcher/minecraft/auth/AccountData.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -34,11 +34,11 @@ */ #pragma once -#include -#include -#include #include +#include #include +#include +#include struct Skin { QString id; @@ -71,22 +71,9 @@ struct MinecraftProfile { Katabasis::Validity validity = Katabasis::Validity::None; }; -enum class AccountType { - MSA, - Mojang, - Offline -}; +enum class AccountType { MSA, Mojang, Offline }; -enum class AccountState { - Unchecked, - Offline, - Working, - Online, - Disabled, - Errored, - Expired, - Gone -}; +enum class AccountState { Unchecked, Offline, Working, Online, Disabled, Errored, Expired, Gone }; struct AccountData { QJsonObject saveState() const; diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index d6f42b75c..c534ea3bd 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -37,14 +37,14 @@ #include "AccountData.h" #include "AccountTask.h" -#include +#include #include -#include -#include +#include #include +#include #include #include -#include +#include #include #include @@ -54,12 +54,10 @@ #include -enum AccountListVersion { - MojangOnly = 2, - MojangMSA = 3 -}; +enum AccountListVersion { MojangOnly = 2, MojangMSA = 3 }; -AccountList::AccountList(QObject *parent) : QAbstractListModel(parent) { +AccountList::AccountList(QObject* parent) : QAbstractListModel(parent) +{ m_refreshTimer = new QTimer(this); m_refreshTimer->setSingleShot(true); connect(m_refreshTimer, &QTimer::timeout, this, &AccountList::fillQueue); @@ -70,7 +68,8 @@ AccountList::AccountList(QObject *parent) : QAbstractListModel(parent) { AccountList::~AccountList() noexcept {} -int AccountList::findAccountByProfileId(const QString& profileId) const { +int AccountList::findAccountByProfileId(const QString& profileId) const +{ for (int i = 0; i < count(); i++) { MinecraftAccountPtr account = at(i); if (account->profileId() == profileId) { @@ -80,7 +79,8 @@ int AccountList::findAccountByProfileId(const QString& profileId) const { return -1; } -MinecraftAccountPtr AccountList::getAccountByProfileName(const QString& profileName) const { +MinecraftAccountPtr AccountList::getAccountByProfileName(const QString& profileName) const +{ for (int i = 0; i < count(); i++) { MinecraftAccountPtr account = at(i); if (account->profileName() == profileName) { @@ -95,11 +95,12 @@ const MinecraftAccountPtr AccountList::at(int i) const return MinecraftAccountPtr(m_accounts.at(i)); } -QStringList AccountList::profileNames() const { +QStringList AccountList::profileNames() const +{ QStringList out; - for(auto & account: m_accounts) { - auto profileName = account->profileName(); - if(profileName.isEmpty()) { + for (auto& account : m_accounts) { + auto profileName = account->profileName(); + if (profileName.isEmpty()) { continue; } out.append(profileName); @@ -122,14 +123,14 @@ void AccountList::addAccount(const MinecraftAccountPtr account) // override/replace existing account with the same profileId auto profileId = account->profileId(); - if(profileId.size()) { + if (profileId.size()) { auto existingAccount = findAccountByProfileId(profileId); - if(existingAccount != -1) { + if (existingAccount != -1) { qDebug() << "Replacing old account with a new one with the same profile ID!"; MinecraftAccountPtr existingAccountPtr = m_accounts[existingAccount]; m_accounts[existingAccount] = account; - if(m_defaultAccount == existingAccountPtr) { + if (m_defaultAccount == existingAccountPtr) { m_defaultAccount = account; } // disconnect notifications for changes in the account being replaced @@ -154,11 +155,9 @@ void AccountList::addAccount(const MinecraftAccountPtr account) void AccountList::removeAccount(QModelIndex index) { int row = index.row(); - if(index.isValid() && row >= 0 && row < m_accounts.size()) - { - auto & account = m_accounts[row]; - if(account == m_defaultAccount) - { + if (index.isValid() && row >= 0 && row < m_accounts.size()) { + auto& account = m_accounts[row]; + if (account == m_defaultAccount) { m_defaultAccount = nullptr; onDefaultAccountChanged(); } @@ -178,43 +177,34 @@ MinecraftAccountPtr AccountList::defaultAccount() const void AccountList::setDefaultAccount(MinecraftAccountPtr newAccount) { - if (!newAccount && m_defaultAccount) - { + if (!newAccount && m_defaultAccount) { int idx = 0; auto previousDefaultAccount = m_defaultAccount; m_defaultAccount = nullptr; - for (MinecraftAccountPtr account : m_accounts) - { - if (account == previousDefaultAccount) - { + for (MinecraftAccountPtr account : m_accounts) { + if (account == previousDefaultAccount) { emit dataChanged(index(idx), index(idx, columnCount(QModelIndex()) - 1)); } - idx ++; + idx++; } onDefaultAccountChanged(); - } - else - { + } else { auto currentDefaultAccount = m_defaultAccount; int currentDefaultAccountIdx = -1; auto newDefaultAccount = m_defaultAccount; int newDefaultAccountIdx = -1; int idx = 0; - for (MinecraftAccountPtr account : m_accounts) - { - if (account == newAccount) - { + for (MinecraftAccountPtr account : m_accounts) { + if (account == newAccount) { newDefaultAccount = account; newDefaultAccountIdx = idx; } - if(currentDefaultAccount == account) - { + if (currentDefaultAccount == account) { currentDefaultAccountIdx = idx; } idx++; } - if(currentDefaultAccount != newDefaultAccount) - { + if (currentDefaultAccount != newDefaultAccount) { emit dataChanged(index(currentDefaultAccountIdx), index(currentDefaultAccountIdx, columnCount(QModelIndex()) - 1)); emit dataChanged(index(newDefaultAccountIdx), index(newDefaultAccountIdx, columnCount(QModelIndex()) - 1)); m_defaultAccount = newDefaultAccount; @@ -231,27 +221,25 @@ void AccountList::accountChanged() void AccountList::accountActivityChanged(bool active) { - MinecraftAccount *account = qobject_cast(sender()); + MinecraftAccount* account = qobject_cast(sender()); bool found = false; for (int i = 0; i < count(); i++) { if (at(i).get() == account) { - emit dataChanged(index(i), index(i, columnCount(QModelIndex()) - 1)); + emit dataChanged(index(i), index(i, columnCount(QModelIndex()) - 1)); found = true; break; } } - if(found) { + if (found) { emit listActivityChanged(); - if(active) { + if (active) { beginActivity(); - } - else { + } else { endActivity(); } } } - void AccountList::onListChanged() { if (m_autosave) @@ -274,7 +262,7 @@ int AccountList::count() const return m_accounts.count(); } -QVariant AccountList::data(const QModelIndex &index, int role) const +QVariant AccountList::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); @@ -284,70 +272,67 @@ QVariant AccountList::data(const QModelIndex &index, int role) const MinecraftAccountPtr account = at(index.row()); - switch (role) - { + switch (role) { case Qt::DisplayRole: - switch (index.column()) - { - case ProfileNameColumn: { - return account->profileName(); - } + switch (index.column()) { + case ProfileNameColumn: { + return account->profileName(); + } - case NameColumn: - return account->accountDisplayString(); + case NameColumn: + return account->accountDisplayString(); - case TypeColumn: { - auto typeStr = account->typeString(); - typeStr[0] = typeStr[0].toUpper(); - return typeStr; - } + case TypeColumn: { + auto typeStr = account->typeString(); + typeStr[0] = typeStr[0].toUpper(); + return typeStr; + } - case StatusColumn: { - switch(account->accountState()) { - case AccountState::Unchecked: { - return tr("Unchecked", "Account status"); - } - case AccountState::Offline: { - return tr("Offline", "Account status"); - } - case AccountState::Online: { - return tr("Ready", "Account status"); - } - case AccountState::Working: { - return tr("Working", "Account status"); - } - case AccountState::Errored: { - return tr("Errored", "Account status"); - } - case AccountState::Expired: { - return tr("Expired", "Account status"); - } - case AccountState::Disabled: { - return tr("Disabled", "Account status"); - } - case AccountState::Gone: { - return tr("Gone", "Account status"); - } - default: { - return tr("Unknown", "Account status"); + case StatusColumn: { + switch (account->accountState()) { + case AccountState::Unchecked: { + return tr("Unchecked", "Account status"); + } + case AccountState::Offline: { + return tr("Offline", "Account status"); + } + case AccountState::Online: { + return tr("Ready", "Account status"); + } + case AccountState::Working: { + return tr("Working", "Account status"); + } + case AccountState::Errored: { + return tr("Errored", "Account status"); + } + case AccountState::Expired: { + return tr("Expired", "Account status"); + } + case AccountState::Disabled: { + return tr("Disabled", "Account status"); + } + case AccountState::Gone: { + return tr("Gone", "Account status"); + } + default: { + return tr("Unknown", "Account status"); + } } } - } - case MigrationColumn: { - if(account->isMSA() || account->isOffline()) { - return tr("N/A", "Can Migrate"); + case MigrationColumn: { + if (account->isMSA() || account->isOffline()) { + return tr("N/A", "Can Migrate"); + } + if (account->canMigrate()) { + return tr("Yes", "Can Migrate"); + } else { + return tr("No", "Can Migrate"); + } } - if (account->canMigrate()) { - return tr("Yes", "Can Migrate"); - } - else { - return tr("No", "Can Migrate"); - } - } - default: - return QVariant(); + default: + return QVariant(); } case Qt::ToolTipRole: @@ -362,88 +347,80 @@ QVariant AccountList::data(const QModelIndex &index, int role) const } else { return QVariant(); } - default: return QVariant(); } } -QVariant AccountList::headerData(int section, Qt::Orientation orientation, int role) const +QVariant AccountList::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const { - switch (role) - { - case Qt::DisplayRole: - switch (section) - { - case ProfileNameColumn: - return tr("Username"); - case NameColumn: - return tr("Account"); - case TypeColumn: - return tr("Type"); - case StatusColumn: - return tr("Status"); - case MigrationColumn: - return tr("Can Migrate?"); + switch (role) { + case Qt::DisplayRole: + switch (section) { + case ProfileNameColumn: + return tr("Username"); + case NameColumn: + return tr("Account"); + case TypeColumn: + return tr("Type"); + case StatusColumn: + return tr("Status"); + case MigrationColumn: + return tr("Can Migrate?"); + default: + return QVariant(); + } + + case Qt::ToolTipRole: + switch (section) { + case ProfileNameColumn: + return tr("Minecraft username associated with the account."); + case NameColumn: + return tr("User name of the account."); + case TypeColumn: + return tr("Type of the account - Mojang or MSA."); + case StatusColumn: + return tr("Current status of the account."); + case MigrationColumn: + return tr("Can this account migrate to a Microsoft account?"); + default: + return QVariant(); + } + default: return QVariant(); - } - - case Qt::ToolTipRole: - switch (section) - { - case ProfileNameColumn: - return tr("Minecraft username associated with the account."); - case NameColumn: - return tr("User name of the account."); - case TypeColumn: - return tr("Type of the account - Mojang or MSA."); - case StatusColumn: - return tr("Current status of the account."); - case MigrationColumn: - return tr("Can this account migrate to a Microsoft account?"); - default: - return QVariant(); - } - - default: - return QVariant(); } } -int AccountList::rowCount(const QModelIndex &parent) const +int AccountList::rowCount(const QModelIndex& parent) const { // Return count return parent.isValid() ? 0 : count(); } -int AccountList::columnCount(const QModelIndex &parent) const +int AccountList::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : NUM_COLUMNS; } -Qt::ItemFlags AccountList::flags(const QModelIndex &index) const +Qt::ItemFlags AccountList::flags(const QModelIndex& index) const { - if (index.row() < 0 || index.row() >= rowCount(index.parent()) || !index.isValid()) - { + if (index.row() < 0 || index.row() >= rowCount(index.parent()) || !index.isValid()) { return Qt::NoItemFlags; } return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; } -bool AccountList::setData(const QModelIndex &idx, const QVariant &value, int role) +bool AccountList::setData(const QModelIndex& idx, const QVariant& value, int role) { - if (idx.row() < 0 || idx.row() >= rowCount(idx) || !idx.isValid()) - { + if (idx.row() < 0 || idx.row() >= rowCount(idx) || !idx.isValid()) { return false; } - if(role == Qt::CheckStateRole) - { - if(value == Qt::Checked) - { + if (role == Qt::CheckStateRole) { + if (value == Qt::Checked) { MinecraftAccountPtr account = at(idx.row()); setDefaultAccount(account); } @@ -455,8 +432,7 @@ bool AccountList::setData(const QModelIndex &idx, const QVariant &value, int rol bool AccountList::loadList() { - if (m_listFilePath.isEmpty()) - { + if (m_listFilePath.isEmpty()) { qCritical() << "Can't load Mojang account list. No file path given and no default set."; return false; } @@ -465,8 +441,7 @@ bool AccountList::loadList() // Try to open the file and fail if we can't. // TODO: We should probably report this error to the user. - if (!file.open(QIODevice::ReadOnly)) - { + if (!file.open(QIODevice::ReadOnly)) { qCritical() << QString("Failed to read the account list file (%1).").arg(m_listFilePath).toUtf8(); return false; } @@ -479,17 +454,15 @@ bool AccountList::loadList() QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError); // Fail if the JSON is invalid. - if (parseError.error != QJsonParseError::NoError) - { + if (parseError.error != QJsonParseError::NoError) { qCritical() << QString("Failed to parse account list file: %1 at offset %2") - .arg(parseError.errorString(), QString::number(parseError.offset)) - .toUtf8(); + .arg(parseError.errorString(), QString::number(parseError.offset)) + .toUtf8(); return false; } // Make sure the root is an object. - if (!jsonDoc.isObject()) - { + if (!jsonDoc.isObject()) { qCritical() << "Invalid account list JSON: Root should be an array."; return false; } @@ -498,15 +471,13 @@ bool AccountList::loadList() // Make sure the format version matches. auto listVersion = root.value("formatVersion").toVariant().toInt(); - switch(listVersion) { + switch (listVersion) { case AccountListVersion::MojangOnly: { return loadV2(root); - } - break; + } break; case AccountListVersion::MojangMSA: { return loadV3(root); - } - break; + } break; default: { QString newName = "accounts-old.json"; qWarning() << "Unknown format version when loading account list. Existing one will be renamed to" << newName; @@ -517,21 +488,20 @@ bool AccountList::loadList() } } -bool AccountList::loadV2(QJsonObject& root) { +bool AccountList::loadV2(QJsonObject& root) +{ beginResetModel(); auto defaultUserName = root.value("activeAccount").toString(""); QJsonArray accounts = root.value("accounts").toArray(); - for (QJsonValue accountVal : accounts) - { + for (QJsonValue accountVal : accounts) { QJsonObject accountObj = accountVal.toObject(); MinecraftAccountPtr account = MinecraftAccount::loadFromJsonV2(accountObj); - if (account.get() != nullptr) - { + if (account.get() != nullptr) { auto profileId = account->profileId(); - if(!profileId.size()) { + if (!profileId.size()) { continue; } - if(findAccountByProfileId(profileId) != -1) { + if (findAccountByProfileId(profileId) != -1) { continue; } connect(account.get(), &MinecraftAccount::changed, this, &AccountList::accountChanged); @@ -540,9 +510,7 @@ bool AccountList::loadV2(QJsonObject& root) { if (defaultUserName.size() && account->mojangUserName() == defaultUserName) { m_defaultAccount = account; } - } - else - { + } else { qWarning() << "Failed to load an account."; } } @@ -550,30 +518,27 @@ bool AccountList::loadV2(QJsonObject& root) { return true; } -bool AccountList::loadV3(QJsonObject& root) { +bool AccountList::loadV3(QJsonObject& root) +{ beginResetModel(); QJsonArray accounts = root.value("accounts").toArray(); - for (QJsonValue accountVal : accounts) - { + for (QJsonValue accountVal : accounts) { QJsonObject accountObj = accountVal.toObject(); MinecraftAccountPtr account = MinecraftAccount::loadFromJsonV3(accountObj); - if (account.get() != nullptr) - { + if (account.get() != nullptr) { auto profileId = account->profileId(); - if(profileId.size()) { - if(findAccountByProfileId(profileId) != -1) { + if (profileId.size()) { + if (findAccountByProfileId(profileId) != -1) { continue; } } connect(account.get(), &MinecraftAccount::changed, this, &AccountList::accountChanged); connect(account.get(), &MinecraftAccount::activityChanged, this, &AccountList::accountActivityChanged); m_accounts.append(account); - if(accountObj.value("active").toBool(false)) { + if (accountObj.value("active").toBool(false)) { m_defaultAccount = account; } - } - else - { + } else { qWarning() << "Failed to load an account."; } } @@ -581,23 +546,20 @@ bool AccountList::loadV3(QJsonObject& root) { return true; } - bool AccountList::saveList() { - if (m_listFilePath.isEmpty()) - { + if (m_listFilePath.isEmpty()) { qCritical() << "Can't save Mojang account list. No file path given and no default set."; return false; } // make sure the parent folder exists - if(!FS::ensureFilePathExists(m_listFilePath)) + if (!FS::ensureFilePathExists(m_listFilePath)) return false; // make sure the file wasn't overwritten with a folder before (fixes a bug) QFileInfo finfo(m_listFilePath); - if(finfo.isDir()) - { + if (finfo.isDir()) { QDir badDir(m_listFilePath); badDir.removeRecursively(); } @@ -613,10 +575,9 @@ bool AccountList::saveList() // Build a list of accounts. qDebug() << "Building account array."; QJsonArray accounts; - for (MinecraftAccountPtr account : m_accounts) - { + for (MinecraftAccountPtr account : m_accounts) { QJsonObject accountObj = account->saveToJson(); - if(m_defaultAccount == account) { + if (m_defaultAccount == account) { accountObj["active"] = true; } accounts.append(accountObj); @@ -634,20 +595,18 @@ bool AccountList::saveList() // Try to open the file and fail if we can't. // TODO: We should probably report this error to the user. - if (!file.open(QIODevice::WriteOnly)) - { + if (!file.open(QIODevice::WriteOnly)) { qCritical() << QString("Failed to read the account list file (%1).").arg(m_listFilePath).toUtf8(); return false; } // Write the JSON to the file. file.write(doc.toJson()); - file.setPermissions(QFile::ReadOwner|QFile::WriteOwner|QFile::ReadUser|QFile::WriteUser); - if(file.commit()) { + file.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser); + if (file.commit()) { qDebug() << "Saved account list to" << m_listFilePath; return true; - } - else { + } else { qDebug() << "Failed to save accounts to" << m_listFilePath; return false; } @@ -661,30 +620,29 @@ void AccountList::setListFilePath(QString path, bool autosave) bool AccountList::anyAccountIsValid() { - for(auto account: m_accounts) - { - if(account->ownsMinecraft()) { + for (auto account : m_accounts) { + if (account->ownsMinecraft()) { return true; } } return false; } -void AccountList::fillQueue() { - - if(m_defaultAccount && m_defaultAccount->shouldRefresh()) { +void AccountList::fillQueue() +{ + if (m_defaultAccount && m_defaultAccount->shouldRefresh()) { auto idToRefresh = m_defaultAccount->internalId(); m_refreshQueue.push_back(idToRefresh); qDebug() << "AccountList: Queued default account with internal ID " << idToRefresh << " to refresh first"; } - for(int i = 0; i < count(); i++) { + for (int i = 0; i < count(); i++) { auto account = at(i); - if(account == m_defaultAccount) { + if (account == m_defaultAccount) { continue; } - if(account->shouldRefresh()) { + if (account->shouldRefresh()) { auto idToRefresh = account->internalId(); queueRefresh(idToRefresh); } @@ -692,40 +650,43 @@ void AccountList::fillQueue() { tryNext(); } -void AccountList::requestRefresh(QString accountId) { +void AccountList::requestRefresh(QString accountId) +{ auto index = m_refreshQueue.indexOf(accountId); - if(index != -1) { + if (index != -1) { m_refreshQueue.removeAt(index); } m_refreshQueue.push_front(accountId); qDebug() << "AccountList: Pushed account with internal ID " << accountId << " to the front of the queue"; - if(!isActive()) { + if (!isActive()) { tryNext(); } } -void AccountList::queueRefresh(QString accountId) { - if(m_refreshQueue.indexOf(accountId) != -1) { +void AccountList::queueRefresh(QString accountId) +{ + if (m_refreshQueue.indexOf(accountId) != -1) { return; } m_refreshQueue.push_back(accountId); qDebug() << "AccountList: Queued account with internal ID " << accountId << " to refresh"; } - -void AccountList::tryNext() { +void AccountList::tryNext() +{ while (m_refreshQueue.length()) { auto accountId = m_refreshQueue.front(); m_refreshQueue.pop_front(); - for(int i = 0; i < count(); i++) { + for (int i = 0; i < count(); i++) { auto account = at(i); - if(account->internalId() == accountId) { + if (account->internalId() == accountId) { m_currentTask = account->refresh(); - if(m_currentTask) { + if (m_currentTask) { connect(m_currentTask.get(), &AccountTask::succeeded, this, &AccountList::authSucceeded); connect(m_currentTask.get(), &AccountTask::failed, this, &AccountList::authFailed); m_currentTask->start(); - qDebug() << "RefreshSchedule: Processing account " << account->accountDisplayString() << " with internal ID " << accountId; + qDebug() << "RefreshSchedule: Processing account " << account->accountDisplayString() << " with internal ID " + << accountId; return; } } @@ -736,38 +697,43 @@ void AccountList::tryNext() { m_refreshTimer->start(1000 * 3600); } -void AccountList::authSucceeded() { +void AccountList::authSucceeded() +{ qDebug() << "RefreshSchedule: Background account refresh succeeded"; m_currentTask.reset(); m_nextTimer->start(1000 * 20); } -void AccountList::authFailed(QString reason) { +void AccountList::authFailed(QString reason) +{ qDebug() << "RefreshSchedule: Background account refresh failed: " << reason; m_currentTask.reset(); m_nextTimer->start(1000 * 20); } -bool AccountList::isActive() const { +bool AccountList::isActive() const +{ return m_activityCount != 0; } -void AccountList::beginActivity() { +void AccountList::beginActivity() +{ bool activating = m_activityCount == 0; m_activityCount++; - if(activating) { + if (activating) { emit activityChanged(true); } } -void AccountList::endActivity() { - if(m_activityCount == 0) { +void AccountList::endActivity() +{ + if (m_activityCount == 0) { qWarning() << m_name << " - Activity count would become below zero"; return; } bool deactivating = m_activityCount == 1; m_activityCount--; - if(deactivating) { + if (deactivating) { emit activityChanged(false); } } diff --git a/launcher/minecraft/auth/AccountList.h b/launcher/minecraft/auth/AccountList.h index a8c3529ac..6a0b01916 100644 --- a/launcher/minecraft/auth/AccountList.h +++ b/launcher/minecraft/auth/AccountList.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -37,26 +37,21 @@ #include "MinecraftAccount.h" -#include -#include #include +#include #include +#include /*! * List of available Mojang accounts. * This should be loaded in the background by Prism Launcher on startup. */ -class AccountList : public QAbstractListModel -{ +class AccountList : public QAbstractListModel { Q_OBJECT -public: - enum ModelRoles - { - PointerRole = 0x34B1CB48 - }; + public: + enum ModelRoles { PointerRole = 0x34B1CB48 }; - enum VListColumns - { + enum VListColumns { // TODO: Add icon column. ProfileNameColumn = 0, NameColumn, @@ -67,24 +62,24 @@ public: NUM_COLUMNS }; - explicit AccountList(QObject *parent = 0); + explicit AccountList(QObject* parent = 0); virtual ~AccountList() noexcept; const MinecraftAccountPtr at(int i) const; int count() const; //////// List Model Functions //////// - QVariant data(const QModelIndex &index, int role) const override; + QVariant data(const QModelIndex& index, int role) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - virtual int rowCount(const QModelIndex &parent) const override; - virtual int columnCount(const QModelIndex &parent) const override; - virtual Qt::ItemFlags flags(const QModelIndex &index) const override; - virtual bool setData(const QModelIndex &index, const QVariant &value, int role) override; + virtual int rowCount(const QModelIndex& parent) const override; + virtual int columnCount(const QModelIndex& parent) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role) override; void addAccount(const MinecraftAccountPtr account); void removeAccount(QModelIndex index); - int findAccountByProfileId(const QString &profileId) const; - MinecraftAccountPtr getAccountByProfileName(const QString &profileName) const; + int findAccountByProfileId(const QString& profileId) const; + MinecraftAccountPtr getAccountByProfileName(const QString& profileName) const; QStringList profileNames() const; // requesting a refresh pushes it to the front of the queue @@ -102,8 +97,8 @@ public: void setListFilePath(QString path, bool autosave = false); bool loadList(); - bool loadV2(QJsonObject &root); - bool loadV3(QJsonObject &root); + bool loadV2(QJsonObject& root); + bool loadV3(QJsonObject& root); bool saveList(); MinecraftAccountPtr defaultAccount() const; @@ -112,20 +107,20 @@ public: bool isActive() const; -protected: + protected: void beginActivity(); void endActivity(); -private: + private: const char* m_name; uint32_t m_activityCount = 0; -signals: + signals: void listChanged(); void listActivityChanged(); void defaultAccountChanged(); void activityChanged(bool active); -public slots: + public slots: /** * This is called when one of the accounts changes and the list needs to be updated */ @@ -141,16 +136,16 @@ public slots: */ void fillQueue(); -private slots: + private slots: void tryNext(); void authSucceeded(); void authFailed(QString reason); -protected: + protected: QList m_refreshQueue; - QTimer *m_refreshTimer; - QTimer *m_nextTimer; + QTimer* m_refreshTimer; + QTimer* m_nextTimer; shared_qobject_ptr m_currentTask; /*! @@ -178,4 +173,3 @@ protected: */ bool m_autosave = false; }; - diff --git a/launcher/minecraft/auth/AccountTask.cpp b/launcher/minecraft/auth/AccountTask.cpp index 4118c3c5d..4c3d6ee19 100644 --- a/launcher/minecraft/auth/AccountTask.cpp +++ b/launcher/minecraft/auth/AccountTask.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -36,43 +36,41 @@ #include "AccountTask.h" #include "MinecraftAccount.h" +#include +#include +#include +#include #include #include -#include -#include -#include -#include #include -AccountTask::AccountTask(AccountData *data, QObject *parent) - : Task(parent), m_data(data) +AccountTask::AccountTask(AccountData* data, QObject* parent) : Task(parent), m_data(data) { changeState(AccountTaskState::STATE_CREATED); } QString AccountTask::getStateMessage() const { - switch (m_taskState) - { - case AccountTaskState::STATE_CREATED: - return "Waiting..."; - case AccountTaskState::STATE_WORKING: - return tr("Sending request to auth servers..."); - case AccountTaskState::STATE_SUCCEEDED: - 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: - return tr("Failed to authenticate. The session has expired."); - case AccountTaskState::STATE_FAILED_GONE: - return tr("Failed to authenticate. The account no longer exists."); - default: - return tr("..."); + switch (m_taskState) { + case AccountTaskState::STATE_CREATED: + return "Waiting..."; + case AccountTaskState::STATE_WORKING: + return tr("Sending request to auth servers..."); + case AccountTaskState::STATE_SUCCEEDED: + 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: + return tr("Failed to authenticate. The session has expired."); + case AccountTaskState::STATE_FAILED_GONE: + return tr("Failed to authenticate. The account no longer exists."); + default: + return tr("..."); } } @@ -82,7 +80,7 @@ bool AccountTask::changeState(AccountTaskState newState, QString reason) // FIXME: virtual method invoked in constructor. // We want that behavior, but maybe make it less weird? setStatus(getStateMessage()); - switch(newState) { + switch (newState) { case AccountTaskState::STATE_CREATED: { m_data->errorString.clear(); return true; diff --git a/launcher/minecraft/auth/AccountTask.h b/launcher/minecraft/auth/AccountTask.h index 1bd6c59f3..82332c0b9 100644 --- a/launcher/minecraft/auth/AccountTask.h +++ b/launcher/minecraft/auth/AccountTask.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -37,10 +37,10 @@ #include -#include -#include -#include #include +#include +#include +#include #include "MinecraftAccount.h" @@ -50,37 +50,32 @@ class QNetworkReply; * Enum for describing the state of the current task. * Used by the getStateMessage function to determine what the status message should be. */ -enum class AccountTaskState -{ +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 - STATE_OFFLINE //!< soft failure. authentication failed in the first step in a 'soft' way + 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 + STATE_OFFLINE //!< soft failure. authentication failed in the first step in a 'soft' way }; -class AccountTask : public Task -{ +class AccountTask : public Task { Q_OBJECT -public: - explicit AccountTask(AccountData * data, QObject *parent = 0); - virtual ~AccountTask() {}; + public: + explicit AccountTask(AccountData* data, QObject* parent = 0); + virtual ~AccountTask(){}; AccountTaskState m_taskState = AccountTaskState::STATE_CREATED; - AccountTaskState taskState() { - return m_taskState; - } + AccountTaskState taskState() { return m_taskState; } -signals: - void showVerificationUriAndCode(const QUrl &uri, const QString &code, int expiresIn); + signals: + void showVerificationUriAndCode(const QUrl& uri, const QString& code, int expiresIn); void hideVerificationUriAndCode(); -protected: - + protected: /** * Returns the state message for the given state. * Used to set the status message for the task. @@ -88,10 +83,10 @@ protected: */ virtual QString getStateMessage() const; -protected slots: + protected slots: // NOTE: true -> non-terminal state, false -> terminal state bool changeState(AccountTaskState newState, QString reason = QString()); -protected: - AccountData *m_data = nullptr; + protected: + AccountData* m_data = nullptr; }; diff --git a/launcher/minecraft/auth/AuthRequest.cpp b/launcher/minecraft/auth/AuthRequest.cpp index a21634b7a..189978cc0 100644 --- a/launcher/minecraft/auth/AuthRequest.cpp +++ b/launcher/minecraft/auth/AuthRequest.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,44 +35,44 @@ #include +#include #include #include -#include #include #include "Application.h" #include "AuthRequest.h" #include "katabasis/Globals.h" -AuthRequest::AuthRequest(QObject *parent): QObject(parent) { -} +AuthRequest::AuthRequest(QObject* parent) : QObject(parent) {} -AuthRequest::~AuthRequest() { -} +AuthRequest::~AuthRequest() {} -void AuthRequest::get(const QNetworkRequest &req, int timeout/* = 60*1000*/) { +void AuthRequest::get(const QNetworkRequest& req, int timeout /* = 60*1000*/) +{ setup(req, QNetworkAccessManager::GetOperation); reply_ = APPLICATION->network()->get(request_); status_ = Requesting; timedReplies_.add(new Katabasis::Reply(reply_, timeout)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 connect(reply_, &QNetworkReply::errorOccurred, this, &AuthRequest::onRequestError); -#else // &QNetworkReply::error SIGNAL depricated +#else // &QNetworkReply::error SIGNAL depricated connect(reply_, QOverload::of(&QNetworkReply::error), this, &AuthRequest::onRequestError); #endif connect(reply_, &QNetworkReply::finished, this, &AuthRequest::onRequestFinished); connect(reply_, &QNetworkReply::sslErrors, this, &AuthRequest::onSslErrors); } -void AuthRequest::post(const QNetworkRequest &req, const QByteArray &data, int timeout/* = 60*1000*/) { +void AuthRequest::post(const QNetworkRequest& req, const QByteArray& data, int timeout /* = 60*1000*/) +{ setup(req, QNetworkAccessManager::PostOperation); data_ = data; status_ = Requesting; reply_ = APPLICATION->network()->post(request_, data_); timedReplies_.add(new Katabasis::Reply(reply_, timeout)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 connect(reply_, &QNetworkReply::errorOccurred, this, &AuthRequest::onRequestError); -#else // &QNetworkReply::error SIGNAL depricated +#else // &QNetworkReply::error SIGNAL depricated connect(reply_, QOverload::of(&QNetworkReply::error), this, &AuthRequest::onRequestError); #endif connect(reply_, &QNetworkReply::finished, this, &AuthRequest::onRequestFinished); @@ -80,35 +80,39 @@ void AuthRequest::post(const QNetworkRequest &req, const QByteArray &data, int t connect(reply_, &QNetworkReply::uploadProgress, this, &AuthRequest::onUploadProgress); } -void AuthRequest::onRequestFinished() { +void AuthRequest::onRequestFinished() +{ if (status_ == Idle) { return; } - if (reply_ != qobject_cast(sender())) { + if (reply_ != qobject_cast(sender())) { return; } httpStatus_ = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); finish(); } -void AuthRequest::onRequestError(QNetworkReply::NetworkError error) { +void AuthRequest::onRequestError(QNetworkReply::NetworkError error) +{ qWarning() << "AuthRequest::onRequestError: Error" << (int)error; if (status_ == Idle) { return; } - if (reply_ != qobject_cast(sender())) { + if (reply_ != qobject_cast(sender())) { return; } errorString_ = reply_->errorString(); httpStatus_ = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); error_ = error; qWarning() << "AuthRequest::onRequestError: Error string: " << errorString_; - qWarning() << "AuthRequest::onRequestError: HTTP status" << httpStatus_ << reply_->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); + qWarning() << "AuthRequest::onRequestError: HTTP status" << httpStatus_ + << reply_->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); // QTimer::singleShot(10, this, SLOT(finish())); } -void AuthRequest::onSslErrors(QList errors) { +void AuthRequest::onSslErrors(QList errors) +{ int i = 1; for (auto error : errors) { qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString(); @@ -118,23 +122,25 @@ void AuthRequest::onSslErrors(QList errors) { } } -void AuthRequest::onUploadProgress(qint64 uploaded, qint64 total) { +void AuthRequest::onUploadProgress(qint64 uploaded, qint64 total) +{ if (status_ == Idle) { qWarning() << "AuthRequest::onUploadProgress: No pending request"; return; } - if (reply_ != qobject_cast(sender())) { + if (reply_ != qobject_cast(sender())) { return; } // Restart timeout because request in progress - Katabasis::Reply *o2Reply = timedReplies_.find(reply_); - if(o2Reply) { + Katabasis::Reply* o2Reply = timedReplies_.find(reply_); + if (o2Reply) { o2Reply->start(); } emit uploadProgress(uploaded, total); } -void AuthRequest::setup(const QNetworkRequest &req, QNetworkAccessManager::Operation operation, const QByteArray &verb) { +void AuthRequest::setup(const QNetworkRequest& req, QNetworkAccessManager::Operation operation, const QByteArray& verb) +{ request_ = req; operation_ = operation; url_ = req.url(); @@ -152,7 +158,8 @@ void AuthRequest::setup(const QNetworkRequest &req, QNetworkAccessManager::Opera httpStatus_ = 0; } -void AuthRequest::finish() { +void AuthRequest::finish() +{ QByteArray data; if (status_ == Idle) { qWarning() << "AuthRequest::finish: No pending request"; diff --git a/launcher/minecraft/auth/AuthRequest.h b/launcher/minecraft/auth/AuthRequest.h index 89f7a123e..84d2a7d68 100644 --- a/launcher/minecraft/auth/AuthRequest.h +++ b/launcher/minecraft/auth/AuthRequest.h @@ -1,27 +1,26 @@ #pragma once -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include #include "katabasis/Reply.h" /// Makes authentication requests. -class AuthRequest: public QObject { +class AuthRequest : public QObject { Q_OBJECT -public: - explicit AuthRequest(QObject *parent = 0); + public: + explicit AuthRequest(QObject* parent = 0); ~AuthRequest(); -public slots: - void get(const QNetworkRequest &req, int timeout = 60*1000); - void post(const QNetworkRequest &req, const QByteArray &data, int timeout = 60*1000); + public slots: + void get(const QNetworkRequest& req, int timeout = 60 * 1000); + void post(const QNetworkRequest& req, const QByteArray& data, int timeout = 60 * 1000); - -signals: + signals: /// Emitted when a request has been completed or failed. void finished(QNetworkReply::NetworkError error, QByteArray data, QList headers); @@ -29,7 +28,7 @@ signals: /// Emitted when an upload has progressed. void uploadProgress(qint64 bytesSent, qint64 bytesTotal); -protected slots: + protected slots: /// Handle request finished. void onRequestFinished(); @@ -46,25 +45,23 @@ protected slots: /// Handle upload progress. void onUploadProgress(qint64 uploaded, qint64 total); -public: + public: QNetworkReply::NetworkError error_; int httpStatus_ = 0; QString errorString_; -protected: - void setup(const QNetworkRequest &request, QNetworkAccessManager::Operation operation, const QByteArray &verb = QByteArray()); + protected: + void setup(const QNetworkRequest& request, QNetworkAccessManager::Operation operation, const QByteArray& verb = QByteArray()); - enum Status { - Idle, Requesting, ReRequesting - }; + enum Status { Idle, Requesting, ReRequesting }; QNetworkRequest request_; QByteArray data_; - QNetworkReply *reply_; + QNetworkReply* reply_; Status status_; QNetworkAccessManager::Operation operation_; QUrl url_; Katabasis::ReplyList timedReplies_; - QTimer *timer_; + QTimer* timer_; }; diff --git a/launcher/minecraft/auth/AuthSession.cpp b/launcher/minecraft/auth/AuthSession.cpp index 6bea74a37..37534f983 100644 --- a/launcher/minecraft/auth/AuthSession.cpp +++ b/launcher/minecraft/auth/AuthSession.cpp @@ -1,7 +1,7 @@ #include "AuthSession.h" -#include #include #include +#include #include QString AuthSession::serializeUserProperties() @@ -16,22 +16,22 @@ QString AuthSession::serializeUserProperties() */ QJsonDocument value(userAttrs); return value.toJson(QJsonDocument::Compact); - } bool AuthSession::MakeOffline(QString offline_playername) { - if (status != PlayableOffline && status != PlayableOnline) - { + if (status != PlayableOffline && status != PlayableOnline) { return false; } session = "-"; + access_token = "0"; player_name = offline_playername; status = PlayableOffline; return true; } -void AuthSession::MakeDemo() { +void AuthSession::MakeDemo() +{ player_name = "Player"; demo = true; } diff --git a/launcher/minecraft/auth/AuthSession.h b/launcher/minecraft/auth/AuthSession.h index a75df506b..40519476d 100644 --- a/launcher/minecraft/auth/AuthSession.h +++ b/launcher/minecraft/auth/AuthSession.h @@ -1,22 +1,20 @@ #pragma once -#include #include +#include #include #include "QObjectPtr.h" class MinecraftAccount; class QNetworkAccessManager; -struct AuthSession -{ +struct AuthSession { bool MakeOffline(QString offline_playername); void MakeDemo(); QString serializeUserProperties(); - enum Status - { + enum Status { Undetermined, RequiresOAuth, RequiresPassword, @@ -45,7 +43,7 @@ struct AuthSession // Did the user request online mode? bool wants_online = true; - //Is this a demo session? + // Is this a demo session? bool demo = false; }; diff --git a/launcher/minecraft/auth/AuthStep.cpp b/launcher/minecraft/auth/AuthStep.cpp index ffa2581b6..6240cc549 100644 --- a/launcher/minecraft/auth/AuthStep.cpp +++ b/launcher/minecraft/auth/AuthStep.cpp @@ -1,7 +1,5 @@ #include "AuthStep.h" -AuthStep::AuthStep(AccountData *data) : QObject(nullptr), m_data(data) { -} +AuthStep::AuthStep(AccountData* data) : QObject(nullptr), m_data(data) {} AuthStep::~AuthStep() noexcept = default; - diff --git a/launcher/minecraft/auth/AuthStep.h b/launcher/minecraft/auth/AuthStep.h index 2a8dc2cac..becd9b0c5 100644 --- a/launcher/minecraft/auth/AuthStep.h +++ b/launcher/minecraft/auth/AuthStep.h @@ -1,33 +1,33 @@ #pragma once -#include #include #include +#include +#include "AccountTask.h" #include "QObjectPtr.h" #include "minecraft/auth/AccountData.h" -#include "AccountTask.h" class AuthStep : public QObject { Q_OBJECT -public: + public: using Ptr = shared_qobject_ptr; -public: - explicit AuthStep(AccountData *data); + public: + explicit AuthStep(AccountData* data); virtual ~AuthStep() noexcept; virtual QString describe() = 0; -public slots: + public slots: virtual void perform() = 0; virtual void rehydrate() = 0; -signals: + signals: void finished(AccountTaskState resultingState, QString message); - void showVerificationUriAndCode(const QUrl &uri, const QString &code, int expiresIn); + void showVerificationUriAndCode(const QUrl& uri, const QString& code, int expiresIn); void hideVerificationUriAndCode(); -protected: - AccountData *m_data; + protected: + AccountData* m_data; }; diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index 3b050ac0f..6c2f0805f 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -37,12 +37,14 @@ #include "MinecraftAccount.h" -#include -#include +#include +#include #include +#include +#include #include #include -#include +#include #include @@ -52,28 +54,30 @@ #include "flows/Mojang.h" #include "flows/Offline.h" -MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) { +MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) +{ data.internalId = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); } - -MinecraftAccountPtr MinecraftAccount::loadFromJsonV2(const QJsonObject& json) { +MinecraftAccountPtr MinecraftAccount::loadFromJsonV2(const QJsonObject& json) +{ MinecraftAccountPtr account(new MinecraftAccount()); - if(account->data.resumeStateFromV2(json)) { + if (account->data.resumeStateFromV2(json)) { return account; } return nullptr; } -MinecraftAccountPtr MinecraftAccount::loadFromJsonV3(const QJsonObject& json) { +MinecraftAccountPtr MinecraftAccount::loadFromJsonV3(const QJsonObject& json) +{ MinecraftAccountPtr account(new MinecraftAccount()); - if(account->data.resumeStateFromV3(json)) { + if (account->data.resumeStateFromV3(json)) { return account; } return nullptr; } -MinecraftAccountPtr MinecraftAccount::createFromUsername(const QString &username) +MinecraftAccountPtr MinecraftAccount::createFromUsername(const QString& username) { auto account = makeShared(); account->data.type = AccountType::Mojang; @@ -89,106 +93,113 @@ MinecraftAccountPtr MinecraftAccount::createBlankMSA() return account; } -MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username) +MinecraftAccountPtr MinecraftAccount::createOffline(const QString& username) { auto account = makeShared(); account->data.type = AccountType::Offline; - account->data.yggdrasilToken.token = "offline"; + account->data.yggdrasilToken.token = "0"; 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(QRegularExpression("[{}-]")); account->data.minecraftEntitlement.ownsMinecraft = true; account->data.minecraftEntitlement.canPlayMinecraft = true; - account->data.minecraftProfile.id = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); + account->data.minecraftProfile.id = uuidFromUsername(username).toString().remove(QRegularExpression("[{}-]")); account->data.minecraftProfile.name = username; account->data.minecraftProfile.validity = Katabasis::Validity::Certain; return account; } - QJsonObject MinecraftAccount::saveToJson() const { return data.saveState(); } -AccountState MinecraftAccount::accountState() const { +AccountState MinecraftAccount::accountState() const +{ return data.accountState; } -QPixmap MinecraftAccount::getFace() const { +QPixmap MinecraftAccount::getFace() const +{ QPixmap skinTexture; - if(!skinTexture.loadFromData(data.minecraftProfile.skin.data, "PNG")) { + if (!skinTexture.loadFromData(data.minecraftProfile.skin.data, "PNG")) { return QPixmap(); } QPixmap skin = QPixmap(8, 8); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + skin.fill(QColorConstants::Transparent); +#else + skin.fill(QColor(0, 0, 0, 0)); +#endif QPainter painter(&skin); painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8)); painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8)); return skin.scaled(64, 64, Qt::KeepAspectRatio); } - -shared_qobject_ptr MinecraftAccount::login(QString password) { +shared_qobject_ptr MinecraftAccount::login(QString password) +{ Q_ASSERT(m_currentTask.get() == nullptr); m_currentTask.reset(new MojangLogin(&data, password)); connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); - connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); + connect(m_currentTask.get(), &Task::aborted, this, [this] { authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; } -shared_qobject_ptr MinecraftAccount::loginMSA() { +shared_qobject_ptr MinecraftAccount::loginMSA() +{ Q_ASSERT(m_currentTask.get() == nullptr); m_currentTask.reset(new MSAInteractive(&data)); connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); - connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); + connect(m_currentTask.get(), &Task::aborted, this, [this] { authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; } -shared_qobject_ptr MinecraftAccount::loginOffline() { +shared_qobject_ptr MinecraftAccount::loginOffline() +{ Q_ASSERT(m_currentTask.get() == nullptr); m_currentTask.reset(new OfflineLogin(&data)); connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); - connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); + connect(m_currentTask.get(), &Task::aborted, this, [this] { authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; } -shared_qobject_ptr MinecraftAccount::refresh() { - if(m_currentTask) { +shared_qobject_ptr MinecraftAccount::refresh() +{ + if (m_currentTask) { return m_currentTask; } - if(data.type == AccountType::MSA) { + if (data.type == AccountType::MSA) { m_currentTask.reset(new MSASilent(&data)); - } - else if(data.type == AccountType::Offline) { + } else if (data.type == AccountType::Offline) { m_currentTask.reset(new OfflineRefresh(&data)); - } - else { + } else { m_currentTask.reset(new MojangRefresh(&data)); } connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); - connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); }); + connect(m_currentTask.get(), &Task::aborted, this, [this] { authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; } -shared_qobject_ptr MinecraftAccount::currentTask() { +shared_qobject_ptr MinecraftAccount::currentTask() +{ return m_currentTask; } - void MinecraftAccount::authSucceeded() { m_currentTask.reset(); @@ -205,28 +216,24 @@ void MinecraftAccount::authFailed(QString reason) } case AccountTaskState::STATE_FAILED_SOFT: { // NOTE: this doesn't do much. There was an error of some sort. - } - break; + } break; case AccountTaskState::STATE_FAILED_HARD: { - if(isMSA()) { + if (isMSA()) { data.msaToken.token = QString(); data.msaToken.refresh_token = QString(); data.msaToken.validity = Katabasis::Validity::None; data.validity_ = Katabasis::Validity::None; - } - else { + } else { data.yggdrasilToken.token = QString(); data.yggdrasilToken.validity = Katabasis::Validity::None; data.validity_ = Katabasis::Validity::None; } emit changed(); - } - break; + } break; case AccountTaskState::STATE_FAILED_GONE: { data.validity_ = Katabasis::Validity::None; emit changed(); - } - break; + } break; case AccountTaskState::STATE_CREATED: case AccountTaskState::STATE_WORKING: case AccountTaskState::STATE_SUCCEEDED: { @@ -237,21 +244,23 @@ void MinecraftAccount::authFailed(QString reason) emit activityChanged(false); } -bool MinecraftAccount::isActive() const { +bool MinecraftAccount::isActive() const +{ return !m_currentTask.isNull(); } -bool MinecraftAccount::shouldRefresh() const { +bool MinecraftAccount::shouldRefresh() const +{ /* * Never refresh accounts that are being used by the game, it breaks the game session. * Always refresh accounts that have not been refreshed yet during this session. * Don't refresh broken accounts. * Refresh accounts that would expire in the next 12 hours (fresh token validity is 24 hours). */ - if(isInUse()) { + if (isInUse()) { return false; } - switch(data.validity_) { + switch (data.validity_) { case Katabasis::Validity::Certain: { break; } @@ -266,7 +275,7 @@ bool MinecraftAccount::shouldRefresh() const { auto issuedTimestamp = data.yggdrasilToken.issueInstant; auto expiresTimestamp = data.yggdrasilToken.notAfter; - if(!expiresTimestamp.isValid()) { + if (!expiresTimestamp.isValid()) { expiresTimestamp = issuedTimestamp.addSecs(24 * 3600); } if (now.secsTo(expiresTimestamp) < (12 * 3600)) { @@ -277,14 +286,12 @@ bool MinecraftAccount::shouldRefresh() const { void MinecraftAccount::fillSession(AuthSessionPtr session) { - if(ownsMinecraft() && !hasProfile()) { + if (ownsMinecraft() && !hasProfile()) { session->status = AuthSession::RequiresProfileSetup; - } - else { - if(session->wants_online) { + } else { + if (session->wants_online) { session->status = AuthSession::PlayableOnline; - } - else { + } else { session->status = AuthSession::PlayableOffline; } } @@ -302,12 +309,9 @@ void MinecraftAccount::fillSession(AuthSessionPtr session) session->uuid = data.profileId(); // 'legacy' or 'mojang', depending on account type session->user_type = typeString(); - if (!session->access_token.isEmpty()) - { + if (!session->access_token.isEmpty()) { session->session = "token:" + data.accessToken() + ":" + data.profileId(); - } - else - { + } else { session->session = "-"; } } @@ -315,8 +319,7 @@ void MinecraftAccount::fillSession(AuthSessionPtr session) void MinecraftAccount::decrementUses() { Usable::decrementUses(); - if(!isInUse()) - { + if (!isInUse()) { emit changed(); // FIXME: we now need a better way to identify accounts... qWarning() << "Profile" << data.profileId() << "is no longer in use."; @@ -327,10 +330,31 @@ void MinecraftAccount::incrementUses() { bool wasInUse = isInUse(); Usable::incrementUses(); - if(!wasInUse) - { + if (!wasInUse) { emit changed(); // FIXME: we now need a better way to identify accounts... qWarning() << "Profile" << data.profileId() << "is now in use."; } } + +QUuid MinecraftAccount::uuidFromUsername(QString username) +{ + auto input = QString("OfflinePlayer:%1").arg(username).toUtf8(); + + // basically a reimplementation of Java's UUID#nameUUIDFromBytes + QByteArray digest = QCryptographicHash::hash(input, QCryptographicHash::Md5); + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + auto bOr = [](QByteArray& array, int index, char value) { array[index] = array.at(index) | value; }; + auto bAnd = [](QByteArray& array, int index, char value) { array[index] = array.at(index) & value; }; +#else + auto bOr = [](QByteArray& array, qsizetype index, char value) { array[index] |= value; }; + auto bAnd = [](QByteArray& array, qsizetype index, char value) { array[index] &= value; }; +#endif + bAnd(digest, 6, (char)0x0f); // clear version + bOr(digest, 6, (char)0x30); // set to version 3 + bAnd(digest, 8, (char)0x3f); // clear variant + bOr(digest, 8, (char)0x80); // set to IETF variant + + return QUuid::fromRfc4122(digest); +} diff --git a/launcher/minecraft/auth/MinecraftAccount.h b/launcher/minecraft/auth/MinecraftAccount.h index 0dcaeb531..f04f947fa 100644 --- a/launcher/minecraft/auth/MinecraftAccount.h +++ b/launcher/minecraft/auth/MinecraftAccount.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,20 +35,20 @@ #pragma once -#include -#include -#include #include -#include +#include #include +#include +#include #include +#include #include -#include "AuthSession.h" -#include "Usable.h" #include "AccountData.h" +#include "AuthSession.h" #include "QObjectPtr.h" +#include "Usable.h" class Task; class AccountTask; @@ -64,8 +64,7 @@ Q_DECLARE_METATYPE(MinecraftAccountPtr) * but we might as well add some things for it in Prism Launcher right now so * we don't have to rip the code to pieces to add it later. */ -struct AccountProfile -{ +struct AccountProfile { QString id; QString name; bool legacy; @@ -77,32 +76,30 @@ struct AccountProfile * Said information may include things such as that account's username, client token, and access * token if the user chose to stay logged in. */ -class MinecraftAccount : - public QObject, - public Usable -{ +class MinecraftAccount : public QObject, public Usable { Q_OBJECT -public: /* construction */ + public: /* construction */ //! Do not copy accounts. ever. - explicit MinecraftAccount(const MinecraftAccount &other, QObject *parent) = delete; + explicit MinecraftAccount(const MinecraftAccount& other, QObject* parent) = delete; //! Default constructor - explicit MinecraftAccount(QObject *parent = 0); + explicit MinecraftAccount(QObject* parent = 0); - static MinecraftAccountPtr createFromUsername(const QString &username); + static MinecraftAccountPtr createFromUsername(const QString& username); static MinecraftAccountPtr createBlankMSA(); - static MinecraftAccountPtr createOffline(const QString &username); + static MinecraftAccountPtr createOffline(const QString& username); - static MinecraftAccountPtr loadFromJsonV2(const QJsonObject &json); - static MinecraftAccountPtr loadFromJsonV3(const QJsonObject &json); + static MinecraftAccountPtr loadFromJsonV2(const QJsonObject& json); + static MinecraftAccountPtr loadFromJsonV3(const QJsonObject& json); + + static QUuid uuidFromUsername(QString username); //! Saves a MinecraftAccount to a JSON object and returns it. QJsonObject saveToJson() const; -public: /* manipulation */ - + public: /* manipulation */ /** * Attempt to login. Empty password means we use the token. * If the attempt fails because we already are performing some task, it returns false. @@ -117,70 +114,46 @@ public: /* manipulation */ shared_qobject_ptr currentTask(); -public: /* queries */ - QString internalId() const { - return data.internalId; - } + public: /* queries */ + QString internalId() const { return data.internalId; } - QString accountDisplayString() const { - return data.accountDisplayString(); - } + QString accountDisplayString() const { return data.accountDisplayString(); } - QString mojangUserName() const { - return data.userName(); - } + QString mojangUserName() const { return data.userName(); } - QString accessToken() const { - return data.accessToken(); - } + QString accessToken() const { return data.accessToken(); } - QString profileId() const { - return data.profileId(); - } + QString profileId() const { return data.profileId(); } - QString profileName() const { - return data.profileName(); - } + QString profileName() const { return data.profileName(); } bool isActive() const; - bool canMigrate() const { - return data.canMigrateToMSA; - } + bool canMigrate() const { return data.canMigrateToMSA; } - bool isMSA() const { - return data.type == AccountType::MSA; - } + bool isMSA() const { return data.type == AccountType::MSA; } - bool isOffline() const { - return data.type == AccountType::Offline; - } + bool isOffline() const { return data.type == AccountType::Offline; } - bool ownsMinecraft() const { - return data.minecraftEntitlement.ownsMinecraft; - } + bool ownsMinecraft() const { return data.minecraftEntitlement.ownsMinecraft; } - bool hasProfile() const { - return data.profileId().size() != 0; - } + bool hasProfile() const { return data.profileId().size() != 0; } - QString typeString() const { - switch(data.type) { + QString typeString() const + { + switch (data.type) { case AccountType::Mojang: { - if(data.legacy) { + if (data.legacy) { return "legacy"; } return "mojang"; - } - break; + } break; case AccountType::MSA: { return "msa"; - } - break; + } break; case AccountType::Offline: { return "offline"; - } - break; + } break; default: { return "unknown"; } @@ -192,19 +165,15 @@ public: /* queries */ //! Returns the current state of the account AccountState accountState() const; - AccountData * accountData() { - return &data; - } + AccountData* accountData() { return &data; } bool shouldRefresh() const; void fillSession(AuthSessionPtr session); - QString lastError() const { - return data.lastError(); - } + QString lastError() const { return data.lastError(); } -signals: + signals: /** * This signal is emitted when the account changes */ @@ -214,20 +183,17 @@ signals: // TODO: better signalling for the various possible state changes - especially errors -protected: /* variables */ + protected: /* variables */ AccountData data; // current task we are executing here shared_qobject_ptr m_currentTask; -protected: /* methods */ - + protected: /* methods */ void incrementUses() override; void decrementUses() override; -private -slots: + private slots: void authSucceeded(); void authFailed(QString reason); }; - diff --git a/launcher/minecraft/auth/Parsers.cpp b/launcher/minecraft/auth/Parsers.cpp index f3d9ad56b..f6179a93e 100644 --- a/launcher/minecraft/auth/Parsers.cpp +++ b/launcher/minecraft/auth/Parsers.cpp @@ -2,46 +2,51 @@ #include "Json.h" #include "Logging.h" -#include -#include #include +#include +#include namespace Parsers { -bool getDateTime(QJsonValue value, QDateTime & out) { - if(!value.isString()) { +bool getDateTime(QJsonValue value, QDateTime& out) +{ + if (!value.isString()) { return false; } out = QDateTime::fromString(value.toString(), Qt::ISODate); return out.isValid(); } -bool getString(QJsonValue value, QString & out) { - if(!value.isString()) { +bool getString(QJsonValue value, QString& out) +{ + if (!value.isString()) { return false; } out = value.toString(); return true; } -bool getNumber(QJsonValue value, double & out) { - if(!value.isDouble()) { +bool getNumber(QJsonValue value, double& out) +{ + if (!value.isDouble()) { return false; } out = value.toDouble(); return true; } -bool getNumber(QJsonValue value, int64_t & out) { - if(!value.isDouble()) { +bool getNumber(QJsonValue value, int64_t& out) +{ + if (!value.isDouble()) { return false; } - out = (int64_t) value.toDouble(); + out = (int64_t)value.toDouble(); return true; } -bool getBool(QJsonValue value, bool & out) { - if(!value.isBool()) { +bool getBool(QJsonValue value, bool& out) +{ + if (!value.isBool()) { return false; } out = value.toBool(); @@ -74,49 +79,50 @@ bool getBool(QJsonValue value, bool & out) { // 2148916238 = child account not linked to a family */ -bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, QString name) { - qDebug() << "Parsing" << name <<":"; +bool parseXTokenResponse(QByteArray& data, Katabasis::Token& output, QString name) +{ + qDebug() << "Parsing" << name << ":"; qCDebug(authCredentials()) << data; QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if(jsonError.error) { + if (jsonError.error) { qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString(); return false; } auto obj = doc.object(); - if(!getDateTime(obj.value("IssueInstant"), output.issueInstant)) { + if (!getDateTime(obj.value("IssueInstant"), output.issueInstant)) { qWarning() << "User IssueInstant is not a timestamp"; return false; } - if(!getDateTime(obj.value("NotAfter"), output.notAfter)) { + if (!getDateTime(obj.value("NotAfter"), output.notAfter)) { qWarning() << "User NotAfter is not a timestamp"; return false; } - if(!getString(obj.value("Token"), output.token)) { + if (!getString(obj.value("Token"), output.token)) { qWarning() << "User Token is not a string"; return false; } auto arrayVal = obj.value("DisplayClaims").toObject().value("xui"); - if(!arrayVal.isArray()) { + if (!arrayVal.isArray()) { qWarning() << "Missing xui claims array"; return false; } bool foundUHS = false; - for(auto item: arrayVal.toArray()) { - if(!item.isObject()) { + for (auto item : arrayVal.toArray()) { + if (!item.isObject()) { continue; } - auto obj = item.toObject(); - if(obj.contains("uhs")) { + auto obj_ = item.toObject(); + if (obj_.contains("uhs")) { foundUHS = true; } else { continue; } // consume all 'display claims' ... whatever that means - for(auto iter = obj.begin(); iter != obj.end(); iter++) { + for (auto iter = obj_.begin(); iter != obj_.end(); iter++) { QString claim; - if(!getString(obj.value(iter.key()), claim)) { + if (!getString(obj_.value(iter.key()), claim)) { qWarning() << "display claim " << iter.key() << " is not a string..."; return false; } @@ -125,7 +131,7 @@ bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, QString na break; } - if(!foundUHS) { + if (!foundUHS) { qWarning() << "Missing uhs"; return false; } @@ -134,46 +140,47 @@ bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, QString na return true; } -bool parseMinecraftProfile(QByteArray & data, MinecraftProfile &output) { +bool parseMinecraftProfile(QByteArray& data, MinecraftProfile& output) +{ qDebug() << "Parsing Minecraft profile..."; qCDebug(authCredentials()) << data; QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if(jsonError.error) { + if (jsonError.error) { qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString(); return false; } auto obj = doc.object(); - if(!getString(obj.value("id"), output.id)) { + if (!getString(obj.value("id"), output.id)) { qWarning() << "Minecraft profile id is not a string"; return false; } - if(!getString(obj.value("name"), output.name)) { + if (!getString(obj.value("name"), output.name)) { qWarning() << "Minecraft profile name is not a string"; return false; } auto skinsArray = obj.value("skins").toArray(); - for(auto skin: skinsArray) { + for (auto skin : skinsArray) { auto skinObj = skin.toObject(); Skin skinOut; - if(!getString(skinObj.value("id"), skinOut.id)) { + if (!getString(skinObj.value("id"), skinOut.id)) { continue; } QString state; - if(!getString(skinObj.value("state"), state)) { + if (!getString(skinObj.value("state"), state)) { continue; } - if(state != "ACTIVE") { + if (state != "ACTIVE") { continue; } - if(!getString(skinObj.value("url"), skinOut.url)) { + if (!getString(skinObj.value("url"), skinOut.url)) { continue; } - if(!getString(skinObj.value("variant"), skinOut.variant)) { + if (!getString(skinObj.value("variant"), skinOut.variant)) { continue; } // we deal with only the active skin @@ -183,23 +190,23 @@ bool parseMinecraftProfile(QByteArray & data, MinecraftProfile &output) { auto capesArray = obj.value("capes").toArray(); QString currentCape; - for(auto cape: capesArray) { + for (auto cape : capesArray) { auto capeObj = cape.toObject(); Cape capeOut; - if(!getString(capeObj.value("id"), capeOut.id)) { + if (!getString(capeObj.value("id"), capeOut.id)) { continue; } QString state; - if(!getString(capeObj.value("state"), state)) { + if (!getString(capeObj.value("state"), state)) { continue; } - if(state == "ACTIVE") { + if (state == "ACTIVE") { currentCape = capeOut.id; } - if(!getString(capeObj.value("url"), capeOut.url)) { + if (!getString(capeObj.value("url"), capeOut.url)) { continue; } - if(!getString(capeObj.value("alias"), capeOut.alias)) { + if (!getString(capeObj.value("alias"), capeOut.alias)) { continue; } @@ -211,30 +218,33 @@ bool parseMinecraftProfile(QByteArray & data, MinecraftProfile &output) { } namespace { - // these skin URLs are for the MHF_Steve and MHF_Alex accounts (made by a Mojang employee) - // they are needed because the session server doesn't return skin urls for default skins - static const QString SKIN_URL_STEVE = "http://textures.minecraft.net/texture/1a4af718455d4aab528e7a61f86fa25e6a369d1768dcb13f7df319a713eb810b"; - static const QString SKIN_URL_ALEX = "http://textures.minecraft.net/texture/83cee5ca6afcdb171285aa00e8049c297b2dbeba0efb8ff970a5677a1b644032"; +// these skin URLs are for the MHF_Steve and MHF_Alex accounts (made by a Mojang employee) +// they are needed because the session server doesn't return skin urls for default skins +static const QString SKIN_URL_STEVE = + "http://textures.minecraft.net/texture/1a4af718455d4aab528e7a61f86fa25e6a369d1768dcb13f7df319a713eb810b"; +static const QString SKIN_URL_ALEX = + "http://textures.minecraft.net/texture/83cee5ca6afcdb171285aa00e8049c297b2dbeba0efb8ff970a5677a1b644032"; - bool isDefaultModelSteve(QString uuid) { - // need to calculate *Java* hashCode of UUID - // if number is even, skin/model is steve, otherwise it is alex +bool isDefaultModelSteve(QString uuid) +{ + // need to calculate *Java* hashCode of UUID + // if number is even, skin/model is steve, otherwise it is alex - // just in case dashes are in the id - uuid.remove('-'); + // just in case dashes are in the id + uuid.remove('-'); - if (uuid.size() != 32) { - return true; - } - - // qulonglong is guaranteed to be 64 bits - // we need to use unsigned numbers to guarantee truncation below - qulonglong most = uuid.left(16).toULongLong(nullptr, 16); - qulonglong least = uuid.right(16).toULongLong(nullptr, 16); - qulonglong xored = most ^ least; - return ((static_cast(xored >> 32)) ^ static_cast(xored)) % 2 == 0; + if (uuid.size() != 32) { + return true; } + + // qulonglong is guaranteed to be 64 bits + // we need to use unsigned numbers to guarantee truncation below + qulonglong most = uuid.left(16).toULongLong(nullptr, 16); + qulonglong least = uuid.right(16).toULongLong(nullptr, 16); + qulonglong xored = most ^ least; + return ((static_cast(xored >> 32)) ^ static_cast(xored)) % 2 == 0; } +} // namespace /** Uses session server for skin/cape lookup instead of profile, @@ -270,31 +280,32 @@ decoded base64 "value": } */ -bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) { +bool parseMinecraftProfileMojang(QByteArray& data, MinecraftProfile& output) +{ qDebug() << "Parsing Minecraft profile..."; qCDebug(authCredentials()) << data; QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if(jsonError.error) { + if (jsonError.error) { qWarning() << "Failed to parse response as JSON: " << jsonError.errorString(); return false; } auto obj = Json::requireObject(doc, "mojang minecraft profile"); - if(!getString(obj.value("id"), output.id)) { + if (!getString(obj.value("id"), output.id)) { qWarning() << "Minecraft profile id is not a string"; return false; } - if(!getString(obj.value("name"), output.name)) { + if (!getString(obj.value("name"), output.name)) { qWarning() << "Minecraft profile name is not a string"; return false; } auto propsArray = obj.value("properties").toArray(); QByteArray texturePayload; - for( auto p : propsArray) { + for (auto p : propsArray) { auto pObj = p.toObject(); auto name = pObj.value("name"); if (!name.isString() || name.toString() != "textures") { @@ -321,7 +332,7 @@ bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) { } doc = QJsonDocument::fromJson(texturePayload, &jsonError); - if(jsonError.error) { + if (jsonError.error) { qWarning() << "Failed to parse response as JSON: " << jsonError.errorString(); return false; } @@ -357,8 +368,7 @@ bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) { // might not be present getString(meta.value("model"), skinOut.variant); } - } - else if (idx.key() == "CAPE") { + } else if (idx.key() == "CAPE") { auto cape = idx->toObject(); if (!getString(cape.value("url"), capeOut.url)) { qWarning() << "Cape url is not a string"; @@ -374,7 +384,7 @@ bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) { output.skin = skinOut; if (capeOut.alias == "cape") { - output.capes = QMap({{capeOut.alias, capeOut}}); + output.capes = QMap({ { capeOut.alias, capeOut } }); output.currentCape = capeOut.alias; } @@ -382,13 +392,14 @@ bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) { return true; } -bool parseMinecraftEntitlements(QByteArray & data, MinecraftEntitlement &output) { +bool parseMinecraftEntitlements(QByteArray& data, MinecraftEntitlement& output) +{ qDebug() << "Parsing Minecraft entitlements..."; qCDebug(authCredentials()) << data; QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if(jsonError.error) { + if (jsonError.error) { qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString(); return false; } @@ -398,16 +409,16 @@ bool parseMinecraftEntitlements(QByteArray & data, MinecraftEntitlement &output) output.ownsMinecraft = false; auto itemsArray = obj.value("items").toArray(); - for(auto item: itemsArray) { + for (auto item : itemsArray) { auto itemObj = item.toObject(); QString name; - if(!getString(itemObj.value("name"), name)) { + if (!getString(itemObj.value("name"), name)) { continue; } - if(name == "game_minecraft") { + if (name == "game_minecraft") { output.canPlayMinecraft = true; } - if(name == "product_minecraft") { + if (name == "product_minecraft") { output.ownsMinecraft = true; } } @@ -415,47 +426,50 @@ bool parseMinecraftEntitlements(QByteArray & data, MinecraftEntitlement &output) return true; } -bool parseRolloutResponse(QByteArray & data, bool& result) { +bool parseRolloutResponse(QByteArray& data, bool& result) +{ qDebug() << "Parsing Rollout response..."; qCDebug(authCredentials()) << data; QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if(jsonError.error) { - qWarning() << "Failed to parse response from https://api.minecraftservices.com/rollout/v1/msamigration as JSON: " << jsonError.errorString(); + if (jsonError.error) { + qWarning() << "Failed to parse response from https://api.minecraftservices.com/rollout/v1/msamigration as JSON: " + << jsonError.errorString(); return false; } auto obj = doc.object(); QString feature; - if(!getString(obj.value("feature"), feature)) { + if (!getString(obj.value("feature"), feature)) { qWarning() << "Rollout feature is not a string"; return false; } - if(feature != "msamigration") { + if (feature != "msamigration") { qWarning() << "Rollout feature is not what we expected (msamigration), but is instead \"" << feature << "\""; return false; } - if(!getBool(obj.value("rollout"), result)) { + if (!getBool(obj.value("rollout"), result)) { qWarning() << "Rollout feature is not a string"; return false; } return true; } -bool parseMojangResponse(QByteArray & data, Katabasis::Token &output) { +bool parseMojangResponse(QByteArray& data, Katabasis::Token& output) +{ QJsonParseError jsonError; qDebug() << "Parsing Mojang response..."; qCDebug(authCredentials()) << data; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if(jsonError.error) { + if (jsonError.error) { qWarning() << "Failed to parse response from api.minecraftservices.com/launcher/login as JSON: " << jsonError.errorString(); return false; } auto obj = doc.object(); double expires_in = 0; - if(!getNumber(obj.value("expires_in"), expires_in)) { + if (!getNumber(obj.value("expires_in"), expires_in)) { qWarning() << "expires_in is not a valid number"; return false; } @@ -464,13 +478,13 @@ bool parseMojangResponse(QByteArray & data, Katabasis::Token &output) { output.notAfter = currentTime.addSecs(expires_in); QString username; - if(!getString(obj.value("username"), username)) { + if (!getString(obj.value("username"), username)) { qWarning() << "username is not valid"; return false; } // TODO: it's a JWT... validate it? - if(!getString(obj.value("access_token"), output.token)) { + if (!getString(obj.value("access_token"), output.token)) { qWarning() << "access_token is not valid"; return false; } @@ -479,4 +493,4 @@ bool parseMojangResponse(QByteArray & data, Katabasis::Token &output) { return true; } -} +} // namespace Parsers diff --git a/launcher/minecraft/auth/Parsers.h b/launcher/minecraft/auth/Parsers.h index 2666d890c..d073f9994 100644 --- a/launcher/minecraft/auth/Parsers.h +++ b/launcher/minecraft/auth/Parsers.h @@ -2,19 +2,18 @@ #include "AccountData.h" -namespace Parsers -{ - bool getDateTime(QJsonValue value, QDateTime & out); - bool getString(QJsonValue value, QString & out); - bool getNumber(QJsonValue value, double & out); - bool getNumber(QJsonValue value, int64_t & out); - bool getBool(QJsonValue value, bool & out); +namespace Parsers { +bool getDateTime(QJsonValue value, QDateTime& out); +bool getString(QJsonValue value, QString& out); +bool getNumber(QJsonValue value, double& out); +bool getNumber(QJsonValue value, int64_t& out); +bool getBool(QJsonValue value, bool& out); - bool parseXTokenResponse(QByteArray &data, Katabasis::Token &output, QString name); - bool parseMojangResponse(QByteArray &data, Katabasis::Token &output); +bool parseXTokenResponse(QByteArray& data, Katabasis::Token& output, QString name); +bool parseMojangResponse(QByteArray& data, Katabasis::Token& output); - bool parseMinecraftProfile(QByteArray &data, MinecraftProfile &output); - bool parseMinecraftProfileMojang(QByteArray &data, MinecraftProfile &output); - bool parseMinecraftEntitlements(QByteArray &data, MinecraftEntitlement &output); - bool parseRolloutResponse(QByteArray &data, bool& result); -} +bool parseMinecraftProfile(QByteArray& data, MinecraftProfile& output); +bool parseMinecraftProfileMojang(QByteArray& data, MinecraftProfile& output); +bool parseMinecraftEntitlements(QByteArray& data, MinecraftEntitlement& output); +bool parseRolloutResponse(QByteArray& data, bool& result); +} // namespace Parsers diff --git a/launcher/minecraft/auth/Yggdrasil.cpp b/launcher/minecraft/auth/Yggdrasil.cpp index d3e7ccddb..97f2a78d4 100644 --- a/launcher/minecraft/auth/Yggdrasil.cpp +++ b/launcher/minecraft/auth/Yggdrasil.cpp @@ -16,24 +16,24 @@ #include "Yggdrasil.h" #include "AccountData.h" +#include +#include +#include +#include #include #include -#include -#include -#include -#include #include #include "Application.h" -Yggdrasil::Yggdrasil(AccountData *data, QObject *parent) - : AccountTask(data, parent) +Yggdrasil::Yggdrasil(AccountData* data, QObject* parent) : AccountTask(data, parent) { changeState(AccountTaskState::STATE_CREATED); } -void Yggdrasil::sendRequest(QUrl endpoint, QByteArray content) { +void Yggdrasil::sendRequest(QUrl endpoint, QByteArray content) +{ changeState(AccountTaskState::STATE_WORKING); QNetworkRequest netRequest(endpoint); @@ -52,10 +52,10 @@ void Yggdrasil::sendRequest(QUrl endpoint, QByteArray content) { connect(&counter, &QTimer::timeout, this, &Yggdrasil::heartbeat); } -void Yggdrasil::executeTask() { -} +void Yggdrasil::executeTask() {} -void Yggdrasil::refresh() { +void Yggdrasil::refresh() +{ start(); /* * { @@ -90,7 +90,8 @@ void Yggdrasil::refresh() { sendRequest(reqUrl, requestData); } -void Yggdrasil::login(QString password) { +void Yggdrasil::login(QString password) +{ start(); /* * { @@ -136,20 +137,21 @@ void Yggdrasil::login(QString password) { sendRequest(reqUrl, requestData); } - - -void Yggdrasil::refreshTimers(qint64, qint64) { +void Yggdrasil::refreshTimers(qint64, qint64) +{ timeout_keeper.stop(); timeout_keeper.start(timeout_max); progress(count = 0, timeout_max); } -void Yggdrasil::heartbeat() { +void Yggdrasil::heartbeat() +{ count += time_step; progress(count, timeout_max); } -bool Yggdrasil::abort() { +bool Yggdrasil::abort() +{ progress(timeout_max, timeout_max); // TODO: actually use this in a meaningful way m_aborted = Yggdrasil::BY_USER; @@ -157,14 +159,16 @@ bool Yggdrasil::abort() { return true; } -void Yggdrasil::abortByTimeout() { +void Yggdrasil::abortByTimeout() +{ progress(timeout_max, timeout_max); // TODO: actually use this in a meaningful way m_aborted = Yggdrasil::BY_TIMEOUT; m_netReply->abort(); } -void Yggdrasil::sslErrors(QList errors) { +void Yggdrasil::sslErrors(QList errors) +{ int i = 1; for (auto error : errors) { qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString(); @@ -174,7 +178,8 @@ void Yggdrasil::sslErrors(QList errors) { } } -void Yggdrasil::processResponse(QJsonObject responseData) { +void Yggdrasil::processResponse(QJsonObject responseData) +{ // Read the response data. We need to get the client token, access token, and the selected // profile. qDebug() << "Processing authentication response."; @@ -188,11 +193,11 @@ void Yggdrasil::processResponse(QJsonObject responseData) { changeState(AccountTaskState::STATE_FAILED_HARD, tr("Authentication server didn't send a client token.")); return; } - if(m_data->clientToken().isEmpty()) { + if (m_data->clientToken().isEmpty()) { m_data->setClientToken(clientToken); - } - else if(clientToken != m_data->clientToken()) { - changeState(AccountTaskState::STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported.")); + } else if (clientToken != m_data->clientToken()) { + changeState(AccountTaskState::STATE_FAILED_HARD, + tr("Authentication server attempted to change the client token. This isn't supported.")); return; } @@ -220,8 +225,7 @@ void Yggdrasil::processResponse(QJsonObject responseData) { for (auto i = profileObj.constBegin(); i != profileObj.constEnd(); ++i) { if (i.key() == "name" && i.value().isString()) { m_data->minecraftProfile.name = i->toString(); - } - else if (i.key() == "id" && i.value().isString()) { + } else if (i.key() == "id" && i.value().isString()) { m_data->minecraftProfile.id = i->toString(); } } @@ -237,50 +241,43 @@ void Yggdrasil::processResponse(QJsonObject responseData) { changeState(AccountTaskState::STATE_SUCCEEDED); } -void Yggdrasil::processReply() { +void Yggdrasil::processReply() +{ changeState(AccountTaskState::STATE_WORKING); - switch (m_netReply->error()) - { - case QNetworkReply::NoError: - break; - case QNetworkReply::TimeoutError: - changeState(AccountTaskState::STATE_FAILED_SOFT, tr("Authentication operation timed out.")); - return; - case QNetworkReply::OperationCanceledError: - changeState(AccountTaskState::STATE_FAILED_SOFT, tr("Authentication operation cancelled.")); - return; - case QNetworkReply::SslHandshakeFailedError: - changeState( - AccountTaskState::STATE_FAILED_SOFT, - tr( - "SSL Handshake failed.
There might be a few causes for it:
" - "
    " - "
  • You use Windows and need to update your root certificates, please install any outstanding updates.
  • " - "
  • Some device on your network is interfering with SSL traffic. In that case, " - "you have bigger worries than Minecraft not starting.
  • " - "
  • Possibly something else. Check the log file for details
  • " - "
" - ) - ); - return; - // used for invalid credentials and similar errors. Fall through. - case QNetworkReply::ContentAccessDenied: - case QNetworkReply::ContentOperationNotPermittedError: - break; - case QNetworkReply::ContentGoneError: { - changeState( - AccountTaskState::STATE_FAILED_GONE, - tr("The Mojang account no longer exists. It may have been migrated to a Microsoft account.") - ); - return; - } - default: - changeState( - AccountTaskState::STATE_FAILED_SOFT, - tr("Authentication operation failed due to a network error: %1 (%2)").arg(m_netReply->errorString()).arg(m_netReply->error()) - ); - return; + switch (m_netReply->error()) { + case QNetworkReply::NoError: + break; + case QNetworkReply::TimeoutError: + changeState(AccountTaskState::STATE_FAILED_SOFT, tr("Authentication operation timed out.")); + return; + case QNetworkReply::OperationCanceledError: + changeState(AccountTaskState::STATE_FAILED_SOFT, tr("Authentication operation cancelled.")); + return; + case QNetworkReply::SslHandshakeFailedError: + changeState(AccountTaskState::STATE_FAILED_SOFT, + tr("SSL Handshake failed.
There might be a few causes for it:
" + "
    " + "
  • You use Windows and need to update your root certificates, please install any outstanding updates.
  • " + "
  • Some device on your network is interfering with SSL traffic. In that case, " + "you have bigger worries than Minecraft not starting.
  • " + "
  • Possibly something else. Check the log file for details
  • " + "
")); + return; + // used for invalid credentials and similar errors. Fall through. + case QNetworkReply::ContentAccessDenied: + case QNetworkReply::ContentOperationNotPermittedError: + break; + case QNetworkReply::ContentGoneError: { + changeState(AccountTaskState::STATE_FAILED_GONE, + tr("The Mojang account no longer exists. It may have been migrated to a Microsoft account.")); + return; + } + default: + changeState(AccountTaskState::STATE_FAILED_SOFT, tr("Authentication operation failed due to a network error: %1 (%2)") + .arg(m_netReply->errorString()) + .arg(m_netReply->error())); + return; } // Try to parse the response regardless of the response code. @@ -299,12 +296,11 @@ void Yggdrasil::processReply() { if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0) { processResponse(replyData.size() > 0 ? doc.object() : QJsonObject()); return; - } - else { - changeState( - AccountTaskState::STATE_FAILED_SOFT, - tr("Failed to parse authentication server response JSON response: %1 at offset %2.").arg(jsonError.errorString()).arg(jsonError.offset) - ); + } else { + changeState(AccountTaskState::STATE_FAILED_SOFT, + tr("Failed to parse authentication server response JSON response: %1 at offset %2.") + .arg(jsonError.errorString()) + .arg(jsonError.offset)); qCritical() << replyData; } return; @@ -320,34 +316,26 @@ void Yggdrasil::processReply() { // stuff there. qDebug() << "The request failed, but the server gave us an error message. Processing error."; processError(doc.object()); - } - else { + } else { // The server didn't say anything regarding the error. Give the user an unknown // error. qDebug() << "The request failed and the server gave no error message. Unknown error."; changeState( AccountTaskState::STATE_FAILED_SOFT, - tr("An unknown error occurred when trying to communicate with the authentication server: %1").arg(m_netReply->errorString()) - ); + tr("An unknown error occurred when trying to communicate with the authentication server: %1").arg(m_netReply->errorString())); } } -void Yggdrasil::processError(QJsonObject responseData) { +void Yggdrasil::processError(QJsonObject responseData) +{ QJsonValue errorVal = responseData.value("error"); QJsonValue errorMessageValue = responseData.value("errorMessage"); QJsonValue causeVal = responseData.value("cause"); if (errorVal.isString() && errorMessageValue.isString()) { - m_error = std::shared_ptr( - new Error { - errorVal.toString(""), - errorMessageValue.toString(""), - causeVal.toString("") - } - ); + m_error = std::shared_ptr(new Error{ errorVal.toString(""), errorMessageValue.toString(""), causeVal.toString("") }); changeState(AccountTaskState::STATE_FAILED_HARD, m_error->m_errorMessageVerbose); - } - else { + } else { // Error is not in standard format. Don't set m_error and return unknown error. changeState(AccountTaskState::STATE_FAILED_HARD, tr("An unknown Yggdrasil error occurred.")); } diff --git a/launcher/minecraft/auth/Yggdrasil.h b/launcher/minecraft/auth/Yggdrasil.h index 4f52a04c2..560d7fb81 100644 --- a/launcher/minecraft/auth/Yggdrasil.h +++ b/launcher/minecraft/auth/Yggdrasil.h @@ -17,10 +17,10 @@ #include "AccountTask.h" -#include -#include -#include #include +#include +#include +#include #include "MinecraftAccount.h" @@ -30,35 +30,25 @@ class QNetworkReply; /** * A Yggdrasil task is a task that performs an operation on a given mojang account. */ -class Yggdrasil : public AccountTask -{ +class Yggdrasil : public AccountTask { Q_OBJECT -public: - explicit Yggdrasil( - AccountData *data, - QObject *parent = 0 - ); + public: + explicit Yggdrasil(AccountData* data, QObject* parent = 0); virtual ~Yggdrasil() = default; void refresh(); void login(QString password); - struct Error - { + struct Error { QString m_errorMessageShort; QString m_errorMessageVerbose; QString m_cause; }; std::shared_ptr m_error; - enum AbortedBy - { - BY_NOTHING, - BY_USER, - BY_TIMEOUT - } m_aborted = BY_NOTHING; + enum AbortedBy { BY_NOTHING, BY_USER, BY_TIMEOUT } m_aborted = BY_NOTHING; -protected: + protected: void executeTask() override; /** @@ -78,24 +68,24 @@ protected: */ virtual void processError(QJsonObject responseData); -protected slots: + protected slots: void processReply(); void refreshTimers(qint64, qint64); void heartbeat(); void sslErrors(QList); void abortByTimeout(); -public slots: + public slots: virtual bool abort() override; -private: + private: void sendRequest(QUrl endpoint, QByteArray content); -protected: - QNetworkReply *m_netReply = nullptr; + protected: + QNetworkReply* m_netReply = nullptr; QTimer timeout_keeper; QTimer counter; - int count = 0; // num msec since time reset + int count = 0; // num msec since time reset const int timeout_max = 30000; const int time_step = 50; diff --git a/launcher/minecraft/auth/flows/AuthFlow.cpp b/launcher/minecraft/auth/flows/AuthFlow.cpp index 4f78e8c31..c51839a8c 100644 --- a/launcher/minecraft/auth/flows/AuthFlow.cpp +++ b/launcher/minecraft/auth/flows/AuthFlow.cpp @@ -1,36 +1,33 @@ -#include -#include -#include #include +#include +#include +#include #include "AuthFlow.h" #include "katabasis/Globals.h" #include -AuthFlow::AuthFlow(AccountData * data, QObject *parent) : - AccountTask(data, parent) +AuthFlow::AuthFlow(AccountData* data, QObject* parent) : AccountTask(data, parent) {} + +void AuthFlow::succeed() { -} - -void AuthFlow::succeed() { m_data->validity_ = Katabasis::Validity::Certain; - changeState( - AccountTaskState::STATE_SUCCEEDED, - tr("Finished all authentication steps") - ); + changeState(AccountTaskState::STATE_SUCCEEDED, tr("Finished all authentication steps")); } -void AuthFlow::executeTask() { - if(m_currentStep) { +void AuthFlow::executeTask() +{ + if (m_currentStep) { return; } changeState(AccountTaskState::STATE_WORKING, tr("Initializing")); nextStep(); } -void AuthFlow::nextStep() { - if(m_steps.size() == 0) { +void AuthFlow::nextStep() +{ + if (m_steps.size() == 0) { // we got to the end without an incident... assume this is all. m_currentStep.reset(); succeed(); @@ -46,15 +43,13 @@ void AuthFlow::nextStep() { m_currentStep->perform(); } - -QString AuthFlow::getStateMessage() const { - switch (m_taskState) - { +QString AuthFlow::getStateMessage() const +{ + switch (m_taskState) { case AccountTaskState::STATE_WORKING: { - if(m_currentStep) { + if (m_currentStep) { return m_currentStep->describe(); - } - else { + } else { return tr("Working..."); } } @@ -64,8 +59,9 @@ QString AuthFlow::getStateMessage() const { } } -void AuthFlow::stepFinished(AccountTaskState resultingState, QString message) { - if(changeState(resultingState, message)) { +void AuthFlow::stepFinished(AccountTaskState resultingState, QString message) +{ + if (changeState(resultingState, message)) { nextStep(); } } diff --git a/launcher/minecraft/auth/flows/AuthFlow.h b/launcher/minecraft/auth/flows/AuthFlow.h index e067cc99f..c2c412abc 100644 --- a/launcher/minecraft/auth/flows/AuthFlow.h +++ b/launcher/minecraft/auth/flows/AuthFlow.h @@ -1,45 +1,42 @@ #pragma once -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include #include -#include "minecraft/auth/Yggdrasil.h" #include "minecraft/auth/AccountData.h" #include "minecraft/auth/AccountTask.h" #include "minecraft/auth/AuthStep.h" +#include "minecraft/auth/Yggdrasil.h" -class AuthFlow : public AccountTask -{ +class AuthFlow : public AccountTask { Q_OBJECT -public: - explicit AuthFlow(AccountData * data, QObject *parent = 0); + public: + explicit AuthFlow(AccountData* data, QObject* parent = 0); - Katabasis::Validity validity() { - return m_data->validity_; - }; + Katabasis::Validity validity() { return m_data->validity_; }; QString getStateMessage() const override; void executeTask() override; -signals: + signals: void activityChanged(Katabasis::Activity activity); -private slots: + private slots: void stepFinished(AccountTaskState resultingState, QString message); -protected: + protected: void succeed(); void nextStep(); -protected: + protected: QList m_steps; AuthStep::Ptr m_currentStep; }; diff --git a/launcher/minecraft/auth/flows/MSA.cpp b/launcher/minecraft/auth/flows/MSA.cpp index f1987e0c1..f0399342e 100644 --- a/launcher/minecraft/auth/flows/MSA.cpp +++ b/launcher/minecraft/auth/flows/MSA.cpp @@ -1,15 +1,16 @@ #include "MSA.h" -#include "minecraft/auth/steps/MSAStep.h" -#include "minecraft/auth/steps/XboxUserStep.h" -#include "minecraft/auth/steps/XboxAuthorizationStep.h" -#include "minecraft/auth/steps/LauncherLoginStep.h" -#include "minecraft/auth/steps/XboxProfileStep.h" #include "minecraft/auth/steps/EntitlementsStep.h" -#include "minecraft/auth/steps/MinecraftProfileStep.h" #include "minecraft/auth/steps/GetSkinStep.h" +#include "minecraft/auth/steps/LauncherLoginStep.h" +#include "minecraft/auth/steps/MSAStep.h" +#include "minecraft/auth/steps/MinecraftProfileStep.h" +#include "minecraft/auth/steps/XboxAuthorizationStep.h" +#include "minecraft/auth/steps/XboxProfileStep.h" +#include "minecraft/auth/steps/XboxUserStep.h" -MSASilent::MSASilent(AccountData* data, QObject* parent) : AuthFlow(data, parent) { +MSASilent::MSASilent(AccountData* data, QObject* parent) : AuthFlow(data, parent) +{ m_steps.append(makeShared(m_data, MSAStep::Action::Refresh)); m_steps.append(makeShared(m_data)); m_steps.append(makeShared(m_data, &m_data->xboxApiToken, "http://xboxlive.com", "Xbox")); @@ -21,10 +22,8 @@ MSASilent::MSASilent(AccountData* data, QObject* parent) : AuthFlow(data, parent m_steps.append(makeShared(m_data)); } -MSAInteractive::MSAInteractive( - AccountData* data, - QObject* parent -) : AuthFlow(data, parent) { +MSAInteractive::MSAInteractive(AccountData* data, QObject* parent) : AuthFlow(data, parent) +{ m_steps.append(makeShared(m_data, MSAStep::Action::Login)); m_steps.append(makeShared(m_data)); m_steps.append(makeShared(m_data, &m_data->xboxApiToken, "http://xboxlive.com", "Xbox")); diff --git a/launcher/minecraft/auth/flows/MSA.h b/launcher/minecraft/auth/flows/MSA.h index 14a4ff430..e403d530f 100644 --- a/launcher/minecraft/auth/flows/MSA.h +++ b/launcher/minecraft/auth/flows/MSA.h @@ -1,22 +1,14 @@ #pragma once #include "AuthFlow.h" -class MSAInteractive : public AuthFlow -{ +class MSAInteractive : public AuthFlow { Q_OBJECT -public: - explicit MSAInteractive( - AccountData *data, - QObject *parent = 0 - ); + public: + explicit MSAInteractive(AccountData* data, QObject* parent = 0); }; -class MSASilent : public AuthFlow -{ +class MSASilent : public AuthFlow { Q_OBJECT -public: - explicit MSASilent( - AccountData * data, - QObject *parent = 0 - ); + public: + explicit MSASilent(AccountData* data, QObject* parent = 0); }; diff --git a/launcher/minecraft/auth/flows/Mojang.cpp b/launcher/minecraft/auth/flows/Mojang.cpp index 5900ea988..7e2db16fa 100644 --- a/launcher/minecraft/auth/flows/Mojang.cpp +++ b/launcher/minecraft/auth/flows/Mojang.cpp @@ -1,25 +1,20 @@ #include "Mojang.h" -#include "minecraft/auth/steps/YggdrasilStep.h" -#include "minecraft/auth/steps/MinecraftProfileStepMojang.h" -#include "minecraft/auth/steps/MigrationEligibilityStep.h" #include "minecraft/auth/steps/GetSkinStep.h" +#include "minecraft/auth/steps/MigrationEligibilityStep.h" +#include "minecraft/auth/steps/MinecraftProfileStepMojang.h" +#include "minecraft/auth/steps/YggdrasilStep.h" -MojangRefresh::MojangRefresh( - AccountData *data, - QObject *parent -) : AuthFlow(data, parent) { +MojangRefresh::MojangRefresh(AccountData* data, QObject* parent) : AuthFlow(data, parent) +{ m_steps.append(makeShared(m_data, QString())); m_steps.append(makeShared(m_data)); m_steps.append(makeShared(m_data)); m_steps.append(makeShared(m_data)); } -MojangLogin::MojangLogin( - AccountData *data, - QString password, - QObject *parent -): AuthFlow(data, parent), m_password(password) { +MojangLogin::MojangLogin(AccountData* data, QString password, QObject* parent) : AuthFlow(data, parent), m_password(password) +{ m_steps.append(makeShared(m_data, m_password)); m_steps.append(makeShared(m_data)); m_steps.append(makeShared(m_data)); diff --git a/launcher/minecraft/auth/flows/Mojang.h b/launcher/minecraft/auth/flows/Mojang.h index c09c81a80..779ca7e34 100644 --- a/launcher/minecraft/auth/flows/Mojang.h +++ b/launcher/minecraft/auth/flows/Mojang.h @@ -1,26 +1,17 @@ #pragma once #include "AuthFlow.h" -class MojangRefresh : public AuthFlow -{ +class MojangRefresh : public AuthFlow { Q_OBJECT -public: - explicit MojangRefresh( - AccountData *data, - QObject *parent = 0 - ); + public: + explicit MojangRefresh(AccountData* data, QObject* parent = 0); }; -class MojangLogin : public AuthFlow -{ +class MojangLogin : public AuthFlow { Q_OBJECT -public: - explicit MojangLogin( - AccountData *data, - QString password, - QObject *parent = 0 - ); + public: + explicit MojangLogin(AccountData* data, QString password, QObject* parent = 0); -private: + private: QString m_password; }; diff --git a/launcher/minecraft/auth/flows/Offline.cpp b/launcher/minecraft/auth/flows/Offline.cpp index d5c632715..3770b869a 100644 --- a/launcher/minecraft/auth/flows/Offline.cpp +++ b/launcher/minecraft/auth/flows/Offline.cpp @@ -2,16 +2,12 @@ #include "minecraft/auth/steps/OfflineStep.h" -OfflineRefresh::OfflineRefresh( - AccountData *data, - QObject *parent -) : AuthFlow(data, parent) { +OfflineRefresh::OfflineRefresh(AccountData* data, QObject* parent) : AuthFlow(data, parent) +{ m_steps.append(makeShared(m_data)); } -OfflineLogin::OfflineLogin( - AccountData *data, - QObject *parent -) : AuthFlow(data, parent) { +OfflineLogin::OfflineLogin(AccountData* data, QObject* parent) : AuthFlow(data, parent) +{ m_steps.append(makeShared(m_data)); } diff --git a/launcher/minecraft/auth/flows/Offline.h b/launcher/minecraft/auth/flows/Offline.h index 5d1f83a46..2bc9c7612 100644 --- a/launcher/minecraft/auth/flows/Offline.h +++ b/launcher/minecraft/auth/flows/Offline.h @@ -1,22 +1,14 @@ #pragma once #include "AuthFlow.h" -class OfflineRefresh : public AuthFlow -{ +class OfflineRefresh : public AuthFlow { Q_OBJECT -public: - explicit OfflineRefresh( - AccountData *data, - QObject *parent = 0 - ); + public: + explicit OfflineRefresh(AccountData* data, QObject* parent = 0); }; -class OfflineLogin : public AuthFlow -{ +class OfflineLogin : public AuthFlow { Q_OBJECT -public: - explicit OfflineLogin( - AccountData *data, - QObject *parent = 0 - ); + public: + explicit OfflineLogin(AccountData* data, QObject* parent = 0); }; diff --git a/launcher/minecraft/auth/steps/EntitlementsStep.cpp b/launcher/minecraft/auth/steps/EntitlementsStep.cpp index bd6042926..0573dcb6e 100644 --- a/launcher/minecraft/auth/steps/EntitlementsStep.cpp +++ b/launcher/minecraft/auth/steps/EntitlementsStep.cpp @@ -11,12 +11,13 @@ EntitlementsStep::EntitlementsStep(AccountData* data) : AuthStep(data) {} EntitlementsStep::~EntitlementsStep() noexcept = default; -QString EntitlementsStep::describe() { +QString EntitlementsStep::describe() +{ return tr("Determining game ownership."); } - -void EntitlementsStep::perform() { +void EntitlementsStep::perform() +{ auto uuid = QUuid::createUuid(); m_entitlementsRequestId = uuid.toString().remove('{').remove('}'); auto url = "https://api.minecraftservices.com/entitlements/license?requestId=" + m_entitlementsRequestId; @@ -24,22 +25,22 @@ void EntitlementsStep::perform() { request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Accept", "application/json"); request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8()); - AuthRequest *requestor = new AuthRequest(this); + AuthRequest* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &EntitlementsStep::onRequestDone); requestor->get(request); qDebug() << "Getting entitlements..."; } -void EntitlementsStep::rehydrate() { +void EntitlementsStep::rehydrate() +{ // NOOP, for now. We only save bools and there's nothing to check. } -void EntitlementsStep::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList headers -) { - auto requestor = qobject_cast(QObject::sender()); +void EntitlementsStep::onRequestDone([[maybe_unused]] QNetworkReply::NetworkError error, + QByteArray data, + [[maybe_unused]] QList headers) +{ + auto requestor = qobject_cast(QObject::sender()); requestor->deleteLater(); qCDebug(authCredentials()) << data; diff --git a/launcher/minecraft/auth/steps/EntitlementsStep.h b/launcher/minecraft/auth/steps/EntitlementsStep.h index 9412ae79d..be16bda13 100644 --- a/launcher/minecraft/auth/steps/EntitlementsStep.h +++ b/launcher/minecraft/auth/steps/EntitlementsStep.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class EntitlementsStep : public AuthStep { Q_OBJECT -public: - explicit EntitlementsStep(AccountData *data); + public: + explicit EntitlementsStep(AccountData* data); virtual ~EntitlementsStep() noexcept; void perform() override; @@ -17,9 +16,9 @@ public: QString describe() override; -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); -private: + private: QString m_entitlementsRequestId; }; diff --git a/launcher/minecraft/auth/steps/GetSkinStep.cpp b/launcher/minecraft/auth/steps/GetSkinStep.cpp index 3521f8dc5..520877020 100644 --- a/launcher/minecraft/auth/steps/GetSkinStep.cpp +++ b/launcher/minecraft/auth/steps/GetSkinStep.cpp @@ -6,34 +6,32 @@ #include "minecraft/auth/AuthRequest.h" #include "minecraft/auth/Parsers.h" -GetSkinStep::GetSkinStep(AccountData* data) : AuthStep(data) { - -} +GetSkinStep::GetSkinStep(AccountData* data) : AuthStep(data) {} GetSkinStep::~GetSkinStep() noexcept = default; -QString GetSkinStep::describe() { +QString GetSkinStep::describe() +{ return tr("Getting skin."); } -void GetSkinStep::perform() { +void GetSkinStep::perform() +{ auto url = QUrl(m_data->minecraftProfile.skin.url); QNetworkRequest request = QNetworkRequest(url); - AuthRequest *requestor = new AuthRequest(this); + AuthRequest* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &GetSkinStep::onRequestDone); requestor->get(request); } -void GetSkinStep::rehydrate() { +void GetSkinStep::rehydrate() +{ // NOOP, for now. } -void GetSkinStep::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList headers -) { - auto requestor = qobject_cast(QObject::sender()); +void GetSkinStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList headers) +{ + auto requestor = qobject_cast(QObject::sender()); requestor->deleteLater(); if (error == QNetworkReply::NoError) { diff --git a/launcher/minecraft/auth/steps/GetSkinStep.h b/launcher/minecraft/auth/steps/GetSkinStep.h index 6b97371e5..105e497d1 100644 --- a/launcher/minecraft/auth/steps/GetSkinStep.h +++ b/launcher/minecraft/auth/steps/GetSkinStep.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class GetSkinStep : public AuthStep { Q_OBJECT -public: - explicit GetSkinStep(AccountData *data); + public: + explicit GetSkinStep(AccountData* data); virtual ~GetSkinStep() noexcept; void perform() override; @@ -17,6 +16,6 @@ public: QString describe() override; -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); }; diff --git a/launcher/minecraft/auth/steps/LauncherLoginStep.cpp b/launcher/minecraft/auth/steps/LauncherLoginStep.cpp index 8a26cbe77..c57f51113 100644 --- a/launcher/minecraft/auth/steps/LauncherLoginStep.cpp +++ b/launcher/minecraft/auth/steps/LauncherLoginStep.cpp @@ -8,17 +8,17 @@ #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" -LauncherLoginStep::LauncherLoginStep(AccountData* data) : AuthStep(data) { - -} +LauncherLoginStep::LauncherLoginStep(AccountData* data) : AuthStep(data) {} LauncherLoginStep::~LauncherLoginStep() noexcept = default; -QString LauncherLoginStep::describe() { +QString LauncherLoginStep::describe() +{ return tr("Accessing Mojang services."); } -void LauncherLoginStep::perform() { +void LauncherLoginStep::perform() +{ auto requestURL = "https://api.minecraftservices.com/launcher/login"; auto uhs = m_data->mojangservicesToken.extra["uhs"].toString(); auto xToken = m_data->mojangservicesToken.token; @@ -34,22 +34,20 @@ void LauncherLoginStep::perform() { QNetworkRequest request = QNetworkRequest(QUrl(requestURL)); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Accept", "application/json"); - AuthRequest *requestor = new AuthRequest(this); + AuthRequest* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &LauncherLoginStep::onRequestDone); requestor->post(request, requestBody.toUtf8()); qDebug() << "Getting Minecraft access token..."; } -void LauncherLoginStep::rehydrate() { +void LauncherLoginStep::rehydrate() +{ // TODO: check the token validity } -void LauncherLoginStep::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList headers -) { - auto requestor = qobject_cast(QObject::sender()); +void LauncherLoginStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList headers) +{ + auto requestor = qobject_cast(QObject::sender()); requestor->deleteLater(); qCDebug(authCredentials()) << data; @@ -57,27 +55,17 @@ void LauncherLoginStep::onRequestDone( qWarning() << "Reply error:" << error; qCDebug(authCredentials()) << data; if (Net::isApplicationError(error)) { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Failed to get Minecraft access token: %1").arg(requestor->errorString_) - ); - } - else { - emit finished( - AccountTaskState::STATE_OFFLINE, - tr("Failed to get Minecraft access token: %1").arg(requestor->errorString_) - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to get Minecraft access token: %1").arg(requestor->errorString_)); + } else { + emit finished(AccountTaskState::STATE_OFFLINE, tr("Failed to get Minecraft access token: %1").arg(requestor->errorString_)); } return; } - if(!Parsers::parseMojangResponse(data, m_data->yggdrasilToken)) { + if (!Parsers::parseMojangResponse(data, m_data->yggdrasilToken)) { qWarning() << "Could not parse login_with_xbox response..."; qCDebug(authCredentials()) << data; - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Failed to parse the Minecraft access token response.") - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to parse the Minecraft access token response.")); return; } emit finished(AccountTaskState::STATE_WORKING, tr("")); diff --git a/launcher/minecraft/auth/steps/LauncherLoginStep.h b/launcher/minecraft/auth/steps/LauncherLoginStep.h index e06a306f9..30c18e675 100644 --- a/launcher/minecraft/auth/steps/LauncherLoginStep.h +++ b/launcher/minecraft/auth/steps/LauncherLoginStep.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class LauncherLoginStep : public AuthStep { Q_OBJECT -public: - explicit LauncherLoginStep(AccountData *data); + public: + explicit LauncherLoginStep(AccountData* data); virtual ~LauncherLoginStep() noexcept; void perform() override; @@ -17,6 +16,6 @@ public: QString describe() override; -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); }; diff --git a/launcher/minecraft/auth/steps/MSAStep.cpp b/launcher/minecraft/auth/steps/MSAStep.cpp index 6fc8d468e..1aa22765d 100644 --- a/launcher/minecraft/auth/steps/MSAStep.cpp +++ b/launcher/minecraft/auth/steps/MSAStep.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -47,7 +47,8 @@ using OAuth2 = Katabasis::DeviceFlow; using Activity = Katabasis::Activity; -MSAStep::MSAStep(AccountData* data, Action action) : AuthStep(data), m_action(action) { +MSAStep::MSAStep(AccountData* data, Action action) : AuthStep(data), m_action(action) +{ m_clientId = APPLICATION->getMSAClientID(); OAuth2::Options opts; opts.scope = "XboxLive.signin offline_access"; @@ -64,13 +65,14 @@ MSAStep::MSAStep(AccountData* data, Action action) : AuthStep(data), m_action(ac MSAStep::~MSAStep() noexcept = default; -QString MSAStep::describe() { +QString MSAStep::describe() +{ return tr("Logging in with Microsoft account."); } - -void MSAStep::rehydrate() { - switch(m_action) { +void MSAStep::rehydrate() +{ + switch (m_action) { case Refresh: { // TODO: check the tokens and see if they are old (older than a day) return; @@ -82,12 +84,14 @@ void MSAStep::rehydrate() { } } -void MSAStep::perform() { - switch(m_action) { +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.")); + emit finished(AccountTaskState::STATE_DISABLED, + tr("Microsoft user authentication failed - client identification has changed.")); } m_oauth2->refresh(); return; @@ -105,8 +109,9 @@ void MSAStep::perform() { } } -void MSAStep::onOAuthActivityChanged(Katabasis::Activity activity) { - switch(activity) { +void MSAStep::onOAuthActivityChanged(Katabasis::Activity activity) +{ + switch (activity) { case Katabasis::Activity::Idle: case Katabasis::Activity::LoggingIn: case Katabasis::Activity::Refreshing: diff --git a/launcher/minecraft/auth/steps/MSAStep.h b/launcher/minecraft/auth/steps/MSAStep.h index e9a1524e8..b6635d4a5 100644 --- a/launcher/minecraft/auth/steps/MSAStep.h +++ b/launcher/minecraft/auth/steps/MSAStep.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -43,13 +43,11 @@ class MSAStep : public AuthStep { Q_OBJECT -public: - enum Action { - Refresh, - Login - }; -public: - explicit MSAStep(AccountData *data, Action action); + public: + enum Action { Refresh, Login }; + + public: + explicit MSAStep(AccountData* data, Action action); virtual ~MSAStep() noexcept; void perform() override; @@ -57,11 +55,11 @@ public: QString describe() override; -private slots: + private slots: void onOAuthActivityChanged(Katabasis::Activity activity); -private: - Katabasis::DeviceFlow *m_oauth2 = nullptr; + private: + Katabasis::DeviceFlow* m_oauth2 = nullptr; Action m_action; QString m_clientId; }; diff --git a/launcher/minecraft/auth/steps/MigrationEligibilityStep.cpp b/launcher/minecraft/auth/steps/MigrationEligibilityStep.cpp index f5b5637a4..5ce953dfb 100644 --- a/launcher/minecraft/auth/steps/MigrationEligibilityStep.cpp +++ b/launcher/minecraft/auth/steps/MigrationEligibilityStep.cpp @@ -5,37 +5,37 @@ #include "minecraft/auth/AuthRequest.h" #include "minecraft/auth/Parsers.h" -MigrationEligibilityStep::MigrationEligibilityStep(AccountData* data) : AuthStep(data) { - -} +MigrationEligibilityStep::MigrationEligibilityStep(AccountData* data) : AuthStep(data) {} MigrationEligibilityStep::~MigrationEligibilityStep() noexcept = default; -QString MigrationEligibilityStep::describe() { +QString MigrationEligibilityStep::describe() +{ return tr("Checking for migration eligibility."); } -void MigrationEligibilityStep::perform() { +void MigrationEligibilityStep::perform() +{ auto url = QUrl("https://api.minecraftservices.com/rollout/v1/msamigration"); QNetworkRequest request = QNetworkRequest(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8()); - AuthRequest *requestor = new AuthRequest(this); + AuthRequest* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &MigrationEligibilityStep::onRequestDone); requestor->get(request); } -void MigrationEligibilityStep::rehydrate() { +void MigrationEligibilityStep::rehydrate() +{ // NOOP, for now. We only save bools and there's nothing to check. } -void MigrationEligibilityStep::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList headers -) { - auto requestor = qobject_cast(QObject::sender()); +void MigrationEligibilityStep::onRequestDone(QNetworkReply::NetworkError error, + QByteArray data, + QList headers) +{ + auto requestor = qobject_cast(QObject::sender()); requestor->deleteLater(); if (error == QNetworkReply::NoError) { diff --git a/launcher/minecraft/auth/steps/MigrationEligibilityStep.h b/launcher/minecraft/auth/steps/MigrationEligibilityStep.h index b1bf9cbf4..8638975d8 100644 --- a/launcher/minecraft/auth/steps/MigrationEligibilityStep.h +++ b/launcher/minecraft/auth/steps/MigrationEligibilityStep.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class MigrationEligibilityStep : public AuthStep { Q_OBJECT -public: - explicit MigrationEligibilityStep(AccountData *data); + public: + explicit MigrationEligibilityStep(AccountData* data); virtual ~MigrationEligibilityStep() noexcept; void perform() override; @@ -17,6 +16,6 @@ public: QString describe() override; -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); }; diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp b/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp index 6cfa7c1cf..7cdce23f0 100644 --- a/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp +++ b/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp @@ -7,52 +7,46 @@ #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" -MinecraftProfileStep::MinecraftProfileStep(AccountData* data) : AuthStep(data) { - -} +MinecraftProfileStep::MinecraftProfileStep(AccountData* data) : AuthStep(data) {} MinecraftProfileStep::~MinecraftProfileStep() noexcept = default; -QString MinecraftProfileStep::describe() { +QString MinecraftProfileStep::describe() +{ return tr("Fetching the Minecraft profile."); } - -void MinecraftProfileStep::perform() { +void MinecraftProfileStep::perform() +{ auto url = QUrl("https://api.minecraftservices.com/minecraft/profile"); QNetworkRequest request = QNetworkRequest(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8()); - AuthRequest *requestor = new AuthRequest(this); + AuthRequest* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &MinecraftProfileStep::onRequestDone); requestor->get(request); } -void MinecraftProfileStep::rehydrate() { +void MinecraftProfileStep::rehydrate() +{ // NOOP, for now. We only save bools and there's nothing to check. } -void MinecraftProfileStep::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList headers -) { - auto requestor = qobject_cast(QObject::sender()); +void MinecraftProfileStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList headers) +{ + auto requestor = qobject_cast(QObject::sender()); requestor->deleteLater(); qCDebug(authCredentials()) << data; if (error == QNetworkReply::ContentNotFoundError) { // NOTE: Succeed even if we do not have a profile. This is a valid account state. - if(m_data->type == AccountType::Mojang) { + if (m_data->type == AccountType::Mojang) { m_data->minecraftEntitlement.canPlayMinecraft = false; m_data->minecraftEntitlement.ownsMinecraft = false; } m_data->minecraftProfile = MinecraftProfile(); - emit finished( - AccountTaskState::STATE_SUCCEEDED, - tr("Account has no Minecraft profile.") - ); + emit finished(AccountTaskState::STATE_SUCCEEDED, tr("Account has no Minecraft profile.")); return; } if (error != QNetworkReply::NoError) { @@ -65,35 +59,24 @@ void MinecraftProfileStep::onRequestDone( qWarning() << QString::fromUtf8(data); if (Net::isApplicationError(error)) { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_) - ); - } - else { - emit finished( - AccountTaskState::STATE_OFFLINE, - tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_) - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)); + } else { + emit finished(AccountTaskState::STATE_OFFLINE, + tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)); } return; } - if(!Parsers::parseMinecraftProfile(data, m_data->minecraftProfile)) { + if (!Parsers::parseMinecraftProfile(data, m_data->minecraftProfile)) { m_data->minecraftProfile = MinecraftProfile(); - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Minecraft Java profile response could not be parsed") - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Minecraft Java profile response could not be parsed")); return; } - if(m_data->type == AccountType::Mojang) { + if (m_data->type == AccountType::Mojang) { auto validProfile = m_data->minecraftProfile.validity == Katabasis::Validity::Certain; m_data->minecraftEntitlement.canPlayMinecraft = validProfile; m_data->minecraftEntitlement.ownsMinecraft = validProfile; } - emit finished( - AccountTaskState::STATE_WORKING, - tr("Minecraft Java profile acquisition succeeded.") - ); + emit finished(AccountTaskState::STATE_WORKING, tr("Minecraft Java profile acquisition succeeded.")); } diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStep.h b/launcher/minecraft/auth/steps/MinecraftProfileStep.h index 8ef3395c6..cb30dab21 100644 --- a/launcher/minecraft/auth/steps/MinecraftProfileStep.h +++ b/launcher/minecraft/auth/steps/MinecraftProfileStep.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class MinecraftProfileStep : public AuthStep { Q_OBJECT -public: - explicit MinecraftProfileStep(AccountData *data); + public: + explicit MinecraftProfileStep(AccountData* data); virtual ~MinecraftProfileStep() noexcept; void perform() override; @@ -17,6 +16,6 @@ public: QString describe() override; -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); }; diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp b/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp index 8c3785882..d035e39a0 100644 --- a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp +++ b/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp @@ -7,18 +7,17 @@ #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" -MinecraftProfileStepMojang::MinecraftProfileStepMojang(AccountData* data) : AuthStep(data) { - -} +MinecraftProfileStepMojang::MinecraftProfileStepMojang(AccountData* data) : AuthStep(data) {} MinecraftProfileStepMojang::~MinecraftProfileStepMojang() noexcept = default; -QString MinecraftProfileStepMojang::describe() { +QString MinecraftProfileStepMojang::describe() +{ return tr("Fetching the Minecraft profile."); } - -void MinecraftProfileStepMojang::perform() { +void MinecraftProfileStepMojang::perform() +{ if (m_data->minecraftProfile.id.isEmpty()) { emit finished(AccountTaskState::STATE_FAILED_HARD, tr("A UUID is required to get the profile.")); return; @@ -27,35 +26,32 @@ void MinecraftProfileStepMojang::perform() { // use session server instead of profile due to profile endpoint being locked for locked Mojang accounts QUrl url = QUrl("https://sessionserver.mojang.com/session/minecraft/profile/" + m_data->minecraftProfile.id); QNetworkRequest req = QNetworkRequest(url); - AuthRequest *request = new AuthRequest(this); + AuthRequest* request = new AuthRequest(this); connect(request, &AuthRequest::finished, this, &MinecraftProfileStepMojang::onRequestDone); request->get(req); } -void MinecraftProfileStepMojang::rehydrate() { +void MinecraftProfileStepMojang::rehydrate() +{ // NOOP, for now. We only save bools and there's nothing to check. } -void MinecraftProfileStepMojang::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList headers -) { - auto requestor = qobject_cast(QObject::sender()); +void MinecraftProfileStepMojang::onRequestDone(QNetworkReply::NetworkError error, + QByteArray data, + QList headers) +{ + auto requestor = qobject_cast(QObject::sender()); requestor->deleteLater(); qCDebug(authCredentials()) << data; if (error == QNetworkReply::ContentNotFoundError) { // NOTE: Succeed even if we do not have a profile. This is a valid account state. - if(m_data->type == AccountType::Mojang) { + if (m_data->type == AccountType::Mojang) { m_data->minecraftEntitlement.canPlayMinecraft = false; m_data->minecraftEntitlement.ownsMinecraft = false; } m_data->minecraftProfile = MinecraftProfile(); - emit finished( - AccountTaskState::STATE_SUCCEEDED, - tr("Account has no Minecraft profile.") - ); + emit finished(AccountTaskState::STATE_SUCCEEDED, tr("Account has no Minecraft profile.")); return; } if (error != QNetworkReply::NoError) { @@ -68,35 +64,24 @@ void MinecraftProfileStepMojang::onRequestDone( qWarning() << QString::fromUtf8(data); if (Net::isApplicationError(error)) { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_) - ); - } - else { - emit finished( - AccountTaskState::STATE_OFFLINE, - tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_) - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)); + } else { + emit finished(AccountTaskState::STATE_OFFLINE, + tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)); } return; } - if(!Parsers::parseMinecraftProfileMojang(data, m_data->minecraftProfile)) { + if (!Parsers::parseMinecraftProfileMojang(data, m_data->minecraftProfile)) { m_data->minecraftProfile = MinecraftProfile(); - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Minecraft Java profile response could not be parsed") - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Minecraft Java profile response could not be parsed")); return; } - if(m_data->type == AccountType::Mojang) { + if (m_data->type == AccountType::Mojang) { auto validProfile = m_data->minecraftProfile.validity == Katabasis::Validity::Certain; m_data->minecraftEntitlement.canPlayMinecraft = validProfile; m_data->minecraftEntitlement.ownsMinecraft = validProfile; } - emit finished( - AccountTaskState::STATE_WORKING, - tr("Minecraft Java profile acquisition succeeded.") - ); + emit finished(AccountTaskState::STATE_WORKING, tr("Minecraft Java profile acquisition succeeded.")); } diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.h b/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.h index e06b30ab0..730ec3f68 100644 --- a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.h +++ b/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class MinecraftProfileStepMojang : public AuthStep { Q_OBJECT -public: - explicit MinecraftProfileStepMojang(AccountData *data); + public: + explicit MinecraftProfileStepMojang(AccountData* data); virtual ~MinecraftProfileStepMojang() noexcept; void perform() override; @@ -17,6 +16,6 @@ public: QString describe() override; -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); }; diff --git a/launcher/minecraft/auth/steps/OfflineStep.cpp b/launcher/minecraft/auth/steps/OfflineStep.cpp index dc092bfd4..bf111abe8 100644 --- a/launcher/minecraft/auth/steps/OfflineStep.cpp +++ b/launcher/minecraft/auth/steps/OfflineStep.cpp @@ -5,14 +5,17 @@ OfflineStep::OfflineStep(AccountData* data) : AuthStep(data) {} OfflineStep::~OfflineStep() noexcept = default; -QString OfflineStep::describe() { +QString OfflineStep::describe() +{ return tr("Creating offline account."); } -void OfflineStep::rehydrate() { +void OfflineStep::rehydrate() +{ // NOOP } -void OfflineStep::perform() { +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 index 436597cd8..3bf123d6a 100644 --- a/launcher/minecraft/auth/steps/OfflineStep.h +++ b/launcher/minecraft/auth/steps/OfflineStep.h @@ -8,8 +8,8 @@ class OfflineStep : public AuthStep { Q_OBJECT -public: - explicit OfflineStep(AccountData *data); + public: + explicit OfflineStep(AccountData* data); virtual ~OfflineStep() noexcept; void perform() override; diff --git a/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp b/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp index b397b7349..c33d7e629 100644 --- a/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp +++ b/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp @@ -1,33 +1,32 @@ #include "XboxAuthorizationStep.h" -#include -#include #include +#include +#include #include "Logging.h" #include "minecraft/auth/AuthRequest.h" #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" -XboxAuthorizationStep::XboxAuthorizationStep(AccountData* data, Katabasis::Token *token, QString relyingParty, QString authorizationKind): - AuthStep(data), - m_token(token), - m_relyingParty(relyingParty), - m_authorizationKind(authorizationKind) -{ -} +XboxAuthorizationStep::XboxAuthorizationStep(AccountData* data, Katabasis::Token* token, QString relyingParty, QString authorizationKind) + : AuthStep(data), m_token(token), m_relyingParty(relyingParty), m_authorizationKind(authorizationKind) +{} XboxAuthorizationStep::~XboxAuthorizationStep() noexcept = default; -QString XboxAuthorizationStep::describe() { +QString XboxAuthorizationStep::describe() +{ return tr("Getting authorization to access %1 services.").arg(m_authorizationKind); } -void XboxAuthorizationStep::rehydrate() { +void XboxAuthorizationStep::rehydrate() +{ // FIXME: check if the tokens are good? } -void XboxAuthorizationStep::perform() { +void XboxAuthorizationStep::perform() +{ QString xbox_auth_template = R"XXX( { "Properties": { @@ -41,129 +40,98 @@ void XboxAuthorizationStep::perform() { } )XXX"; auto xbox_auth_data = xbox_auth_template.arg(m_data->userToken.token, m_relyingParty); -// http://xboxlive.com + // http://xboxlive.com QNetworkRequest request = QNetworkRequest(QUrl("https://xsts.auth.xboxlive.com/xsts/authorize")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Accept", "application/json"); - AuthRequest *requestor = new AuthRequest(this); + AuthRequest* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &XboxAuthorizationStep::onRequestDone); requestor->post(request, xbox_auth_data.toUtf8()); qDebug() << "Getting authorization token for " << m_relyingParty; } -void XboxAuthorizationStep::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList headers -) { - auto requestor = qobject_cast(QObject::sender()); +void XboxAuthorizationStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList headers) +{ + auto requestor = qobject_cast(QObject::sender()); requestor->deleteLater(); qCDebug(authCredentials()) << data; if (error != QNetworkReply::NoError) { qWarning() << "Reply error:" << error; if (Net::isApplicationError(error)) { - if(!processSTSError(error, data, headers)) { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Failed to get authorization for %1 services. Error %2.").arg(m_authorizationKind, error) - ); + if (!processSTSError(error, data, headers)) { + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("Failed to get authorization for %1 services. Error %2.").arg(m_authorizationKind, error)); + } else { + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("Unknown STS error for %1 services: %2").arg(m_authorizationKind, requestor->errorString_)); } - else { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Unknown STS error for %1 services: %2").arg(m_authorizationKind, requestor->errorString_) - ); - } - } - else { - emit finished( - AccountTaskState::STATE_OFFLINE, - tr("Failed to get authorization for %1 services: %2").arg(m_authorizationKind, requestor->errorString_) - ); + } else { + emit finished(AccountTaskState::STATE_OFFLINE, + tr("Failed to get authorization for %1 services: %2").arg(m_authorizationKind, requestor->errorString_)); } return; } Katabasis::Token temp; - if(!Parsers::parseXTokenResponse(data, temp, m_authorizationKind)) { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Could not parse authorization response for access to %1 services.").arg(m_authorizationKind) - ); + if (!Parsers::parseXTokenResponse(data, temp, m_authorizationKind)) { + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("Could not parse authorization response for access to %1 services.").arg(m_authorizationKind)); return; } - if(temp.extra["uhs"] != m_data->userToken.extra["uhs"]) { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Server has changed %1 authorization user hash in the reply. Something is wrong.").arg(m_authorizationKind) - ); + if (temp.extra["uhs"] != m_data->userToken.extra["uhs"]) { + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("Server has changed %1 authorization user hash in the reply. Something is wrong.").arg(m_authorizationKind)); return; } - auto & token = *m_token; + auto& token = *m_token; token = temp; emit finished(AccountTaskState::STATE_WORKING, tr("Got authorization to access %1").arg(m_relyingParty)); } - -bool XboxAuthorizationStep::processSTSError( - QNetworkReply::NetworkError error, - QByteArray data, - QList headers -) { - if(error == QNetworkReply::AuthenticationRequiredError) { +bool XboxAuthorizationStep::processSTSError(QNetworkReply::NetworkError error, QByteArray data, QList headers) +{ + if (error == QNetworkReply::AuthenticationRequiredError) { QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if(jsonError.error) { + if (jsonError.error) { qWarning() << "Cannot parse error XSTS response as JSON: " << jsonError.errorString(); - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Cannot parse %1 authorization error response as JSON: %2").arg(m_authorizationKind, jsonError.errorString()) - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("Cannot parse %1 authorization error response as JSON: %2").arg(m_authorizationKind, jsonError.errorString())); return true; } int64_t errorCode = -1; auto obj = doc.object(); - if(!Parsers::getNumber(obj.value("XErr"), errorCode)) { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("XErr element is missing from %1 authorization error response.").arg(m_authorizationKind) - ); + if (!Parsers::getNumber(obj.value("XErr"), errorCode)) { + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("XErr element is missing from %1 authorization error response.").arg(m_authorizationKind)); return true; } - switch(errorCode) { - case 2148916233:{ - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("This Microsoft account does not have an XBox Live profile. Buy the game on %1 first.") - .arg("minecraft.net") - ); + switch (errorCode) { + case 2148916233: { + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("This Microsoft account does not have an XBox Live profile. Buy the game on %1 first.") + .arg("minecraft.net")); return true; } case 2148916235: { // NOTE: this is the Grulovia error - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("XBox Live is not available in your country. You've been blocked.") - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XBox Live is not available in your country. You've been blocked.")); return true; } case 2148916238: { emit finished( AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account is underaged and is not linked to a family.\n\nPlease set up your account according to %1.") - .arg("help.minecraft.net") - ); + .arg("help.minecraft.net")); return true; } default: { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("XSTS authentication ended with unrecognized error(s):\n\n%1").arg(errorCode) - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("XSTS authentication ended with unrecognized error(s):\n\n%1").arg(errorCode)); return true; } } diff --git a/launcher/minecraft/auth/steps/XboxAuthorizationStep.h b/launcher/minecraft/auth/steps/XboxAuthorizationStep.h index 31e43bf05..dee24c954 100644 --- a/launcher/minecraft/auth/steps/XboxAuthorizationStep.h +++ b/launcher/minecraft/auth/steps/XboxAuthorizationStep.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class XboxAuthorizationStep : public AuthStep { Q_OBJECT -public: - explicit XboxAuthorizationStep(AccountData *data, Katabasis::Token *token, QString relyingParty, QString authorizationKind); + public: + explicit XboxAuthorizationStep(AccountData* data, Katabasis::Token* token, QString relyingParty, QString authorizationKind); virtual ~XboxAuthorizationStep() noexcept; void perform() override; @@ -17,18 +16,14 @@ public: QString describe() override; -private: - bool processSTSError( - QNetworkReply::NetworkError error, - QByteArray data, - QList headers - ); + private: + bool processSTSError(QNetworkReply::NetworkError error, QByteArray data, QList headers); -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); -private: - Katabasis::Token *m_token; + private: + Katabasis::Token* m_token; QString m_relyingParty; QString m_authorizationKind; }; diff --git a/launcher/minecraft/auth/steps/XboxProfileStep.cpp b/launcher/minecraft/auth/steps/XboxProfileStep.cpp index 644c419b1..fd2b32cce 100644 --- a/launcher/minecraft/auth/steps/XboxProfileStep.cpp +++ b/launcher/minecraft/auth/steps/XboxProfileStep.cpp @@ -8,66 +8,56 @@ #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" -XboxProfileStep::XboxProfileStep(AccountData* data) : AuthStep(data) { - -} +XboxProfileStep::XboxProfileStep(AccountData* data) : AuthStep(data) {} XboxProfileStep::~XboxProfileStep() noexcept = default; -QString XboxProfileStep::describe() { +QString XboxProfileStep::describe() +{ return tr("Fetching Xbox profile."); } -void XboxProfileStep::rehydrate() { +void XboxProfileStep::rehydrate() +{ // NOOP, for now. We only save bools and there's nothing to check. } -void XboxProfileStep::perform() { +void XboxProfileStep::perform() +{ auto url = QUrl("https://profile.xboxlive.com/users/me/profile/settings"); QUrlQuery q; - q.addQueryItem( - "settings", - "GameDisplayName,AppDisplayName,AppDisplayPicRaw,GameDisplayPicRaw," - "PublicGamerpic,ShowUserAsAvatar,Gamerscore,Gamertag,ModernGamertag,ModernGamertagSuffix," - "UniqueModernGamertag,AccountTier,TenureLevel,XboxOneRep," - "PreferredColor,Location,Bio,Watermarks," - "RealName,RealNameOverride,IsQuarantined" - ); + q.addQueryItem("settings", + "GameDisplayName,AppDisplayName,AppDisplayPicRaw,GameDisplayPicRaw," + "PublicGamerpic,ShowUserAsAvatar,Gamerscore,Gamertag,ModernGamertag,ModernGamertagSuffix," + "UniqueModernGamertag,AccountTier,TenureLevel,XboxOneRep," + "PreferredColor,Location,Bio,Watermarks," + "RealName,RealNameOverride,IsQuarantined"); url.setQuery(q); QNetworkRequest request = QNetworkRequest(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Accept", "application/json"); request.setRawHeader("x-xbl-contract-version", "3"); - request.setRawHeader("Authorization", QString("XBL3.0 x=%1;%2").arg(m_data->userToken.extra["uhs"].toString(), m_data->xboxApiToken.token).toUtf8()); - AuthRequest *requestor = new AuthRequest(this); + request.setRawHeader("Authorization", + QString("XBL3.0 x=%1;%2").arg(m_data->userToken.extra["uhs"].toString(), m_data->xboxApiToken.token).toUtf8()); + AuthRequest* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &XboxProfileStep::onRequestDone); requestor->get(request); qDebug() << "Getting Xbox profile..."; } -void XboxProfileStep::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList headers -) { - auto requestor = qobject_cast(QObject::sender()); +void XboxProfileStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList headers) +{ + auto requestor = qobject_cast(QObject::sender()); requestor->deleteLater(); if (error != QNetworkReply::NoError) { qWarning() << "Reply error:" << error; qCDebug(authCredentials()) << data; if (Net::isApplicationError(error)) { - emit finished( - AccountTaskState::STATE_FAILED_SOFT, - tr("Failed to retrieve the Xbox profile: %1").arg(requestor->errorString_) - ); - } - else { - emit finished( - AccountTaskState::STATE_OFFLINE, - tr("Failed to retrieve the Xbox profile: %1").arg(requestor->errorString_) - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to retrieve the Xbox profile: %1").arg(requestor->errorString_)); + } else { + emit finished(AccountTaskState::STATE_OFFLINE, tr("Failed to retrieve the Xbox profile: %1").arg(requestor->errorString_)); } return; } diff --git a/launcher/minecraft/auth/steps/XboxProfileStep.h b/launcher/minecraft/auth/steps/XboxProfileStep.h index 7a0c5873d..b8494b6e5 100644 --- a/launcher/minecraft/auth/steps/XboxProfileStep.h +++ b/launcher/minecraft/auth/steps/XboxProfileStep.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class XboxProfileStep : public AuthStep { Q_OBJECT -public: - explicit XboxProfileStep(AccountData *data); + public: + explicit XboxProfileStep(AccountData* data); virtual ~XboxProfileStep() noexcept; void perform() override; @@ -17,6 +16,6 @@ public: QString describe() override; -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); }; diff --git a/launcher/minecraft/auth/steps/XboxUserStep.cpp b/launcher/minecraft/auth/steps/XboxUserStep.cpp index 842eb60ff..61c33a18d 100644 --- a/launcher/minecraft/auth/steps/XboxUserStep.cpp +++ b/launcher/minecraft/auth/steps/XboxUserStep.cpp @@ -6,22 +6,22 @@ #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" -XboxUserStep::XboxUserStep(AccountData* data) : AuthStep(data) { - -} +XboxUserStep::XboxUserStep(AccountData* data) : AuthStep(data) {} XboxUserStep::~XboxUserStep() noexcept = default; -QString XboxUserStep::describe() { +QString XboxUserStep::describe() +{ return tr("Logging in as an Xbox user."); } - -void XboxUserStep::rehydrate() { +void XboxUserStep::rehydrate() +{ // NOOP, for now. We only save bools and there's nothing to check. } -void XboxUserStep::perform() { +void XboxUserStep::perform() +{ QString xbox_auth_template = R"XXX( { "Properties": { @@ -40,40 +40,31 @@ void XboxUserStep::perform() { request.setRawHeader("Accept", "application/json"); // set contract-verison header (prevent err 400 bad-request?) // https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/reference/live/rest/additional/httpstandardheaders - request.setRawHeader("x-xbl-contract-version", "1"); + request.setRawHeader("x-xbl-contract-version", "1"); - auto *requestor = new AuthRequest(this); + auto* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &XboxUserStep::onRequestDone); requestor->post(request, xbox_auth_data.toUtf8()); qDebug() << "First layer of XBox auth ... commencing."; } -void XboxUserStep::onRequestDone( - QNetworkReply::NetworkError error, - QByteArray data, - QList headers -) { - auto requestor = qobject_cast(QObject::sender()); +void XboxUserStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList headers) +{ + auto requestor = qobject_cast(QObject::sender()); requestor->deleteLater(); if (error != QNetworkReply::NoError) { qWarning() << "Reply error:" << error; if (Net::isApplicationError(error)) { - emit finished(AccountTaskState::STATE_FAILED_SOFT, - tr("XBox user authentication failed: %1").arg(requestor->errorString_) - ); - } - else { - emit finished( - AccountTaskState::STATE_OFFLINE, - tr("XBox user authentication failed: %1").arg(requestor->errorString_) - ); + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XBox user authentication failed: %1").arg(requestor->errorString_)); + } else { + emit finished(AccountTaskState::STATE_OFFLINE, tr("XBox user authentication failed: %1").arg(requestor->errorString_)); } return; } Katabasis::Token temp; - if(!Parsers::parseXTokenResponse(data, temp, "UToken")) { + if (!Parsers::parseXTokenResponse(data, temp, "UToken")) { qWarning() << "Could not parse user authentication response..."; emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XBox user authentication response could not be understood.")); return; diff --git a/launcher/minecraft/auth/steps/XboxUserStep.h b/launcher/minecraft/auth/steps/XboxUserStep.h index 83e9405f4..e92727a4d 100644 --- a/launcher/minecraft/auth/steps/XboxUserStep.h +++ b/launcher/minecraft/auth/steps/XboxUserStep.h @@ -4,12 +4,11 @@ #include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" - class XboxUserStep : public AuthStep { Q_OBJECT -public: - explicit XboxUserStep(AccountData *data); + public: + explicit XboxUserStep(AccountData* data); virtual ~XboxUserStep() noexcept; void perform() override; @@ -17,6 +16,6 @@ public: QString describe() override; -private slots: + private slots: void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); }; diff --git a/launcher/minecraft/auth/steps/YggdrasilStep.cpp b/launcher/minecraft/auth/steps/YggdrasilStep.cpp index e1d331726..fdcaa0d67 100644 --- a/launcher/minecraft/auth/steps/YggdrasilStep.cpp +++ b/launcher/minecraft/auth/steps/YggdrasilStep.cpp @@ -4,7 +4,8 @@ #include "minecraft/auth/Parsers.h" #include "minecraft/auth/Yggdrasil.h" -YggdrasilStep::YggdrasilStep(AccountData* data, QString password) : AuthStep(data), m_password(password) { +YggdrasilStep::YggdrasilStep(AccountData* data, QString password) : AuthStep(data), m_password(password) +{ m_yggdrasil = new Yggdrasil(m_data, this); connect(m_yggdrasil, &Task::failed, this, &YggdrasilStep::onAuthFailed); @@ -14,28 +15,32 @@ YggdrasilStep::YggdrasilStep(AccountData* data, QString password) : AuthStep(dat YggdrasilStep::~YggdrasilStep() noexcept = default; -QString YggdrasilStep::describe() { +QString YggdrasilStep::describe() +{ return tr("Logging in with Mojang account."); } -void YggdrasilStep::rehydrate() { +void YggdrasilStep::rehydrate() +{ // NOOP, for now. } -void YggdrasilStep::perform() { - if(m_password.size()) { +void YggdrasilStep::perform() +{ + if (m_password.size()) { m_yggdrasil->login(m_password); - } - else { + } else { m_yggdrasil->refresh(); } } -void YggdrasilStep::onAuthSucceeded() { +void YggdrasilStep::onAuthSucceeded() +{ emit finished(AccountTaskState::STATE_WORKING, tr("Logged in with Mojang")); } -void YggdrasilStep::onAuthFailed() { +void YggdrasilStep::onAuthFailed() +{ // TODO: hook these in again, expand to MSA // m_error = m_yggdrasil->m_error; // m_aborted = m_yggdrasil->m_aborted; @@ -44,7 +49,7 @@ void YggdrasilStep::onAuthFailed() { QString errorMessage = tr("Mojang user authentication failed."); // NOTE: soft error in the first step means 'offline' - if(state == AccountTaskState::STATE_FAILED_SOFT) { + if (state == AccountTaskState::STATE_FAILED_SOFT) { state = AccountTaskState::STATE_OFFLINE; errorMessage = tr("Mojang user authentication ended with a network error."); } diff --git a/launcher/minecraft/auth/steps/YggdrasilStep.h b/launcher/minecraft/auth/steps/YggdrasilStep.h index ebafb8e5b..ef31f34d5 100644 --- a/launcher/minecraft/auth/steps/YggdrasilStep.h +++ b/launcher/minecraft/auth/steps/YggdrasilStep.h @@ -9,8 +9,8 @@ class Yggdrasil; class YggdrasilStep : public AuthStep { Q_OBJECT -public: - explicit YggdrasilStep(AccountData *data, QString password); + public: + explicit YggdrasilStep(AccountData* data, QString password); virtual ~YggdrasilStep() noexcept; void perform() override; @@ -18,11 +18,11 @@ public: QString describe() override; -private slots: + private slots: void onAuthSucceeded(); void onAuthFailed(); -private: - Yggdrasil *m_yggdrasil = nullptr; + private: + Yggdrasil* m_yggdrasil = nullptr; QString m_password; }; diff --git a/launcher/minecraft/gameoptions/GameOptions.cpp b/launcher/minecraft/gameoptions/GameOptions.cpp index e547b32a3..4f4fb99a7 100644 --- a/launcher/minecraft/gameoptions/GameOptions.cpp +++ b/launcher/minecraft/gameoptions/GameOptions.cpp @@ -1,59 +1,51 @@ #include "GameOptions.h" -#include "FileSystem.h" #include #include +#include "FileSystem.h" namespace { -bool load(const QString& path, std::vector &contents, int & version) +bool load(const QString& path, std::vector& contents, int& version) { contents.clear(); QFile file(path); - if (!file.open(QFile::ReadOnly)) - { + if (!file.open(QFile::ReadOnly)) { qWarning() << "Failed to read options file."; return false; } version = 0; - while(!file.atEnd()) - { + while (!file.atEnd()) { auto line = file.readLine(); - if(line.endsWith('\n')) - { + if (line.endsWith('\n')) { line.chop(1); } auto separatorIndex = line.indexOf(':'); - if(separatorIndex == -1) - { + if (separatorIndex == -1) { continue; } auto key = QString::fromUtf8(line.data(), separatorIndex); auto value = QString::fromUtf8(line.data() + separatorIndex + 1, line.size() - 1 - separatorIndex); qDebug() << "!!" << key << "!!"; - if(key == "version") - { + if (key == "version") { version = value.toInt(); continue; } - contents.emplace_back(GameOptionItem{key, value}); + contents.emplace_back(GameOptionItem{ key, value }); } qDebug() << "Loaded" << path << "with version:" << version; return true; } -bool save(const QString& path, std::vector &mapping, int version) +bool save(const QString& path, std::vector& mapping, int version) { QSaveFile out(path); - if(!out.open(QIODevice::WriteOnly)) - { + if (!out.open(QIODevice::WriteOnly)) { return false; } - if(version != 0) - { + if (version != 0) { QString versionLine = QString("version:%1\n").arg(version); out.write(versionLine.toUtf8()); } auto iter = mapping.begin(); - while (iter != mapping.end()) - { + while (iter != mapping.end()) { out.write(iter->key.toUtf8()); out.write(":"); out.write(iter->value.toUtf8()); @@ -62,22 +54,19 @@ bool save(const QString& path, std::vector &mapping, int version } return out.commit(); } -} +} // namespace -GameOptions::GameOptions(const QString& path): - path(path) +GameOptions::GameOptions(const QString& path) : path(path) { reload(); } QVariant GameOptions::headerData(int section, Qt::Orientation orientation, int role) const { - if(role != Qt::DisplayRole) - { + if (role != Qt::DisplayRole) { return QAbstractListModel::headerData(section, orientation, role); } - switch(section) - { + switch (section) { case 0: return tr("Key"); case 1: @@ -98,26 +87,21 @@ QVariant GameOptions::data(const QModelIndex& index, int role) const if (row < 0 || row >= int(contents.size())) return QVariant(); - switch (role) - { - case Qt::DisplayRole: - if(column == 0) - { - return contents[row].key; - } - else - { - return contents[row].value; - } - default: - return QVariant(); + switch (role) { + case Qt::DisplayRole: + if (column == 0) { + return contents[row].key; + } else { + return contents[row].value; + } + default: + return QVariant(); } - return QVariant(); } int GameOptions::rowCount(const QModelIndex&) const { - return contents.size(); + return static_cast(contents.size()); } int GameOptions::columnCount(const QModelIndex&) const diff --git a/launcher/minecraft/gameoptions/GameOptions.h b/launcher/minecraft/gameoptions/GameOptions.h index c6d254928..ae031efb2 100644 --- a/launcher/minecraft/gameoptions/GameOptions.h +++ b/launcher/minecraft/gameoptions/GameOptions.h @@ -1,32 +1,30 @@ #pragma once -#include -#include #include +#include +#include -struct GameOptionItem -{ +struct GameOptionItem { QString key; QString value; }; -class GameOptions : public QAbstractListModel -{ +class GameOptions : public QAbstractListModel { Q_OBJECT -public: + public: explicit GameOptions(const QString& path); virtual ~GameOptions() = default; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - int columnCount(const QModelIndex & parent) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; bool isLoaded() const; bool reload(); bool save(); -private: + private: std::vector contents; bool loaded = false; QString path; diff --git a/launcher/minecraft/launch/ClaimAccount.cpp b/launcher/minecraft/launch/ClaimAccount.cpp index 1cd7c0da7..a3de1516a 100644 --- a/launcher/minecraft/launch/ClaimAccount.cpp +++ b/launcher/minecraft/launch/ClaimAccount.cpp @@ -4,10 +4,9 @@ #include "Application.h" #include "minecraft/auth/AccountList.h" -ClaimAccount::ClaimAccount(LaunchTask* parent, AuthSessionPtr session): LaunchStep(parent) +ClaimAccount::ClaimAccount(LaunchTask* parent, AuthSessionPtr session) : LaunchStep(parent) { - if(session->status == AuthSession::Status::PlayableOnline && !session->demo) - { + if (session->status == AuthSession::Status::PlayableOnline && !session->demo) { auto accounts = APPLICATION->accounts(); m_account = accounts->getAccountByProfileName(session->player_name); } @@ -15,8 +14,7 @@ ClaimAccount::ClaimAccount(LaunchTask* parent, AuthSessionPtr session): LaunchSt void ClaimAccount::executeTask() { - if(m_account) - { + if (m_account) { lock.reset(new UseLock(m_account)); emitSucceeded(); } diff --git a/launcher/minecraft/launch/ClaimAccount.h b/launcher/minecraft/launch/ClaimAccount.h index cb4de23f5..3d47539ac 100644 --- a/launcher/minecraft/launch/ClaimAccount.h +++ b/launcher/minecraft/launch/ClaimAccount.h @@ -18,20 +18,17 @@ #include #include -class ClaimAccount: public LaunchStep -{ +class ClaimAccount : public LaunchStep { Q_OBJECT -public: - explicit ClaimAccount(LaunchTask *parent, AuthSessionPtr session); - virtual ~ClaimAccount() {}; + public: + explicit ClaimAccount(LaunchTask* parent, AuthSessionPtr session); + virtual ~ClaimAccount(){}; void executeTask() override; void finalize() override; - bool canAbort() const override - { - return false; - } -private: + bool canAbort() const override { return false; } + + private: std::unique_ptr lock; MinecraftAccountPtr m_account; }; diff --git a/launcher/minecraft/launch/CreateGameFolders.cpp b/launcher/minecraft/launch/CreateGameFolders.cpp index 4081e72ed..36f5e6407 100644 --- a/launcher/minecraft/launch/CreateGameFolders.cpp +++ b/launcher/minecraft/launch/CreateGameFolders.cpp @@ -1,27 +1,23 @@ #include "CreateGameFolders.h" -#include "minecraft/MinecraftInstance.h" -#include "launch/LaunchTask.h" #include "FileSystem.h" +#include "launch/LaunchTask.h" +#include "minecraft/MinecraftInstance.h" -CreateGameFolders::CreateGameFolders(LaunchTask* parent): LaunchStep(parent) -{ -} +CreateGameFolders::CreateGameFolders(LaunchTask* parent) : LaunchStep(parent) {} void CreateGameFolders::executeTask() { auto instance = m_parent->instance(); std::shared_ptr minecraftInstance = std::dynamic_pointer_cast(instance); - if(!FS::ensureFolderPathExists(minecraftInstance->gameRoot())) - { + if (!FS::ensureFolderPathExists(minecraftInstance->gameRoot())) { emit logLine("Couldn't create the main game folder", MessageLevel::Error); emitFailed(tr("Couldn't create the main game folder")); return; } // HACK: this is a workaround for MCL-3732 - 'server-resource-packs' folder is created. - if(!FS::ensureFolderPathExists(FS::PathCombine(minecraftInstance->gameRoot(), "server-resource-packs"))) - { + if (!FS::ensureFolderPathExists(FS::PathCombine(minecraftInstance->gameRoot(), "server-resource-packs"))) { emit logLine("Couldn't create the 'server-resource-packs' folder", MessageLevel::Error); } emitSucceeded(); diff --git a/launcher/minecraft/launch/CreateGameFolders.h b/launcher/minecraft/launch/CreateGameFolders.h index 9c7d3c940..44524ded5 100644 --- a/launcher/minecraft/launch/CreateGameFolders.h +++ b/launcher/minecraft/launch/CreateGameFolders.h @@ -15,23 +15,17 @@ #pragma once -#include #include +#include #include // Create the main .minecraft for the instance and any other necessary folders -class CreateGameFolders: public LaunchStep -{ +class CreateGameFolders : public LaunchStep { Q_OBJECT -public: - explicit CreateGameFolders(LaunchTask *parent); - virtual ~CreateGameFolders() {}; + public: + explicit CreateGameFolders(LaunchTask* parent); + virtual ~CreateGameFolders(){}; virtual void executeTask(); - virtual bool canAbort() const - { - return false; - } + virtual bool canAbort() const { return false; } }; - - diff --git a/launcher/minecraft/launch/DirectJavaLaunch.cpp b/launcher/minecraft/launch/DirectJavaLaunch.cpp deleted file mode 100644 index ca55cd2ed..000000000 --- a/launcher/minecraft/launch/DirectJavaLaunch.cpp +++ /dev/null @@ -1,166 +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 "DirectJavaLaunch.h" - -#include - -#include -#include -#include -#include - -#include "Application.h" - -#ifdef Q_OS_LINUX -#include "gamemode_client.h" -#endif - -DirectJavaLaunch::DirectJavaLaunch(LaunchTask *parent) : LaunchStep(parent) -{ - connect(&m_process, &LoggedProcess::log, this, &DirectJavaLaunch::logLines); - connect(&m_process, &LoggedProcess::stateChanged, this, &DirectJavaLaunch::on_state); -} - -void DirectJavaLaunch::executeTask() -{ - auto instance = m_parent->instance(); - std::shared_ptr minecraftInstance = std::dynamic_pointer_cast(instance); - QStringList args = minecraftInstance->javaArguments(); - - args.append("-Djava.library.path=" + minecraftInstance->getNativePath()); - - auto classPathEntries = minecraftInstance->getClassPath(); - args.append("-cp"); - QString classpath; -#ifdef Q_OS_WIN32 - classpath = classPathEntries.join(';'); -#else - classpath = classPathEntries.join(':'); -#endif - args.append(classpath); - args.append(minecraftInstance->getMainClass()); - - QString allArgs = args.join(", "); - emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::Launcher); - - auto javaPath = FS::ResolveExecutable(instance->settings()->get("JavaPath").toString()); - - m_process.setProcessEnvironment(instance->createLaunchEnvironment()); - - // make detachable - this will keep the process running even if the object is destroyed - m_process.setDetachable(true); - - auto mcArgs = minecraftInstance->processMinecraftArgs(m_session, m_serverToJoin); - args.append(mcArgs); - - QString wrapperCommandStr = instance->getWrapperCommand().trimmed(); - if(!wrapperCommandStr.isEmpty()) - { - auto wrapperArgs = Commandline::splitArgs(wrapperCommandStr); - auto wrapperCommand = wrapperArgs.takeFirst(); - auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand); - if (realWrapperCommand.isEmpty()) - { - const char *reason = QT_TR_NOOP("The wrapper command \"%1\" couldn't be found."); - emit logLine(QString(reason).arg(wrapperCommand), MessageLevel::Fatal); - emitFailed(tr(reason).arg(wrapperCommand)); - return; - } - emit logLine("Wrapper command is:\n" + wrapperCommandStr + "\n\n", MessageLevel::Launcher); - args.prepend(javaPath); - m_process.start(wrapperCommand, wrapperArgs + args); - } - else - { - m_process.start(javaPath, args); - } - -#ifdef Q_OS_LINUX - if (instance->settings()->get("EnableFeralGamemode").toBool() && APPLICATION->capabilities() & Application::SupportsGameMode) - { - auto pid = m_process.processId(); - if (pid) - { - gamemode_request_start_for(pid); - } - } -#endif -} - -void DirectJavaLaunch::on_state(LoggedProcess::State state) -{ - switch(state) - { - case LoggedProcess::FailedToStart: - { - //: Error message displayed if instance can't start - const char *reason = QT_TR_NOOP("Could not launch Minecraft!"); - emit logLine(reason, MessageLevel::Fatal); - emitFailed(tr(reason)); - return; - } - case LoggedProcess::Aborted: - case LoggedProcess::Crashed: - { - m_parent->setPid(-1); - emitFailed(tr("Game crashed.")); - return; - } - case LoggedProcess::Finished: - { - m_parent->setPid(-1); - // if the exit code wasn't 0, report this as a crash - auto exitCode = m_process.exitCode(); - if(exitCode != 0) - { - emitFailed(tr("Game crashed.")); - return; - } - //FIXME: make this work again - // m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(exitCode)); - // run post-exit - emitSucceeded(); - break; - } - case LoggedProcess::Running: - emit logLine(QString("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::Launcher); - m_parent->setPid(m_process.processId()); - m_parent->instance()->setLastLaunch(); - break; - default: - break; - } -} - -void DirectJavaLaunch::setWorkingDirectory(const QString &wd) -{ - m_process.setWorkingDirectory(wd); -} - -void DirectJavaLaunch::proceed() -{ - // nil -} - -bool DirectJavaLaunch::abort() -{ - auto state = m_process.state(); - if (state == LoggedProcess::Running || state == LoggedProcess::Starting) - { - m_process.kill(); - } - return true; -} - diff --git a/launcher/minecraft/launch/DirectJavaLaunch.h b/launcher/minecraft/launch/DirectJavaLaunch.h deleted file mode 100644 index 58b119b8c..000000000 --- a/launcher/minecraft/launch/DirectJavaLaunch.h +++ /dev/null @@ -1,58 +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 -#include -#include - -#include "MinecraftServerTarget.h" - -class DirectJavaLaunch: public LaunchStep -{ - Q_OBJECT -public: - explicit DirectJavaLaunch(LaunchTask *parent); - virtual ~DirectJavaLaunch() {}; - - virtual void executeTask(); - virtual bool abort(); - virtual void proceed(); - virtual bool canAbort() const - { - return true; - } - void setWorkingDirectory(const QString &wd); - void setAuthSession(AuthSessionPtr session) - { - m_session = session; - } - - void setServerToJoin(MinecraftServerTargetPtr serverToJoin) - { - m_serverToJoin = std::move(serverToJoin); - } - -private slots: - void on_state(LoggedProcess::State state); - -private: - LoggedProcess m_process; - QString m_command; - AuthSessionPtr m_session; - MinecraftServerTargetPtr m_serverToJoin; -}; - diff --git a/launcher/minecraft/launch/ExtractNatives.cpp b/launcher/minecraft/launch/ExtractNatives.cpp index 7d5f41791..cebeaedd4 100644 --- a/launcher/minecraft/launch/ExtractNatives.cpp +++ b/launcher/minecraft/launch/ExtractNatives.cpp @@ -14,26 +14,25 @@ */ #include "ExtractNatives.h" -#include #include +#include #include #include -#include "MMCZip.h" -#include "FileSystem.h" #include +#include "FileSystem.h" +#include "MMCZip.h" #ifdef major - #undef major +#undef major #endif #ifdef minor - #undef minor +#undef minor #endif -static QString replaceSuffix (QString target, const QString &suffix, const QString &replacement) +static QString replaceSuffix(QString target, const QString& suffix, const QString& replacement) { - if (!target.endsWith(suffix)) - { + if (!target.endsWith(suffix)) { return target; } target.resize(target.length() - suffix.length()); @@ -43,17 +42,14 @@ static QString replaceSuffix (QString target, const QString &suffix, const QStri static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack, bool nativeOpenAL, bool nativeGLFW) { QuaZip zip(source); - if(!zip.open(QuaZip::mdUnzip)) - { + if (!zip.open(QuaZip::mdUnzip)) { return false; } QDir directory(targetFolder); - if (!zip.goToFirstFile()) - { + if (!zip.goToFirstFile()) { return false; } - do - { + do { QString name = zip.getCurrentFileName(); auto lowercase = name.toLower(); if (nativeGLFW && name.contains("glfw")) { @@ -62,19 +58,16 @@ static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibH if (nativeOpenAL && name.contains("openal")) { continue; } - if(applyJnilibHack) - { + if (applyJnilibHack) { name = replaceSuffix(name, ".jnilib", ".dylib"); } QString absFilePath = directory.absoluteFilePath(name); - if (!JlCompress::extractFile(&zip, "", absFilePath)) - { + if (!JlCompress::extractFile(&zip, "", absFilePath)) { return false; } } while (zip.goToNextFile()); zip.close(); - if(zip.getZipError()!=0) - { + if (zip.getZipError() != 0) { return false; } return true; @@ -85,8 +78,7 @@ void ExtractNatives::executeTask() auto instance = m_parent->instance(); std::shared_ptr minecraftInstance = std::dynamic_pointer_cast(instance); auto toExtract = minecraftInstance->getNativeJars(); - if(toExtract.isEmpty()) - { + if (toExtract.isEmpty()) { emitSucceeded(); return; } @@ -94,14 +86,12 @@ void ExtractNatives::executeTask() bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool(); bool nativeGLFW = settings->get("UseNativeGLFW").toBool(); - auto outputPath = minecraftInstance->getNativePath(); + auto outputPath = minecraftInstance->getNativePath(); auto javaVersion = minecraftInstance->getJavaVersion(); bool jniHackEnabled = javaVersion.major() >= 8; - for(const auto &source: toExtract) - { - if(!unzipNatives(source, outputPath, jniHackEnabled, nativeOpenAL, nativeGLFW)) - { - const char *reason = QT_TR_NOOP("Couldn't extract native jar '%1' to destination '%2'"); + for (const auto& source : toExtract) { + if (!unzipNatives(source, outputPath, jniHackEnabled, nativeOpenAL, nativeGLFW)) { + const char* reason = QT_TR_NOOP("Couldn't extract native jar '%1' to destination '%2'"); emit logLine(QString(reason).arg(source, outputPath), MessageLevel::Fatal); emitFailed(tr(reason).arg(source, outputPath)); } diff --git a/launcher/minecraft/launch/ExtractNatives.h b/launcher/minecraft/launch/ExtractNatives.h index 094fcd6bb..2ab8816bd 100644 --- a/launcher/minecraft/launch/ExtractNatives.h +++ b/launcher/minecraft/launch/ExtractNatives.h @@ -20,19 +20,13 @@ #include "minecraft/auth/AuthSession.h" // FIXME: temporary wrapper for existing task. -class ExtractNatives: public LaunchStep -{ +class ExtractNatives : public LaunchStep { Q_OBJECT -public: - explicit ExtractNatives(LaunchTask *parent) : LaunchStep(parent){}; + public: + explicit ExtractNatives(LaunchTask* parent) : LaunchStep(parent){}; virtual ~ExtractNatives(){}; void executeTask() override; - bool canAbort() const override - { - return false; - } + bool canAbort() const override { return false; } void finalize() override; }; - - diff --git a/launcher/minecraft/launch/LauncherPartLaunch.cpp b/launcher/minecraft/launch/LauncherPartLaunch.cpp index 8ecf715db..44e5d0a63 100644 --- a/launcher/minecraft/launch/LauncherPartLaunch.cpp +++ b/launcher/minecraft/launch/LauncherPartLaunch.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,29 +35,27 @@ #include "LauncherPartLaunch.h" -#include #include +#include +#include "Application.h" +#include "Commandline.h" +#include "FileSystem.h" #include "launch/LaunchTask.h" #include "minecraft/MinecraftInstance.h" -#include "FileSystem.h" -#include "Commandline.h" -#include "Application.h" #ifdef Q_OS_LINUX #include "gamemode_client.h" #endif -LauncherPartLaunch::LauncherPartLaunch(LaunchTask *parent) : LaunchStep(parent) +LauncherPartLaunch::LauncherPartLaunch(LaunchTask* parent) : LaunchStep(parent) { auto instance = parent->instance(); - if (instance->settings()->get("CloseAfterLaunch").toBool()) - { - std::shared_ptr connection{new QMetaObject::Connection}; - *connection = connect(&m_process, &LoggedProcess::log, this, [=](QStringList lines, MessageLevel::Enum level) { + if (instance->settings()->get("CloseAfterLaunch").toBool()) { + std::shared_ptr connection{ new QMetaObject::Connection }; + *connection = connect(&m_process, &LoggedProcess::log, this, [=](QStringList lines, [[maybe_unused]] MessageLevel::Enum level) { qDebug() << lines; - if (lines.filter(QRegularExpression(".*Setting user.+", QRegularExpression::CaseInsensitiveOption)).length() != 0) - { + if (lines.filter(QRegularExpression(".*Setting user.+", QRegularExpression::CaseInsensitiveOption)).length() != 0) { APPLICATION->closeAllWindows(); disconnect(*connection); } @@ -71,7 +69,7 @@ LauncherPartLaunch::LauncherPartLaunch(LaunchTask *parent) : LaunchStep(parent) #ifdef Q_OS_WIN // returns 8.3 file format from long path #include -QString shortPathName(const QString & file) +QString shortPathName(const QString& file) { auto input = file.toStdWString(); std::wstring output; @@ -81,15 +79,15 @@ QString shortPathName(const QString & file) // when it succeeds, it returns length excluding null character // See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx output.resize(length); - GetShortPathNameW(input.c_str(),(LPWSTR)output.c_str(),length); - output.resize(length-1); + GetShortPathNameW(input.c_str(), (LPWSTR)output.c_str(), length); + output.resize(length - 1); QString ret = QString::fromStdWString(output); return ret; } #endif // if the string survives roundtrip through local 8bit encoding... -bool fitsInLocal8bit(const QString & string) +bool fitsInLocal8bit(const QString& string) { return string == QString::fromLocal8Bit(string.toLocal8Bit()); } @@ -97,9 +95,8 @@ bool fitsInLocal8bit(const QString & string) void LauncherPartLaunch::executeTask() { QString jarPath = APPLICATION->getJarPath("NewLaunch.jar"); - if (jarPath.isEmpty()) - { - const char *reason = QT_TR_NOOP("Launcher library could not be found. Please check your installation."); + if (jarPath.isEmpty()) { + const char* reason = QT_TR_NOOP("Launcher library could not be found. Please check your installation."); emit logLine(tr(reason), MessageLevel::Fatal); emitFailed(tr(reason)); return; @@ -125,12 +122,9 @@ void LauncherPartLaunch::executeTask() auto natPath = minecraftInstance->getNativePath(); #ifdef Q_OS_WIN - if (!fitsInLocal8bit(natPath)) - { + if (!fitsInLocal8bit(natPath)) { args << "-Djava.library.path=" + shortPathName(natPath); - } - else - { + } else { args << "-Djava.library.path=" + natPath; } #else @@ -140,14 +134,10 @@ void LauncherPartLaunch::executeTask() args << "-cp"; #ifdef Q_OS_WIN QStringList processed; - for(auto & item: classPath) - { - if (!fitsInLocal8bit(item)) - { + for (auto& item : classPath) { + if (!fitsInLocal8bit(item)) { processed << shortPathName(item); - } - else - { + } else { processed << item; } } @@ -160,14 +150,12 @@ void LauncherPartLaunch::executeTask() qDebug() << args.join(' '); QString wrapperCommandStr = instance->getWrapperCommand().trimmed(); - if(!wrapperCommandStr.isEmpty()) - { + if (!wrapperCommandStr.isEmpty()) { auto wrapperArgs = Commandline::splitArgs(wrapperCommandStr); auto wrapperCommand = wrapperArgs.takeFirst(); auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand); - if (realWrapperCommand.isEmpty()) - { - const char *reason = QT_TR_NOOP("The wrapper command \"%1\" couldn't be found."); + if (realWrapperCommand.isEmpty()) { + const char* reason = QT_TR_NOOP("The wrapper command \"%1\" couldn't be found."); emit logLine(QString(reason).arg(wrapperCommand), MessageLevel::Fatal); emitFailed(tr(reason).arg(wrapperCommand)); return; @@ -175,18 +163,14 @@ void LauncherPartLaunch::executeTask() emit logLine("Wrapper command is:\n" + wrapperCommandStr + "\n\n", MessageLevel::Launcher); args.prepend(javaPath); m_process.start(wrapperCommand, wrapperArgs + args); - } - else - { + } else { m_process.start(javaPath, args); } #ifdef Q_OS_LINUX - if (instance->settings()->get("EnableFeralGamemode").toBool() && APPLICATION->capabilities() & Application::SupportsGameMode) - { + if (instance->settings()->get("EnableFeralGamemode").toBool() && APPLICATION->capabilities() & Application::SupportsGameMode) { auto pid = m_process.processId(); - if (pid) - { + if (pid) { gamemode_request_start_for(pid); } } @@ -195,25 +179,21 @@ void LauncherPartLaunch::executeTask() void LauncherPartLaunch::on_state(LoggedProcess::State state) { - switch(state) - { - case LoggedProcess::FailedToStart: - { + switch (state) { + case LoggedProcess::FailedToStart: { //: Error message displayed if instace can't start - const char *reason = QT_TR_NOOP("Could not launch Minecraft!"); + const char* reason = QT_TR_NOOP("Could not launch Minecraft!"); emit logLine(reason, MessageLevel::Fatal); emitFailed(tr(reason)); return; } case LoggedProcess::Aborted: - case LoggedProcess::Crashed: - { + case LoggedProcess::Crashed: { m_parent->setPid(-1); emitFailed(tr("Game crashed.")); return; } - case LoggedProcess::Finished: - { + case LoggedProcess::Finished: { auto instance = m_parent->instance(); if (instance->settings()->get("CloseAfterLaunch").toBool()) APPLICATION->showMainWindow(); @@ -221,14 +201,13 @@ void LauncherPartLaunch::on_state(LoggedProcess::State state) m_parent->setPid(-1); // if the exit code wasn't 0, report this as a crash auto exitCode = m_process.exitCode(); - if(exitCode != 0) - { + if (exitCode != 0) { emitFailed(tr("Game crashed.")); return; } - //FIXME: make this work again - // m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(exitCode)); - // run post-exit + // FIXME: make this work again + // m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(exitCode)); + // run post-exit emitSucceeded(); break; } @@ -247,15 +226,14 @@ void LauncherPartLaunch::on_state(LoggedProcess::State state) } } -void LauncherPartLaunch::setWorkingDirectory(const QString &wd) +void LauncherPartLaunch::setWorkingDirectory(const QString& wd) { m_process.setWorkingDirectory(wd); } void LauncherPartLaunch::proceed() { - if(mayProceed) - { + if (mayProceed) { QString launchString("launch\n"); m_process.write(launchString.toUtf8()); mayProceed = false; @@ -264,17 +242,13 @@ void LauncherPartLaunch::proceed() bool LauncherPartLaunch::abort() { - if(mayProceed) - { + if (mayProceed) { mayProceed = false; QString launchString("abort\n"); m_process.write(launchString.toUtf8()); - } - else - { + } else { auto state = m_process.state(); - if (state == LoggedProcess::Running || state == LoggedProcess::Starting) - { + if (state == LoggedProcess::Running || state == LoggedProcess::Starting) { m_process.kill(); } } diff --git a/launcher/minecraft/launch/LauncherPartLaunch.h b/launcher/minecraft/launch/LauncherPartLaunch.h index 6a7ee0e5f..9f6ca1e7b 100644 --- a/launcher/minecraft/launch/LauncherPartLaunch.h +++ b/launcher/minecraft/launch/LauncherPartLaunch.h @@ -15,41 +15,31 @@ #pragma once -#include #include +#include #include #include "MinecraftServerTarget.h" -class LauncherPartLaunch: public LaunchStep -{ +class LauncherPartLaunch : public LaunchStep { Q_OBJECT -public: - explicit LauncherPartLaunch(LaunchTask *parent); - virtual ~LauncherPartLaunch() {}; + public: + explicit LauncherPartLaunch(LaunchTask* parent); + virtual ~LauncherPartLaunch(){}; virtual void executeTask(); virtual bool abort(); virtual void proceed(); - virtual bool canAbort() const - { - return true; - } - void setWorkingDirectory(const QString &wd); - void setAuthSession(AuthSessionPtr session) - { - m_session = session; - } + virtual bool canAbort() const { return true; } + void setWorkingDirectory(const QString& wd); + void setAuthSession(AuthSessionPtr session) { m_session = session; } - void setServerToJoin(MinecraftServerTargetPtr serverToJoin) - { - m_serverToJoin = std::move(serverToJoin); - } + void setServerToJoin(MinecraftServerTargetPtr serverToJoin) { m_serverToJoin = std::move(serverToJoin); } -private slots: + private slots: void on_state(LoggedProcess::State state); -private: + private: LoggedProcess m_process; QString m_command; AuthSessionPtr m_session; diff --git a/launcher/minecraft/launch/MinecraftServerTarget.cpp b/launcher/minecraft/launch/MinecraftServerTarget.cpp index a3383ec08..e201efab1 100644 --- a/launcher/minecraft/launch/MinecraftServerTarget.cpp +++ b/launcher/minecraft/launch/MinecraftServerTarget.cpp @@ -18,50 +18,43 @@ #include // FIXME: the way this is written, it can't ever do any sort of validation and can accept total junk -MinecraftServerTarget MinecraftServerTarget::parse(const QString &fullAddress) { +MinecraftServerTarget MinecraftServerTarget::parse(const QString& fullAddress) +{ QStringList split = fullAddress.split(":"); // 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 Prism Launcher when entering server addresses. - if (fullAddress.startsWith("[")) - { + if (fullAddress.startsWith("[")) { int bracket = fullAddress.indexOf("]"); - if (bracket > 0) - { + if (bracket > 0) { QString ipv6 = fullAddress.mid(1, bracket - 1); QString port = fullAddress.mid(bracket + 1).trimmed(); - if (port.startsWith(":") && !ipv6.isEmpty()) - { + if (port.startsWith(":") && !ipv6.isEmpty()) { port = port.mid(1); split = QStringList({ ipv6, port }); - } - else - { - split = QStringList({ipv6}); + } else { + split = QStringList({ ipv6 }); } } } - if (split.size() > 2) - { - split = QStringList({fullAddress}); + if (split.size() > 2) { + split = QStringList({ fullAddress }); } QString realAddress = split[0]; quint16 realPort = 25565; - if (split.size() > 1) - { + if (split.size() > 1) { bool ok; realPort = split[1].toUInt(&ok); - if (!ok) - { + if (!ok) { realPort = 25565; } } - return MinecraftServerTarget { realAddress, realPort }; + return MinecraftServerTarget{ realAddress, realPort }; } diff --git a/launcher/minecraft/launch/MinecraftServerTarget.h b/launcher/minecraft/launch/MinecraftServerTarget.h index a402421af..af8d6550b 100644 --- a/launcher/minecraft/launch/MinecraftServerTarget.h +++ b/launcher/minecraft/launch/MinecraftServerTarget.h @@ -23,7 +23,7 @@ struct MinecraftServerTarget { QString address; quint16 port; - static MinecraftServerTarget parse(const QString &fullAddress); + static MinecraftServerTarget parse(const QString& fullAddress); }; typedef std::shared_ptr MinecraftServerTargetPtr; diff --git a/launcher/minecraft/launch/ModMinecraftJar.cpp b/launcher/minecraft/launch/ModMinecraftJar.cpp index 1d6eecf2d..6e73333b1 100644 --- a/launcher/minecraft/launch/ModMinecraftJar.cpp +++ b/launcher/minecraft/launch/ModMinecraftJar.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -34,9 +34,9 @@ */ #include "ModMinecraftJar.h" -#include "launch/LaunchTask.h" -#include "MMCZip.h" #include "FileSystem.h" +#include "MMCZip.h" +#include "launch/LaunchTask.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" @@ -44,20 +44,17 @@ void ModMinecraftJar::executeTask() { auto m_inst = std::dynamic_pointer_cast(m_parent->instance()); - if(!m_inst->getJarMods().size()) - { + if (!m_inst->getJarMods().size()) { emitSucceeded(); return; } // nuke obsolete stripped jar(s) if needed - if(!FS::ensureFolderPathExists(m_inst->binRoot())) - { + if (!FS::ensureFolderPathExists(m_inst->binRoot())) { emitFailed(tr("Couldn't create the bin folder for Minecraft.jar")); } auto finalJarPath = QDir(m_inst->binRoot()).absoluteFilePath("minecraft.jar"); - if(!removeJar()) - { + if (!removeJar()) { emitFailed(tr("Couldn't remove stale jar file: %1").arg(finalJarPath)); } @@ -65,14 +62,12 @@ void ModMinecraftJar::executeTask() auto components = m_inst->getPackProfile(); auto profile = components->getProfile(); auto jarMods = m_inst->getJarMods(); - if(jarMods.size()) - { + if (jarMods.size()) { auto mainJar = profile->getMainJar(); QStringList jars, temp1, temp2, temp3, temp4; mainJar->getApplicableFiles(m_inst->runtimeContext(), jars, temp1, temp2, temp3, m_inst->getLocalLibraryPath()); auto sourceJarPath = jars[0]; - if(!MMCZip::createModdedJar(sourceJarPath, finalJarPath, jarMods)) - { + if (!MMCZip::createModdedJar(sourceJarPath, finalJarPath, jarMods)) { emitFailed(tr("Failed to create the custom Minecraft jar file.")); return; } @@ -90,10 +85,8 @@ bool ModMinecraftJar::removeJar() auto m_inst = std::dynamic_pointer_cast(m_parent->instance()); auto finalJarPath = QDir(m_inst->binRoot()).absoluteFilePath("minecraft.jar"); QFile finalJar(finalJarPath); - if(finalJar.exists()) - { - if(!finalJar.remove()) - { + if (finalJar.exists()) { + if (!finalJar.remove()) { return false; } } diff --git a/launcher/minecraft/launch/ModMinecraftJar.h b/launcher/minecraft/launch/ModMinecraftJar.h index 081c6a914..12e73b5f8 100644 --- a/launcher/minecraft/launch/ModMinecraftJar.h +++ b/launcher/minecraft/launch/ModMinecraftJar.h @@ -18,19 +18,16 @@ #include #include -class ModMinecraftJar: public LaunchStep -{ +class ModMinecraftJar : public LaunchStep { Q_OBJECT -public: - explicit ModMinecraftJar(LaunchTask *parent) : LaunchStep(parent) {}; + public: + explicit ModMinecraftJar(LaunchTask* parent) : LaunchStep(parent){}; virtual ~ModMinecraftJar(){}; virtual void executeTask() override; - virtual bool canAbort() const override - { - return false; - } + virtual bool canAbort() const override { return false; } void finalize() override; -private: + + private: bool removeJar(); }; diff --git a/launcher/minecraft/launch/PrintInstanceInfo.cpp b/launcher/minecraft/launch/PrintInstanceInfo.cpp index e8fbcb9b7..e3a45b030 100644 --- a/launcher/minecraft/launch/PrintInstanceInfo.cpp +++ b/launcher/minecraft/launch/PrintInstanceInfo.cpp @@ -16,50 +16,44 @@ #include #include -#include "PrintInstanceInfo.h" #include +#include "PrintInstanceInfo.h" #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) namespace { #if defined(Q_OS_LINUX) -void probeProcCpuinfo(QStringList &log) +void probeProcCpuinfo(QStringList& log) { std::ifstream cpuin("/proc/cpuinfo"); - for (std::string line; std::getline(cpuin, line);) - { - if (strncmp(line.c_str(), "model name", 10) == 0) - { + for (std::string line; std::getline(cpuin, line);) { + if (strncmp(line.c_str(), "model name", 10) == 0) { log << QString::fromStdString(line.substr(13, std::string::npos)); break; } } } -void runLspci(QStringList &log) +void runLspci(QStringList& log) { // FIXME: fixed size buffers... char buff[512]; int gpuline = -1; int cline = 0; - FILE * lspci = popen("lspci -k", "r"); + FILE* lspci = popen("lspci -k", "r"); if (!lspci) return; - while (fgets(buff, 512, lspci) != NULL) - { + while (fgets(buff, 512, lspci) != NULL) { std::string str(buff); if (str.length() < 9) continue; - if (str.substr(8, 3) == "VGA") - { + if (str.substr(8, 3) == "VGA") { gpuline = cline; log << QString::fromStdString(str.substr(35, std::string::npos)); } - if (gpuline > -1 && gpuline != cline) - { - if (cline - gpuline < 3) - { + if (gpuline > -1 && gpuline != cline) { + if (cline - gpuline < 3) { log << QString::fromStdString(str.substr(1, std::string::npos)); } } @@ -68,54 +62,47 @@ void runLspci(QStringList &log) pclose(lspci); } #elif defined(Q_OS_FREEBSD) -void runSysctlHwModel(QStringList &log) +void runSysctlHwModel(QStringList& log) { char buff[512]; - FILE *hwmodel = popen("sysctl hw.model", "r"); - while (fgets(buff, 512, hwmodel) != NULL) - { - log << QString::fromUtf8(buff); - break; + FILE* hwmodel = popen("sysctl hw.model", "r"); + while (fgets(buff, 512, hwmodel) != NULL) { + log << QString::fromUtf8(buff); + break; } pclose(hwmodel); } -void runPciconf(QStringList &log) +void runPciconf(QStringList& log) { char buff[512]; std::string strcard; - FILE *pciconf = popen("pciconf -lv -a vgapci0", "r"); - while (fgets(buff, 512, pciconf) != NULL) - { - if (strncmp(buff, " vendor", 10) == 0) - { - std::string str(buff); - strcard.append(str.substr(str.find_first_of("'") + 1, str.find_last_not_of("'") - (str.find_first_of("'") + 2))); - strcard.append(" "); - } - else if (strncmp(buff, " device", 10) == 0) - { - std::string str2(buff); - strcard.append(str2.substr(str2.find_first_of("'") + 1, str2.find_last_not_of("'") - (str2.find_first_of("'") + 2))); - } - log << QString::fromStdString(strcard); - break; + FILE* pciconf = popen("pciconf -lv -a vgapci0", "r"); + while (fgets(buff, 512, pciconf) != NULL) { + if (strncmp(buff, " vendor", 10) == 0) { + std::string str(buff); + strcard.append(str.substr(str.find_first_of("'") + 1, str.find_last_not_of("'") - (str.find_first_of("'") + 2))); + strcard.append(" "); + } else if (strncmp(buff, " device", 10) == 0) { + std::string str2(buff); + strcard.append(str2.substr(str2.find_first_of("'") + 1, str2.find_last_not_of("'") - (str2.find_first_of("'") + 2))); + } + log << QString::fromStdString(strcard); + break; } pclose(pciconf); } #endif -void runGlxinfo(QStringList & log) +void runGlxinfo(QStringList& log) { // FIXME: fixed size buffers... char buff[512]; - FILE *glxinfo = popen("glxinfo", "r"); + FILE* glxinfo = popen("glxinfo", "r"); if (!glxinfo) return; - while (fgets(buff, 512, glxinfo) != NULL) - { - if (strncmp(buff, "OpenGL version string:", 22) == 0) - { + while (fgets(buff, 512, glxinfo) != NULL) { + if (strncmp(buff, "OpenGL version string:", 22) == 0) { log << QString::fromUtf8(buff); break; } @@ -123,7 +110,7 @@ void runGlxinfo(QStringList & log) pclose(glxinfo); } -} +} // namespace #endif void PrintInstanceInfo::executeTask() diff --git a/launcher/minecraft/launch/PrintInstanceInfo.h b/launcher/minecraft/launch/PrintInstanceInfo.h index fdc30f316..8e1c41b62 100644 --- a/launcher/minecraft/launch/PrintInstanceInfo.h +++ b/launcher/minecraft/launch/PrintInstanceInfo.h @@ -21,21 +21,17 @@ #include "minecraft/launch/MinecraftServerTarget.h" // FIXME: temporary wrapper for existing task. -class PrintInstanceInfo: public LaunchStep -{ +class PrintInstanceInfo : public LaunchStep { Q_OBJECT -public: - explicit PrintInstanceInfo(LaunchTask *parent, AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) : - LaunchStep(parent), m_session(session), m_serverToJoin(serverToJoin) {}; + public: + explicit PrintInstanceInfo(LaunchTask* parent, AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) + : LaunchStep(parent), m_session(session), m_serverToJoin(serverToJoin){}; virtual ~PrintInstanceInfo(){}; virtual void executeTask(); - virtual bool canAbort() const - { - return false; - } -private: + virtual bool canAbort() const { return false; } + + private: AuthSessionPtr m_session; MinecraftServerTargetPtr m_serverToJoin; }; - diff --git a/launcher/minecraft/launch/ReconstructAssets.cpp b/launcher/minecraft/launch/ReconstructAssets.cpp index 4d206665f..843ccc554 100644 --- a/launcher/minecraft/launch/ReconstructAssets.cpp +++ b/launcher/minecraft/launch/ReconstructAssets.cpp @@ -14,10 +14,10 @@ */ #include "ReconstructAssets.h" +#include "launch/LaunchTask.h" +#include "minecraft/AssetsUtils.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" -#include "minecraft/AssetsUtils.h" -#include "launch/LaunchTask.h" void ReconstructAssets::executeTask() { @@ -27,8 +27,7 @@ void ReconstructAssets::executeTask() auto profile = components->getProfile(); auto assets = profile->getMinecraftAssets(); - if(!AssetsUtils::reconstructAssets(assets->id, minecraftInstance->resourcesDir())) - { + if (!AssetsUtils::reconstructAssets(assets->id, minecraftInstance->resourcesDir())) { emit logLine("Failed to reconstruct Minecraft assets.", MessageLevel::Error); } diff --git a/launcher/minecraft/launch/ReconstructAssets.h b/launcher/minecraft/launch/ReconstructAssets.h index 58d7febd1..bd867c8d4 100644 --- a/launcher/minecraft/launch/ReconstructAssets.h +++ b/launcher/minecraft/launch/ReconstructAssets.h @@ -18,16 +18,12 @@ #include #include -class ReconstructAssets: public LaunchStep -{ +class ReconstructAssets : public LaunchStep { Q_OBJECT -public: - explicit ReconstructAssets(LaunchTask *parent) : LaunchStep(parent){}; + public: + explicit ReconstructAssets(LaunchTask* parent) : LaunchStep(parent){}; virtual ~ReconstructAssets(){}; void executeTask() override; - bool canAbort() const override - { - return false; - } + bool canAbort() const override { return false; } }; diff --git a/launcher/minecraft/launch/ScanModFolders.cpp b/launcher/minecraft/launch/ScanModFolders.cpp index 71e7638cd..7e08a4e36 100644 --- a/launcher/minecraft/launch/ScanModFolders.cpp +++ b/launcher/minecraft/launch/ScanModFolders.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -34,9 +34,9 @@ */ #include "ScanModFolders.h" -#include "launch/LaunchTask.h" -#include "MMCZip.h" #include "FileSystem.h" +#include "MMCZip.h" +#include "launch/LaunchTask.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/mod/ModFolderModel.h" @@ -46,19 +46,19 @@ void ScanModFolders::executeTask() auto loaders = m_inst->loaderModList(); connect(loaders.get(), &ModFolderModel::updateFinished, this, &ScanModFolders::modsDone); - if(!loaders->update()) { + if (!loaders->update()) { m_modsDone = true; } auto cores = m_inst->coreModList(); connect(cores.get(), &ModFolderModel::updateFinished, this, &ScanModFolders::coreModsDone); - if(!cores->update()) { + if (!cores->update()) { m_coreModsDone = true; } auto nils = m_inst->nilModList(); connect(nils.get(), &ModFolderModel::updateFinished, this, &ScanModFolders::nilModsDone); - if(!nils->update()) { + if (!nils->update()) { m_nilModsDone = true; } checkDone(); @@ -84,7 +84,7 @@ void ScanModFolders::nilModsDone() void ScanModFolders::checkDone() { - if(m_modsDone && m_coreModsDone && m_nilModsDone) { + if (m_modsDone && m_coreModsDone && m_nilModsDone) { emitSucceeded(); } } diff --git a/launcher/minecraft/launch/ScanModFolders.h b/launcher/minecraft/launch/ScanModFolders.h index 111a5850b..a5b75825b 100644 --- a/launcher/minecraft/launch/ScanModFolders.h +++ b/launcher/minecraft/launch/ScanModFolders.h @@ -18,26 +18,23 @@ #include #include -class ScanModFolders: public LaunchStep -{ +class ScanModFolders : public LaunchStep { Q_OBJECT -public: - explicit ScanModFolders(LaunchTask *parent) : LaunchStep(parent) {}; + public: + explicit ScanModFolders(LaunchTask* parent) : LaunchStep(parent){}; virtual ~ScanModFolders(){}; virtual void executeTask() override; - virtual bool canAbort() const override - { - return false; - } -private slots: + virtual bool canAbort() const override { return false; } + private slots: void coreModsDone(); void modsDone(); void nilModsDone(); -private: + + private: void checkDone(); -private: // DATA + private: // DATA bool m_modsDone = false; bool m_nilModsDone = false; bool m_coreModsDone = false; diff --git a/launcher/minecraft/launch/VerifyJavaInstall.cpp b/launcher/minecraft/launch/VerifyJavaInstall.cpp index 6ae666b47..cdd1f7fd1 100644 --- a/launcher/minecraft/launch/VerifyJavaInstall.cpp +++ b/launcher/minecraft/launch/VerifyJavaInstall.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -36,10 +36,11 @@ #include "VerifyJavaInstall.h" #include "java/JavaVersion.h" -#include "minecraft/PackProfile.h" #include "minecraft/MinecraftInstance.h" +#include "minecraft/PackProfile.h" -void VerifyJavaInstall::executeTask() { +void VerifyJavaInstall::executeTask() +{ auto instance = std::dynamic_pointer_cast(m_parent->instance()); auto packProfile = instance->getPackProfile(); auto settings = instance->settings(); @@ -50,28 +51,27 @@ void VerifyJavaInstall::executeTask() { JavaVersion javaVersion(storedVersion); - if (compatibleMajors.isEmpty() || compatibleMajors.contains(javaVersion.major())) - { + if (compatibleMajors.isEmpty() || compatibleMajors.contains(javaVersion.major())) { emitSucceeded(); return; } - - if (ignoreCompatibility) - { + if (ignoreCompatibility) { emit logLine(tr("Java major version is incompatible. Things might break."), MessageLevel::Warning); emitSucceeded(); return; } emit logLine(tr("This instance is not compatible with Java version %1.\n" - "Please switch to one of the following Java versions for this instance:").arg(javaVersion.major()), + "Please switch to one of the following Java versions for this instance:") + .arg(javaVersion.major()), MessageLevel::Error); - for (auto major : compatibleMajors) - { + for (auto major : compatibleMajors) { emit logLine(tr("Java version %1").arg(major), MessageLevel::Error); } - emit logLine(tr("Go to instance Java settings to change your Java version or disable the Java compatibility check if you know what you're doing."), MessageLevel::Error); + emit logLine(tr("Go to instance Java settings to change your Java version or disable the Java compatibility check if you know what " + "you're doing."), + MessageLevel::Error); emitFailed(QString("Incompatible Java major version")); } diff --git a/launcher/minecraft/launch/VerifyJavaInstall.h b/launcher/minecraft/launch/VerifyJavaInstall.h index 9139c0faf..dabbf3b25 100644 --- a/launcher/minecraft/launch/VerifyJavaInstall.h +++ b/launcher/minecraft/launch/VerifyJavaInstall.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -41,13 +41,10 @@ class VerifyJavaInstall : public LaunchStep { Q_OBJECT -public: - explicit VerifyJavaInstall(LaunchTask *parent) : LaunchStep(parent) { - }; + public: + explicit VerifyJavaInstall(LaunchTask* parent) : LaunchStep(parent){}; ~VerifyJavaInstall() override = default; void executeTask() override; - bool canAbort() const override { - return false; - } + bool canAbort() const override { return false; } }; diff --git a/launcher/minecraft/mod/DataPack.cpp b/launcher/minecraft/mod/DataPack.cpp index c5754638a..7bf5a3112 100644 --- a/launcher/minecraft/mod/DataPack.cpp +++ b/launcher/minecraft/mod/DataPack.cpp @@ -30,10 +30,10 @@ // Values taken from: // https://minecraft.fandom.com/wiki/Tutorials/Creating_a_data_pack#%22pack_format%22 static const QMap> s_pack_format_versions = { - { 4, { Version("1.13"), Version("1.14.4") } }, { 5, { Version("1.15"), Version("1.16.1") } }, - { 6, { Version("1.16.2"), Version("1.16.5") } }, { 7, { Version("1.17"), Version("1.17.1") } }, - { 8, { Version("1.18"), Version("1.18.1") } }, { 9, { Version("1.18.2"), Version("1.18.2") } }, - { 10, { Version("1.19"), Version("1.19.3") } }, { 11, { Version("23w03a"), Version("23w05a") } }, + { 4, { Version("1.13"), Version("1.14.4") } }, { 5, { Version("1.15"), Version("1.16.1") } }, + { 6, { Version("1.16.2"), Version("1.16.5") } }, { 7, { Version("1.17"), Version("1.17.1") } }, + { 8, { Version("1.18"), Version("1.18.1") } }, { 9, { Version("1.18.2"), Version("1.18.2") } }, + { 10, { Version("1.19"), Version("1.19.3") } }, { 11, { Version("23w03a"), Version("23w05a") } }, { 12, { Version("1.19.4"), Version("1.19.4") } }, { 13, { Version("23w12a"), Version("23w14a") } }, { 14, { Version("23w16a"), Version("23w17a") } }, { 15, { Version("1.20"), Version("1.20") } }, }; diff --git a/launcher/minecraft/mod/MetadataHandler.h b/launcher/minecraft/mod/MetadataHandler.h index 39723b49c..88e9ff2b6 100644 --- a/launcher/minecraft/mod/MetadataHandler.h +++ b/launcher/minecraft/mod/MetadataHandler.h @@ -1,20 +1,20 @@ // SPDX-License-Identifier: GPL-3.0-only /* -* PolyMC - Minecraft Launcher -* Copyright (c) 2022 flowln -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, version 3. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #pragma once @@ -42,28 +42,13 @@ class Metadata { return Packwiz::V1::createModFormat(index_dir, internal_mod, mod_slug); } - static void update(QDir& index_dir, ModStruct& mod) - { - Packwiz::V1::updateModIndex(index_dir, mod); - } + static void update(QDir& index_dir, ModStruct& mod) { Packwiz::V1::updateModIndex(index_dir, mod); } - static void remove(QDir& index_dir, QString mod_slug) - { - Packwiz::V1::deleteModIndex(index_dir, mod_slug); - } + static void remove(QDir& index_dir, QString mod_slug) { Packwiz::V1::deleteModIndex(index_dir, mod_slug); } - static void remove(QDir& index_dir, QVariant& mod_id) - { - Packwiz::V1::deleteModIndex(index_dir, mod_id); - } + static void remove(QDir& index_dir, QVariant& mod_id) { Packwiz::V1::deleteModIndex(index_dir, mod_id); } - static auto get(QDir& index_dir, QString mod_slug) -> ModStruct - { - return Packwiz::V1::getIndexForMod(index_dir, mod_slug); - } + static auto get(QDir& index_dir, QString mod_slug) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_slug); } - static auto get(QDir& index_dir, QVariant& mod_id) -> ModStruct - { - return Packwiz::V1::getIndexForMod(index_dir, mod_id); - } + static auto get(QDir& index_dir, QVariant& mod_id) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_id); } }; diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 880dacb15..ae3dea8d8 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -1,45 +1,45 @@ // SPDX-License-Identifier: GPL-3.0-only /* -* PolyMC - Minecraft Launcher -* Copyright (c) 2022 flowln -* Copyright (C) 2022 Sefa Eyeoglu -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, version 3. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* This file incorporates work covered by the following copyright and -* permission notice: -* -* Copyright 2013-2021 MultiMC Contributors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "Mod.h" #include #include -#include #include +#include #include "MTPixmapCache.h" #include "MetadataHandler.h" @@ -54,8 +54,7 @@ Mod::Mod(const QFileInfo& file) : Resource(file), m_local_details() m_enabled = (file.suffix() != "disabled"); } -Mod::Mod(const QDir& mods_dir, const Metadata::ModStruct& metadata) - : Mod(mods_dir.absoluteFilePath(metadata.filename)) +Mod::Mod(const QDir& mods_dir, const Metadata::ModStruct& metadata) : Mod(mods_dir.absoluteFilePath(metadata.filename)) { m_name = metadata.name; m_local_details.metadata = std::make_shared(std::move(metadata)); @@ -73,7 +72,8 @@ void Mod::setMetadata(std::shared_ptr&& metadata) m_local_details.metadata = metadata; } -void Mod::setDetails(const ModDetails& details) { +void Mod::setDetails(const ModDetails& details) +{ m_local_details = details; } @@ -103,7 +103,8 @@ std::pair Mod::compare(const Resource& other, SortType type) const break; } case SortType::PROVIDER: { - auto compare_result = QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive); + auto compare_result = + QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive); if (compare_result != 0) return { compare_result, type == SortType::PROVIDER }; break; @@ -230,7 +231,7 @@ auto Mod::licenses() const -> const QList& return details().licenses; } - auto Mod::issueTracker() const -> QString +auto Mod::issueTracker() const -> QString { return details().issue_tracker; } @@ -245,7 +246,7 @@ void Mod::setIcon(QImage new_image) const PixmapCache::remove(m_pack_image_cache_key.key); // scale the image to avoid flooding the pixmapcache - auto pixmap = QPixmap::fromImage(new_image.scaled({64, 64}, Qt::AspectRatioMode::KeepAspectRatioByExpanding)); + auto pixmap = QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding)); m_pack_image_cache_key.key = PixmapCache::insert(pixmap); m_pack_image_cache_key.was_ever_used = true; diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index b67bd4659..6dafecfc5 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -1,79 +1,78 @@ // SPDX-License-Identifier: GPL-3.0-only /* -* PolyMC - Minecraft Launcher -* Copyright (c) 2022 flowln -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, version 3. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* This file incorporates work covered by the following copyright and -* permission notice: -* -* Copyright 2013-2021 MultiMC Contributors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include #include -#include #include +#include #include #include #include #include -#include "Resource.h" #include "ModDetails.h" +#include "Resource.h" -class Mod : public Resource -{ +class Mod : public Resource { Q_OBJECT -public: + public: using Ptr = shared_qobject_ptr; using WeakPtr = QPointer; Mod() = default; - Mod(const QFileInfo &file); + Mod(const QFileInfo& file); Mod(const QDir& mods_dir, const Metadata::ModStruct& metadata); Mod(QString file_path) : Mod(QFileInfo(file_path)) {} - auto details() const -> const ModDetails&; - auto name() const -> QString override; - auto version() const -> QString; - auto homeurl() const -> QString; + auto details() const -> const ModDetails&; + auto name() const -> QString override; + auto version() const -> QString; + auto homeurl() const -> QString; auto description() const -> QString; - auto authors() const -> QStringList; - auto status() const -> ModStatus; - auto provider() const -> std::optional; - auto licenses() const -> const QList&; + auto authors() const -> QStringList; + auto status() const -> ModStatus; + auto provider() const -> std::optional; + auto licenses() const -> const QList&; auto issueTracker() const -> QString; - auto metaurl() const -> QString; + auto metaurl() const -> QString; /** Get the intneral path to the mod's icon file*/ - QString iconPath() const { return m_local_details.icon_file; }; + QString iconPath() const { return m_local_details.icon_file; } /** Gets the icon of the mod, converted to a QPixmap for drawing, and scaled to size. */ [[nodiscard]] QPixmap icon(QSize size, Qt::AspectRatioMode mode = Qt::AspectRatioMode::IgnoreAspectRatio) const; /** Thread-safe. */ @@ -97,7 +96,7 @@ public: void finishResolvingWithDetails(ModDetails&& details); -protected: + protected: ModDetails m_local_details; mutable QMutex m_data_lock; @@ -107,5 +106,4 @@ protected: bool was_ever_used = false; bool was_read_attempt = false; } mutable m_pack_image_cache_key; - }; diff --git a/launcher/minecraft/mod/ModDetails.h b/launcher/minecraft/mod/ModDetails.h index b4e59d52d..a00d5a24b 100644 --- a/launcher/minecraft/mod/ModDetails.h +++ b/launcher/minecraft/mod/ModDetails.h @@ -1,37 +1,37 @@ // SPDX-License-Identifier: GPL-3.0-only /* -* PolyMC - Minecraft Launcher -* Copyright (c) 2022 flowln -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, version 3. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* This file incorporates work covered by the following copyright and -* permission notice: -* -* Copyright 2013-2021 MultiMC Contributors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once @@ -44,10 +44,10 @@ #include "minecraft/mod/MetadataHandler.h" enum class ModStatus { - Installed, // Both JAR and Metadata are present - NotInstalled, // Only the Metadata is present - NoMetadata, // Only the JAR is present - Unknown, // Default status + Installed, // Both JAR and Metadata are present + NotInstalled, // Only the Metadata is present + NoMetadata, // Only the JAR is present + Unknown, // Default status }; struct ModLicense { @@ -58,18 +58,19 @@ struct ModLicense { ModLicense() {} - ModLicense(const QString license) { - // FIXME: come up with a better license parseing. + ModLicense(const QString license) + { + // FIXME: come up with a better license parsing. // handle SPDX identifiers? https://spdx.org/licenses/ auto parts = license.split(' '); - QStringList notNameParts = {}; + QStringList notNameParts = {}; for (auto part : parts) { - auto url = QUrl(part); + auto _url = QUrl(part); if (part.startsWith("(") && part.endsWith(")")) - url = QUrl(part.mid(1, part.size() - 2)); + _url = QUrl(part.mid(1, part.size() - 2)); - if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { - this->url = url.toString(); + if (_url.isValid() && !_url.scheme().isEmpty() && !_url.host().isEmpty()) { + this->url = _url.toString(); notNameParts.append(part); continue; } @@ -78,7 +79,7 @@ struct ModLicense { for (auto part : notNameParts) { parts.removeOne(part); } - + auto licensePart = parts.join(' '); this->name = licensePart; this->description = licensePart; @@ -86,23 +87,14 @@ struct ModLicense { if (parts.size() == 1) { this->id = parts.first(); } - } - ModLicense(const QString name, const QString id, const QString url, const QString description) { - this->name = name; - this->id = id; - this->url = url; - this->description = description; - } - - ModLicense(const ModLicense& other) - : name(other.name) - , id(other.id) - , url(other.url) - , description(other.description) + ModLicense(const QString& name_, const QString& id_, const QString& url_, const QString& description_) + : name(name_), id(id_), url(url_), description(description_) {} + ModLicense(const ModLicense& other) : name(other.name), id(other.id), url(other.url), description(other.description) {} + ModLicense& operator=(const ModLicense& other) { this->name = other.name; @@ -123,28 +115,25 @@ struct ModLicense { return *this; } - bool isEmpty() { - return this->name.isEmpty() && this->id.isEmpty() && this->url.isEmpty() && this->description.isEmpty(); - } + bool isEmpty() { return this->name.isEmpty() && this->id.isEmpty() && this->url.isEmpty() && this->description.isEmpty(); } }; -struct ModDetails -{ +struct ModDetails { /* Mod ID as defined in the ModLoader-specific metadata */ QString mod_id = {}; - + /* Human-readable name */ QString name = {}; - + /* Human-readable mod version */ QString version = {}; - + /* Human-readable minecraft version */ QString mcversion = {}; - + /* URL for mod's home page */ QString homeurl = {}; - + /* Human-readable description */ QString description = {}; diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 51383edf0..280e70d7b 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -1,38 +1,38 @@ // SPDX-License-Identifier: GPL-3.0-only /* -* PolyMC - Minecraft Launcher -* Copyright (c) 2022 flowln -* Copyright (C) 2022 Sefa Eyeoglu -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, version 3. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* This file incorporates work covered by the following copyright and -* permission notice: -* -* Copyright 2013-2021 MultiMC Contributors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ModFolderModel.h" @@ -59,11 +59,13 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool { m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME , SortType::VERSION, SortType::DATE, SortType::PROVIDER}; - m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents}; + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER }; + m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, + QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; + m_columnsHideable = { false, true, false, true, true, true }; } -QVariant ModFolderModel::data(const QModelIndex &index, int role) const +QVariant ModFolderModel::data(const QModelIndex& index, int role) const { if (!validateIndex(index)) return {}; @@ -71,115 +73,109 @@ QVariant ModFolderModel::data(const QModelIndex &index, int role) const int row = index.row(); int column = index.column(); - switch (role) - { - case Qt::DisplayRole: - switch (column) - { - case NameColumn: - return m_resources[row]->name(); - case VersionColumn: { - switch(m_resources[row]->type()) { - case ResourceType::FOLDER: - return tr("Folder"); - case ResourceType::SINGLEFILE: - return tr("File"); + switch (role) { + case Qt::DisplayRole: + switch (column) { + case NameColumn: + return m_resources[row]->name(); + case VersionColumn: { + switch (m_resources[row]->type()) { + case ResourceType::FOLDER: + return tr("Folder"); + case ResourceType::SINGLEFILE: + return tr("File"); + default: + break; + } + return at(row)->version(); + } + case DateColumn: + return m_resources[row]->dateTimeChanged(); + case ProviderColumn: { + auto provider = at(row)->provider(); + if (!provider.has_value()) { + //: Unknown mod provider (i.e. not Modrinth, CurseForge, etc...) + return tr("Unknown"); + } + + return provider.value(); + } default: - break; - } - return at(row)->version(); - } - case DateColumn: - return m_resources[row]->dateTimeChanged(); - case ProviderColumn: { - auto provider = at(row)->provider(); - if (!provider.has_value()) { - //: Unknown mod provider (i.e. not Modrinth, CurseForge, etc...) - return tr("Unknown"); + return QVariant(); } - return provider.value(); + case Qt::ToolTipRole: + if (column == NAME_COLUMN) { + if (at(row)->isSymLinkUnder(instDirPath())) { + return m_resources[row]->internal_id() + + tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original." + "\nCanonical Path: %1") + .arg(at(row)->fileinfo().canonicalFilePath()); + } + if (at(row)->isMoreThanOneHardLink()) { + return m_resources[row]->internal_id() + + tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original."); + } + } + return m_resources[row]->internal_id(); + case Qt::DecorationRole: { + if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink())) + return APPLICATION->getThemedIcon("status-yellow"); + if (column == ImageColumn) { + return at(row)->icon({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); + } + return {}; } + case Qt::CheckStateRole: + switch (column) { + case ActiveColumn: + return at(row)->enabled() ? Qt::Checked : Qt::Unchecked; + default: + return QVariant(); + } default: return QVariant(); - } - - case Qt::ToolTipRole: - if (column == NAME_COLUMN) { - if (at(row)->isSymLinkUnder(instDirPath())) { - return m_resources[row]->internal_id() + - tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original." - "\nCanonical Path: %1") - .arg(at(row)->fileinfo().canonicalFilePath()); - } - if (at(row)->isMoreThanOneHardLink()) { - return m_resources[row]->internal_id() + - tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original."); - } - } - return m_resources[row]->internal_id(); - case Qt::DecorationRole: { - if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink())) - return APPLICATION->getThemedIcon("status-yellow"); - if (column == ImageColumn) { - return at(row)->icon({32, 32}, Qt::AspectRatioMode::KeepAspectRatioByExpanding); - } - return {}; - } - case Qt::CheckStateRole: - switch (column) - { - case ActiveColumn: - return at(row)->enabled() ? Qt::Checked : Qt::Unchecked; - default: - return QVariant(); - } - default: - return QVariant(); } } -QVariant ModFolderModel::headerData(int section, Qt::Orientation orientation, int role) const +QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const { - switch (role) - { - case Qt::DisplayRole: - switch (section) - { - case ActiveColumn: - case NameColumn: - case VersionColumn: - case DateColumn: - case ProviderColumn: - case ImageColumn: - return columnNames().at(section); - default: - return QVariant(); - } + switch (role) { + case Qt::DisplayRole: + switch (section) { + case ActiveColumn: + case NameColumn: + case VersionColumn: + case DateColumn: + case ProviderColumn: + case ImageColumn: + return columnNames().at(section); + default: + return QVariant(); + } - case Qt::ToolTipRole: - switch (section) - { - case ActiveColumn: - return tr("Is the mod enabled?"); - case NameColumn: - return tr("The name of the mod."); - case VersionColumn: - return tr("The version of the mod."); - case DateColumn: - return tr("The date and time this mod was last changed (or added)."); - case ProviderColumn: - return tr("Where the mod was downloaded from."); + case Qt::ToolTipRole: + switch (section) { + case ActiveColumn: + return tr("Is the mod enabled?"); + case NameColumn: + return tr("The name of the mod."); + case VersionColumn: + return tr("The version of the mod."); + case DateColumn: + return tr("The date and time this mod was last changed (or added)."); + case ProviderColumn: + return tr("Where the mod was downloaded from."); + default: + return QVariant(); + } default: return QVariant(); - } - default: - return QVariant(); } return QVariant(); } -int ModFolderModel::columnCount(const QModelIndex &parent) const +int ModFolderModel::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : NUM_COLUMNS; } @@ -199,8 +195,8 @@ Task* ModFolderModel::createParseTask(Resource& resource) bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata) { - for(auto mod : allMods()) { - if(mod->fileinfo().fileName() == filename) { + for (auto mod : allMods()) { + if (mod->fileinfo().fileName() == filename) { auto index_dir = indexDir(); mod->destroy(index_dir, preserve_metadata, false); @@ -253,7 +249,7 @@ auto ModFolderModel::selectedMods(QModelIndexList& indexes) -> QList { QList selected_resources; for (auto i : indexes) { - if(i.column() != 0) + if (i.column() != 0) continue; selected_resources.push_back(at(i.row())); diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 6ccaba235..06fd78149 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -1,53 +1,53 @@ // SPDX-License-Identifier: GPL-3.0-only /* -* PolyMC - Minecraft Launcher -* Copyright (c) 2022 flowln -* Copyright (C) 2022 Sefa Eyeoglu -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, version 3. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* This file incorporates work covered by the following copyright and -* permission notice: -* -* Copyright 2013-2021 MultiMC Contributors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once +#include +#include #include #include #include #include -#include -#include #include "Mod.h" #include "ResourceFolderModel.h" -#include "minecraft/mod/tasks/ModFolderLoadTask.h" #include "minecraft/mod/tasks/LocalModParseTask.h" +#include "minecraft/mod/tasks/ModFolderLoadTask.h" class LegacyInstance; class BaseInstance; @@ -57,33 +57,19 @@ class QFileSystemWatcher; * A legacy mod list. * Backed by a folder. */ -class ModFolderModel : public ResourceFolderModel -{ +class ModFolderModel : public ResourceFolderModel { Q_OBJECT -public: - enum Columns - { - ActiveColumn = 0, - ImageColumn, - NameColumn, - VersionColumn, - DateColumn, - ProviderColumn, - NUM_COLUMNS - }; - enum ModStatusAction { - Disable, - Enable, - Toggle - }; - ModFolderModel(const QString &dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true); + public: + enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, VersionColumn, DateColumn, ProviderColumn, NUM_COLUMNS }; + enum ModStatusAction { Disable, Enable, Toggle }; + ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true); virtual QString id() const override { return "mods"; } - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - int columnCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex& parent) const override; [[nodiscard]] Task* createUpdateTask() override; [[nodiscard]] Task* createParseTask(Resource&) override; @@ -92,7 +78,7 @@ public: bool uninstallMod(const QString& filename, bool preserve_metadata = false); /// Deletes all the selected mods - bool deleteMods(const QModelIndexList &indexes); + bool deleteMods(const QModelIndexList& indexes); bool isValid(); @@ -106,12 +92,11 @@ public: RESOURCE_HELPERS(Mod) -private -slots: + private slots: void onUpdateSucceeded() override; void onParseSucceeded(int ticket, QString resource_id) override; -protected: + protected: bool m_is_indexed; bool m_first_folder_load = true; }; diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index 098a617f8..da806f0f4 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -1,8 +1,7 @@ #include "Resource.h" - -#include #include +#include #include "FileSystem.h" @@ -105,7 +104,6 @@ bool Resource::enable(EnableAction action) if (m_type == ResourceType::UNKNOWN || m_type == ResourceType::FOLDER) return false; - QString path = m_file_info.absoluteFilePath(); QFile file(path); @@ -154,7 +152,7 @@ bool Resource::destroy(bool attemptTrash) return (attemptTrash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath()); } -bool Resource::isSymLinkUnder(const QString& instPath) const +bool Resource::isSymLinkUnder(const QString& instPath) const { if (isSymLink()) return true; @@ -167,7 +165,7 @@ bool Resource::isSymLinkUnder(const QString& instPath) const return relAbsPath != relCanonPath; } -bool Resource::isMoreThanOneHardLink() const +bool Resource::isMoreThanOneHardLink() const { return FS::hardLinkCount(m_file_info.absoluteFilePath()) > 1; } diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index 94f3160c3..c1ed49461 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -15,20 +15,9 @@ enum class ResourceType { LITEMOD, //!< The resource is a litemod }; -enum class SortType { - NAME, - DATE, - VERSION, - ENABLED, - PACK_FORMAT, - PROVIDER -}; +enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER }; -enum class EnableAction { - ENABLE, - DISABLE, - TOGGLE -}; +enum class EnableAction { ENABLE, DISABLE, TOGGLE }; /** General class for managed resources. It mirrors a file in disk, with some more info * for display and house-keeping purposes. @@ -98,10 +87,10 @@ class Resource : public QObject { /** * @brief Take a instance path, checks if the file pointed to by the resource is a symlink or under a symlink in that instance - * + * * @param instPath path to an instance directory - * @return true - * @return false + * @return true + * @return false */ [[nodiscard]] bool isSymLinkUnder(const QString& instPath) const; diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 39a61067e..d3237b34b 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -447,7 +447,7 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const } } -bool ResourceFolderModel::setData(const QModelIndex& index, const QVariant& value, int role) +bool ResourceFolderModel::setData(const QModelIndex& index, [[maybe_unused]] const QVariant& value, int role) { int row = index.row(); if (row < 0 || row >= rowCount(index.parent()) || !index.isValid()) @@ -471,7 +471,7 @@ bool ResourceFolderModel::setData(const QModelIndex& index, const QVariant& valu return false; } -QVariant ResourceFolderModel::headerData(int section, Qt::Orientation orientation, int role) const +QVariant ResourceFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const { switch (role) { case Qt::DisplayRole: @@ -515,25 +515,25 @@ void ResourceFolderModel::setupHeaderAction(QAction* act, int column) void ResourceFolderModel::saveHiddenColumn(int column, bool hidden) { auto const setting_name = QString("UI/%1_Page/HiddenColumns").arg(id()); - auto setting = (m_instance->settings()->contains(setting_name)) ? - m_instance->settings()->getSetting(setting_name) : m_instance->settings()->registerSetting(setting_name); + auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name) + : m_instance->settings()->registerSetting(setting_name); auto hiddenColumns = setting->get().toStringList(); auto name = columnNames(false).at(column); auto index = hiddenColumns.indexOf(name); if (index >= 0 && !hidden) { hiddenColumns.removeAt(index); - } else if ( index < 0 && hidden) { + } else if (index < 0 && hidden) { hiddenColumns.append(name); } setting->set(hiddenColumns); } -void ResourceFolderModel::loadHiddenColumns(QTreeView *tree) +void ResourceFolderModel::loadHiddenColumns(QTreeView* tree) { auto const setting_name = QString("UI/%1_Page/HiddenColumns").arg(id()); - auto setting = (m_instance->settings()->contains(setting_name)) ? - m_instance->settings()->getSetting(setting_name) : m_instance->settings()->registerSetting(setting_name); + auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name) + : m_instance->settings()->registerSetting(setting_name); auto hiddenColumns = setting->get().toStringList(); auto col_names = columnNames(false); @@ -542,7 +542,6 @@ void ResourceFolderModel::loadHiddenColumns(QTreeView *tree) if (index >= 0) tree->setColumnHidden(index, true); } - } QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree) @@ -552,15 +551,18 @@ QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree) menu->addSeparator()->setText(tr("Show / Hide Columns")); for (int col = 0; col < columnCount(); ++col) { + // Skip creating actions for columns that should not be hidden + if (!m_columnsHideable.at(col)) + continue; auto act = new QAction(menu); setupHeaderAction(act, col); act->setCheckable(true); act->setChecked(!tree->isColumnHidden(col)); - connect(act, &QAction::toggled, tree, [this, col, tree](bool toggled){ + connect(act, &QAction::toggled, tree, [this, col, tree](bool toggled) { tree->setColumnHidden(col, !toggled); - for(int c = 0; c < columnCount(); ++c) { + for (int c = 0; c < columnCount(); ++c) { if (m_column_resize_modes.at(c) == QHeaderView::ResizeToContents) tree->resizeColumnToContents(c); } @@ -568,7 +570,6 @@ QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree) }); menu->addAction(act); - } return menu; @@ -586,7 +587,8 @@ SortType ResourceFolderModel::columnToSortKey(size_t column) const } /* Standard Proxy Model for createFilterProxyModel */ -[[nodiscard]] bool ResourceFolderModel::ProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const +[[nodiscard]] bool ResourceFolderModel::ProxyModel::filterAcceptsRow(int source_row, + [[maybe_unused]] const QModelIndex& source_parent) const { auto* model = qobject_cast(sourceModel()); if (!model) diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 454b84c36..80c31e456 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -1,14 +1,14 @@ #pragma once -#include -#include -#include #include +#include #include #include +#include #include #include #include +#include #include "Resource.h" @@ -49,8 +49,8 @@ class ResourceFolderModel : public QAbstractListModel { bool stopWatching(const QStringList paths); /* Helper methods for subclasses, using a predetermined list of paths. */ - virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); }; - virtual bool stopWatching() { return stopWatching({ m_dir.absolutePath() }); }; + virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); } + virtual bool stopWatching() { return stopWatching({ m_dir.absolutePath() }); } /** Given a path in the system, install that resource, moving it to its place in the * instance file hierarchy. @@ -78,7 +78,7 @@ class ResourceFolderModel : public QAbstractListModel { /** Creates a new parse task, if needed, for 'res' and start it.*/ virtual void resolveResource(Resource* res); - [[nodiscard]] size_t size() const { return m_resources.size(); }; + [[nodiscard]] qsizetype size() const { return m_resources.size(); } [[nodiscard]] bool empty() const { return size() == 0; } [[nodiscard]] Resource& at(int index) { return *m_resources.at(index); } [[nodiscard]] Resource const& at(int index) const { return *m_resources.at(index); } @@ -97,10 +97,10 @@ class ResourceFolderModel : public QAbstractListModel { /* Basic columns */ enum Columns { ACTIVE_COLUMN = 0, NAME_COLUMN, DATE_COLUMN, NUM_COLUMNS }; - QStringList columnNames(bool translated = true) const { return translated ? m_column_names_translated : m_column_names; }; + QStringList columnNames(bool translated = true) const { return translated ? m_column_names_translated : m_column_names; } [[nodiscard]] int rowCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : static_cast(size()); } - [[nodiscard]] int columnCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : NUM_COLUMNS; }; + [[nodiscard]] int columnCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : NUM_COLUMNS; } [[nodiscard]] Qt::DropActions supportedDropActions() const override; @@ -120,7 +120,7 @@ class ResourceFolderModel : public QAbstractListModel { void saveHiddenColumn(int column, bool hidden); void loadHiddenColumns(QTreeView* tree); QMenu* createHeaderContextMenu(QTreeView* tree); - + /** This creates a proxy model to filter / sort the model for a UI. * * The actual comparisons and filtering are done directly by the Resource, so to modify behavior go there instead! @@ -159,7 +159,7 @@ class ResourceFolderModel : public QAbstractListModel { * This task should load and parse all heavy info needed by a resource, such as parsing a manifest. It gets executed * in the background, so it slowly updates the UI as tasks get done. */ - [[nodiscard]] virtual Task* createParseTask(Resource&) { return nullptr; }; + [[nodiscard]] virtual Task* createParseTask(Resource&) { return nullptr; } /** Standard implementation of the model update logic. * @@ -199,9 +199,11 @@ class ResourceFolderModel : public QAbstractListModel { // Represents the relationship between a column's index (represented by the list index), and it's sorting key. // As such, the order in with they appear is very important! QList m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE }; - QStringList m_column_names = {"Enable", "Name", "Last Modified"}; - QStringList m_column_names_translated = {tr("Enable"), tr("Name"), tr("Last Modified")}; - QList m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch, QHeaderView::ResizeToContents }; + QStringList m_column_names = { "Enable", "Name", "Last Modified" }; + QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified") }; + QList m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch, + QHeaderView::ResizeToContents }; + QList m_columnsHideable = { false, false, true }; QDir m_dir; BaseInstance* m_instance; @@ -223,15 +225,15 @@ class ResourceFolderModel : public QAbstractListModel { /* A macro to define useful functions to handle Resource* -> T* more easily on derived classes */ #define RESOURCE_HELPERS(T) \ - [[nodiscard]] T* operator[](size_t index) \ + [[nodiscard]] T* operator[](int index) \ { \ return static_cast(m_resources[index].get()); \ } \ - [[nodiscard]] T* at(size_t index) \ + [[nodiscard]] T* at(int index) \ { \ return static_cast(m_resources[index].get()); \ } \ - [[nodiscard]] const T* at(size_t index) const \ + [[nodiscard]] const T* at(int index) const \ { \ return static_cast(m_resources.at(index).get()); \ } \ diff --git a/launcher/minecraft/mod/ResourcePack.cpp b/launcher/minecraft/mod/ResourcePack.cpp index 6d5978d4d..dab0f6d67 100644 --- a/launcher/minecraft/mod/ResourcePack.cpp +++ b/launcher/minecraft/mod/ResourcePack.cpp @@ -50,7 +50,7 @@ void ResourcePack::setImage(QImage new_image) const PixmapCache::instance().remove(m_pack_image_cache_key.key); // scale the image to avoid flooding the pixmapcache - auto pixmap = QPixmap::fromImage(new_image.scaled({64, 64}, Qt::AspectRatioMode::KeepAspectRatioByExpanding)); + auto pixmap = QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding)); m_pack_image_cache_key.key = PixmapCache::instance().insert(pixmap); m_pack_image_cache_key.was_ever_used = true; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index 41455599b..f27431576 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * @@ -47,14 +47,14 @@ #include "minecraft/mod/tasks/BasicFolderLoadTask.h" #include "minecraft/mod/tasks/LocalResourcePackParseTask.h" -ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstance* instance) - : ResourceFolderModel(QDir(dir), instance) +ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) { m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE}; - m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; - + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE }; + m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, + QHeaderView::ResizeToContents }; + m_columnsHideable = { false, true, false, true, true }; } QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const @@ -93,7 +93,7 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const if (column == NameColumn && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink())) return APPLICATION->getThemedIcon("status-yellow"); if (column == ImageColumn) { - return at(row)->image({32, 32}, Qt::AspectRatioMode::KeepAspectRatioByExpanding); + return at(row)->image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); } return {}; } @@ -105,13 +105,14 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const if (column == NameColumn) { if (at(row)->isSymLinkUnder(instDirPath())) { return m_resources[row]->internal_id() + - tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original." - "\nCanonical Path: %1") - .arg(at(row)->fileinfo().canonicalFilePath());; + tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original." + "\nCanonical Path: %1") + .arg(at(row)->fileinfo().canonicalFilePath()); + ; } if (at(row)->isMoreThanOneHardLink()) { return m_resources[row]->internal_id() + - tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original."); + tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original."); } } return m_resources[row]->internal_id(); @@ -128,7 +129,7 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const } } -QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orientation, int role) const +QVariant ResourcePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const { switch (role) { case Qt::DisplayRole: @@ -159,13 +160,12 @@ QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orient } case Qt::SizeHintRole: if (section == ImageColumn) { - return QSize(64,0); + return QSize(64, 0); } return {}; default: return {}; } - return {}; } int ResourcePackFolderModel::columnCount(const QModelIndex& parent) const diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.h b/launcher/minecraft/mod/ResourcePackFolderModel.h index 531d81928..29c2c5995 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.h +++ b/launcher/minecraft/mod/ResourcePackFolderModel.h @@ -4,28 +4,19 @@ #include "ResourcePack.h" -class ResourcePackFolderModel : public ResourceFolderModel -{ +class ResourcePackFolderModel : public ResourceFolderModel { Q_OBJECT -public: - enum Columns - { - ActiveColumn = 0, - ImageColumn, - NameColumn, - PackFormatColumn, - DateColumn, - NUM_COLUMNS - }; + public: + enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, PackFormatColumn, DateColumn, NUM_COLUMNS }; - explicit ResourcePackFolderModel(const QString &dir, BaseInstance* instance); + explicit ResourcePackFolderModel(const QString& dir, BaseInstance* instance); virtual QString id() const override { return "resourcepacks"; } - [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + [[nodiscard]] QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - [[nodiscard]] int columnCount(const QModelIndex &parent) const override; + [[nodiscard]] int columnCount(const QModelIndex& parent) const override; [[nodiscard]] Task* createUpdateTask() override; [[nodiscard]] Task* createParseTask(Resource&) override; diff --git a/launcher/minecraft/mod/ShaderPackFolderModel.h b/launcher/minecraft/mod/ShaderPackFolderModel.h index f8249962f..44ed37a47 100644 --- a/launcher/minecraft/mod/ShaderPackFolderModel.h +++ b/launcher/minecraft/mod/ShaderPackFolderModel.h @@ -6,9 +6,7 @@ class ShaderPackFolderModel : public ResourceFolderModel { Q_OBJECT public: - explicit ShaderPackFolderModel(const QString& dir, BaseInstance* instance) - : ResourceFolderModel(QDir(dir), instance) - {} - + explicit ShaderPackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) {} + virtual QString id() const override { return "shaderpacks"; } }; diff --git a/launcher/minecraft/mod/TexturePack.cpp b/launcher/minecraft/mod/TexturePack.cpp index c7a50a97a..7d8c67137 100644 --- a/launcher/minecraft/mod/TexturePack.cpp +++ b/launcher/minecraft/mod/TexturePack.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * @@ -44,7 +44,7 @@ void TexturePack::setImage(QImage new_image) const PixmapCache::remove(m_pack_image_cache_key.key); // scale the image to avoid flooding the pixmapcache - auto pixmap = QPixmap::fromImage(new_image.scaled({64, 64}, Qt::AspectRatioMode::KeepAspectRatioByExpanding)); + auto pixmap = QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding)); m_pack_image_cache_key.key = PixmapCache::insert(pixmap); m_pack_image_cache_key.was_ever_used = true; diff --git a/launcher/minecraft/mod/TexturePack.h b/launcher/minecraft/mod/TexturePack.h index 577005655..bf4b5b6b4 100644 --- a/launcher/minecraft/mod/TexturePack.h +++ b/launcher/minecraft/mod/TexturePack.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index 531a70232..5c5f2b7c1 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * @@ -42,14 +42,14 @@ #include "minecraft/mod/tasks/BasicFolderLoadTask.h" #include "minecraft/mod/tasks/LocalTexturePackParseTask.h" -TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* instance) - : ResourceFolderModel(QDir(dir), instance) +TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) { m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") }); m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE }; - m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents}; - + m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, + QHeaderView::ResizeToContents }; + m_columnsHideable = { false, true, false, true }; } Task* TexturePackFolderModel::createUpdateTask() @@ -62,7 +62,6 @@ Task* TexturePackFolderModel::createParseTask(Resource& resource) return new LocalTexturePackParseTask(m_next_resolution_ticket, static_cast(resource)); } - QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const { if (!validateIndex(index)) @@ -85,28 +84,29 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const if (column == NameColumn) { if (at(row)->isSymLinkUnder(instDirPath())) { return m_resources[row]->internal_id() + - tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original." - "\nCanonical Path: %1") - .arg(at(row)->fileinfo().canonicalFilePath());; + tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original." + "\nCanonical Path: %1") + .arg(at(row)->fileinfo().canonicalFilePath()); + ; } if (at(row)->isMoreThanOneHardLink()) { return m_resources[row]->internal_id() + - tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original."); + tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original."); } } - + return m_resources[row]->internal_id(); case Qt::DecorationRole: { if (column == NameColumn && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink())) return APPLICATION->getThemedIcon("status-yellow"); if (column == ImageColumn) { - return at(row)->image({32, 32}, Qt::AspectRatioMode::KeepAspectRatioByExpanding); + return at(row)->image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); } return {}; } case Qt::CheckStateRole: if (column == ActiveColumn) { - return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked; + return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked; } return {}; default: @@ -114,7 +114,7 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const } } -QVariant TexturePackFolderModel::headerData(int section, Qt::Orientation orientation, int role) const +QVariant TexturePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const { switch (role) { case Qt::DisplayRole: @@ -153,4 +153,3 @@ int TexturePackFolderModel::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : NUM_COLUMNS; } - diff --git a/launcher/minecraft/mod/TexturePackFolderModel.h b/launcher/minecraft/mod/TexturePackFolderModel.h index 71a8bdd16..b975d8641 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.h +++ b/launcher/minecraft/mod/TexturePackFolderModel.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * @@ -40,31 +40,22 @@ #include "TexturePack.h" -class TexturePackFolderModel : public ResourceFolderModel -{ +class TexturePackFolderModel : public ResourceFolderModel { Q_OBJECT -public: + public: + enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, DateColumn, NUM_COLUMNS }; - enum Columns - { - ActiveColumn = 0, - ImageColumn, - NameColumn, - DateColumn, - NUM_COLUMNS - }; - - explicit TexturePackFolderModel(const QString &dir, std::shared_ptr instance); + explicit TexturePackFolderModel(const QString& dir, std::shared_ptr instance); virtual QString id() const override { return "texturepacks"; } - [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + [[nodiscard]] QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - [[nodiscard]] int columnCount(const QModelIndex &parent) const override; + [[nodiscard]] int columnCount(const QModelIndex& parent) const override; - explicit TexturePackFolderModel(const QString &dir, BaseInstance* instance); + explicit TexturePackFolderModel(const QString& dir, BaseInstance* instance); [[nodiscard]] Task* createUpdateTask() override; [[nodiscard]] Task* createParseTask(Resource&) override; diff --git a/launcher/minecraft/mod/tasks/BasicFolderLoadTask.h b/launcher/minecraft/mod/tasks/BasicFolderLoadTask.h index 3ee7e2e0f..23a2b649a 100644 --- a/launcher/minecraft/mod/tasks/BasicFolderLoadTask.h +++ b/launcher/minecraft/mod/tasks/BasicFolderLoadTask.h @@ -26,12 +26,14 @@ class BasicFolderLoadTask : public Task { public: BasicFolderLoadTask(QDir dir) : Task(nullptr, false), m_dir(dir), m_result(new Result), m_thread_to_spawn_into(thread()) { - m_create_func = [](QFileInfo const& entry) -> Resource::Ptr { - return makeShared(entry); - }; + m_create_func = [](QFileInfo const& entry) -> Resource::Ptr { return makeShared(entry); }; } BasicFolderLoadTask(QDir dir, std::function create_function) - : Task(nullptr, false), m_dir(dir), m_result(new Result), m_create_func(std::move(create_function)), m_thread_to_spawn_into(thread()) + : Task(nullptr, false) + , m_dir(dir) + , m_result(new Result) + , m_create_func(std::move(create_function)) + , m_thread_to_spawn_into(thread()) {} [[nodiscard]] bool canAbort() const override { return true; } @@ -59,7 +61,7 @@ class BasicFolderLoadTask : public Task { emitSucceeded(); } -private: + private: QDir m_dir; ResultPtr m_result; diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index f8ecdb33e..0a0f57bf3 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -61,7 +61,7 @@ GetModDependenciesTask::GetModDependenciesTask(QObject* parent, if (auto meta = mod->metadata(); meta) m_mods.append(meta); prepare(); -}; +} void GetModDependenciesTask::prepare() { @@ -130,7 +130,7 @@ QList GetModDependenciesTask::getDependenciesForVersion c_dependencies.append(getOverride(ver_dep, providerName)); } return c_dependencies; -}; +} Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr pDep) { @@ -181,7 +181,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen ResourceAPI::DependencySearchArgs args = { dep, m_version, m_loaderType }; ResourceAPI::DependencySearchCallbacks callbacks; - callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, auto& pack) { + callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, [[maybe_unused]] auto& pack) { try { QJsonArray arr; if (dep.version.length() != 0 && doc.isObject()) { @@ -215,27 +215,27 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen return; } if (level == 0) { - qWarning() << "Dependency cycle exeeded"; + qWarning() << "Dependency cycle exceeded"; return; } if (dep.addonId.toString().isEmpty() && !pDep->version.addonId.toString().isEmpty()) { pDep->pack->addonId = pDep->version.addonId; - auto dep = getOverride({ pDep->version.addonId, pDep->dependency.type }, provider.name); - if (dep.addonId != pDep->version.addonId) { + auto dep_ = getOverride({ pDep->version.addonId, pDep->dependency.type }, provider.name); + if (dep_.addonId != pDep->version.addonId) { removePack(pDep->version.addonId); - addTask(prepareDependencyTask(dep, provider.name, level)); + addTask(prepareDependencyTask(dep_, provider.name, level)); } else addTask(getProjectInfoTask(pDep)); } - for (auto dep : getDependenciesForVersion(pDep->version, provider.name)) { - addTask(prepareDependencyTask(dep, provider.name, level - 1)); + for (auto dep_ : getDependenciesForVersion(pDep->version, provider.name)) { + addTask(prepareDependencyTask(dep_, provider.name, level - 1)); } }; auto version = provider.api->getDependencyVersion(std::move(args), std::move(callbacks)); tasks->addTask(version); return tasks; -}; +} void GetModDependenciesTask::removePack(const QVariant addonId) { diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp index 264019f84..7ec00c0c6 100644 --- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp @@ -1,9 +1,9 @@ #include "LocalModParseTask.h" +#include #include #include #include -#include #include #include #include @@ -104,14 +104,15 @@ ModDetails ReadMCModTOML(QByteArray contents) #if TOML_EXCEPTIONS try { tomlData = toml::parse(contents.toStdString()); - } catch (const toml::parse_error& err) { + } catch ([[maybe_unused]] const toml::parse_error& err) { return {}; } #else - tomlData = toml::parse(contents.toStdString()); - if (!tomlData) { + toml::parse_result result = toml::parse(contents.toStdString()); + if (!result) { return {}; } + tomlData = result.table(); #endif // array defined by [[mods]] @@ -151,8 +152,8 @@ ModDetails ReadMCModTOML(QByteArray contents) QString authors = ""; if (auto authorsDatum = tomlData["authors"].as_string()) { authors = QString::fromStdString(authorsDatum->get()); - } else if (auto authorsDatum = (*modsTable)["authors"].as_string()) { - authors = QString::fromStdString(authorsDatum->get()); + } else if (auto authorsDatumMods = (*modsTable)["authors"].as_string()) { + authors = QString::fromStdString(authorsDatumMods->get()); } if (!authors.isEmpty()) { details.authors.append(authors); @@ -161,8 +162,8 @@ ModDetails ReadMCModTOML(QByteArray contents) QString homeurl = ""; if (auto homeurlDatum = tomlData["displayURL"].as_string()) { homeurl = QString::fromStdString(homeurlDatum->get()); - } else if (auto homeurlDatum = (*modsTable)["displayURL"].as_string()) { - homeurl = QString::fromStdString(homeurlDatum->get()); + } else if (auto homeurlDatumMods = (*modsTable)["displayURL"].as_string()) { + homeurl = QString::fromStdString(homeurlDatumMods->get()); } // fix up url. if (!homeurl.isEmpty() && !homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://")) { @@ -173,16 +174,16 @@ ModDetails ReadMCModTOML(QByteArray contents) QString issueTrackerURL = ""; if (auto issueTrackerURLDatum = tomlData["issueTrackerURL"].as_string()) { issueTrackerURL = QString::fromStdString(issueTrackerURLDatum->get()); - } else if (auto issueTrackerURLDatum = (*modsTable)["issueTrackerURL"].as_string()) { - issueTrackerURL = QString::fromStdString(issueTrackerURLDatum->get()); + } else if (auto issueTrackerURLDatumMods = (*modsTable)["issueTrackerURL"].as_string()) { + issueTrackerURL = QString::fromStdString(issueTrackerURLDatumMods->get()); } details.issue_tracker = issueTrackerURL; QString license = ""; if (auto licenseDatum = tomlData["license"].as_string()) { license = QString::fromStdString(licenseDatum->get()); - } else if (auto licenseDatum =(*modsTable)["license"].as_string()) { - license = QString::fromStdString(licenseDatum->get()); + } else if (auto licenseDatumMods = (*modsTable)["license"].as_string()) { + license = QString::fromStdString(licenseDatumMods->get()); } if (!license.isEmpty()) details.licenses.append(ModLicense(license)); @@ -190,8 +191,8 @@ ModDetails ReadMCModTOML(QByteArray contents) QString logoFile = ""; if (auto logoFileDatum = tomlData["logoFile"].as_string()) { logoFile = QString::fromStdString(logoFileDatum->get()); - } else if (auto logoFileDatum =(*modsTable)["logoFile"].as_string()) { - logoFile = QString::fromStdString(logoFileDatum->get()); + } else if (auto logoFileDatumMods = (*modsTable)["logoFile"].as_string()) { + logoFile = QString::fromStdString(logoFileDatumMods->get()); } details.icon_file = logoFile; @@ -271,7 +272,7 @@ ModDetails ReadFabricModInfo(QByteArray contents) if (largest > 0) { auto key = QString::number(largest) + "x" + QString::number(largest); details.icon_file = obj.value(key).toString(); - } else { // parsing the sizes failed + } else { // parsing the sizes failed // take the first for (auto i : obj) { details.icon_file = i.toString(); @@ -358,7 +359,7 @@ ModDetails ReadQuiltModInfo(QByteArray contents) if (largest > 0) { auto key = QString::number(largest) + "x" + QString::number(largest); details.icon_file = obj.value(key).toString(); - } else { // parsing the sizes failed + } else { // parsing the sizes failed // take the first for (auto i : obj) { details.icon_file = i.toString(); @@ -369,12 +370,11 @@ ModDetails ReadQuiltModInfo(QByteArray contents) details.icon_file = icon.toString(); } } - } return details; } -ModDetails ReadForgeInfo(QString fileName) +ModDetails ReadForgeInfo(QByteArray contents) { ModDetails details; // Read the data @@ -382,7 +382,7 @@ ModDetails ReadForgeInfo(QString fileName) details.mod_id = "Forge"; details.homeurl = "http://www.minecraftforge.net/forum/"; INIFile ini; - if (!ini.loadFile(fileName)) + if (!ini.loadFile(contents)) return details; QString major = ini.get("forge.major.number", "0").toString(); @@ -459,7 +459,7 @@ bool process(Mod& mod, ProcessingLevel level) } } -bool processZIP(Mod& mod, ProcessingLevel level) +bool processZIP(Mod& mod, [[maybe_unused]] ProcessingLevel level) { ModDetails details; @@ -554,7 +554,7 @@ bool processZIP(Mod& mod, ProcessingLevel level) return false; } - details = ReadForgeInfo(file.getFileName()); + details = ReadForgeInfo(file.readAll()); file.close(); zip.close(); @@ -592,7 +592,7 @@ bool processZIP(Mod& mod, ProcessingLevel level) return false; // no valid mod found in archive } -bool processFolder(Mod& mod, ProcessingLevel level) +bool processFolder(Mod& mod, [[maybe_unused]] ProcessingLevel level) { ModDetails details; @@ -613,7 +613,7 @@ bool processFolder(Mod& mod, ProcessingLevel level) return false; // no valid mcmod.info file found } -bool processLitemod(Mod& mod, ProcessingLevel level) +bool processLitemod(Mod& mod, [[maybe_unused]] ProcessingLevel level) { ModDetails details; @@ -659,7 +659,8 @@ bool processIconPNG(const Mod& mod, QByteArray&& raw_data) return true; } -bool loadIconFile(const Mod& mod) { +bool loadIconFile(const Mod& mod) +{ if (mod.iconPath().isEmpty()) { qWarning() << "No Iconfile set, be sure to parse the mod first"; return false; @@ -671,15 +672,14 @@ bool loadIconFile(const Mod& mod) { }; switch (mod.type()) { - case ResourceType::FOLDER: - { + case ResourceType::FOLDER: { QFileInfo icon_info(FS::PathCombine(mod.fileinfo().filePath(), mod.iconPath())); if (icon_info.exists() && icon_info.isFile()) { QFile icon(icon_info.filePath()); if (!icon.open(QIODevice::ReadOnly)) return false; auto data = icon.readAll(); - + bool icon_result = ModUtils::processIconPNG(mod, std::move(data)); icon.close(); @@ -689,8 +689,7 @@ bool loadIconFile(const Mod& mod) { } } } - case ResourceType::ZIPFILE: - { + case ResourceType::ZIPFILE: { QuaZip zip(mod.fileinfo().filePath()); if (!zip.open(QuaZip::mdUnzip)) return false; @@ -716,9 +715,8 @@ bool loadIconFile(const Mod& mod) { return png_invalid(); // could not set icon as current file. } } - case ResourceType::LITEMOD: - { - return false; // can lightmods even have icons? + case ResourceType::LITEMOD: { + return false; // can lightmods even have icons? } default: qWarning() << "Invalid type for mod, can not load icon."; diff --git a/launcher/minecraft/mod/tasks/LocalModUpdateTask.h b/launcher/minecraft/mod/tasks/LocalModUpdateTask.h index 1d2f06a6f..080999294 100644 --- a/launcher/minecraft/mod/tasks/LocalModUpdateTask.h +++ b/launcher/minecraft/mod/tasks/LocalModUpdateTask.h @@ -1,20 +1,20 @@ // SPDX-License-Identifier: GPL-3.0-only /* -* PolyMC - Minecraft Launcher -* Copyright (c) 2022 flowln -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, version 3. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #pragma once diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index a67c56a8f..73cbf891c 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify @@ -207,15 +207,14 @@ bool processPackPNG(const ResourcePack& pack, QByteArray&& raw_data) } bool processPackPNG(const ResourcePack& pack) -{ +{ auto png_invalid = [&pack]() { qWarning() << "Resource pack at" << pack.fileinfo().filePath() << "does not have a valid pack.png"; return false; }; switch (pack.type()) { - case ResourceType::FOLDER: - { + case ResourceType::FOLDER: { QFileInfo image_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.png")); if (image_file_info.exists() && image_file_info.isFile()) { QFile pack_png_file(image_file_info.filePath()); @@ -234,8 +233,7 @@ bool processPackPNG(const ResourcePack& pack) return png_invalid(); // pack.png does not exists or is not a valid file. } } - case ResourceType::ZIPFILE: - { + case ResourceType::ZIPFILE: { Q_ASSERT(pack.type() == ResourceType::ZIPFILE); QuaZip zip(pack.fileinfo().filePath()); diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h index 58d90b3b9..5199bf3f0 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/minecraft/mod/tasks/LocalResourceParse.cpp b/launcher/minecraft/mod/tasks/LocalResourceParse.cpp index 0894049cd..d5a090832 100644 --- a/launcher/minecraft/mod/tasks/LocalResourceParse.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourceParse.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -#include +#include #include "LocalResourceParse.h" @@ -30,19 +30,17 @@ #include "LocalTexturePackParseTask.h" #include "LocalWorldSaveParseTask.h" - -static const QMap s_packed_type_names = { - {PackedResourceType::ResourcePack, QObject::tr("resource pack")}, - {PackedResourceType::TexturePack, QObject::tr("texture pack")}, - {PackedResourceType::DataPack, QObject::tr("data pack")}, - {PackedResourceType::ShaderPack, QObject::tr("shader pack")}, - {PackedResourceType::WorldSave, QObject::tr("world save")}, - {PackedResourceType::Mod , QObject::tr("mod")}, - {PackedResourceType::UNKNOWN, QObject::tr("unknown")} -}; +static const QMap s_packed_type_names = { { PackedResourceType::ResourcePack, QObject::tr("resource pack") }, + { PackedResourceType::TexturePack, QObject::tr("texture pack") }, + { PackedResourceType::DataPack, QObject::tr("data pack") }, + { PackedResourceType::ShaderPack, QObject::tr("shader pack") }, + { PackedResourceType::WorldSave, QObject::tr("world save") }, + { PackedResourceType::Mod, QObject::tr("mod") }, + { PackedResourceType::UNKNOWN, QObject::tr("unknown") } }; namespace ResourceUtils { -PackedResourceType identify(QFileInfo file){ +PackedResourceType identify(QFileInfo file) +{ if (file.exists() && file.isFile()) { if (ModUtils::validate(file)) { // mods can contain resource and data packs so they must be tested first @@ -64,7 +62,7 @@ PackedResourceType identify(QFileInfo file){ qDebug() << file.fileName() << "is a shader pack"; return PackedResourceType::ShaderPack; } else { - qDebug() << "Can't Identify" << file.fileName() ; + qDebug() << "Can't Identify" << file.fileName(); } } else { qDebug() << "Can't find" << file.absolutePath(); @@ -72,8 +70,9 @@ PackedResourceType identify(QFileInfo file){ return PackedResourceType::UNKNOWN; } -QString getPackedTypeName(PackedResourceType type) { +QString getPackedTypeName(PackedResourceType type) +{ return s_packed_type_names.constFind(type).value(); } -} +} // namespace ResourceUtils diff --git a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp index a72e81150..887a1062e 100644 --- a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * @@ -161,15 +161,14 @@ bool processPackPNG(const TexturePack& pack, QByteArray&& raw_data) } bool processPackPNG(const TexturePack& pack) -{ +{ auto png_invalid = [&pack]() { qWarning() << "Texture pack at" << pack.fileinfo().filePath() << "does not have a valid pack.png"; return false; }; switch (pack.type()) { - case ResourceType::FOLDER: - { + case ResourceType::FOLDER: { QFileInfo image_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.png")); if (image_file_info.exists() && image_file_info.isFile()) { QFile pack_png_file(image_file_info.filePath()); @@ -188,8 +187,7 @@ bool processPackPNG(const TexturePack& pack) return png_invalid(); // pack.png does not exists or is not a valid file. } } - case ResourceType::ZIPFILE: - { + case ResourceType::ZIPFILE: { Q_ASSERT(pack.type() == ResourceType::ZIPFILE); QuaZip zip(pack.fileinfo().filePath()); @@ -232,8 +230,7 @@ bool validate(QFileInfo file) } // namespace TexturePackUtils -LocalTexturePackParseTask::LocalTexturePackParseTask(int token, TexturePack& rp) - : Task(nullptr, false), m_token(token), m_texture_pack(rp) +LocalTexturePackParseTask::LocalTexturePackParseTask(int token, TexturePack& rp) : Task(nullptr, false), m_token(token), m_texture_pack(rp) {} bool LocalTexturePackParseTask::abort() diff --git a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.h b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.h index 6b91565ad..1341590f2 100644 --- a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * diff --git a/launcher/minecraft/mod/tasks/LocalWorldSaveParseTask.cpp b/launcher/minecraft/mod/tasks/LocalWorldSaveParseTask.cpp index cbc8f8cee..9d564ddb3 100644 --- a/launcher/minecraft/mod/tasks/LocalWorldSaveParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalWorldSaveParseTask.cpp @@ -50,8 +50,8 @@ bool process(WorldSave& pack, ProcessingLevel level) /// @param dir the path to check /// @param saves used in recursive call if a "saves" dir was found /// @return std::tuple of ( -/// bool , -/// QString , +/// bool , +/// QString , /// bool /// ) static std::tuple contains_level_dat(QDir dir, bool saves = false) @@ -101,8 +101,8 @@ bool processFolder(WorldSave& save, ProcessingLevel level) /// @brief checks a folder structure to see if it contains a level.dat /// @param zip the zip file to check /// @return std::tuple of ( -/// bool , -/// QString , +/// bool , +/// QString , /// bool /// ) static std::tuple contains_level_dat(QuaZip& zip) diff --git a/launcher/minecraft/mod/tasks/LocalWorldSaveParseTask.h b/launcher/minecraft/mod/tasks/LocalWorldSaveParseTask.h index 9dcdca2b9..12f677b02 100644 --- a/launcher/minecraft/mod/tasks/LocalWorldSaveParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalWorldSaveParseTask.h @@ -39,7 +39,7 @@ bool processFolder(WorldSave& pack, ProcessingLevel level = ProcessingLevel::Ful bool validate(QFileInfo file); -} // namespace WorldSaveUtils +} // namespace WorldSaveUtils class LocalWorldSaveParseTask : public Task { Q_OBJECT diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp index ef353c701..9f79ba098 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp @@ -1,38 +1,38 @@ // SPDX-License-Identifier: GPL-3.0-only /* -* PolyMC - Minecraft Launcher -* Copyright (c) 2022 flowln -* Copyright (C) 2022 Sefa Eyeoglu -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, version 3. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* This file incorporates work covered by the following copyright and -* permission notice: -* -* Copyright 2013-2021 MultiMC Contributors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ModFolderLoadTask.h" @@ -70,13 +70,11 @@ void ModFolderLoadTask::executeTask() m_result->mods[mod->internal_id()]->setStatus(ModStatus::Installed); // Delete the object we just created, since a valid one is already in the mods list. delete mod; - } - else { + } else { m_result->mods[mod->internal_id()].reset(std::move(mod)); m_result->mods[mod->internal_id()]->setStatus(ModStatus::NoMetadata); } - } - else { + } else { QString chopped_id = mod->internal_id().chopped(9); if (m_result->mods.contains(chopped_id)) { m_result->mods[mod->internal_id()].reset(std::move(mod)); @@ -88,8 +86,7 @@ void ModFolderLoadTask::executeTask() m_result->mods[mod->internal_id()]->setStatus(ModStatus::Installed); m_result->mods.remove(chopped_id); } - } - else { + } else { m_result->mods[mod->internal_id()].reset(std::move(mod)); m_result->mods[mod->internal_id()]->setStatus(ModStatus::NoMetadata); } @@ -124,7 +121,7 @@ void ModFolderLoadTask::getFromMetadata() for (auto entry : m_index_dir.entryList(QDir::Files)) { auto metadata = Metadata::get(m_index_dir, entry); - if(!metadata.isValid()){ + if (!metadata.isValid()) { return; } diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.h b/launcher/minecraft/mod/tasks/ModFolderLoadTask.h index af5f58a5e..4200ef6d9 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.h +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.h @@ -1,38 +1,38 @@ // SPDX-License-Identifier: GPL-3.0-only /* -* PolyMC - Minecraft Launcher -* Copyright (c) 2022 flowln -* Copyright (C) 2022 Sefa Eyeoglu -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, version 3. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* This file incorporates work covered by the following copyright and -* permission notice: -* -* Copyright 2013-2021 MultiMC Contributors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once @@ -44,19 +44,16 @@ #include "minecraft/mod/Mod.h" #include "tasks/Task.h" -class ModFolderLoadTask : public Task -{ +class ModFolderLoadTask : public Task { Q_OBJECT -public: + public: struct Result { QMap mods; }; using ResultPtr = std::shared_ptr; - ResultPtr result() const { - return m_result; - } + ResultPtr result() const { return m_result; } -public: + public: ModFolderLoadTask(QDir mods_dir, QDir index_dir, bool is_indexed, bool clean_orphan = false); [[nodiscard]] bool canAbort() const override { return true; } @@ -66,13 +63,12 @@ public: return true; } - void executeTask() override; -private: + private: void getFromMetadata(); -private: + private: QDir m_mods_dir, m_index_dir; bool m_is_indexed; bool m_clean_orphan; diff --git a/launcher/minecraft/services/CapeChange.cpp b/launcher/minecraft/services/CapeChange.cpp index 1d5ea36da..2ba38a6af 100644 --- a/launcher/minecraft/services/CapeChange.cpp +++ b/launcher/minecraft/services/CapeChange.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,27 +35,25 @@ #include "CapeChange.h" -#include #include +#include #include "Application.h" -CapeChange::CapeChange(QObject *parent, QString token, QString cape) - : Task(parent), m_capeId(cape), m_token(token) -{ -} +CapeChange::CapeChange(QObject* parent, QString token, QString cape) : Task(parent), m_capeId(cape), m_token(token) {} -void CapeChange::setCape(QString& cape) { +void CapeChange::setCape([[maybe_unused]] QString& cape) +{ QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active")); auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId); request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); - QNetworkReply *rep = APPLICATION->network()->put(request, requestString.toUtf8()); + QNetworkReply* rep = APPLICATION->network()->put(request, requestString.toUtf8()); setStatus(tr("Equipping cape")); m_reply = shared_qobject_ptr(rep); connect(rep, &QNetworkReply::uploadProgress, this, &CapeChange::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 connect(rep, &QNetworkReply::errorOccurred, this, &CapeChange::downloadError); #else connect(rep, QOverload::of(&QNetworkReply::error), this, &CapeChange::downloadError); @@ -64,17 +62,18 @@ void CapeChange::setCape(QString& cape) { connect(rep, &QNetworkReply::finished, this, &CapeChange::downloadFinished); } -void CapeChange::clearCape() { +void CapeChange::clearCape() +{ QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active")); auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId); request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); - QNetworkReply *rep = APPLICATION->network()->deleteResource(request); + QNetworkReply* rep = APPLICATION->network()->deleteResource(request); setStatus(tr("Removing cape")); m_reply = shared_qobject_ptr(rep); connect(rep, &QNetworkReply::uploadProgress, this, &CapeChange::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 connect(rep, &QNetworkReply::errorOccurred, this, &CapeChange::downloadError); #else connect(rep, QOverload::of(&QNetworkReply::error), this, &CapeChange::downloadError); @@ -83,13 +82,11 @@ void CapeChange::clearCape() { connect(rep, &QNetworkReply::finished, this, &CapeChange::downloadFinished); } - void CapeChange::executeTask() { - if(m_capeId.isEmpty()) { + if (m_capeId.isEmpty()) { clearCape(); - } - else { + } else { setCape(m_capeId); } } @@ -115,8 +112,7 @@ void CapeChange::sslErrors(const QList& errors) void CapeChange::downloadFinished() { // if the download failed - if (m_reply->error() != QNetworkReply::NetworkError::NoError) - { + if (m_reply->error() != QNetworkReply::NetworkError::NoError) { emitFailed(QString("Network error: %1").arg(m_reply->errorString())); m_reply.reset(); return; diff --git a/launcher/minecraft/services/CapeChange.h b/launcher/minecraft/services/CapeChange.h index 38069f90a..d0c893c44 100644 --- a/launcher/minecraft/services/CapeChange.h +++ b/launcher/minecraft/services/CapeChange.h @@ -3,31 +3,29 @@ #include #include #include -#include "tasks/Task.h" #include "QObjectPtr.h" +#include "tasks/Task.h" -class CapeChange : public Task -{ +class CapeChange : public Task { Q_OBJECT -public: - CapeChange(QObject *parent, QString token, QString capeId); + public: + CapeChange(QObject* parent, QString token, QString capeId); virtual ~CapeChange() {} -private: - void setCape(QString & cape); + private: + void setCape(QString& cape); void clearCape(); -private: + private: QString m_capeId; QString m_token; shared_qobject_ptr m_reply; -protected: + protected: virtual void executeTask(); -public slots: + public slots: void downloadError(QNetworkReply::NetworkError); void sslErrors(const QList& errors); void downloadFinished(); }; - diff --git a/launcher/minecraft/services/SkinDelete.cpp b/launcher/minecraft/services/SkinDelete.cpp index fbaaeacb6..9e9020692 100644 --- a/launcher/minecraft/services/SkinDelete.cpp +++ b/launcher/minecraft/services/SkinDelete.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,26 +35,23 @@ #include "SkinDelete.h" -#include #include +#include #include "Application.h" -SkinDelete::SkinDelete(QObject *parent, QString token) - : Task(parent), m_token(token) -{ -} +SkinDelete::SkinDelete(QObject* parent, QString token) : Task(parent), m_token(token) {} void SkinDelete::executeTask() { QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/skins/active")); request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); - QNetworkReply *rep = APPLICATION->network()->deleteResource(request); + QNetworkReply* rep = APPLICATION->network()->deleteResource(request); m_reply = shared_qobject_ptr(rep); setStatus(tr("Deleting skin")); connect(rep, &QNetworkReply::uploadProgress, this, &SkinDelete::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 connect(rep, &QNetworkReply::errorOccurred, this, &SkinDelete::downloadError); #else connect(rep, QOverload::of(&QNetworkReply::error), this, &SkinDelete::downloadError); @@ -84,12 +81,10 @@ void SkinDelete::sslErrors(const QList& errors) void SkinDelete::downloadFinished() { // if the download failed - if (m_reply->error() != QNetworkReply::NetworkError::NoError) - { + if (m_reply->error() != QNetworkReply::NetworkError::NoError) { emitFailed(QString("Network error: %1").arg(m_reply->errorString())); m_reply.reset(); return; } emitSucceeded(); } - diff --git a/launcher/minecraft/services/SkinDelete.h b/launcher/minecraft/services/SkinDelete.h index b9a1c9d3f..d5b2e63db 100644 --- a/launcher/minecraft/services/SkinDelete.h +++ b/launcher/minecraft/services/SkinDelete.h @@ -6,21 +6,20 @@ typedef shared_qobject_ptr SkinDeletePtr; -class SkinDelete : public Task -{ +class SkinDelete : public Task { Q_OBJECT -public: - SkinDelete(QObject *parent, QString token); + public: + SkinDelete(QObject* parent, QString token); virtual ~SkinDelete() = default; -private: + private: QString m_token; shared_qobject_ptr m_reply; -protected: + protected: virtual void executeTask(); -public slots: + public slots: void downloadError(QNetworkReply::NetworkError); void sslErrors(const QList& errors); void downloadFinished(); diff --git a/launcher/minecraft/services/SkinUpload.cpp b/launcher/minecraft/services/SkinUpload.cpp index 711f87392..163b481b1 100644 --- a/launcher/minecraft/services/SkinUpload.cpp +++ b/launcher/minecraft/services/SkinUpload.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,12 +35,13 @@ #include "SkinUpload.h" -#include #include +#include #include "Application.h" -QByteArray getVariant(SkinUpload::Model model) { +QByteArray getVariant(SkinUpload::Model model) +{ switch (model) { default: qDebug() << "Unknown skin type!"; @@ -51,16 +52,15 @@ QByteArray getVariant(SkinUpload::Model model) { } } -SkinUpload::SkinUpload(QObject *parent, QString token, QByteArray skin, SkinUpload::Model model) +SkinUpload::SkinUpload(QObject* parent, QString token, QByteArray skin, SkinUpload::Model model) : Task(parent), m_model(model), m_skin(skin), m_token(token) -{ -} +{} void SkinUpload::executeTask() { QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/skins")); request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); - QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpPart skin; skin.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png")); @@ -74,12 +74,12 @@ void SkinUpload::executeTask() multiPart->append(skin); multiPart->append(model); - QNetworkReply *rep = APPLICATION->network()->post(request, multiPart); + QNetworkReply* rep = APPLICATION->network()->post(request, multiPart); m_reply = shared_qobject_ptr(rep); setStatus(tr("Uploading skin")); connect(rep, &QNetworkReply::uploadProgress, this, &SkinUpload::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 connect(rep, &QNetworkReply::errorOccurred, this, &SkinUpload::downloadError); #else connect(rep, QOverload::of(&QNetworkReply::error), this, &SkinUpload::downloadError); @@ -109,8 +109,7 @@ void SkinUpload::sslErrors(const QList& errors) void SkinUpload::downloadFinished() { // if the download failed - if (m_reply->error() != QNetworkReply::NetworkError::NoError) - { + if (m_reply->error() != QNetworkReply::NetworkError::NoError) { emitFailed(QString("Network error: %1").arg(m_reply->errorString())); m_reply.reset(); return; diff --git a/launcher/minecraft/services/SkinUpload.h b/launcher/minecraft/services/SkinUpload.h index ac8c5b361..5716aa996 100644 --- a/launcher/minecraft/services/SkinUpload.h +++ b/launcher/minecraft/services/SkinUpload.h @@ -7,29 +7,25 @@ typedef shared_qobject_ptr SkinUploadPtr; -class SkinUpload : public Task -{ +class SkinUpload : public Task { Q_OBJECT -public: - enum Model - { - STEVE, - ALEX - }; + public: + enum Model { STEVE, ALEX }; // Note this class takes ownership of the file. - SkinUpload(QObject *parent, QString token, QByteArray skin, Model model = STEVE); + SkinUpload(QObject* parent, QString token, QByteArray skin, Model model = STEVE); virtual ~SkinUpload() {} -private: + private: Model m_model; QByteArray m_skin; QString m_token; shared_qobject_ptr m_reply; -protected: + + protected: virtual void executeTask(); -public slots: + public slots: void downloadError(QNetworkReply::NetworkError); void sslErrors(const QList& errors); diff --git a/launcher/minecraft/update/AssetUpdateTask.cpp b/launcher/minecraft/update/AssetUpdateTask.cpp index 31fd5eb11..8af014996 100644 --- a/launcher/minecraft/update/AssetUpdateTask.cpp +++ b/launcher/minecraft/update/AssetUpdateTask.cpp @@ -1,20 +1,20 @@ #include "AssetUpdateTask.h" +#include "minecraft/AssetsUtils.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "net/ChecksumValidator.h" -#include "minecraft/AssetsUtils.h" #include "Application.h" -AssetUpdateTask::AssetUpdateTask(MinecraftInstance * inst) +#include "net/ApiDownload.h" + +AssetUpdateTask::AssetUpdateTask(MinecraftInstance* inst) { m_inst = inst; } -AssetUpdateTask::~AssetUpdateTask() -{ -} +AssetUpdateTask::~AssetUpdateTask() {} void AssetUpdateTask::executeTask() { @@ -24,17 +24,14 @@ void AssetUpdateTask::executeTask() auto assets = profile->getMinecraftAssets(); QUrl indexUrl = assets->url; QString localPath = assets->id + ".json"; - auto job = makeShared( - tr("Asset index for %1").arg(m_inst->name()), - APPLICATION->network() - ); + auto job = makeShared(tr("Asset index for %1").arg(m_inst->name()), APPLICATION->network()); auto metacache = APPLICATION->metacache(); auto entry = metacache->resolveEntry("asset_indexes", localPath); entry->setStale(true); auto hexSha1 = assets->sha1.toLatin1(); qDebug() << "Asset index SHA1:" << hexSha1; - auto dl = Net::Download::makeCached(indexUrl, entry); + auto dl = Net::ApiDownload::makeCached(indexUrl, entry); auto rawSha1 = QByteArray::fromHex(assets->sha1.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); job->addNetAction(dl); @@ -43,9 +40,9 @@ void AssetUpdateTask::executeTask() connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::assetIndexFinished); connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetIndexFailed); - connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); + connect(downloadJob.get(), &NetJob::aborted, this, [this] { emitFailed(tr("Aborted")); }); connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); - connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propogateStepProgress); + connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propagateStepProgress); qDebug() << m_inst->name() << ": Starting asset index download"; downloadJob->start(); @@ -67,8 +64,7 @@ void AssetUpdateTask::assetIndexFinished() QString asset_fname = "assets/indexes/" + assets->id + ".json"; // FIXME: this looks like a job for a generic validator based on json schema? - if (!AssetsUtils::loadAssetsIndexJson(assets->id, asset_fname, index)) - { + if (!AssetsUtils::loadAssetsIndexJson(assets->id, asset_fname, index)) { auto metacache = APPLICATION->metacache(); auto entry = metacache->resolveEntry("asset_indexes", assets->id + ".json"); metacache->evictEntry(entry); @@ -76,15 +72,14 @@ void AssetUpdateTask::assetIndexFinished() } auto job = index.getDownloadJob(); - if(job) - { + if (job) { setStatus(tr("Getting the assets files from Mojang...")); downloadJob = job; connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::emitSucceeded); connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed); - connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); + connect(downloadJob.get(), &NetJob::aborted, this, [this] { emitFailed(tr("Aborted")); }); connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); - connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propogateStepProgress); + connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propagateStepProgress); downloadJob->start(); return; } @@ -104,12 +99,9 @@ void AssetUpdateTask::assetsFailed(QString reason) bool AssetUpdateTask::abort() { - if(downloadJob) - { + if (downloadJob) { return downloadJob->abort(); - } - else - { + } else { qWarning() << "Prematurely aborted AssetUpdateTask"; } return true; diff --git a/launcher/minecraft/update/AssetUpdateTask.h b/launcher/minecraft/update/AssetUpdateTask.h index 6d7356f38..6f053a54a 100644 --- a/launcher/minecraft/update/AssetUpdateTask.h +++ b/launcher/minecraft/update/AssetUpdateTask.h @@ -1,28 +1,27 @@ #pragma once -#include "tasks/Task.h" #include "net/NetJob.h" +#include "tasks/Task.h" class MinecraftInstance; -class AssetUpdateTask : public Task -{ +class AssetUpdateTask : public Task { Q_OBJECT -public: - AssetUpdateTask(MinecraftInstance * inst); + public: + AssetUpdateTask(MinecraftInstance* inst); virtual ~AssetUpdateTask(); void executeTask() override; bool canAbort() const override; -private slots: + private slots: void assetIndexFinished(); void assetIndexFailed(QString reason); void assetsFailed(QString reason); -public slots: + public slots: bool abort() override; -private: - MinecraftInstance *m_inst; + private: + MinecraftInstance* m_inst; NetJob::Ptr downloadJob; }; diff --git a/launcher/minecraft/update/FMLLibrariesTask.cpp b/launcher/minecraft/update/FMLLibrariesTask.cpp index 75e5c5720..ce0c9a723 100644 --- a/launcher/minecraft/update/FMLLibrariesTask.cpp +++ b/launcher/minecraft/update/FMLLibrariesTask.cpp @@ -1,51 +1,49 @@ #include "FMLLibrariesTask.h" #include "FileSystem.h" -#include "minecraft/VersionFilterData.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" +#include "minecraft/VersionFilterData.h" -#include "BuildConfig.h" #include "Application.h" +#include "BuildConfig.h" -FMLLibrariesTask::FMLLibrariesTask(MinecraftInstance * inst) +#include "net/ApiDownload.h" + +FMLLibrariesTask::FMLLibrariesTask(MinecraftInstance* inst) { m_inst = inst; } void FMLLibrariesTask::executeTask() { // Get the mod list - MinecraftInstance *inst = (MinecraftInstance *)m_inst; + MinecraftInstance* inst = (MinecraftInstance*)m_inst; auto components = inst->getPackProfile(); auto profile = components->getProfile(); - if (!profile->hasTrait("legacyFML")) - { + if (!profile->hasTrait("legacyFML")) { emitSucceeded(); return; } QString version = components->getComponentVersion("net.minecraft"); - auto &fmlLibsMapping = g_VersionFilterData.fmlLibsMapping; - if (!fmlLibsMapping.contains(version)) - { + auto& fmlLibsMapping = g_VersionFilterData.fmlLibsMapping; + if (!fmlLibsMapping.contains(version)) { emitSucceeded(); return; } - auto &libList = fmlLibsMapping[version]; + auto& libList = fmlLibsMapping[version]; // determine if we need some libs for FML or forge setStatus(tr("Checking for FML libraries...")); - if(!components->getComponent("net.minecraftforge")) - { + if (!components->getComponent("net.minecraftforge")) { emitSucceeded(); return; } // now check the lib folder inside the instance for files. - for (auto &lib : libList) - { + for (auto& lib : libList) { QFileInfo libInfo(FS::PathCombine(inst->libDir(), lib.filename)); if (libInfo.exists()) continue; @@ -53,8 +51,7 @@ void FMLLibrariesTask::executeTask() } // if everything is in place, there's nothing to do here... - if (fmlLibsToProcess.isEmpty()) - { + if (fmlLibsToProcess.isEmpty()) { emitSucceeded(); return; } @@ -64,18 +61,17 @@ void FMLLibrariesTask::executeTask() NetJob::Ptr dljob{ new NetJob("FML libraries", APPLICATION->network()) }; auto metacache = APPLICATION->metacache(); Net::Download::Options options = Net::Download::Option::MakeEternal; - for (auto &lib : fmlLibsToProcess) - { + for (auto& lib : fmlLibsToProcess) { auto entry = metacache->resolveEntry("fmllibs", lib.filename); QString urlString = BuildConfig.FMLLIBS_BASE_URL + lib.filename; - dljob->addNetAction(Net::Download::makeCached(QUrl(urlString), entry, options)); + dljob->addNetAction(Net::ApiDownload::makeCached(QUrl(urlString), entry, options)); } connect(dljob.get(), &NetJob::succeeded, this, &FMLLibrariesTask::fmllibsFinished); connect(dljob.get(), &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed); - connect(dljob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); + connect(dljob.get(), &NetJob::aborted, this, [this] { emitFailed(tr("Aborted")); }); connect(dljob.get(), &NetJob::progress, this, &FMLLibrariesTask::progress); - connect(dljob.get(), &NetJob::stepProgress, this, &FMLLibrariesTask::propogateStepProgress); + connect(dljob.get(), &NetJob::stepProgress, this, &FMLLibrariesTask::propagateStepProgress); downloadJob.reset(dljob); downloadJob->start(); } @@ -88,24 +84,20 @@ bool FMLLibrariesTask::canAbort() const void FMLLibrariesTask::fmllibsFinished() { downloadJob.reset(); - if (!fmlLibsToProcess.isEmpty()) - { + if (!fmlLibsToProcess.isEmpty()) { setStatus(tr("Copying FML libraries into the instance...")); - MinecraftInstance *inst = (MinecraftInstance *)m_inst; + MinecraftInstance* inst = (MinecraftInstance*)m_inst; auto metacache = APPLICATION->metacache(); int index = 0; - for (auto &lib : fmlLibsToProcess) - { + for (auto& lib : fmlLibsToProcess) { progress(index, fmlLibsToProcess.size()); auto entry = metacache->resolveEntry("fmllibs", lib.filename); auto path = FS::PathCombine(inst->libDir(), lib.filename); - if (!FS::ensureFilePathExists(path)) - { + if (!FS::ensureFilePathExists(path)) { emitFailed(tr("Failed creating FML library folder inside the instance.")); return; } - if (!QFile::copy(entry->getFullPath(), FS::PathCombine(inst->libDir(), lib.filename))) - { + if (!QFile::copy(entry->getFullPath(), FS::PathCombine(inst->libDir(), lib.filename))) { emitFailed(tr("Failed copying Forge/FML library: %1.").arg(lib.filename)); return; } @@ -124,12 +116,9 @@ void FMLLibrariesTask::fmllibsFailed(QString reason) bool FMLLibrariesTask::abort() { - if(downloadJob) - { + if (downloadJob) { return downloadJob->abort(); - } - else - { + } else { qWarning() << "Prematurely aborted FMLLibrariesTask"; } return true; diff --git a/launcher/minecraft/update/FMLLibrariesTask.h b/launcher/minecraft/update/FMLLibrariesTask.h index 2e5ad83a2..9d0102be7 100644 --- a/launcher/minecraft/update/FMLLibrariesTask.h +++ b/launcher/minecraft/update/FMLLibrariesTask.h @@ -1,31 +1,29 @@ #pragma once -#include "tasks/Task.h" -#include "net/NetJob.h" #include "minecraft/VersionFilterData.h" +#include "net/NetJob.h" +#include "tasks/Task.h" class MinecraftInstance; -class FMLLibrariesTask : public Task -{ +class FMLLibrariesTask : public Task { Q_OBJECT -public: - FMLLibrariesTask(MinecraftInstance * inst); - virtual ~FMLLibrariesTask() {}; + public: + FMLLibrariesTask(MinecraftInstance* inst); + virtual ~FMLLibrariesTask(){}; void executeTask() override; bool canAbort() const override; -private slots: + private slots: void fmllibsFinished(); void fmllibsFailed(QString reason); -public slots: + public slots: bool abort() override; -private: - MinecraftInstance *m_inst; + private: + MinecraftInstance* m_inst; NetJob::Ptr downloadJob; QList fmlLibsToProcess; }; - diff --git a/launcher/minecraft/update/FoldersTask.cpp b/launcher/minecraft/update/FoldersTask.cpp index b9ee9d980..c74e8d2ef 100644 --- a/launcher/minecraft/update/FoldersTask.cpp +++ b/launcher/minecraft/update/FoldersTask.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -34,11 +34,10 @@ */ #include "FoldersTask.h" -#include "minecraft/MinecraftInstance.h" #include +#include "minecraft/MinecraftInstance.h" -FoldersTask::FoldersTask(MinecraftInstance * inst) - :Task() +FoldersTask::FoldersTask(MinecraftInstance* inst) : Task() { m_inst = inst; } @@ -47,8 +46,7 @@ void FoldersTask::executeTask() { // Make directories QDir mcDir(m_inst->gameRoot()); - if (!mcDir.exists() && !mcDir.mkpath(".")) - { + if (!mcDir.exists() && !mcDir.mkpath(".")) { emitFailed(tr("Failed to create folder for Minecraft binaries.")); return; } diff --git a/launcher/minecraft/update/FoldersTask.h b/launcher/minecraft/update/FoldersTask.h index f6ed5e6db..2d2954b2a 100644 --- a/launcher/minecraft/update/FoldersTask.h +++ b/launcher/minecraft/update/FoldersTask.h @@ -3,15 +3,14 @@ #include "tasks/Task.h" class MinecraftInstance; -class FoldersTask : public Task -{ +class FoldersTask : public Task { Q_OBJECT -public: - FoldersTask(MinecraftInstance * inst); - virtual ~FoldersTask() {}; + public: + FoldersTask(MinecraftInstance* inst); + virtual ~FoldersTask(){}; void executeTask() override; -private: - MinecraftInstance *m_inst; -}; + private: + MinecraftInstance* m_inst; +}; diff --git a/launcher/minecraft/update/LibrariesTask.cpp b/launcher/minecraft/update/LibrariesTask.cpp index 415b9a660..1581b32ee 100644 --- a/launcher/minecraft/update/LibrariesTask.cpp +++ b/launcher/minecraft/update/LibrariesTask.cpp @@ -5,7 +5,7 @@ #include "Application.h" -LibrariesTask::LibrariesTask(MinecraftInstance * inst) +LibrariesTask::LibrariesTask(MinecraftInstance* inst) { m_inst = inst; } @@ -14,7 +14,7 @@ void LibrariesTask::executeTask() { setStatus(tr("Downloading required library files...")); qDebug() << m_inst->name() << ": downloading libraries"; - MinecraftInstance *inst = (MinecraftInstance *)m_inst; + MinecraftInstance* inst = (MinecraftInstance*)m_inst; // Build a list of URLs that will need to be downloaded. auto components = inst->getPackProfile(); @@ -25,18 +25,14 @@ void LibrariesTask::executeTask() auto metacache = APPLICATION->metacache(); - auto processArtifactPool = [&](const QList & pool, QStringList & errors, const QString & localPath) - { - for (auto lib : pool) - { - if(!lib) - { + auto processArtifactPool = [&](const QList& pool, QStringList& errors, const QString& localPath) { + for (auto lib : pool) { + if (!lib) { emitFailed(tr("Null jar is specified in the metadata, aborting.")); return false; } auto dls = lib->getDownloads(inst->runtimeContext(), metacache.get(), errors, localPath); - for(auto dl : dls) - { + for (auto dl : dls) { downloadJob->addNetAction(dl); } } @@ -48,8 +44,7 @@ void LibrariesTask::executeTask() libArtifactPool.append(profile->getLibraries()); libArtifactPool.append(profile->getNativeLibraries()); libArtifactPool.append(profile->getMavenFiles()); - for (auto agent : profile->getAgents()) - { + for (auto agent : profile->getAgents()) { libArtifactPool.append(agent->library()); } libArtifactPool.append(profile->getMainJar()); @@ -58,19 +53,20 @@ void LibrariesTask::executeTask() QStringList failedLocalJarMods; processArtifactPool(profile->getJarMods(), failedLocalJarMods, inst->jarModsDir()); - if (!failedLocalJarMods.empty() || !failedLocalLibraries.empty()) - { + if (!failedLocalJarMods.empty() || !failedLocalLibraries.empty()) { downloadJob.reset(); QString failed_all = (failedLocalLibraries + failedLocalJarMods).join("\n"); - emitFailed(tr("Some artifacts marked as 'local' are missing their files:\n%1\n\nYou need to either add the files, or removed the packages that require them.\nYou'll have to correct this problem manually.").arg(failed_all)); + emitFailed(tr("Some artifacts marked as 'local' are missing their files:\n%1\n\nYou need to either add the files, or removed the " + "packages that require them.\nYou'll have to correct this problem manually.") + .arg(failed_all)); return; } connect(downloadJob.get(), &NetJob::succeeded, this, &LibrariesTask::emitSucceeded); connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed); - connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); + connect(downloadJob.get(), &NetJob::aborted, this, [this] { emitFailed(tr("Aborted")); }); connect(downloadJob.get(), &NetJob::progress, this, &LibrariesTask::progress); - connect(downloadJob.get(), &NetJob::stepProgress, this, &LibrariesTask::propogateStepProgress); + connect(downloadJob.get(), &NetJob::stepProgress, this, &LibrariesTask::propagateStepProgress); downloadJob->start(); } @@ -87,12 +83,9 @@ void LibrariesTask::jarlibFailed(QString reason) bool LibrariesTask::abort() { - if(downloadJob) - { + if (downloadJob) { return downloadJob->abort(); - } - else - { + } else { qWarning() << "Prematurely aborted LibrariesTask"; } return true; diff --git a/launcher/minecraft/update/LibrariesTask.h b/launcher/minecraft/update/LibrariesTask.h index b966ad6cb..c969e74df 100644 --- a/launcher/minecraft/update/LibrariesTask.h +++ b/launcher/minecraft/update/LibrariesTask.h @@ -1,26 +1,25 @@ #pragma once -#include "tasks/Task.h" #include "net/NetJob.h" +#include "tasks/Task.h" class MinecraftInstance; -class LibrariesTask : public Task -{ +class LibrariesTask : public Task { Q_OBJECT -public: - LibrariesTask(MinecraftInstance * inst); - virtual ~LibrariesTask() {}; + public: + LibrariesTask(MinecraftInstance* inst); + virtual ~LibrariesTask(){}; void executeTask() override; bool canAbort() const override; -private slots: + private slots: void jarlibFailed(QString reason); -public slots: + public slots: bool abort() override; -private: - MinecraftInstance *m_inst; + private: + MinecraftInstance* m_inst; NetJob::Ptr downloadJob; }; diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index f7582b8f1..6d968ea48 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -1,8 +1,8 @@ #pragma once #include "minecraft/mod/Mod.h" -#include "modplatform/ResourceAPI.h" #include "modplatform/ModIndex.h" +#include "modplatform/ResourceAPI.h" #include "tasks/Task.h" class ResourceDownloadTask; @@ -12,8 +12,11 @@ class CheckUpdateTask : public Task { Q_OBJECT public: - CheckUpdateTask(QList& mods, std::list& mcVersions, std::optional loaders, std::shared_ptr mods_folder) - : Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder) {}; + CheckUpdateTask(QList& mods, + std::list& mcVersions, + std::optional loaders, + std::shared_ptr mods_folder) + : Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder){}; struct UpdatableMod { QString name; @@ -25,7 +28,13 @@ class CheckUpdateTask : public Task { shared_qobject_ptr download; public: - UpdatableMod(QString name, QString old_h, QString old_v, QString new_v, QString changelog, ModPlatform::ResourceProvider p, shared_qobject_ptr t) + UpdatableMod(QString name, + QString old_h, + QString old_v, + QString new_v, + QString changelog, + ModPlatform::ResourceProvider p, + shared_qobject_ptr t) : name(name), old_hash(old_h), old_version(old_v), new_version(new_v), changelog(changelog), provider(p), download(t) {} }; diff --git a/launcher/modplatform/EnsureMetadataTask.h b/launcher/modplatform/EnsureMetadataTask.h index 03cae4e4a..2f276e5a0 100644 --- a/launcher/modplatform/EnsureMetadataTask.h +++ b/launcher/modplatform/EnsureMetadataTask.h @@ -35,10 +35,7 @@ class EnsureMetadataTask : public Task { auto flameProjectsTask() -> Task::Ptr; // Helpers - enum class RemoveFromList { - Yes, - No - }; + enum class RemoveFromList { Yes, No }; void emitReady(Mod*, QString key = {}, RemoveFromList = RemoveFromList::Yes); void emitFail(Mod*, QString key = {}, RemoveFromList = RemoveFromList::Yes); diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index a1c4d8917..350a9f10b 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -1,20 +1,20 @@ // SPDX-License-Identifier: GPL-3.0-only /* -* PolyMC - Minecraft Launcher -* Copyright (c) 2022 flowln -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, version 3. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "modplatform/ModIndex.h" diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 2aa91602b..cad217034 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -113,7 +113,7 @@ struct IndexedPack { ExtraPackData extraData; // For internal use, not provided by APIs - [[nodiscard]] bool isVersionSelected(size_t index) const + [[nodiscard]] bool isVersionSelected(int index) const { if (!versionsLoaded) return false; @@ -144,7 +144,8 @@ inline auto getOverrideDeps() -> QList { "qvIfYCYJ", "P7dR8mSH", "API", ModPlatform::ResourceProvider::MODRINTH }, { "lwVhp9o5", "Ha28R6CL", "KotlinLibraries", ModPlatform::ResourceProvider::MODRINTH } }; -}; +} + QString getMetaURL(ResourceProvider provider, QVariant projectID); } // namespace ModPlatform diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index d3277761e..a92217a06 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -128,28 +128,30 @@ class ResourceAPI { public slots: [[nodiscard]] virtual Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const { - qWarning() << "TODO"; + qWarning() << "TODO: ResourceAPI::searchProjects"; return nullptr; } - [[nodiscard]] virtual Task::Ptr getProject(QString addonId, std::shared_ptr response) const + [[nodiscard]] virtual Task::Ptr getProject([[maybe_unused]] QString addonId, + [[maybe_unused]] std::shared_ptr response) const { - qWarning() << "TODO"; + qWarning() << "TODO: ResourceAPI::getProject"; return nullptr; } - [[nodiscard]] virtual Task::Ptr getProjects(QStringList addonIds, std::shared_ptr response) const + [[nodiscard]] virtual Task::Ptr getProjects([[maybe_unused]] QStringList addonIds, + [[maybe_unused]] std::shared_ptr response) const { - qWarning() << "TODO"; + qWarning() << "TODO: ResourceAPI::getProjects"; return nullptr; } [[nodiscard]] virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const { - qWarning() << "TODO"; + qWarning() << "TODO: ResourceAPI::getProjectInfo"; return nullptr; } [[nodiscard]] virtual Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const { - qWarning() << "TODO"; + qWarning() << "TODO: ResourceAPI::getProjectVersions"; return nullptr; } diff --git a/launcher/modplatform/atlauncher/ATLPackIndex.cpp b/launcher/modplatform/atlauncher/ATLPackIndex.cpp index e649c43a6..3be169739 100644 --- a/launcher/modplatform/atlauncher/ATLPackIndex.cpp +++ b/launcher/modplatform/atlauncher/ATLPackIndex.cpp @@ -21,23 +21,20 @@ #include "Json.h" -static void loadIndexedVersion(ATLauncher::IndexedVersion & v, QJsonObject & obj) +static void loadIndexedVersion(ATLauncher::IndexedVersion& v, QJsonObject& obj) { v.version = Json::requireString(obj, "version"); v.minecraft = Json::requireString(obj, "minecraft"); } -void ATLauncher::loadIndexedPack(ATLauncher::IndexedPack & m, QJsonObject & obj) +void ATLauncher::loadIndexedPack(ATLauncher::IndexedPack& m, QJsonObject& obj) { m.id = Json::requireInteger(obj, "id"); m.position = Json::requireInteger(obj, "position"); m.name = Json::requireString(obj, "name"); - m.type = Json::requireString(obj, "type") == "private" ? - ATLauncher::PackType::Private : - ATLauncher::PackType::Public; + m.type = Json::requireString(obj, "type") == "private" ? ATLauncher::PackType::Private : ATLauncher::PackType::Public; auto versionsArr = Json::requireArray(obj, "versions"); - for (const auto versionRaw : versionsArr) - { + for (const auto versionRaw : versionsArr) { auto versionObj = Json::requireObject(versionRaw); ATLauncher::IndexedVersion version; loadIndexedVersion(version, versionObj); diff --git a/launcher/modplatform/atlauncher/ATLPackIndex.h b/launcher/modplatform/atlauncher/ATLPackIndex.h index 337b80b88..8d18c671d 100644 --- a/launcher/modplatform/atlauncher/ATLPackIndex.h +++ b/launcher/modplatform/atlauncher/ATLPackIndex.h @@ -18,21 +18,18 @@ #include "ATLPackManifest.h" +#include #include #include -#include -namespace ATLauncher -{ +namespace ATLauncher { -struct IndexedVersion -{ +struct IndexedVersion { QString version; QString minecraft; }; -struct IndexedPack -{ +struct IndexedPack { int id; int position; QString name; @@ -44,7 +41,7 @@ struct IndexedPack QString safeName; }; -void loadIndexedPack(IndexedPack & m, QJsonObject & obj); -} +void loadIndexedPack(IndexedPack& m, QJsonObject& obj); +} // namespace ATLauncher Q_DECLARE_METATYPE(ATLauncher::IndexedPack) diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 22ea02da2..e5771b7cd 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -40,27 +40,29 @@ #include -#include "MMCZip.h" -#include "minecraft/OneSixVersionFormat.h" -#include "Version.h" -#include "net/ChecksumValidator.h" #include "FileSystem.h" #include "Json.h" -#include "minecraft/MinecraftInstance.h" -#include "minecraft/PackProfile.h" -#include "settings/INISettingsObject.h" +#include "MMCZip.h" +#include "Version.h" #include "meta/Index.h" #include "meta/Version.h" #include "meta/VersionList.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/OneSixVersionFormat.h" +#include "minecraft/PackProfile.h" +#include "net/ChecksumValidator.h" +#include "settings/INISettingsObject.h" + +#include "net/ApiDownload.h" -#include "BuildConfig.h" #include "Application.h" +#include "BuildConfig.h" namespace ATLauncher { static Meta::Version::Ptr getComponentVersion(const QString& uid, const QString& version); -PackInstallTask::PackInstallTask(UserInteractionSupport *support, QString packName, QString version, InstallMode installMode) +PackInstallTask::PackInstallTask(UserInteractionSupport* support, QString packName, QString version, InstallMode installMode) { m_support = support; m_pack_name = packName; @@ -71,8 +73,7 @@ PackInstallTask::PackInstallTask(UserInteractionSupport *support, QString packNa bool PackInstallTask::abort() { - if(abortable) - { + if (abortable) { return jobPtr->abort(); } return false; @@ -84,7 +85,7 @@ void PackInstallTask::executeTask() NetJob::Ptr netJob{ new NetJob("ATLauncher::VersionFetch", APPLICATION->network()) }; auto searchUrl = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.json").arg(m_pack_safe_name).arg(m_version_name); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response)); QObject::connect(netJob.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded); QObject::connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed); @@ -110,12 +111,9 @@ void PackInstallTask::onDownloadSucceeded() auto obj = doc.object(); ATLauncher::PackVersion version; - try - { + try { ATLauncher::loadVersion(version, obj); - } - catch (const JSONValidationError &e) - { + } catch (const JSONValidationError& e) { emitFailed(tr("Could not understand pack manifest:\n") + e.cause()); return; } @@ -126,20 +124,20 @@ void PackInstallTask::onDownloadSucceeded() bool resetDirectory; switch (m_install_mode) { - case InstallMode::Reinstall: - case InstallMode::Update: - message = m_version.messages.update; - resetDirectory = true; - break; + case InstallMode::Reinstall: + case InstallMode::Update: + message = m_version.messages.update; + resetDirectory = true; + break; - case InstallMode::Install: - message = m_version.messages.install; - resetDirectory = false; - break; + case InstallMode::Install: + message = m_version.messages.install; + resetDirectory = false; + break; - default: - emitFailed(tr("Unsupported installation mode")); - return; + default: + emitFailed(tr("Unsupported installation mode")); + return; } // Display message if one exists @@ -157,10 +155,9 @@ void PackInstallTask::onDownloadSucceeded() deleteExistingFiles(); } - if(m_version.noConfigs) { + if (m_version.noConfigs) { downloadMods(); - } - else { + } else { installConfigs(); } } @@ -212,11 +209,9 @@ void PackInstallTask::deleteExistingFiles() if (base == "root") { return minecraftPath; - } - else if (base == "config") { + } else if (base == "config") { return FS::PathCombine(minecraftPath, "config"); - } - else { + } else { qWarning() << "Unrecognised base path" << base; return minecraftPath; } @@ -338,19 +333,18 @@ QString PackInstallTask::getDirForModType(ModType type, QString raw) QString PackInstallTask::getVersionForLoader(QString uid) { - if(m_version.loader.recommended || m_version.loader.latest || m_version.loader.choose) { + if (m_version.loader.recommended || m_version.loader.latest || m_version.loader.choose) { auto vlist = APPLICATION->metadataIndex()->get(uid); - if(!vlist) - { + if (!vlist) { emitFailed(tr("Failed to get local metadata index for %1").arg(uid)); return Q_NULLPTR; } - if(!vlist->isLoaded()) { + if (!vlist->isLoaded()) { vlist->load(Net::Mode::Online); } - if(m_version.loader.recommended || m_version.loader.latest) { + if (m_version.loader.recommended || m_version.loader.latest) { for (int i = 0; i < vlist->versions().size(); i++) { auto version = vlist->versions().at(i); auto reqs = version->requiredSet(); @@ -359,16 +353,17 @@ QString PackInstallTask::getVersionForLoader(QString uid) // not all mod loaders depend on a given Minecraft version, so we won't do this // filtering for those loaders. if (m_version.loader.type != "fabric") { - auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Meta::Require &req) { - return req.uid == "net.minecraft"; - }); - if (iter == reqs.end()) continue; - if (iter->equalsVersion != m_version.minecraft) continue; + auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Meta::Require& req) { return req.uid == "net.minecraft"; }); + if (iter == reqs.end()) + continue; + if (iter->equalsVersion != m_version.minecraft) + continue; } if (m_version.loader.recommended) { // first recommended build we find, we use. - if (!version->isRecommended()) continue; + if (!version->isRecommended()) + continue; } return version->descriptor(); @@ -376,8 +371,7 @@ QString PackInstallTask::getVersionForLoader(QString uid) emitFailed(tr("Failed to find version for %1 loader").arg(m_version.loader.type)); return Q_NULLPTR; - } - else if(m_version.loader.choose) { + } else if (m_version.loader.choose) { // Fabric Loader doesn't depend on a given Minecraft version. if (m_version.loader.type == "fabric") { return m_support->chooseVersion(vlist, Q_NULLPTR); @@ -414,15 +408,14 @@ QString PackInstallTask::detectLibrary(VersionLibrary library) return group + ":" + artefact + ":" + version; } - if(library.file.contains("-")) { + if (library.file.contains("-")) { auto lastSlash = library.file.lastIndexOf("-"); auto name = library.file.mid(0, lastSlash); auto version = library.file.mid(lastSlash + 1).remove(".jar"); - if(name == QString("guava")) { + if (name == QString("guava")) { return "com.google.guava:guava:" + version; - } - else if(name == QString("commons-lang3")) { + } else if (name == QString("commons-lang3")) { return "org.apache.commons:commons-lang3:" + version; } } @@ -432,22 +425,22 @@ QString PackInstallTask::detectLibrary(VersionLibrary library) bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared_ptr profile) { - if(m_version.libraries.isEmpty()) { + if (m_version.libraries.isEmpty()) { return true; } QList exempt; - for(const auto & componentUid : componentsToInstall.keys()) { + for (const auto& componentUid : componentsToInstall.keys()) { auto componentVersion = componentsToInstall.value(componentUid); - for(const auto & library : componentVersion->data()->libraries) { + for (const auto& library : componentVersion->data()->libraries) { GradleSpecifier lib(library->rawName()); exempt.append(lib); } } { - for(const auto & library : minecraftVersion->data()->libraries) { + for (const auto& library : minecraftVersion->data()->libraries) { GradleSpecifier lib(library->rawName()); exempt.append(lib); } @@ -458,8 +451,7 @@ bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared auto target_id = "org.multimc.atlauncher." + id; auto patchDir = FS::PathCombine(instanceRoot, "patches"); - if(!FS::ensureFolderPathExists(patchDir)) - { + if (!FS::ensureFolderPathExists(patchDir)) { return false; } auto patchFileName = FS::PathCombine(patchDir, target_id + ".json"); @@ -468,38 +460,24 @@ bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared f->name = m_pack_name + " " + m_version_name + " (libraries)"; const static QMap liteLoaderMap = { - { "61179803bcd5fb7790789b790908663d", "1.12-SNAPSHOT" }, - { "1420785ecbfed5aff4a586c5c9dd97eb", "1.12.2-SNAPSHOT" }, - { "073f68e2fcb518b91fd0d99462441714", "1.6.2_03" }, - { "10a15b52fc59b1bfb9c05b56de1097d6", "1.6.2_02" }, - { "b52f90f08303edd3d4c374e268a5acf1", "1.6.2_04" }, - { "ea747e24e03e24b7cad5bc8a246e0319", "1.6.2_01" }, - { "55785ccc82c07ff0ba038fe24be63ea2", "1.7.10_01" }, - { "63ada46e033d0cb6782bada09ad5ca4e", "1.7.10_04" }, - { "7983e4b28217c9ae8569074388409c86", "1.7.10_03" }, - { "c09882458d74fe0697c7681b8993097e", "1.7.10_02" }, - { "db7235aefd407ac1fde09a7baba50839", "1.7.10_00" }, - { "6e9028816027f53957bd8fcdfabae064", "1.8" }, - { "5e732dc446f9fe2abe5f9decaec40cde", "1.10-SNAPSHOT" }, - { "3a98b5ed95810bf164e71c1a53be568d", "1.11.2-SNAPSHOT" }, - { "ba8e6285966d7d988a96496f48cbddaa", "1.8.9-SNAPSHOT" }, - { "8524af3ac3325a82444cc75ae6e9112f", "1.11-SNAPSHOT" }, - { "53639d52340479ccf206a04f5e16606f", "1.5.2_01" }, - { "1fcdcf66ce0a0806b7ad8686afdce3f7", "1.6.4_00" }, - { "531c116f71ae2b11033f9a11a0f8e668", "1.6.4_01" }, - { "4009eeb99c9068f608d3483a6439af88", "1.7.2_03" }, - { "66f343354b8417abce1a10d557d2c6e9", "1.7.2_04" }, - { "ab554c21f28fbc4ae9b098bcb5f4cceb", "1.7.2_05" }, - { "e1d76a05a3723920e2f80a5e66c45f16", "1.7.2_02" }, - { "00318cb0c787934d523f63cdfe8ddde4", "1.9-SNAPSHOT" }, - { "986fd1ee9525cb0dcab7609401cef754", "1.9.4-SNAPSHOT" }, - { "571ad5e6edd5ff40259570c9be588bb5", "1.9.4" }, - { "1cdd72f7232e45551f16cc8ffd27ccf3", "1.10.2-SNAPSHOT" }, - { "8a7c21f32d77ee08b393dd3921ced8eb", "1.10.2" }, - { "b9bef8abc8dc309069aeba6fbbe58980", "1.12.1-SNAPSHOT" } + { "61179803bcd5fb7790789b790908663d", "1.12-SNAPSHOT" }, { "1420785ecbfed5aff4a586c5c9dd97eb", "1.12.2-SNAPSHOT" }, + { "073f68e2fcb518b91fd0d99462441714", "1.6.2_03" }, { "10a15b52fc59b1bfb9c05b56de1097d6", "1.6.2_02" }, + { "b52f90f08303edd3d4c374e268a5acf1", "1.6.2_04" }, { "ea747e24e03e24b7cad5bc8a246e0319", "1.6.2_01" }, + { "55785ccc82c07ff0ba038fe24be63ea2", "1.7.10_01" }, { "63ada46e033d0cb6782bada09ad5ca4e", "1.7.10_04" }, + { "7983e4b28217c9ae8569074388409c86", "1.7.10_03" }, { "c09882458d74fe0697c7681b8993097e", "1.7.10_02" }, + { "db7235aefd407ac1fde09a7baba50839", "1.7.10_00" }, { "6e9028816027f53957bd8fcdfabae064", "1.8" }, + { "5e732dc446f9fe2abe5f9decaec40cde", "1.10-SNAPSHOT" }, { "3a98b5ed95810bf164e71c1a53be568d", "1.11.2-SNAPSHOT" }, + { "ba8e6285966d7d988a96496f48cbddaa", "1.8.9-SNAPSHOT" }, { "8524af3ac3325a82444cc75ae6e9112f", "1.11-SNAPSHOT" }, + { "53639d52340479ccf206a04f5e16606f", "1.5.2_01" }, { "1fcdcf66ce0a0806b7ad8686afdce3f7", "1.6.4_00" }, + { "531c116f71ae2b11033f9a11a0f8e668", "1.6.4_01" }, { "4009eeb99c9068f608d3483a6439af88", "1.7.2_03" }, + { "66f343354b8417abce1a10d557d2c6e9", "1.7.2_04" }, { "ab554c21f28fbc4ae9b098bcb5f4cceb", "1.7.2_05" }, + { "e1d76a05a3723920e2f80a5e66c45f16", "1.7.2_02" }, { "00318cb0c787934d523f63cdfe8ddde4", "1.9-SNAPSHOT" }, + { "986fd1ee9525cb0dcab7609401cef754", "1.9.4-SNAPSHOT" }, { "571ad5e6edd5ff40259570c9be588bb5", "1.9.4" }, + { "1cdd72f7232e45551f16cc8ffd27ccf3", "1.10.2-SNAPSHOT" }, { "8a7c21f32d77ee08b393dd3921ced8eb", "1.10.2" }, + { "b9bef8abc8dc309069aeba6fbbe58980", "1.12.1-SNAPSHOT" } }; - for(const auto & lib : m_version.libraries) { + for (const auto& lib : m_version.libraries) { // If the library is LiteLoader, we need to ignore it and handle it separately. if (liteLoaderMap.contains(lib.md5)) { auto ver = getComponentVersion("com.mumfrey.liteloader", liteLoaderMap.value(lib.md5)); @@ -513,18 +491,19 @@ bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared GradleSpecifier libSpecifier(libName); bool libExempt = false; - for(const auto & existingLib : exempt) { - if(libSpecifier.matchName(existingLib)) { + for (const auto& existingLib : exempt) { + if (libSpecifier.matchName(existingLib)) { // If the pack specifies a newer version of the lib, use that! libExempt = Version(libSpecifier.version()) >= Version(existingLib.version()); } } - if(libExempt) continue; + if (libExempt) + continue; auto library = std::make_shared(); library->setRawName(libName); - switch(lib.download) { + switch (lib.download) { case DownloadType::Server: library->setAbsoluteUrl(BuildConfig.ATL_DOWNLOAD_SERVER_URL + lib.url); break; @@ -540,15 +519,13 @@ bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared f->libraries.append(library); } - if(f->libraries.isEmpty()) { + if (f->libraries.isEmpty()) { return true; } QFile file(patchFileName); - if (!file.open(QFile::WriteOnly)) - { - qCritical() << "Error opening" << file.fileName() - << "for reading:" << file.errorString(); + if (!file.open(QFile::WriteOnly)) { + qCritical() << "Error opening" << file.fileName() << "for reading:" << file.errorString(); return false; } file.write(OneSixVersionFormat::versionFileToJson(f).toJson()); @@ -593,18 +570,17 @@ bool PackInstallTask::createPackComponent(QString instanceRoot, std::shared_ptr< auto target_id = "org.multimc.atlauncher." + id; auto patchDir = FS::PathCombine(instanceRoot, "patches"); - if(!FS::ensureFolderPathExists(patchDir)) - { + if (!FS::ensureFolderPathExists(patchDir)) { return false; } auto patchFileName = FS::PathCombine(patchDir, target_id + ".json"); QStringList mainClasses; QStringList tweakers; - for(const auto & componentUid : componentsToInstall.keys()) { + for (const auto& componentUid : componentsToInstall.keys()) { auto componentVersion = componentsToInstall.value(componentUid); - if(componentVersion->data()->mainClass != QString("")) { + if (componentVersion->data()->mainClass != QString("")) { mainClasses.append(componentVersion->data()->mainClass); } tweakers.append(componentVersion->data()->addTweakers); @@ -619,25 +595,24 @@ bool PackInstallTask::createPackComponent(QString instanceRoot, std::shared_ptr< // Parse out tweakers auto args = extraArguments.split(" "); QString previous; - for(auto arg : args) { - if(arg.startsWith("--tweakClass=") || previous == "--tweakClass") { + for (auto arg : args) { + if (arg.startsWith("--tweakClass=") || previous == "--tweakClass") { auto tweakClass = arg.remove("--tweakClass="); - if(tweakers.contains(tweakClass)) continue; + if (tweakers.contains(tweakClass)) + continue; f->addTweakers.append(tweakClass); } previous = arg; } - if(f->mainClass == QString() && f->addTweakers.isEmpty()) { + if (f->mainClass == QString() && f->addTweakers.isEmpty()) { return true; } QFile file(patchFileName); - if (!file.open(QFile::WriteOnly)) - { - qCritical() << "Error opening" << file.fileName() - << "for reading:" << file.errorString(); + if (!file.open(QFile::WriteOnly)) { + qCritical() << "Error opening" << file.fileName() << "for reading:" << file.errorString(); return false; } file.write(OneSixVersionFormat::versionFileToJson(f).toJson()); @@ -654,12 +629,11 @@ void PackInstallTask::installConfigs() jobPtr.reset(new NetJob(tr("Config download"), APPLICATION->network())); auto path = QString("Configs/%1/%2.zip").arg(m_pack_safe_name).arg(m_version_name); - auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.zip") - .arg(m_pack_safe_name).arg(m_version_name); + auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.zip").arg(m_pack_safe_name).arg(m_version_name); auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", path); entry->setStale(true); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); if (!m_version.configs.sha1.isEmpty()) { auto rawSha1 = QByteArray::fromHex(m_version.configs.sha1.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); @@ -667,25 +641,22 @@ void PackInstallTask::installConfigs() jobPtr->addNetAction(dl); archivePath = entry->getFullPath(); - connect(jobPtr.get(), &NetJob::succeeded, this, [&]() - { + connect(jobPtr.get(), &NetJob::succeeded, this, [&]() { abortable = false; jobPtr.reset(); extractConfigs(); }); - connect(jobPtr.get(), &NetJob::failed, [&](QString reason) - { + connect(jobPtr.get(), &NetJob::failed, [&](QString reason) { abortable = false; jobPtr.reset(); emitFailed(reason); }); - connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total) - { + connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total) { abortable = true; setProgress(current, total); }); - connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress); - connect(jobPtr.get(), &NetJob::aborted, [&]{ + connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress); + connect(jobPtr.get(), &NetJob::aborted, [&] { abortable = false; jobPtr.reset(); emitAborted(); @@ -702,25 +673,20 @@ void PackInstallTask::extractConfigs() QDir extractDir(m_stagingPath); QuaZip packZip(archivePath); - if(!packZip.open(QuaZip::mdUnzip)) - { + if (!packZip.open(QuaZip::mdUnzip)) { emitFailed(tr("Failed to open pack configs %1!").arg(archivePath)); return; } #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload::of(MMCZip::extractDir), archivePath, extractDir.absolutePath() + "/minecraft"); + m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload::of(MMCZip::extractDir), archivePath, + extractDir.absolutePath() + "/minecraft"); #else - m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/minecraft"); + m_extractFuture = + QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/minecraft"); #endif - connect(&m_extractFutureWatcher, &QFutureWatcher::finished, this, [&]() - { - downloadMods(); - }); - connect(&m_extractFutureWatcher, &QFutureWatcher::canceled, this, [&]() - { - emitAborted(); - }); + connect(&m_extractFutureWatcher, &QFutureWatcher::finished, this, [&]() { downloadMods(); }); + connect(&m_extractFutureWatcher, &QFutureWatcher::canceled, this, [&]() { emitAborted(); }); m_extractFutureWatcher.setFuture(m_extractFuture); } @@ -751,15 +717,17 @@ void PackInstallTask::downloadMods() jarmods.clear(); jobPtr.reset(new NetJob(tr("Mod download"), APPLICATION->network())); - for(const auto& mod : m_version.mods) { + for (const auto& mod : m_version.mods) { // skip non-client mods - if(!mod.client) continue; + if (!mod.client) + continue; // skip optional mods that were not selected - if(mod.optional && !selectedMods.contains(mod.name)) continue; + if (mod.optional && !selectedMods.contains(mod.name)) + continue; QString url; - switch(mod.download) { + switch (mod.download) { case DownloadType::Server: url = BuildConfig.ATL_DOWNLOAD_SERVER_URL + mod.url; break; @@ -782,33 +750,32 @@ void PackInstallTask::downloadMods() entry->setStale(true); modsToExtract.insert(entry->getFullPath(), mod); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); } jobPtr->addNetAction(dl); - } - else if(mod.type == ModType::Decomp) { + } else if (mod.type == ModType::Decomp) { auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", cacheName); entry->setStale(true); modsToDecomp.insert(entry->getFullPath(), mod); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); } jobPtr->addNetAction(dl); - } - else { + } else { auto relpath = getDirForModType(mod.type, mod.type_raw); - if(relpath == Q_NULLPTR) continue; + if (relpath == Q_NULLPTR) + continue; auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", cacheName); entry->setStale(true); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); @@ -817,7 +784,7 @@ void PackInstallTask::downloadMods() auto path = FS::PathCombine(m_stagingPath, "minecraft", relpath, mod.file); - if(mod.type == ModType::Forge) { + if (mod.type == ModType::Forge) { auto ver = getComponentVersion("net.minecraftforge", mod.version); if (ver) { componentsToInstall.insert("net.minecraftforge", ver); @@ -828,7 +795,7 @@ void PackInstallTask::downloadMods() jarmods.push_back(path); } - if(mod.type == ModType::Jar) { + if (mod.type == ModType::Jar) { qDebug() << "Jarmod: " + path; jarmods.push_back(path); } @@ -840,21 +807,18 @@ void PackInstallTask::downloadMods() } connect(jobPtr.get(), &NetJob::succeeded, this, &PackInstallTask::onModsDownloaded); - connect(jobPtr.get(), &NetJob::failed, [&](QString reason) - { + connect(jobPtr.get(), &NetJob::failed, [&](QString reason) { abortable = false; jobPtr.reset(); emitFailed(reason); }); - connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total) - { + connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total) { setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); abortable = true; setProgress(current, total); }); - connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress); - connect(jobPtr.get(), &NetJob::aborted, [&] - { + connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress); + connect(jobPtr.get(), &NetJob::aborted, [&] { abortable = false; jobPtr.reset(); emitAborted(); @@ -863,60 +827,56 @@ void PackInstallTask::downloadMods() jobPtr->start(); } -void PackInstallTask::onModsDownloaded() { +void PackInstallTask::onModsDownloaded() +{ abortable = false; qDebug() << "PackInstallTask::onModsDownloaded: " << QThread::currentThreadId(); jobPtr.reset(); - if(!modsToExtract.empty() || !modsToDecomp.empty() || !modsToCopy.empty()) { + if (!modsToExtract.empty() || !modsToDecomp.empty() || !modsToCopy.empty()) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - m_modExtractFuture = QtConcurrent::run(QThreadPool::globalInstance(), &PackInstallTask::extractMods, this, modsToExtract, modsToDecomp, modsToCopy); + m_modExtractFuture = + QtConcurrent::run(QThreadPool::globalInstance(), &PackInstallTask::extractMods, this, modsToExtract, modsToDecomp, modsToCopy); #else - m_modExtractFuture = QtConcurrent::run(QThreadPool::globalInstance(), this, &PackInstallTask::extractMods, modsToExtract, modsToDecomp, modsToCopy); + m_modExtractFuture = + QtConcurrent::run(QThreadPool::globalInstance(), this, &PackInstallTask::extractMods, modsToExtract, modsToDecomp, modsToCopy); #endif connect(&m_modExtractFutureWatcher, &QFutureWatcher::finished, this, &PackInstallTask::onModsExtracted); - connect(&m_modExtractFutureWatcher, &QFutureWatcher::canceled, this, [&]() - { - emitAborted(); - }); + connect(&m_modExtractFutureWatcher, &QFutureWatcher::canceled, this, [&]() { emitAborted(); }); m_modExtractFutureWatcher.setFuture(m_modExtractFuture); - } - else { + } else { install(); } } -void PackInstallTask::onModsExtracted() { +void PackInstallTask::onModsExtracted() +{ qDebug() << "PackInstallTask::onModsExtracted: " << QThread::currentThreadId(); - if(m_modExtractFuture.result()) { + if (m_modExtractFuture.result()) { install(); - } - else { + } else { emitFailed(tr("Failed to extract mods...")); } } -bool PackInstallTask::extractMods( - const QMap &toExtract, - const QMap &toDecomp, - const QMap &toCopy -) { +bool PackInstallTask::extractMods(const QMap& toExtract, + const QMap& toDecomp, + const QMap& toCopy) +{ qDebug() << "PackInstallTask::extractMods: " << QThread::currentThreadId(); setStatus(tr("Extracting mods...")); for (auto iter = toExtract.begin(); iter != toExtract.end(); iter++) { - auto &modPath = iter.key(); - auto &mod = iter.value(); + auto& modPath = iter.key(); + auto& mod = iter.value(); QString extractToDir; - if(mod.type == ModType::Extract) { + if (mod.type == ModType::Extract) { extractToDir = getDirForModType(mod.extractTo, mod.extractTo_raw); - } - else if(mod.type == ModType::TexturePackExtract) { + } else if (mod.type == ModType::TexturePackExtract) { extractToDir = FS::PathCombine("texturepacks", "extracted"); - } - else if(mod.type == ModType::ResourcePackExtract) { + } else if (mod.type == ModType::ResourcePackExtract) { extractToDir = FS::PathCombine("resourcepacks", "extracted"); } @@ -924,36 +884,36 @@ bool PackInstallTask::extractMods( auto extractToPath = FS::PathCombine(extractDir.absolutePath(), "minecraft", extractToDir); QString folderToExtract = ""; - if(mod.type == ModType::Extract) { + if (mod.type == ModType::Extract) { folderToExtract = mod.extractFolder; folderToExtract.remove(QRegularExpression("^/")); } qDebug() << "Extracting " + mod.file + " to " + extractToDir; - if(!MMCZip::extractDir(modPath, folderToExtract, extractToPath)) { + if (!MMCZip::extractDir(modPath, folderToExtract, extractToPath)) { // assume error return false; } } for (auto iter = toDecomp.begin(); iter != toDecomp.end(); iter++) { - auto &modPath = iter.key(); - auto &mod = iter.value(); + auto& modPath = iter.key(); + auto& mod = iter.value(); auto extractToDir = getDirForModType(mod.decompType, mod.decompType_raw); QDir extractDir(m_stagingPath); auto extractToPath = FS::PathCombine(extractDir.absolutePath(), "minecraft", extractToDir, mod.decompFile); qDebug() << "Extracting " + mod.decompFile + " to " + extractToDir; - if(!MMCZip::extractFile(modPath, mod.decompFile, extractToPath)) { + if (!MMCZip::extractFile(modPath, mod.decompFile, extractToPath)) { qWarning() << "Failed to extract" << mod.decompFile; return false; } } for (auto iter = toCopy.begin(); iter != toCopy.end(); iter++) { - auto &from = iter.key(); - auto &to = iter.value(); + auto& from = iter.key(); + auto& to = iter.value(); // If the file already exists, assume the mod is the correct copy - and remove // the copy from the Configs.zip @@ -966,7 +926,7 @@ bool PackInstallTask::extractMods( } FS::copy fileCopyOperation(from, to); - if(!fileCopyOperation()) { + if (!fileCopyOperation()) { qWarning() << "Failed to copy" << from << "to" << to; return false; } @@ -988,7 +948,7 @@ void PackInstallTask::install() components->buildingFromScratch(); // Use a component to add libraries BEFORE Minecraft - if(!createLibrariesComponent(instance.instanceRoot(), components)) { + if (!createLibrariesComponent(instance.instanceRoot(), components)) { emitFailed(tr("Failed to create libraries component")); return; } @@ -997,27 +957,24 @@ void PackInstallTask::install() components->setComponentVersion("net.minecraft", m_version.minecraft, true); // Loader - if(m_version.loader.type == QString("forge")) - { + if (m_version.loader.type == QString("forge")) { auto version = getVersionForLoader("net.minecraftforge"); - if(version == Q_NULLPTR) return; + if (version == Q_NULLPTR) + return; components->setComponentVersion("net.minecraftforge", version); - } - else if(m_version.loader.type == QString("fabric")) - { + } else if (m_version.loader.type == QString("fabric")) { auto version = getVersionForLoader("net.fabricmc.fabric-loader"); - if(version == Q_NULLPTR) return; + if (version == Q_NULLPTR) + return; components->setComponentVersion("net.fabricmc.fabric-loader", version); - } - else if(m_version.loader.type != QString()) - { + } else if (m_version.loader.type != QString()) { emitFailed(tr("Unknown loader type: ") + m_version.loader.type); return; } - for(const auto & componentUid : componentsToInstall.keys()) { + for (const auto& componentUid : componentsToInstall.keys()) { auto version = componentsToInstall.value(componentUid); components->setComponentVersion(componentUid, version->version()); } @@ -1026,7 +983,7 @@ void PackInstallTask::install() // Use a component to fill in the rest of the data // todo: use more detection - if(!createPackComponent(instance.instanceRoot(), components)) { + if (!createPackComponent(instance.instanceRoot(), components)) { emitFailed(tr("Failed to create pack component")); return; } @@ -1048,17 +1005,32 @@ static Meta::Version::Ptr getComponentVersion(const QString& uid, const QString& if (!vlist) return {}; - if (!vlist->isLoaded()) - vlist->load(Net::Mode::Online); + if (!vlist->isLoaded()) { + QEventLoop loadVersionLoop; + auto task = vlist->getLoadTask(); + QObject::connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit); + if (!task->isRunning()) + task->start(); + + loadVersionLoop.exec(); + } auto ver = vlist->getVersion(version); if (!ver) return {}; - if (!ver->isLoaded()) + if (!ver->isLoaded()) { + QEventLoop loadVersionLoop; ver->load(Net::Mode::Online); + auto task = ver->getCurrentTask(); + QObject::connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit); + if (!task->isRunning()) + task->start(); + + loadVersionLoop.exec(); + } return ver; } -} +} // namespace ATLauncher diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.h b/launcher/modplatform/atlauncher/ATLPackInstallTask.h index b82f523fe..ffc358fbb 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.h +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/modplatform/atlauncher/ATLPackManifest.cpp b/launcher/modplatform/atlauncher/ATLPackManifest.cpp index 5a458f4e2..9ff2f339e 100644 --- a/launcher/modplatform/atlauncher/ATLPackManifest.cpp +++ b/launcher/modplatform/atlauncher/ATLPackManifest.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -38,84 +38,67 @@ #include "Json.h" -static ATLauncher::DownloadType parseDownloadType(QString rawType) { - if(rawType == QString("server")) { +static ATLauncher::DownloadType parseDownloadType(QString rawType) +{ + if (rawType == QString("server")) { return ATLauncher::DownloadType::Server; - } - else if(rawType == QString("browser")) { + } else if (rawType == QString("browser")) { return ATLauncher::DownloadType::Browser; - } - else if(rawType == QString("direct")) { + } else if (rawType == QString("direct")) { return ATLauncher::DownloadType::Direct; } return ATLauncher::DownloadType::Unknown; } -static ATLauncher::ModType parseModType(QString rawType) { +static ATLauncher::ModType parseModType(QString rawType) +{ // See https://wiki.atlauncher.com/mod_types - if(rawType == QString("root")) { + if (rawType == QString("root")) { return ATLauncher::ModType::Root; - } - else if(rawType == QString("forge")) { + } else if (rawType == QString("forge")) { return ATLauncher::ModType::Forge; - } - else if(rawType == QString("jar")) { + } else if (rawType == QString("jar")) { return ATLauncher::ModType::Jar; - } - else if(rawType == QString("mods")) { + } else if (rawType == QString("mods")) { return ATLauncher::ModType::Mods; - } - else if(rawType == QString("flan")) { + } else if (rawType == QString("flan")) { return ATLauncher::ModType::Flan; - } - else if(rawType == QString("dependency") || rawType == QString("depandency")) { + } else if (rawType == QString("dependency") || rawType == QString("depandency")) { return ATLauncher::ModType::Dependency; - } - else if(rawType == QString("ic2lib")) { + } else if (rawType == QString("ic2lib")) { return ATLauncher::ModType::Ic2Lib; - } - else if(rawType == QString("denlib")) { + } else if (rawType == QString("denlib")) { return ATLauncher::ModType::DenLib; - } - else if(rawType == QString("coremods")) { + } else if (rawType == QString("coremods")) { return ATLauncher::ModType::Coremods; - } - else if(rawType == QString("mcpc")) { + } else if (rawType == QString("mcpc")) { return ATLauncher::ModType::MCPC; - } - else if(rawType == QString("plugins")) { + } else if (rawType == QString("plugins")) { return ATLauncher::ModType::Plugins; - } - else if(rawType == QString("extract")) { + } else if (rawType == QString("extract")) { return ATLauncher::ModType::Extract; - } - else if(rawType == QString("decomp")) { + } else if (rawType == QString("decomp")) { return ATLauncher::ModType::Decomp; - } - else if(rawType == QString("texturepack")) { + } else if (rawType == QString("texturepack")) { return ATLauncher::ModType::TexturePack; - } - else if(rawType == QString("resourcepack")) { + } else if (rawType == QString("resourcepack")) { return ATLauncher::ModType::ResourcePack; - } - else if(rawType == QString("shaderpack")) { + } else if (rawType == QString("shaderpack")) { return ATLauncher::ModType::ShaderPack; - } - else if(rawType == QString("texturepackextract")) { + } else if (rawType == QString("texturepackextract")) { return ATLauncher::ModType::TexturePackExtract; - } - else if(rawType == QString("resourcepackextract")) { + } else if (rawType == QString("resourcepackextract")) { return ATLauncher::ModType::ResourcePackExtract; - } - else if(rawType == QString("millenaire")) { + } else if (rawType == QString("millenaire")) { return ATLauncher::ModType::Millenaire; } return ATLauncher::ModType::Unknown; } -static void loadVersionLoader(ATLauncher::VersionLoader & p, QJsonObject & obj) { +static void loadVersionLoader(ATLauncher::VersionLoader& p, QJsonObject& obj) +{ p.type = Json::requireString(obj, "type"); p.choose = Json::ensureBoolean(obj, QString("choose"), false); @@ -134,7 +117,8 @@ static void loadVersionLoader(ATLauncher::VersionLoader & p, QJsonObject & obj) } } -static void loadVersionLibrary(ATLauncher::VersionLibrary & p, QJsonObject & obj) { +static void loadVersionLibrary(ATLauncher::VersionLibrary& p, QJsonObject& obj) +{ p.url = Json::requireString(obj, "url"); p.file = Json::requireString(obj, "file"); p.md5 = Json::requireString(obj, "md5"); @@ -145,12 +129,14 @@ static void loadVersionLibrary(ATLauncher::VersionLibrary & p, QJsonObject & obj p.server = Json::ensureString(obj, "server", ""); } -static void loadVersionConfigs(ATLauncher::VersionConfigs & p, QJsonObject & obj) { +static void loadVersionConfigs(ATLauncher::VersionConfigs& p, QJsonObject& obj) +{ p.filesize = Json::requireInteger(obj, "filesize"); p.sha1 = Json::requireString(obj, "sha1"); } -static void loadVersionMod(ATLauncher::VersionMod & p, QJsonObject & obj) { +static void loadVersionMod(ATLauncher::VersionMod& p, QJsonObject& obj) +{ p.name = Json::requireString(obj, "name"); p.version = Json::requireString(obj, "version"); p.url = Json::requireString(obj, "url"); @@ -167,18 +153,18 @@ static void loadVersionMod(ATLauncher::VersionMod & p, QJsonObject & obj) { // when the mod represents Forge. As there is little difference between "Jar" and "Forge, some // packs regretfully use "Jar". This will correct the type to "Forge" in these cases (as best // it can). - if(p.name == QString("Minecraft Forge") && p.type == ATLauncher::ModType::Jar) { + if (p.name == QString("Minecraft Forge") && p.type == ATLauncher::ModType::Jar) { p.type_raw = "forge"; p.type = ATLauncher::ModType::Forge; } - if(obj.contains("extractTo")) { + if (obj.contains("extractTo")) { p.extractTo_raw = Json::requireString(obj, "extractTo"); p.extractTo = parseModType(p.extractTo_raw); p.extractFolder = Json::ensureString(obj, "extractFolder", "").replace("%s%", "/"); } - if(obj.contains("decompType")) { + if (obj.contains("decompType")) { p.decompType_raw = Json::requireString(obj, "decompType"); p.decompType = parseModType(p.decompType_raw); p.decompFile = Json::requireString(obj, "decompFile"); @@ -191,7 +177,7 @@ static void loadVersionMod(ATLauncher::VersionMod & p, QJsonObject & obj) { p.hidden = Json::ensureBoolean(obj, QString("hidden"), false); p.library = Json::ensureBoolean(obj, QString("library"), false); p.group = Json::ensureString(obj, QString("group"), ""); - if(obj.contains("depends")) { + if (obj.contains("depends")) { auto dependsArr = Json::requireArray(obj, "depends"); for (const auto depends : dependsArr) { p.depends.append(Json::requireString(depends)); @@ -282,31 +268,30 @@ static void loadVersionDeletes(ATLauncher::VersionDeletes& d, QJsonObject& obj) } } -void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj) +void ATLauncher::loadVersion(PackVersion& v, QJsonObject& obj) { v.version = Json::requireString(obj, "version"); v.minecraft = Json::requireString(obj, "minecraft"); v.noConfigs = Json::ensureBoolean(obj, QString("noConfigs"), false); - if(obj.contains("mainClass")) { + if (obj.contains("mainClass")) { auto main = Json::requireObject(obj, "mainClass"); loadVersionMainClass(v.mainClass, main); } - if(obj.contains("extraArguments")) { + if (obj.contains("extraArguments")) { auto arguments = Json::requireObject(obj, "extraArguments"); loadVersionExtraArguments(v.extraArguments, arguments); } - if(obj.contains("loader")) { + if (obj.contains("loader")) { auto loader = Json::requireObject(obj, "loader"); loadVersionLoader(v.loader, loader); } - if(obj.contains("libraries")) { + if (obj.contains("libraries")) { auto libraries = Json::requireArray(obj, "libraries"); - for (const auto libraryRaw : libraries) - { + for (const auto libraryRaw : libraries) { auto libraryObj = Json::requireObject(libraryRaw); ATLauncher::VersionLibrary target; loadVersionLibrary(target, libraryObj); @@ -314,10 +299,9 @@ void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj) } } - if(obj.contains("mods")) { + if (obj.contains("mods")) { auto mods = Json::requireArray(obj, "mods"); - for (const auto modRaw : mods) - { + for (const auto modRaw : mods) { auto modObj = Json::requireObject(modRaw); ATLauncher::VersionMod mod; loadVersionMod(mod, modObj); @@ -325,18 +309,18 @@ void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj) } } - if(obj.contains("configs")) { + if (obj.contains("configs")) { auto configsObj = Json::requireObject(obj, "configs"); loadVersionConfigs(v.configs, configsObj); } auto colourObj = Json::ensureObject(obj, "colours"); - for (const auto &key : colourObj.keys()) { + for (const auto& key : colourObj.keys()) { v.colours[key] = Json::requireString(colourObj.value(key), "colour"); } auto warningsObj = Json::ensureObject(obj, "warnings"); - for (const auto &key : warningsObj.keys()) { + for (const auto& key : warningsObj.keys()) { v.warnings[key] = Json::requireString(warningsObj.value(key), "warning"); } diff --git a/launcher/modplatform/atlauncher/ATLPackManifest.h b/launcher/modplatform/atlauncher/ATLPackManifest.h index 571c976da..8db91087d 100644 --- a/launcher/modplatform/atlauncher/ATLPackManifest.h +++ b/launcher/modplatform/atlauncher/ATLPackManifest.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -40,17 +40,11 @@ #include #include -namespace ATLauncher -{ +namespace ATLauncher { -enum class PackType -{ - Public, - Private -}; +enum class PackType { Public, Private }; -enum class ModType -{ +enum class ModType { Root, Forge, Jar, @@ -73,16 +67,9 @@ enum class ModType Unknown }; -enum class DownloadType -{ - Server, - Browser, - Direct, - Unknown -}; +enum class DownloadType { Server, Browser, Direct, Unknown }; -struct VersionLoader -{ +struct VersionLoader { QString type; bool latest; bool recommended; @@ -91,8 +78,7 @@ struct VersionLoader QString version; }; -struct VersionLibrary -{ +struct VersionLibrary { QString url; QString file; QString server; @@ -101,8 +87,7 @@ struct VersionLibrary QString download_raw; }; -struct VersionMod -{ +struct VersionMod { QString name; QString version; QString url; @@ -138,14 +123,12 @@ struct VersionMod bool effectively_hidden; }; -struct VersionConfigs -{ +struct VersionConfigs { int filesize; QString sha1; }; -struct VersionMessages -{ +struct VersionMessages { QString install; QString update; }; @@ -170,20 +153,17 @@ struct VersionDeletes { QVector folders; }; -struct PackVersionMainClass -{ +struct PackVersionMainClass { QString mainClass; QString depends; }; -struct PackVersionExtraArguments -{ +struct PackVersionExtraArguments { QString arguments; QString depends; }; -struct PackVersion -{ +struct PackVersion { QString version; QString minecraft; bool noConfigs; @@ -203,6 +183,6 @@ struct PackVersion VersionDeletes deletes; }; -void loadVersion(PackVersion & v, QJsonObject & obj); +void loadVersion(PackVersion& v, QJsonObject& obj); -} +} // namespace ATLauncher diff --git a/launcher/modplatform/atlauncher/ATLShareCode.cpp b/launcher/modplatform/atlauncher/ATLShareCode.cpp index 59030c873..800eac538 100644 --- a/launcher/modplatform/atlauncher/ATLShareCode.cpp +++ b/launcher/modplatform/atlauncher/ATLShareCode.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -57,4 +57,4 @@ void loadShareCodeResponse(ShareCodeResponse& r, QJsonObject& obj) } } -} +} // namespace ATLauncher diff --git a/launcher/modplatform/atlauncher/ATLShareCode.h b/launcher/modplatform/atlauncher/ATLShareCode.h index 88c30c98e..531945bce 100644 --- a/launcher/modplatform/atlauncher/ATLShareCode.h +++ b/launcher/modplatform/atlauncher/ATLShareCode.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -18,9 +18,9 @@ #pragma once +#include #include #include -#include namespace ATLauncher { @@ -44,4 +44,4 @@ struct ShareCodeResponse { void loadShareCodeResponse(ShareCodeResponse& r, QJsonObject& obj); -} +} // namespace ATLauncher diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index ce7a60551..860d7340f 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -1,6 +1,8 @@ #include "FileResolvingTask.h" #include "Json.h" +#include "net/ApiDownload.h" +#include "net/ApiUpload.h" #include "net/Upload.h" #include "modplatform/modrinth/ModrinthPackIndex.h" @@ -21,6 +23,10 @@ bool Flame::FileResolvingTask::abort() void Flame::FileResolvingTask::executeTask() { + if (m_toProcess.files.isEmpty()) { // no file to resolve so leave it empty and emit success immediately + emitSucceeded(); + return; + } setStatus(tr("Resolving mod IDs...")); setProgress(0, 3); m_dljob.reset(new NetJob("Mod id resolver", m_network)); @@ -34,7 +40,7 @@ void Flame::FileResolvingTask::executeTask() return l; })); QByteArray data = Json::toText(object); - auto dl = Net::Upload::makeByteArray(QUrl("https://api.curseforge.com/v1/mods/files"), result, data); + auto dl = Net::ApiUpload::makeByteArray(QUrl("https://api.curseforge.com/v1/mods/files"), result, data); m_dljob->addNetAction(dl); auto step_progress = std::make_shared(); @@ -48,7 +54,7 @@ void Flame::FileResolvingTask::executeTask() stepProgress(*step_progress); emitFailed(reason); }); - connect(m_dljob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propogateStepProgress); + connect(m_dljob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress); connect(m_dljob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) { qDebug() << "Resolve slug progress" << current << total; step_progress->update(current, total); @@ -89,13 +95,13 @@ void Flame::FileResolvingTask::netJobFinished() auto& out = m_toProcess.files[fileid]; try { out.parseFromObject(Json::requireObject(file)); - } catch (const JSONValidationError& e) { + } catch ([[maybe_unused]] const JSONValidationError& e) { qDebug() << "Blocked mod on curseforge" << out.fileName; auto hash = out.hash; if (!hash.isEmpty()) { auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash); auto output = std::make_shared(); - auto dl = Net::Download::makeByteArray(QUrl(url), output); + auto dl = Net::ApiDownload::makeByteArray(QUrl(url), output); QObject::connect(dl.get(), &Net::Download::succeeded, [&out]() { out.resolved = true; }); m_checkJob->addNetAction(dl); @@ -114,7 +120,7 @@ void Flame::FileResolvingTask::netJobFinished() stepProgress(*step_progress); emitFailed(reason); }); - connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propogateStepProgress); + connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress); connect(m_checkJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) { qDebug() << "Resolve slug progress" << current << total; step_progress->update(current, total); @@ -128,12 +134,13 @@ void Flame::FileResolvingTask::netJobFinished() m_checkJob->start(); } -void Flame::FileResolvingTask::modrinthCheckFinished() { +void Flame::FileResolvingTask::modrinthCheckFinished() +{ setProgress(2, 3); qDebug() << "Finished with blocked mods : " << blockedProjects.size(); for (auto it = blockedProjects.keyBegin(); it != blockedProjects.keyEnd(); it++) { - auto &out = *it; + auto& out = *it; auto bytes = blockedProjects[out]; if (!out->resolved) { continue; @@ -153,28 +160,26 @@ void Flame::FileResolvingTask::modrinthCheckFinished() { out->resolved = false; } } - //copy to an output list and filter out projects found on modrinth + // copy to an output list and filter out projects found on modrinth auto block = std::make_shared>(); auto it = blockedProjects.keys(); - std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File *f) { - return !f->resolved; - }); - //Display not found mods early + std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File* f) { return !f->resolved; }); + // Display not found mods early if (!block->empty()) { - //blocked mods found, we need the slug for displaying.... we need another job :D ! + // blocked mods found, we need the slug for displaying.... we need another job :D ! m_slugJob.reset(new NetJob("Slug Job", m_network)); int index = 0; for (auto mod : *block) { auto projectId = mod->projectId; auto output = std::make_shared(); auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId); - auto dl = Net::Download::makeByteArray(url, output); + auto dl = Net::ApiDownload::makeByteArray(url, output); qDebug() << "Fetching url slug for file:" << mod->fileName; QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() { auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done auto json = QJsonDocument::fromJson(*output); - auto base = Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json),"data"),"links"), - "websiteUrl"); + auto base = + Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json), "data"), "links"), "websiteUrl"); auto link = QString("%1/download/%2").arg(base, QString::number(mod->fileId)); mod->websiteUrl = link; }); @@ -192,7 +197,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished() { stepProgress(*step_progress); emitFailed(reason); }); - connect(m_slugJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propogateStepProgress); + connect(m_slugJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress); connect(m_slugJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) { qDebug() << "Resolve slug progress" << current << total; step_progress->update(current, total); diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index 5b0b1d8b9..74d7db975 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -8,6 +8,8 @@ #include "Application.h" #include "BuildConfig.h" #include "Json.h" +#include "net/ApiDownload.h" +#include "net/ApiUpload.h" #include "net/NetJob.h" #include "net/Upload.h" @@ -26,7 +28,7 @@ Task::Ptr FlameAPI::matchFingerprints(const QList& fingerprints, std::shar QJsonDocument body(body_obj); auto body_raw = body.toJson(); - netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/fingerprints"), response, body_raw)); + netJob->addNetAction(Net::ApiUpload::makeByteArray(QString("https://api.curseforge.com/v1/fingerprints"), response, body_raw)); return netJob; } @@ -38,7 +40,7 @@ auto FlameAPI::getModFileChangelog(int modId, int fileId) -> QString auto netJob = makeShared(QString("Flame::FileChangelog"), APPLICATION->network()); auto response = std::make_shared(); - netJob->addNetAction(Net::Download::makeByteArray( + netJob->addNetAction(Net::ApiDownload::makeByteArray( QString("https://api.curseforge.com/v1/mods/%1/files/%2/changelog") .arg(QString::fromStdString(std::to_string(modId)), QString::fromStdString(std::to_string(fileId))), response)); @@ -73,8 +75,8 @@ auto FlameAPI::getModDescription(int modId) -> QString auto netJob = makeShared(QString("Flame::ModDescription"), APPLICATION->network()); auto response = std::make_shared(); - netJob->addNetAction( - Net::Download::makeByteArray(QString("https://api.curseforge.com/v1/mods/%1/description").arg(QString::number(modId)), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray( + QString("https://api.curseforge.com/v1/mods/%1/description").arg(QString::number(modId)), response)); QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &description] { QJsonParseError parse_error{}; @@ -113,7 +115,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe auto response = std::make_shared(); ModPlatform::IndexedVersion ver; - netJob->addNetAction(Net::Download::makeByteArray(versions_url, response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response)); QObject::connect(netJob.get(), &NetJob::succeeded, [response, args, &ver] { QJsonParseError parse_error{}; @@ -173,7 +175,7 @@ Task::Ptr FlameAPI::getProjects(QStringList addonIds, std::shared_ptraddNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods"), response, body_raw)); + netJob->addNetAction(Net::ApiUpload::makeByteArray(QString("https://api.curseforge.com/v1/mods"), response, body_raw)); QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; }); @@ -195,13 +197,24 @@ Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, std::shared_ptraddNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods/files"), response, body_raw)); + netJob->addNetAction(Net::ApiUpload::makeByteArray(QString("https://api.curseforge.com/v1/mods/files"), response, body_raw)); QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; }); return netJob; } +Task::Ptr FlameAPI::getFile(const QString& addonId, const QString& fileId, std::shared_ptr response) const +{ + auto netJob = makeShared(QString("Flame::GetFile"), APPLICATION->network()); + netJob->addNetAction( + Net::ApiDownload::makeByteArray(QUrl(QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(addonId, fileId)), response)); + + QObject::connect(netJob.get(), &NetJob::failed, [addonId, fileId] { qDebug() << "Flame API file failure" << addonId << fileId; }); + + return netJob; +} + // https://docs.curseforge.com/?python#tocS_ModsSearchSortField static QList s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") }, { 2, "Popularity", QObject::tr("Sort by Popularity") }, diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index 49bc316f2..281c0a099 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -20,6 +20,7 @@ class FlameAPI : public NetworkResourceAPI { Task::Ptr getProjects(QStringList addonIds, std::shared_ptr response) const override; Task::Ptr matchFingerprints(const QList& fingerprints, std::shared_ptr response); Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr response) const; + Task::Ptr getFile(const QString& addonId, const QString& fileId, std::shared_ptr response) const; [[nodiscard]] auto getSortingMethods() const -> QList override; diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index a2628e34c..e10fedc3c 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -13,6 +13,8 @@ #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourceFolderModel.h" +#include "net/ApiDownload.h" + static FlameAPI api; bool FlameCheckUpdate::abort() @@ -33,7 +35,7 @@ ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info) auto response = std::make_shared(); auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(ver_info.addonId.toString()); - auto dl = Net::Download::makeByteArray(url, response); + auto dl = Net::ApiDownload::makeByteArray(url, response); get_project_job->addNetAction(dl); QObject::connect(get_project_job, &NetJob::succeeded, [response, &pack]() { @@ -77,7 +79,7 @@ ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId) auto response = std::make_shared(); auto url = QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(QString::number(addonId), QString::number(fileId)); - auto dl = Net::Download::makeByteArray(url, response); + auto dl = Net::ApiDownload::makeByteArray(url, response); get_file_info_job->addNetAction(dl); QObject::connect(get_file_info_job, &NetJob::succeeded, [response, &ver]() { diff --git a/launcher/modplatform/flame/FlameCheckUpdate.h b/launcher/modplatform/flame/FlameCheckUpdate.h index 4a98d684f..e3465d7e2 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.h +++ b/launcher/modplatform/flame/FlameCheckUpdate.h @@ -8,7 +8,10 @@ class FlameCheckUpdate : public CheckUpdateTask { Q_OBJECT public: - FlameCheckUpdate(QList& mods, std::list& mcVersions, std::optional loaders, std::shared_ptr mods_folder) + FlameCheckUpdate(QList& mods, + std::list& mcVersions, + std::optional loaders, + std::shared_ptr mods_folder) : CheckUpdateTask(mods, mcVersions, loaders, mods_folder) {} diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index b57db288a..9fe8d4865 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -57,14 +57,11 @@ #include #include +#include "meta/Index.h" +#include "meta/VersionList.h" #include "minecraft/World.h" #include "minecraft/mod/tasks/LocalResourceParse.h" - - -const static QMap forgemap = { { "1.2.5", "3.4.9.171" }, - { "1.4.2", "6.0.1.355" }, - { "1.4.7", "6.6.2.534" }, - { "1.5.2", "7.8.1.737" } }; +#include "net/ApiDownload.h" static const FlameAPI api; @@ -259,6 +256,56 @@ bool FlameCreationTask::updateInstance() return false; } +QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType, QString loaderVersion, QString mcVersion) +{ + if (loaderVersion == "recommended") { + auto vlist = APPLICATION->metadataIndex()->get(uid); + if (!vlist) { + setError(tr("Failed to get local metadata index for %1").arg(uid)); + return {}; + } + + if (!vlist->isLoaded()) { + QEventLoop loadVersionLoop; + auto task = vlist->getLoadTask(); + connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit); + if (!task->isRunning()) + task->start(); + + loadVersionLoop.exec(); + } + + for (auto version : vlist->versions()) { + // first recommended build we find, we use. + if (!version->isRecommended()) + continue; + auto reqs = version->requiredSet(); + + // filter by minecraft version, if the loader depends on a certain version. + // not all mod loaders depend on a given Minecraft version, so we won't do this + // filtering for those loaders. + if (loaderType == "forge") { + auto iter = std::find_if(reqs.begin(), reqs.end(), [mcVersion](const Meta::Require& req) { + return req.uid == "net.minecraft" && req.equalsVersion == mcVersion; + }); + if (iter == reqs.end()) + continue; + } + return version->descriptor(); + } + + setError(tr("Failed to find version for %1 loader").arg(loaderType)); + return {}; + } + + if (loaderVersion.isEmpty()) { + emitFailed(tr("No loader version set for modpack!")); + return {}; + } + + return loaderVersion; +} + bool FlameCreationTask::createInstance() { QEventLoop loop; @@ -297,22 +344,29 @@ bool FlameCreationTask::createInstance() } } - QString forgeVersion; - QString fabricVersion; - // TODO: is Quilt relevant here? + QString loaderType; + QString loaderUid; + QString loaderVersion; + for (auto& loader : m_pack.minecraft.modLoaders) { auto id = loader.id; if (id.startsWith("forge-")) { id.remove("forge-"); - forgeVersion = id; - continue; - } - if (id.startsWith("fabric-")) { + loaderType = "forge"; + loaderUid = "net.minecraftforge"; + } else if (id.startsWith("fabric-")) { id.remove("fabric-"); - fabricVersion = id; + loaderType = "fabric"; + loaderUid = "net.fabricmc.fabric-loader"; + } else if (id.startsWith("quilt-")) { + id.remove("quilt-"); + loaderType = "quilt"; + loaderUid = "org.quiltmc.quilt-loader"; + } else { + logWarning(tr("Unknown mod loader in manifest: %1").arg(id)); continue; } - logWarning(tr("Unknown mod loader in manifest: %1").arg(id)); + loaderVersion = id; } QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); @@ -329,19 +383,12 @@ bool FlameCreationTask::createInstance() auto components = instance.getPackProfile(); components->buildingFromScratch(); components->setComponentVersion("net.minecraft", mcVersion, true); - if (!forgeVersion.isEmpty()) { - // FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata. - if (forgeVersion == "recommended") { - if (forgemap.contains(mcVersion)) { - forgeVersion = forgemap[mcVersion]; - } else { - logWarning(tr("Could not map recommended Forge version for Minecraft %1").arg(mcVersion)); - } - } - components->setComponentVersion("net.minecraftforge", forgeVersion); + if (!loaderType.isEmpty()) { + auto version = getVersionForLoader(loaderUid, loaderType, loaderVersion, mcVersion); + if (version.isEmpty()) + return false; + components->setComponentVersion(loaderUid, version); } - if (!fabricVersion.isEmpty()) - components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion); if (m_instIcon != "default") { instance.setIconKey(m_instIcon); @@ -386,7 +433,7 @@ bool FlameCreationTask::createInstance() }); connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::progress, this, &FlameCreationTask::setProgress); connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::status, this, &FlameCreationTask::setStatus); - connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::stepProgress, this, &FlameCreationTask::propogateStepProgress); + connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::stepProgress, this, &FlameCreationTask::propagateStepProgress); connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::details, this, &FlameCreationTask::setDetails); m_mod_id_resolver->start(); @@ -477,7 +524,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) case Flame::File::Type::Mod: { if (!result.url.isEmpty()) { qDebug() << "Will download" << result.url << "to" << path; - auto dl = Net::Download::makeFile(result.url, path); + auto dl = Net::ApiDownload::makeFile(result.url, path); m_files_job->addNetAction(dl); } break; @@ -502,11 +549,11 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) m_files_job.reset(); setError(reason); }); - connect(m_files_job.get(), &NetJob::progress, this, [this](qint64 current, qint64 total){ + connect(m_files_job.get(), &NetJob::progress, this, [this](qint64 current, qint64 total) { setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); setProgress(current, total); }); - connect(m_files_job.get(), &NetJob::stepProgress, this, &FlameCreationTask::propogateStepProgress); + connect(m_files_job.get(), &NetJob::stepProgress, this, &FlameCreationTask::propagateStepProgress); connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit); setStatus(tr("Downloading mods...")); @@ -545,7 +592,6 @@ void FlameCreationTask::copyBlockedMods(QList const& blocked_mods) setAbortable(true); } - void FlameCreationTask::validateZIPResouces() { qDebug() << "Validating whether resources stored as .zip are in the right place"; @@ -569,7 +615,7 @@ void FlameCreationTask::validateZIPResouces() return localPath; }; - auto installWorld = [this](QString worldPath){ + auto installWorld = [this](QString worldPath) { qDebug() << "Installing World from" << worldPath; QFileInfo worldFileInfo(worldPath); World w(worldFileInfo); @@ -586,29 +632,29 @@ void FlameCreationTask::validateZIPResouces() QString worldPath; switch (type) { - case PackedResourceType::Mod : + case PackedResourceType::Mod: validatePath(fileName, targetFolder, "mods"); break; - case PackedResourceType::ResourcePack : + case PackedResourceType::ResourcePack: validatePath(fileName, targetFolder, "resourcepacks"); break; - case PackedResourceType::TexturePack : + case PackedResourceType::TexturePack: validatePath(fileName, targetFolder, "texturepacks"); break; - case PackedResourceType::DataPack : + case PackedResourceType::DataPack: validatePath(fileName, targetFolder, "datapacks"); break; - case PackedResourceType::ShaderPack : + case PackedResourceType::ShaderPack: // in theroy flame API can't do this but who knows, that *may* change ? // better to handle it if it *does* occure in the future validatePath(fileName, targetFolder, "shaderpacks"); break; - case PackedResourceType::WorldSave : + case PackedResourceType::WorldSave: worldPath = validatePath(fileName, targetFolder, "saves"); installWorld(worldPath); break; - case PackedResourceType::UNKNOWN : - default : + case PackedResourceType::UNKNOWN: + default: qDebug() << "Can't Identify" << fileName << "at" << localPath << ", leaving it where it is."; break; } diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.h b/launcher/modplatform/flame/FlameInstanceCreationTask.h index 0ae4735bf..603d3693e 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.h +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.h @@ -57,10 +57,7 @@ class FlameCreationTask final : public InstanceCreationTask { QString id, QString version_id, QString original_instance_id = {}) - : InstanceCreationTask() - , m_parent(parent) - , m_managed_id(std::move(id)) - , m_managed_version_id(std::move(version_id)) + : InstanceCreationTask(), m_parent(parent), m_managed_id(std::move(id)), m_managed_version_id(std::move(version_id)) { setStagingPath(staging_path); setParentSettings(global_settings); @@ -78,6 +75,7 @@ class FlameCreationTask final : public InstanceCreationTask { void setupDownloadJob(QEventLoop&); void copyBlockedMods(QList const& blocked_mods); void validateZIPResouces(); + QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion); private: QWidget* m_parent = nullptr; diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 227ce4898..19803cf6d 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -54,7 +54,7 @@ void FlameMod::loadURLs(ModPlatform::IndexedPack& pack, QJsonObject& obj) pack.extraDataLoaded = true; } -void FlameMod::loadBody(ModPlatform::IndexedPack& pack, QJsonObject& obj) +void FlameMod::loadBody(ModPlatform::IndexedPack& pack, [[maybe_unused]] QJsonObject& obj) { pack.extraData.body = api.getModDescription(pack.addonId.toInt()); @@ -75,7 +75,7 @@ static QString enumToString(int hash_algorithm) void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, - const shared_qobject_ptr& network, + [[maybe_unused]] const shared_qobject_ptr& network, const BaseInstance* inst) { QVector unsortedVersions; @@ -193,4 +193,4 @@ ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform:: }; std::sort(versions.begin(), versions.end(), orderSortPredicate); return versions.front(); -}; +} diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp index ac0da2142..f5f3af372 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.cpp +++ b/launcher/modplatform/flame/FlamePackExportTask.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "Json.h" #include "MMCZip.h" @@ -64,20 +65,11 @@ void FlamePackExportTask::executeTask() bool FlamePackExportTask::abort() { - if (task != nullptr) { + if (task) { task->abort(); - task = nullptr; emitAborted(); return true; } - - if (buildZipFuture.isRunning()) { - buildZipFuture.cancel(); - // NOTE: Here we don't do `emitAborted()` because it will be done when `buildZipFuture` actually cancels, which may not occur - // immediately. - return true; - } - return false; } @@ -166,7 +158,7 @@ void FlamePackExportTask::collectHashes() stepProgress(*progressStep); emitFailed(reason); }); - connect(hashingTask.get(), &Task::stepProgress, this, &FlamePackExportTask::propogateStepProgress); + connect(hashingTask.get(), &Task::stepProgress, this, &FlamePackExportTask::propagateStepProgress); connect(hashingTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { progressStep->update(current, total); @@ -336,89 +328,40 @@ void FlamePackExportTask::buildZip() setStatus(tr("Adding files...")); setProgress(4, 5); - buildZipFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { - QuaZip zip(output); - if (!zip.open(QuaZip::mdCreate)) { - QFile::remove(output); - return BuildZipResult(tr("Could not create file")); - } + auto zipTask = makeShared(output, gameRoot, files, "overrides/", true); + zipTask->addExtraFile("manifest.json", generateIndex()); + zipTask->addExtraFile("modlist.html", generateHTML()); - if (buildZipFuture.isCanceled()) - return BuildZipResult(); + QStringList exclude; + std::transform(resolvedFiles.keyBegin(), resolvedFiles.keyEnd(), std::back_insert_iterator(exclude), + [this](QString file) { return gameRoot.relativeFilePath(file); }); + zipTask->setExcludeFiles(exclude); - QuaZipFile indexFile(&zip); - if (!indexFile.open(QIODevice::WriteOnly, QuaZipNewInfo("manifest.json"))) { - QFile::remove(output); - return BuildZipResult(tr("Could not create index")); - } - indexFile.write(generateIndex()); - - QuaZipFile modlist(&zip); - if (!modlist.open(QIODevice::WriteOnly, QuaZipNewInfo("modlist.html"))) { - QFile::remove(output); - return BuildZipResult(tr("Could not create index")); - } - QString content = ""; - for (auto mod : resolvedFiles) { - if (mod.isMod) { - content += QString(TEMPLATE) - .replace("{name}", mod.name.toHtmlEscaped()) - .replace("{url}", ModPlatform::getMetaURL(ModPlatform::ResourceProvider::FLAME, mod.addonId).toHtmlEscaped()) - .replace("{authors}", !mod.authors.isEmpty() ? QString(" (by %1)").arg(mod.authors).toHtmlEscaped() : ""); - } - } - content = "
    " + content + "
"; - modlist.write(content.toUtf8()); - - auto progressStep = std::make_shared(); - - size_t progress = 0; - for (const QFileInfo& file : files) { - if (buildZipFuture.isCanceled()) { - QFile::remove(output); - progressStep->state = TaskStepState::Failed; - stepProgress(*progressStep); - return BuildZipResult(); - } - progressStep->update(progress, files.length()); - stepProgress(*progressStep); - - const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath()); - if (!resolvedFiles.contains(file.absoluteFilePath()) && - !JlCompress::compressFile(&zip, file.absoluteFilePath(), "overrides/" + relative)) { - QFile::remove(output); - return BuildZipResult(tr("Could not read and compress %1").arg(relative)); - } - progress++; - } - - zip.close(); - - if (zip.getZipError() != 0) { - QFile::remove(output); - progressStep->state = TaskStepState::Failed; - stepProgress(*progressStep); - return BuildZipResult(tr("A zip error occurred")); - } + auto progressStep = std::make_shared(); + connect(zipTask.get(), &Task::finished, this, [this, progressStep] { progressStep->state = TaskStepState::Succeeded; stepProgress(*progressStep); - return BuildZipResult(); }); - connect(&buildZipWatcher, &QFutureWatcher::finished, this, &FlamePackExportTask::finish); - buildZipWatcher.setFuture(buildZipFuture); -} -void FlamePackExportTask::finish() -{ - if (buildZipFuture.isCanceled()) - emitAborted(); - else { - const BuildZipResult result = buildZipFuture.result(); - if (result.has_value()) - emitFailed(result.value()); - else - emitSucceeded(); - } + connect(zipTask.get(), &Task::succeeded, this, &FlamePackExportTask::emitSucceeded); + connect(zipTask.get(), &Task::aborted, this, &FlamePackExportTask::emitAborted); + connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { + progressStep->state = TaskStepState::Failed; + stepProgress(*progressStep); + emitFailed(reason); + }); + connect(zipTask.get(), &Task::stepProgress, this, &FlamePackExportTask::propagateStepProgress); + + connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { + progressStep->update(current, total); + stepProgress(*progressStep); + }); + connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) { + progressStep->status = status; + stepProgress(*progressStep); + }); + task.reset(zipTask); + zipTask->start(); } QByteArray FlamePackExportTask::generateIndex() @@ -471,3 +414,18 @@ QByteArray FlamePackExportTask::generateIndex() return QJsonDocument(obj).toJson(QJsonDocument::Compact); } + +QByteArray FlamePackExportTask::generateHTML() +{ + QString content = ""; + for (auto mod : resolvedFiles) { + if (mod.isMod) { + content += QString(TEMPLATE) + .replace("{name}", mod.name.toHtmlEscaped()) + .replace("{url}", ModPlatform::getMetaURL(ModPlatform::ResourceProvider::FLAME, mod.addonId).toHtmlEscaped()) + .replace("{authors}", !mod.authors.isEmpty() ? QString(" (by %1)").arg(mod.authors).toHtmlEscaped() : ""); + } + } + content = "
    " + content + "
"; + return content.toUtf8(); +} \ No newline at end of file diff --git a/launcher/modplatform/flame/FlamePackExportTask.h b/launcher/modplatform/flame/FlamePackExportTask.h index 3dee0a7ea..d3dc6281e 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.h +++ b/launcher/modplatform/flame/FlamePackExportTask.h @@ -19,8 +19,6 @@ #pragma once -#include -#include #include "BaseInstance.h" #include "MMCZip.h" #include "minecraft/MinecraftInstance.h" @@ -52,7 +50,6 @@ class FlamePackExportTask : public Task { const QString output; const MMCZip::FilterFunction filter; - typedef std::optional BuildZipResult; struct ResolvedFile { int addonId; int version; @@ -76,15 +73,13 @@ class FlamePackExportTask : public Task { QMap pendingHashes{}; QMap resolvedFiles{}; Task::Ptr task; - QFuture buildZipFuture; - QFutureWatcher buildZipWatcher; void collectFiles(); void collectHashes(); void makeApiRequest(); void getProjectsInfo(); void buildZip(); - void finish(); QByteArray generateIndex(); + QByteArray generateHTML(); }; diff --git a/launcher/modplatform/flame/FlamePackIndex.cpp b/launcher/modplatform/flame/FlamePackIndex.cpp index ad48b7b6a..21835a543 100644 --- a/launcher/modplatform/flame/FlamePackIndex.cpp +++ b/launcher/modplatform/flame/FlamePackIndex.cpp @@ -54,23 +54,22 @@ void Flame::loadIndexedInfo(IndexedPack& pack, QJsonObject& obj) auto links_obj = Json::ensureObject(obj, "links"); pack.extra.websiteUrl = Json::ensureString(links_obj, "websiteUrl"); - if(pack.extra.websiteUrl.endsWith('/')) + if (pack.extra.websiteUrl.endsWith('/')) pack.extra.websiteUrl.chop(1); pack.extra.issuesUrl = Json::ensureString(links_obj, "issuesUrl"); - if(pack.extra.issuesUrl.endsWith('/')) + if (pack.extra.issuesUrl.endsWith('/')) pack.extra.issuesUrl.chop(1); pack.extra.sourceUrl = Json::ensureString(links_obj, "sourceUrl"); - if(pack.extra.sourceUrl.endsWith('/')) + if (pack.extra.sourceUrl.endsWith('/')) pack.extra.sourceUrl.chop(1); pack.extra.wikiUrl = Json::ensureString(links_obj, "wikiUrl"); - if(pack.extra.wikiUrl.endsWith('/')) + if (pack.extra.wikiUrl.endsWith('/')) pack.extra.wikiUrl.chop(1); pack.extraInfoLoaded = true; - } void Flame::loadIndexedPackVersions(Flame::IndexedPack& pack, QJsonArray& arr) diff --git a/launcher/modplatform/flame/PackManifest.cpp b/launcher/modplatform/flame/PackManifest.cpp index ee4d07662..40a523d31 100644 --- a/launcher/modplatform/flame/PackManifest.cpp +++ b/launcher/modplatform/flame/PackManifest.cpp @@ -46,7 +46,7 @@ static void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest) Flame::File file; loadFileV1(file, obj); - pack.files.insert(file.fileId,file); + pack.files.insert(file.fileId, file); } pack.overrides = Json::ensureString(manifest, "overrides", "overrides"); @@ -69,7 +69,7 @@ void Flame::loadManifest(Flame::Manifest& m, const QString& filepath) loadManifestV1(m, obj); } -bool Flame::File::parseFromObject(const QJsonObject& obj, bool throw_on_blocked) +bool Flame::File::parseFromObject(const QJsonObject& obj, bool throw_on_blocked) { fileName = Json::requireString(obj, "fileName"); // This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience @@ -81,7 +81,7 @@ bool Flame::File::parseFromObject(const QJsonObject& obj, bool throw_on_blocked // get the hash hash = QString(); auto hashes = Json::ensureArray(obj, "hashes"); - for(QJsonValueRef item : hashes) { + for (QJsonValueRef item : hashes) { auto hobj = Json::requireObject(item); auto algo = Json::requireInteger(hobj, "algo"); auto value = Json::requireString(hobj, "value"); @@ -90,7 +90,6 @@ bool Flame::File::parseFromObject(const QJsonObject& obj, bool throw_on_blocked } } - // may throw, if the project is blocked QString rawUrl = Json::ensureString(obj, "downloadUrl"); url = QUrl(rawUrl, QUrl::TolerantMode); diff --git a/launcher/modplatform/flame/PackManifest.h b/launcher/modplatform/flame/PackManifest.h index 0b7461d84..854cdbc41 100644 --- a/launcher/modplatform/flame/PackManifest.h +++ b/launcher/modplatform/flame/PackManifest.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -41,10 +41,8 @@ #include #include -namespace Flame -{ -struct File -{ +namespace Flame { +struct File { // NOTE: throws JSONValidationError bool parseFromObject(const QJsonObject& object, bool throw_on_blocked = true); @@ -61,45 +59,33 @@ struct File QString fileName; QUrl url; QString targetFolder = QStringLiteral("mods"); - enum class Type - { - Unknown, - Folder, - Ctoc, - SingleFile, - Cmod2, - Modpack, - Mod - } type = Type::Mod; + enum class Type { Unknown, Folder, Ctoc, SingleFile, Cmod2, Modpack, Mod } type = Type::Mod; }; -struct Modloader -{ +struct Modloader { QString id; bool primary = false; }; -struct Minecraft -{ +struct Minecraft { QString version; QString libraries; QVector modLoaders; }; -struct Manifest -{ +struct Manifest { QString manifestType; int manifestVersion = 0; Flame::Minecraft minecraft; QString name; QString version; QString author; - //File id -> File - QMap files; + // File id -> File + QMap files; QString overrides; bool is_loaded = false; }; -void loadManifest(Flame::Manifest & m, const QString &filepath); -} +void loadManifest(Flame::Manifest& m, const QString& filepath); +} // namespace Flame diff --git a/launcher/modplatform/helpers/ExportToModList.cpp b/launcher/modplatform/helpers/ExportToModList.cpp new file mode 100644 index 000000000..1f01c4a89 --- /dev/null +++ b/launcher/modplatform/helpers/ExportToModList.cpp @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "ExportToModList.h" +#include +#include +#include + +namespace ExportToModList { +QString toHTML(QList mods, OptionalData extraData) +{ + QStringList lines; + for (auto mod : mods) { + auto meta = mod->metadata(); + auto modName = mod->name().toHtmlEscaped(); + if (extraData & Url) { + auto url = mod->metaurl().toHtmlEscaped(); + if (!url.isEmpty()) + modName = QString("%2").arg(url, modName); + } + auto line = modName; + if (extraData & Version) { + auto ver = mod->version(); + if (ver.isEmpty() && meta != nullptr) + ver = meta->version().toString(); + if (!ver.isEmpty()) + line += QString(" [%1]").arg(ver.toHtmlEscaped()); + } + if (extraData & Authors && !mod->authors().isEmpty()) + line += " by " + mod->authors().join(", ").toHtmlEscaped(); + lines.append(QString("
  • %1
  • ").arg(line)); + } + return QString("
      \n\t%1\n
    ").arg(lines.join("\n\t")); +} + +QString toMarkdown(QList mods, OptionalData extraData) +{ + QStringList lines; + for (auto mod : mods) { + auto meta = mod->metadata(); + auto modName = mod->name(); + if (extraData & Url) { + auto url = mod->metaurl(); + if (!url.isEmpty()) + modName = QString("[%1](%2)").arg(modName, url); + } + auto line = modName; + if (extraData & Version) { + auto ver = mod->version(); + if (ver.isEmpty() && meta != nullptr) + ver = meta->version().toString(); + if (!ver.isEmpty()) + line += QString(" [%1]").arg(ver); + } + if (extraData & Authors && !mod->authors().isEmpty()) + line += " by " + mod->authors().join(", "); + lines << "- " + line; + } + return lines.join("\n"); +} + +QString toPlainTXT(QList mods, OptionalData extraData) +{ + QStringList lines; + for (auto mod : mods) { + auto meta = mod->metadata(); + auto modName = mod->name(); + + auto line = modName; + if (extraData & Url) { + auto url = mod->metaurl(); + if (!url.isEmpty()) + line += QString(" (%1)").arg(url); + } + if (extraData & Version) { + auto ver = mod->version(); + if (ver.isEmpty() && meta != nullptr) + ver = meta->version().toString(); + if (!ver.isEmpty()) + line += QString(" [%1]").arg(ver); + } + if (extraData & Authors && !mod->authors().isEmpty()) + line += " by " + mod->authors().join(", "); + lines << line; + } + return lines.join("\n"); +} + +QString toJSON(QList mods, OptionalData extraData) +{ + QJsonArray lines; + for (auto mod : mods) { + auto meta = mod->metadata(); + auto modName = mod->name(); + QJsonObject line; + line["name"] = modName; + if (extraData & Url) { + auto url = mod->metaurl(); + if (!url.isEmpty()) + line["url"] = url; + } + if (extraData & Version) { + auto ver = mod->version(); + if (ver.isEmpty() && meta != nullptr) + ver = meta->version().toString(); + if (!ver.isEmpty()) + line["version"] = ver; + } + if (extraData & Authors && !mod->authors().isEmpty()) + line["authors"] = QJsonArray::fromStringList(mod->authors()); + lines << line; + } + QJsonDocument doc; + doc.setArray(lines); + return doc.toJson(); +} + +QString toCSV(QList mods, OptionalData extraData) +{ + QStringList lines; + for (auto mod : mods) { + QStringList data; + auto meta = mod->metadata(); + auto modName = mod->name(); + + data << modName; + if (extraData & Url) + data << mod->metaurl(); + if (extraData & Version) { + auto ver = mod->version(); + if (ver.isEmpty() && meta != nullptr) + ver = meta->version().toString(); + data << ver; + } + if (extraData & Authors) { + QString authors; + if (mod->authors().length() == 1) + authors = mod->authors().back(); + else if (mod->authors().length() > 1) + authors = QString("\"%1\"").arg(mod->authors().join(",")); + data << authors; + } + lines << data.join(","); + } + return lines.join("\n"); +} + +QString exportToModList(QList mods, Formats format, OptionalData extraData) +{ + switch (format) { + case HTML: + return toHTML(mods, extraData); + case MARKDOWN: + return toMarkdown(mods, extraData); + case PLAINTXT: + return toPlainTXT(mods, extraData); + case JSON: + return toJSON(mods, extraData); + case CSV: + return toCSV(mods, extraData); + default: { + return QString("unknown format:%1").arg(format); + } + } +} + +QString exportToModList(QList mods, QString lineTemplate) +{ + QStringList lines; + for (auto mod : mods) { + auto meta = mod->metadata(); + auto modName = mod->name(); + auto url = mod->metaurl(); + auto ver = mod->version(); + if (ver.isEmpty() && meta != nullptr) + ver = meta->version().toString(); + auto authors = mod->authors().join(", "); + lines << QString(lineTemplate) + .replace("{name}", modName) + .replace("{url}", url) + .replace("{version}", ver) + .replace("{authors}", authors); + } + return lines.join("\n"); +} +} // namespace ExportToModList \ No newline at end of file diff --git a/launcher/modplatform/helpers/ExportToModList.h b/launcher/modplatform/helpers/ExportToModList.h new file mode 100644 index 000000000..7ea4ba9c2 --- /dev/null +++ b/launcher/modplatform/helpers/ExportToModList.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include "minecraft/mod/Mod.h" + +namespace ExportToModList { + +enum Formats { HTML, MARKDOWN, PLAINTXT, JSON, CSV, CUSTOM }; +enum OptionalData { + Authors = 1 << 0, + Url = 1 << 1, + Version = 1 << 2, +}; +QString exportToModList(QList mods, Formats format, OptionalData extraData); +QString exportToModList(QList mods, QString lineTemplate); +} // namespace ExportToModList diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.cpp b/launcher/modplatform/helpers/NetworkResourceAPI.cpp index c278f800d..46b966620 100644 --- a/launcher/modplatform/helpers/NetworkResourceAPI.cpp +++ b/launcher/modplatform/helpers/NetworkResourceAPI.cpp @@ -10,6 +10,8 @@ #include "modplatform/ModIndex.h" +#include "net/ApiDownload.h" + Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&& callbacks) const { auto search_url_optional = getSearchURL(args); @@ -23,7 +25,7 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks& auto response = std::make_shared(); auto netJob = makeShared(QString("%1::Search").arg(debugName()), APPLICATION->network()); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(search_url), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(search_url), response)); QObject::connect(netJob.get(), &NetJob::succeeded, [this, response, callbacks] { QJsonParseError parse_error{}; @@ -85,7 +87,7 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi auto netJob = makeShared(QString("%1::Versions").arg(args.pack.name), APPLICATION->network()); auto response = std::make_shared(); - netJob->addNetAction(Net::Download::makeByteArray(versions_url, response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response)); QObject::connect(netJob.get(), &NetJob::succeeded, [response, callbacks, args] { QJsonParseError parse_error{}; @@ -113,7 +115,7 @@ Task::Ptr NetworkResourceAPI::getProject(QString addonId, std::shared_ptr(QString("%1::GetProject").arg(addonId), APPLICATION->network()); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(project_url), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(project_url), response)); return netJob; } @@ -145,4 +147,4 @@ Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args, }); return netJob; -}; +} diff --git a/launcher/modplatform/import_ftb/PackHelpers.cpp b/launcher/modplatform/import_ftb/PackHelpers.cpp new file mode 100644 index 000000000..4a1bbef96 --- /dev/null +++ b/launcher/modplatform/import_ftb/PackHelpers.cpp @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "modplatform/import_ftb/PackHelpers.h" + +#include +#include +#include + +#include "FileSystem.h" +#include "Json.h" + +namespace FTBImportAPP { + +Modpack parseDirectory(QString path) +{ + Modpack modpack{ path }; + auto instanceFile = QFileInfo(FS::PathCombine(path, "instance.json")); + if (!instanceFile.exists() || !instanceFile.isFile()) + return {}; + try { + auto doc = Json::requireDocument(instanceFile.absoluteFilePath(), "FTB_APP instance JSON file"); + const auto root = doc.object(); + modpack.uuid = Json::requireString(root, "uuid", "uuid"); + modpack.id = Json::requireInteger(root, "id", "id"); + modpack.versionId = Json::requireInteger(root, "versionId", "versionId"); + modpack.name = Json::requireString(root, "name", "name"); + modpack.version = Json::requireString(root, "version", "version"); + modpack.mcVersion = Json::requireString(root, "mcVersion", "mcVersion"); + modpack.jvmArgs = Json::ensureVariant(root, "jvmArgs", {}, "jvmArgs"); + } catch (const Exception& e) { + qDebug() << "Couldn't load ftb instance json: " << e.cause(); + return {}; + } + auto versionsFile = QFileInfo(FS::PathCombine(path, "version.json")); + if (!versionsFile.exists() || !versionsFile.isFile()) + return {}; + try { + auto doc = Json::requireDocument(versionsFile.absoluteFilePath(), "FTB_APP version JSON file"); + const auto root = doc.object(); + auto targets = Json::requireArray(root, "targets", "targets"); + + for (auto target : targets) { + auto obj = Json::requireObject(target, "target"); + auto name = Json::requireString(obj, "name", "name"); + auto version = Json::requireString(obj, "version", "version"); + if (name == "forge") { + modpack.loaderType = ResourceAPI::Forge; + modpack.version = version; + break; + } else if (name == "fabric") { + modpack.loaderType = ResourceAPI::Fabric; + modpack.version = version; + break; + } else if (name == "quilt") { + modpack.loaderType = ResourceAPI::Quilt; + modpack.version = version; + break; + } + } + } catch (const Exception& e) { + qDebug() << "Couldn't load ftb version json: " << e.cause(); + return {}; + } + auto iconFile = QFileInfo(FS::PathCombine(path, "folder.jpg")); + if (iconFile.exists() && iconFile.isFile()) { + modpack.icon = QIcon(iconFile.absoluteFilePath()); + } + return modpack; +} + +} // namespace FTBImportAPP diff --git a/launcher/modplatform/import_ftb/PackHelpers.h b/launcher/modplatform/import_ftb/PackHelpers.h new file mode 100644 index 000000000..8ea4f3faf --- /dev/null +++ b/launcher/modplatform/import_ftb/PackHelpers.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include +#include +#include +#include +#include "modplatform/ResourceAPI.h" + +namespace FTBImportAPP { + +struct Modpack { + QString path; + + // json data + QString uuid; + int id; + int versionId; + QString name; + QString version; + QString mcVersion; + // not needed for instance creation + QVariant jvmArgs; + + std::optional loaderType; + QString loaderVersion; + + QIcon icon; +}; + +typedef QList ModpackList; + +Modpack parseDirectory(QString path); + +} // namespace FTBImportAPP + +// We need it for the proxy model +Q_DECLARE_METATYPE(FTBImportAPP::Modpack) diff --git a/launcher/modplatform/import_ftb/PackInstallTask.cpp b/launcher/modplatform/import_ftb/PackInstallTask.cpp new file mode 100644 index 000000000..b5e424d12 --- /dev/null +++ b/launcher/modplatform/import_ftb/PackInstallTask.cpp @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PackInstallTask.h" + +#include + +#include "BaseInstance.h" +#include "FileSystem.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/PackProfile.h" +#include "modplatform/ResourceAPI.h" +#include "modplatform/import_ftb/PackHelpers.h" +#include "settings/INISettingsObject.h" + +namespace FTBImportAPP { + +void PackInstallTask::executeTask() +{ + setStatus(tr("Copying files...")); + setAbortable(false); + progress(1, 2); + + m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] { + FS::copy folderCopy(m_pack.path, FS::PathCombine(m_stagingPath, ".minecraft")); + folderCopy.followSymlinks(true); + return folderCopy(); + }); + connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &PackInstallTask::copySettings); + connect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &PackInstallTask::emitAborted); + m_copyFutureWatcher.setFuture(m_copyFuture); +} + +void PackInstallTask::copySettings() +{ + setStatus(tr("Copying settings...")); + progress(2, 2); + QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg"); + auto instanceSettings = std::make_shared(instanceConfigPath); + instanceSettings->suspendSave(); + MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath); + instance.settings()->set("InstanceType", "OneSix"); + + if (m_pack.jvmArgs.isValid() && !m_pack.jvmArgs.toString().isEmpty()) { + instance.settings()->set("OverrideJavaArgs", true); + instance.settings()->set("JvmArgs", m_pack.jvmArgs.toString()); + } + + auto components = instance.getPackProfile(); + components->buildingFromScratch(); + components->setComponentVersion("net.minecraft", m_pack.mcVersion, true); + + auto modloader = m_pack.loaderType; + if (modloader.has_value()) + switch (modloader.value()) { + case ResourceAPI::Forge: { + components->setComponentVersion("net.minecraftforge", m_pack.version, true); + break; + } + case ResourceAPI::Fabric: { + components->setComponentVersion("net.fabricmc.fabric-loader", m_pack.version, true); + break; + } + case ResourceAPI::Quilt: { + components->setComponentVersion("org.quiltmc.quilt-loader", m_pack.version, true); + break; + } + case ResourceAPI::Cauldron: + break; + case ResourceAPI::LiteLoader: + break; + } + components->saveNow(); + + instance.setName(name()); + if (m_instIcon == "default") + m_instIcon = "ftb_logo"; + instance.setIconKey(m_instIcon); + instanceSettings->resumeSave(); + + emitSucceeded(); +} + +} // namespace FTBImportAPP diff --git a/launcher/modplatform/import_ftb/PackInstallTask.h b/launcher/modplatform/import_ftb/PackInstallTask.h new file mode 100644 index 000000000..842e4b35e --- /dev/null +++ b/launcher/modplatform/import_ftb/PackInstallTask.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#include "InstanceTask.h" +#include "PackHelpers.h" + +namespace FTBImportAPP { + +class PackInstallTask : public InstanceTask { + Q_OBJECT + + public: + explicit PackInstallTask(const Modpack& pack) : m_pack(pack) {} + virtual ~PackInstallTask() = default; + + protected: + virtual void executeTask() override; + + private slots: + void copySettings(); + + private: + QFuture m_copyFuture; + QFutureWatcher m_copyFutureWatcher; + + const Modpack m_pack; +}; + +} // namespace FTBImportAPP diff --git a/launcher/modplatform/legacy_ftb/PackFetchTask.cpp b/launcher/modplatform/legacy_ftb/PackFetchTask.cpp index a8a0fc2c2..8f1a6e2ff 100644 --- a/launcher/modplatform/legacy_ftb/PackFetchTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackFetchTask.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -37,8 +37,10 @@ #include "PrivatePackManager.h" #include -#include "BuildConfig.h" #include "Application.h" +#include "BuildConfig.h" + +#include "net/ApiDownload.h" namespace LegacyFTB { @@ -51,7 +53,7 @@ void PackFetchTask::fetch() QUrl publicPacksUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/modpacks.xml"); qDebug() << "Downloading public version info from" << publicPacksUrl.toString(); - jobPtr->addNetAction(Net::Download::makeByteArray(publicPacksUrl, publicModpacksXmlFileData)); + jobPtr->addNetAction(Net::ApiDownload::makeByteArray(publicPacksUrl, publicModpacksXmlFileData)); QUrl thirdPartyUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/thirdparty.xml"); qDebug() << "Downloading thirdparty version info from" << thirdPartyUrl.toString(); @@ -71,7 +73,7 @@ void PackFetchTask::fetchPrivate(const QStringList& toFetch) for (auto& packCode : toFetch) { auto data = std::make_shared(); NetJob* job = new NetJob("Fetching private pack", m_network); - job->addNetAction(Net::Download::makeByteArray(privatePackBaseUrl.arg(packCode), data)); + job->addNetAction(Net::ApiDownload::makeByteArray(privatePackBaseUrl.arg(packCode), data)); QObject::connect(job, &NetJob::succeeded, this, [this, job, data, packCode] { ModpackList packs; diff --git a/launcher/modplatform/legacy_ftb/PackHelpers.h b/launcher/modplatform/legacy_ftb/PackHelpers.h index 566210d05..4fb535530 100644 --- a/launcher/modplatform/legacy_ftb/PackHelpers.h +++ b/launcher/modplatform/legacy_ftb/PackHelpers.h @@ -1,22 +1,16 @@ #pragma once #include +#include #include #include -#include namespace LegacyFTB { -//Header for structs etc... -enum class PackType -{ - Public, - ThirdParty, - Private -}; +// Header for structs etc... +enum class PackType { Public, ThirdParty, Private }; -struct Modpack -{ +struct Modpack { QString name; QString description; QString author; @@ -26,9 +20,9 @@ struct Modpack QString mods; QString logo; - //Technical data + // Technical data QString dir; - QString file; //<- Url in the xml, but doesn't make much sense + QString file; //<- Url in the xml, but doesn't make much sense bool bugged = false; bool broken = false; @@ -39,7 +33,7 @@ struct Modpack typedef QList ModpackList; -} +} // namespace LegacyFTB -//We need it for the proxy model +// We need it for the proxy model Q_DECLARE_METATYPE(LegacyFTB::Modpack) diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp index a4c78397b..761f622bb 100644 --- a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -48,6 +48,8 @@ #include "Application.h" #include "BuildConfig.h" +#include "net/ApiDownload.h" + namespace LegacyFTB { PackInstallTask::PackInstallTask(shared_qobject_ptr network, Modpack pack, QString version) @@ -77,11 +79,11 @@ void PackInstallTask::downloadPack() } else { url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "modpacks/%1").arg(archivePath); } - netJobContainer->addNetAction(Net::Download::makeFile(url, archivePath)); + netJobContainer->addNetAction(Net::ApiDownload::makeFile(url, archivePath)); connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip); connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed); - connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress); + connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress); connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::emitAborted); netJobContainer->start(); diff --git a/launcher/modplatform/legacy_ftb/PrivatePackManager.cpp b/launcher/modplatform/legacy_ftb/PrivatePackManager.cpp index 1a81f0265..2ae351329 100644 --- a/launcher/modplatform/legacy_ftb/PrivatePackManager.cpp +++ b/launcher/modplatform/legacy_ftb/PrivatePackManager.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -43,8 +43,7 @@ namespace LegacyFTB { void PrivatePackManager::load() { - try - { + try { #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) auto foo = QString::fromUtf8(FS::read(m_filename)).split('\n', Qt::SkipEmptyParts); currentPacks = QSet(foo.begin(), foo.end()); @@ -53,9 +52,7 @@ void PrivatePackManager::load() #endif dirty = false; - } - catch(...) - { + } catch (...) { currentPacks = {}; qWarning() << "Failed to read third party FTB pack codes from" << m_filename; } @@ -63,20 +60,16 @@ void PrivatePackManager::load() void PrivatePackManager::save() const { - if(!dirty) - { + if (!dirty) { return; } - try - { + try { QStringList list = currentPacks.values(); FS::write(m_filename, list.join('\n').toUtf8()); dirty = false; - } - catch(...) - { + } catch (...) { qWarning() << "Failed to write third party FTB pack codes to" << m_filename; } } -} +} // namespace LegacyFTB diff --git a/launcher/modplatform/legacy_ftb/PrivatePackManager.h b/launcher/modplatform/legacy_ftb/PrivatePackManager.h index 0e8146462..be811f832 100644 --- a/launcher/modplatform/legacy_ftb/PrivatePackManager.h +++ b/launcher/modplatform/legacy_ftb/PrivatePackManager.h @@ -1,43 +1,33 @@ #pragma once +#include #include #include -#include namespace LegacyFTB { -class PrivatePackManager -{ -public: - ~PrivatePackManager() - { - save(); - } +class PrivatePackManager { + public: + ~PrivatePackManager() { save(); } void load(); void save() const; - bool empty() const - { - return currentPacks.empty(); - } - const QSet &getCurrentPackCodes() const - { - return currentPacks; - } - void add(const QString &code) + bool empty() const { return currentPacks.empty(); } + const QSet& getCurrentPackCodes() const { return currentPacks; } + void add(const QString& code) { currentPacks.insert(code); dirty = true; } - void remove(const QString &code) + void remove(const QString& code) { currentPacks.remove(code); dirty = true; } -private: + private: QSet currentPacks; QString m_filename = "private_packs.txt"; mutable bool dirty = false; }; -} +} // namespace LegacyFTB diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp index 364cf3f30..466c5b102 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.cpp +++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp @@ -6,6 +6,8 @@ #include "Application.h" #include "Json.h" +#include "net/ApiDownload.h" +#include "net/ApiUpload.h" #include "net/NetJob.h" #include "net/Upload.h" @@ -13,7 +15,7 @@ Task::Ptr ModrinthAPI::currentVersion(QString hash, QString hash_format, std::sh { auto netJob = makeShared(QString("Modrinth::GetCurrentVersion"), APPLICATION->network()); - netJob->addNetAction(Net::Download::makeByteArray( + netJob->addNetAction(Net::ApiDownload::makeByteArray( QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1?algorithm=%2").arg(hash, hash_format), response)); return netJob; @@ -31,7 +33,7 @@ Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_f QJsonDocument body(body_obj); auto body_raw = body.toJson(); - netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), response, body_raw)); + netJob->addNetAction(Net::ApiUpload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), response, body_raw)); return netJob; } @@ -60,7 +62,7 @@ Task::Ptr ModrinthAPI::latestVersion(QString hash, QJsonDocument body(body_obj); auto body_raw = body.toJson(); - netJob->addNetAction(Net::Upload::makeByteArray( + netJob->addNetAction(Net::ApiUpload::makeByteArray( QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1/update?algorithm=%2").arg(hash, hash_format), response, body_raw)); return netJob; @@ -93,7 +95,8 @@ Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes, QJsonDocument body(body_obj); auto body_raw = body.toJson(); - netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), response, body_raw)); + netJob->addNetAction( + Net::ApiUpload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), response, body_raw)); return netJob; } @@ -103,7 +106,7 @@ Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, std::shared_ptr(QString("Modrinth::GetProjects"), APPLICATION->network()); auto searchUrl = getMultipleModInfoURL(addonIds); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response)); return netJob; } diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h index 88e1a6751..4583dd6ce 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h @@ -8,7 +8,10 @@ class ModrinthCheckUpdate : public CheckUpdateTask { Q_OBJECT public: - ModrinthCheckUpdate(QList& mods, std::list& mcVersions, std::optional loaders, std::shared_ptr mods_folder) + ModrinthCheckUpdate(QList& mods, + std::list& mcVersions, + std::optional loaders, + std::shared_ptr mods_folder) : CheckUpdateTask(mods, mcVersions, loaders, mods_folder) {} diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index 76f072773..cdbbd42d0 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -11,6 +11,7 @@ #include "net/ChecksumValidator.h" +#include "net/ApiDownload.h" #include "net/NetJob.h" #include "settings/INISettingsObject.h" @@ -133,10 +134,10 @@ bool ModrinthCreationTask::updateInstance() } } else { // We don't have an old index file, so we may duplicate stuff! - auto dialog = CustomMessageBox::selectable(m_parent, - tr("No index file."), - tr("We couldn't find a suitable index file for the older version. This may cause some of the files to be duplicated. Do you want to continue?"), - QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Cancel); + auto dialog = CustomMessageBox::selectable(m_parent, tr("No index file."), + tr("We couldn't find a suitable index file for the older version. This may cause some " + "of the files to be duplicated. Do you want to continue?"), + QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Cancel); if (dialog->exec() == QDialog::DialogCode::Rejected) { m_abort = true; @@ -144,7 +145,6 @@ bool ModrinthCreationTask::updateInstance() } } - setOverride(true, inst->id()); qDebug() << "Will override instance!"; @@ -233,12 +233,13 @@ bool ModrinthCreationTask::createInstance() auto file_path = FS::PathCombine(root_modpack_path, file.path); if (!root_modpack_url.isParentOf(QUrl::fromLocalFile(file_path))) { // This means we somehow got out of the root folder, so abort here to prevent exploits - setError(tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.").arg(file.path)); + setError(tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.") + .arg(file.path)); return false; } qDebug() << "Will try to download" << file.downloads.front() << "to" << file_path; - auto dl = Net::Download::makeFile(file.downloads.dequeue(), file_path); + auto dl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path); dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash)); m_files_job->addNetAction(dl); @@ -247,10 +248,11 @@ bool ModrinthCreationTask::createInstance() // MultipleOptionsTask's , once those exist :) auto param = dl.toWeakRef(); connect(dl.get(), &NetAction::failed, [this, &file, file_path, param] { - auto ndl = Net::Download::makeFile(file.downloads.dequeue(), file_path); + auto ndl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path); ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash)); m_files_job->addNetAction(ndl); - if (auto shared = param.lock()) shared->succeeded(); + if (auto shared = param.lock()) + shared->succeeded(); }); } } @@ -263,11 +265,11 @@ bool ModrinthCreationTask::createInstance() setError(reason); }); connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit); - connect(m_files_job.get(), &NetJob::progress, [&](qint64 current, qint64 total) { + connect(m_files_job.get(), &NetJob::progress, [&](qint64 current, qint64 total) { setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); - setProgress(current, total); + setProgress(current, total); }); - connect(m_files_job.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propogateStepProgress); + connect(m_files_job.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propagateStepProgress); setStatus(tr("Downloading mods...")); m_files_job->start(); @@ -293,7 +295,10 @@ bool ModrinthCreationTask::createInstance() return ended_well; } -bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector& files, bool set_internal_data, bool show_optional_dialog) +bool ModrinthCreationTask::parseManifest(const QString& index_path, + std::vector& files, + bool set_internal_data, + bool show_optional_dialog) { try { auto doc = Json::requireDocument(index_path); diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h index 6de24fd40..07e417be5 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h @@ -20,10 +20,7 @@ class ModrinthCreationTask final : public InstanceCreationTask { QString id, QString version_id = {}, QString original_instance_id = {}) - : InstanceCreationTask() - , m_parent(parent) - , m_managed_id(std::move(id)) - , m_managed_version_id(std::move(version_id)) + : InstanceCreationTask(), m_parent(parent), m_managed_id(std::move(id)), m_managed_version_id(std::move(version_id)) { setStagingPath(staging_path); setParentSettings(global_settings); diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp index 30fe566da..64c06d1ba 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp @@ -55,20 +55,11 @@ void ModrinthPackExportTask::executeTask() bool ModrinthPackExportTask::abort() { - if (task != nullptr) { + if (task) { task->abort(); - task = nullptr; emitAborted(); return true; } - - if (buildZipFuture.isRunning()) { - buildZipFuture.cancel(); - // NOTE: Here we don't do `emitAborted()` because it will be done when `buildZipFuture` actually cancels, which may not occur - // immediately. - return true; - } - return false; } @@ -183,10 +174,10 @@ void ModrinthPackExportTask::parseApiResponse(const std::shared_ptr if (obj.isEmpty()) continue; - const QJsonArray files = obj["files"].toArray(); - if (auto fileIter = std::find_if(files.begin(), files.end(), + const QJsonArray files_array = obj["files"].toArray(); + if (auto fileIter = std::find_if(files_array.begin(), files_array.end(), [&iterator](const QJsonValue& file) { return file["hashes"]["sha512"] == iterator.value(); }); - fileIter != files.end()) { + fileIter != files_array.end()) { // map the file to the url resolvedFiles[iterator.key()] = ResolvedFile{ fileIter->toObject()["hashes"].toObject()["sha1"].toString(), iterator.value(), @@ -205,63 +196,36 @@ void ModrinthPackExportTask::buildZip() { setStatus(tr("Adding files...")); - buildZipFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { - QuaZip zip(output); - if (!zip.open(QuaZip::mdCreate)) { - QFile::remove(output); - return BuildZipResult(tr("Could not create file")); - } + auto zipTask = makeShared(output, gameRoot, files, "overrides/", true); + zipTask->addExtraFile("modrinth.index.json", generateIndex()); - if (buildZipFuture.isCanceled()) - return BuildZipResult(); + zipTask->setExcludeFiles(resolvedFiles.keys()); - QuaZipFile indexFile(&zip); - if (!indexFile.open(QIODevice::WriteOnly, QuaZipNewInfo("modrinth.index.json"))) { - QFile::remove(output); - return BuildZipResult(tr("Could not create index")); - } - indexFile.write(generateIndex()); - - size_t progress = 0; - for (const QFileInfo& file : files) { - if (buildZipFuture.isCanceled()) { - QFile::remove(output); - return BuildZipResult(); - } - - setProgress(progress, files.length()); - const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath()); - if (!resolvedFiles.contains(relative) && !JlCompress::compressFile(&zip, file.absoluteFilePath(), "overrides/" + relative)) { - QFile::remove(output); - return BuildZipResult(tr("Could not read and compress %1").arg(relative)); - } - progress++; - } - - zip.close(); - - if (zip.getZipError() != 0) { - QFile::remove(output); - return BuildZipResult(tr("A zip error occurred")); - } - - return BuildZipResult(); + auto progressStep = std::make_shared(); + connect(zipTask.get(), &Task::finished, this, [this, progressStep] { + progressStep->state = TaskStepState::Succeeded; + stepProgress(*progressStep); }); - connect(&buildZipWatcher, &QFutureWatcher::finished, this, &ModrinthPackExportTask::finish); - buildZipWatcher.setFuture(buildZipFuture); -} -void ModrinthPackExportTask::finish() -{ - if (buildZipFuture.isCanceled()) - emitAborted(); - else { - const BuildZipResult result = buildZipFuture.result(); - if (result.has_value()) - emitFailed(result.value()); - else - emitSucceeded(); - } + connect(zipTask.get(), &Task::succeeded, this, &ModrinthPackExportTask::emitSucceeded); + connect(zipTask.get(), &Task::aborted, this, &ModrinthPackExportTask::emitAborted); + connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { + progressStep->state = TaskStepState::Failed; + stepProgress(*progressStep); + emitFailed(reason); + }); + connect(zipTask.get(), &Task::stepProgress, this, &ModrinthPackExportTask::propagateStepProgress); + + connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { + progressStep->update(current, total); + stepProgress(*progressStep); + }); + connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) { + progressStep->status = status; + stepProgress(*progressStep); + }); + task.reset(zipTask); + zipTask->start(); } QByteArray ModrinthPackExportTask::generateIndex() diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.h b/launcher/modplatform/modrinth/ModrinthPackExportTask.h index 96f292c1b..1f9e0eb77 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.h +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.h @@ -56,22 +56,17 @@ class ModrinthPackExportTask : public Task { const QString output; const MMCZip::FilterFunction filter; - typedef std::optional BuildZipResult; - ModrinthAPI api; QFileInfoList files; QMap pendingHashes; QMap resolvedFiles; Task::Ptr task; - QFuture buildZipFuture; - QFutureWatcher buildZipWatcher; void collectFiles(); void collectHashes(); void makeApiRequest(); void parseApiResponse(const std::shared_ptr response); void buildZip(); - void finish(); QByteArray generateIndex(); }; diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index b40373496..85e66a91e 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify @@ -95,7 +95,7 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, - const shared_qobject_ptr& network, + [[maybe_unused]] const shared_qobject_ptr& network, const BaseInstance* inst) { QVector unsortedVersions; @@ -218,7 +218,7 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t return {}; } -auto Modrinth::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion +auto Modrinth::loadDependencyVersions([[maybe_unused]] const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion { QVector versions; @@ -235,4 +235,4 @@ auto Modrinth::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArr }; std::sort(versions.begin(), versions.end(), orderSortPredicate); return versions.length() != 0 ? versions.front() : ModPlatform::IndexedVersion(); -} \ No newline at end of file +} diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.h b/launcher/modplatform/modrinth/ModrinthPackIndex.h index a8d986c57..58a0f227c 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.h +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * * 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 diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index 4dca786f0..0d07c6361 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify @@ -66,23 +66,23 @@ void loadIndexedInfo(Modpack& pack, QJsonObject& obj) pack.extra.projectUrl = QString("https://modrinth.com/modpack/%1").arg(Json::ensureString(obj, "slug")); pack.extra.issuesUrl = Json::ensureString(obj, "issues_url"); - if(pack.extra.issuesUrl.endsWith('/')) + if (pack.extra.issuesUrl.endsWith('/')) pack.extra.issuesUrl.chop(1); pack.extra.sourceUrl = Json::ensureString(obj, "source_url"); - if(pack.extra.sourceUrl.endsWith('/')) + if (pack.extra.sourceUrl.endsWith('/')) pack.extra.sourceUrl.chop(1); pack.extra.wikiUrl = Json::ensureString(obj, "wiki_url"); - if(pack.extra.wikiUrl.endsWith('/')) + if (pack.extra.wikiUrl.endsWith('/')) pack.extra.wikiUrl.chop(1); pack.extra.discordUrl = Json::ensureString(obj, "discord_url"); - if(pack.extra.discordUrl.endsWith('/')) + if (pack.extra.discordUrl.endsWith('/')) pack.extra.discordUrl.chop(1); auto donate_arr = Json::ensureArray(obj, "donation_urls"); - for(auto d : donate_arr){ + for (auto d : donate_arr) { auto d_obj = Json::requireObject(d); DonationData donate; @@ -107,7 +107,7 @@ void loadIndexedVersions(Modpack& pack, QJsonDocument& doc) auto obj = Json::requireObject(versionIter); auto file = loadIndexedVersion(obj); - if(!file.id.isEmpty()) // Heuristic to check if the returned value is valid + if (!file.id.isEmpty()) // Heuristic to check if the returned value is valid unsortedVersions.append(file); } auto orderSortPredicate = [](const ModpackVersion& a, const ModpackVersion& b) -> bool { @@ -122,7 +122,7 @@ void loadIndexedVersions(Modpack& pack, QJsonDocument& doc) pack.versionsLoaded = true; } -auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion +auto loadIndexedVersion(QJsonObject& obj) -> ModpackVersion { ModpackVersion file; @@ -132,12 +132,11 @@ auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion file.id = Json::requireString(obj, "id"); file.project_id = Json::requireString(obj, "project_id"); - + file.date = Json::requireString(obj, "date_published"); auto files = Json::requireArray(obj, "files"); - for (auto file_iter : files) { File indexed_file; auto parent = Json::requireObject(file_iter); @@ -146,21 +145,21 @@ auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion auto filename = Json::ensureString(parent, "filename"); // Checking suffix here is fine because it's the response from Modrinth, // so one would assume it will always be in English. - if(!filename.endsWith("mrpack") && !filename.endsWith("zip")) + if (!filename.endsWith("mrpack") && !filename.endsWith("zip")) continue; } auto url = Json::requireString(parent, "url"); file.download_url = url; - if(is_primary) + if (is_primary) break; } - if(file.download_url.isEmpty()) + if (file.download_url.isEmpty()) return {}; return file; -} +} } // namespace Modrinth diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index 2973dfba2..effa1a84a 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify @@ -74,7 +74,6 @@ struct ModpackExtra { QString discordUrl; QList donate; - }; struct ModpackVersion { @@ -97,10 +96,10 @@ struct Modpack { QString description; std::tuple author; QString iconName; - QUrl iconUrl; + QUrl iconUrl; - bool versionsLoaded = false; - bool extraInfoLoaded = false; + bool versionsLoaded = false; + bool extraInfoLoaded = false; ModpackExtra extra; QVector versions; @@ -113,7 +112,7 @@ auto loadIndexedVersion(QJsonObject&) -> ModpackVersion; auto validateDownloadUrl(QUrl) -> bool; -} +} // namespace Modrinth Q_DECLARE_METATYPE(Modrinth::Modpack) Q_DECLARE_METATYPE(Modrinth::ModpackVersion) diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 510c7309d..71f66bf3e 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify @@ -89,7 +89,8 @@ auto intEntry(toml::table table, QString entry_name) -> int return node.value_or(0); } -auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod +auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) + -> Mod { Mod mod; @@ -114,7 +115,7 @@ auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, Mo return mod; } -auto V1::createModFormat(QDir& index_dir, ::Mod& internal_mod, QString slug) -> Mod +auto V1::createModFormat(QDir& index_dir, [[maybe_unused]] ::Mod& internal_mod, QString slug) -> Mod { // Try getting metadata if it exists Mod mod{ getIndexForMod(index_dir, slug) }; @@ -241,12 +242,13 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod return {}; } #else - table = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname))); - if (!table) { + toml::parse_result result = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname))); + if (!result) { qWarning() << QString("Could not open file %1!").arg(normalized_fname); - qWarning() << "Reason: " << QString(table.error().what()); + qWarning() << "Reason: " << result.error().description(); return {}; } + table = result.table(); #endif // index_file.close(); diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 4b096eec7..7edc18cde 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -1,20 +1,20 @@ // SPDX-License-Identifier: GPL-3.0-only /* -* PolyMC - Minecraft Launcher -* Copyright (c) 2022 flowln -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, version 3. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #pragma once @@ -36,22 +36,22 @@ auto getRealIndexName(QDir& index_dir, QString normalized_index_name, bool shoul class V1 { public: struct Mod { - QString slug {}; - QString name {}; - QString filename {}; + QString slug{}; + QString name{}; + QString filename{}; // FIXME: make side an enum - QString side {"both"}; + QString side{ "both" }; // [download] - QString mode {}; - QUrl url {}; - QString hash_format {}; - QString hash {}; + QString mode{}; + QUrl url{}; + QString hash_format{}; + QString hash{}; // [update] - ModPlatform::ResourceProvider provider {}; - QVariant file_id {}; - QVariant project_id {}; + ModPlatform::ResourceProvider provider{}; + QVariant file_id{}; + QVariant project_id{}; public: // This is a totally heuristic, but should work for now. @@ -95,4 +95,4 @@ class V1 { static auto getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod; }; -} // namespace Packwiz +} // namespace Packwiz diff --git a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp index f07ca24af..ad08d72d4 100644 --- a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp +++ b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp @@ -17,21 +17,23 @@ #include +#include "FileSystem.h" #include "MMCZip.h" #include "TechnicPackProcessor.h" -#include "FileSystem.h" #include "Application.h" -Technic::SingleZipPackInstallTask::SingleZipPackInstallTask(const QUrl &sourceUrl, const QString &minecraftVersion) +#include "net/ApiDownload.h" + +Technic::SingleZipPackInstallTask::SingleZipPackInstallTask(const QUrl& sourceUrl, const QString& minecraftVersion) { m_sourceUrl = sourceUrl; m_minecraftVersion = minecraftVersion; } -bool Technic::SingleZipPackInstallTask::abort() { - if(m_abortable) - { +bool Technic::SingleZipPackInstallTask::abort() +{ + if (m_abortable) { return m_filesNetJob->abort(); } return false; @@ -45,12 +47,12 @@ void Technic::SingleZipPackInstallTask::executeTask() auto entry = APPLICATION->metacache()->resolveEntry("general", path); entry->setStale(true); m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); - m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); + m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry)); m_archivePath = entry->getFullPath(); auto job = m_filesNetJob.get(); connect(job, &NetJob::succeeded, this, &Technic::SingleZipPackInstallTask::downloadSucceeded); connect(job, &NetJob::progress, this, &Technic::SingleZipPackInstallTask::downloadProgressChanged); - connect(job, &NetJob::stepProgress, this, &Technic::SingleZipPackInstallTask::propogateStepProgress); + connect(job, &NetJob::stepProgress, this, &Technic::SingleZipPackInstallTask::propagateStepProgress); connect(job, &NetJob::failed, this, &Technic::SingleZipPackInstallTask::downloadFailed); m_filesNetJob->start(); } @@ -65,12 +67,12 @@ void Technic::SingleZipPackInstallTask::downloadSucceeded() // open the zip and find relevant files in it m_packZip.reset(new QuaZip(m_archivePath)); - if (!m_packZip->open(QuaZip::mdUnzip)) - { + if (!m_packZip->open(QuaZip::mdUnzip)) { emitFailed(tr("Unable to open supplied modpack zip file.")); return; } - m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), QString(""), extractDir.absolutePath()); + m_extractFuture = + QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), QString(""), extractDir.absolutePath()); connect(&m_extractFutureWatcher, &QFutureWatcher::finished, this, &Technic::SingleZipPackInstallTask::extractFinished); connect(&m_extractFutureWatcher, &QFutureWatcher::canceled, this, &Technic::SingleZipPackInstallTask::extractAborted); m_extractFutureWatcher.setFuture(m_extractFuture); @@ -93,8 +95,7 @@ void Technic::SingleZipPackInstallTask::downloadProgressChanged(qint64 current, void Technic::SingleZipPackInstallTask::extractFinished() { m_packZip.reset(); - if (!m_extractFuture.result()) - { + if (!m_extractFuture.result()) { emitFailed(tr("Failed to extract modpack")); return; } @@ -102,30 +103,22 @@ void Technic::SingleZipPackInstallTask::extractFinished() qDebug() << "Fixing permissions for extracted pack files..."; QDirIterator it(extractDir, QDirIterator::Subdirectories); - while (it.hasNext()) - { + while (it.hasNext()) { auto filepath = it.next(); QFileInfo file(filepath); auto permissions = QFile::permissions(filepath); auto origPermissions = permissions; - if (file.isDir()) - { + if (file.isDir()) { // Folder +rwx for current user permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser; - } - else - { + } else { // File +rw for current user permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser; } - if (origPermissions != permissions) - { - if (!QFile::setPermissions(filepath, permissions)) - { + if (origPermissions != permissions) { + if (!QFile::setPermissions(filepath, permissions)) { logWarning(tr("Could not fix permissions for %1").arg(filepath)); - } - else - { + } else { qDebug() << "Fixed" << filepath; } } diff --git a/launcher/modplatform/technic/SingleZipPackInstallTask.h b/launcher/modplatform/technic/SingleZipPackInstallTask.h index 981ccf8a7..d49d008b9 100644 --- a/launcher/modplatform/technic/SingleZipPackInstallTask.h +++ b/launcher/modplatform/technic/SingleZipPackInstallTask.h @@ -28,28 +28,26 @@ namespace Technic { -class SingleZipPackInstallTask : public InstanceTask -{ +class SingleZipPackInstallTask : public InstanceTask { Q_OBJECT -public: - SingleZipPackInstallTask(const QUrl &sourceUrl, const QString &minecraftVersion); + public: + SingleZipPackInstallTask(const QUrl& sourceUrl, const QString& minecraftVersion); bool canAbort() const override { return true; } bool abort() override; -protected: + protected: void executeTask() override; - -private slots: + private slots: void downloadSucceeded(); void downloadFailed(QString reason); void downloadProgressChanged(qint64 current, qint64 total); void extractFinished(); void extractAborted(); -private: + private: bool m_abortable = false; QUrl m_sourceUrl; @@ -61,4 +59,4 @@ private: QFutureWatcher> m_extractFutureWatcher; }; -} // namespace Technic +} // namespace Technic diff --git a/launcher/modplatform/technic/SolderPackInstallTask.cpp b/launcher/modplatform/technic/SolderPackInstallTask.cpp index 6a05d17ae..c162d6253 100644 --- a/launcher/modplatform/technic/SolderPackInstallTask.cpp +++ b/launcher/modplatform/technic/SolderPackInstallTask.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2021-2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -42,6 +42,7 @@ #include "SolderPackManifest.h" #include "TechnicPackProcessor.h" +#include "net/ApiDownload.h" #include "net/ChecksumValidator.h" Technic::SolderPackInstallTask::SolderPackInstallTask(shared_qobject_ptr network, @@ -71,7 +72,7 @@ void Technic::SolderPackInstallTask::executeTask() m_filesNetJob.reset(new NetJob(tr("Resolving modpack files"), m_network)); auto sourceUrl = QString("%1/modpack/%2/%3").arg(m_solderUrl.toString(), m_pack, m_version); - m_filesNetJob->addNetAction(Net::Download::makeByteArray(sourceUrl, m_response)); + m_filesNetJob->addNetAction(Net::ApiDownload::makeByteArray(sourceUrl, m_response)); auto job = m_filesNetJob.get(); connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::fileListSucceeded); @@ -96,8 +97,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded() TechnicSolder::PackBuild build; try { TechnicSolder::loadPackBuild(build, obj); - } - catch (const JSONValidationError& e) { + } catch (const JSONValidationError& e) { emitFailed(tr("Could not understand pack manifest:\n") + e.cause()); m_filesNetJob.reset(); return; @@ -112,7 +112,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded() for (const auto& mod : build.mods) { auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i)); - auto dl = Net::Download::makeFile(mod.url, path); + auto dl = Net::ApiDownload::makeFile(mod.url, path); if (!mod.md5.isEmpty()) { auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); @@ -126,7 +126,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded() connect(m_filesNetJob.get(), &NetJob::succeeded, this, &Technic::SolderPackInstallTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::progress, this, &Technic::SolderPackInstallTask::downloadProgressChanged); - connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &Technic::SolderPackInstallTask::propogateStepProgress); + connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &Technic::SolderPackInstallTask::propagateStepProgress); connect(m_filesNetJob.get(), &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed); connect(m_filesNetJob.get(), &NetJob::aborted, this, &Technic::SolderPackInstallTask::downloadAborted); m_filesNetJob->start(); @@ -138,17 +138,14 @@ void Technic::SolderPackInstallTask::downloadSucceeded() setStatus(tr("Extracting modpack")); m_filesNetJob.reset(); - m_extractFuture = QtConcurrent::run([this]() - { + m_extractFuture = QtConcurrent::run([this]() { int i = 0; QString extractDir = FS::PathCombine(m_stagingPath, ".minecraft"); FS::ensureFolderPathExists(extractDir); - while (m_modCount > i) - { + while (m_modCount > i) { auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i)); - if (!MMCZip::extractDir(path, extractDir)) - { + if (!MMCZip::extractDir(path, extractDir)) { return false; } i++; @@ -181,8 +178,7 @@ void Technic::SolderPackInstallTask::downloadAborted() void Technic::SolderPackInstallTask::extractFinished() { - if (!m_extractFuture.result()) - { + if (!m_extractFuture.result()) { emitFailed(tr("Failed to extract modpack")); return; } @@ -190,30 +186,22 @@ void Technic::SolderPackInstallTask::extractFinished() qDebug() << "Fixing permissions for extracted pack files..."; QDirIterator it(extractDir, QDirIterator::Subdirectories); - while (it.hasNext()) - { + while (it.hasNext()) { auto filepath = it.next(); QFileInfo file(filepath); auto permissions = QFile::permissions(filepath); auto origPermissions = permissions; - if(file.isDir()) - { + if (file.isDir()) { // Folder +rwx for current user permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser; - } - else - { + } else { // File +rw for current user permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser; } - if(origPermissions != permissions) - { - if(!QFile::setPermissions(filepath, permissions)) - { + if (origPermissions != permissions) { + if (!QFile::setPermissions(filepath, permissions)) { logWarning(tr("Could not fix permissions for %1").arg(filepath)); - } - else - { + } else { qDebug() << "Fixed" << filepath; } } @@ -229,4 +217,3 @@ void Technic::SolderPackInstallTask::extractAborted() { emitFailed(tr("Instance import has been aborted.")); } - diff --git a/launcher/modplatform/technic/SolderPackInstallTask.h b/launcher/modplatform/technic/SolderPackInstallTask.h index f2c6a83a4..2ea701e23 100644 --- a/launcher/modplatform/technic/SolderPackInstallTask.h +++ b/launcher/modplatform/technic/SolderPackInstallTask.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2021-2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/modplatform/technic/SolderPackManifest.cpp b/launcher/modplatform/technic/SolderPackManifest.cpp index e52a7ec07..38b668f6b 100644 --- a/launcher/modplatform/technic/SolderPackManifest.cpp +++ b/launcher/modplatform/technic/SolderPackManifest.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -55,4 +55,4 @@ void loadPackBuild(PackBuild& v, QJsonObject& obj) } } -} +} // namespace TechnicSolder diff --git a/launcher/modplatform/technic/SolderPackManifest.h b/launcher/modplatform/technic/SolderPackManifest.h index 09f18df02..1a06d7037 100644 --- a/launcher/modplatform/technic/SolderPackManifest.h +++ b/launcher/modplatform/technic/SolderPackManifest.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -18,9 +18,9 @@ #pragma once +#include #include #include -#include namespace TechnicSolder { @@ -46,4 +46,4 @@ struct PackBuild { void loadPackBuild(PackBuild& v, QJsonObject& obj); -} +} // namespace TechnicSolder diff --git a/launcher/modplatform/technic/TechnicPackProcessor.cpp b/launcher/modplatform/technic/TechnicPackProcessor.cpp index df713a725..3b9424bf8 100644 --- a/launcher/modplatform/technic/TechnicPackProcessor.cpp +++ b/launcher/modplatform/technic/TechnicPackProcessor.cpp @@ -26,7 +26,12 @@ #include -void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const QString &instName, const QString &instIcon, const QString &stagingPath, const QString &minecraftVersion, const bool isSolder) +void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, + const QString& instName, + const QString& instIcon, + const QString& stagingPath, + const QString& minecraftVersion, + [[maybe_unused]] const bool isSolder) { QString minecraftPath = FS::PathCombine(stagingPath, ".minecraft"); QString configPath = FS::PathCombine(stagingPath, "instance.cfg"); @@ -35,8 +40,7 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const instance.setName(instName); - if (instIcon != "default") - { + if (instIcon != "default") { instance.setIconKey(instIcon); } @@ -48,23 +52,18 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const QString modpackJar = FS::PathCombine(minecraftPath, "bin", "modpack.jar"); QString versionJson = FS::PathCombine(minecraftPath, "bin", "version.json"); QString fmlMinecraftVersion; - if (QFile::exists(modpackJar)) - { + if (QFile::exists(modpackJar)) { QuaZip zipFile(modpackJar); - if (!zipFile.open(QuaZip::mdUnzip)) - { + if (!zipFile.open(QuaZip::mdUnzip)) { emit failed(tr("Unable to open \"bin/modpack.jar\" file!")); return; } QuaZipDir zipFileRoot(&zipFile, "/"); - if (zipFileRoot.exists("/version.json")) - { - if (zipFileRoot.exists("/fmlversion.properties")) - { + if (zipFileRoot.exists("/version.json")) { + if (zipFileRoot.exists("/fmlversion.properties")) { zipFile.setCurrentFile("fmlversion.properties"); QuaZipFile file(&zipFile); - if (!file.open(QIODevice::ReadOnly)) - { + if (!file.open(QIODevice::ReadOnly)) { emit failed(tr("Unable to open \"fmlversion.properties\"!")); return; } @@ -77,30 +76,25 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const } zipFile.setCurrentFile("version.json", QuaZip::csSensitive); QuaZipFile file(&zipFile); - if (!file.open(QIODevice::ReadOnly)) - { + if (!file.open(QIODevice::ReadOnly)) { emit failed(tr("Unable to open \"version.json\"!")); return; } data = file.readAll(); file.close(); - } - else - { + } else { if (minecraftVersion.isEmpty()) emit failed(tr("Could not find \"version.json\" inside \"bin/modpack.jar\", but Minecraft version is unknown")); components->setComponentVersion("net.minecraft", minecraftVersion, true); - components->installJarMods({modpackJar}); + components->installJarMods({ modpackJar }); // Forge for 1.4.7 and for 1.5.2 require extra libraries. // Figure out the forge version and add it as a component // (the code still comes from the jar mod installed above) - if (zipFileRoot.exists("/forgeversion.properties")) - { + if (zipFileRoot.exists("/forgeversion.properties")) { zipFile.setCurrentFile("forgeversion.properties", QuaZip::csSensitive); QuaZipFile file(&zipFile); - if (!file.open(QIODevice::ReadOnly)) - { + if (!file.open(QIODevice::ReadOnly)) { // Really shouldn't happen, but error handling shall not be forgotten emit failed(tr("Unable to open \"forgeversion.properties\"")); return; @@ -115,8 +109,7 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const revision = iniFile["forge.revision.number"].toString(); build = iniFile["forge.build.number"].toString(); - if (major.isEmpty() || minor.isEmpty() || revision.isEmpty() || build.isEmpty()) - { + if (major.isEmpty() || minor.isEmpty() || revision.isEmpty() || build.isEmpty()) { emit failed(tr("Invalid \"forgeversion.properties\"!")); return; } @@ -128,84 +121,63 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const emit succeeded(); return; } - } - else if (QFile::exists(versionJson)) - { + } else if (QFile::exists(versionJson)) { QFile file(versionJson); - if (!file.open(QIODevice::ReadOnly)) - { + if (!file.open(QIODevice::ReadOnly)) { emit failed(tr("Unable to open \"version.json\"!")); return; } data = file.readAll(); file.close(); - } - else - { + } else { // This is the "Vanilla" modpack, excluded by the search code emit failed(tr("Unable to find a \"version.json\"!")); return; } - try - { + try { QJsonDocument doc = Json::requireDocument(data); QJsonObject root = Json::requireObject(doc, "version.json"); - QString minecraftVersion = Json::ensureString(root, "inheritsFrom", QString(), ""); - if (minecraftVersion.isEmpty()) - { - if (fmlMinecraftVersion.isEmpty()) - { + QString packMinecraftVersion = Json::ensureString(root, "inheritsFrom", QString(), ""); + if (packMinecraftVersion.isEmpty()) { + if (fmlMinecraftVersion.isEmpty()) { emit failed(tr("Could not understand \"version.json\":\ninheritsFrom is missing")); return; } - minecraftVersion = fmlMinecraftVersion; + packMinecraftVersion = fmlMinecraftVersion; } - components->setComponentVersion("net.minecraft", minecraftVersion, true); - for (auto library: Json::ensureArray(root, "libraries", {})) - { - if (!library.isObject()) - { + components->setComponentVersion("net.minecraft", packMinecraftVersion, true); + for (auto library : Json::ensureArray(root, "libraries", {})) { + if (!library.isObject()) { continue; } auto libraryObject = Json::ensureObject(library, {}, ""); auto libraryName = Json::ensureString(libraryObject, "name", "", ""); - if ((libraryName.startsWith("net.minecraftforge:forge:") || libraryName.startsWith("net.minecraftforge:fmlloader:")) && libraryName.contains('-')) - { + if ((libraryName.startsWith("net.minecraftforge:forge:") || libraryName.startsWith("net.minecraftforge:fmlloader:")) && + libraryName.contains('-')) { QString libraryVersion = libraryName.section(':', 2); - if (!libraryVersion.startsWith("1.7.10-")) - { + if (!libraryVersion.startsWith("1.7.10-")) { components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1)); - } - else - { + } else { // 1.7.10 versions sometimes look like 1.7.10-10.13.4.1614-1.7.10, this filters out the 10.13.4.1614 part components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1, 1)); } - } - else - { + } else { // -> - static QMap loaderMap { - {"net.minecraftforge:minecraftforge:", "net.minecraftforge"}, - {"net.fabricmc:fabric-loader:", "net.fabricmc.fabric-loader"}, - {"org.quiltmc:quilt-loader:", "org.quiltmc.quilt-loader"} - }; - for (const auto& loader : loaderMap.keys()) - { - if (libraryName.startsWith(loader)) - { + static QMap loaderMap{ { "net.minecraftforge:minecraftforge:", "net.minecraftforge" }, + { "net.fabricmc:fabric-loader:", "net.fabricmc.fabric-loader" }, + { "org.quiltmc:quilt-loader:", "org.quiltmc.quilt-loader" } }; + for (const auto& loader : loaderMap.keys()) { + if (libraryName.startsWith(loader)) { components->setComponentVersion(loaderMap.value(loader), libraryName.section(':', 2)); break; } } } } - } - catch (const JSONValidationError &e) - { + } catch (const JSONValidationError& e) { emit failed(tr("Could not understand \"version.json\":\n") + e.cause()); return; } diff --git a/launcher/modplatform/technic/TechnicPackProcessor.h b/launcher/modplatform/technic/TechnicPackProcessor.h index 2ad803b34..466bce596 100644 --- a/launcher/modplatform/technic/TechnicPackProcessor.h +++ b/launcher/modplatform/technic/TechnicPackProcessor.h @@ -18,18 +18,21 @@ #include #include "settings/SettingsObject.h" -namespace Technic -{ - // not exporting it, only used in SingleZipPackInstallTask, InstanceImportTask and SolderPackInstallTask - class TechnicPackProcessor : public QObject - { - Q_OBJECT +namespace Technic { +// not exporting it, only used in SingleZipPackInstallTask, InstanceImportTask and SolderPackInstallTask +class TechnicPackProcessor : public QObject { + Q_OBJECT - signals: - void succeeded(); - void failed(QString reason); + signals: + void succeeded(); + void failed(QString reason); - public: - void run(SettingsObjectPtr globalSettings, const QString &instName, const QString &instIcon, const QString &stagingPath, const QString &minecraftVersion=QString(), const bool isSolder = false); - }; -} + public: + void run(SettingsObjectPtr globalSettings, + const QString& instName, + const QString& instIcon, + const QString& stagingPath, + const QString& minecraftVersion = QString(), + const bool isSolder = false); +}; +} // namespace Technic diff --git a/launcher/net/ApiDownload.cpp b/launcher/net/ApiDownload.cpp new file mode 100644 index 000000000..aaa8ff650 --- /dev/null +++ b/launcher/net/ApiDownload.cpp @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * 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 . + * + */ + +#include "net/ApiDownload.h" +#include "ByteArraySink.h" +#include "ChecksumValidator.h" +#include "MetaCacheSink.h" +#include "net/NetAction.h" + +namespace Net { + +auto ApiDownload::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr +{ + auto dl = makeShared(); + dl->m_url = url; + dl->setObjectName(QString("CACHE:") + url.toString()); + dl->m_options = options; + auto md5Node = new ChecksumValidator(QCryptographicHash::Md5); + auto cachedNode = new MetaCacheSink(entry, md5Node, options.testFlag(Option::MakeEternal)); + dl->m_sink.reset(cachedNode); + return dl; +} + +auto ApiDownload::makeByteArray(QUrl url, std::shared_ptr output, Options options) -> Download::Ptr +{ + auto dl = makeShared(); + dl->m_url = url; + dl->setObjectName(QString("BYTES:") + url.toString()); + dl->m_options = options; + dl->m_sink.reset(new ByteArraySink(output)); + return dl; +} + +auto ApiDownload::makeFile(QUrl url, QString path, Options options) -> Download::Ptr +{ + auto dl = makeShared(); + dl->m_url = url; + dl->setObjectName(QString("FILE:") + url.toString()); + dl->m_options = options; + dl->m_sink.reset(new FileSink(path)); + return dl; +} + +void ApiDownload::init() +{ + qDebug() << "Setting up api download"; + auto api_headers = new ApiHeaderProxy(); + addHeaderProxy(api_headers); +} +} // namespace Net diff --git a/launcher/net/ApiDownload.h b/launcher/net/ApiDownload.h new file mode 100644 index 000000000..638c94e11 --- /dev/null +++ b/launcher/net/ApiDownload.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * 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 . + * + */ + +#pragma once + +#include "ApiHeaderProxy.h" +#include "Download.h" + +namespace Net { + +class ApiDownload : public Download { + public: + virtual ~ApiDownload() = default; + + static auto makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions) -> Download::Ptr; + static auto makeByteArray(QUrl url, std::shared_ptr output, Options options = Option::NoOptions) -> Download::Ptr; + static auto makeFile(QUrl url, QString path, Options options = Option::NoOptions) -> Download::Ptr; + + void init() override; +}; + +} // namespace Net diff --git a/launcher/net/ApiHeaderProxy.h b/launcher/net/ApiHeaderProxy.h new file mode 100644 index 000000000..789a6fada --- /dev/null +++ b/launcher/net/ApiHeaderProxy.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * 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 . + * + */ + +#pragma once + +#include "Application.h" +#include "BuildConfig.h" +#include "net/HeaderProxy.h" + +namespace Net { + +class ApiHeaderProxy : public HeaderProxy { + public: + ApiHeaderProxy() : HeaderProxy() {} + virtual ~ApiHeaderProxy() = default; + + public: + virtual QList headers(const QNetworkRequest& request) const override + { + QList hdrs; + if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) { + hdrs.append({ "x-api-key", APPLICATION->getFlameAPIKey().toUtf8() }); + } else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || + request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { + QString token = APPLICATION->getModrinthAPIToken(); + if (!token.isNull()) + hdrs.append({ "Authorization", token.toUtf8() }); + } + return hdrs; + }; +}; + +} // namespace Net diff --git a/launcher/net/ApiUpload.cpp b/launcher/net/ApiUpload.cpp new file mode 100644 index 000000000..c1221b764 --- /dev/null +++ b/launcher/net/ApiUpload.cpp @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * 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 . + * + */ + +#include "net/ApiUpload.h" +#include "ByteArraySink.h" +#include "ChecksumValidator.h" +#include "MetaCacheSink.h" +#include "net/NetAction.h" + +namespace Net { + +Upload::Ptr ApiUpload::makeByteArray(QUrl url, std::shared_ptr output, QByteArray m_post_data) +{ + auto up = makeShared(); + up->m_url = std::move(url); + up->m_sink.reset(new ByteArraySink(output)); + up->m_post_data = std::move(m_post_data); + return up; +} + +void ApiUpload::init() +{ + qDebug() << "Setting up api upload"; + auto api_headers = new ApiHeaderProxy(); + addHeaderProxy(api_headers); +} +} // namespace Net diff --git a/launcher/net/ApiUpload.h b/launcher/net/ApiUpload.h new file mode 100644 index 000000000..b12842b05 --- /dev/null +++ b/launcher/net/ApiUpload.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * 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 . + * + */ + +#pragma once + +#include "ApiHeaderProxy.h" +#include "Upload.h" + +namespace Net { + +class ApiUpload : public Upload { + public: + virtual ~ApiUpload() = default; + + static Upload::Ptr makeByteArray(QUrl url, std::shared_ptr output, QByteArray m_post_data); + + void init() override; +}; + +} // namespace Net diff --git a/launcher/net/ByteArraySink.h b/launcher/net/ByteArraySink.h index d6b17d605..7b8f0f8aa 100644 --- a/launcher/net/ByteArraySink.h +++ b/launcher/net/ByteArraySink.h @@ -42,8 +42,6 @@ namespace Net { /* * Sink object for downloads that uses an external QByteArray it doesn't own as a target. - * FIXME: It is possible that the QByteArray is freed while we're doing some operation on it, - * causing a segmentation fault. */ class ByteArraySink : public Sink { public: diff --git a/launcher/net/ChecksumValidator.h b/launcher/net/ChecksumValidator.h index a2ca2c7a4..dfee0aee5 100644 --- a/launcher/net/ChecksumValidator.h +++ b/launcher/net/ChecksumValidator.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index 4ea45c635..d25447b2d 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -47,17 +47,11 @@ #include "ChecksumValidator.h" #include "MetaCacheSink.h" -#include "Application.h" -#include "BuildConfig.h" - -#include "net/Logging.h" #include "net/NetAction.h" -#include "MMCTime.h" -#include "StringUtils.h" - namespace Net { +#if defined(LAUNCHER_APPLICATION) auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr { auto dl = makeShared(); @@ -69,6 +63,7 @@ auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Down dl->m_sink.reset(cachedNode); return dl; } +#endif auto Download::makeByteArray(QUrl url, std::shared_ptr output, Options options) -> Download::Ptr { @@ -90,260 +85,8 @@ auto Download::makeFile(QUrl url, QString path, Options options) -> Download::Pt return dl; } -void Download::addValidator(Validator* v) +QNetworkReply* Download::getReply(QNetworkRequest& request) { - m_sink->addValidator(v); -} - -void Download::executeTask() -{ - setStatus(tr("Downloading %1").arg(StringUtils::truncateUrlHumanFriendly(m_url, 80))); - - if (getState() == Task::State::AbortedByUser) { - qCWarning(taskDownloadLogC) << getUid().toString() << "Attempt to start an aborted Download:" << m_url.toString(); - emitAborted(); - return; - } - - QNetworkRequest request(m_url); - m_state = m_sink->init(request); - switch (m_state) { - case State::Succeeded: - emit succeeded(); - qCDebug(taskDownloadLogC) << getUid().toString() << "Download cache hit " << m_url.toString(); - return; - case State::Running: - qCDebug(taskDownloadLogC) << getUid().toString() << "Downloading " << m_url.toString(); - break; - case State::Inactive: - case State::Failed: - emitFailed(); - return; - case State::AbortedByUser: - emitAborted(); - return; - } - - request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8()); - // TODO remove duplication - if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) { - request.setRawHeader("x-api-key", APPLICATION->getFlameAPIKey().toUtf8()); - } else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || - request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { - QString token = APPLICATION->getModrinthAPIToken(); - if (!token.isNull()) - request.setRawHeader("Authorization", token.toUtf8()); - } - -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) - request.setTransferTimeout(); -#endif - - m_last_progress_time = m_clock.now(); - m_last_progress_bytes = 0; - - QNetworkReply* rep = m_network->get(request); - m_reply.reset(rep); - connect(rep, &QNetworkReply::downloadProgress, this, &Download::downloadProgress); - connect(rep, &QNetworkReply::finished, this, &Download::downloadFinished); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &Download::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &Download::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &Download::sslErrors); - connect(rep, &QNetworkReply::readyRead, this, &Download::downloadReadyRead); -} - -void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) -{ - auto now = m_clock.now(); - auto elapsed = now - m_last_progress_time; - - // use milliseconds for speed precision - auto elapsed_ms = std::chrono::duration_cast(elapsed); - auto bytes_received_since = bytesReceived - m_last_progress_bytes; - auto dl_speed_bps = (double)bytes_received_since / elapsed_ms.count() * 1000; - auto remaing_time_s = (bytesTotal - bytesReceived) / dl_speed_bps; - - //: Current amount of bytes downloaded, out of the total amount of bytes in the download - QString dl_progress = - tr("%1 / %2").arg(StringUtils::humanReadableFileSize(bytesReceived)).arg(StringUtils::humanReadableFileSize(bytesTotal)); - - QString dl_speed_str; - if (elapsed_ms.count() > 0) { - auto str_eta = bytesTotal > 0 ? Time::humanReadableDuration(remaing_time_s) : tr("unknown"); - //: Download speed, in bytes per second (remaining download time in parenthesis) - dl_speed_str = - tr("%1 /s (%2)").arg(StringUtils::humanReadableFileSize(dl_speed_bps)).arg(str_eta); - } else { - //: Download speed at 0 bytes per second - dl_speed_str = tr("0 B/s"); - } - - setDetails(dl_progress + "\n" + dl_speed_str); - - setProgress(bytesReceived, bytesTotal); -} - -void Download::downloadError(QNetworkReply::NetworkError error) -{ - if (error == QNetworkReply::OperationCanceledError) { - qCCritical(taskDownloadLogC) << getUid().toString() << "Aborted " << m_url.toString(); - m_state = State::AbortedByUser; - } else { - if (m_options & Option::AcceptLocalFiles) { - if (m_sink->hasLocalData()) { - m_state = State::Succeeded; - return; - } - } - // error happened during download. - qCCritical(taskDownloadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error; - m_state = State::Failed; - } -} - -void Download::sslErrors(const QList& errors) -{ - int i = 1; - for (auto error : errors) { - qCCritical(taskDownloadLogC) << getUid().toString() << "Download" << m_url.toString() << "SSL Error #" << i << " : " - << error.errorString(); - auto cert = error.certificate(); - qCCritical(taskDownloadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -auto Download::handleRedirect() -> bool -{ - QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl(); - if (!redirect.isValid()) { - if (!m_reply->hasRawHeader("Location")) { - // no redirect -> it's fine to continue - return false; - } - // there is a Location header, but it's not correct. we need to apply some workarounds... - QByteArray redirectBA = m_reply->rawHeader("Location"); - if (redirectBA.size() == 0) { - // empty, yet present redirect header? WTF? - return false; - } - QString redirectStr = QString::fromUtf8(redirectBA); - - if (redirectStr.startsWith("//")) { - /* - * IF the URL begins with //, we need to insert the URL scheme. - * See: https://bugreports.qt.io/browse/QTBUG-41061 - * See: http://tools.ietf.org/html/rfc3986#section-4.2 - */ - redirectStr = m_reply->url().scheme() + ":" + redirectStr; - } else if (redirectStr.startsWith("/")) { - /* - * IF the URL begins with /, we need to process it as a relative URL - */ - auto url = m_reply->url(); - url.setPath(redirectStr, QUrl::TolerantMode); - redirectStr = url.toString(); - } - - /* - * Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues. - * FIXME: report Qt bug for this - */ - redirect = QUrl(redirectStr, QUrl::TolerantMode); - if (!redirect.isValid()) { - qCWarning(taskDownloadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr; - downloadError(QNetworkReply::ProtocolFailure); - return false; - } - qCDebug(taskDownloadLogC) << getUid().toString() << "Fixed location header:" << redirect; - } else { - qCDebug(taskDownloadLogC) << getUid().toString() << "Location header:" << redirect; - } - - m_url = QUrl(redirect.toString()); - qCDebug(taskDownloadLogC) << getUid().toString() << "Following redirect to " << m_url.toString(); - startAction(m_network); - - return true; -} - -void Download::downloadFinished() -{ - // handle HTTP redirection first - if (handleRedirect()) { - qCDebug(taskDownloadLogC) << getUid().toString() << "Download redirected:" << m_url.toString(); - return; - } - - // if the download failed before this point ... - if (m_state == State::Succeeded) // pretend to succeed so we continue processing :) - { - qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed but we are allowed to proceed:" << m_url.toString(); - m_sink->abort(); - m_reply.reset(); - emit succeeded(); - return; - } else if (m_state == State::Failed) { - qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed in previous step:" << m_url.toString(); - m_sink->abort(); - m_reply.reset(); - emit failed(""); - return; - } else if (m_state == State::AbortedByUser) { - qCDebug(taskDownloadLogC) << getUid().toString() << "Download aborted in previous step:" << m_url.toString(); - m_sink->abort(); - m_reply.reset(); - emit aborted(); - return; - } - - // make sure we got all the remaining data, if any - auto data = m_reply->readAll(); - if (data.size()) { - qCDebug(taskDownloadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes"; - m_state = m_sink->write(data); - } - - // otherwise, finalize the whole graph - m_state = m_sink->finalize(*m_reply.get()); - if (m_state != State::Succeeded) { - qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed to finalize:" << m_url.toString(); - m_sink->abort(); - m_reply.reset(); - emit failed(""); - return; - } - - m_reply.reset(); - qCDebug(taskDownloadLogC) << getUid().toString() << "Download succeeded:" << m_url.toString(); - emit succeeded(); -} - -void Download::downloadReadyRead() -{ - if (m_state == State::Running) { - auto data = m_reply->readAll(); - m_state = m_sink->write(data); - if (m_state == State::Failed) { - qCCritical(taskDownloadLogC) << getUid().toString() << "Failed to process response chunk"; - } - // qDebug() << "Download" << m_url.toString() << "gained" << data.size() << "bytes"; - } else { - qCCritical(taskDownloadLogC) << getUid().toString() << "Cannot write download data! illegal status " << m_status; - } -} - -} // namespace Net - -auto Net::Download::abort() -> bool -{ - if (m_reply) { - m_reply->abort(); - } else { - m_state = State::AbortedByUser; - } - return true; + return m_network->get(request); } +} // namespace Net \ No newline at end of file diff --git a/launcher/net/Download.h b/launcher/net/Download.h index 2e861732d..5f6a5caf1 100644 --- a/launcher/net/Download.h +++ b/launcher/net/Download.h @@ -38,57 +38,26 @@ #pragma once -#include - #include "HttpMetaCache.h" -#include "NetAction.h" -#include "Sink.h" -#include "Validator.h" #include "QObjectPtr.h" +#include "net/NetRequest.h" namespace Net { -class Download : public NetAction { +class Download : public NetRequest { Q_OBJECT - public: using Ptr = shared_qobject_ptr; - enum class Option { NoOptions = 0, AcceptLocalFiles = 1, MakeEternal = 2 }; - Q_DECLARE_FLAGS(Options, Option) - - public: - ~Download() override = default; + explicit Download() : NetRequest() { logCat = taskDownloadLogC; } +#if defined(LAUNCHER_APPLICATION) static auto makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions) -> Download::Ptr; +#endif + static auto makeByteArray(QUrl url, std::shared_ptr output, Options options = Option::NoOptions) -> Download::Ptr; static auto makeFile(QUrl url, QString path, Options options = Option::NoOptions) -> Download::Ptr; - public: - void addValidator(Validator* v); - auto abort() -> bool override; - auto canAbort() const -> bool override { return true; }; - - private: - auto handleRedirect() -> bool; - - protected slots: - void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; - void downloadError(QNetworkReply::NetworkError error) override; - void sslErrors(const QList& errors) override; - void downloadFinished() override; - void downloadReadyRead() override; - - public slots: - void executeTask() override; - - private: - std::unique_ptr m_sink; - Options m_options; - - std::chrono::steady_clock m_clock; - std::chrono::time_point m_last_progress_time; - qint64 m_last_progress_bytes; + protected: + virtual QNetworkReply* getReply(QNetworkRequest&) override; }; } // namespace Net - -Q_DECLARE_OPERATORS_FOR_FLAGS(Net::Download::Options) diff --git a/launcher/net/HeaderProxy.h b/launcher/net/HeaderProxy.h new file mode 100644 index 000000000..20362924f --- /dev/null +++ b/launcher/net/HeaderProxy.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * 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 . + * + */ + +#pragma once + +#include +#include + +namespace Net { + +struct HeaderPair { + QByteArray headerName; + QByteArray headerValue; +}; + +class HeaderProxy { + public: + HeaderProxy() {} + virtual ~HeaderProxy() {} + + public: + virtual QList headers(const QNetworkRequest& request) const = 0; + + public: + void writeHeaders(QNetworkRequest& request) + { + for (auto header : headers(request)) { + request.setRawHeader(header.headerName, header.headerValue); + } + } +}; + +} // namespace Net diff --git a/launcher/net/HttpMetaCache.cpp b/launcher/net/HttpMetaCache.cpp index 689dbac96..7809d40fc 100644 --- a/launcher/net/HttpMetaCache.cpp +++ b/launcher/net/HttpMetaCache.cpp @@ -125,8 +125,9 @@ auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString ex // Get rid of old entries, to prevent cache problems auto current_time = QDateTime::currentSecsSinceEpoch(); - if (entry->isExpired(current_time - ( file_last_changed / 1000 ))) { - qCWarning(taskNetLogC) << "[HttpMetaCache]" << "Removing cache entry because of old age!"; + if (entry->isExpired(current_time - (file_last_changed / 1000))) { + qCWarning(taskNetLogC) << "[HttpMetaCache]" + << "Removing cache entry because of old age!"; selected_base.entry_list.remove(resource_path); return staleEntry(base, resource_path); } diff --git a/launcher/net/HttpMetaCache.h b/launcher/net/HttpMetaCache.h index 0dcb5668d..036a8dd94 100644 --- a/launcher/net/HttpMetaCache.h +++ b/launcher/net/HttpMetaCache.h @@ -74,7 +74,7 @@ class MetaEntry { auto getMaximumAge() -> qint64 { return m_max_age; } void setMaximumAge(qint64 age) { m_max_age = age; } - bool isExpired(qint64 offset) { return !m_is_eternal && (m_current_age >= m_max_age - offset); }; + bool isExpired(qint64 offset) { return !m_is_eternal && (m_current_age >= m_max_age - offset); } protected: QString m_baseId; diff --git a/launcher/net/Logging.h b/launcher/net/Logging.h index b692e7075..4deed2b49 100644 --- a/launcher/net/Logging.h +++ b/launcher/net/Logging.h @@ -16,8 +16,8 @@ * along with this program. If not, see . * */ - -#pragma once + +#pragma once #include diff --git a/launcher/net/MetaCacheSink.cpp b/launcher/net/MetaCacheSink.cpp index e203bc06d..889588a11 100644 --- a/launcher/net/MetaCacheSink.cpp +++ b/launcher/net/MetaCacheSink.cpp @@ -46,32 +46,27 @@ namespace Net { /** Maximum time to hold a cache entry * = 1 week in seconds */ -#define MAX_TIME_TO_EXPIRE 1*7*24*60*60 +#define MAX_TIME_TO_EXPIRE 1 * 7 * 24 * 60 * 60 - -MetaCacheSink::MetaCacheSink(MetaEntryPtr entry, ChecksumValidator * md5sum, bool is_eternal) - :Net::FileSink(entry->getFullPath()), m_entry(entry), m_md5Node(md5sum), m_is_eternal(is_eternal) +MetaCacheSink::MetaCacheSink(MetaEntryPtr entry, ChecksumValidator* md5sum, bool is_eternal) + : Net::FileSink(entry->getFullPath()), m_entry(entry), m_md5Node(md5sum), m_is_eternal(is_eternal) { addValidator(md5sum); } Task::State MetaCacheSink::initCache(QNetworkRequest& request) { - if (!m_entry->isStale()) - { + if (!m_entry->isStale()) { return Task::State::Succeeded; } // check if file exists, if it does, use its information for the request QFile current(m_filename); - if(current.exists() && current.size() != 0) - { - if (m_entry->getRemoteChangedTimestamp().size()) - { + if (current.exists() && current.size() != 0) { + if (m_entry->getRemoteChangedTimestamp().size()) { request.setRawHeader(QString("If-Modified-Since").toLatin1(), m_entry->getRemoteChangedTimestamp().toLatin1()); } - if (m_entry->getETag().size()) - { + if (m_entry->getETag().size()) { request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->getETag().toLatin1()); } } @@ -79,25 +74,23 @@ Task::State MetaCacheSink::initCache(QNetworkRequest& request) return Task::State::Running; } -Task::State MetaCacheSink::finalizeCache(QNetworkReply & reply) +Task::State MetaCacheSink::finalizeCache(QNetworkReply& reply) { QFileInfo output_file_info(m_filename); - if(wroteAnyData) - { + if (wroteAnyData) { m_entry->setMD5Sum(m_md5Node->hash().toHex().constData()); } m_entry->setETag(reply.rawHeader("ETag").constData()); - if (reply.hasRawHeader("Last-Modified")) - { + if (reply.hasRawHeader("Last-Modified")) { m_entry->setRemoteChangedTimestamp(reply.rawHeader("Last-Modified").constData()); } m_entry->setLocalChangedTimestamp(output_file_info.lastModified().toUTC().toMSecsSinceEpoch()); - { // Cache lifetime + { // Cache lifetime if (m_is_eternal) { qCDebug(taskMetaCacheLogC) << "Adding eternal cache entry:" << m_entry->getFullPath(); m_entry->makeEternal(true); @@ -141,4 +134,4 @@ bool MetaCacheSink::hasLocalData() QFileInfo info(m_filename); return info.exists() && info.size() != 0; } -} +} // namespace Net diff --git a/launcher/net/NetAction.h b/launcher/net/NetAction.h index ab9322c26..b66b91941 100644 --- a/launcher/net/NetAction.h +++ b/launcher/net/NetAction.h @@ -42,10 +42,12 @@ #include "QObjectPtr.h" #include "tasks/Task.h" +#include "HeaderProxy.h" + class NetAction : public Task { Q_OBJECT protected: - explicit NetAction() : Task(){}; + explicit NetAction() : Task() {} public: using Ptr = shared_qobject_ptr; @@ -56,13 +58,17 @@ class NetAction : public Task { void setNetwork(shared_qobject_ptr network) { m_network = network; } + void addHeaderProxy(Net::HeaderProxy* proxy) { m_headerProxies.push_back(std::shared_ptr(proxy)); } + virtual void init() = 0; + protected slots: virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0; virtual void downloadError(QNetworkReply::NetworkError error) = 0; virtual void downloadFinished() = 0; virtual void downloadReadyRead() = 0; - virtual void sslErrors(const QList& errors) { + virtual void sslErrors(const QList& errors) + { int i = 1; for (auto error : errors) { qCritical() << "Network SSL Error #" << i << " : " << error.errorString(); @@ -70,8 +76,7 @@ class NetAction : public Task { qCritical() << "Certificate in question:\n" << cert.toText(); i++; } - - }; + } public slots: void startAction(shared_qobject_ptr network) @@ -81,7 +86,7 @@ class NetAction : public Task { } protected: - void executeTask() override{}; + void executeTask() override {} public: shared_qobject_ptr m_network; @@ -91,4 +96,5 @@ class NetAction : public Task { /// source URL QUrl m_url; + std::vector> m_headerProxies; }; diff --git a/launcher/net/NetJob.h b/launcher/net/NetJob.h index 406ddda63..334745f9c 100644 --- a/launcher/net/NetJob.h +++ b/launcher/net/NetJob.h @@ -52,7 +52,9 @@ class NetJob : public ConcurrentTask { public: using Ptr = shared_qobject_ptr; - explicit NetJob(QString job_name, shared_qobject_ptr network) : ConcurrentTask(nullptr, job_name), m_network(network) {} + explicit NetJob(QString job_name, shared_qobject_ptr network) + : ConcurrentTask(nullptr, job_name), m_network(network) + {} ~NetJob() override = default; void startNext() override; diff --git a/launcher/net/NetRequest.cpp b/launcher/net/NetRequest.cpp new file mode 100644 index 000000000..ff59da18b --- /dev/null +++ b/launcher/net/NetRequest.cpp @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 TheKodeToad + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * 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 . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NetRequest.h" +#include + +#include +#include +#include + +#if defined(LAUNCHER_APPLICATION) +#include "Application.h" +#endif + +#include "net/NetAction.h" + +#include "MMCTime.h" +#include "StringUtils.h" + +namespace Net { + +void NetRequest::addValidator(Validator* v) +{ + m_sink->addValidator(v); +} + +void NetRequest::executeTask() +{ + init(); + + setStatus(tr("Requesting %1").arg(StringUtils::truncateUrlHumanFriendly(m_url, 80))); + + if (getState() == Task::State::AbortedByUser) { + qCWarning(logCat) << getUid().toString() << "Attempt to start an aborted Request:" << m_url.toString(); + emitAborted(); + return; + } + + QNetworkRequest request(m_url); + m_state = m_sink->init(request); + switch (m_state) { + case State::Succeeded: + qCDebug(logCat) << getUid().toString() << "Request cache hit " << m_url.toString(); + emit succeeded(); + emit finished(); + return; + case State::Running: + qCDebug(logCat) << getUid().toString() << "Runninng " << m_url.toString(); + break; + case State::Inactive: + case State::Failed: + emitFailed(); + return; + case State::AbortedByUser: + emitAborted(); + return; + } + +#if defined(LAUNCHER_APPLICATION) + auto user_agent = APPLICATION->getUserAgent(); +#else + auto user_agent = BuildConfig.USER_AGENT; +#endif + + request.setHeader(QNetworkRequest::UserAgentHeader, user_agent.toUtf8()); + for (auto& header_proxy : m_headerProxies) { + header_proxy->writeHeaders(request); + } + // TODO remove duplication + +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + request.setTransferTimeout(); +#endif + + m_last_progress_time = m_clock.now(); + m_last_progress_bytes = 0; + + QNetworkReply* rep = getReply(request); + m_reply.reset(rep); + connect(rep, &QNetworkReply::downloadProgress, this, &NetRequest::downloadProgress); + connect(rep, &QNetworkReply::finished, this, &NetRequest::downloadFinished); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 + connect(rep, &QNetworkReply::errorOccurred, this, &NetRequest::downloadError); +#else + connect(rep, QOverload::of(&QNetworkReply::error), this, &NetRequest::downloadError); +#endif + connect(rep, &QNetworkReply::sslErrors, this, &NetRequest::sslErrors); + connect(rep, &QNetworkReply::readyRead, this, &NetRequest::downloadReadyRead); +} + +void NetRequest::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + auto now = m_clock.now(); + auto elapsed = now - m_last_progress_time; + + // use milliseconds for speed precision + auto elapsed_ms = std::chrono::duration_cast(elapsed); + auto bytes_received_since = bytesReceived - m_last_progress_bytes; + auto dl_speed_bps = (double)bytes_received_since / elapsed_ms.count() * 1000; + auto remaining_time_s = (bytesTotal - bytesReceived) / dl_speed_bps; + + //: Current amount of bytes downloaded, out of the total amount of bytes in the download + QString dl_progress = + tr("%1 / %2").arg(StringUtils::humanReadableFileSize(bytesReceived)).arg(StringUtils::humanReadableFileSize(bytesTotal)); + + QString dl_speed_str; + if (elapsed_ms.count() > 0) { + auto str_eta = bytesTotal > 0 ? Time::humanReadableDuration(remaining_time_s) : tr("unknown"); + //: Download speed, in bytes per second (remaining download time in parenthesis) + dl_speed_str = tr("%1 /s (%2)").arg(StringUtils::humanReadableFileSize(dl_speed_bps)).arg(str_eta); + } else { + //: Download speed at 0 bytes per second + dl_speed_str = tr("0 B/s"); + } + + setDetails(dl_progress + "\n" + dl_speed_str); + + setProgress(bytesReceived, bytesTotal); +} + +void NetRequest::downloadError(QNetworkReply::NetworkError error) +{ + if (error == QNetworkReply::OperationCanceledError) { + qCCritical(logCat) << getUid().toString() << "Aborted " << m_url.toString(); + m_state = State::Failed; + } else { + if (m_options & Option::AcceptLocalFiles) { + if (m_sink->hasLocalData()) { + m_state = State::Succeeded; + return; + } + } + // error happened during download. + qCCritical(logCat) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error; + m_state = State::Failed; + } +} + +void NetRequest::sslErrors(const QList& errors) +{ + int i = 1; + for (auto error : errors) { + qCCritical(logCat) << getUid().toString() << "Request" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString(); + auto cert = error.certificate(); + qCCritical(logCat) << getUid().toString() << "Certificate in question:\n" << cert.toText(); + i++; + } +} + +auto NetRequest::handleRedirect() -> bool +{ + QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl(); + if (!redirect.isValid()) { + if (!m_reply->hasRawHeader("Location")) { + // no redirect -> it's fine to continue + return false; + } + // there is a Location header, but it's not correct. we need to apply some workarounds... + QByteArray redirectBA = m_reply->rawHeader("Location"); + if (redirectBA.size() == 0) { + // empty, yet present redirect header? WTF? + return false; + } + QString redirectStr = QString::fromUtf8(redirectBA); + + if (redirectStr.startsWith("//")) { + /* + * IF the URL begins with //, we need to insert the URL scheme. + * See: https://bugreports.qt.io/browse/QTBUG-41061 + * See: http://tools.ietf.org/html/rfc3986#section-4.2 + */ + redirectStr = m_reply->url().scheme() + ":" + redirectStr; + } else if (redirectStr.startsWith("/")) { + /* + * IF the URL begins with /, we need to process it as a relative URL + */ + auto url = m_reply->url(); + url.setPath(redirectStr, QUrl::TolerantMode); + redirectStr = url.toString(); + } + + /* + * Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues. + * FIXME: report Qt bug for this + */ + redirect = QUrl(redirectStr, QUrl::TolerantMode); + if (!redirect.isValid()) { + qCWarning(logCat) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr; + downloadError(QNetworkReply::ProtocolFailure); + return false; + } + qCDebug(logCat) << getUid().toString() << "Fixed location header:" << redirect; + } else { + qCDebug(logCat) << getUid().toString() << "Location header:" << redirect; + } + + m_url = QUrl(redirect.toString()); + qCDebug(logCat) << getUid().toString() << "Following redirect to " << m_url.toString(); + startAction(m_network); + + return true; +} + +void NetRequest::downloadFinished() +{ + // handle HTTP redirection first + if (handleRedirect()) { + qCDebug(logCat) << getUid().toString() << "Request redirected:" << m_url.toString(); + return; + } + + // if the download failed before this point ... + if (m_state == State::Succeeded) // pretend to succeed so we continue processing :) + { + qCDebug(logCat) << getUid().toString() << "Request failed but we are allowed to proceed:" << m_url.toString(); + m_sink->abort(); + m_reply.reset(); + emit succeeded(); + emit finished(); + return; + } else if (m_state == State::Failed) { + qCDebug(logCat) << getUid().toString() << "Request failed in previous step:" << m_url.toString(); + m_sink->abort(); + m_reply.reset(); + emit failed(""); + emit finished(); + return; + } else if (m_state == State::AbortedByUser) { + qCDebug(logCat) << getUid().toString() << "Request aborted in previous step:" << m_url.toString(); + m_sink->abort(); + m_reply.reset(); + emit aborted(); + emit finished(); + return; + } + + // make sure we got all the remaining data, if any + auto data = m_reply->readAll(); + if (data.size()) { + qCDebug(logCat) << getUid().toString() << "Writing extra" << data.size() << "bytes"; + m_state = m_sink->write(data); + if (m_state != State::Succeeded) { + qCDebug(logCat) << getUid().toString() << "Request failed to write:" << m_url.toString(); + m_sink->abort(); + emit failed(""); + emit finished(); + return; + } + } + + // otherwise, finalize the whole graph + m_state = m_sink->finalize(*m_reply.get()); + if (m_state != State::Succeeded) { + qCDebug(logCat) << getUid().toString() << "Request failed to finalize:" << m_url.toString(); + m_sink->abort(); + m_reply.reset(); + emit failed(""); + emit finished(); + return; + } + + m_reply.reset(); + qCDebug(logCat) << getUid().toString() << "Request succeeded:" << m_url.toString(); + emit succeeded(); + emit finished(); +} + +void NetRequest::downloadReadyRead() +{ + if (m_state == State::Running) { + auto data = m_reply->readAll(); + m_state = m_sink->write(data); + if (m_state == State::Failed) { + qCCritical(logCat) << getUid().toString() << "Failed to process response chunk"; + } + // qDebug() << "Request" << m_url.toString() << "gained" << data.size() << "bytes"; + } else { + qCCritical(logCat) << getUid().toString() << "Cannot write download data! illegal status " << m_status; + } +} + +auto NetRequest::abort() -> bool +{ + m_state = State::AbortedByUser; + if (m_reply) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 + disconnect(m_reply.get(), &QNetworkReply::errorOccurred, nullptr, nullptr); +#else + disconnect(m_reply.get(), QOverload::of(&QNetworkReply::error), nullptr, nullptr); +#endif + m_reply->abort(); + } + return true; +} + +} // namespace Net diff --git a/launcher/net/NetRequest.h b/launcher/net/NetRequest.h new file mode 100644 index 000000000..ee47ab2a6 --- /dev/null +++ b/launcher/net/NetRequest.h @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * 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 . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include "NetAction.h" +#include "Sink.h" +#include "Validator.h" + +#include "QObjectPtr.h" +#include "net/Logging.h" + +namespace Net { +class NetRequest : public NetAction { + Q_OBJECT + protected: + explicit NetRequest() : NetAction() {} + + public: + using Ptr = shared_qobject_ptr; + enum class Option { NoOptions = 0, AcceptLocalFiles = 1, MakeEternal = 2 }; + Q_DECLARE_FLAGS(Options, Option) + + public: + ~NetRequest() override = default; + + void init() override {} + + public: + void addValidator(Validator* v); + auto abort() -> bool override; + auto canAbort() const -> bool override { return true; } + + private: + auto handleRedirect() -> bool; + virtual QNetworkReply* getReply(QNetworkRequest&) = 0; + + protected slots: + void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; + void downloadError(QNetworkReply::NetworkError error) override; + void sslErrors(const QList& errors) override; + void downloadFinished() override; + void downloadReadyRead() override; + + public slots: + void executeTask() override; + + protected: + std::unique_ptr m_sink; + Options m_options; + + typedef const QLoggingCategory& (*logCatFunc)(); + logCatFunc logCat = taskUploadLogC; + + std::chrono::steady_clock m_clock; + std::chrono::time_point m_last_progress_time; + qint64 m_last_progress_bytes; +}; +} // namespace Net + +Q_DECLARE_OPERATORS_FOR_FLAGS(Net::NetRequest::Options) diff --git a/launcher/net/NetUtils.h b/launcher/net/NetUtils.h index fa3bd8c03..cd517bcca 100644 --- a/launcher/net/NetUtils.h +++ b/launcher/net/NetUtils.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -22,23 +22,22 @@ #include namespace Net { - inline bool isApplicationError(QNetworkReply::NetworkError x) { - // Mainly taken from https://github.com/qt/qtbase/blob/dev/src/network/access/qhttpthreaddelegate.cpp - static QSet errors = { - QNetworkReply::ProtocolInvalidOperationError, - QNetworkReply::AuthenticationRequiredError, - QNetworkReply::ContentAccessDenied, - QNetworkReply::ContentNotFoundError, - QNetworkReply::ContentOperationNotPermittedError, - QNetworkReply::ProxyAuthenticationRequiredError, - QNetworkReply::ContentConflictError, - QNetworkReply::ContentGoneError, - QNetworkReply::InternalServerError, - QNetworkReply::OperationNotImplementedError, - QNetworkReply::ServiceUnavailableError, - QNetworkReply::UnknownServerError, - QNetworkReply::UnknownContentError - }; - return errors.contains(x); - } +inline bool isApplicationError(QNetworkReply::NetworkError x) +{ + // Mainly taken from https://github.com/qt/qtbase/blob/dev/src/network/access/qhttpthreaddelegate.cpp + static QSet errors = { QNetworkReply::ProtocolInvalidOperationError, + QNetworkReply::AuthenticationRequiredError, + QNetworkReply::ContentAccessDenied, + QNetworkReply::ContentNotFoundError, + QNetworkReply::ContentOperationNotPermittedError, + QNetworkReply::ProxyAuthenticationRequiredError, + QNetworkReply::ContentConflictError, + QNetworkReply::ContentGoneError, + QNetworkReply::InternalServerError, + QNetworkReply::OperationNotImplementedError, + QNetworkReply::ServiceUnavailableError, + QNetworkReply::UnknownServerError, + QNetworkReply::UnknownContentError }; + return errors.contains(x); +} } // namespace Net diff --git a/launcher/net/PasteUpload.cpp b/launcher/net/PasteUpload.cpp index 595279a3a..c67d3b23c 100644 --- a/launcher/net/PasteUpload.cpp +++ b/launcher/net/PasteUpload.cpp @@ -36,26 +36,26 @@ */ #include "PasteUpload.h" -#include "BuildConfig.h" #include "Application.h" +#include "BuildConfig.h" #include -#include +#include #include #include #include -#include +#include #include #include "net/Logging.h" -std::array PasteUpload::PasteTypes = { - {{"0x0.st", "https://0x0.st", ""}, - {"hastebin", "https://hst.sh", "/documents"}, - {"paste.gg", "https://paste.gg", "/api/v1/pastes"}, - {"mclo.gs", "https://api.mclo.gs", "/1/log"}}}; +std::array PasteUpload::PasteTypes = { { { "0x0.st", "https://0x0.st", "" }, + { "hastebin", "https://hst.sh", "/documents" }, + { "paste.gg", "https://paste.gg", "/api/v1/pastes" }, + { "mclo.gs", "https://api.mclo.gs", "/1/log" } } }; -PasteUpload::PasteUpload(QWidget *window, QString text, QString baseUrl, PasteType pasteType) : m_window(window), m_baseUrl(baseUrl), m_pasteType(pasteType), m_text(text.toUtf8()) +PasteUpload::PasteUpload(QWidget* window, QString text, QString baseUrl, PasteType pasteType) + : m_window(window), m_baseUrl(baseUrl), m_pasteType(pasteType), m_text(text.toUtf8()) { if (m_baseUrl == "") m_baseUrl = PasteTypes.at(pasteType).defaultBase; @@ -67,68 +67,64 @@ PasteUpload::PasteUpload(QWidget *window, QString text, QString baseUrl, PasteTy m_uploadUrl = m_baseUrl + PasteTypes.at(pasteType).endpointPath; } -PasteUpload::~PasteUpload() -{ -} +PasteUpload::~PasteUpload() {} void PasteUpload::executeTask() { - QNetworkRequest request{QUrl(m_uploadUrl)}; - QNetworkReply *rep{}; + QNetworkRequest request{ QUrl(m_uploadUrl) }; + QNetworkReply* rep{}; request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgentUncached().toUtf8()); switch (m_pasteType) { - case NullPointer: { - QHttpMultiPart *multiPart = - new QHttpMultiPart{QHttpMultiPart::FormDataType}; + case NullPointer: { + 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\""); - multiPart->append(filePart); + QHttpPart filePart; + filePart.setBody(m_text); + filePart.setHeader(QNetworkRequest::ContentTypeHeader, "text/plain"); + filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"file\"; filename=\"log.txt\""); + multiPart->append(filePart); - rep = APPLICATION->network()->post(request, multiPart); - multiPart->setParent(rep); + rep = APPLICATION->network()->post(request, multiPart); + multiPart->setParent(rep); - break; - } - case Hastebin: { - request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgentUncached().toUtf8()); - rep = APPLICATION->network()->post(request, m_text); - break; - } - case Mclogs: { - QUrlQuery postData; - postData.addQueryItem("content", m_text); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - rep = APPLICATION->network()->post(request, postData.toString().toUtf8()); - break; - } - case PasteGG: { - QJsonObject obj; - QJsonDocument doc; - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + break; + } + case Hastebin: { + request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgentUncached().toUtf8()); + rep = APPLICATION->network()->post(request, m_text); + break; + } + case Mclogs: { + QUrlQuery postData; + postData.addQueryItem("content", m_text); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + rep = APPLICATION->network()->post(request, postData.toString().toUtf8()); + break; + } + case PasteGG: { + QJsonObject obj; + QJsonDocument doc; + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - obj.insert("expires", QDateTime::currentDateTimeUtc().addDays(100).toString(Qt::DateFormat::ISODate)); + obj.insert("expires", QDateTime::currentDateTimeUtc().addDays(100).toString(Qt::DateFormat::ISODate)); - QJsonArray files; - QJsonObject logFileInfo; - QJsonObject logFileContentInfo; - logFileContentInfo.insert("format", "text"); - logFileContentInfo.insert("value", QString::fromUtf8(m_text)); - logFileInfo.insert("name", "log.txt"); - logFileInfo.insert("content", logFileContentInfo); - files.append(logFileInfo); + QJsonArray files; + QJsonObject logFileInfo; + QJsonObject logFileContentInfo; + logFileContentInfo.insert("format", "text"); + logFileContentInfo.insert("value", QString::fromUtf8(m_text)); + logFileInfo.insert("name", "log.txt"); + logFileInfo.insert("content", logFileContentInfo); + files.append(logFileInfo); - obj.insert("files", files); + obj.insert("files", files); - doc.setObject(obj); - rep = APPLICATION->network()->post(request, doc.toJson()); - break; - } + doc.setObject(obj); + rep = APPLICATION->network()->post(request, doc.toJson()); + break; + } } connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress); @@ -140,7 +136,6 @@ void PasteUpload::executeTask() connect(rep, QOverload::of(&QNetworkReply::error), this, &PasteUpload::downloadError); #endif - m_reply = std::shared_ptr(rep); setStatus(tr("Uploading to %1").arg(m_uploadUrl)); @@ -158,97 +153,81 @@ void PasteUpload::downloadFinished() QByteArray data = m_reply->readAll(); int statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - if (m_reply->error() != QNetworkReply::NetworkError::NoError) - { + if (m_reply->error() != QNetworkReply::NetworkError::NoError) { emitFailed(tr("Network error: %1").arg(m_reply->errorString())); m_reply.reset(); return; - } - else if (statusCode != 200 && statusCode != 201) - { + } else if (statusCode != 200 && statusCode != 201) { 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)); - qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned unexpected status code " << statusCode << " with body: " << data; + qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned unexpected status code " << statusCode + << " with body: " << data; m_reply.reset(); return; } - switch (m_pasteType) - { - case NullPointer: - m_pasteLink = QString::fromUtf8(data).trimmed(); - break; - case Hastebin: { - QJsonDocument jsonDoc{QJsonDocument::fromJson(data)}; - QJsonObject jsonObj{jsonDoc.object()}; - if (jsonObj.contains("key") && jsonObj["key"].isString()) - { - QString key = jsonDoc.object()["key"].toString(); - m_pasteLink = m_baseUrl + "/" + key; - } - else - { - emitFailed(tr("Error: %1 returned a malformed response body").arg(m_uploadUrl)); - qCCritical(taskUploadLogC) << getUid().toString() << getUid().toString() << m_uploadUrl << " returned malformed response body: " << data; - return; - } - break; - } - case Mclogs: { - QJsonDocument jsonDoc{QJsonDocument::fromJson(data)}; - QJsonObject jsonObj{jsonDoc.object()}; - if (jsonObj.contains("success") && jsonObj["success"].isBool()) - { - bool success = jsonObj["success"].toBool(); - if (success) - { - m_pasteLink = jsonObj["url"].toString(); - } - else - { - QString error = jsonObj["error"].toString(); - emitFailed(tr("Error: %1 returned an error: %2").arg(m_uploadUrl, error)); - qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned error: " << error; - qCCritical(taskUploadLogC) << getUid().toString() << "Response body: " << data; + switch (m_pasteType) { + case NullPointer: + m_pasteLink = QString::fromUtf8(data).trimmed(); + break; + case Hastebin: { + QJsonDocument jsonDoc{ QJsonDocument::fromJson(data) }; + QJsonObject jsonObj{ jsonDoc.object() }; + if (jsonObj.contains("key") && jsonObj["key"].isString()) { + QString key = jsonDoc.object()["key"].toString(); + m_pasteLink = m_baseUrl + "/" + key; + } else { + emitFailed(tr("Error: %1 returned a malformed response body").arg(m_uploadUrl)); + qCCritical(taskUploadLogC) << getUid().toString() << getUid().toString() << m_uploadUrl + << " returned malformed response body: " << data; return; } + break; } - else - { - emitFailed(tr("Error: %1 returned a malformed response body").arg(m_uploadUrl)); - qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned malformed response body: " << data; - return; - } - break; - } - case PasteGG: - QJsonDocument jsonDoc{QJsonDocument::fromJson(data)}; - QJsonObject jsonObj{jsonDoc.object()}; - if (jsonObj.contains("status") && jsonObj["status"].isString()) - { - QString status = jsonObj["status"].toString(); - if (status == "success") - { - m_pasteLink = m_baseUrl + "/p/anonymous/" + jsonObj["result"].toObject()["id"].toString(); - } - else - { - QString error = jsonObj["error"].toString(); - QString message = (jsonObj.contains("message") && jsonObj["message"].isString()) ? jsonObj["message"].toString() : "none"; - emitFailed(tr("Error: %1 returned an error code: %2\nError message: %3").arg(m_uploadUrl, error, message)); - qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned error: " << error; - qCCritical(taskUploadLogC) << getUid().toString() << "Error message: " << message; - qCCritical(taskUploadLogC) << getUid().toString() << "Response body: " << data; + case Mclogs: { + QJsonDocument jsonDoc{ QJsonDocument::fromJson(data) }; + QJsonObject jsonObj{ jsonDoc.object() }; + if (jsonObj.contains("success") && jsonObj["success"].isBool()) { + bool success = jsonObj["success"].toBool(); + if (success) { + m_pasteLink = jsonObj["url"].toString(); + } else { + QString error = jsonObj["error"].toString(); + emitFailed(tr("Error: %1 returned an error: %2").arg(m_uploadUrl, error)); + qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned error: " << error; + qCCritical(taskUploadLogC) << getUid().toString() << "Response body: " << data; + return; + } + } else { + emitFailed(tr("Error: %1 returned a malformed response body").arg(m_uploadUrl)); + qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned malformed response body: " << data; return; } + break; } - else - { - emitFailed(tr("Error: %1 returned a malformed response body").arg(m_uploadUrl)); - qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned malformed response body: " << data; - return; - } - break; + case PasteGG: + QJsonDocument jsonDoc{ QJsonDocument::fromJson(data) }; + QJsonObject jsonObj{ jsonDoc.object() }; + if (jsonObj.contains("status") && jsonObj["status"].isString()) { + QString status = jsonObj["status"].toString(); + if (status == "success") { + m_pasteLink = m_baseUrl + "/p/anonymous/" + jsonObj["result"].toObject()["id"].toString(); + } else { + QString error = jsonObj["error"].toString(); + QString message = + (jsonObj.contains("message") && jsonObj["message"].isString()) ? jsonObj["message"].toString() : "none"; + emitFailed(tr("Error: %1 returned an error code: %2\nError message: %3").arg(m_uploadUrl, error, message)); + qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned error: " << error; + qCCritical(taskUploadLogC) << getUid().toString() << "Error message: " << message; + qCCritical(taskUploadLogC) << getUid().toString() << "Response body: " << data; + return; + } + } else { + emitFailed(tr("Error: %1 returned a malformed response body").arg(m_uploadUrl)); + qCCritical(taskUploadLogC) << getUid().toString() << m_uploadUrl << " returned malformed response body: " << data; + return; + } + break; } emitSucceeded(); } diff --git a/launcher/net/PasteUpload.h b/launcher/net/PasteUpload.h index b72ab5b00..2ba6067c3 100644 --- a/launcher/net/PasteUpload.h +++ b/launcher/net/PasteUpload.h @@ -35,17 +35,16 @@ #pragma once -#include "tasks/Task.h" +#include #include #include -#include -#include #include +#include +#include "tasks/Task.h" -class PasteUpload : public Task -{ +class PasteUpload : public Task { Q_OBJECT -public: + public: enum PasteType : int { // 0x0.st NullPointer, @@ -68,26 +67,23 @@ public: static std::array PasteTypes; - PasteUpload(QWidget *window, QString text, QString url, PasteType pasteType); + PasteUpload(QWidget* window, QString text, QString url, PasteType pasteType); virtual ~PasteUpload(); - QString pasteLink() - { - return m_pasteLink; - } -protected: + QString pasteLink() { return m_pasteLink; } + + protected: virtual void executeTask(); -private: - QWidget *m_window; + private: + QWidget* m_window; QString m_pasteLink; QString m_baseUrl; QString m_uploadUrl; PasteType m_pasteType; QByteArray m_text; std::shared_ptr m_reply; -public -slots: + public slots: void downloadError(QNetworkReply::NetworkError); void downloadFinished(); }; diff --git a/launcher/net/RawHeaderProxy.h b/launcher/net/RawHeaderProxy.h new file mode 100644 index 000000000..09b3d4d02 --- /dev/null +++ b/launcher/net/RawHeaderProxy.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * 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 . + * + */ + +#pragma once + +#include "net/HeaderProxy.h" + +namespace Net { + +class RawHeaderProxy : public HeaderProxy { + public: + RawHeaderProxy() : HeaderProxy() {} + virtual ~RawHeaderProxy() = default; + + public: + virtual QList headers(const QNetworkRequest&) const override { return m_headers; }; + + void addHeader(const HeaderPair& header) { m_headers.append(header); } + void addHeader(const QByteArray& headerName, const QByteArray& headerValue) { m_headers.append({ headerName, headerValue }); } + void addHeaders(const QList& headers) { m_headers.append(headers); } + + private: + QList m_headers; +}; + +} // namespace Net diff --git a/launcher/net/Sink.h b/launcher/net/Sink.h index 3870f29bc..fcdabf372 100644 --- a/launcher/net/Sink.h +++ b/launcher/net/Sink.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/net/Upload.cpp b/launcher/net/Upload.cpp index 3f6f58290..726572e52 100644 --- a/launcher/net/Upload.cpp +++ b/launcher/net/Upload.cpp @@ -38,219 +38,16 @@ #include "Upload.h" +#include #include -#include "Application.h" -#include "BuildConfig.h" #include "ByteArraySink.h" -#include "net/Logging.h" - namespace Net { -bool Upload::abort() +QNetworkReply* Upload::getReply(QNetworkRequest& request) { - if (m_reply) { - m_reply->abort(); - } else { - m_state = State::AbortedByUser; - } - return true; -} - -void Upload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) -{ - setProgress(bytesReceived, bytesTotal); -} - -void Upload::downloadError(QNetworkReply::NetworkError error) -{ - if (error == QNetworkReply::OperationCanceledError) { - qCCritical(taskUploadLogC) << getUid().toString() << "Aborted " << m_url.toString(); - m_state = State::AbortedByUser; - } else { - // error happened during download. - qCCritical(taskUploadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error; - m_state = State::Failed; - } -} - -void Upload::sslErrors(const QList& errors) -{ - int i = 1; - for (const auto& error : errors) { - qCCritical(taskUploadLogC) << getUid().toString() << "Upload" << m_url.toString() << "SSL Error #" << i << " : " - << error.errorString(); - auto cert = error.certificate(); - qCCritical(taskUploadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -bool Upload::handleRedirect() -{ - QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl(); - if (!redirect.isValid()) { - if (!m_reply->hasRawHeader("Location")) { - // no redirect -> it's fine to continue - return false; - } - // there is a Location header, but it's not correct. we need to apply some workarounds... - QByteArray redirectBA = m_reply->rawHeader("Location"); - if (redirectBA.size() == 0) { - // empty, yet present redirect header? WTF? - return false; - } - QString redirectStr = QString::fromUtf8(redirectBA); - - if (redirectStr.startsWith("//")) { - /* - * IF the URL begins with //, we need to insert the URL scheme. - * See: https://bugreports.qt.io/browse/QTBUG-41061 - * See: http://tools.ietf.org/html/rfc3986#section-4.2 - */ - redirectStr = m_reply->url().scheme() + ":" + redirectStr; - } else if (redirectStr.startsWith("/")) { - /* - * IF the URL begins with /, we need to process it as a relative URL - */ - auto url = m_reply->url(); - url.setPath(redirectStr, QUrl::TolerantMode); - redirectStr = url.toString(); - } - - /* - * Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues. - * FIXME: report Qt bug for this - */ - redirect = QUrl(redirectStr, QUrl::TolerantMode); - if (!redirect.isValid()) { - qCWarning(taskUploadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr; - downloadError(QNetworkReply::ProtocolFailure); - return false; - } - qCDebug(taskUploadLogC) << getUid().toString() << "Fixed location header:" << redirect; - } else { - qCDebug(taskUploadLogC) << getUid().toString() << "Location header:" << redirect; - } - - m_url = QUrl(redirect.toString()); - qCDebug(taskUploadLogC) << getUid().toString() << "Following redirect to " << m_url.toString(); - startAction(m_network); - return true; -} - -void Upload::downloadFinished() -{ - // handle HTTP redirection first - // very unlikely for post requests, still can happen - if (handleRedirect()) { - qCDebug(taskUploadLogC) << getUid().toString() << "Upload redirected:" << m_url.toString(); - return; - } - - // if the download failed before this point ... - if (m_state == State::Succeeded) { - qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed but we are allowed to proceed:" << m_url.toString(); - m_sink->abort(); - m_reply.reset(); - emit succeeded(); - return; - } else if (m_state == State::Failed) { - qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed in previous step:" << m_url.toString(); - m_sink->abort(); - m_reply.reset(); - emit failed(""); - return; - } else if (m_state == State::AbortedByUser) { - qCDebug(taskUploadLogC) << getUid().toString() << "Upload aborted in previous step:" << m_url.toString(); - m_sink->abort(); - m_reply.reset(); - emit aborted(); - return; - } - - // make sure we got all the remaining data, if any - auto data = m_reply->readAll(); - if (data.size()) { - qCDebug(taskUploadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes"; - m_state = m_sink->write(data); - } - - // otherwise, finalize the whole graph - m_state = m_sink->finalize(*m_reply.get()); - if (m_state != State::Succeeded) { - qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed to finalize:" << m_url.toString(); - m_sink->abort(); - m_reply.reset(); - emit failed(""); - return; - } - m_reply.reset(); - qCDebug(taskUploadLogC) << getUid().toString() << "Upload succeeded:" << m_url.toString(); - emit succeeded(); -} - -void Upload::downloadReadyRead() -{ - if (m_state == State::Running) { - auto data = m_reply->readAll(); - m_state = m_sink->write(data); - } -} - -void Upload::executeTask() -{ - setStatus(tr("Uploading %1").arg(m_url.toString())); - - if (m_state == State::AbortedByUser) { - qCWarning(taskUploadLogC) << getUid().toString() << "Attempt to start an aborted Upload:" << m_url.toString(); - emit aborted(); - return; - } - QNetworkRequest request(m_url); - m_state = m_sink->init(request); - switch (m_state) { - case State::Succeeded: - emitSucceeded(); - qCDebug(taskUploadLogC) << getUid().toString() << "Upload cache hit " << m_url.toString(); - return; - case State::Running: - qCDebug(taskUploadLogC) << getUid().toString() << "Uploading " << m_url.toString(); - break; - case State::Inactive: - case State::Failed: - emitFailed(""); - return; - case State::AbortedByUser: - emitAborted(); - return; - } - - request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8()); - // TODO remove duplication - if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) { - request.setRawHeader("x-api-key", APPLICATION->getFlameAPIKey().toUtf8()); - } else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || - request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { - QString token = APPLICATION->getModrinthAPIToken(); - if (!token.isNull()) - request.setRawHeader("Authorization", token.toUtf8()); - } - - // TODO other types of post requests ? request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QNetworkReply* rep = m_network->post(request, m_post_data); - - m_reply.reset(rep); - connect(rep, &QNetworkReply::downloadProgress, this, &Upload::downloadProgress); - connect(rep, &QNetworkReply::finished, this, &Upload::downloadFinished); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &Upload::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &Upload::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &Upload::sslErrors); - connect(rep, &QNetworkReply::readyRead, this, &Upload::downloadReadyRead); + return m_network->post(request, m_post_data); } Upload::Ptr Upload::makeByteArray(QUrl url, std::shared_ptr output, QByteArray m_post_data) diff --git a/launcher/net/Upload.h b/launcher/net/Upload.h index 0b0c94976..f920e5561 100644 --- a/launcher/net/Upload.h +++ b/launcher/net/Upload.h @@ -37,36 +37,21 @@ #pragma once -#include "NetAction.h" -#include "Sink.h" +#include "net/NetRequest.h" namespace Net { -class Upload : public NetAction { +class Upload : public NetRequest { Q_OBJECT - public: using Ptr = shared_qobject_ptr; + explicit Upload() : NetRequest() { logCat = taskUploadLogC; }; static Upload::Ptr makeByteArray(QUrl url, std::shared_ptr output, QByteArray m_post_data); - auto abort() -> bool override; - auto canAbort() const -> bool override { return true; }; - protected slots: - void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; - void downloadError(QNetworkReply::NetworkError error) override; - void sslErrors(const QList& errors) override; - void downloadFinished() override; - void downloadReadyRead() override; - - public slots: - void executeTask() override; - - private: - std::unique_ptr m_sink; + protected: + virtual QNetworkReply* getReply(QNetworkRequest&) override; QByteArray m_post_data; - - bool handleRedirect(); }; } // namespace Net diff --git a/launcher/net/Validator.h b/launcher/net/Validator.h index 6b3d46352..92ac6ea15 100644 --- a/launcher/net/Validator.h +++ b/launcher/net/Validator.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * * 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 @@ -37,16 +37,15 @@ #include "net/NetAction.h" namespace Net { -class Validator -{ -public: /* con/des */ - Validator() {}; - virtual ~Validator() {}; +class Validator { + public: /* con/des */ + Validator() {} + virtual ~Validator() {} -public: /* methods */ - virtual bool init(QNetworkRequest & request) = 0; - virtual bool write(QByteArray & data) = 0; + public: /* methods */ + virtual bool init(QNetworkRequest& request) = 0; + virtual bool write(QByteArray& data) = 0; virtual bool abort() = 0; - virtual bool validate(QNetworkReply & reply) = 0; + virtual bool validate(QNetworkReply& reply) = 0; }; -} +} // namespace Net diff --git a/launcher/news/NewsChecker.cpp b/launcher/news/NewsChecker.cpp index 4f02bf5e0..33fb7eceb 100644 --- a/launcher/news/NewsChecker.cpp +++ b/launcher/news/NewsChecker.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -49,8 +49,7 @@ NewsChecker::NewsChecker(shared_qobject_ptr network, cons void NewsChecker::reloadNews() { // Start a netjob to download the RSS feed and call rssDownloadFinished() when it's done. - if (isLoadingNews()) - { + if (isLoadingNews()) { qDebug() << "Ignored request to reload news. Currently reloading already."; return; } @@ -113,7 +112,6 @@ void NewsChecker::rssDownloadFailed(QString reason) fail(tr("Failed to load news RSS feed:\n%1").arg(reason)); } - QList NewsChecker::getNewsEntries() const { return m_newsEntries; @@ -144,4 +142,3 @@ void NewsChecker::fail(const QString& errorMsg) m_newsNetJob.reset(); emit newsLoadingFailed(errorMsg); } - diff --git a/launcher/news/NewsChecker.h b/launcher/news/NewsChecker.h index 41babfff7..cdd621a20 100644 --- a/launcher/news/NewsChecker.h +++ b/launcher/news/NewsChecker.h @@ -15,18 +15,17 @@ #pragma once +#include #include #include -#include #include #include "NewsEntry.h" -class NewsChecker : public QObject -{ +class NewsChecker : public QObject { Q_OBJECT -public: + public: /*! * Constructs a news reader to read from the given RSS feed URL. */ @@ -57,7 +56,7 @@ public: */ void Q_SLOT reloadNews(); -signals: + signals: /*! * Signal fired after the news has finished loading. */ @@ -68,11 +67,11 @@ signals: */ void newsLoadingFailed(QString errorMsg); -protected slots: + protected slots: void rssDownloadFinished(); void rssDownloadFailed(QString reason); -protected: /* data */ + protected: /* data */ //! The URL for the RSS feed to fetch. QString m_feedUrl; @@ -95,11 +94,10 @@ protected: /* data */ shared_qobject_ptr m_network; -protected slots: + protected slots: /// Emits newsLoaded() and sets m_lastLoadError to empty string. void succeed(); /// Emits newsLoadingFailed() and sets m_lastLoadError to the given message. void fail(const QString& errorMsg); }; - diff --git a/launcher/news/NewsEntry.cpp b/launcher/news/NewsEntry.cpp index cfe07e864..ea25f2e54 100644 --- a/launcher/news/NewsEntry.cpp +++ b/launcher/news/NewsEntry.cpp @@ -18,16 +18,14 @@ #include #include -NewsEntry::NewsEntry(QObject* parent) : - QObject(parent) +NewsEntry::NewsEntry(QObject* parent) : QObject(parent) { this->title = tr("Untitled"); this->content = tr("No content."); this->link = ""; } -NewsEntry::NewsEntry(const QString& title, const QString& content, const QString& link, QObject* parent) : - QObject(parent) +NewsEntry::NewsEntry(const QString& title, const QString& content, const QString& link, QObject* parent) : QObject(parent) { this->title = title; this->content = content; @@ -37,21 +35,18 @@ NewsEntry::NewsEntry(const QString& title, const QString& content, const QString /*! * Gets the text content of the given child element as a QVariant. */ -inline QString childValue(const QDomElement& element, const QString& childName, QString defaultVal="") +inline QString childValue(const QDomElement& element, const QString& childName, QString defaultVal = "") { QDomNodeList nodes = element.elementsByTagName(childName); - if (nodes.count() > 0) - { - QDomElement element = nodes.at(0).toElement(); - return element.text(); - } - else - { + if (nodes.count() > 0) { + QDomElement elem = nodes.at(0).toElement(); + return elem.text(); + } else { return defaultVal; } } -bool NewsEntry::fromXmlElement(const QDomElement& element, NewsEntry* entry, QString* errorMsg) +bool NewsEntry::fromXmlElement(const QDomElement& element, NewsEntry* entry, [[maybe_unused]] QString* errorMsg) { QString title = childValue(element, "title", tr("Untitled")); QString content = childValue(element, "content", tr("No content.")); @@ -62,4 +57,3 @@ bool NewsEntry::fromXmlElement(const QDomElement& element, NewsEntry* entry, QSt entry->link = link; return true; } - diff --git a/launcher/news/NewsEntry.h b/launcher/news/NewsEntry.h index 1fe95623d..2d409a9fb 100644 --- a/launcher/news/NewsEntry.h +++ b/launcher/news/NewsEntry.h @@ -15,33 +15,31 @@ #pragma once +#include #include #include -#include #include -class NewsEntry : public QObject -{ +class NewsEntry : public QObject { Q_OBJECT -public: + public: /*! * Constructs an empty news entry. */ - explicit NewsEntry(QObject* parent=0); + explicit NewsEntry(QObject* parent = 0); /*! * Constructs a new news entry. * Note that content may contain HTML. */ - NewsEntry(const QString& title, const QString& content, const QString& link, 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. * If this fails, the function will return false and store an error message in the errorMsg pointer. */ - static bool fromXmlElement(const QDomElement& element, NewsEntry* entry, QString* errorMsg=0); - + static bool fromXmlElement(const QDomElement& element, NewsEntry* entry, QString* errorMsg = 0); //! The post title. QString title; @@ -54,4 +52,3 @@ public: }; typedef std::shared_ptr NewsEntryPtr; - diff --git a/launcher/pathmatcher/FSTreeMatcher.h b/launcher/pathmatcher/FSTreeMatcher.h index b84af2945..52f1404df 100644 --- a/launcher/pathmatcher/FSTreeMatcher.h +++ b/launcher/pathmatcher/FSTreeMatcher.h @@ -1,21 +1,15 @@ #pragma once -#include "IPathMatcher.h" #include #include +#include "IPathMatcher.h" -class FSTreeMatcher : public IPathMatcher -{ -public: - virtual ~FSTreeMatcher() {}; - FSTreeMatcher(SeparatorPrefixTree<'/'> & tree) : m_fsTree(tree) - { - } +class FSTreeMatcher : public IPathMatcher { + public: + virtual ~FSTreeMatcher(){}; + FSTreeMatcher(SeparatorPrefixTree<'/'>& tree) : m_fsTree(tree) {} - bool matches(const QString &string) const override - { - return m_fsTree.covers(string); - } + bool matches(const QString& string) const override { return m_fsTree.covers(string); } - SeparatorPrefixTree<'/'> & m_fsTree; + SeparatorPrefixTree<'/'>& m_fsTree; }; diff --git a/launcher/pathmatcher/IPathMatcher.h b/launcher/pathmatcher/IPathMatcher.h index 192782d7c..cd6121979 100644 --- a/launcher/pathmatcher/IPathMatcher.h +++ b/launcher/pathmatcher/IPathMatcher.h @@ -1,13 +1,12 @@ #pragma once -#include #include +#include -class IPathMatcher -{ -public: +class IPathMatcher { + public: typedef std::shared_ptr Ptr; -public: - virtual ~IPathMatcher(){}; - virtual bool matches(const QString &string) const = 0; + public: + virtual ~IPathMatcher() {} + virtual bool matches(const QString& string) const = 0; }; diff --git a/launcher/pathmatcher/MultiMatcher.h b/launcher/pathmatcher/MultiMatcher.h index 8bc1b6eed..101595809 100644 --- a/launcher/pathmatcher/MultiMatcher.h +++ b/launcher/pathmatcher/MultiMatcher.h @@ -1,26 +1,21 @@ -#include "IPathMatcher.h" #include #include +#include "IPathMatcher.h" -class MultiMatcher : public IPathMatcher -{ -public: - virtual ~MultiMatcher() {}; - MultiMatcher() - { - } - MultiMatcher &add(Ptr add) +class MultiMatcher : public IPathMatcher { + public: + virtual ~MultiMatcher(){}; + MultiMatcher() {} + MultiMatcher& add(Ptr add) { m_matchers.append(add); return *this; } - virtual bool matches(const QString &string) const override + virtual bool matches(const QString& string) const override { - for(auto iter: m_matchers) - { - if(iter->matches(string)) - { + for (auto iter : m_matchers) { + if (iter->matches(string)) { return true; } } diff --git a/launcher/pathmatcher/RegexpMatcher.h b/launcher/pathmatcher/RegexpMatcher.h index 825d488c3..a6a3e616d 100644 --- a/launcher/pathmatcher/RegexpMatcher.h +++ b/launcher/pathmatcher/RegexpMatcher.h @@ -1,36 +1,30 @@ -#include "IPathMatcher.h" #include +#include "IPathMatcher.h" -class RegexpMatcher : public IPathMatcher -{ -public: - virtual ~RegexpMatcher() {}; - RegexpMatcher(const QString ®exp) +class RegexpMatcher : public IPathMatcher { + public: + virtual ~RegexpMatcher() {} + RegexpMatcher(const QString& regexp) { m_regexp.setPattern(regexp); m_onlyFilenamePart = !regexp.contains('/'); } - RegexpMatcher &caseSensitive(bool cs = true) + RegexpMatcher& caseSensitive(bool cs = true) { - if(cs) - { + if (cs) { m_regexp.setPatternOptions(QRegularExpression::CaseInsensitiveOption); - } - else - { + } else { m_regexp.setPatternOptions(QRegularExpression::NoPatternOption); } return *this; } - virtual bool matches(const QString &string) const override + virtual bool matches(const QString& string) const override { - if(m_onlyFilenamePart) - { + if (m_onlyFilenamePart) { auto slash = string.lastIndexOf('/'); - if(slash != -1) - { + if (slash != -1) { auto part = string.mid(slash + 1); return m_regexp.match(part).hasMatch(); } diff --git a/launcher/resources/multimc/128x128/instances/forge.png b/launcher/resources/multimc/128x128/instances/forge.png new file mode 100644 index 000000000..d8ff79a53 Binary files /dev/null and b/launcher/resources/multimc/128x128/instances/forge.png differ diff --git a/launcher/resources/multimc/128x128/instances/liteloader.png b/launcher/resources/multimc/128x128/instances/liteloader.png new file mode 100644 index 000000000..646217de0 Binary files /dev/null and b/launcher/resources/multimc/128x128/instances/liteloader.png differ diff --git a/launcher/resources/multimc/index.theme b/launcher/resources/multimc/index.theme index 070e23f10..4da8072d9 100644 --- a/launcher/resources/multimc/index.theme +++ b/launcher/resources/multimc/index.theme @@ -1,5 +1,5 @@ [Icon Theme] -Name=multimc +Name=Legacy Comment=Default Icons Inherits=default Directories=8x8,16x16,22x22,24x24,32x32,32x32/instances,48x48,50x50/instances,64x64,128x128/instances,256x256,scalable,scalable/instances diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc index 2c00f28fa..8f079bb37 100644 --- a/launcher/resources/multimc/multimc.qrc +++ b/launcher/resources/multimc/multimc.qrc @@ -347,5 +347,10 @@ scalable/export.svg scalable/launch.svg scalable/server.svg + + scalable/instances/quiltmc.svg + scalable/instances/fabricmc.svg + 128x128/instances/forge.png + 128x128/instances/liteloader.png diff --git a/launcher/resources/multimc/scalable/instances/fabricmc.svg b/launcher/resources/multimc/scalable/instances/fabricmc.svg new file mode 100644 index 000000000..7bfc75487 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/fabricmc.svg @@ -0,0 +1,71 @@ + + + + diff --git a/launcher/resources/multimc/scalable/instances/quiltmc.svg b/launcher/resources/multimc/scalable/instances/quiltmc.svg new file mode 100644 index 000000000..a7aaca53f --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/quiltmc.svg @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + diff --git a/launcher/resources/pe_blue/index.theme b/launcher/resources/pe_blue/index.theme index c9e0d93ad..6d842b5d2 100644 --- a/launcher/resources/pe_blue/index.theme +++ b/launcher/resources/pe_blue/index.theme @@ -1,5 +1,5 @@ [Icon Theme] -Name=pe_blue +Name=Simple (Blue) Comment=Icons by pexner (blue) Inherits=multimc Directories=scalable diff --git a/launcher/resources/pe_colored/index.theme b/launcher/resources/pe_colored/index.theme index b757bbd79..bca5494f1 100644 --- a/launcher/resources/pe_colored/index.theme +++ b/launcher/resources/pe_colored/index.theme @@ -1,5 +1,5 @@ [Icon Theme] -Name=pe_colored +Name=Simple (Colored) Comment=Icons by pexner (colored) Inherits=multimc Directories=scalable diff --git a/launcher/resources/pe_dark/index.theme b/launcher/resources/pe_dark/index.theme index b7d1ad011..4cfbf09c4 100644 --- a/launcher/resources/pe_dark/index.theme +++ b/launcher/resources/pe_dark/index.theme @@ -1,5 +1,5 @@ [Icon Theme] -Name=pe_dark +Name=Simple (Dark) Comment=Icons by pexner (dark) Inherits=multimc Directories=scalable diff --git a/launcher/resources/pe_light/index.theme b/launcher/resources/pe_light/index.theme index c106acc8b..87b76d139 100644 --- a/launcher/resources/pe_light/index.theme +++ b/launcher/resources/pe_light/index.theme @@ -1,5 +1,5 @@ [Icon Theme] -Name=pe_light +Name=Simple (Light) Comment=Icons by pexner (light) Inherits=multimc Directories=scalable diff --git a/launcher/screenshots/ImgurAlbumCreation.cpp b/launcher/screenshots/ImgurAlbumCreation.cpp index ab425f1a0..d548c89ae 100644 --- a/launcher/screenshots/ImgurAlbumCreation.cpp +++ b/launcher/screenshots/ImgurAlbumCreation.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * @@ -36,15 +36,15 @@ #include "ImgurAlbumCreation.h" -#include +#include #include #include -#include +#include #include -#include +#include -#include "BuildConfig.h" #include "Application.h" +#include "BuildConfig.h" ImgurAlbumCreation::ImgurAlbumCreation(QList screenshots) : NetAction(), m_screenshots(screenshots) { @@ -62,19 +62,18 @@ void ImgurAlbumCreation::executeTask() request.setRawHeader("Accept", "application/json"); QStringList hashes; - for (auto shot : m_screenshots) - { + for (auto shot : m_screenshots) { hashes.append(shot->m_imgurDeleteHash); } const QByteArray data = "deletehashes=" + hashes.join(',').toUtf8() + "&title=Minecraft%20Screenshots&privacy=hidden"; - QNetworkReply *rep = APPLICATION->network()->post(request, data); + QNetworkReply* rep = APPLICATION->network()->post(request, data); m_reply.reset(rep); connect(rep, &QNetworkReply::uploadProgress, this, &ImgurAlbumCreation::downloadProgress); connect(rep, &QNetworkReply::finished, this, &ImgurAlbumCreation::downloadFinished); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 connect(rep, &QNetworkReply::errorOccurred, this, &ImgurAlbumCreation::downloadError); #else connect(rep, QOverload::of(&QNetworkReply::error), this, &ImgurAlbumCreation::downloadError); @@ -82,7 +81,7 @@ void ImgurAlbumCreation::executeTask() connect(rep, &QNetworkReply::sslErrors, this, &ImgurAlbumCreation::sslErrors); } -void ImgurAlbumCreation::downloadError(QNetworkReply::NetworkError error) +void ImgurAlbumCreation::downloadError([[maybe_unused]] QNetworkReply::NetworkError error) { qDebug() << m_reply->errorString(); m_state = State::Failed; @@ -90,21 +89,18 @@ void ImgurAlbumCreation::downloadError(QNetworkReply::NetworkError error) void ImgurAlbumCreation::downloadFinished() { - if (m_state != State::Failed) - { + if (m_state != State::Failed) { QByteArray data = m_reply->readAll(); m_reply.reset(); QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if (jsonError.error != QJsonParseError::NoError) - { + if (jsonError.error != QJsonParseError::NoError) { qDebug() << jsonError.errorString(); emitFailed(); return; } auto object = doc.object(); - if (!object.value("success").toBool()) - { + if (!object.value("success").toBool()) { qDebug() << doc.toJson(); emitFailed(); return; @@ -114,9 +110,7 @@ void ImgurAlbumCreation::downloadFinished() m_state = State::Succeeded; emit succeeded(); return; - } - else - { + } else { qDebug() << m_reply->readAll(); m_reply.reset(); emitFailed(); diff --git a/launcher/screenshots/ImgurAlbumCreation.h b/launcher/screenshots/ImgurAlbumCreation.h index 0228b6e4a..bb71bec2b 100644 --- a/launcher/screenshots/ImgurAlbumCreation.h +++ b/launcher/screenshots/ImgurAlbumCreation.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify @@ -35,40 +35,33 @@ #pragma once -#include "net/NetAction.h" #include "Screenshot.h" +#include "net/NetAction.h" typedef shared_qobject_ptr ImgurAlbumCreationPtr; -class ImgurAlbumCreation : public NetAction -{ -public: +class ImgurAlbumCreation : public NetAction { + public: explicit ImgurAlbumCreation(QList screenshots); static ImgurAlbumCreationPtr make(QList screenshots) { return ImgurAlbumCreationPtr(new ImgurAlbumCreation(screenshots)); } - QString deleteHash() const - { - return m_deleteHash; - } - QString id() const - { - return m_id; - } + QString deleteHash() const { return m_deleteHash; } + QString id() const { return m_id; } -protected -slots: + void init() override{}; + + protected slots: void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; void downloadError(QNetworkReply::NetworkError error) override; void downloadFinished() override; void downloadReadyRead() override {} -public -slots: + public slots: void executeTask() override; -private: + private: QList m_screenshots; QString m_deleteHash; diff --git a/launcher/screenshots/ImgurUpload.cpp b/launcher/screenshots/ImgurUpload.cpp index a50f9afae..29499a20d 100644 --- a/launcher/screenshots/ImgurUpload.cpp +++ b/launcher/screenshots/ImgurUpload.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * @@ -35,17 +35,17 @@ */ #include "ImgurUpload.h" -#include "BuildConfig.h" #include "Application.h" +#include "BuildConfig.h" -#include +#include +#include #include +#include #include #include -#include -#include +#include #include -#include ImgurUpload::ImgurUpload(ScreenShot::Ptr shot) : NetAction(), m_shot(shot) { @@ -63,13 +63,12 @@ void ImgurUpload::executeTask() request.setRawHeader("Accept", "application/json"); QFile f(m_shot->m_file.absoluteFilePath()); - if (!f.open(QFile::ReadOnly)) - { + if (!f.open(QFile::ReadOnly)) { emitFailed(); return; } - QHttpMultiPart *multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpPart filePart; filePart.setBody(f.readAll().toBase64()); filePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png"); @@ -84,12 +83,12 @@ void ImgurUpload::executeTask() namePart.setBody(m_shot->m_file.baseName().toUtf8()); multipart->append(namePart); - QNetworkReply *rep = m_network->post(request, multipart); + QNetworkReply* rep = m_network->post(request, multipart); m_reply.reset(rep); connect(rep, &QNetworkReply::uploadProgress, this, &ImgurUpload::downloadProgress); connect(rep, &QNetworkReply::finished, this, &ImgurUpload::downloadFinished); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 connect(rep, &QNetworkReply::errorOccurred, this, &ImgurUpload::downloadError); #else connect(rep, QOverload::of(&QNetworkReply::error), this, &ImgurUpload::downloadError); @@ -97,11 +96,10 @@ void ImgurUpload::executeTask() connect(rep, &QNetworkReply::sslErrors, this, &ImgurUpload::sslErrors); } -void ImgurUpload::downloadError(QNetworkReply::NetworkError error) +void ImgurUpload::downloadError([[maybe_unused]] QNetworkReply::NetworkError error) { qCritical() << "ImgurUpload failed with error" << m_reply->errorString() << "Server reply:\n" << m_reply->readAll(); - if(finished) - { + if (finished) { qCritical() << "Double finished ImgurUpload!"; return; } @@ -113,8 +111,7 @@ void ImgurUpload::downloadError(QNetworkReply::NetworkError error) void ImgurUpload::downloadFinished() { - if(finished) - { + if (finished) { qCritical() << "Double finished ImgurUpload!"; return; } @@ -122,8 +119,7 @@ void ImgurUpload::downloadFinished() m_reply.reset(); QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if (jsonError.error != QJsonParseError::NoError) - { + if (jsonError.error != QJsonParseError::NoError) { qDebug() << "imgur server did not reply with JSON" << jsonError.errorString(); finished = true; m_reply.reset(); @@ -131,8 +127,7 @@ void ImgurUpload::downloadFinished() return; } auto object = doc.object(); - if (!object.value("success").toBool()) - { + if (!object.value("success").toBool()) { qDebug() << "Screenshot upload not successful:" << doc.toJson(); finished = true; m_reply.reset(); diff --git a/launcher/screenshots/ImgurUpload.h b/launcher/screenshots/ImgurUpload.h index 404dc8765..542255b2d 100644 --- a/launcher/screenshots/ImgurUpload.h +++ b/launcher/screenshots/ImgurUpload.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify @@ -35,30 +35,27 @@ #pragma once -#include "net/NetAction.h" #include "Screenshot.h" +#include "net/NetAction.h" class ImgurUpload : public NetAction { -public: + public: using Ptr = shared_qobject_ptr; explicit ImgurUpload(ScreenShot::Ptr shot); - static Ptr make(ScreenShot::Ptr shot) { - return Ptr(new ImgurUpload(shot)); - } + static Ptr make(ScreenShot::Ptr shot) { return Ptr(new ImgurUpload(shot)); } + void init() override{}; -protected -slots: + protected slots: void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; void downloadError(QNetworkReply::NetworkError error) override; void downloadFinished() override; void downloadReadyRead() override {} -public -slots: + public slots: void executeTask() override; -private: + private: ScreenShot::Ptr m_shot; bool finished = true; }; diff --git a/launcher/screenshots/Screenshot.h b/launcher/screenshots/Screenshot.h index ca45aabff..767f39061 100644 --- a/launcher/screenshots/Screenshot.h +++ b/launcher/screenshots/Screenshot.h @@ -1,16 +1,14 @@ #pragma once #include -#include #include +#include #include struct ScreenShot { using Ptr = std::shared_ptr; - ScreenShot(QFileInfo file) { - m_file = file; - } + ScreenShot(QFileInfo file) { m_file = file; } QFileInfo m_file; QString m_url; QString m_imgurId; diff --git a/launcher/settings/INIFile.cpp b/launcher/settings/INIFile.cpp index d16256b96..4fb11ed35 100644 --- a/launcher/settings/INIFile.cpp +++ b/launcher/settings/INIFile.cpp @@ -37,11 +37,12 @@ #include "settings/INIFile.h" #include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include #include @@ -71,6 +72,7 @@ bool INIFile::saveFile(QString fileName) return true; } + QString unescape(QString orig) { QString out; @@ -185,6 +187,19 @@ bool INIFile::loadFile(QString fileName) return true; } +bool INIFile::loadFile(QByteArray data) +{ + QTemporaryFile file; + if (!file.open()) + return false; + file.write(data); + file.flush(); + file.close(); + auto loaded = loadFile(file.fileName()); + file.remove(); + return loaded; +} + QVariant INIFile::get(QString key, QVariant def) const { if (!this->contains(key)) diff --git a/launcher/settings/INIFile.h b/launcher/settings/INIFile.h index 0d5c05ebb..c15578f6d 100644 --- a/launcher/settings/INIFile.h +++ b/launcher/settings/INIFile.h @@ -36,20 +36,20 @@ #pragma once +#include #include #include -#include -#include #include +#include // Sectionless INI parser (for instance config files) -class INIFile : public QMap -{ -public: +class INIFile : public QMap { + public: explicit INIFile(); bool loadFile(QString fileName); + bool loadFile(QByteArray data); bool saveFile(QString fileName); QVariant get(QString key, QVariant def) const; diff --git a/launcher/settings/INISettingsObject.cpp b/launcher/settings/INISettingsObject.cpp index da962ee97..519b8193e 100644 --- a/launcher/settings/INISettingsObject.cpp +++ b/launcher/settings/INISettingsObject.cpp @@ -19,8 +19,7 @@ #include #include -INISettingsObject::INISettingsObject(QStringList paths, QObject *parent) - : SettingsObject(parent) +INISettingsObject::INISettingsObject(QStringList paths, QObject* parent) : SettingsObject(parent) { auto first_path = paths.constFirst(); for (auto path : paths) { @@ -39,14 +38,13 @@ INISettingsObject::INISettingsObject(QStringList paths, QObject *parent) m_ini.loadFile(first_path); } -INISettingsObject::INISettingsObject(QString path, QObject* parent) - : SettingsObject(parent) +INISettingsObject::INISettingsObject(QString path, QObject* parent) : SettingsObject(parent) { m_filePath = path; m_ini.loadFile(path); } -void INISettingsObject::setFilePath(const QString &filePath) +void INISettingsObject::setFilePath(const QString& filePath) { m_filePath = filePath; } @@ -64,28 +62,24 @@ void INISettingsObject::suspendSave() void INISettingsObject::resumeSave() { m_suspendSave = false; - if(m_doSave) - { + if (m_doSave) { m_ini.saveFile(m_filePath); } } -void INISettingsObject::changeSetting(const Setting &setting, QVariant value) +void INISettingsObject::changeSetting(const Setting& setting, QVariant value) { - if (contains(setting.id())) - { + if (contains(setting.id())) { // valid value -> set the main config, remove all the sysnonyms - if (value.isValid()) - { + if (value.isValid()) { auto list = setting.configKeys(); m_ini.set(list.takeFirst(), value); - for(auto iter: list) + for (auto iter : list) m_ini.remove(iter); } // invalid -> remove all (just like resetSetting) - else - { - for(auto iter: setting.configKeys()) + else { + for (auto iter : setting.configKeys()) m_ini.remove(iter); } doSave(); @@ -94,35 +88,29 @@ void INISettingsObject::changeSetting(const Setting &setting, QVariant value) void INISettingsObject::doSave() { - if(m_suspendSave) - { + if (m_suspendSave) { m_doSave = true; - } - else - { + } else { m_ini.saveFile(m_filePath); } } -void INISettingsObject::resetSetting(const Setting &setting) +void INISettingsObject::resetSetting(const Setting& setting) { // if we have the setting, remove all the synonyms. ALL OF THEM - if (contains(setting.id())) - { - for(auto iter: setting.configKeys()) + if (contains(setting.id())) { + for (auto iter : setting.configKeys()) m_ini.remove(iter); doSave(); } } -QVariant INISettingsObject::retrieveValue(const Setting &setting) +QVariant INISettingsObject::retrieveValue(const Setting& setting) { // if we have the setting, return value of the first matching synonym - if (contains(setting.id())) - { - for(auto iter: setting.configKeys()) - { - if(m_ini.contains(iter)) + if (contains(setting.id())) { + for (auto iter : setting.configKeys()) { + if (m_ini.contains(iter)) return m_ini[iter]; } } diff --git a/launcher/settings/INISettingsObject.h b/launcher/settings/INISettingsObject.h index d2f448a9c..4cc808580 100644 --- a/launcher/settings/INISettingsObject.h +++ b/launcher/settings/INISettingsObject.h @@ -24,44 +24,40 @@ /*! * \brief A settings object that stores its settings in an INIFile. */ -class INISettingsObject : public SettingsObject -{ +class INISettingsObject : public SettingsObject { Q_OBJECT -public: + public: /** 'paths' is a list of INI files to try, in order, for fallback support. */ explicit INISettingsObject(QStringList paths, QObject* parent = nullptr); - explicit INISettingsObject(QString path, QObject* parent = nullptr); + explicit INISettingsObject(QString path, QObject* parent = nullptr); /*! * \brief Gets the path to the INI file. * \return The path to the INI file. */ - virtual QString filePath() const - { - return m_filePath; - } + virtual QString filePath() const { return m_filePath; } /*! * \brief Sets the path to the INI file and reloads it. * \param filePath The INI file's new path. */ - virtual void setFilePath(const QString &filePath); + virtual void setFilePath(const QString& filePath); bool reload() override; void suspendSave() override; void resumeSave() override; -protected slots: - virtual void changeSetting(const Setting &setting, QVariant value) override; - virtual void resetSetting(const Setting &setting) override; + protected slots: + virtual void changeSetting(const Setting& setting, QVariant value) override; + virtual void resetSetting(const Setting& setting) override; -protected: - virtual QVariant retrieveValue(const Setting &setting) override; + protected: + virtual QVariant retrieveValue(const Setting& setting) override; void doSave(); -protected: + protected: INIFile m_ini; QString m_filePath; }; diff --git a/launcher/settings/OverrideSetting.cpp b/launcher/settings/OverrideSetting.cpp index 4396a3817..fee65b8db 100644 --- a/launcher/settings/OverrideSetting.cpp +++ b/launcher/settings/OverrideSetting.cpp @@ -15,8 +15,7 @@ #include "OverrideSetting.h" -OverrideSetting::OverrideSetting(std::shared_ptr other, std::shared_ptr gate) - : Setting(other->configKeys(), QVariant()) +OverrideSetting::OverrideSetting(std::shared_ptr other, std::shared_ptr gate) : Setting(other->configKeys(), QVariant()) { Q_ASSERT(other); Q_ASSERT(gate); @@ -36,8 +35,7 @@ QVariant OverrideSetting::defValue() const QVariant OverrideSetting::get() const { - if(isOverriding()) - { + if (isOverriding()) { return Setting::get(); } return m_other->get(); diff --git a/launcher/settings/OverrideSetting.h b/launcher/settings/OverrideSetting.h index 9f0c98b52..faa3e7948 100644 --- a/launcher/settings/OverrideSetting.h +++ b/launcher/settings/OverrideSetting.h @@ -26,21 +26,20 @@ * The other setting can be (and usually is) a part of a different SettingsObject * than this one. */ -class OverrideSetting : public Setting -{ +class OverrideSetting : public Setting { Q_OBJECT -public: + public: explicit OverrideSetting(std::shared_ptr overriden, std::shared_ptr gate); virtual QVariant defValue() const; virtual QVariant get() const; - virtual void set (QVariant value); + virtual void set(QVariant value); virtual void reset(); -private: + private: bool isOverriding() const; -protected: + protected: std::shared_ptr m_other; std::shared_ptr m_gate; }; diff --git a/launcher/settings/PassthroughSetting.cpp b/launcher/settings/PassthroughSetting.cpp index 8f93b2519..86bb861e4 100644 --- a/launcher/settings/PassthroughSetting.cpp +++ b/launcher/settings/PassthroughSetting.cpp @@ -25,8 +25,7 @@ PassthroughSetting::PassthroughSetting(std::shared_ptr other, std::shar bool PassthroughSetting::isOverriding() const { - if(!m_gate) - { + if (!m_gate) { return false; } return m_gate->get().toBool(); @@ -34,8 +33,7 @@ bool PassthroughSetting::isOverriding() const QVariant PassthroughSetting::defValue() const { - if(isOverriding()) - { + if (isOverriding()) { return m_other->get(); } return m_other->defValue(); @@ -43,8 +41,7 @@ QVariant PassthroughSetting::defValue() const QVariant PassthroughSetting::get() const { - if(isOverriding()) - { + if (isOverriding()) { return Setting::get(); } return m_other->get(); @@ -52,8 +49,7 @@ QVariant PassthroughSetting::get() const void PassthroughSetting::reset() { - if(isOverriding()) - { + if (isOverriding()) { Setting::reset(); } m_other->reset(); @@ -61,8 +57,7 @@ void PassthroughSetting::reset() void PassthroughSetting::set(QVariant value) { - if(isOverriding()) - { + if (isOverriding()) { Setting::set(value); } m_other->set(value); diff --git a/launcher/settings/PassthroughSetting.h b/launcher/settings/PassthroughSetting.h index 22008f83d..c776ca951 100644 --- a/launcher/settings/PassthroughSetting.h +++ b/launcher/settings/PassthroughSetting.h @@ -25,21 +25,20 @@ * If 'gate' evaluates to true, the override stores and returns data * If 'gate' evaluates to false, the original does, */ -class PassthroughSetting : public Setting -{ +class PassthroughSetting : public Setting { Q_OBJECT -public: + public: explicit PassthroughSetting(std::shared_ptr overriden, std::shared_ptr gate); virtual QVariant defValue() const; virtual QVariant get() const; - virtual void set (QVariant value); + virtual void set(QVariant value); virtual void reset(); -private: + private: bool isOverriding() const; -protected: + protected: std::shared_ptr m_other; std::shared_ptr m_gate; }; diff --git a/launcher/settings/Setting.cpp b/launcher/settings/Setting.cpp index cfe5a7f92..1e861e36b 100644 --- a/launcher/settings/Setting.cpp +++ b/launcher/settings/Setting.cpp @@ -16,20 +16,14 @@ #include "Setting.h" #include "settings/SettingsObject.h" -Setting::Setting(QStringList synonyms, QVariant defVal) - : QObject(), m_synonyms(synonyms), m_defVal(defVal) -{ -} +Setting::Setting(QStringList synonyms, QVariant defVal) : QObject(), m_synonyms(synonyms), m_defVal(defVal) {} QVariant Setting::get() const { - SettingsObject *sbase = m_storage; - if (!sbase) - { + SettingsObject* sbase = m_storage; + if (!sbase) { return defValue(); - } - else - { + } else { QVariant test = sbase->retrieveValue(*this); if (!test.isValid()) return defValue(); diff --git a/launcher/settings/Setting.h b/launcher/settings/Setting.h index 86007c13d..44b350379 100644 --- a/launcher/settings/Setting.h +++ b/launcher/settings/Setting.h @@ -16,8 +16,8 @@ #pragma once #include -#include #include +#include #include class SettingsObject; @@ -25,10 +25,9 @@ class SettingsObject; /*! * */ -class Setting : public QObject -{ +class Setting : public QObject { Q_OBJECT -public: + public: /** * Construct a Setting * @@ -47,10 +46,7 @@ public: * undefined behavior. * \return The ID of the setting. */ - virtual QString id() const - { - return m_synonyms.first(); - } + virtual QString id() const { return m_synonyms.first(); } /*! * \brief Gets this setting's config file key. @@ -58,10 +54,7 @@ public: * the same as the setting's ID, but it can be different. * \return The setting's config file key. */ - virtual QStringList configKeys() const - { - return m_synonyms; - } + virtual QStringList configKeys() const { return m_synonyms; } /*! * \brief Gets this setting's value as a QVariant. @@ -78,22 +71,21 @@ public: */ virtual QVariant defValue() const; -signals: + signals: /*! * \brief Signal emitted when this Setting object's value changes. * \param setting A reference to the Setting that changed. * \param value This Setting object's new value. */ - void SettingChanged(const Setting &setting, QVariant value); + void SettingChanged(const Setting& setting, QVariant value); /*! * \brief Signal emitted when this Setting object's value resets to default. * \param setting A reference to the Setting that changed. */ - void settingReset(const Setting &setting); + void settingReset(const Setting& setting); -public -slots: + public slots: /*! * \brief Changes the setting's value. * This is done by emitting the SettingChanged() signal which will then be @@ -109,10 +101,9 @@ slots: */ virtual void reset(); -protected: + protected: friend class SettingsObject; - SettingsObject * m_storage; + SettingsObject* m_storage; QStringList m_synonyms; QVariant m_defVal; }; - diff --git a/launcher/settings/SettingsObject.cpp b/launcher/settings/SettingsObject.cpp index 634acd341..1e5dce251 100644 --- a/launcher/settings/SettingsObject.cpp +++ b/launcher/settings/SettingsObject.cpp @@ -14,30 +14,25 @@ */ #include "settings/SettingsObject.h" -#include "settings/Setting.h" -#include "settings/OverrideSetting.h" -#include "PassthroughSetting.h" #include +#include "PassthroughSetting.h" +#include "settings/OverrideSetting.h" +#include "settings/Setting.h" #include -SettingsObject::SettingsObject(QObject *parent) : QObject(parent) -{ -} +SettingsObject::SettingsObject(QObject* parent) : QObject(parent) {} SettingsObject::~SettingsObject() { m_settings.clear(); } -std::shared_ptr SettingsObject::registerOverride(std::shared_ptr original, - std::shared_ptr gate) +std::shared_ptr SettingsObject::registerOverride(std::shared_ptr original, std::shared_ptr gate) { - if (contains(original->id())) - { - qCritical() << QString("Failed to register setting %1. ID already exists.") - .arg(original->id()); - return nullptr; // Fail + if (contains(original->id())) { + qCritical() << QString("Failed to register setting %1. ID already exists.").arg(original->id()); + return nullptr; // Fail } auto override = std::make_shared(original, gate); override->m_storage = this; @@ -46,14 +41,11 @@ std::shared_ptr SettingsObject::registerOverride(std::shared_ptr SettingsObject::registerPassthrough(std::shared_ptr original, - std::shared_ptr gate) +std::shared_ptr SettingsObject::registerPassthrough(std::shared_ptr original, std::shared_ptr gate) { - if (contains(original->id())) - { - qCritical() << QString("Failed to register setting %1. ID already exists.") - .arg(original->id()); - return nullptr; // Fail + if (contains(original->id())) { + qCritical() << QString("Failed to register setting %1. ID already exists.").arg(original->id()); + return nullptr; // Fail } auto passthrough = std::make_shared(original, gate); passthrough->m_storage = this; @@ -66,11 +58,9 @@ std::shared_ptr SettingsObject::registerSetting(QStringList synonyms, Q { if (synonyms.empty()) return nullptr; - if (contains(synonyms.first())) - { - qCritical() << QString("Failed to register setting %1. ID already exists.") - .arg(synonyms.first()); - return nullptr; // Fail + if (contains(synonyms.first())) { + qCritical() << QString("Failed to register setting %1. ID already exists.").arg(synonyms.first()); + return nullptr; // Fail } auto setting = std::make_shared(synonyms, defVal); setting->m_storage = this; @@ -79,7 +69,7 @@ std::shared_ptr SettingsObject::registerSetting(QStringList synonyms, Q return setting; } -std::shared_ptr SettingsObject::getSetting(const QString &id) const +std::shared_ptr SettingsObject::getSetting(const QString& id) const { // Make sure there is a setting with the given ID. if (!m_settings.contains(id)) @@ -88,54 +78,49 @@ std::shared_ptr SettingsObject::getSetting(const QString &id) const return m_settings[id]; } -QVariant SettingsObject::get(const QString &id) const +QVariant SettingsObject::get(const QString& id) const { auto setting = getSetting(id); return (setting ? setting->get() : QVariant()); } -bool SettingsObject::set(const QString &id, QVariant value) +bool SettingsObject::set(const QString& id, QVariant value) { auto setting = getSetting(id); - if (!setting) - { + if (!setting) { qCritical() << QString("Error changing setting %1. Setting doesn't exist.").arg(id); return false; - } - else - { + } else { setting->set(value); return true; } } -void SettingsObject::reset(const QString &id) const +void SettingsObject::reset(const QString& id) const { auto setting = getSetting(id); if (setting) setting->reset(); } -bool SettingsObject::contains(const QString &id) +bool SettingsObject::contains(const QString& id) { return m_settings.contains(id); } bool SettingsObject::reload() { - for (auto setting : m_settings.values()) - { + for (auto setting : m_settings.values()) { setting->set(setting->get()); } return true; } -void SettingsObject::connectSignals(const Setting &setting) +void SettingsObject::connectSignals(const Setting& setting) { connect(&setting, &Setting::SettingChanged, this, &SettingsObject::changeSetting); - connect(&setting, SIGNAL(SettingChanged(const Setting &, QVariant)), this, - SIGNAL(SettingChanged(const Setting &, QVariant))); + connect(&setting, SIGNAL(SettingChanged(const Setting&, QVariant)), this, SIGNAL(SettingChanged(const Setting&, QVariant))); connect(&setting, &Setting::settingReset, this, &SettingsObject::resetSetting); - connect(&setting, SIGNAL(settingReset(Setting)), this, SIGNAL(settingReset(const Setting &))); + connect(&setting, SIGNAL(settingReset(Setting)), this, SIGNAL(settingReset(const Setting&))); } diff --git a/launcher/settings/SettingsObject.h b/launcher/settings/SettingsObject.h index 4d735511c..75631f247 100644 --- a/launcher/settings/SettingsObject.h +++ b/launcher/settings/SettingsObject.h @@ -15,12 +15,12 @@ #pragma once -#include +#include +#include #include +#include #include #include -#include -#include #include class Setting; @@ -41,27 +41,20 @@ typedef std::weak_ptr SettingsObjectWeakPtr; * * \sa Setting */ -class SettingsObject : public QObject -{ +class SettingsObject : public QObject { Q_OBJECT -public: - class Lock - { - public: - Lock(SettingsObjectPtr locked) - :m_locked(locked) - { - m_locked->suspendSave(); - } - ~Lock() - { - m_locked->resumeSave(); - } - private: + public: + class Lock { + public: + Lock(SettingsObjectPtr locked) : m_locked(locked) { m_locked->suspendSave(); } + ~Lock() { m_locked->resumeSave(); } + + private: SettingsObjectPtr m_locked; }; -public: - explicit SettingsObject(QObject *parent = 0); + + public: + explicit SettingsObject(QObject* parent = 0); virtual ~SettingsObject(); /*! * Registers an override setting for the given original setting in this settings object @@ -90,8 +83,7 @@ public: * the one that is being registered. * \return A valid Setting shared pointer if successful. */ - std::shared_ptr registerSetting(QStringList synonyms, - QVariant defVal = QVariant()); + std::shared_ptr registerSetting(QStringList synonyms, QVariant defVal = QVariant()); /*! * Registers the given setting with this SettingsObject and connects the necessary signals. @@ -100,10 +92,7 @@ public: * the one that is being registered. * \return A valid Setting shared pointer if successful. */ - std::shared_ptr registerSetting(QString id, QVariant defVal = QVariant()) - { - return registerSetting(QStringList(id), defVal); - } + std::shared_ptr registerSetting(QString id, QVariant defVal = QVariant()) { return registerSetting(QStringList(id), defVal); } /*! * \brief Gets the setting with the given ID. @@ -112,7 +101,7 @@ public: * Returns null if there is no setting with the given ID. * \sa operator []() */ - std::shared_ptr getSetting(const QString &id) const; + std::shared_ptr getSetting(const QString& id) const; /*! * \brief Gets the value of the setting with the given ID. @@ -120,7 +109,7 @@ public: * \return The setting's value as a QVariant. * If no setting with the given ID exists, returns an invalid QVariant. */ - QVariant get(const QString &id) const; + QVariant get(const QString& id) const; /*! * \brief Sets the value of the setting with the given ID. @@ -129,20 +118,20 @@ public: * \param value The new value of the setting. * \return True if successful, false if it failed. */ - bool set(const QString &id, QVariant value); + bool set(const QString& id, QVariant value); /*! * \brief Reverts the setting with the given ID to default. * \param id The ID of the setting to reset. */ - void reset(const QString &id) const; + void reset(const QString& id) const; /*! * \brief Checks if this SettingsObject contains a setting with the given ID. * \param id The ID to check for. * \return True if the SettingsObject has a setting with the given ID. */ - bool contains(const QString &id); + bool contains(const QString& id); /*! * \brief Reloads the settings and emit signals for changed settings @@ -152,7 +141,7 @@ public: virtual void suspendSave() = 0; virtual void resumeSave() = 0; -signals: + signals: /*! * \brief Signal emitted when one of this SettingsObject object's settings changes. * This is usually just connected directly to each Setting object's @@ -160,7 +149,7 @@ signals: * \param setting A reference to the Setting object that changed. * \param value The Setting object's new value. */ - void SettingChanged(const Setting &setting, QVariant value); + void SettingChanged(const Setting& setting, QVariant value); /*! * \brief Signal emitted when one of this SettingsObject object's settings resets. @@ -168,10 +157,9 @@ signals: * settingReset() signals. * \param setting A reference to the Setting object that changed. */ - void settingReset(const Setting &setting); + void settingReset(const Setting& setting); -protected -slots: + protected slots: /*! * \brief Changes a setting. * This slot is usually connected to each Setting object's @@ -180,7 +168,7 @@ slots: * \param setting A reference to the Setting object that changed. * \param value The setting's new value. */ - virtual void changeSetting(const Setting &setting, QVariant value) = 0; + virtual void changeSetting(const Setting& setting, QVariant value) = 0; /*! * \brief Resets a setting. @@ -189,27 +177,28 @@ slots: * to update the setting's value in the config file. * \param setting A reference to the Setting object that changed. */ - virtual void resetSetting(const Setting &setting) = 0; + virtual void resetSetting(const Setting& setting) = 0; -protected: + protected: /*! * \brief Connects the necessary signals to the given Setting. * \param setting The setting to connect. */ - void connectSignals(const Setting &setting); + void connectSignals(const Setting& setting); /*! * \brief Function used by Setting objects to get their values from the SettingsObject. * \param setting The * \return */ - virtual QVariant retrieveValue(const Setting &setting) = 0; + virtual QVariant retrieveValue(const Setting& setting) = 0; friend class Setting; -private: + private: QMap> m_settings; -protected: + + protected: bool m_suspendSave = false; bool m_doSave = false; }; diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp index 9aada5e69..6cfd864cc 100644 --- a/launcher/tasks/ConcurrentTask.cpp +++ b/launcher/tasks/ConcurrentTask.cpp @@ -88,6 +88,7 @@ bool ConcurrentTask::abort() QMutableHashIterator doing_iter(m_doing); while (doing_iter.hasNext()) { auto task = doing_iter.next(); + disconnect(task->get(), &Task::aborted, this, 0); suceedeed &= (task.value())->abort(); } @@ -130,6 +131,7 @@ void ConcurrentTask::startNext() connect(next.get(), &Task::succeeded, this, [this, next]() { subTaskSucceeded(next); }); connect(next.get(), &Task::failed, this, [this, next](QString msg) { subTaskFailed(next, msg); }); + connect(next.get(), &Task::aborted, this, [this, next] { subTaskFailed(next, "Aborted"); }); connect(next.get(), &Task::status, this, [this, next](QString msg) { subTaskStatus(next, msg); }); connect(next.get(), &Task::details, this, [this, next](QString msg) { subTaskDetails(next, msg); }); @@ -171,7 +173,7 @@ void ConcurrentTask::subTaskSucceeded(Task::Ptr task) startNext(); } -void ConcurrentTask::subTaskFailed(Task::Ptr task, const QString& msg) +void ConcurrentTask::subTaskFailed(Task::Ptr task, [[maybe_unused]] const QString& msg) { m_done.insert(task.get(), task); m_failed.insert(task.get(), task); @@ -194,7 +196,7 @@ void ConcurrentTask::subTaskStatus(Task::Ptr task, const QString& msg) auto task_progress = m_task_progress.value(task->getUid()); task_progress->status = msg; task_progress->state = TaskStepState::Running; - + emit stepProgress(*task_progress); if (totalSize() == 1) { @@ -207,7 +209,7 @@ void ConcurrentTask::subTaskDetails(Task::Ptr task, const QString& msg) auto task_progress = m_task_progress.value(task->getUid()); task_progress->details = msg; task_progress->state = TaskStepState::Running; - + emit stepProgress(*task_progress); if (totalSize() == 1) { @@ -220,7 +222,7 @@ void ConcurrentTask::subTaskProgress(Task::Ptr task, qint64 current, qint64 tota auto task_progress = m_task_progress.value(task->getUid()); task_progress->update(current, total); - + emit stepProgress(*task_progress); updateStepProgress(*task_progress, Operation::CHANGED); updateState(); @@ -233,7 +235,7 @@ void ConcurrentTask::subTaskProgress(Task::Ptr task, qint64 current, qint64 tota void ConcurrentTask::subTaskStepProgress(Task::Ptr task, TaskStepProgress const& task_progress) { Operation op = Operation::ADDED; - + if (!m_task_progress.contains(task_progress.uid)) { m_task_progress.insert(task_progress.uid, std::make_shared(task_progress)); op = Operation::ADDED; @@ -254,12 +256,10 @@ void ConcurrentTask::subTaskStepProgress(Task::Ptr task, TaskStepProgress const& emit stepProgress(*tp.get()); updateStepProgress(*tp.get(), op); } - } void ConcurrentTask::updateStepProgress(TaskStepProgress const& changed_progress, Operation op) { - switch (op) { case Operation::ADDED: m_stepProgress += changed_progress.current; @@ -274,9 +274,8 @@ void ConcurrentTask::updateStepProgress(TaskStepProgress const& changed_progress m_stepTotalProgress -= changed_progress.old_total; m_stepProgress += changed_progress.current; m_stepTotalProgress += changed_progress.total; - break; + break; } - } void ConcurrentTask::updateState() diff --git a/launcher/tasks/ConcurrentTask.h b/launcher/tasks/ConcurrentTask.h index 6325fc9e7..8b696bf58 100644 --- a/launcher/tasks/ConcurrentTask.h +++ b/launcher/tasks/ConcurrentTask.h @@ -53,7 +53,7 @@ class ConcurrentTask : public Task { bool canAbort() const override { return true; } - inline auto isMultiStep() const -> bool override { return totalSize() > 1; }; + inline auto isMultiStep() const -> bool override { return totalSize() > 1; } auto getStepProgress() const -> TaskStepProgressList override; void addTask(Task::Ptr task); @@ -80,7 +80,7 @@ class ConcurrentTask : public Task { protected: // NOTE: This is not thread-safe. - [[nodiscard]] unsigned int totalSize() const { return m_queue.size() + m_doing.size() + m_done.size(); } + [[nodiscard]] unsigned int totalSize() const { return static_cast(m_queue.size() + m_doing.size() + m_done.size()); } enum class Operation { ADDED, REMOVED, CHANGED }; void updateStepProgress(TaskStepProgress const& changed_progress, Operation); diff --git a/launcher/tasks/Task.cpp b/launcher/tasks/Task.cpp index 29c55cd48..b17096ca7 100644 --- a/launcher/tasks/Task.cpp +++ b/launcher/tasks/Task.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (c) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * @@ -40,16 +40,15 @@ Q_LOGGING_CATEGORY(taskLogC, "launcher.task") -Task::Task(QObject *parent, bool show_debug) : QObject(parent), m_show_debug(show_debug) +Task::Task(QObject* parent, bool show_debug) : QObject(parent), m_show_debug(show_debug) { m_uid = QUuid::createUuid(); setAutoDelete(false); } -void Task::setStatus(const QString &new_status) +void Task::setStatus(const QString& new_status) { - if(m_status != new_status) - { + if (m_status != new_status) { m_status = new_status; emit status(m_status); } @@ -57,8 +56,7 @@ void Task::setStatus(const QString &new_status) void Task::setDetails(const QString& new_details) { - if (m_details != new_details) - { + if (m_details != new_details) { m_details = new_details; emit details(m_details); } @@ -69,41 +67,35 @@ void Task::setProgress(qint64 current, qint64 total) if ((m_progress != current) || (m_progressTotal != total)) { m_progress = current; m_progressTotal = total; - + emit progress(m_progress, m_progressTotal); - } + } } void Task::start() { - switch(m_state) - { - case State::Inactive: - { + switch (m_state) { + case State::Inactive: { if (m_show_debug) qCDebug(taskLogC) << "Task" << describe() << "starting for the first time"; break; } - case State::AbortedByUser: - { + case State::AbortedByUser: { if (m_show_debug) qCDebug(taskLogC) << "Task" << describe() << "restarting for after being aborted by user"; break; } - case State::Failed: - { + case State::Failed: { if (m_show_debug) qCDebug(taskLogC) << "Task" << describe() << "restarting for after failing at first"; break; } - case State::Succeeded: - { + case State::Succeeded: { if (m_show_debug) qCDebug(taskLogC) << "Task" << describe() << "restarting for after succeeding at first"; break; } - case State::Running: - { + case State::Running: { if (m_show_debug) qCWarning(taskLogC) << "The launcher tried to start task" << describe() << "while it was already running!"; return; @@ -118,8 +110,7 @@ void Task::start() void Task::emitFailed(QString reason) { // Don't fail twice. - if (!isRunning()) - { + if (!isRunning()) { qCCritical(taskLogC) << "Task" << describe() << "failed while not running!!!!: " << reason; return; } @@ -133,8 +124,7 @@ void Task::emitFailed(QString reason) void Task::emitAborted() { // Don't abort twice. - if (!isRunning()) - { + if (!isRunning()) { qCCritical(taskLogC) << "Task" << describe() << "aborted while not running!!!!"; return; } @@ -149,8 +139,7 @@ void Task::emitAborted() void Task::emitSucceeded() { // Don't succeed twice. - if (!isRunning()) - { + if (!isRunning()) { qCCritical(taskLogC) << "Task" << describe() << "succeeded while not running!!!!"; return; } @@ -161,7 +150,7 @@ void Task::emitSucceeded() emit finished(); } -void Task::propogateStepProgress(TaskStepProgress const& task_progress) +void Task::propagateStepProgress(TaskStepProgress const& task_progress) { emit stepProgress(task_progress); } @@ -172,12 +161,9 @@ QString Task::describe() QTextStream out(&outStr); out << metaObject()->className() << QChar('('); auto name = objectName(); - if(name.isEmpty()) - { + if (name.isEmpty()) { out << QString("0x%1").arg(reinterpret_cast(this), 0, 16); - } - else - { + } else { out << name; } out << " ID: " << m_uid.toString(QUuid::WithoutBraces); diff --git a/launcher/tasks/Task.h b/launcher/tasks/Task.h index 6d8bbbb46..94b4089f3 100644 --- a/launcher/tasks/Task.h +++ b/launcher/tasks/Task.h @@ -36,25 +36,20 @@ #pragma once +#include #include #include -#include #include "QObjectPtr.h" Q_DECLARE_LOGGING_CATEGORY(taskLogC) -enum class TaskStepState { - Waiting, - Running, - Failed, - Succeeded -}; +enum class TaskStepState { Waiting, Running, Failed, Succeeded }; Q_DECLARE_METATYPE(TaskStepState) struct TaskStepProgress { - QUuid uid; + QUuid uid; qint64 current = 0; qint64 total = -1; @@ -64,19 +59,18 @@ struct TaskStepProgress { QString status = ""; QString details = ""; TaskStepState state = TaskStepState::Waiting; - TaskStepProgress() { - this->uid = QUuid::createUuid(); - } - TaskStepProgress(QUuid uid) { - this->uid = uid; - } + + TaskStepProgress() { this->uid = QUuid::createUuid(); } + TaskStepProgress(QUuid uid_) : uid(uid_) {} + bool isDone() const { return (state == TaskStepState::Failed) || (state == TaskStepState::Succeeded); } - void update(qint64 current, qint64 total) { + void update(qint64 new_current, qint64 new_total) + { this->old_current = this->current; this->old_total = this->total; - this->current = current; - this->total = total; + this->current = new_current; + this->total = new_total; this->state = TaskStepState::Running; } }; @@ -100,8 +94,8 @@ class Task : public QObject, public QRunnable { bool isFinished() const; bool wasSuccessful() const; - /*! - * MultiStep tasks are combinations of multiple tasks into a single logical task. + /*! + * MultiStep tasks are combinations of multiple tasks into a single logical task. * The main usage of this is in SequencialTask. */ virtual auto isMultiStep() const -> bool { return false; } @@ -125,8 +119,6 @@ class Task : public QObject, public QRunnable { qint64 getTotalProgress() { return m_progressTotal; } virtual auto getStepProgress() const -> TaskStepProgressList { return {}; } - - QUuid getUid() { return m_uid; } protected: @@ -155,9 +147,18 @@ class Task : public QObject, public QRunnable { void run() override { start(); } virtual void start(); - virtual bool abort() { if(canAbort()) emitAborted(); return canAbort(); }; + virtual bool abort() + { + if (canAbort()) + emitAborted(); + return canAbort(); + } - void setAbortable(bool can_abort) { m_can_abort = can_abort; emit abortStatusChanged(can_abort); } + void setAbortable(bool can_abort) + { + m_can_abort = can_abort; + emit abortStatusChanged(can_abort); + } protected: virtual void executeTask() = 0; @@ -167,7 +168,7 @@ class Task : public QObject, public QRunnable { virtual void emitAborted(); virtual void emitFailed(QString reason = ""); - virtual void propogateStepProgress(TaskStepProgress const& task_progress); + virtual void propagateStepProgress(TaskStepProgress const& task_progress); public slots: void setStatus(const QString& status); @@ -190,5 +191,4 @@ class Task : public QObject, public QRunnable { // Change using setAbortStatus bool m_can_abort = false; QUuid m_uid; - }; diff --git a/launcher/tools/BaseExternalTool.cpp b/launcher/tools/BaseExternalTool.cpp index 38d817887..9e4b91cd8 100644 --- a/launcher/tools/BaseExternalTool.cpp +++ b/launcher/tools/BaseExternalTool.cpp @@ -1,7 +1,7 @@ #include "BaseExternalTool.h" -#include #include +#include #ifdef Q_OS_WIN #include @@ -9,33 +9,24 @@ #include "BaseInstance.h" -BaseExternalTool::BaseExternalTool(SettingsObjectPtr settings, InstancePtr instance, QObject *parent) +BaseExternalTool::BaseExternalTool(SettingsObjectPtr settings, InstancePtr instance, QObject* parent) : QObject(parent), m_instance(instance), globalSettings(settings) -{ -} +{} -BaseExternalTool::~BaseExternalTool() -{ -} +BaseExternalTool::~BaseExternalTool() {} -BaseDetachedTool::BaseDetachedTool(SettingsObjectPtr settings, InstancePtr instance, QObject *parent) +BaseDetachedTool::BaseDetachedTool(SettingsObjectPtr settings, InstancePtr instance, QObject* parent) : BaseExternalTool(settings, instance, parent) -{ - -} +{} void BaseDetachedTool::run() { runImpl(); } +BaseExternalToolFactory::~BaseExternalToolFactory() {} -BaseExternalToolFactory::~BaseExternalToolFactory() +BaseDetachedTool* BaseDetachedToolFactory::createDetachedTool(InstancePtr instance, QObject* parent) { -} - -BaseDetachedTool *BaseDetachedToolFactory::createDetachedTool(InstancePtr instance, - QObject *parent) -{ - return qobject_cast(createTool(instance, parent)); + return qobject_cast(createTool(instance, parent)); } diff --git a/launcher/tools/BaseExternalTool.h b/launcher/tools/BaseExternalTool.h index 1ebed6ae2..eb2d07e1e 100644 --- a/launcher/tools/BaseExternalTool.h +++ b/launcher/tools/BaseExternalTool.h @@ -1,58 +1,53 @@ #pragma once -#include #include +#include class BaseInstance; class SettingsObject; class QProcess; -class BaseExternalTool : public QObject -{ +class BaseExternalTool : public QObject { Q_OBJECT -public: - explicit BaseExternalTool(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0); + public: + explicit BaseExternalTool(SettingsObjectPtr settings, InstancePtr instance, QObject* parent = 0); virtual ~BaseExternalTool(); -protected: + protected: InstancePtr m_instance; SettingsObjectPtr globalSettings; }; -class BaseDetachedTool : public BaseExternalTool -{ +class BaseDetachedTool : public BaseExternalTool { Q_OBJECT -public: - explicit BaseDetachedTool(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0); + public: + explicit BaseDetachedTool(SettingsObjectPtr settings, InstancePtr instance, QObject* parent = 0); -public -slots: + public slots: void run(); -protected: + protected: virtual void runImpl() = 0; }; -class BaseExternalToolFactory -{ -public: +class BaseExternalToolFactory { + public: virtual ~BaseExternalToolFactory(); virtual QString name() const = 0; virtual void registerSettings(SettingsObjectPtr settings) = 0; - virtual BaseExternalTool *createTool(InstancePtr instance, QObject *parent = 0) = 0; + virtual BaseExternalTool* createTool(InstancePtr instance, QObject* parent = 0) = 0; - virtual bool check(QString *error) = 0; - virtual bool check(const QString &path, QString *error) = 0; + virtual bool check(QString* error) = 0; + virtual bool check(const QString& path, QString* error) = 0; -protected: + protected: SettingsObjectPtr globalSettings; }; -class BaseDetachedToolFactory : public BaseExternalToolFactory -{ -public: - virtual BaseDetachedTool *createDetachedTool(InstancePtr instance, QObject *parent = 0); +class BaseDetachedToolFactory : public BaseExternalToolFactory { + public: + virtual BaseDetachedTool* createDetachedTool(InstancePtr instance, QObject* parent = 0); }; diff --git a/launcher/tools/BaseProfiler.cpp b/launcher/tools/BaseProfiler.cpp index 300d1a736..2ab1254e9 100644 --- a/launcher/tools/BaseProfiler.cpp +++ b/launcher/tools/BaseProfiler.cpp @@ -3,10 +3,8 @@ #include -BaseProfiler::BaseProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject *parent) - : BaseExternalTool(settings, instance, parent) -{ -} +BaseProfiler::BaseProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject* parent) : BaseExternalTool(settings, instance, parent) +{} void BaseProfiler::beginProfiling(shared_qobject_ptr process) { @@ -20,8 +18,7 @@ void BaseProfiler::abortProfiling() void BaseProfiler::abortProfilingImpl() { - if (!m_profilerProcess) - { + if (!m_profilerProcess) { return; } m_profilerProcess->terminate(); @@ -30,7 +27,7 @@ void BaseProfiler::abortProfilingImpl() emit abortLaunch(tr("Profiler aborted")); } -BaseProfiler *BaseProfilerFactory::createProfiler(InstancePtr instance, QObject *parent) +BaseProfiler* BaseProfilerFactory::createProfiler(InstancePtr instance, QObject* parent) { - return qobject_cast(createTool(instance, parent)); + return qobject_cast(createTool(instance, parent)); } diff --git a/launcher/tools/BaseProfiler.h b/launcher/tools/BaseProfiler.h index 1c934aa35..ac0f3a786 100644 --- a/launcher/tools/BaseProfiler.h +++ b/launcher/tools/BaseProfiler.h @@ -8,30 +8,27 @@ class SettingsObject; class LaunchTask; class QProcess; -class BaseProfiler : public BaseExternalTool -{ +class BaseProfiler : public BaseExternalTool { Q_OBJECT -public: - explicit BaseProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0); + public: + explicit BaseProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject* parent = 0); -public -slots: + public slots: void beginProfiling(shared_qobject_ptr process); void abortProfiling(); -protected: - QProcess *m_profilerProcess; + protected: + QProcess* m_profilerProcess; virtual void beginProfilingImpl(shared_qobject_ptr process) = 0; virtual void abortProfilingImpl(); -signals: - void readyToLaunch(const QString &message); - void abortLaunch(const QString &message); + signals: + void readyToLaunch(const QString& message); + void abortLaunch(const QString& message); }; -class BaseProfilerFactory : public BaseExternalToolFactory -{ -public: - virtual BaseProfiler *createProfiler(InstancePtr instance, QObject *parent = 0); +class BaseProfilerFactory : public BaseExternalToolFactory { + public: + virtual BaseProfiler* createProfiler(InstancePtr instance, QObject* parent = 0); }; diff --git a/launcher/tools/JProfiler.cpp b/launcher/tools/JProfiler.cpp index 15c0cab6d..7a532a3d2 100644 --- a/launcher/tools/JProfiler.cpp +++ b/launcher/tools/JProfiler.cpp @@ -2,46 +2,39 @@ #include -#include "settings/SettingsObject.h" -#include "launch/LaunchTask.h" #include "BaseInstance.h" +#include "launch/LaunchTask.h" +#include "settings/SettingsObject.h" -class JProfiler : public BaseProfiler -{ +class JProfiler : public BaseProfiler { Q_OBJECT -public: - JProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0); + public: + JProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject* parent = 0); -private slots: + private slots: void profilerStarted(); void profilerFinished(int exit, QProcess::ExitStatus status); -protected: + protected: void beginProfilingImpl(shared_qobject_ptr process); -private: + private: int listeningPort = 0; }; -JProfiler::JProfiler(SettingsObjectPtr settings, InstancePtr instance, - QObject *parent) - : BaseProfiler(settings, instance, parent) -{ -} +JProfiler::JProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject* parent) : BaseProfiler(settings, instance, parent) {} void JProfiler::profilerStarted() { emit readyToLaunch(tr("Listening on port: %1").arg(listeningPort)); } -void JProfiler::profilerFinished(int exit, QProcess::ExitStatus status) +void JProfiler::profilerFinished([[maybe_unused]] int exit, QProcess::ExitStatus status) { - if (status == QProcess::CrashExit) - { + if (status == QProcess::CrashExit) { emit abortLaunch(tr("Profiler aborted")); } - if (m_profilerProcess) - { + if (m_profilerProcess) { m_profilerProcess->deleteLater(); m_profilerProcess = 0; } @@ -50,13 +43,8 @@ void JProfiler::profilerFinished(int exit, QProcess::ExitStatus status) void JProfiler::beginProfilingImpl(shared_qobject_ptr process) { listeningPort = globalSettings->get("JProfilerPort").toInt(); - QProcess *profiler = new QProcess(this); - QStringList profilerArgs = - { - "-d", QString::number(process->pid()), - "--gui", - "-p", QString::number(listeningPort) - }; + QProcess* profiler = new QProcess(this); + QStringList profilerArgs = { "-d", QString::number(process->pid()), "--gui", "-p", QString::number(listeningPort) }; auto basePath = globalSettings->get("JProfilerPath").toString(); #ifdef Q_OS_WIN @@ -82,31 +70,28 @@ void JProfilerFactory::registerSettings(SettingsObjectPtr settings) globalSettings = settings; } -BaseExternalTool *JProfilerFactory::createTool(InstancePtr instance, QObject *parent) +BaseExternalTool* JProfilerFactory::createTool(InstancePtr instance, QObject* parent) { return new JProfiler(globalSettings, instance, parent); } -bool JProfilerFactory::check(QString *error) +bool JProfilerFactory::check(QString* error) { return check(globalSettings->get("JProfilerPath").toString(), error); } -bool JProfilerFactory::check(const QString &path, QString *error) +bool JProfilerFactory::check(const QString& path, QString* error) { - if (path.isEmpty()) - { + if (path.isEmpty()) { *error = QObject::tr("Empty path"); return false; } QDir dir(path); - if (!dir.exists()) - { + if (!dir.exists()) { *error = QObject::tr("Path does not exist"); return false; } - if (!dir.exists("bin") || !(dir.exists("bin/jprofiler") || dir.exists("bin/jprofiler.exe")) || !dir.exists("bin/agent.jar")) - { + if (!dir.exists("bin") || !(dir.exists("bin/jprofiler") || dir.exists("bin/jprofiler.exe")) || !dir.exists("bin/agent.jar")) { *error = QObject::tr("Invalid JProfiler install"); return false; } diff --git a/launcher/tools/JProfiler.h b/launcher/tools/JProfiler.h index 0e9a3a85a..55715df32 100644 --- a/launcher/tools/JProfiler.h +++ b/launcher/tools/JProfiler.h @@ -2,12 +2,11 @@ #include "BaseProfiler.h" -class JProfilerFactory : public BaseProfilerFactory -{ -public: +class JProfilerFactory : public BaseProfilerFactory { + public: QString name() const override { return "JProfiler"; } void registerSettings(SettingsObjectPtr settings) override; - BaseExternalTool *createTool(InstancePtr instance, QObject *parent = 0) override; - bool check(QString *error) override; - bool check(const QString &path, QString *error) override; + BaseExternalTool* createTool(InstancePtr instance, QObject* parent = 0) override; + bool check(QString* error) override; + bool check(const QString& path, QString* error) override; }; diff --git a/launcher/tools/JVisualVM.cpp b/launcher/tools/JVisualVM.cpp index 28ffb9cdc..4da4e1e54 100644 --- a/launcher/tools/JVisualVM.cpp +++ b/launcher/tools/JVisualVM.cpp @@ -3,43 +3,36 @@ #include #include -#include "settings/SettingsObject.h" -#include "launch/LaunchTask.h" #include "BaseInstance.h" +#include "launch/LaunchTask.h" +#include "settings/SettingsObject.h" -class JVisualVM : public BaseProfiler -{ +class JVisualVM : public BaseProfiler { Q_OBJECT -public: - JVisualVM(SettingsObjectPtr settings, InstancePtr instance, QObject *parent = 0); + public: + JVisualVM(SettingsObjectPtr settings, InstancePtr instance, QObject* parent = 0); -private slots: + private slots: void profilerStarted(); void profilerFinished(int exit, QProcess::ExitStatus status); -protected: + protected: void beginProfilingImpl(shared_qobject_ptr process); }; - -JVisualVM::JVisualVM(SettingsObjectPtr settings, InstancePtr instance, QObject *parent) - : BaseProfiler(settings, instance, parent) -{ -} +JVisualVM::JVisualVM(SettingsObjectPtr settings, InstancePtr instance, QObject* parent) : BaseProfiler(settings, instance, parent) {} void JVisualVM::profilerStarted() { emit readyToLaunch(tr("JVisualVM started")); } -void JVisualVM::profilerFinished(int exit, QProcess::ExitStatus status) +void JVisualVM::profilerFinished([[maybe_unused]] int exit, QProcess::ExitStatus status) { - if (status == QProcess::CrashExit) - { + if (status == QProcess::CrashExit) { emit abortLaunch(tr("Profiler aborted")); } - if (m_profilerProcess) - { + if (m_profilerProcess) { m_profilerProcess->deleteLater(); m_profilerProcess = 0; } @@ -47,18 +40,15 @@ void JVisualVM::profilerFinished(int exit, QProcess::ExitStatus status) void JVisualVM::beginProfilingImpl(shared_qobject_ptr process) { - QProcess *profiler = new QProcess(this); - QStringList profilerArgs = - { - "--openpid", QString::number(process->pid()) - }; + QProcess* profiler = new QProcess(this); + QStringList profilerArgs = { "--openpid", QString::number(process->pid()) }; auto programPath = globalSettings->get("JVisualVMPath").toString(); profiler->setArguments(profilerArgs); profiler->setProgram(programPath); connect(profiler, &QProcess::started, this, &JVisualVM::profilerStarted); - connect(profiler, QOverload::of(&QProcess::finished), this, &JVisualVM::profilerFinished); + connect(profiler, QOverload::of(&QProcess::finished), this, &JVisualVM::profilerFinished); profiler->start(); m_profilerProcess = profiler; @@ -67,34 +57,31 @@ void JVisualVM::beginProfilingImpl(shared_qobject_ptr process) void JVisualVMFactory::registerSettings(SettingsObjectPtr settings) { QString defaultValue = QStandardPaths::findExecutable("jvisualvm"); - if (defaultValue.isNull()) - { + if (defaultValue.isNull()) { defaultValue = QStandardPaths::findExecutable("visualvm"); } settings->registerSetting("JVisualVMPath", defaultValue); globalSettings = settings; } -BaseExternalTool *JVisualVMFactory::createTool(InstancePtr instance, QObject *parent) +BaseExternalTool* JVisualVMFactory::createTool(InstancePtr instance, QObject* parent) { return new JVisualVM(globalSettings, instance, parent); } -bool JVisualVMFactory::check(QString *error) +bool JVisualVMFactory::check(QString* error) { return check(globalSettings->get("JVisualVMPath").toString(), error); } -bool JVisualVMFactory::check(const QString &path, QString *error) +bool JVisualVMFactory::check(const QString& path, QString* error) { - if (path.isEmpty()) - { + if (path.isEmpty()) { *error = QObject::tr("Empty path"); return false; } QFileInfo finfo(path); - if (!finfo.isExecutable() || !finfo.fileName().contains("visualvm")) - { + if (!finfo.isExecutable() || !finfo.fileName().contains("visualvm")) { *error = QObject::tr("Invalid path to JVisualVM"); return false; } diff --git a/launcher/tools/JVisualVM.h b/launcher/tools/JVisualVM.h index ebdea9f33..2828119a1 100644 --- a/launcher/tools/JVisualVM.h +++ b/launcher/tools/JVisualVM.h @@ -2,12 +2,11 @@ #include "BaseProfiler.h" -class JVisualVMFactory : public BaseProfilerFactory -{ -public: +class JVisualVMFactory : public BaseProfilerFactory { + public: QString name() const override { return "JVisualVM"; } void registerSettings(SettingsObjectPtr settings) override; - BaseExternalTool *createTool(InstancePtr instance, QObject *parent = 0) override; - bool check(QString *error) override; - bool check(const QString &path, QString *error) override; + BaseExternalTool* createTool(InstancePtr instance, QObject* parent = 0) override; + bool check(QString* error) override; + bool check(const QString& path, QString* error) override; }; diff --git a/launcher/tools/MCEditTool.cpp b/launcher/tools/MCEditTool.cpp index 2c1ec613c..19bd5a062 100644 --- a/launcher/tools/MCEditTool.cpp +++ b/launcher/tools/MCEditTool.cpp @@ -4,9 +4,9 @@ #include #include -#include "settings/SettingsObject.h" #include "BaseInstance.h" #include "minecraft/MinecraftInstance.h" +#include "settings/SettingsObject.h" MCEditTool::MCEditTool(SettingsObjectPtr settings) { @@ -26,19 +26,17 @@ QString MCEditTool::path() const bool MCEditTool::check(const QString& toolPath, QString& error) { - if (toolPath.isEmpty()) - { + if (toolPath.isEmpty()) { error = QObject::tr("Path is empty"); return false; } const QDir dir(toolPath); - if (!dir.exists()) - { + if (!dir.exists()) { error = QObject::tr("Path does not exist"); return false; } - if (!dir.exists("mcedit.sh") && !dir.exists("mcedit.py") && !dir.exists("mcedit.exe") && !dir.exists("Contents") && !dir.exists("mcedit2.exe")) - { + if (!dir.exists("mcedit.sh") && !dir.exists("mcedit.py") && !dir.exists("mcedit.exe") && !dir.exists("Contents") && + !dir.exists("mcedit2.exe")) { error = QObject::tr("Path does not seem to be a MCEdit path"); return false; } @@ -53,22 +51,16 @@ QString MCEditTool::getProgramPath() const QString mceditPath = path(); QDir mceditDir(mceditPath); #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) - if (mceditDir.exists("mcedit.sh")) - { + if (mceditDir.exists("mcedit.sh")) { return mceditDir.absoluteFilePath("mcedit.sh"); - } - else if (mceditDir.exists("mcedit.py")) - { + } else if (mceditDir.exists("mcedit.py")) { return mceditDir.absoluteFilePath("mcedit.py"); } return QString(); #elif defined(Q_OS_WIN32) - if (mceditDir.exists("mcedit.exe")) - { + if (mceditDir.exists("mcedit.exe")) { return mceditDir.absoluteFilePath("mcedit.exe"); - } - else if (mceditDir.exists("mcedit2.exe")) - { + } else if (mceditDir.exists("mcedit2.exe")) { return mceditDir.absoluteFilePath("mcedit2.exe"); } return QString(); diff --git a/launcher/tools/MCEditTool.h b/launcher/tools/MCEditTool.h index 733dff866..fd2de1b6d 100644 --- a/launcher/tools/MCEditTool.h +++ b/launcher/tools/MCEditTool.h @@ -3,14 +3,14 @@ #include #include "settings/SettingsObject.h" -class MCEditTool -{ -public: +class MCEditTool { + public: MCEditTool(SettingsObjectPtr settings); - void setPath(QString & path); + void setPath(QString& path); QString path() const; - bool check(const QString &toolPath, QString &error); + bool check(const QString& toolPath, QString& error); QString getProgramPath(); -private: + + private: SettingsObjectPtr m_settings; }; diff --git a/launcher/translations/POTranslator.cpp b/launcher/translations/POTranslator.cpp index c77ae45d3..51ef4852b 100644 --- a/launcher/translations/POTranslator.cpp +++ b/launcher/translations/POTranslator.cpp @@ -3,14 +3,12 @@ #include #include "FileSystem.h" -struct POEntry -{ +struct POEntry { QString text; bool fuzzy; }; -struct POTranslatorPrivate -{ +struct POTranslatorPrivate { QString filename; QHash mapping; QHash mapping_disambiguatrion; @@ -19,47 +17,37 @@ struct POTranslatorPrivate void reload(); }; -class ParserArray : public QByteArray -{ -public: - ParserArray(const QByteArray &in) : QByteArray(in) +class ParserArray : public QByteArray { + public: + ParserArray(const QByteArray& in) : QByteArray(in) {} + bool chomp(const char* data, int length) { - } - bool chomp(const char * data, int length) - { - if(startsWith(data)) - { + if (startsWith(data)) { remove(0, length); return true; } return false; } - bool chompString(QByteArray & appendHere) + bool chompString(QByteArray& appendHere) { QByteArray msg; bool escape = false; - if(size() < 2) - { + if (size() < 2) { qDebug() << "String fragment is too short"; return false; } - if(!startsWith('"')) - { + if (!startsWith('"')) { qDebug() << "String fragment does not start with \""; return false; } - if(!endsWith('"')) - { + if (!endsWith('"')) { qDebug() << "String fragment does not end with \", instead, there is" << at(size() - 1); return false; } - for(int i = 1; i < size() - 1; i++) - { + for (int i = 1; i < size() - 1; i++) { char c = operator[](i); - if(escape) - { - switch(c) - { + if (escape) { + switch (c) { case 'r': msg += '\r'; break; @@ -94,14 +82,11 @@ public: case '4': case '5': case '6': - case '7': - { + case '7': { int octal_start = i; - while ((c = operator[](i)) >= '0' && c <= '7') - { + while ((c = operator[](i)) >= '0' && c <= '7') { i++; - if (i == length() - 1) - { + if (i == length() - 1) { qDebug() << "Something went bad while parsing an octal escape string..."; return false; } @@ -109,16 +94,13 @@ public: msg += mid(octal_start, i - octal_start).toUInt(0, 8); break; } - case 'x': - { + case 'x': { // chomp the 'x' i++; int hex_start = i; - while (isxdigit(operator[](i))) - { + while (isxdigit(operator[](i))) { i++; - if (i == length() - 1) - { + if (i == length() - 1) { qDebug() << "Something went bad while parsing a hex escape string..."; return false; } @@ -126,25 +108,19 @@ public: msg += mid(hex_start, i - hex_start).toUInt(0, 16); break; } - default: - { + default: { qDebug() << "Invalid escape sequence character:" << c; return false; } } escape = false; - } - else if(c == '\\') - { + } else if (c == '\\') { escape = true; - } - else - { + } else { msg += c; } } - if(escape) - { + if (escape) { qDebug() << "Unterminated escape sequence..."; return false; } @@ -156,8 +132,7 @@ public: void POTranslatorPrivate::reload() { QFile file(filename); - if(!file.open(QFile::OpenMode::enum_type::ReadOnly | QFile::OpenMode::enum_type::Text)) - { + if (!file.open(QFile::OpenMode::enum_type::ReadOnly | QFile::OpenMode::enum_type::Text)) { qDebug() << "Failed to open PO file:" << filename; return; } @@ -169,13 +144,7 @@ void POTranslatorPrivate::reload() bool fuzzy = false; bool nextFuzzy = false; - enum class Mode - { - First, - MessageContext, - MessageId, - MessageString - } mode = Mode::First; + enum class Mode { First, MessageContext, MessageId, MessageString } mode = Mode::First; int lineNumber = 0; QHash newMapping; @@ -183,14 +152,12 @@ void POTranslatorPrivate::reload() auto endEntry = [&]() { auto strStr = QString::fromUtf8(str); // NOTE: PO header has empty id. We skip it. - if(!id.isEmpty()) - { + if (!id.isEmpty()) { auto normalKey = context + "|" + id; - newMapping.insert(normalKey, {strStr, fuzzy}); - if(!disambiguation.isEmpty()) - { + newMapping.insert(normalKey, { strStr, fuzzy }); + if (!disambiguation.isEmpty()) { auto disambiguationKey = context + "|" + id + "@" + disambiguation; - newMapping_disambiguation.insert(disambiguationKey, {strStr, fuzzy}); + newMapping_disambiguation.insert(disambiguationKey, { strStr, fuzzy }); } } context.clear(); @@ -200,36 +167,26 @@ void POTranslatorPrivate::reload() fuzzy = nextFuzzy; nextFuzzy = false; }; - while (!file.atEnd()) - { + while (!file.atEnd()) { ParserArray line = file.readLine(); - if(line.endsWith('\n')) - { + if (line.endsWith('\n')) { line.resize(line.size() - 1); } - if(line.endsWith('\r')) - { + if (line.endsWith('\r')) { line.resize(line.size() - 1); } - if(!line.size()) - { + if (!line.size()) { // NIL - } - else if(line[0] == '#') - { - if(line.contains(", fuzzy")) - { + } else if (line[0] == '#') { + if (line.contains(", fuzzy")) { nextFuzzy = true; } - } - else if(line.startsWith('"')) - { + } else if (line.startsWith('"')) { QByteArray temp; - QByteArray *out = &temp; + QByteArray* out = &temp; - switch(mode) - { + switch (mode) { case Mode::First: qDebug() << "Unexpected escaped string during initial state... line:" << lineNumber; return; @@ -243,16 +200,12 @@ void POTranslatorPrivate::reload() out = &id; break; } - if(!line.chompString(*out)) - { + if (!line.chompString(*out)) { qDebug() << "Badly formatted string on line:" << lineNumber; return; } - } - else if(line.chomp("msgctxt ", 8)) - { - switch(mode) - { + } else if (line.chomp("msgctxt ", 8)) { + switch (mode) { case Mode::First: break; case Mode::MessageString: @@ -263,21 +216,16 @@ void POTranslatorPrivate::reload() qDebug() << "Unexpected msgctxt line:" << lineNumber; return; } - if(line.chompString(context)) - { + if (line.chompString(context)) { auto parts = context.split('|'); context = parts[0]; - if(parts.size() > 1 && !parts[1].isEmpty()) - { + if (parts.size() > 1 && !parts[1].isEmpty()) { disambiguation = parts[1]; } mode = Mode::MessageContext; } - } - else if (line.chomp("msgid ", 6)) - { - switch(mode) - { + } else if (line.chomp("msgid ", 6)) { + switch (mode) { case Mode::MessageContext: case Mode::First: break; @@ -288,15 +236,11 @@ void POTranslatorPrivate::reload() qDebug() << "Unexpected msgid line:" << lineNumber; return; } - if(line.chompString(id)) - { + if (line.chompString(id)) { mode = Mode::MessageId; } - } - else if (line.chomp("msgstr ", 7)) - { - switch(mode) - { + } else if (line.chomp("msgstr ", 7)) { + switch (mode) { case Mode::First: case Mode::MessageString: case Mode::MessageContext: @@ -305,13 +249,10 @@ void POTranslatorPrivate::reload() case Mode::MessageId: break; } - if(line.chompString(str)) - { + if (line.chompString(str)) { mode = Mode::MessageString; } - } - else - { + } else { qDebug() << "I did not understand line: " << lineNumber << ":" << QString::fromUtf8(line); } lineNumber++; @@ -334,21 +275,17 @@ POTranslator::~POTranslator() delete d; } -QString POTranslator::translate(const char* context, const char* sourceText, const char* disambiguation, int n) const +QString POTranslator::translate(const char* context, const char* sourceText, const char* disambiguation, [[maybe_unused]] int n) const { - if(disambiguation) - { + if (disambiguation) { auto disambiguationKey = QByteArray(context) + "|" + QByteArray(sourceText) + "@" + QByteArray(disambiguation); auto iter = d->mapping_disambiguatrion.find(disambiguationKey); - if(iter != d->mapping_disambiguatrion.end()) - { - auto & entry = *iter; - if(entry.text.isEmpty()) - { + if (iter != d->mapping_disambiguatrion.end()) { + auto& entry = *iter; + if (entry.text.isEmpty()) { qDebug() << "Translation entry has no content:" << disambiguationKey; } - if(entry.fuzzy) - { + if (entry.fuzzy) { qDebug() << "Translation entry is fuzzy:" << disambiguationKey << "->" << entry.text; } return entry.text; @@ -356,15 +293,12 @@ QString POTranslator::translate(const char* context, const char* sourceText, con } auto key = QByteArray(context) + "|" + QByteArray(sourceText); auto iter = d->mapping.find(key); - if(iter != d->mapping.end()) - { - auto & entry = *iter; - if(entry.text.isEmpty()) - { + if (iter != d->mapping.end()) { + auto& entry = *iter; + if (entry.text.isEmpty()) { qDebug() << "Translation entry has no content:" << key; } - if(entry.fuzzy) - { + if (entry.fuzzy) { qDebug() << "Translation entry is fuzzy:" << key << "->" << entry.text; } return entry.text; diff --git a/launcher/translations/POTranslator.h b/launcher/translations/POTranslator.h index 1634018cc..58451e459 100644 --- a/launcher/translations/POTranslator.h +++ b/launcher/translations/POTranslator.h @@ -4,14 +4,14 @@ struct POTranslatorPrivate; -class POTranslator : public QTranslator -{ +class POTranslator : public QTranslator { Q_OBJECT -public: - explicit POTranslator(const QString& filename, QObject * parent = nullptr); + public: + explicit POTranslator(const QString& filename, QObject* parent = nullptr); virtual ~POTranslator(); - QString translate(const char * context, const char * sourceText, const char * disambiguation, int n) const override; + QString translate(const char* context, const char* sourceText, const char* disambiguation, int n) const override; bool isEmpty() const override; -private: - POTranslatorPrivate * d; + + private: + POTranslatorPrivate* d; }; diff --git a/launcher/translations/TranslationsModel.cpp b/launcher/translations/TranslationsModel.cpp index 2763cca26..933fe2d35 100644 --- a/launcher/translations/TranslationsModel.cpp +++ b/launcher/translations/TranslationsModel.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * @@ -37,18 +37,18 @@ #include "TranslationsModel.h" #include -#include -#include +#include #include #include -#include +#include +#include #include -#include "FileSystem.h" -#include "net/NetJob.h" -#include "net/ChecksumValidator.h" #include "BuildConfig.h" +#include "FileSystem.h" #include "Json.h" +#include "net/ChecksumValidator.h" +#include "net/NetJob.h" #include "POTranslator.h" @@ -56,50 +56,35 @@ const static QLatin1String defaultLangCode("en_US"); -enum class FileType -{ - NONE, - QM, - PO -}; +enum class FileType { NONE, QM, PO }; -struct Language -{ - Language() - { - updated = true; - } - Language(const QString & _key) +struct Language { + Language() { updated = true; } + Language(const QString& _key) { key = _key; locale = QLocale(key); updated = (key == defaultLangCode); } - QString languageName() const { + QString languageName() const + { QString result; - if(key == "ja_KANJI") { + if (key == "ja_KANJI") { result = locale.nativeLanguageName() + u8" (漢字)"; - } - else if(key == "es_UY") { + } else if (key == "es_UY") { result = u8"español de Latinoamérica"; - } - else if(key == "en_NZ") { - result = u8"New Zealand English"; // No idea why qt translates this to just english and not to New Zealand English - } - else if(key == "en@pirate") { + } else if (key == "en_NZ") { + result = u8"New Zealand English"; // No idea why qt translates this to just english and not to New Zealand English + } else if (key == "en@pirate") { result = u8"Tongue of the High Seas"; - } - else if(key == "en@uwu") { + } else if (key == "en@uwu") { result = u8"Cute Engwish"; - } - else if(key == "tok") { + } else if (key == "tok") { result = u8"toki pona"; - } - else if(key == "nan") { + } else if (key == "nan") { result = u8"閩南語"; // Using traditional Chinese script. Not sure if we should use simplified instead? - } - else { + } else { result = locale.nativeLanguageName(); } @@ -111,8 +96,7 @@ struct Language float percentTranslated() const { - if (total == 0) - { + if (total == 0) { return 100.0f; } return 100.0f * float(translated) / float(total); @@ -126,30 +110,17 @@ struct Language total = translated + untranslated + fuzzy; } - bool isOfSameNameAs(const Language& other) const - { - return key == other.key; - } + bool isOfSameNameAs(const Language& other) const { return key == other.key; } bool isIdenticalTo(const Language& other) const { - return - ( - key == other.key && - file_name == other.file_name && - file_size == other.file_size && - file_sha1 == other.file_sha1 && - translated == other.translated && - fuzzy == other.fuzzy && - total == other.fuzzy && - localFileType == other.localFileType - ); + return (key == other.key && file_name == other.file_name && file_size == other.file_size && file_sha1 == other.file_sha1 && + translated == other.translated && fuzzy == other.fuzzy && total == other.fuzzy && localFileType == other.localFileType); } - Language & apply(Language & other) + Language& apply(Language& other) { - if(!isOfSameNameAs(other)) - { + if (!isOfSameNameAs(other)) { return *this; } file_name = other.file_name; @@ -178,14 +149,11 @@ struct Language FileType localFileType = FileType::NONE; }; - - -struct TranslationsModel::Private -{ +struct TranslationsModel::Private { QDir m_dir; // initial state is just english - QVector m_languages = {Language (defaultLangCode)}; + QVector m_languages = { Language(defaultLangCode) }; QString m_selectedLanguage = defaultLangCode; std::unique_ptr m_qt_translator; @@ -198,7 +166,7 @@ struct TranslationsModel::Private QString m_nextDownload; std::unique_ptr m_po_translator; - QFileSystemWatcher *watcher; + QFileSystemWatcher* watcher; const QString m_system_locale = QLocale::system().name(); const QString m_system_language = m_system_locale.split('_').front(); @@ -206,7 +174,7 @@ struct TranslationsModel::Private bool no_language_set = false; }; -TranslationsModel::TranslationsModel(QString path, QObject* parent): QAbstractListModel(parent) +TranslationsModel::TranslationsModel(QString path, QObject* parent) : QAbstractListModel(parent) { d.reset(new Private); d->m_dir.setPath(path); @@ -218,15 +186,12 @@ TranslationsModel::TranslationsModel(QString path, QObject* parent): QAbstractLi d->watcher->addPath(d->m_dir.canonicalPath()); } -TranslationsModel::~TranslationsModel() -{ -} +TranslationsModel::~TranslationsModel() {} void TranslationsModel::translationDirChanged(const QString& path) { qDebug() << "Dir changed:" << path; - if (!d->no_language_set) - { + if (!d->no_language_set) { reloadLocalFiles(); } selectLanguage(selectedLanguage()); @@ -237,126 +202,97 @@ void TranslationsModel::indexReceived() qDebug() << "Got translations index!"; d->m_index_job.reset(); - if (d->no_language_set) - { + if (d->no_language_set) { reloadLocalFiles(); auto language = d->m_system_locale; - if (!findLanguage(language)) - { + if (!findLanguage(language)) { language = d->m_system_language; } selectLanguage(language); - if (selectedLanguage() != defaultLangCode) - { + if (selectedLanguage() != defaultLangCode) { updateLanguage(selectedLanguage()); } APPLICATION->settings()->set("Language", selectedLanguage()); d->no_language_set = false; } - else if(d->m_selectedLanguage != defaultLangCode) - { + else if (d->m_selectedLanguage != defaultLangCode) { downloadTranslation(d->m_selectedLanguage); } } namespace { -void readIndex(const QString & path, QMap& languages) +void readIndex(const QString& path, QMap& languages) { QByteArray data; - try - { + try { data = FS::read(path); - } - catch (const Exception &e) - { + } catch ([[maybe_unused]] const Exception& e) { qCritical() << "Translations Download Failed: index file not readable"; return; } - int index = 1; - try - { + try { auto toplevel_doc = Json::requireDocument(data); auto doc = Json::requireObject(toplevel_doc); auto file_type = Json::requireString(doc, "file_type"); - if(file_type != "MMC-TRANSLATION-INDEX") - { + if (file_type != "MMC-TRANSLATION-INDEX") { qCritical() << "Translations Download Failed: index file is of unknown file type" << file_type; return; } auto version = Json::requireInteger(doc, "version"); - if(version > 2) - { + if (version > 2) { qCritical() << "Translations Download Failed: index file is of unknown format version" << file_type; return; } auto langObjs = Json::requireObject(doc, "languages"); - for(auto iter = langObjs.begin(); iter != langObjs.end(); iter++) - { + for (auto iter = langObjs.begin(); iter != langObjs.end(); iter++) { Language lang(iter.key()); - auto langObj = Json::requireObject(iter.value()); - lang.setTranslationStats( - Json::ensureInteger(langObj, "translated", 0), - Json::ensureInteger(langObj, "untranslated", 0), - Json::ensureInteger(langObj, "fuzzy", 0) - ); + auto langObj = Json::requireObject(iter.value()); + lang.setTranslationStats(Json::ensureInteger(langObj, "translated", 0), Json::ensureInteger(langObj, "untranslated", 0), + Json::ensureInteger(langObj, "fuzzy", 0)); lang.file_name = Json::requireString(langObj, "file"); lang.file_sha1 = Json::requireString(langObj, "sha1"); lang.file_size = Json::requireInteger(langObj, "size"); languages.insert(lang.key, lang); - index++; } - } - catch (Json::JsonException & e) - { + } catch ([[maybe_unused]] Json::JsonException& e) { qCritical() << "Translations Download Failed: index file could not be parsed as json"; } } -} +} // namespace void TranslationsModel::reloadLocalFiles() { - QMap languages = {{defaultLangCode, Language(defaultLangCode)}}; + QMap languages = { { defaultLangCode, Language(defaultLangCode) } }; readIndex(d->m_dir.absoluteFilePath("index_v2.json"), languages); - auto entries = d->m_dir.entryInfoList({"mmc_*.qm", "*.po"}, QDir::Files | QDir::NoDotAndDotDot); - for(auto & entry: entries) - { + auto entries = d->m_dir.entryInfoList({ "mmc_*.qm", "*.po" }, QDir::Files | QDir::NoDotAndDotDot); + for (auto& entry : entries) { auto completeSuffix = entry.completeSuffix(); QString langCode; FileType fileType = FileType::NONE; - if(completeSuffix == "qm") - { - langCode = entry.baseName().remove(0,4); + if (completeSuffix == "qm") { + langCode = entry.baseName().remove(0, 4); fileType = FileType::QM; - } - else if(completeSuffix == "po") - { + } else if (completeSuffix == "po") { langCode = entry.baseName(); fileType = FileType::PO; - } - else - { + } else { continue; } auto langIter = languages.find(langCode); - if(langIter != languages.end()) - { - auto & language = *langIter; - if(int(fileType) > int(language.localFileType)) - { + if (langIter != languages.end()) { + auto& language = *langIter; + if (int(fileType) > int(language.localFileType)) { language.localFileType = fileType; } - } - else - { - if(fileType == FileType::PO) - { + } else { + if (fileType == FileType::PO) { Language localFound(langCode); localFound.localFileType = FileType::PO; languages.insert(langCode, localFound); @@ -365,52 +301,40 @@ void TranslationsModel::reloadLocalFiles() } // changed and removed languages - for(auto iter = d->m_languages.begin(); iter != d->m_languages.end();) - { - auto &language = *iter; + for (auto iter = d->m_languages.begin(); iter != d->m_languages.end();) { + auto& language = *iter; auto row = iter - d->m_languages.begin(); auto updatedLanguageIter = languages.find(language.key); - if(updatedLanguageIter != languages.end()) - { - if(language.isIdenticalTo(*updatedLanguageIter)) - { + if (updatedLanguageIter != languages.end()) { + if (language.isIdenticalTo(*updatedLanguageIter)) { languages.remove(language.key); - } - else - { + } else { language.apply(*updatedLanguageIter); emit dataChanged(index(row), index(row)); languages.remove(language.key); } iter++; - } - else - { + } else { beginRemoveRows(QModelIndex(), row, row); iter = d->m_languages.erase(iter); endRemoveRows(); } } // added languages - if(languages.isEmpty()) - { + if (languages.isEmpty()) { return; } beginInsertRows(QModelIndex(), 0, d->m_languages.size() + languages.size() - 1); - for(auto & language: languages) - { + for (auto& language : languages) { d->m_languages.append(language); } 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) - { + 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) - { + if (b.key == d->m_system_locale || b.key == d->m_system_language) { return false; } } @@ -420,14 +344,9 @@ void TranslationsModel::reloadLocalFiles() } namespace { -enum class Column -{ - Language, - Completeness -}; +enum class Column { Language, Completeness }; } - QVariant TranslationsModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) @@ -439,62 +358,48 @@ QVariant TranslationsModel::data(const QModelIndex& index, int role) const if (row < 0 || row >= d->m_languages.size()) return QVariant(); - auto & lang = d->m_languages[row]; - switch (role) - { - case Qt::DisplayRole: - { - switch(column) - { - case Column::Language: - { - return lang.languageName(); - } - case Column::Completeness: - { - return QString("%1%").arg(lang.percentTranslated(), 3, 'f', 1); + auto& lang = d->m_languages[row]; + switch (role) { + case Qt::DisplayRole: { + switch (column) { + case Column::Language: { + return lang.languageName(); + } + case Column::Completeness: { + return QString("%1%").arg(lang.percentTranslated(), 3, 'f', 1); + } } + qWarning("TranslationModel::data not implemented when role is DisplayRole"); } - qWarning("TranslationModel::data not implemented when role is DisplayRole"); - } - case Qt::ToolTipRole: - { - return tr("%1:\n%2 translated\n%3 fuzzy\n%4 total").arg(lang.key, QString::number(lang.translated), QString::number(lang.fuzzy), QString::number(lang.total)); - } - case Qt::UserRole: - return lang.key; - default: - return QVariant(); + case Qt::ToolTipRole: { + return tr("%1:\n%2 translated\n%3 fuzzy\n%4 total") + .arg(lang.key, QString::number(lang.translated), QString::number(lang.fuzzy), QString::number(lang.total)); + } + case Qt::UserRole: + return lang.key; + default: + return QVariant(); } } QVariant TranslationsModel::headerData(int section, Qt::Orientation orientation, int role) const { auto column = static_cast(section); - if(role == Qt::DisplayRole) - { - switch(column) - { - case Column::Language: - { + if (role == Qt::DisplayRole) { + switch (column) { + case Column::Language: { return tr("Language"); } - case Column::Completeness: - { + case Column::Completeness: { return tr("Completeness"); } } - } - else if(role == Qt::ToolTipRole) - { - switch(column) - { - case Column::Language: - { + } else if (role == Qt::ToolTipRole) { + switch (column) { + case Column::Language: { return tr("The native language name."); } - case Column::Completeness: - { + case Column::Completeness: { return tr("Completeness is the percentage of fully translated strings, not counting automatically guessed ones."); } } @@ -502,28 +407,22 @@ QVariant TranslationsModel::headerData(int section, Qt::Orientation orientation, return QAbstractListModel::headerData(section, orientation, role); } -int TranslationsModel::rowCount(const QModelIndex& parent) const +int TranslationsModel::rowCount([[maybe_unused]] const QModelIndex& parent) const { return d->m_languages.size(); } -int TranslationsModel::columnCount(const QModelIndex& parent) const +int TranslationsModel::columnCount([[maybe_unused]] const QModelIndex& parent) const { return 2; } -Language * TranslationsModel::findLanguage(const QString& key) +Language* TranslationsModel::findLanguage(const QString& key) { - auto found = std::find_if(d->m_languages.begin(), d->m_languages.end(), [&](Language & lang) - { - return lang.key == key; - }); - if(found == d->m_languages.end()) - { + auto found = std::find_if(d->m_languages.begin(), d->m_languages.end(), [&](Language& lang) { return lang.key == key; }); + if (found == d->m_languages.end()) { return nullptr; - } - else - { + } else { return found; } } @@ -568,10 +467,8 @@ bool TranslationsModel::selectLanguage(QString key) QLocale::setDefault( QLocale(APPLICATION->settings()->get("UseSystemLocale").toBool() ? QString::fromStdString(std::locale().name()) : langCode)); - // if it's the default UI language, finish - if(langCode == defaultLangCode) - { + if (langCode == defaultLangCode) { d->m_selectedLanguage = langCode; return true; } @@ -580,70 +477,47 @@ bool TranslationsModel::selectLanguage(QString key) bool successful = false; // FIXME: this is likely never present. FIX IT. d->m_qt_translator.reset(new QTranslator()); - if (d->m_qt_translator->load("qt_" + langCode, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) - { + if (d->m_qt_translator->load("qt_" + langCode, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { qDebug() << "Loading Qt Language File for" << langCode.toLocal8Bit().constData() << "..."; - if (!QCoreApplication::installTranslator(d->m_qt_translator.get())) - { + if (!QCoreApplication::installTranslator(d->m_qt_translator.get())) { qCritical() << "Loading Qt Language File failed."; d->m_qt_translator.reset(); - } - else - { + } else { successful = true; } - } - else - { + } else { d->m_qt_translator.reset(); } - if(langPtr->localFileType == FileType::PO) - { + if (langPtr->localFileType == FileType::PO) { qDebug() << "Loading Application Language File for" << langCode.toLocal8Bit().constData() << "..."; auto poTranslator = new POTranslator(FS::PathCombine(d->m_dir.path(), langCode + ".po")); - if(!poTranslator->isEmpty()) - { - if (!QCoreApplication::installTranslator(poTranslator)) - { + if (!poTranslator->isEmpty()) { + if (!QCoreApplication::installTranslator(poTranslator)) { delete poTranslator; qCritical() << "Installing Application Language File failed."; - } - else - { + } else { d->m_app_translator.reset(poTranslator); successful = true; } - } - else - { + } else { qCritical() << "Loading Application Language File failed."; d->m_app_translator.reset(); } - } - else if(langPtr->localFileType == FileType::QM) - { + } else if (langPtr->localFileType == FileType::QM) { d->m_app_translator.reset(new QTranslator()); - if (d->m_app_translator->load("mmc_" + langCode, d->m_dir.path())) - { + if (d->m_app_translator->load("mmc_" + langCode, d->m_dir.path())) { qDebug() << "Loading Application Language File for" << langCode.toLocal8Bit().constData() << "..."; - if (!QCoreApplication::installTranslator(d->m_app_translator.get())) - { + if (!QCoreApplication::installTranslator(d->m_app_translator.get())) { qCritical() << "Installing Application Language File failed."; d->m_app_translator.reset(); - } - else - { + } else { successful = true; } - } - else - { + } else { d->m_app_translator.reset(); } - } - else - { + } else { d->m_app_translator.reset(); } d->m_selectedLanguage = langCode; @@ -653,8 +527,7 @@ bool TranslationsModel::selectLanguage(QString key) QModelIndex TranslationsModel::selectedIndex() { auto found = findLanguage(d->m_selectedLanguage); - if(found) - { + if (found) { // QVector iterator freely converts to pointer to contained type return index(found - d->m_languages.begin(), 0, QModelIndex()); } @@ -668,8 +541,7 @@ QString TranslationsModel::selectedLanguage() void TranslationsModel::downloadIndex() { - if(d->m_index_job || d->m_dl_job) - { + if (d->m_index_job || d->m_dl_job) { return; } qDebug() << "Downloading Translations Index..."; @@ -686,33 +558,28 @@ void TranslationsModel::downloadIndex() void TranslationsModel::updateLanguage(QString key) { - if(key == defaultLangCode) - { + if (key == defaultLangCode) { qWarning() << "Cannot update builtin language" << key; return; } auto found = findLanguage(key); - if(!found) - { + if (!found) { qWarning() << "Cannot update invalid language" << key; return; } - if(!found->updated) - { + if (!found->updated) { downloadTranslation(key); } } void TranslationsModel::downloadTranslation(QString key) { - if(d->m_dl_job) - { + if (d->m_dl_job) { d->m_nextDownload = key; return; } auto lang = findLanguage(key); - if(!lang) - { + if (!lang) { qWarning() << "Will not download an unknown translation" << key; return; } @@ -737,8 +604,7 @@ void TranslationsModel::downloadTranslation(QString key) void TranslationsModel::downloadNext() { - if(!d->m_nextDownload.isEmpty()) - { + if (!d->m_nextDownload.isEmpty()) { downloadTranslation(d->m_nextDownload); d->m_nextDownload.clear(); } @@ -755,8 +621,7 @@ void TranslationsModel::dlGood() { qDebug() << "Got translation:" << d->m_downloadingTranslation; - if(d->m_downloadingTranslation == d->m_selectedLanguage) - { + if (d->m_downloadingTranslation == d->m_selectedLanguage) { selectLanguage(d->m_selectedLanguage); } d->m_dl_job.reset(); diff --git a/launcher/ui/ColorCache.cpp b/launcher/ui/ColorCache.cpp index ef268dd2b..f941a6093 100644 --- a/launcher/ui/ColorCache.cpp +++ b/launcher/ui/ColorCache.cpp @@ -1,13 +1,11 @@ #include "ColorCache.h" - /** * Blend the color with the front color, adapting to the back color */ QColor ColorCache::blend(QColor color) { - if (Rainbow::luma(m_front) > Rainbow::luma(m_back)) - { + if (Rainbow::luma(m_front) > Rainbow::luma(m_back)) { // for dark color schemes, produce a fitting color first color = Rainbow::tint(m_front, color, 0.5); } @@ -27,8 +25,7 @@ QColor ColorCache::blendBackground(QColor color) void ColorCache::recolorAll() { auto iter = m_colors.begin(); - while(iter != m_colors.end()) - { + while (iter != m_colors.end()) { iter->front = blend(iter->original); iter->back = blendBackground(iter->original); } diff --git a/launcher/ui/ColorCache.h b/launcher/ui/ColorCache.h index a840664da..1cf292c13 100644 --- a/launcher/ui/ColorCache.h +++ b/launcher/ui/ColorCache.h @@ -1,12 +1,11 @@ #pragma once -#include -#include #include +#include #include +#include -class ColorCache -{ -public: +class ColorCache { + public: ColorCache(QColor front, QColor back, qreal bias) { m_front = front; @@ -14,15 +13,11 @@ public: m_bias = bias; }; - void addColor(int key, QColor color) - { - m_colors[key] = {color, blend(color), blendBackground(color)}; - } + void addColor(int key, QColor color) { m_colors[key] = { color, blend(color), blendBackground(color) }; } void setForeground(QColor front) { - if(m_front != front) - { + if (m_front != front) { m_front = front; recolorAll(); } @@ -30,8 +25,7 @@ public: void setBackground(QColor back) { - if(m_back != back) - { + if (m_back != back) { m_back = back; recolorAll(); } @@ -40,8 +34,7 @@ public: QColor getFront(int key) { auto iter = m_colors.find(key); - if(iter == m_colors.end()) - { + if (iter == m_colors.end()) { return QColor(); } return (*iter).front; @@ -50,8 +43,7 @@ public: QColor getBack(int key) { auto iter = m_colors.find(key); - if(iter == m_colors.end()) - { + if (iter == m_colors.end()) { return QColor(); } return (*iter).back; @@ -67,29 +59,26 @@ public: */ QColor blendBackground(QColor color); -protected: + protected: void recolorAll(); -protected: - struct ColorEntry - { + protected: + struct ColorEntry { QColor original; QColor front; QColor back; }; -protected: + protected: qreal m_bias; QColor m_front; QColor m_back; QMap m_colors; }; -class LogColorCache : public ColorCache -{ -public: - LogColorCache(QColor front, QColor back) - : ColorCache(front, back, 1.0) +class LogColorCache : public ColorCache { + public: + LogColorCache(QColor front, QColor back) : ColorCache(front, back, 1.0) { addColor((int)MessageLevel::Launcher, QColor("purple")); addColor((int)MessageLevel::Debug, QColor("green")); @@ -101,8 +90,7 @@ public: QColor getFront(MessageLevel::Enum level) { - if(!m_colors.contains((int) level)) - { + if (!m_colors.contains((int)level)) { return ColorCache::getFront((int)MessageLevel::Message); } return ColorCache::getFront((int)level); @@ -110,8 +98,7 @@ public: QColor getBack(MessageLevel::Enum level) { - if(level == MessageLevel::Fatal) - { + if (level == MessageLevel::Fatal) { return QColor(Qt::black); } return QColor(Qt::transparent); diff --git a/launcher/ui/GuiUtil.cpp b/launcher/ui/GuiUtil.cpp index 930e088a7..584a34710 100644 --- a/launcher/ui/GuiUtil.cpp +++ b/launcher/ui/GuiUtil.cpp @@ -37,21 +37,21 @@ #include "GuiUtil.h" -#include #include +#include #include #include -#include "ui/dialogs/ProgressDialog.h" -#include "ui/dialogs/CustomMessageBox.h" #include "net/PasteUpload.h" +#include "ui/dialogs/CustomMessageBox.h" +#include "ui/dialogs/ProgressDialog.h" -#include "Application.h" -#include -#include #include +#include +#include +#include "Application.h" -std::optional GuiUtil::uploadPaste(const QString &name, const QString &text, QWidget *parentWidget) +std::optional GuiUtil::uploadPaste(const QString& name, const QString& text, QWidget* parentWidget) { ProgressDialog dialog(parentWidget); auto pasteTypeSetting = static_cast(APPLICATION->settings()->get("PastebinType").toInt()); @@ -64,8 +64,7 @@ std::optional GuiUtil::uploadPaste(const QString &name, const QString & else baseUrl = pasteCustomAPIBaseSetting; - if (baseUrl.isValid()) - { + if (baseUrl.isValid()) { auto response = CustomMessageBox::selectable(parentWidget, QObject::tr("Confirm Upload"), QObject::tr("You are about to upload \"%1\" to %2.\n" "You should double-check for personal information.\n\n" @@ -82,41 +81,38 @@ std::optional GuiUtil::uploadPaste(const QString &name, const QString & std::unique_ptr paste(new PasteUpload(parentWidget, text, pasteCustomAPIBaseSetting, pasteTypeSetting)); dialog.execWithTask(paste.get()); - if (!paste->wasSuccessful()) - { - CustomMessageBox::selectable( - parentWidget, - QObject::tr("Upload failed"), - paste->failReason(), - QMessageBox::Critical - )->exec(); + if (!paste->wasSuccessful()) { + CustomMessageBox::selectable(parentWidget, QObject::tr("Upload failed"), paste->failReason(), QMessageBox::Critical)->exec(); return QString(); - } - else - { + } else { const QString link = paste->pasteLink(); setClipboardText(link); CustomMessageBox::selectable( parentWidget, QObject::tr("Upload finished"), QObject::tr("The link to the uploaded log has been placed in your clipboard.").arg(link), - QMessageBox::Information)->exec(); + QMessageBox::Information) + ->exec(); return link; } } -void GuiUtil::setClipboardText(const QString &text) +void GuiUtil::setClipboardText(const QString& text) { QApplication::clipboard()->setText(text); } -static QStringList BrowseForFileInternal(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget, bool single) +static QStringList BrowseForFileInternal(QString context, + QString caption, + QString filter, + QString defaultPath, + QWidget* parentWidget, + bool single) { static QMap savedPaths; QFileDialog w(parentWidget, caption); QSet locations; - auto f = [&](QStandardPaths::StandardLocation l) - { + auto f = [&](QStandardPaths::StandardLocation l) { QString location = QStandardPaths::writableLocation(l); QFileInfo finfo(location); if (!finfo.exists()) { @@ -129,8 +125,7 @@ static QStringList BrowseForFileInternal(QString context, QString caption, QStri f(QStandardPaths::DownloadLocation); f(QStandardPaths::HomeLocation); QList urls; - for (auto location : locations) - { + for (auto location : locations) { urls.append(QUrl::fromLocalFile(location)); } urls.append(QUrl::fromLocalFile(defaultPath)); @@ -140,27 +135,21 @@ static QStringList BrowseForFileInternal(QString context, QString caption, QStri w.setNameFilter(filter); QString pathToOpen; - if(savedPaths.contains(context)) - { + if (savedPaths.contains(context)) { pathToOpen = savedPaths[context]; - } - else - { + } else { pathToOpen = defaultPath; } - if(!pathToOpen.isEmpty()) - { + if (!pathToOpen.isEmpty()) { QFileInfo finfo(pathToOpen); - if(finfo.exists() && finfo.isDir()) - { + if (finfo.exists() && finfo.isDir()) { w.setDirectory(finfo.absoluteFilePath()); } } w.setSidebarUrls(urls); - if (w.exec()) - { + if (w.exec()) { savedPaths[context] = w.directory().absolutePath(); return w.selectedFiles(); } @@ -168,18 +157,16 @@ static QStringList BrowseForFileInternal(QString context, QString caption, QStri return {}; } -QString GuiUtil::BrowseForFile(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget) +QString GuiUtil::BrowseForFile(QString context, QString caption, QString filter, QString defaultPath, QWidget* parentWidget) { auto resultList = BrowseForFileInternal(context, caption, filter, defaultPath, parentWidget, true); - if(resultList.size()) - { + if (resultList.size()) { return resultList[0]; } return QString(); } - -QStringList GuiUtil::BrowseForFiles(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget) +QStringList GuiUtil::BrowseForFiles(QString context, QString caption, QString filter, QString defaultPath, QWidget* parentWidget) { return BrowseForFileInternal(context, caption, filter, defaultPath, parentWidget, false); } diff --git a/launcher/ui/GuiUtil.h b/launcher/ui/GuiUtil.h index 96ebd9a2d..8d384d3f6 100644 --- a/launcher/ui/GuiUtil.h +++ b/launcher/ui/GuiUtil.h @@ -3,10 +3,9 @@ #include #include -namespace GuiUtil -{ -std::optional uploadPaste(const QString &name, const QString &text, QWidget *parentWidget); -void setClipboardText(const QString &text); -QStringList BrowseForFiles(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget); -QString BrowseForFile(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget); -} +namespace GuiUtil { +std::optional uploadPaste(const QString& name, const QString& text, QWidget* parentWidget); +void setClipboardText(const QString& text); +QStringList BrowseForFiles(QString context, QString caption, QString filter, QString defaultPath, QWidget* parentWidget); +QString BrowseForFile(QString context, QString caption, QString filter, QString defaultPath, QWidget* parentWidget); +} // namespace GuiUtil diff --git a/launcher/ui/InstanceWindow.cpp b/launcher/ui/InstanceWindow.cpp index c62b370fd..7025cb795 100644 --- a/launcher/ui/InstanceWindow.cpp +++ b/launcher/ui/InstanceWindow.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 TheKodeToad * * 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 @@ -36,12 +37,12 @@ #include "InstanceWindow.h" #include "Application.h" -#include -#include -#include -#include #include #include +#include +#include +#include +#include #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" @@ -51,8 +52,7 @@ #include "icons/IconList.h" -InstanceWindow::InstanceWindow(InstancePtr instance, QWidget *parent) - : QMainWindow(parent), m_instance(instance) +InstanceWindow::InstanceWindow(InstancePtr instance, QWidget* parent) : QMainWindow(parent), m_instance(instance) { setAttribute(Qt::WA_DeleteOnClose); @@ -143,8 +143,7 @@ InstanceWindow::InstanceWindow(InstancePtr instance, QWidget *parent) void InstanceWindow::on_instanceStatusChanged(BaseInstance::Status, BaseInstance::Status newStatus) { - if(newStatus == BaseInstance::Status::Gone) - { + if (newStatus == BaseInstance::Status::Gone) { m_doNotSave = true; close(); } @@ -152,25 +151,20 @@ void InstanceWindow::on_instanceStatusChanged(BaseInstance::Status, BaseInstance void InstanceWindow::updateLaunchButtons() { - if(m_instance->isRunning()) - { + if (m_instance->isRunning()) { m_launchOfflineButton->setEnabled(false); m_launchDemoButton->setEnabled(false); m_killButton->setText(tr("Kill")); m_killButton->setObjectName("killButton"); m_killButton->setToolTip(tr("Kill the running instance")); - } - else if(!m_instance->canLaunch()) - { + } else if (!m_instance->canLaunch()) { m_launchOfflineButton->setEnabled(false); m_launchDemoButton->setEnabled(false); m_killButton->setText(tr("Launch")); m_killButton->setObjectName("launchButton"); m_killButton->setToolTip(tr("Launch the instance")); m_killButton->setEnabled(false); - } - else - { + } else { m_launchOfflineButton->setEnabled(true); // Disable demo-mode if not available. @@ -207,7 +201,7 @@ void InstanceWindow::runningStateChanged(bool running) { updateLaunchButtons(); m_container->refreshContainer(); - if(running) { + if (running) { selectPage("log"); } } @@ -217,16 +211,14 @@ void InstanceWindow::on_closeButton_clicked() close(); } -void InstanceWindow::closeEvent(QCloseEvent *event) +void InstanceWindow::closeEvent(QCloseEvent* event) { bool proceed = true; - if(!m_doNotSave) - { + if (!m_doNotSave) { proceed &= m_container->prepareToClose(); } - if(!proceed) - { + if (!proceed) { return; } @@ -243,12 +235,9 @@ bool InstanceWindow::saveAll() void InstanceWindow::on_btnKillMinecraft_clicked() { - if(m_instance->isRunning()) - { + if (m_instance->isRunning()) { APPLICATION->kill(m_instance); - } - else - { + } else { APPLICATION->launch(m_instance, true, false, nullptr); } } @@ -263,19 +252,21 @@ bool InstanceWindow::selectPage(QString pageId) return m_container->selectPage(pageId); } +BasePage* InstanceWindow::selectedPage() const +{ + return m_container->selectedPage(); +} + void InstanceWindow::refreshContainer() { m_container->refreshContainer(); } -InstanceWindow::~InstanceWindow() -{ -} +InstanceWindow::~InstanceWindow() {} bool InstanceWindow::requestClose() { - if(m_container->prepareToClose()) - { + if (m_container->prepareToClose()) { close(); return true; } diff --git a/launcher/ui/InstanceWindow.h b/launcher/ui/InstanceWindow.h index 554c4c740..70f206f28 100644 --- a/launcher/ui/InstanceWindow.h +++ b/launcher/ui/InstanceWindow.h @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 TheKodeToad * * 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 @@ -47,15 +48,15 @@ class QPushButton; class PageContainer; -class InstanceWindow : public QMainWindow, public BasePageContainer -{ +class InstanceWindow : public QMainWindow, public BasePageContainer { Q_OBJECT -public: - explicit InstanceWindow(InstancePtr proc, QWidget *parent = 0); + public: + explicit InstanceWindow(InstancePtr proc, QWidget* parent = 0); virtual ~InstanceWindow(); bool selectPage(QString pageId) override; + BasePage* selectedPage() const override; void refreshContainer() override; QString instanceId(); @@ -66,11 +67,10 @@ public: // request closing the window (from a page) bool requestClose() override; -signals: + signals: void isClosing(); -private -slots: + private slots: void on_closeButton_clicked(); void on_btnKillMinecraft_clicked(); void on_btnLaunchMinecraftOffline_clicked(); @@ -80,19 +80,19 @@ slots: void runningStateChanged(bool running); void on_instanceStatusChanged(BaseInstance::Status, BaseInstance::Status newStatus); -protected: - void closeEvent(QCloseEvent *) override; + protected: + void closeEvent(QCloseEvent*) override; -private: + private: void updateLaunchButtons(); -private: + private: shared_qobject_ptr m_proc; InstancePtr m_instance; bool m_doNotSave = false; - PageContainer *m_container = nullptr; - QPushButton *m_closeButton = nullptr; - QPushButton *m_killButton = nullptr; - QPushButton *m_launchOfflineButton = nullptr; - QPushButton *m_launchDemoButton = nullptr; + PageContainer* m_container = nullptr; + QPushButton* m_closeButton = nullptr; + QPushButton* m_killButton = nullptr; + QPushButton* m_launchOfflineButton = nullptr; + QPushButton* m_launchDemoButton = nullptr; }; diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index ed894ead5..067108f2d 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -43,102 +43,104 @@ #include "FileSystem.h" #include "MainWindow.h" +#include "ui/dialogs/ExportToModListDialog.h" #include "ui_MainWindow.h" -#include -#include #include #include +#include +#include -#include #include #include #include #include +#include #include #include +#include +#include +#include #include -#include -#include -#include #include #include #include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include #include #include #include -#include -#include "InstanceWindow.h" #include "InstancePageProvider.h" +#include "InstanceWindow.h" #include "JavaCommon.h" #include "LaunchController.h" -#include "ui/instanceview/InstanceProxyModel.h" -#include "ui/instanceview/InstanceView.h" -#include "ui/instanceview/InstanceDelegate.h" -#include "ui/widgets/LabeledToolButton.h" -#include "ui/dialogs/NewInstanceDialog.h" -#include "ui/dialogs/NewsDialog.h" -#include "ui/dialogs/ProgressDialog.h" #include "ui/dialogs/AboutDialog.h" -#include "ui/dialogs/CustomMessageBox.h" -#include "ui/dialogs/IconPickerDialog.h" #include "ui/dialogs/CopyInstanceDialog.h" +#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/EditAccountDialog.h" #include "ui/dialogs/ExportInstanceDialog.h" #include "ui/dialogs/ExportPackDialog.h" +#include "ui/dialogs/IconPickerDialog.h" #include "ui/dialogs/ImportResourceDialog.h" +#include "ui/dialogs/NewInstanceDialog.h" +#include "ui/dialogs/NewsDialog.h" +#include "ui/dialogs/ProgressDialog.h" +#include "ui/instanceview/InstanceDelegate.h" +#include "ui/instanceview/InstanceProxyModel.h" +#include "ui/instanceview/InstanceView.h" #include "ui/themes/ITheme.h" #include "ui/themes/ThemeManager.h" +#include "ui/widgets/LabeledToolButton.h" -#include "minecraft/mod/tasks/LocalResourceParse.h" +#include "minecraft/WorldList.h" #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ShaderPackFolderModel.h" -#include "minecraft/WorldList.h" +#include "minecraft/mod/tasks/LocalResourceParse.h" + +#include "modplatform/flame/FlameAPI.h" #include "KonamiCode.h" -#include "InstanceImportTask.h" #include "InstanceCopyTask.h" +#include "InstanceImportTask.h" + +#include "Json.h" #include "MMCTime.h" namespace { -QString profileInUseFilter(const QString & profile, bool used) +QString profileInUseFilter(const QString& profile, bool used) { - if(used) - { + if (used) { return QObject::tr("%1 (in use)").arg(profile); - } - else - { + } else { return profile; } } -} +} // namespace -MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) +MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); @@ -184,7 +186,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi ui->instanceToolBar->addContextMenuAction(ui->newsToolBar->toggleViewAction()); ui->instanceToolBar->addContextMenuAction(ui->instanceToolBar->toggleViewAction()); ui->instanceToolBar->addContextMenuAction(ui->actionLockToolbars); - } // set the menu for the folders help, accounts, and export tool buttons @@ -206,6 +207,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi exportInstanceMenu->addAction(ui->actionExportInstanceZip); exportInstanceMenu->addAction(ui->actionExportInstanceMrPack); exportInstanceMenu->addAction(ui->actionExportInstanceFlamePack); + exportInstanceMenu->addAction(ui->actionExportInstanceToModList); ui->actionExportInstance->setMenu(exportInstanceMenu); } @@ -231,7 +233,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi if (qgetenv("XDG_CURRENT_DESKTOP") == "gamescope") { ui->mainToolBar->addAction(ui->actionCloseWindow); } - } // add the toolbar toggles to the view menu @@ -301,9 +302,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi connect(proxymodel, &InstanceProxyModel::dataChanged, this, &MainWindow::instanceDataChanged); view->setModel(proxymodel); - view->setSourceOfGroupCollapseStatus([](const QString & groupName)->bool { - return APPLICATION->instances()->isGroupCollapsed(groupName); - }); + view->setSourceOfGroupCollapseStatus( + [](const QString& groupName) -> bool { return APPLICATION->instances()->isGroupCollapsed(groupName); }); connect(view, &InstanceView::groupStateChanged, APPLICATION->instances().get(), &InstanceList::on_GroupStateChanged); ui->horizontalLayout->addWidget(view); } @@ -349,7 +349,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi statusBar()->addPermanentWidget(m_statusCenter, 0); // Add "manage accounts" button, right align - QWidget *spacer = new QWidget(); + QWidget* spacer = new QWidget(); spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); ui->mainToolBar->insertWidget(ui->actionAccountsButton, spacer); @@ -361,21 +361,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi // Update the menu when the active account changes. // Shouldn't have to use lambdas here like this, but if I don't, the compiler throws a fit. // Template hell sucks... - connect( - APPLICATION->accounts().get(), - &AccountList::defaultAccountChanged, - [this] { - defaultAccountChanged(); - } - ); - connect( - APPLICATION->accounts().get(), - &AccountList::listChanged, - [this] - { - repopulateAccountsMenu(); - } - ); + connect(APPLICATION->accounts().get(), &AccountList::defaultAccountChanged, [this] { defaultAccountChanged(); }); + connect(APPLICATION->accounts().get(), &AccountList::listChanged, [this] { repopulateAccountsMenu(); }); // Show initial account defaultAccountChanged(); @@ -416,9 +403,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi // macOS always has a native menu bar, so these fixes are not applicable // Other systems may or may not have a native menu bar (most do not - it seems like only Ubuntu Unity does) #ifndef Q_OS_MAC -void MainWindow::keyReleaseEvent(QKeyEvent *event) +void MainWindow::keyReleaseEvent(QKeyEvent* event) { - if(event->key()==Qt::Key_Alt && !APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool()) + if (event->key() == Qt::Key_Alt && !APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool()) ui->menuBar->setVisible(!ui->menuBar->isVisible()); else QMainWindow::keyReleaseEvent(event); @@ -427,7 +414,6 @@ void MainWindow::keyReleaseEvent(QKeyEvent *event) void MainWindow::retranslateUi() { - if (m_selectedInstance) { m_statusLeft->setText(m_selectedInstance->getStatusbarDescription()); } else { @@ -437,7 +423,7 @@ void MainWindow::retranslateUi() ui->retranslateUi(this); MinecraftAccountPtr defaultAccount = APPLICATION->accounts()->defaultAccount(); - if(defaultAccount) { + if (defaultAccount) { auto profileLabel = profileInUseFilter(defaultAccount->profileName(), defaultAccount->isInUse()); ui->actionAccountsButton->setText(profileLabel); } @@ -457,14 +443,12 @@ void MainWindow::retranslateUi() } } -MainWindow::~MainWindow() -{ -} +MainWindow::~MainWindow() {} -QMenu * MainWindow::createPopupMenu() +QMenu* MainWindow::createPopupMenu() { QMenu* filteredMenu = QMainWindow::createPopupMenu(); - filteredMenu->removeAction( ui->mainToolBar->toggleViewAction() ); + filteredMenu->removeAction(ui->mainToolBar->toggleViewAction()); filteredMenu->addAction(ui->actionLockToolbars); @@ -478,10 +462,11 @@ void MainWindow::lockToolbars(bool state) APPLICATION->settings()->set("ToolbarsLocked", state); } - void MainWindow::konamiTriggered() { - QString gradient = " stop:0 rgba(125, 0, 0, 255), stop:0.166 rgba(125, 125, 0, 255), stop:0.333 rgba(0, 125, 0, 255), stop:0.5 rgba(0, 125, 125, 255), stop:0.666 rgba(0, 0, 125, 255), stop:0.833 rgba(125, 0, 125, 255), stop:1 rgba(125, 0, 0, 255));"; + QString gradient = + " stop:0 rgba(125, 0, 0, 255), stop:0.166 rgba(125, 125, 0, 255), stop:0.333 rgba(0, 125, 0, 255), stop:0.5 rgba(0, 125, 125, " + "255), stop:0.666 rgba(0, 0, 125, 255), stop:0.833 rgba(125, 0, 125, 255), stop:1 rgba(125, 0, 0, 255));"; QString stylesheet = "background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0," + gradient; if (ui->mainToolBar->styleSheet() == stylesheet) { ui->mainToolBar->setStyleSheet(""); @@ -500,16 +485,15 @@ void MainWindow::konamiTriggered() } } -void MainWindow::showInstanceContextMenu(const QPoint &pos) +void MainWindow::showInstanceContextMenu(const QPoint& pos) { - QList actions; + QList actions; - QAction *actionSep = new QAction("", this); + QAction* actionSep = new QAction("", this); actionSep->setSeparator(true); bool onInstance = view->indexAt(pos).isValid(); - if (onInstance) - { + if (onInstance) { // reuse the file menu actions actions = ui->fileMenu->actions(); @@ -523,24 +507,21 @@ void MainWindow::showInstanceContextMenu(const QPoint &pos) // add header actions.prepend(actionSep); - QAction *actionVoid = new QAction(m_selectedInstance->name(), this); + QAction* actionVoid = new QAction(m_selectedInstance->name(), this); actionVoid->setEnabled(false); actions.prepend(actionVoid); - } - else - { + } else { auto group = view->groupNameAt(pos); - QAction *actionVoid = new QAction(BuildConfig.LAUNCHER_DISPLAYNAME, this); + QAction* actionVoid = new QAction(BuildConfig.LAUNCHER_DISPLAYNAME, this); actionVoid->setEnabled(false); - QAction *actionCreateInstance = new QAction(tr("Create instance"), this); + QAction* actionCreateInstance = new QAction(tr("Create instance"), this); actionCreateInstance->setToolTip(ui->actionAddInstance->toolTip()); - if(!group.isNull()) - { - QVariantMap data; - data["group"] = group; - actionCreateInstance->setData(data); + if (!group.isNull()) { + QVariantMap instance_action_data; + instance_action_data["group"] = group; + actionCreateInstance->setData(instance_action_data); } connect(actionCreateInstance, SIGNAL(triggered(bool)), SLOT(on_actionAddInstance_triggered())); @@ -548,12 +529,11 @@ void MainWindow::showInstanceContextMenu(const QPoint &pos) actions.prepend(actionSep); actions.prepend(actionVoid); actions.append(actionCreateInstance); - if(!group.isNull()) - { - QAction *actionDeleteGroup = new QAction(tr("Delete group '%1'").arg(group), this); - QVariantMap data; - data["group"] = group; - actionDeleteGroup->setData(data); + if (!group.isNull()) { + QAction* actionDeleteGroup = new QAction(tr("Delete group '%1'").arg(group), this); + QVariantMap delete_group_action_data; + delete_group_action_data["group"] = group; + actionDeleteGroup->setData(delete_group_action_data); connect(actionDeleteGroup, SIGNAL(triggered(bool)), SLOT(deleteGroup())); actions.append(actionDeleteGroup); } @@ -581,39 +561,27 @@ void MainWindow::updateToolsMenu() ui->actionLaunchInstanceOffline->setDisabled(!m_selectedInstance || currentInstanceRunning); ui->actionLaunchInstanceDemo->setDisabled(!m_selectedInstance || currentInstanceRunning); - QMenu *launchMenu = ui->actionLaunchInstance->menu(); - if (launchMenu) - { + QMenu* launchMenu = ui->actionLaunchInstance->menu(); + if (launchMenu) { launchMenu->clear(); - } - else - { + } else { launchMenu = new QMenu(this); } - QAction *normalLaunch = launchMenu->addAction(tr("Launch")); + QAction* normalLaunch = launchMenu->addAction(tr("Launch")); normalLaunch->setShortcut(QKeySequence::Open); - QAction *normalLaunchOffline = launchMenu->addAction(tr("Launch Offline")); + QAction* normalLaunchOffline = launchMenu->addAction(tr("Launch Offline")); normalLaunchOffline->setShortcut(QKeySequence(tr("Ctrl+Shift+O"))); - QAction *normalLaunchDemo = launchMenu->addAction(tr("Launch Demo")); + QAction* normalLaunchDemo = launchMenu->addAction(tr("Launch Demo")); normalLaunchDemo->setShortcut(QKeySequence(tr("Ctrl+Alt+O"))); - if (m_selectedInstance) - { + if (m_selectedInstance) { normalLaunch->setEnabled(m_selectedInstance->canLaunch()); normalLaunchOffline->setEnabled(m_selectedInstance->canLaunch()); normalLaunchDemo->setEnabled(m_selectedInstance->canLaunch()); - connect(normalLaunch, &QAction::triggered, [this]() { - APPLICATION->launch(m_selectedInstance, true, false); - }); - connect(normalLaunchOffline, &QAction::triggered, [this]() { - APPLICATION->launch(m_selectedInstance, false, false); - }); - connect(normalLaunchDemo, &QAction::triggered, [this]() { - APPLICATION->launch(m_selectedInstance, false, true); - }); - } - else - { + connect(normalLaunch, &QAction::triggered, [this]() { APPLICATION->launch(m_selectedInstance, true, false); }); + connect(normalLaunchOffline, &QAction::triggered, [this]() { APPLICATION->launch(m_selectedInstance, false, false); }); + connect(normalLaunchDemo, &QAction::triggered, [this]() { APPLICATION->launch(m_selectedInstance, false, true); }); + } else { normalLaunch->setDisabled(true); normalLaunchOffline->setDisabled(true); normalLaunchDemo->setDisabled(true); @@ -627,35 +595,25 @@ void MainWindow::updateToolsMenu() QString profilersTitle = tr("Profilers"); launchMenu->addSeparator()->setText(profilersTitle); - for (auto profiler : APPLICATION->profilers().values()) - { - QAction *profilerAction = launchMenu->addAction(profiler->name()); - QAction *profilerOfflineAction = launchMenu->addAction(tr("%1 Offline").arg(profiler->name())); + for (auto profiler : APPLICATION->profilers().values()) { + QAction* profilerAction = launchMenu->addAction(profiler->name()); + QAction* profilerOfflineAction = launchMenu->addAction(tr("%1 Offline").arg(profiler->name())); QString error; - if (!profiler->check(&error)) - { + if (!profiler->check(&error)) { profilerAction->setDisabled(true); profilerOfflineAction->setDisabled(true); QString profilerToolTip = tr("Profiler not setup correctly. Go into settings, \"External Tools\"."); profilerAction->setToolTip(profilerToolTip); profilerOfflineAction->setToolTip(profilerToolTip); - } - else if (m_selectedInstance) - { + } else if (m_selectedInstance) { profilerAction->setEnabled(m_selectedInstance->canLaunch()); profilerOfflineAction->setEnabled(m_selectedInstance->canLaunch()); - connect(profilerAction, &QAction::triggered, [this, profiler]() - { - APPLICATION->launch(m_selectedInstance, true, false, profiler.get()); - }); - connect(profilerOfflineAction, &QAction::triggered, [this, profiler]() - { - APPLICATION->launch(m_selectedInstance, false, false, profiler.get()); - }); - } - else - { + connect(profilerAction, &QAction::triggered, + [this, profiler]() { APPLICATION->launch(m_selectedInstance, true, false, profiler.get()); }); + connect(profilerOfflineAction, &QAction::triggered, + [this, profiler]() { APPLICATION->launch(m_selectedInstance, false, false, profiler.get()); }); + } else { profilerAction->setDisabled(true); profilerOfflineAction->setDisabled(true); } @@ -665,7 +623,7 @@ void MainWindow::updateToolsMenu() void MainWindow::updateThemeMenu() { - QMenu *themeMenu = ui->actionChangeTheme->menu(); + QMenu* themeMenu = ui->actionChangeTheme->menu(); if (themeMenu) { themeMenu->clear(); @@ -673,12 +631,12 @@ void MainWindow::updateThemeMenu() themeMenu = new QMenu(this); } - auto themes = APPLICATION->getValidApplicationThemes(); + auto themes = APPLICATION->themeManager()->getValidApplicationThemes(); - QActionGroup* themesGroup = new QActionGroup( this ); + QActionGroup* themesGroup = new QActionGroup(this); for (auto* theme : themes) { - QAction * themeAction = themeMenu->addAction(theme->name()); + QAction* themeAction = themeMenu->addAction(theme->name()); themeAction->setCheckable(true); if (APPLICATION->settings()->get("ApplicationTheme").toString() == theme->id()) { @@ -687,7 +645,7 @@ void MainWindow::updateThemeMenu() themeAction->setActionGroup(themesGroup); connect(themeAction, &QAction::triggered, [theme]() { - APPLICATION->setApplicationTheme(theme->id()); + APPLICATION->themeManager()->setApplicationTheme(theme->id()); APPLICATION->settings()->set("ApplicationTheme", theme->id()); }); } @@ -700,7 +658,7 @@ void MainWindow::repopulateAccountsMenu() ui->accountsMenu->clear(); // NOTE: this is done so the accounts button text is not set to the accounts menu title - QMenu *accountsButtonMenu = ui->actionAccountsButton->menu(); + QMenu* accountsButtonMenu = ui->actionAccountsButton->menu(); if (accountsButtonMenu) { accountsButtonMenu->clear(); } else { @@ -712,11 +670,9 @@ void MainWindow::repopulateAccountsMenu() MinecraftAccountPtr defaultAccount = accounts->defaultAccount(); QString active_profileId = ""; - if (defaultAccount) - { + if (defaultAccount) { // this can be called before accountMenuButton exists - if (ui->actionAccountsButton) - { + if (ui->actionAccountsButton) { auto profileLabel = profileInUseFilter(defaultAccount->profileName(), defaultAccount->isInUse()); ui->actionAccountsButton->setText(profileLabel); } @@ -724,38 +680,31 @@ void MainWindow::repopulateAccountsMenu() QActionGroup* accountsGroup = new QActionGroup(this); - if (accounts->count() <= 0) - { + if (accounts->count() <= 0) { ui->actionNoAccountsAdded->setEnabled(false); ui->accountsMenu->addAction(ui->actionNoAccountsAdded); - } - else - { + } else { // TODO: Nicer way to iterate? - for (int i = 0; i < accounts->count(); i++) - { + for (int i = 0; i < accounts->count(); i++) { MinecraftAccountPtr account = accounts->at(i); auto profileLabel = profileInUseFilter(account->profileName(), account->isInUse()); - QAction *action = new QAction(profileLabel, this); + QAction* action = new QAction(profileLabel, this); action->setData(i); action->setCheckable(true); action->setActionGroup(accountsGroup); - if (defaultAccount == account) - { + if (defaultAccount == account) { action->setChecked(true); } auto face = account->getFace(); - if(!face.isNull()) { + if (!face.isNull()) { action->setIcon(face); - } - else { + } else { action->setIcon(APPLICATION->getThemedIcon("noaccount")); } const int highestNumberKey = 9; - if(isetShortcut(QKeySequence(tr("Ctrl+%1").arg(i + 1))); } @@ -782,8 +731,7 @@ void MainWindow::repopulateAccountsMenu() void MainWindow::updatesAllowedChanged(bool allowed) { - if(!BuildConfig.UPDATER_ENABLED) - { + if (!BuildConfig.UPDATER_ENABLED) { return; } ui->actionCheckUpdate->setEnabled(allowed); @@ -794,16 +742,16 @@ void MainWindow::updatesAllowedChanged(bool allowed) */ void MainWindow::changeActiveAccount() { - QAction *sAction = (QAction *)sender(); + QAction* sAction = (QAction*)sender(); // Profile's associated Mojang username if (sAction->data().type() != QVariant::Type::Int) return; - QVariant data = sAction->data(); + QVariant action_data = sAction->data(); bool valid = false; - int index = data.toInt(&valid); - if(!valid) { + int index = action_data.toInt(&valid); + if (!valid) { index = -1; } auto accounts = APPLICATION->accounts(); @@ -818,15 +766,13 @@ void MainWindow::defaultAccountChanged() MinecraftAccountPtr account = APPLICATION->accounts()->defaultAccount(); // FIXME: this needs adjustment for MSA - if (account && account->profileName() != "") - { + if (account && account->profileName() != "") { auto profileLabel = profileInUseFilter(account->profileName(), account->isInUse()); ui->actionAccountsButton->setText(profileLabel); auto face = account->getFace(); - if(face.isNull()) { + if (face.isNull()) { ui->actionAccountsButton->setIcon(APPLICATION->getThemedIcon("noaccount")); - } - else { + } else { ui->actionAccountsButton->setIcon(face); } return; @@ -837,33 +783,30 @@ void MainWindow::defaultAccountChanged() ui->actionAccountsButton->setText(tr("Accounts")); } -bool MainWindow::eventFilter(QObject *obj, QEvent *ev) +bool MainWindow::eventFilter(QObject* obj, QEvent* ev) { - if (obj == view) - { - if (ev->type() == QEvent::KeyPress) - { + if (obj == view) { + if (ev->type() == QEvent::KeyPress) { secretEventFilter->input(ev); - QKeyEvent *keyEvent = static_cast(ev); - switch (keyEvent->key()) - { - /* - case Qt::Key_Enter: - case Qt::Key_Return: - activateInstance(m_selectedInstance); - return true; - */ - case Qt::Key_Delete: - on_actionDeleteInstance_triggered(); - return true; - case Qt::Key_F5: - refreshInstances(); - return true; - case Qt::Key_F2: - on_actionRenameInstance_triggered(); - return true; - default: - break; + QKeyEvent* keyEvent = static_cast(ev); + switch (keyEvent->key()) { + /* + case Qt::Key_Enter: + case Qt::Key_Return: + activateInstance(m_selectedInstance); + return true; + */ + case Qt::Key_Delete: + on_actionDeleteInstance_triggered(); + return true; + case Qt::Key_F5: + refreshInstances(); + return true; + case Qt::Key_F2: + on_actionRenameInstance_triggered(); + return true; + default: + break; } } } @@ -872,23 +815,17 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *ev) void MainWindow::updateNewsLabel() { - if (m_newsChecker->isLoadingNews()) - { + if (m_newsChecker->isLoadingNews()) { newsLabel->setText(tr("Loading news...")); newsLabel->setEnabled(false); ui->actionMoreNews->setVisible(false); - } - else - { + } else { QList entries = m_newsChecker->getNewsEntries(); - if (entries.length() > 0) - { + if (entries.length() > 0) { newsLabel->setText(entries[0]->title); newsLabel->setEnabled(true); ui->actionMoreNews->setVisible(true); - } - else - { + } else { newsLabel->setText(tr("No news available.")); newsLabel->setEnabled(false); ui->actionMoreNews->setVisible(false); @@ -896,7 +833,7 @@ void MainWindow::updateNewsLabel() } } -QList stringToIntList(const QString &string) +QList stringToIntList(const QString& string) { #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QStringList split = string.split(',', Qt::SkipEmptyParts); @@ -904,17 +841,15 @@ QList stringToIntList(const QString &string) QStringList split = string.split(',', QString::SkipEmptyParts); #endif QList out; - for (int i = 0; i < split.size(); ++i) - { + for (int i = 0; i < split.size(); ++i) { out.append(split.at(i).toInt()); } return out; } -QString intListToString(const QList &list) +QString intListToString(const QList& list) { QStringList slist; - for (int i = 0; i < list.size(); ++i) - { + for (int i = 0; i < list.size(); ++i) { slist.append(QString::number(list.at(i))); } return slist.join(','); @@ -932,30 +867,26 @@ void MainWindow::setCatBackground(bool enabled) view->viewport()->repaint(); } -void MainWindow::runModalTask(Task *task) +void MainWindow::runModalTask(Task* task) { - connect(task, &Task::failed, [this](QString reason) - { - 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(); - } - }); - connect(task, &Task::aborted, [this] - { - CustomMessageBox::selectable(this, tr("Task aborted"), tr("The task has been aborted by the user."), QMessageBox::Information)->show(); - }); + connect(task, &Task::failed, + [this](QString reason) { 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(); + } + }); + connect(task, &Task::aborted, [this] { + CustomMessageBox::selectable(this, tr("Task aborted"), tr("The task has been aborted by the user."), QMessageBox::Information) + ->show(); + }); ProgressDialog loadDialog(this); loadDialog.setSkipButton(true, tr("Abort")); loadDialog.execWithTask(task); } -void MainWindow::instanceFromInstanceTask(InstanceTask *rawTask) +void MainWindow::instanceFromInstanceTask(InstanceTask* rawTask) { unique_qobject_ptr task(APPLICATION->instances()->wrapInstanceTask(rawTask)); runModalTask(task.get()); @@ -982,64 +913,54 @@ void MainWindow::finalizeInstance(InstancePtr inst) { view->updateGeometries(); setSelectedInstanceById(inst->id()); - if (APPLICATION->accounts()->anyAccountIsValid()) - { + if (APPLICATION->accounts()->anyAccountIsValid()) { ProgressDialog loadDialog(this); auto update = inst->createUpdateTask(Net::Mode::Online); - connect(update.get(), &Task::failed, [this](QString reason) - { - QString error = QString("Instance load failed: %1").arg(reason); - CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show(); - }); - if(update) - { + connect(update.get(), &Task::failed, [this](QString reason) { + QString error = QString("Instance load failed: %1").arg(reason); + CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show(); + }); + if (update) { loadDialog.setSkipButton(true, tr("Abort")); loadDialog.execWithTask(update.get()); } - } - else - { - CustomMessageBox::selectable( - this, - tr("Error"), - tr("The launcher cannot download Minecraft or update instances unless you have at least " - "one account added.\nPlease add your Mojang or Minecraft account."), - QMessageBox::Warning - )->show(); + } else { + CustomMessageBox::selectable(this, tr("Error"), + tr("The launcher cannot download Minecraft or update instances unless you have at least " + "one account added.\nPlease add your Mojang or Minecraft account."), + QMessageBox::Warning) + ->show(); } } -void MainWindow::addInstance(QString url) +void MainWindow::addInstance(const QString& url, const QMap& extra_info) { QString groupName; - do - { + do { QObject* obj = sender(); - if(!obj) + if (!obj) break; - QAction *action = qobject_cast(obj); - if(!action) + QAction* action = qobject_cast(obj); + if (!action) break; auto map = action->data().toMap(); - if(!map.contains("group")) + if (!map.contains("group")) break; groupName = map["group"].toString(); - } while(0); + } while (0); - if(groupName.isEmpty()) - { + if (groupName.isEmpty()) { groupName = APPLICATION->settings()->get("LastUsedGroupForNewInstance").toString(); } - NewInstanceDialog newInstDlg(groupName, url, this); + NewInstanceDialog newInstDlg(groupName, url, extra_info, this); if (!newInstDlg.exec()) return; APPLICATION->settings()->set("LastUsedGroupForNewInstance", newInstDlg.instGroup()); - InstanceTask * creationTask = newInstDlg.extractTask(); - if(creationTask) - { + InstanceTask* creationTask = newInstDlg.extractTask(); + if (creationTask) { instanceFromInstanceTask(creationTask); } } @@ -1059,18 +980,101 @@ void MainWindow::processURLs(QList urls) if (url.scheme().isEmpty()) url.setScheme("file"); - if (!url.isLocalFile()) { // probably instance/modpack - addInstance(url.toString()); - break; + QMap extra_info; + QUrl local_url; + if (!url.isLocalFile()) { // download the remote resource and identify + QUrl dl_url; + if (url.scheme() == "curseforge") { + // need to find the download link for the modpack / resource + // format of url curseforge://install?addonId=IDHERE&fileId=IDHERE + QUrlQuery query(url); + + auto addonId = query.allQueryItemValues("addonId")[0]; + auto fileId = query.allQueryItemValues("fileId")[0]; + + extra_info.insert("pack_id", addonId); + extra_info.insert("pack_version_id", fileId); + + auto array = std::make_shared(); + + auto api = FlameAPI(); + auto job = api.getFile(addonId, fileId, array); + + QString resource_name; + + connect(job.get(), &Task::failed, this, + [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); + connect(job.get(), &Task::succeeded, this, [this, array, addonId, fileId, &dl_url, &resource_name] { + qDebug() << "Returned CFURL Json:\n" << array->toStdString().c_str(); + auto doc = Json::requireDocument(*array); + auto data = Json::ensureObject(Json::ensureObject(doc.object()), "data"); + // No way to find out if it's a mod or a modpack before here + // And also we need to check if it ends with .zip, instead of any better way + auto fileName = Json::ensureString(data, "fileName"); + + // Have to use ensureString then use QUrl to get proper url encoding + dl_url = QUrl(Json::ensureString(data, "downloadUrl", "", "downloadUrl")); + if (!dl_url.isValid()) { + CustomMessageBox::selectable( + this, tr("Error"), + tr("The modpack, mod, or resource %1 is blocked for third-parties! Please download it manually.").arg(fileName), + QMessageBox::Critical) + ->show(); + return; + } + + QFileInfo dl_file(dl_url.fileName()); + resource_name = Json::ensureString(data, "displayName", dl_file.completeBaseName(), "displayName"); + }); + + { // drop stack + ProgressDialog dlUrlDialod(this); + dlUrlDialod.setSkipButton(true, tr("Abort")); + dlUrlDialod.execWithTask(job.get()); + } + + } else { + dl_url = url; + } + + if (!dl_url.isValid()) { + continue; // no valid url to download this resource + } + + const QString path = dl_url.host() + '/' + dl_url.path(); + auto entry = APPLICATION->metacache()->resolveEntry("general", path); + entry->setStale(true); + auto dl_job = unique_qobject_ptr(new NetJob(tr("Modpack download"), APPLICATION->network())); + dl_job->addNetAction(Net::ApiDownload::makeCached(dl_url, entry)); + auto archivePath = entry->getFullPath(); + + bool dl_success = false; + connect(dl_job.get(), &Task::failed, this, + [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); + connect(dl_job.get(), &Task::succeeded, this, [&dl_success] { dl_success = true; }); + + { // drop stack + ProgressDialog dlUrlDialod(this); + dlUrlDialod.setSkipButton(true, tr("Abort")); + dlUrlDialod.execWithTask(dl_job.get()); + } + + if (!dl_success) { + continue; // no local file to identify + } + local_url = QUrl::fromLocalFile(archivePath); + + } else { + local_url = url; } - auto localFileName = QDir::toNativeSeparators(url.toLocalFile()) ; + auto localFileName = QDir::toNativeSeparators(local_url.toLocalFile()); QFileInfo localFileInfo(localFileName); auto type = ResourceUtils::identify(localFileInfo); if (ResourceUtils::ValidResourceTypes.count(type) == 0) { // probably instance/modpack - addInstance(localFileName); + addInstance(localFileName, extra_info); continue; } @@ -1133,8 +1137,7 @@ void MainWindow::on_actionChangeInstIcon_triggered() IconPickerDialog dlg(this); dlg.execWithSelection(m_selectedInstance->iconKey()); - if (dlg.result() == QDialog::Accepted) - { + if (dlg.result() == QDialog::Accepted) { m_selectedInstance->setIconKey(dlg.selectedIconKey); auto icon = APPLICATION->icons()->getIcon(dlg.selectedIconKey); ui->actionChangeInstIcon->setIcon(icon); @@ -1144,11 +1147,10 @@ void MainWindow::on_actionChangeInstIcon_triggered() void MainWindow::iconUpdated(QString icon) { - if (icon == m_currentInstIcon) - { - auto icon = APPLICATION->icons()->getIcon(m_currentInstIcon); - ui->actionChangeInstIcon->setIcon(icon); - changeIconButton->setIcon(icon); + if (icon == m_currentInstIcon) { + auto new_icon = APPLICATION->icons()->getIcon(m_currentInstIcon); + ui->actionChangeInstIcon->setIcon(new_icon); + changeIconButton->setIcon(new_icon); } } @@ -1160,13 +1162,12 @@ void MainWindow::updateInstanceToolIcon(QString new_icon) changeIconButton->setIcon(icon); } -void MainWindow::setSelectedInstanceById(const QString &id) +void MainWindow::setSelectedInstanceById(const QString& id) { if (id.isNull()) return; const QModelIndex index = APPLICATION->instances()->getInstanceIndexById(id); - if (index.isValid()) - { + if (index.isValid()) { QModelIndex selectionIndex = proxymodel->mapFromSource(index); view->selectionModel()->setCurrentIndex(selectionIndex, QItemSelectionModel::ClearAndSelect); updateStatusCenter(); @@ -1188,8 +1189,7 @@ void MainWindow::on_actionChangeInstGroup_triggered() name = QInputDialog::getItem(this, tr("Group name"), tr("Enter a new group name."), groups, foo, true, &ok); name = name.simplified(); - if (ok) - { + if (ok) { APPLICATION->instances()->setInstanceGroup(instId, name); } } @@ -1197,21 +1197,19 @@ void MainWindow::on_actionChangeInstGroup_triggered() void MainWindow::deleteGroup() { QObject* obj = sender(); - if(!obj) + if (!obj) return; - QAction *action = qobject_cast(obj); - if(!action) + QAction* action = qobject_cast(obj); + if (!action) return; auto map = action->data().toMap(); - if(!map.contains("group")) + if (!map.contains("group")) return; QString groupName = map["group"].toString(); - if(!groupName.isEmpty()) - { - auto reply = QMessageBox::question(this, tr("Delete group"), tr("Are you sure you want to delete the group %1?") - .arg(groupName), QMessageBox::Yes | QMessageBox::No); - if(reply == QMessageBox::Yes) - { + if (!groupName.isEmpty()) { + auto reply = QMessageBox::question(this, tr("Delete group"), tr("Are you sure you want to delete the group %1?").arg(groupName), + QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::Yes) { APPLICATION->instances()->deleteGroup(groupName); } } @@ -1223,36 +1221,47 @@ void MainWindow::undoTrashInstance() ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething()); } +void MainWindow::on_actionViewLauncherRootFolder_triggered() +{ + DesktopServices::openDirectory("."); +} + void MainWindow::on_actionViewInstanceFolder_triggered() { QString str = APPLICATION->settings()->get("InstanceDir").toString(); DesktopServices::openDirectory(str); } -void MainWindow::on_actionViewLauncherRootFolder_triggered() -{ - const QString dataPath = QDir::currentPath(); - DesktopServices::openDirectory(dataPath); -} - -void MainWindow::refreshInstances() -{ - APPLICATION->instances()->loadList(); -} - void MainWindow::on_actionViewCentralModsFolder_triggered() { DesktopServices::openDirectory(APPLICATION->settings()->get("CentralModsDir").toString(), true); } +void MainWindow::on_actionViewIconThemeFolder_triggered() +{ + DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path()); +} + +void MainWindow::on_actionViewWidgetThemeFolder_triggered() +{ + DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path()); +} + +void MainWindow::on_actionViewCatPackFolder_triggered() +{ + DesktopServices::openDirectory(APPLICATION->themeManager()->getCatPacksFolder().path()); +} + +void MainWindow::refreshInstances() +{ + APPLICATION->instances()->loadList(); +} + void MainWindow::checkForUpdates() { - if(BuildConfig.UPDATER_ENABLED) - { + if (BuildConfig.UPDATER_ENABLED) { APPLICATION->triggerUpdateCheck(); - } - else - { + } else { qWarning() << "Updater not set up. Cannot check for updates."; } } @@ -1280,16 +1289,16 @@ void MainWindow::globalSettingsClosed() void MainWindow::on_actionEditInstance_triggered() { - if (!m_selectedInstance) return; if (m_selectedInstance->canEdit()) { APPLICATION->showInstanceWindow(m_selectedInstance); - } else { - CustomMessageBox::selectable(this, tr("Instance not editable"), + } else { + CustomMessageBox::selectable(this, tr("Instance not editable"), tr("This instance is not editable. It may be broken, invalid, or too old. Check logs for details."), - QMessageBox::Critical)->show(); + QMessageBox::Critical) + ->show(); } } @@ -1352,7 +1361,8 @@ void MainWindow::newsButtonClicked() news_dialog.exec(); } -void MainWindow::onCatChanged(int) { +void MainWindow::onCatChanged(int) +{ setCatBackground(APPLICATION->settings()->get("TheCat").toBool()); } @@ -1383,14 +1393,15 @@ void MainWindow::on_actionDeleteInstance_triggered() auto linkedInstances = APPLICATION->instances()->getLinkedInstancesById(id); if (!linkedInstances.empty()) { - response = CustomMessageBox::selectable( - this, tr("There are linked instances"), - tr("The following instance(s) might reference files in this instance:\n\n" - "%1\n\n" - "Deleting it could break the other instance(s), \n\n" - "Do you wish to proceed?", nullptr, linkedInstances.count()).arg(linkedInstances.join("\n")), - QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No - )->exec(); + response = CustomMessageBox::selectable(this, tr("There are linked instances"), + tr("The following instance(s) might reference files in this instance:\n\n" + "%1\n\n" + "Deleting it could break the other instance(s), \n\n" + "Do you wish to proceed?", + nullptr, linkedInstances.count()) + .arg(linkedInstances.join("\n")), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); if (response != QMessageBox::Yes) return; } @@ -1405,8 +1416,7 @@ void MainWindow::on_actionDeleteInstance_triggered() void MainWindow::on_actionExportInstanceZip_triggered() { - if (m_selectedInstance) - { + if (m_selectedInstance) { ExportInstanceDialog dlg(m_selectedInstance, this); dlg.exec(); } @@ -1414,28 +1424,29 @@ void MainWindow::on_actionExportInstanceZip_triggered() void MainWindow::on_actionExportInstanceMrPack_triggered() { - if (m_selectedInstance) - { + if (m_selectedInstance) { ExportPackDialog dlg(m_selectedInstance, this); dlg.exec(); } } +void MainWindow::on_actionExportInstanceToModList_triggered() +{ + if (m_selectedInstance) { + ExportToModListDialog dlg(m_selectedInstance, this); + dlg.exec(); + } +} + void MainWindow::on_actionExportInstanceFlamePack_triggered() { if (m_selectedInstance) { auto instance = dynamic_cast(m_selectedInstance.get()); if (instance) { - QString errorMsg; - if (instance->getPackProfile()->getComponent("org.quiltmc.quilt-loader")) { - errorMsg = tr("Quilt is currently not supported by CurseForge modpacks."); - } else if (auto cmp = instance->getPackProfile()->getComponent("net.minecraft"); - cmp && cmp->getVersionFile() && cmp->getVersionFile()->type == "snapshot") { - errorMsg = tr("Snapshots are currently not supported by CurseForge modpacks."); - } - if (!errorMsg.isEmpty()) { - QMessageBox msgBox; - msgBox.setText(errorMsg); + if (auto cmp = instance->getPackProfile()->getComponent("net.minecraft"); + cmp && cmp->getVersionFile() && cmp->getVersionFile()->type == "snapshot") { + QMessageBox msgBox(this); + msgBox.setText("Snapshots are currently not supported by CurseForge modpacks."); msgBox.exec(); return; } @@ -1447,22 +1458,20 @@ void MainWindow::on_actionExportInstanceFlamePack_triggered() void MainWindow::on_actionRenameInstance_triggered() { - if (m_selectedInstance) - { + if (m_selectedInstance) { view->edit(view->currentIndex()); } } void MainWindow::on_actionViewSelectedInstFolder_triggered() { - if (m_selectedInstance) - { + if (m_selectedInstance) { QString str = m_selectedInstance->instanceRoot(); DesktopServices::openDirectory(QDir(str).absolutePath()); } } -void MainWindow::closeEvent(QCloseEvent *event) +void MainWindow::closeEvent(QCloseEvent* event) { // Save the window state and geometry. APPLICATION->settings()->set("MainWindowState", saveState().toBase64()); @@ -1474,8 +1483,7 @@ void MainWindow::closeEvent(QCloseEvent *event) void MainWindow::changeEvent(QEvent* event) { - if (event->type() == QEvent::LanguageChange) - { + if (event->type() == QEvent::LanguageChange) { retranslateUi(); } QMainWindow::changeEvent(event); @@ -1495,8 +1503,7 @@ void MainWindow::instanceActivated(QModelIndex index) void MainWindow::on_actionLaunchInstance_triggered() { - if(m_selectedInstance && !m_selectedInstance->isRunning()) - { + if (m_selectedInstance && !m_selectedInstance->isRunning()) { APPLICATION->launch(m_selectedInstance); } } @@ -1508,24 +1515,21 @@ void MainWindow::activateInstance(InstancePtr instance) void MainWindow::on_actionLaunchInstanceOffline_triggered() { - if (m_selectedInstance) - { + if (m_selectedInstance) { APPLICATION->launch(m_selectedInstance, false); } } void MainWindow::on_actionLaunchInstanceDemo_triggered() { - if (m_selectedInstance) - { + if (m_selectedInstance) { APPLICATION->launch(m_selectedInstance, false, true); } } void MainWindow::on_actionKillInstance_triggered() { - if(m_selectedInstance && m_selectedInstance->isRunning()) - { + if (m_selectedInstance && m_selectedInstance->isRunning()) { APPLICATION->kill(m_selectedInstance); } } @@ -1546,39 +1550,36 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() QString iconPath; QStringList args; #if defined(Q_OS_MACOS) - appPath = QApplication::applicationFilePath(); - if (appPath.startsWith("/private/var/")) { - QMessageBox::critical(this, tr("Create instance shortcut"), - tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts.")); - return; - } + appPath = QApplication::applicationFilePath(); + if (appPath.startsWith("/private/var/")) { + QMessageBox::critical(this, tr("Create instance shortcut"), + tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts.")); + return; + } - auto pIcon = APPLICATION->icons()->icon(m_selectedInstance->iconKey()); - if (pIcon == nullptr) - { - pIcon = APPLICATION->icons()->icon("grass"); - } + auto pIcon = APPLICATION->icons()->icon(m_selectedInstance->iconKey()); + if (pIcon == nullptr) { + pIcon = APPLICATION->icons()->icon("grass"); + } - iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "Icon.icns"); + iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "Icon.icns"); - QFile iconFile(iconPath); - if (!iconFile.open(QFile::WriteOnly)) - { - QMessageBox::critical(this, tr("Create instance Application"), tr("Failed to create icon for Application.")); - return; - } + QFile iconFile(iconPath); + if (!iconFile.open(QFile::WriteOnly)) { + QMessageBox::critical(this, tr("Create instance Application"), tr("Failed to create icon for Application.")); + return; + } - QIcon icon = pIcon->icon(); + QIcon icon = pIcon->icon(); - bool success = icon.pixmap(1024, 1024).save(iconPath, "ICNS"); - iconFile.close(); + bool success = icon.pixmap(1024, 1024).save(iconPath, "ICNS"); + iconFile.close(); - if (!success) - { - iconFile.remove(); - QMessageBox::critical(this, tr("Create instance Application"), tr("Failed to create icon for Application.")); - return; - } + if (!success) { + iconFile.remove(); + QMessageBox::critical(this, tr("Create instance Application"), tr("Failed to create icon for Application.")); + return; + } #elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) if (appPath.startsWith("/tmp/.mount_")) { // AppImage! @@ -1662,7 +1663,7 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() args.append({ "--launch", m_selectedInstance->id() }); if (FS::createShortcut(desktopFilePath, appPath, args, m_selectedInstance->name(), iconPath)) { #if not defined(Q_OS_MACOS) - QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!")); + QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!")); #else QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance!")); #endif @@ -1676,24 +1677,23 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() void MainWindow::taskEnd() { - QObject *sender = QObject::sender(); + QObject* sender = QObject::sender(); if (sender == m_versionLoadTask) m_versionLoadTask = NULL; sender->deleteLater(); } -void MainWindow::startTask(Task *task) +void MainWindow::startTask(Task* task) { connect(task, SIGNAL(succeeded()), SLOT(taskEnd())); connect(task, SIGNAL(failed(QString)), SLOT(taskEnd())); task->start(); } -void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex &previous) +void MainWindow::instanceChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { - if (!current.isValid()) - { + if (!current.isValid()) { APPLICATION->settings()->set("SelectedInstance", QString()); selectionBad(); return; @@ -1703,8 +1703,7 @@ void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex & } QString id = current.data(InstanceList::InstanceIDRole).toString(); m_selectedInstance = APPLICATION->instances()->getInstanceById(id); - if (m_selectedInstance) - { + if (m_selectedInstance) { ui->instanceToolBar->setEnabled(true); setInstanceActionsEnabled(true); ui->actionLaunchInstance->setEnabled(m_selectedInstance->canLaunch()); @@ -1714,7 +1713,7 @@ void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex & // Disable demo-mode if not available. auto instance = dynamic_cast(m_selectedInstance.get()); if (instance) { - ui->actionLaunchInstanceDemo->setEnabled(instance->supportsDemo()); + ui->actionLaunchInstanceDemo->setEnabled(instance->supportsDemo()); } ui->actionKillInstance->setEnabled(m_selectedInstance->isRunning()); @@ -1729,9 +1728,7 @@ void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex & APPLICATION->settings()->set("SelectedInstance", m_selectedInstance->id()); connect(m_selectedInstance.get(), &BaseInstance::runningStatusChanged, this, &MainWindow::refreshCurrentInstance); - } - else - { + } else { ui->instanceToolBar->setEnabled(false); setInstanceActionsEnabled(false); ui->actionLaunchInstance->setEnabled(false); @@ -1749,12 +1746,11 @@ void MainWindow::instanceSelectRequest(QString id) setSelectedInstanceById(id); } -void MainWindow::instanceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +void MainWindow::instanceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) { auto current = view->selectionModel()->currentIndex(); QItemSelection test(topLeft, bottomRight); - if (test.contains(current)) - { + if (test.contains(current)) { instanceChanged(current, current); } } @@ -1778,34 +1774,28 @@ void MainWindow::selectionBad() void MainWindow::checkInstancePathForProblems() { QString instanceFolder = APPLICATION->settings()->get("InstanceDir").toString(); - if (FS::checkProblemticPathJava(QDir(instanceFolder))) - { + if (FS::checkProblemticPathJava(QDir(instanceFolder))) { QMessageBox warning(this); warning.setText(tr("Your instance folder contains \'!\' and this is known to cause Java problems!")); - warning.setInformativeText( - tr( - "You have now two options:
    " - " - change the instance folder in the settings
    " - " - move this installation of %1 to a different folder" - ).arg(BuildConfig.LAUNCHER_DISPLAYNAME) - ); + warning.setInformativeText(tr("You have now two options:
    " + " - change the instance folder in the settings
    " + " - move this installation of %1 to a different folder") + .arg(BuildConfig.LAUNCHER_DISPLAYNAME)); warning.setDefaultButton(QMessageBox::Ok); warning.exec(); } - auto tempFolderText = tr("This is a problem:
    " - " - The launcher will likely be deleted without warning by the operating system
    " - " - close the launcher now and extract it to a real location, not a temporary folder"); + auto tempFolderText = + tr("This is a problem:
    " + " - The launcher will likely be deleted without warning by the operating system
    " + " - close the launcher now and extract it to a real location, not a temporary folder"); QString pathfoldername = QDir(instanceFolder).absolutePath(); - if (pathfoldername.contains("Rar$", Qt::CaseInsensitive)) - { + if (pathfoldername.contains("Rar$", Qt::CaseInsensitive)) { QMessageBox warning(this); warning.setText(tr("Your instance folder contains \'Rar$\' - that means you haven't extracted the launcher archive!")); warning.setInformativeText(tempFolderText); warning.setDefaultButton(QMessageBox::Ok); warning.exec(); - } - else if (pathfoldername.startsWith(QDir::tempPath()) || pathfoldername.contains("/TempState/")) - { + } else if (pathfoldername.startsWith(QDir::tempPath()) || pathfoldername.contains("/TempState/")) { QMessageBox warning(this); warning.setText(tr("Your instance folder is in a temporary folder: \'%1\'!").arg(QDir::tempPath())); warning.setInformativeText(tempFolderText); @@ -1836,7 +1826,7 @@ void MainWindow::setInstanceActionsEnabled(bool enabled) ui->actionCreateInstanceShortcut->setEnabled(enabled); } -void MainWindow::refreshCurrentInstance(bool running) +void MainWindow::refreshCurrentInstance([[maybe_unused]] bool running) { auto current = view->selectionModel()->currentIndex(); instanceChanged(current, current); diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index 5f74b501c..9fd72d25d 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -63,34 +63,32 @@ class KonamiCode; class InstanceTask; class LabeledToolButton; -namespace Ui -{ +namespace Ui { class MainWindow; } -class MainWindow : public QMainWindow -{ +class MainWindow : public QMainWindow { Q_OBJECT -public: - explicit MainWindow(QWidget *parent = 0); + public: + explicit MainWindow(QWidget* parent = 0); ~MainWindow(); - bool eventFilter(QObject *obj, QEvent *ev) override; - void closeEvent(QCloseEvent *event) override; - void changeEvent(QEvent * event) override; + bool eventFilter(QObject* obj, QEvent* ev) override; + void closeEvent(QCloseEvent* event) override; + void changeEvent(QEvent* event) override; void checkInstancePathForProblems(); void updatesAllowedChanged(bool allowed); void processURLs(QList urls); -signals: + signals: void isClosing(); -protected: - QMenu * createPopupMenu() override; + protected: + QMenu* createPopupMenu() override; -private slots: + private slots: void onCatToggled(bool); void onCatChanged(int); @@ -111,16 +109,19 @@ private slots: void on_actionChangeInstIcon_triggered(); - void on_actionViewInstanceFolder_triggered(); - void on_actionViewLauncherRootFolder_triggered(); + void on_actionViewInstanceFolder_triggered(); + void on_actionViewCentralModsFolder_triggered(); + + void on_actionViewIconThemeFolder_triggered(); + void on_actionViewWidgetThemeFolder_triggered(); + void on_actionViewCatPackFolder_triggered(); + void on_actionViewSelectedInstFolder_triggered(); void refreshInstances(); - void on_actionViewCentralModsFolder_triggered(); - void checkForUpdates(); void on_actionSettings_triggered(); @@ -131,9 +132,9 @@ private slots: void on_actionClearMetadata_triggered(); - #ifdef Q_OS_MAC +#ifdef Q_OS_MAC void on_actionAddToPATH_triggered(); - #endif +#endif void on_actionOpenWiki_triggered(); @@ -154,10 +155,14 @@ private slots: void deleteGroup(); void undoTrashInstance(); - inline void on_actionExportInstance_triggered() { on_actionExportInstanceZip_triggered(); } + inline void on_actionExportInstance_triggered() + { + on_actionExportInstanceZip_triggered(); + } void on_actionExportInstanceZip_triggered(); void on_actionExportInstanceMrPack_triggered(); void on_actionExportInstanceFlamePack_triggered(); + void on_actionExportInstanceToModList_triggered(); void on_actionRenameInstance_triggered(); @@ -172,7 +177,7 @@ private slots: */ void iconUpdated(QString); - void showInstanceContextMenu(const QPoint &); + void showInstanceContextMenu(const QPoint&); void updateMainToolBar(); @@ -182,15 +187,15 @@ private slots: void instanceActivated(QModelIndex); - void instanceChanged(const QModelIndex ¤t, const QModelIndex &previous); + void instanceChanged(const QModelIndex& current, const QModelIndex& previous); void instanceSelectRequest(QString id); - void instanceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void instanceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); void selectionBad(); - void startTask(Task *task); + void startTask(Task* task); void defaultAccountChanged(); @@ -200,7 +205,6 @@ private slots: void updateNewsLabel(); - void konamiTriggered(); void globalSettingsClosed(); @@ -208,38 +212,38 @@ private slots: void lockToolbars(bool); #ifndef Q_OS_MAC - void keyReleaseEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent* event) override; #endif void refreshCurrentInstance(bool running); -private: + private: void retranslateUi(); - void addInstance(QString url = QString()); + void addInstance(const QString& url = QString(), const QMap& extra_info = {}); void activateInstance(InstancePtr instance); void setCatBackground(bool enabled); void updateInstanceToolIcon(QString new_icon); - void setSelectedInstanceById(const QString &id); + void setSelectedInstanceById(const QString& id); void updateStatusCenter(); void setInstanceActionsEnabled(bool enabled); - void runModalTask(Task *task); - void instanceFromInstanceTask(InstanceTask *task); + void runModalTask(Task* task); + void instanceFromInstanceTask(InstanceTask* task); void finalizeInstance(InstancePtr inst); -private: - Ui::MainWindow *ui; + private: + Ui::MainWindow* ui; // these are managed by Qt's memory management model! - InstanceView *view = nullptr; - InstanceProxyModel *proxymodel = nullptr; - QToolButton *newsLabel = nullptr; - QLabel *m_statusLeft = nullptr; - QLabel *m_statusCenter = nullptr; - LabeledToolButton *changeIconButton = nullptr; - LabeledToolButton *renameButton = nullptr; - QToolButton *helpMenuButton = nullptr; - KonamiCode * secretEventFilter = nullptr; + InstanceView* view = nullptr; + InstanceProxyModel* proxymodel = nullptr; + QToolButton* newsLabel = nullptr; + QLabel* m_statusLeft = nullptr; + QLabel* m_statusCenter = nullptr; + LabeledToolButton* changeIconButton = nullptr; + LabeledToolButton* renameButton = nullptr; + QToolButton* helpMenuButton = nullptr; + KonamiCode* secretEventFilter = nullptr; std::shared_ptr instanceToolbarSetting = nullptr; @@ -249,5 +253,5 @@ private: QString m_currentInstIcon; // managed by the application object - Task *m_versionLoadTask = nullptr; + Task* m_versionLoadTask = nullptr; }; diff --git a/launcher/ui/MainWindow.ui b/launcher/ui/MainWindow.ui index 190219b9b..6ef32099f 100644 --- a/launcher/ui/MainWindow.ui +++ b/launcher/ui/MainWindow.ui @@ -186,9 +186,14 @@ true - + + + + + + @@ -465,7 +470,8 @@ - + + .. Prism Launcher (zip) @@ -473,7 +479,8 @@ - + + .. Modrinth (mrpack) @@ -481,12 +488,22 @@ - + + .. CurseForge (zip) + + + + .. + + + Mod List + + @@ -544,7 +561,7 @@ .. - &View Instance Folder + View &Instance Folder Open the instance folder in a file browser. @@ -556,7 +573,7 @@ .. - &View Launcher Root Folder + View Launcher &Root Folder Open the launcher's root folder in a file browser. @@ -711,6 +728,38 @@ Open the %1 wiki + + + + .. + + + View &Widget Themes Folder + + + View Widget Theme Folder + + + + + + .. + + + View I&con Theme Folder + + + View Icon Theme Folder + + + + + + + + View Cat Packs Folder + + diff --git a/launcher/ui/dialogs/AboutDialog.cpp b/launcher/ui/dialogs/AboutDialog.cpp index 88739463f..3c6f6ef16 100644 --- a/launcher/ui/dialogs/AboutDialog.cpp +++ b/launcher/ui/dialogs/AboutDialog.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -34,26 +34,28 @@ */ #include "AboutDialog.h" -#include "BuildConfig.h" -#include "ui_AboutDialog.h" #include #include "Application.h" #include "BuildConfig.h" #include "Markdown.h" +#include "ui_AboutDialog.h" #include #include namespace { -QString getLink(QString link, QString name) { +QString getLink(QString link, QString name) +{ return QString("<%2>").arg(link).arg(name); } -QString getWebsite(QString link) { +QString getWebsite(QString link) +{ return getLink(link, QObject::tr("Website")); } -QString getGitHub(QString username) { +QString getGitHub(QString username) +{ return getLink("https://github.com/" + username, "GitHub"); } @@ -70,19 +72,19 @@ QString getCreditsHtml() //: %1 is the name of the launcher, determined at build time, e.g. "Prism Launcher Developers" stream << "

    " << QObject::tr("%1 Developers", "About Credits").arg(BuildConfig.LAUNCHER_DISPLAYNAME) << "

    \n"; - stream << QString("

    Sefa Eyeoglu (Scrumplex) %1

    \n") .arg(getWebsite("https://scrumplex.net")); - stream << QString("

    d-513 %1

    \n") .arg(getGitHub("d-513")); - stream << QString("

    txtsd %1

    \n") .arg(getWebsite("https://ihavea.quest")); - stream << QString("

    timoreo %1

    \n") .arg(getGitHub("timoreo22")); - stream << QString("

    Ezekiel Smith (ZekeSmith) %1

    \n") .arg(getGitHub("ZekeSmith")); - stream << QString("

    cozyGalvinism %1

    \n") .arg(getGitHub("cozyGalvinism")); - stream << QString("

    DioEgizio %1

    \n") .arg(getGitHub("DioEgizio")); - stream << QString("

    flowln %1

    \n") .arg(getGitHub("flowln")); - stream << QString("

    ViRb3 %1

    \n") .arg(getGitHub("ViRb3")); - stream << QString("

    Rachel Powers (Ryex) %1

    \n") .arg(getGitHub("Ryex")); - stream << QString("

    TayouVR %1

    \n") .arg(getGitHub("TayouVR")); - stream << QString("

    TheKodeToad %1

    \n") .arg(getGitHub("TheKodeToad")); - stream << QString("

    getchoo %1

    \n") .arg(getGitHub("getchoo")); + stream << QString("

    Sefa Eyeoglu (Scrumplex) %1

    \n").arg(getWebsite("https://scrumplex.net")); + stream << QString("

    d-513 %1

    \n").arg(getGitHub("d-513")); + stream << QString("

    txtsd %1

    \n").arg(getWebsite("https://ihavea.quest")); + stream << QString("

    timoreo %1

    \n").arg(getGitHub("timoreo22")); + stream << QString("

    Ezekiel Smith (ZekeSmith) %1

    \n").arg(getGitHub("ZekeSmith")); + stream << QString("

    cozyGalvinism %1

    \n").arg(getGitHub("cozyGalvinism")); + stream << QString("

    DioEgizio %1

    \n").arg(getGitHub("DioEgizio")); + stream << QString("

    flowln %1

    \n").arg(getGitHub("flowln")); + stream << QString("

    ViRb3 %1

    \n").arg(getGitHub("ViRb3")); + stream << QString("

    Rachel Powers (Ryex) %1

    \n").arg(getGitHub("Ryex")); + stream << QString("

    TayouVR %1

    \n").arg(getGitHub("TayouVR")); + stream << QString("

    TheKodeToad %1

    \n").arg(getGitHub("TheKodeToad")); + stream << QString("

    getchoo %1

    \n").arg(getGitHub("getchoo")); stream << "
    \n"; // TODO: possibly retrieve from git history at build time? @@ -96,20 +98,21 @@ QString getCreditsHtml() stream << "
    \n"; stream << "

    " << QObject::tr("With thanks to", "About Credits") << "

    \n"; - stream << QString("

    Boba %1

    \n") .arg(getWebsite("https://bobaonline.neocities.org/")); - stream << QString("

    Davi Rafael %1

    \n") .arg(getWebsite("https://auti.one/")); - stream << QString("

    Fulmine %1

    \n") .arg(getWebsite("https://www.fulmine.xyz/")); - stream << QString("

    ely %1

    \n") .arg(getGitHub("elyrodso")); - stream << QString("

    gon sawa %1

    \n") .arg(getGitHub("gonsawa")); + stream << QString("

    Boba %1

    \n").arg(getWebsite("https://bobaonline.neocities.org/")); + stream << QString("

    Davi Rafael %1

    \n").arg(getWebsite("https://auti.one/")); + stream << QString("

    Fulmine %1

    \n").arg(getWebsite("https://www.fulmine.xyz/")); + stream << QString("

    ely %1

    \n").arg(getGitHub("elyrodso")); + stream << QString("

    gon sawa %1

    \n").arg(getGitHub("gonsawa")); stream << QString("

    Pankakes

    \n"); - stream << QString("

    tobimori %1

    \n") .arg(getGitHub("tobimori")); + stream << QString("

    tobimori %1

    \n").arg(getGitHub("tobimori")); stream << "

    Orochimarufan <orochimarufan.x3@gmail.com>

    \n"; stream << "

    TakSuyu <taksuyu@gmail.com>

    \n"; stream << "

    Kilobyte <stiepen22@gmx.de>

    \n"; stream << "

    Rootbear75 <@rootbear75>

    \n"; stream << "

    Zeker Zhayard <@Zeker_Zhayard>

    \n"; stream << "

    Everyone who helped establish our branding!

    \n"; - stream << "

    And everyone else who contributed!

    \n"; + stream + << "

    And everyone else who contributed!

    \n"; stream << "
    \n"; stream << "\n"; @@ -124,9 +127,9 @@ QString getLicenseHtml() return output; } -} +} // namespace -AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDialog) +AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDialog) { ui->setupUi(this); @@ -148,7 +151,7 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia ui->versionLabel->setText(BuildConfig.printableVersionString()); if (!BuildConfig.BUILD_PLATFORM.isEmpty()) - ui->platformLabel->setText(tr("Platform") +": " + BuildConfig.BUILD_PLATFORM); + ui->platformLabel->setText(tr("Platform") + ": " + BuildConfig.BUILD_PLATFORM); else ui->platformLabel->setVisible(false); @@ -163,14 +166,14 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia ui->buildDateLabel->setVisible(false); if (!BuildConfig.VERSION_CHANNEL.isEmpty()) - ui->channelLabel->setText(tr("Channel") +": " + BuildConfig.VERSION_CHANNEL); + ui->channelLabel->setText(tr("Channel") + ": " + BuildConfig.VERSION_CHANNEL); else ui->channelLabel->setVisible(false); QString urlText("

    %1

    "); ui->urlLabel->setText(urlText.arg(BuildConfig.LAUNCHER_GIT)); - QString copyText("© 2022 %1"); + QString copyText("© 2022-2023 %1"); ui->copyLabel->setText(copyText.arg(BuildConfig.LAUNCHER_COPYRIGHT)); connect(ui->closeButton, SIGNAL(clicked()), SLOT(close())); diff --git a/launcher/ui/dialogs/AboutDialog.h b/launcher/ui/dialogs/AboutDialog.h index 814fd98c6..356f005e0 100644 --- a/launcher/ui/dialogs/AboutDialog.h +++ b/launcher/ui/dialogs/AboutDialog.h @@ -15,26 +15,23 @@ #pragma once -#include #include +#include -namespace Ui -{ +namespace Ui { class AboutDialog; } -class AboutDialog : public QDialog -{ +class AboutDialog : public QDialog { Q_OBJECT -public: - explicit AboutDialog(QWidget *parent = 0); + public: + explicit AboutDialog(QWidget* parent = 0); ~AboutDialog(); -private: - Ui::AboutDialog *ui; + private: + Ui::AboutDialog* ui; NetJob::Ptr netJob; QByteArray dataSink; }; - diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp index fdfae5973..727c06148 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.cpp +++ b/launcher/ui/dialogs/BlockedModsDialog.cpp @@ -313,7 +313,7 @@ bool BlockedModsDialog::checkValidPath(QString path) // efectivly compare two strings ignoring all separators and case auto laxCompare = [](QString fsfilename, QString metadataFilename) { // allowed character seperators - QList allowedSeperators = { '-', '+', '.' , '_'}; + QList allowedSeperators = { '-', '+', '.', '_' }; // copy in lowercase auto fsName = fsfilename.toLower(); diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp index d75bb5fe3..8b2f5bb80 100644 --- a/launcher/ui/dialogs/CopyInstanceDialog.cpp +++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -107,8 +107,8 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget* parent) #if defined(Q_OS_WIN) ui->symbolicLinksCheckbox->setIcon(style()->standardIcon(QStyle::SP_VistaShield)); - ui->symbolicLinksCheckbox->setToolTip(tr("Use symbolic links instead of copying files.") + - "\n" + tr("On Windows, symbolic links may require admin permission to create.")); + ui->symbolicLinksCheckbox->setToolTip(tr("Use symbolic links instead of copying files.") + "\n" + + tr("On Windows, symbolic links may require admin permission to create.")); #endif updateLinkOptions(); @@ -220,7 +220,7 @@ void CopyInstanceDialog::on_iconButton_clicked() } } -void CopyInstanceDialog::on_instNameTextBox_textChanged(const QString& arg1) +void CopyInstanceDialog::on_instNameTextBox_textChanged([[maybe_unused]] const QString& arg1) { updateDialogState(); } diff --git a/launcher/ui/dialogs/CustomMessageBox.cpp b/launcher/ui/dialogs/CustomMessageBox.cpp index 19029f682..1af47a449 100644 --- a/launcher/ui/dialogs/CustomMessageBox.cpp +++ b/launcher/ui/dialogs/CustomMessageBox.cpp @@ -15,13 +15,15 @@ #include "CustomMessageBox.h" -namespace CustomMessageBox -{ -QMessageBox *selectable(QWidget *parent, const QString &title, const QString &text, - QMessageBox::Icon icon, QMessageBox::StandardButtons buttons, +namespace CustomMessageBox { +QMessageBox* selectable(QWidget* parent, + const QString& title, + const QString& text, + QMessageBox::Icon icon, + QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { - QMessageBox *messageBox = new QMessageBox(parent); + QMessageBox* messageBox = new QMessageBox(parent); messageBox->setWindowTitle(title); messageBox->setText(text); messageBox->setStandardButtons(buttons); @@ -32,4 +34,4 @@ QMessageBox *selectable(QWidget *parent, const QString &title, const QString &te return messageBox; } -} +} // namespace CustomMessageBox diff --git a/launcher/ui/dialogs/CustomMessageBox.h b/launcher/ui/dialogs/CustomMessageBox.h index 712c65186..a9bc6a24a 100644 --- a/launcher/ui/dialogs/CustomMessageBox.h +++ b/launcher/ui/dialogs/CustomMessageBox.h @@ -17,9 +17,10 @@ #include -namespace CustomMessageBox -{ -QMessageBox *selectable(QWidget *parent, const QString &title, const QString &text, +namespace CustomMessageBox { +QMessageBox* selectable(QWidget* parent, + const QString& title, + const QString& text, QMessageBox::Icon icon = QMessageBox::NoIcon, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); diff --git a/launcher/ui/dialogs/EditAccountDialog.cpp b/launcher/ui/dialogs/EditAccountDialog.cpp index 002c064b4..58036fd82 100644 --- a/launcher/ui/dialogs/EditAccountDialog.cpp +++ b/launcher/ui/dialogs/EditAccountDialog.cpp @@ -14,12 +14,11 @@ */ #include "EditAccountDialog.h" -#include "ui_EditAccountDialog.h" #include #include +#include "ui_EditAccountDialog.h" -EditAccountDialog::EditAccountDialog(const QString &text, QWidget *parent, int flags) - : QDialog(parent), ui(new Ui::EditAccountDialog) +EditAccountDialog::EditAccountDialog(const QString& text, QWidget* parent, int flags) : QDialog(parent), ui(new Ui::EditAccountDialog) { ui->setupUi(this); @@ -35,12 +34,12 @@ EditAccountDialog::~EditAccountDialog() delete ui; } -void EditAccountDialog::on_label_linkActivated(const QString &link) +void EditAccountDialog::on_label_linkActivated(const QString& link) { DesktopServices::openUrl(QUrl(link)); } -void EditAccountDialog::setUsername(const QString & user) const +void EditAccountDialog::setUsername(const QString& user) const { ui->userTextBox->setText(user); } @@ -50,7 +49,7 @@ QString EditAccountDialog::username() const return ui->userTextBox->text(); } -void EditAccountDialog::setPassword(const QString & pass) const +void EditAccountDialog::setPassword(const QString& pass) const { ui->passTextBox->setText(pass); } diff --git a/launcher/ui/dialogs/EditAccountDialog.h b/launcher/ui/dialogs/EditAccountDialog.h index 6b5eb4aa9..7a9ccba79 100644 --- a/launcher/ui/dialogs/EditAccountDialog.h +++ b/launcher/ui/dialogs/EditAccountDialog.h @@ -17,28 +17,24 @@ #include -namespace Ui -{ +namespace Ui { class EditAccountDialog; } -class EditAccountDialog : public QDialog -{ +class EditAccountDialog : public QDialog { Q_OBJECT -public: - explicit EditAccountDialog(const QString &text = "", QWidget *parent = 0, - int flags = UsernameField | PasswordField); + public: + explicit EditAccountDialog(const QString& text = "", QWidget* parent = 0, int flags = UsernameField | PasswordField); ~EditAccountDialog(); - void setUsername(const QString & user) const; - void setPassword(const QString & pass) const; + void setUsername(const QString& user) const; + void setPassword(const QString& pass) const; QString username() const; QString password() const; - enum Flags - { + enum Flags { NoFlags = 0, //! Specifies that the dialog should have a username field. @@ -48,9 +44,9 @@ public: PasswordField, }; -private slots: - void on_label_linkActivated(const QString &link); + private slots: + void on_label_linkActivated(const QString& link); -private: - Ui::EditAccountDialog *ui; + private: + Ui::EditAccountDialog* ui; }; diff --git a/launcher/ui/dialogs/ExportInstanceDialog.cpp b/launcher/ui/dialogs/ExportInstanceDialog.cpp index cc41c394d..703736d68 100644 --- a/launcher/ui/dialogs/ExportInstanceDialog.cpp +++ b/launcher/ui/dialogs/ExportInstanceDialog.cpp @@ -3,6 +3,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad + * Copyright (c) 2023 Trial97 * * 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 @@ -41,6 +42,9 @@ #include #include #include "FileIgnoreProxy.h" +#include "QObjectPtr.h" +#include "ui/dialogs/CustomMessageBox.h" +#include "ui/dialogs/ProgressDialog.h" #include "ui_ExportInstanceDialog.h" #include @@ -66,13 +70,15 @@ ExportInstanceDialog::ExportInstanceDialog(InstancePtr instance, QWidget* parent auto prefix = QDir(instance->instanceRoot()).relativeFilePath(instance->gameRoot()); proxyModel->ignoreFilesWithPath().insert({ FS::PathCombine(prefix, "logs"), FS::PathCombine(prefix, "crash-reports") }); proxyModel->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" }); + proxyModel->ignoreFilesWithPath().insert( + { FS::PathCombine(prefix, ".cache"), FS::PathCombine(prefix, ".fabric"), FS::PathCombine(prefix, ".quilt") }); loadPackIgnore(); ui->treeView->setModel(proxyModel); ui->treeView->setRootIndex(proxyModel->mapFromSource(model->index(root))); ui->treeView->sortByColumn(0, Qt::AscendingOrder); - connect(proxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(rowsInserted(QModelIndex,int,int))); + connect(proxyModel, SIGNAL(rowsInserted(QModelIndex, int, int)), SLOT(rowsInserted(QModelIndex, int, int))); model->setFilter(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Hidden); model->setRootPath(root); @@ -92,32 +98,26 @@ void SaveIcon(InstancePtr m_instance) auto iconKey = m_instance->iconKey(); auto iconList = APPLICATION->icons(); auto mmcIcon = iconList->icon(iconKey); - if(!mmcIcon || mmcIcon->isBuiltIn()) { + if (!mmcIcon || mmcIcon->isBuiltIn()) { return; } auto path = mmcIcon->getFilePath(); - if(!path.isNull()) { - QFileInfo inInfo (path); - FS::copy(path, FS::PathCombine(m_instance->instanceRoot(), inInfo.fileName())) (); + if (!path.isNull()) { + QFileInfo inInfo(path); + FS::copy(path, FS::PathCombine(m_instance->instanceRoot(), inInfo.fileName()))(); return; } - auto & image = mmcIcon->m_images[mmcIcon->type()]; - auto & icon = image.icon; + auto& image = mmcIcon->m_images[mmcIcon->type()]; + auto& icon = image.icon; auto sizes = icon.availableSizes(); - if(sizes.size() == 0) - { + if (sizes.size() == 0) { return; } - auto areaOf = [](QSize size) - { - return size.width() * size.height(); - }; + auto areaOf = [](QSize size) { return size.width() * size.height(); }; QSize largest = sizes[0]; // find variant with largest area - for(auto size: sizes) - { - if(areaOf(largest) < areaOf(size)) - { + for (auto size : sizes) { + if (areaOf(largest) < areaOf(size)) { largest = size; } } @@ -125,16 +125,15 @@ void SaveIcon(InstancePtr m_instance) pixmap.save(FS::PathCombine(m_instance->instanceRoot(), iconKey + ".png")); } -bool ExportInstanceDialog::doExport() +void ExportInstanceDialog::doExport() { auto name = FS::RemoveInvalidFilenameChars(m_instance->name()); - const QString output = QFileDialog::getSaveFileName( - this, tr("Export %1").arg(m_instance->name()), - FS::PathCombine(QDir::homePath(), name + ".zip"), "Zip (*.zip)", nullptr); - if (output.isEmpty()) - { - return false; + const QString output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(m_instance->name()), + FS::PathCombine(QDir::homePath(), name + ".zip"), "Zip (*.zip)", nullptr); + if (output.isEmpty()) { + QDialog::done(QDialog::Rejected); + return; } SaveIcon(m_instance); @@ -143,46 +142,40 @@ bool ExportInstanceDialog::doExport() if (!MMCZip::collectFileListRecursively(m_instance->instanceRoot(), nullptr, &files, std::bind(&FileIgnoreProxy::filterFile, proxyModel, std::placeholders::_1))) { QMessageBox::warning(this, tr("Error"), tr("Unable to export instance")); - return false; + QDialog::done(QDialog::Rejected); + return; } - if (!MMCZip::compressDirFiles(output, m_instance->instanceRoot(), files, true)) - { - QMessageBox::warning(this, tr("Error"), tr("Unable to export instance")); - return false; - } - return true; + auto task = makeShared(output, m_instance->instanceRoot(), files, "", true); + + connect(task.get(), &Task::failed, this, + [this, output](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); + connect(task.get(), &Task::finished, this, [task] { task->deleteLater(); }); + + ProgressDialog progress(this); + progress.setSkipButton(true, tr("Abort")); + auto result = progress.execWithTask(task.get()); + QDialog::done(result); } void ExportInstanceDialog::done(int result) { savePackIgnore(); - if (result == QDialog::Accepted) - { - if (doExport()) - { - QDialog::done(QDialog::Accepted); - return; - } - else - { - return; - } + if (result == QDialog::Accepted) { + doExport(); + return; } QDialog::done(result); } void ExportInstanceDialog::rowsInserted(QModelIndex parent, int top, int bottom) { - //WARNING: possible off-by-one? - for(int i = top; i < bottom; i++) - { + // WARNING: possible off-by-one? + for (int i = top; i < bottom; i++) { auto node = proxyModel->index(i, 0, parent); - if(proxyModel->shouldExpand(node)) - { + if (proxyModel->shouldExpand(node)) { auto expNode = node.parent(); - if(!expNode.isValid()) - { + if (!expNode.isValid()) { continue; } ui->treeView->expand(node); @@ -199,12 +192,11 @@ void ExportInstanceDialog::loadPackIgnore() { auto filename = ignoreFileName(); QFile ignoreFile(filename); - if(!ignoreFile.open(QIODevice::ReadOnly)) - { + if (!ignoreFile.open(QIODevice::ReadOnly)) { return; } - auto data = ignoreFile.readAll(); - auto string = QString::fromUtf8(data); + auto ignoreData = ignoreFile.readAll(); + auto string = QString::fromUtf8(ignoreData); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) proxyModel->setBlockedPaths(string.split('\n', Qt::SkipEmptyParts)); #else @@ -214,14 +206,11 @@ void ExportInstanceDialog::loadPackIgnore() void ExportInstanceDialog::savePackIgnore() { - auto data = proxyModel->blockedPaths().toStringList().join('\n').toUtf8(); + auto ignoreData = proxyModel->blockedPaths().toStringList().join('\n').toUtf8(); auto filename = ignoreFileName(); - try - { - FS::write(filename, data); - } - catch (const Exception &e) - { + try { + FS::write(filename, ignoreData); + } catch (const Exception& e) { qWarning() << e.cause(); } } diff --git a/launcher/ui/dialogs/ExportInstanceDialog.h b/launcher/ui/dialogs/ExportInstanceDialog.h index 5e8018751..02f38f63d 100644 --- a/launcher/ui/dialogs/ExportInstanceDialog.h +++ b/launcher/ui/dialogs/ExportInstanceDialog.h @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 TheKodeToad + * Copyright (c) 2023 Trial97 * * 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 @@ -38,39 +39,37 @@ #include #include #include -#include "FileIgnoreProxy.h" #include "FastFileIconProvider.h" +#include "FileIgnoreProxy.h" class BaseInstance; typedef std::shared_ptr InstancePtr; -namespace Ui -{ +namespace Ui { class ExportInstanceDialog; } -class ExportInstanceDialog : public QDialog -{ +class ExportInstanceDialog : public QDialog { Q_OBJECT -public: - explicit ExportInstanceDialog(InstancePtr instance, QWidget *parent = 0); + public: + explicit ExportInstanceDialog(InstancePtr instance, QWidget* parent = 0); ~ExportInstanceDialog(); virtual void done(int result); -private: - bool doExport(); + private: + void doExport(); void loadPackIgnore(); void savePackIgnore(); QString ignoreFileName(); -private: - Ui::ExportInstanceDialog *ui; + private: + Ui::ExportInstanceDialog* ui; InstancePtr m_instance; - FileIgnoreProxy * proxyModel; + FileIgnoreProxy* proxyModel; FastFileIconProvider icons; -private slots: + private slots: void rowsInserted(QModelIndex parent, int top, int bottom); }; diff --git a/launcher/ui/dialogs/ExportPackDialog.cpp b/launcher/ui/dialogs/ExportPackDialog.cpp index 2abe2805b..ad8db5ffb 100644 --- a/launcher/ui/dialogs/ExportPackDialog.cpp +++ b/launcher/ui/dialogs/ExportPackDialog.cpp @@ -61,7 +61,7 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPla // use the game root - everything outside cannot be exported const QDir root(instance->gameRoot()); proxy = new FileIgnoreProxy(instance->gameRoot(), this); - proxy->ignoreFilesWithPath().insert({ "logs", "crash-reports" }); + proxy->ignoreFilesWithPath().insert({ "logs", "crash-reports", ".cache", ".fabric", ".quilt" }); proxy->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" }); proxy->setSourceModel(model); diff --git a/launcher/ui/dialogs/ExportToModListDialog.cpp b/launcher/ui/dialogs/ExportToModListDialog.cpp new file mode 100644 index 000000000..c811bfe6a --- /dev/null +++ b/launcher/ui/dialogs/ExportToModListDialog.cpp @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ExportToModListDialog.h" +#include +#include +#include +#include "FileSystem.h" +#include "Markdown.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/mod/ModFolderModel.h" +#include "modplatform/helpers/ExportToModList.h" +#include "ui_ExportToModListDialog.h" + +#include +#include +#include +#include +#include + +const QHash ExportToModListDialog::exampleLines = { + { ExportToModList::HTML, "
  • {name} [{version}] by {authors}
  • " }, + { ExportToModList::MARKDOWN, "[{name}]({url}) [{version}] by {authors}" }, + { ExportToModList::PLAINTXT, "{name} ({url}) [{version}] by {authors}" }, + { ExportToModList::JSON, "{\"name\":\"{name}\",\"url\":\"{url}\",\"version\":\"{version}\",\"authors\":\"{authors}\"}," }, + { ExportToModList::CSV, "{name},{url},{version},\"{authors}\"" }, +}; + +ExportToModListDialog::ExportToModListDialog(InstancePtr instance, QWidget* parent) + : QDialog(parent), m_template_changed(false), name(instance->name()), ui(new Ui::ExportToModListDialog) +{ + ui->setupUi(this); + enableCustom(false); + + MinecraftInstance* mcInstance = dynamic_cast(instance.get()); + if (mcInstance) { + mcInstance->loaderModList()->update(); + connect(mcInstance->loaderModList().get(), &ModFolderModel::updateFinished, this, [this, mcInstance]() { + m_allMods = mcInstance->loaderModList()->allMods(); + triggerImp(); + }); + } + + connect(ui->formatComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ExportToModListDialog::formatChanged); + connect(ui->authorsCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger); + connect(ui->versionCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger); + connect(ui->urlCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger); + connect(ui->authorsButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Authors); }); + connect(ui->versionButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Version); }); + connect(ui->urlButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Url); }); + connect(ui->templateText, &QTextEdit::textChanged, this, [this] { + if (ui->templateText->toPlainText() != exampleLines[format]) + ui->formatComboBox->setCurrentIndex(5); + else + triggerImp(); + }); + connect(ui->copyButton, &QPushButton::clicked, this, [this](bool) { + this->ui->finalText->selectAll(); + this->ui->finalText->copy(); + }); +} + +ExportToModListDialog::~ExportToModListDialog() +{ + delete ui; +} + +void ExportToModListDialog::formatChanged(int index) +{ + switch (index) { + case 0: { + enableCustom(false); + ui->resultText->show(); + format = ExportToModList::HTML; + break; + } + case 1: { + enableCustom(false); + ui->resultText->show(); + format = ExportToModList::MARKDOWN; + break; + } + case 2: { + enableCustom(false); + ui->resultText->hide(); + format = ExportToModList::PLAINTXT; + break; + } + case 3: { + enableCustom(false); + ui->resultText->hide(); + format = ExportToModList::JSON; + break; + } + case 4: { + enableCustom(false); + ui->resultText->hide(); + format = ExportToModList::CSV; + break; + } + case 5: { + m_template_changed = true; + enableCustom(true); + ui->resultText->hide(); + format = ExportToModList::CUSTOM; + break; + } + } + triggerImp(); +} + +void ExportToModListDialog::triggerImp() +{ + if (format == ExportToModList::CUSTOM) { + ui->finalText->setPlainText(ExportToModList::exportToModList(m_allMods, ui->templateText->toPlainText())); + return; + } + auto opt = 0; + if (ui->authorsCheckBox->isChecked()) + opt |= ExportToModList::Authors; + if (ui->versionCheckBox->isChecked()) + opt |= ExportToModList::Version; + if (ui->urlCheckBox->isChecked()) + opt |= ExportToModList::Url; + auto txt = ExportToModList::exportToModList(m_allMods, format, static_cast(opt)); + ui->finalText->setPlainText(txt); + switch (format) { + case ExportToModList::CUSTOM: + return; + case ExportToModList::HTML: + ui->resultText->setHtml(txt); + break; + case ExportToModList::MARKDOWN: + ui->resultText->setHtml(markdownToHTML(txt)); + break; + case ExportToModList::PLAINTXT: + break; + case ExportToModList::JSON: + break; + case ExportToModList::CSV: + break; + } + auto exampleLine = exampleLines[format]; + if (!m_template_changed && ui->templateText->toPlainText() != exampleLine) + ui->templateText->setPlainText(exampleLine); +} + +void ExportToModListDialog::done(int result) +{ + if (result == Accepted) { + const QString filename = FS::RemoveInvalidFilenameChars(name); + const QString output = + QFileDialog::getSaveFileName(this, tr("Export %1").arg(name), FS::PathCombine(QDir::homePath(), filename + extension()), + "File (*.txt *.html *.md *.json *.csv)", nullptr); + + if (output.isEmpty()) + return; + FS::write(output, ui->finalText->toPlainText().toUtf8()); + } + + QDialog::done(result); +} + +QString ExportToModListDialog::extension() +{ + switch (format) { + case ExportToModList::HTML: + return ".html"; + case ExportToModList::MARKDOWN: + return ".md"; + case ExportToModList::PLAINTXT: + return ".txt"; + case ExportToModList::CUSTOM: + return ".txt"; + case ExportToModList::JSON: + return ".json"; + case ExportToModList::CSV: + return ".csv"; + } + return ".txt"; +} + +void ExportToModListDialog::addExtra(ExportToModList::OptionalData option) +{ + if (format != ExportToModList::CUSTOM) + return; + switch (option) { + case ExportToModList::Authors: + ui->templateText->insertPlainText("{authors}"); + break; + case ExportToModList::Url: + ui->templateText->insertPlainText("{url}"); + break; + case ExportToModList::Version: + ui->templateText->insertPlainText("{version}"); + break; + } +} +void ExportToModListDialog::enableCustom(bool enabled) +{ + ui->authorsCheckBox->setHidden(enabled); + ui->versionCheckBox->setHidden(enabled); + ui->urlCheckBox->setHidden(enabled); + + ui->authorsButton->setHidden(!enabled); + ui->versionButton->setHidden(!enabled); + ui->urlButton->setHidden(!enabled); +} diff --git a/launcher/ui/dialogs/ExportToModListDialog.h b/launcher/ui/dialogs/ExportToModListDialog.h new file mode 100644 index 000000000..9886ae5a0 --- /dev/null +++ b/launcher/ui/dialogs/ExportToModListDialog.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include "BaseInstance.h" +#include "minecraft/mod/Mod.h" +#include "modplatform/helpers/ExportToModList.h" + +namespace Ui { +class ExportToModListDialog; +} + +class ExportToModListDialog : public QDialog { + Q_OBJECT + + public: + explicit ExportToModListDialog(InstancePtr instance, QWidget* parent = nullptr); + ~ExportToModListDialog(); + + void done(int result) override; + + protected slots: + void formatChanged(int index); + void triggerImp(); + void trigger(int) { triggerImp(); }; + void addExtra(ExportToModList::OptionalData option); + + private: + QString extension(); + void enableCustom(bool enabled); + QList m_allMods; + bool m_template_changed; + QString name; + ExportToModList::Formats format = ExportToModList::Formats::HTML; + Ui::ExportToModListDialog* ui; + static const QHash exampleLines; +}; diff --git a/launcher/ui/dialogs/ExportToModListDialog.ui b/launcher/ui/dialogs/ExportToModListDialog.ui new file mode 100644 index 000000000..25eb43429 --- /dev/null +++ b/launcher/ui/dialogs/ExportToModListDialog.ui @@ -0,0 +1,240 @@ + + + ExportToModListDialog + + + + 0 + 0 + 650 + 446 + + + + Export Pack to ModList + + + true + + + + + + + + Settings + + + + + + + HTML + + + + + Markdown + + + + + Plaintext + + + + + JSON + + + + + CSV + + + + + Custom + + + + + + + + Template + + + + + + + + + + + + Optional Info + + + + + + Version + + + + + + + Authors + + + + + + + URL + + + + + + + Version + + + + + + + Authors + + + + + + + URL + + + + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 1 + + + Format + + + + + + + + + + Result + + + + + + + 0 + 143 + + + + true + + + + + + + true + + + + + + + + + + This depends on the mods' metadata. To ensure it is available, run an update on the instance. Installing the updates isn't necessary. + + + true + + + + + + + + + + + Copy + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Save + + + + + + + + + + + buttonBox + accepted() + ExportToModListDialog + accept() + + + 334 + 435 + + + 324 + 206 + + + + + buttonBox + rejected() + ExportToModListDialog + reject() + + + 324 + 390 + + + 324 + 206 + + + + + diff --git a/launcher/ui/dialogs/IconPickerDialog.cpp b/launcher/ui/dialogs/IconPickerDialog.cpp index 5131686af..faad3ce75 100644 --- a/launcher/ui/dialogs/IconPickerDialog.cpp +++ b/launcher/ui/dialogs/IconPickerDialog.cpp @@ -13,9 +13,9 @@ * limitations under the License. */ +#include #include #include -#include #include "Application.h" @@ -24,12 +24,11 @@ #include "ui/instanceview/InstanceDelegate.h" +#include #include "icons/IconList.h" #include "icons/IconUtils.h" -#include -IconPickerDialog::IconPickerDialog(QWidget *parent) - : QDialog(parent), ui(new Ui::IconPickerDialog) +IconPickerDialog::IconPickerDialog(QWidget* parent) : QDialog(parent), ui(new Ui::IconPickerDialog) { ui->setupUi(this); setWindowModality(Qt::WindowModal); @@ -69,31 +68,30 @@ IconPickerDialog::IconPickerDialog(QWidget *parent) connect(contentsWidget, SIGNAL(doubleClicked(QModelIndex)), SLOT(activated(QModelIndex))); - connect(contentsWidget->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), SLOT(selectionChanged(QItemSelection, QItemSelection))); + connect(contentsWidget->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + SLOT(selectionChanged(QItemSelection, QItemSelection))); auto buttonFolder = ui->buttonBox->addButton(tr("Open Folder"), QDialogButtonBox::ResetRole); connect(buttonFolder, &QPushButton::clicked, this, &IconPickerDialog::openFolder); } -bool IconPickerDialog::eventFilter(QObject *obj, QEvent *evt) +bool IconPickerDialog::eventFilter(QObject* obj, QEvent* evt) { if (obj != ui->iconView) return QDialog::eventFilter(obj, evt); - if (evt->type() != QEvent::KeyPress) - { + if (evt->type() != QEvent::KeyPress) { return QDialog::eventFilter(obj, evt); } - QKeyEvent *keyEvent = static_cast(evt); - switch (keyEvent->key()) - { - case Qt::Key_Delete: - removeSelectedIcon(); - return true; - case Qt::Key_Plus: - addNewIcon(); - return true; - default: - break; + QKeyEvent* keyEvent = static_cast(evt); + switch (keyEvent->key()) { + case Qt::Key_Delete: + removeSelectedIcon(); + return true; + case Qt::Key_Plus: + addNewIcon(); + return true; + default: + break; } return QDialog::eventFilter(obj, evt); } @@ -142,8 +140,7 @@ int IconPickerDialog::execWithSelection(QString selection) int index_nr = list->getIconIndex(selection); auto model_index = list->index(index_nr); - contentsWidget->selectionModel()->select( - model_index, QItemSelectionModel::Current | QItemSelectionModel::Select); + contentsWidget->selectionModel()->select(model_index, QItemSelectionModel::Current | QItemSelectionModel::Select); QMetaObject::invokeMethod(this, "delayed_scroll", Qt::QueuedConnection, Q_ARG(QModelIndex, model_index)); return QDialog::exec(); diff --git a/launcher/ui/dialogs/IconPickerDialog.h b/launcher/ui/dialogs/IconPickerDialog.h index c93f565f1..37e53dcce 100644 --- a/launcher/ui/dialogs/IconPickerDialog.h +++ b/launcher/ui/dialogs/IconPickerDialog.h @@ -17,30 +17,27 @@ #include #include -namespace Ui -{ +namespace Ui { class IconPickerDialog; } -class IconPickerDialog : public QDialog -{ +class IconPickerDialog : public QDialog { Q_OBJECT -public: - explicit IconPickerDialog(QWidget *parent = 0); + public: + explicit IconPickerDialog(QWidget* parent = 0); ~IconPickerDialog(); int execWithSelection(QString selection); QString selectedIconKey; -protected: - virtual bool eventFilter(QObject *, QEvent *); + protected: + virtual bool eventFilter(QObject*, QEvent*); -private: - Ui::IconPickerDialog *ui; - QPushButton *buttonRemove; + private: + Ui::IconPickerDialog* ui; + QPushButton* buttonRemove; -private -slots: + private slots: void selectionChanged(QItemSelection, QItemSelection); void activated(QModelIndex); void delayed_scroll(QModelIndex); diff --git a/launcher/ui/dialogs/ImportResourceDialog.h b/launcher/ui/dialogs/ImportResourceDialog.h index 5f2f7a92e..bbde1ba7b 100644 --- a/launcher/ui/dialogs/ImportResourceDialog.h +++ b/launcher/ui/dialogs/ImportResourceDialog.h @@ -17,7 +17,7 @@ class ImportResourceDialog : public QDialog { explicit ImportResourceDialog(QString file_path, PackedResourceType type, QWidget* parent = nullptr); ~ImportResourceDialog() override; QString selectedInstanceKey; - + private: Ui::ImportResourceDialog* ui; PackedResourceType m_resource_type; diff --git a/launcher/ui/dialogs/InstallLoaderDialog.cpp b/launcher/ui/dialogs/InstallLoaderDialog.cpp new file mode 100644 index 000000000..840a328f3 --- /dev/null +++ b/launcher/ui/dialogs/InstallLoaderDialog.cpp @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 TheKodeToad + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "InstallLoaderDialog.h" + +#include +#include +#include +#include "Application.h" +#include "BuildConfig.h" +#include "DesktopServices.h" +#include "meta/Index.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/PackProfile.h" +#include "ui/widgets/PageContainer.h" +#include "ui/widgets/VersionSelectWidget.h" + +class InstallLoaderPage : public VersionSelectWidget, public BasePage { + public: + InstallLoaderPage(const QString& id, + const QString& iconName, + const QString& name, + const Version& oldestVersion, + const std::shared_ptr profile) + : VersionSelectWidget(nullptr), uid(id), iconName(iconName), name(name) + { + const QString minecraftVersion = profile->getComponentVersion("net.minecraft"); + setEmptyString(tr("No versions are currently available for Minecraft %1").arg(minecraftVersion)); + setExactIfPresentFilter(BaseVersionList::ParentVersionRole, minecraftVersion); + + if (oldestVersion != Version() && Version(minecraftVersion) < oldestVersion) + setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); + + if (const QString currentVersion = profile->getComponentVersion(id); !currentVersion.isNull()) + setCurrentVersion(currentVersion); + } + + QString id() const override { return uid; } + QString displayName() const override { return name; } + QIcon icon() const override { return APPLICATION->getThemedIcon(iconName); } + + void openedImpl() override + { + if (loaded) + return; + + const auto versions = APPLICATION->metadataIndex()->get(uid); + if (!versions) + return; + + initialize(versions.get()); + loaded = true; + } + + void setParentContainer(BasePageContainer* container) override + { + auto dialog = dynamic_cast(dynamic_cast(container)->parent()); + connect(view(), &QAbstractItemView::doubleClicked, dialog, &QDialog::accept); + } + + private: + const QString uid; + const QString iconName; + const QString name; + bool loaded = false; +}; + +static InstallLoaderPage* pageCast(BasePage* page) +{ + auto result = dynamic_cast(page); + Q_ASSERT(result != nullptr); + return result; +} + +InstallLoaderDialog::InstallLoaderDialog(std::shared_ptr profile, const QString& uid, QWidget* parent) + : QDialog(parent), profile(std::move(profile)), container(new PageContainer(this, QString(), this)), buttons(new QDialogButtonBox(this)) +{ + auto layout = new QVBoxLayout(this); + + container->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + layout->addWidget(container); + + auto buttonLayout = new QHBoxLayout(this); + + auto refreshButton = new QPushButton(tr("&Refresh"), this); + connect(refreshButton, &QPushButton::clicked, this, [this] { pageCast(container->selectedPage())->loadList(); }); + buttonLayout->addWidget(refreshButton); + + buttons->setOrientation(Qt::Horizontal); + buttons->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); + connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); + buttonLayout->addWidget(buttons); + + layout->addLayout(buttonLayout); + + setWindowTitle(dialogTitle()); + setWindowModality(Qt::WindowModal); + resize(520, 347); + + for (BasePage* page : container->getPages()) { + if (page->id() == uid) + container->selectPage(page->id()); + + connect(pageCast(page), &VersionSelectWidget::selectedVersionChanged, this, [this, page] { + if (page->id() == container->selectedPage()->id()) + validate(container->selectedPage()); + }); + } + connect(container, &PageContainer::selectedPageChanged, this, [this](BasePage* previous, BasePage* current) { validate(current); }); + pageCast(container->selectedPage())->selectSearch(); + validate(container->selectedPage()); +} + +QList InstallLoaderDialog::getPages() +{ + return { // Forge + new InstallLoaderPage("net.minecraftforge", "forge", tr("Forge"), {}, profile), + // Fabric + new InstallLoaderPage("net.fabricmc.fabric-loader", "fabricmc", tr("Fabric"), Version("1.14"), profile), + // Quilt + new InstallLoaderPage("org.quiltmc.quilt-loader", "quiltmc", tr("Quilt"), Version("1.14"), profile), + // LiteLoader + new InstallLoaderPage("com.mumfrey.liteloader", "liteloader", tr("LiteLoader"), {}, profile) + }; +} + +QString InstallLoaderDialog::dialogTitle() +{ + return tr("Install Loader"); +} + +void InstallLoaderDialog::validate(BasePage* page) +{ + buttons->button(QDialogButtonBox::Ok)->setEnabled(pageCast(page)->selectedVersion() != nullptr); +} + +void InstallLoaderDialog::done(int result) +{ + if (result == Accepted) { + auto* page = pageCast(container->selectedPage()); + if (page->selectedVersion()) { + profile->setComponentVersion(page->id(), page->selectedVersion()->descriptor()); + profile->resolve(Net::Mode::Online); + } + } + + QDialog::done(result); +} diff --git a/launcher/ui/dialogs/InstallLoaderDialog.h b/launcher/ui/dialogs/InstallLoaderDialog.h new file mode 100644 index 000000000..86cb3bdd2 --- /dev/null +++ b/launcher/ui/dialogs/InstallLoaderDialog.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 TheKodeToad + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include "ui/pages/BasePageProvider.h" + +class MinecraftInstance; +class PageContainer; +class PackProfile; +class QDialogButtonBox; + +class InstallLoaderDialog final : public QDialog, protected BasePageProvider { + Q_OBJECT + + public: + explicit InstallLoaderDialog(std::shared_ptr instance, const QString& uid = QString(), QWidget* parent = nullptr); + + QList getPages() override; + QString dialogTitle() override; + + void validate(BasePage* page); + void done(int result) override; + + private: + std::shared_ptr profile; + PageContainer* container; + QDialogButtonBox* buttons; +}; diff --git a/launcher/ui/dialogs/LoginDialog.cpp b/launcher/ui/dialogs/LoginDialog.cpp index 30394b725..7296a13ef 100644 --- a/launcher/ui/dialogs/LoginDialog.cpp +++ b/launcher/ui/dialogs/LoginDialog.cpp @@ -20,7 +20,7 @@ #include -LoginDialog::LoginDialog(QWidget *parent) : QDialog(parent), ui(new Ui::LoginDialog) +LoginDialog::LoginDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LoginDialog) { ui->setupUi(this); ui->progressBar->setVisible(false); @@ -59,27 +59,24 @@ void LoginDialog::setUserInputsEnabled(bool enable) } // Enable the OK button only when both textboxes contain something. -void LoginDialog::on_userTextBox_textEdited(const QString &newText) +void LoginDialog::on_userTextBox_textEdited(const QString& newText) { - ui->buttonBox->button(QDialogButtonBox::Ok) - ->setEnabled(!newText.isEmpty() && !ui->passTextBox->text().isEmpty()); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!newText.isEmpty() && !ui->passTextBox->text().isEmpty()); } -void LoginDialog::on_passTextBox_textEdited(const QString &newText) +void LoginDialog::on_passTextBox_textEdited(const QString& newText) { - ui->buttonBox->button(QDialogButtonBox::Ok) - ->setEnabled(!newText.isEmpty() && !ui->userTextBox->text().isEmpty()); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!newText.isEmpty() && !ui->userTextBox->text().isEmpty()); } -void LoginDialog::onTaskFailed(const QString &reason) +void LoginDialog::onTaskFailed(const QString& reason) { // Set message auto lines = reason.split('\n'); QString processed; - for(auto line: lines) { - if(line.size()) { + for (auto line : lines) { + if (line.size()) { processed += "" + line + "
    "; - } - else { + } else { processed += "
    "; } } @@ -95,7 +92,7 @@ void LoginDialog::onTaskSucceeded() QDialog::accept(); } -void LoginDialog::onTaskStatus(const QString &status) +void LoginDialog::onTaskStatus(const QString& status) { ui->label->setText(status); } @@ -107,12 +104,11 @@ void LoginDialog::onTaskProgress(qint64 current, qint64 total) } // Public interface -MinecraftAccountPtr LoginDialog::newAccount(QWidget *parent, QString msg) +MinecraftAccountPtr LoginDialog::newAccount(QWidget* parent, QString msg) { LoginDialog dlg(parent); dlg.ui->label->setText(msg); - if (dlg.exec() == QDialog::Accepted) - { + if (dlg.exec() == QDialog::Accepted) { return dlg.m_account; } return nullptr; diff --git a/launcher/ui/dialogs/LoginDialog.h b/launcher/ui/dialogs/LoginDialog.h index f8101ff57..601b5fa77 100644 --- a/launcher/ui/dialogs/LoginDialog.h +++ b/launcher/ui/dialogs/LoginDialog.h @@ -15,45 +15,42 @@ #pragma once -#include #include +#include #include "minecraft/auth/MinecraftAccount.h" #include "tasks/Task.h" -namespace Ui -{ +namespace Ui { class LoginDialog; } -class LoginDialog : public QDialog -{ +class LoginDialog : public QDialog { Q_OBJECT -public: + public: ~LoginDialog(); - static MinecraftAccountPtr newAccount(QWidget *parent, QString message); + static MinecraftAccountPtr newAccount(QWidget* parent, QString message); -private: - explicit LoginDialog(QWidget *parent = 0); + private: + explicit LoginDialog(QWidget* parent = 0); void setUserInputsEnabled(bool enable); -protected -slots: + protected slots: void accept(); - void onTaskFailed(const QString &reason); + void onTaskFailed(const QString& reason); void onTaskSucceeded(); - void onTaskStatus(const QString &status); + void onTaskStatus(const QString& status); void onTaskProgress(qint64 current, qint64 total); - void on_userTextBox_textEdited(const QString &newText); - void on_passTextBox_textEdited(const QString &newText); + void on_userTextBox_textEdited(const QString& newText); + void on_passTextBox_textEdited(const QString& newText); -private: - Ui::LoginDialog *ui; + private: + Ui::LoginDialog* ui; MinecraftAccountPtr m_account; Task::Ptr m_loginTask; }; diff --git a/launcher/ui/dialogs/MSALoginDialog.cpp b/launcher/ui/dialogs/MSALoginDialog.cpp index be49babba..74fff9fd3 100644 --- a/launcher/ui/dialogs/MSALoginDialog.cpp +++ b/launcher/ui/dialogs/MSALoginDialog.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -39,12 +39,12 @@ #include "DesktopServices.h" #include "minecraft/auth/AccountTask.h" -#include -#include #include #include +#include +#include -MSALoginDialog::MSALoginDialog(QWidget *parent) : QDialog(parent), ui(new Ui::MSALoginDialog) +MSALoginDialog::MSALoginDialog(QWidget* parent) : QDialog(parent), ui(new Ui::MSALoginDialog) { ui->setupUi(this); ui->progressBar->setVisible(false); @@ -55,7 +55,8 @@ MSALoginDialog::MSALoginDialog(QWidget *parent) : QDialog(parent), ui(new Ui::MS connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); } -int MSALoginDialog::exec() { +int MSALoginDialog::exec() +{ setUserInputsEnabled(false); ui->progressBar->setVisible(true); @@ -74,24 +75,24 @@ int MSALoginDialog::exec() { return QDialog::exec(); } - MSALoginDialog::~MSALoginDialog() { delete ui; } -void MSALoginDialog::externalLoginTick() { +void MSALoginDialog::externalLoginTick() +{ m_externalLoginElapsed++; ui->progressBar->setValue(m_externalLoginElapsed); ui->progressBar->repaint(); - if(m_externalLoginElapsed >= m_externalLoginTimeout) { + if (m_externalLoginElapsed >= m_externalLoginTimeout) { m_externalLoginTimer.stop(); } } - -void MSALoginDialog::showVerificationUriAndCode(const QUrl& uri, const QString& code, int expiresIn) { +void MSALoginDialog::showVerificationUriAndCode(const QUrl& uri, const QString& code, int expiresIn) +{ m_externalLoginElapsed = 0; m_externalLoginTimeout = expiresIn; @@ -104,7 +105,8 @@ void MSALoginDialog::showVerificationUriAndCode(const QUrl& uri, const QString& QString urlString = uri.toString(); QString linkString = QString("%2").arg(urlString, urlString); - ui->label->setText(tr("

    Please open up %1 in a browser and put in the code %2 to proceed with login.

    ").arg(linkString, code)); + ui->label->setText( + tr("

    Please open up %1 in a browser and put in the code %2 to proceed with login.

    ").arg(linkString, code)); ui->actionButton->setVisible(true); connect(ui->actionButton, &QPushButton::clicked, [=]() { DesktopServices::openUrl(uri); @@ -113,7 +115,8 @@ void MSALoginDialog::showVerificationUriAndCode(const QUrl& uri, const QString& }); } -void MSALoginDialog::hideVerificationUriAndCode() { +void MSALoginDialog::hideVerificationUriAndCode() +{ m_externalLoginTimer.stop(); ui->actionButton->setVisible(false); } @@ -123,16 +126,15 @@ void MSALoginDialog::setUserInputsEnabled(bool enable) ui->buttonBox->setEnabled(enable); } -void MSALoginDialog::onTaskFailed(const QString &reason) +void MSALoginDialog::onTaskFailed(const QString& reason) { // Set message auto lines = reason.split('\n'); QString processed; - for(auto line: lines) { - if(line.size()) { + for (auto line : lines) { + if (line.size()) { processed += "" + line + "
    "; - } - else { + } else { processed += "
    "; } } @@ -149,7 +151,7 @@ void MSALoginDialog::onTaskSucceeded() QDialog::accept(); } -void MSALoginDialog::onTaskStatus(const QString &status) +void MSALoginDialog::onTaskStatus(const QString& status) { ui->label->setText(status); } @@ -161,12 +163,11 @@ void MSALoginDialog::onTaskProgress(qint64 current, qint64 total) } // Public interface -MinecraftAccountPtr MSALoginDialog::newAccount(QWidget *parent, QString msg) +MinecraftAccountPtr MSALoginDialog::newAccount(QWidget* parent, QString msg) { MSALoginDialog dlg(parent); dlg.ui->label->setText(msg); - if (dlg.exec() == QDialog::Accepted) - { + if (dlg.exec() == QDialog::Accepted) { return dlg.m_account; } return nullptr; diff --git a/launcher/ui/dialogs/MSALoginDialog.h b/launcher/ui/dialogs/MSALoginDialog.h index 4cf146ab3..03e276bc0 100644 --- a/launcher/ui/dialogs/MSALoginDialog.h +++ b/launcher/ui/dialogs/MSALoginDialog.h @@ -15,49 +15,45 @@ #pragma once -#include -#include #include +#include +#include #include "minecraft/auth/MinecraftAccount.h" -namespace Ui -{ +namespace Ui { class MSALoginDialog; } -class MSALoginDialog : public QDialog -{ +class MSALoginDialog : public QDialog { Q_OBJECT -public: + public: ~MSALoginDialog(); - static MinecraftAccountPtr newAccount(QWidget *parent, QString message); + static MinecraftAccountPtr newAccount(QWidget* parent, QString message); int exec() override; -private: - explicit MSALoginDialog(QWidget *parent = 0); + private: + explicit MSALoginDialog(QWidget* parent = 0); void setUserInputsEnabled(bool enable); -protected -slots: - void onTaskFailed(const QString &reason); + protected slots: + void onTaskFailed(const QString& reason); void onTaskSucceeded(); - void onTaskStatus(const QString &status); + void onTaskStatus(const QString& status); void onTaskProgress(qint64 current, qint64 total); - void showVerificationUriAndCode(const QUrl &uri, const QString &code, int expiresIn); + void showVerificationUriAndCode(const QUrl& uri, const QString& code, int expiresIn); void hideVerificationUriAndCode(); void externalLoginTick(); -private: - Ui::MSALoginDialog *ui; + private: + Ui::MSALoginDialog* ui; MinecraftAccountPtr m_account; shared_qobject_ptr m_loginTask; QTimer m_externalLoginTimer; int m_externalLoginElapsed = 0; int m_externalLoginTimeout = 0; }; - diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 8618b9240..0af1ec59b 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -89,15 +89,17 @@ void ModUpdateDialog::checkCandidates() if (!m_modrinth_to_update.empty()) { m_modrinth_check_task.reset(new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model)); - connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this, - [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({mod, reason, recover_url}); }); + connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this, [this](Mod* mod, QString reason, QUrl recover_url) { + m_failed_check_update.append({ mod, reason, recover_url }); + }); check_task.addTask(m_modrinth_check_task); } if (!m_flame_to_update.empty()) { m_flame_check_task.reset(new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_mod_model)); - connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this, - [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({mod, reason, recover_url}); }); + connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this, [this](Mod* mod, QString reason, QUrl recover_url) { + m_failed_check_update.append({ mod, reason, recover_url }); + }); check_task.addTask(m_flame_check_task); } @@ -162,7 +164,7 @@ void ModUpdateDialog::checkCandidates() if (!recover_url.isEmpty()) //: %1 is the link to download it manually text += tr("Possible solution: Getting the latest version manually:
    %1
    ") - .arg(QString("%1").arg(recover_url.toString())); + .arg(QString("%1").arg(recover_url.toString())); text += "
    "; } @@ -342,7 +344,7 @@ void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::R } else { QString reason{ tr("Couldn't find a valid version on the selected mod provider(s)") }; - m_failed_metadata.append({mod, reason}); + m_failed_metadata.append({ mod, reason }); } } diff --git a/launcher/ui/dialogs/ModUpdateDialog.h b/launcher/ui/dialogs/ModUpdateDialog.h index 1a92f6134..12dddf5e1 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.h +++ b/launcher/ui/dialogs/ModUpdateDialog.h @@ -36,7 +36,9 @@ class ModUpdateDialog final : public ReviewMessageBox { private slots: void onMetadataEnsured(Mod*); - void onMetadataFailed(Mod*, bool try_others = false, ModPlatform::ResourceProvider first_choice = ModPlatform::ResourceProvider::MODRINTH); + void onMetadataFailed(Mod*, + bool try_others = false, + ModPlatform::ResourceProvider first_choice = ModPlatform::ResourceProvider::MODRINTH); private: QWidget* m_parent; diff --git a/launcher/ui/dialogs/NewComponentDialog.cpp b/launcher/ui/dialogs/NewComponentDialog.cpp index ea790e8c2..b47b85ff1 100644 --- a/launcher/ui/dialogs/NewComponentDialog.cpp +++ b/launcher/ui/dialogs/NewComponentDialog.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -33,28 +33,28 @@ * limitations under the License. */ -#include "Application.h" #include "NewComponentDialog.h" +#include "Application.h" #include "ui_NewComponentDialog.h" #include +#include #include #include -#include -#include "VersionSelectDialog.h" -#include "ProgressDialog.h" #include "IconPickerDialog.h" +#include "ProgressDialog.h" +#include "VersionSelectDialog.h" +#include #include #include -#include #include #include #include -NewComponentDialog::NewComponentDialog(const QString & initialName, const QString & initialUid, QWidget *parent) +NewComponentDialog::NewComponentDialog(const QString& initialName, const QString& initialUid, QWidget* parent) : QDialog(parent), ui(new Ui::NewComponentDialog) { ui->setupUi(this); @@ -81,12 +81,9 @@ void NewComponentDialog::updateDialogState() { auto protoUid = ui->nameTextBox->text().toLower(); protoUid.remove(QRegularExpression("[^a-z]")); - if(protoUid.isEmpty()) - { + if (protoUid.isEmpty()) { ui->uidTextBox->setPlaceholderText(originalPlaceholderText); - } - else - { + } else { QString suggestedUid = "org.multimc.custom." + protoUid; ui->uidTextBox->setPlaceholderText(suggestedUid); } @@ -97,8 +94,7 @@ void NewComponentDialog::updateDialogState() QString NewComponentDialog::name() const { auto result = ui->nameTextBox->text(); - if(result.size()) - { + if (result.size()) { return result.trimmed(); } return QString(); @@ -107,13 +103,11 @@ QString NewComponentDialog::name() const QString NewComponentDialog::uid() const { auto result = ui->uidTextBox->text(); - if(result.size()) - { + if (result.size()) { return result.trimmed(); } result = ui->uidTextBox->placeholderText(); - if(result.size() && result != originalPlaceholderText) - { + if (result.size() && result != originalPlaceholderText) { return result.trimmed(); } return QString(); diff --git a/launcher/ui/dialogs/NewComponentDialog.h b/launcher/ui/dialogs/NewComponentDialog.h index 8c790beb3..4fb68ff2f 100644 --- a/launcher/ui/dialogs/NewComponentDialog.h +++ b/launcher/ui/dialogs/NewComponentDialog.h @@ -20,28 +20,26 @@ #include #include -namespace Ui -{ +namespace Ui { class NewComponentDialog; } -class NewComponentDialog : public QDialog -{ +class NewComponentDialog : public QDialog { Q_OBJECT -public: - explicit NewComponentDialog(const QString & initialName = QString(), const QString & initialUid = QString(), QWidget *parent = 0); + public: + explicit NewComponentDialog(const QString& initialName = QString(), const QString& initialUid = QString(), QWidget* parent = 0); virtual ~NewComponentDialog(); void setBlacklist(QStringList badUids); QString name() const; QString uid() const; -private slots: + private slots: void updateDialogState(); -private: - Ui::NewComponentDialog *ui; + private: + Ui::NewComponentDialog* ui; QString originalPlaceholderText; QStringList uidBlacklist; diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp index 7b9bb944c..9613c6b00 100644 --- a/launcher/ui/dialogs/NewInstanceDialog.cpp +++ b/launcher/ui/dialogs/NewInstanceDialog.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -33,38 +33,39 @@ * limitations under the License. */ -#include "Application.h" #include "NewInstanceDialog.h" +#include "Application.h" +#include "ui/pages/modplatform/import_ftb/ImportFTBPage.h" #include "ui_NewInstanceDialog.h" #include +#include #include #include -#include -#include "VersionSelectDialog.h" -#include "ProgressDialog.h" #include "IconPickerDialog.h" +#include "ProgressDialog.h" +#include "VersionSelectDialog.h" +#include +#include #include #include -#include #include -#include #include -#include "ui/widgets/PageContainer.h" #include "ui/pages/modplatform/CustomPage.h" -#include "ui/pages/modplatform/atlauncher/AtlPage.h" -#include "ui/pages/modplatform/legacy_ftb/Page.h" -#include "ui/pages/modplatform/flame/FlamePage.h" #include "ui/pages/modplatform/ImportPage.h" +#include "ui/pages/modplatform/atlauncher/AtlPage.h" +#include "ui/pages/modplatform/flame/FlamePage.h" +#include "ui/pages/modplatform/legacy_ftb/Page.h" #include "ui/pages/modplatform/modrinth/ModrinthPage.h" #include "ui/pages/modplatform/technic/TechnicPage.h" - - - -NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString & url, QWidget *parent) +#include "ui/widgets/PageContainer.h" +NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, + const QString& url, + const QMap& extra_info, + QWidget* parent) : QDialog(parent), ui(new Ui::NewInstanceDialog) { ui->setupUi(this); @@ -88,15 +89,14 @@ NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString groupList.push_front(""); ui->groupBox->addItems(groupList); int index = groupList.indexOf(initialGroup); - if(index == -1) - { + if (index == -1) { index = 0; } ui->groupBox->setCurrentIndex(index); ui->groupBox->lineEdit()->setPlaceholderText(tr("No group")); - - // NOTE: m_buttons must be initialized before PageContainer, because it indirectly accesses m_buttons through setSuggestedPack! Do not move this below. + // 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, {}, this); @@ -123,11 +123,11 @@ NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString HelpButton->setAutoDefault(false); connect(HelpButton, &QPushButton::clicked, m_container, &PageContainer::help); - if(!url.isEmpty()) - { + if (!url.isEmpty()) { QUrl actualUrl(url); m_container->selectPage("import"); importPage->setUrl(url); + importPage->setExtraInfo(extra_info); } updateDialogState(); @@ -156,9 +156,9 @@ void NewInstanceDialog::accept() QDialog::accept(); } -QList NewInstanceDialog::getPages() +QList NewInstanceDialog::getPages() { - QList pages; + QList pages; importPage = new ImportPage(this); @@ -168,6 +168,7 @@ QList NewInstanceDialog::getPages() if (APPLICATION->capabilities() & Application::SupportsFlame) pages.append(new FlamePage(this)); pages.append(new LegacyFTB::Page(this)); + pages.append(new FTBImportAPP::ImportFTBPage(this)); pages.append(new ModrinthPage(this)); pages.append(new TechnicPage(this)); @@ -216,17 +217,17 @@ void NewInstanceDialog::setSuggestedPack(const QString& name, QString version, I m_buttons->button(QDialogButtonBox::Ok)->setEnabled(allowOK); } -void NewInstanceDialog::setSuggestedIconFromFile(const QString &path, const QString &name) +void NewInstanceDialog::setSuggestedIconFromFile(const QString& path, const QString& name) { importIcon = true; importIconPath = path; importIconName = name; - //Hmm, for some reason they can be to small + // Hmm, for some reason they can be to small ui->iconButton->setIcon(QIcon(path)); } -void NewInstanceDialog::setSuggestedIcon(const QString &key) +void NewInstanceDialog::setSuggestedIcon(const QString& key) { auto icon = APPLICATION->icons()->getIcon(key); importIcon = false; @@ -234,9 +235,9 @@ void NewInstanceDialog::setSuggestedIcon(const QString &key) ui->iconButton->setIcon(icon); } -InstanceTask * NewInstanceDialog::extractTask() +InstanceTask* NewInstanceDialog::extractTask() { - InstanceTask * extracted = creationTask.get(); + InstanceTask* extracted = creationTask.get(); creationTask.release(); InstanceName inst_name(ui->instNameTextBox->placeholderText().trimmed(), importVersion); @@ -252,8 +253,7 @@ void NewInstanceDialog::updateDialogState() { auto allowOK = creationTask && !instName().isEmpty(); auto OkButton = m_buttons->button(QDialogButtonBox::Ok); - if(OkButton->isEnabled() != allowOK) - { + if (OkButton->isEnabled() != allowOK) { OkButton->setEnabled(allowOK); } } @@ -261,13 +261,11 @@ void NewInstanceDialog::updateDialogState() QString NewInstanceDialog::instName() const { auto result = ui->instNameTextBox->text().trimmed(); - if(result.size()) - { + if (result.size()) { return result; } result = ui->instNameTextBox->placeholderText().trimmed(); - if(result.size()) - { + if (result.size()) { return result; } return QString(); @@ -284,28 +282,27 @@ QString NewInstanceDialog::iconKey() const void NewInstanceDialog::on_iconButton_clicked() { - importIconNow(); //so the user can switch back + importIconNow(); // so the user can switch back IconPickerDialog dlg(this); dlg.execWithSelection(InstIconKey); - if (dlg.result() == QDialog::Accepted) - { + if (dlg.result() == QDialog::Accepted) { InstIconKey = dlg.selectedIconKey; ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey)); importIcon = false; } } -void NewInstanceDialog::on_instNameTextBox_textChanged(const QString &arg1) +void NewInstanceDialog::on_instNameTextBox_textChanged([[maybe_unused]] const QString& arg1) { updateDialogState(); } void NewInstanceDialog::importIconNow() { - if(importIcon) { + if (importIcon) { APPLICATION->icons()->installIcon(importIconPath, importIconName); - InstIconKey = importIconName; + InstIconKey = importIconName.mid(0, importIconName.lastIndexOf('.')); importIcon = false; } APPLICATION->settings()->set("NewInstanceGeometry", saveGeometry().toBase64()); diff --git a/launcher/ui/dialogs/NewInstanceDialog.h b/launcher/ui/dialogs/NewInstanceDialog.h index 961f512e2..923579567 100644 --- a/launcher/ui/dialogs/NewInstanceDialog.h +++ b/launcher/ui/dialogs/NewInstanceDialog.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -37,11 +37,10 @@ #include -#include "ui/pages/BasePageProvider.h" #include "InstanceTask.h" +#include "ui/pages/BasePageProvider.h" -namespace Ui -{ +namespace Ui { class NewInstanceDialog; } @@ -50,45 +49,47 @@ class QDialogButtonBox; class ImportPage; class FlamePage; -class NewInstanceDialog : public QDialog, public BasePageProvider -{ +class NewInstanceDialog : public QDialog, public BasePageProvider { Q_OBJECT -public: - explicit NewInstanceDialog(const QString & initialGroup, const QString & url = QString(), QWidget *parent = 0); + public: + explicit NewInstanceDialog(const QString& initialGroup, + const QString& url = QString(), + const QMap& extra_info = {}, + QWidget* parent = 0); ~NewInstanceDialog(); void updateDialogState(); - void setSuggestedPack(const QString& name = QString(), InstanceTask * task = nullptr); - void setSuggestedPack(const QString& name, QString version, InstanceTask * task = nullptr); - void setSuggestedIconFromFile(const QString &path, const QString &name); - void setSuggestedIcon(const QString &key); + void setSuggestedPack(const QString& name = QString(), InstanceTask* task = nullptr); + void setSuggestedPack(const QString& name, QString version, InstanceTask* task = nullptr); + void setSuggestedIconFromFile(const QString& path, const QString& name); + void setSuggestedIcon(const QString& key); - InstanceTask * extractTask(); + InstanceTask* extractTask(); QString dialogTitle() override; - QList getPages() override; + QList getPages() override; QString instName() const; QString instGroup() const; QString iconKey() const; -public slots: + public slots: void accept() override; void reject() override; -private slots: + private slots: void on_iconButton_clicked(); - void on_instNameTextBox_textChanged(const QString &arg1); + void on_instNameTextBox_textChanged(const QString& arg1); -private: - Ui::NewInstanceDialog *ui = nullptr; - PageContainer * m_container = nullptr; - QDialogButtonBox * m_buttons = nullptr; + private: + Ui::NewInstanceDialog* ui = nullptr; + PageContainer* m_container = nullptr; + QDialogButtonBox* m_buttons = nullptr; QString InstIconKey; - ImportPage *importPage = nullptr; + ImportPage* importPage = nullptr; std::unique_ptr creationTask; bool importIcon = false; diff --git a/launcher/ui/dialogs/OfflineLoginDialog.cpp b/launcher/ui/dialogs/OfflineLoginDialog.cpp index a69537abb..137620be4 100644 --- a/launcher/ui/dialogs/OfflineLoginDialog.cpp +++ b/launcher/ui/dialogs/OfflineLoginDialog.cpp @@ -5,7 +5,7 @@ #include -OfflineLoginDialog::OfflineLoginDialog(QWidget *parent) : QDialog(parent), ui(new Ui::OfflineLoginDialog) +OfflineLoginDialog::OfflineLoginDialog(QWidget* parent) : QDialog(parent), ui(new Ui::OfflineLoginDialog) { ui->setupUi(this); ui->progressBar->setVisible(false); @@ -52,22 +52,20 @@ void OfflineLoginDialog::on_allowLongUsernames_stateChanged(int value) } // Enable the OK button only when the textbox contains something. -void OfflineLoginDialog::on_userTextBox_textEdited(const QString &newText) +void OfflineLoginDialog::on_userTextBox_textEdited(const QString& newText) { - ui->buttonBox->button(QDialogButtonBox::Ok) - ->setEnabled(!newText.isEmpty()); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!newText.isEmpty()); } -void OfflineLoginDialog::onTaskFailed(const QString &reason) +void OfflineLoginDialog::onTaskFailed(const QString& reason) { // Set message auto lines = reason.split('\n'); QString processed; - for(auto line: lines) { - if(line.size()) { + for (auto line : lines) { + if (line.size()) { processed += "" + line + "
    "; - } - else { + } else { processed += "
    "; } } @@ -83,7 +81,7 @@ void OfflineLoginDialog::onTaskSucceeded() QDialog::accept(); } -void OfflineLoginDialog::onTaskStatus(const QString &status) +void OfflineLoginDialog::onTaskStatus(const QString& status) { ui->label->setText(status); } @@ -95,12 +93,11 @@ void OfflineLoginDialog::onTaskProgress(qint64 current, qint64 total) } // Public interface -MinecraftAccountPtr OfflineLoginDialog::newAccount(QWidget *parent, QString msg) +MinecraftAccountPtr OfflineLoginDialog::newAccount(QWidget* parent, QString msg) { OfflineLoginDialog dlg(parent); dlg.ui->label->setText(msg); - if (dlg.exec() == QDialog::Accepted) - { + if (dlg.exec() == QDialog::Accepted) { return dlg.m_account; } return nullptr; diff --git a/launcher/ui/dialogs/OfflineLoginDialog.h b/launcher/ui/dialogs/OfflineLoginDialog.h index fdb3d91fa..a50024a6c 100644 --- a/launcher/ui/dialogs/OfflineLoginDialog.h +++ b/launcher/ui/dialogs/OfflineLoginDialog.h @@ -1,44 +1,41 @@ #pragma once -#include #include +#include #include "minecraft/auth/MinecraftAccount.h" #include "tasks/Task.h" -namespace Ui -{ +namespace Ui { class OfflineLoginDialog; } -class OfflineLoginDialog : public QDialog -{ +class OfflineLoginDialog : public QDialog { Q_OBJECT -public: + public: ~OfflineLoginDialog(); - static MinecraftAccountPtr newAccount(QWidget *parent, QString message); + static MinecraftAccountPtr newAccount(QWidget* parent, QString message); -private: - explicit OfflineLoginDialog(QWidget *parent = 0); + private: + explicit OfflineLoginDialog(QWidget* parent = 0); void setUserInputsEnabled(bool enable); -protected -slots: + protected slots: void accept(); - void onTaskFailed(const QString &reason); + void onTaskFailed(const QString& reason); void onTaskSucceeded(); - void onTaskStatus(const QString &status); + void onTaskStatus(const QString& status); void onTaskProgress(qint64 current, qint64 total); - void on_userTextBox_textEdited(const QString &newText); + void on_userTextBox_textEdited(const QString& newText); void on_allowLongUsernames_stateChanged(int value); -private: - Ui::OfflineLoginDialog *ui; + private: + Ui::OfflineLoginDialog* ui; MinecraftAccountPtr m_account; Task::Ptr m_loginTask; }; diff --git a/launcher/ui/dialogs/ProfileSelectDialog.cpp b/launcher/ui/dialogs/ProfileSelectDialog.cpp index 7882cf45e..a62238bdb 100644 --- a/launcher/ui/dialogs/ProfileSelectDialog.cpp +++ b/launcher/ui/dialogs/ProfileSelectDialog.cpp @@ -16,43 +16,38 @@ #include "ProfileSelectDialog.h" #include "ui_ProfileSelectDialog.h" -#include #include +#include -#include "SkinUtils.h" #include "Application.h" +#include "SkinUtils.h" #include "ui/dialogs/ProgressDialog.h" -ProfileSelectDialog::ProfileSelectDialog(const QString &message, int flags, QWidget *parent) +ProfileSelectDialog::ProfileSelectDialog(const QString& message, int flags, QWidget* parent) : QDialog(parent), ui(new Ui::ProfileSelectDialog) { ui->setupUi(this); m_accounts = APPLICATION->accounts(); auto view = ui->listView; - //view->setModel(m_accounts.get()); - //view->hideColumn(AccountList::ActiveColumn); + // view->setModel(m_accounts.get()); + // view->hideColumn(AccountList::ActiveColumn); view->setColumnCount(1); view->setRootIsDecorated(false); // FIXME: use a real model, not this - if(QTreeWidgetItem* header = view->headerItem()) - { + if (QTreeWidgetItem* header = view->headerItem()) { header->setText(0, tr("Name")); - } - else - { + } else { view->setHeaderLabel(tr("Name")); } - QList items; - for (int i = 0; i < m_accounts->count(); i++) - { + QList items; + for (int i = 0; i < m_accounts->count(); i++) { MinecraftAccountPtr account = m_accounts->at(i); QString profileLabel; - if(account->isInUse()) { + if (account->isInUse()) { profileLabel = tr("%1 (in use)").arg(account->profileName()); - } - else { + } else { profileLabel = account->profileName(); } auto item = new QTreeWidgetItem(view); @@ -101,8 +96,7 @@ bool ProfileSelectDialog::useAsInstDefaullt() const void ProfileSelectDialog::on_buttonBox_accepted() { QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes(); - if (selection.size() > 0) - { + if (selection.size() > 0) { QModelIndex selected = selection.first(); m_selected = selected.data(AccountList::PointerRole).value(); } diff --git a/launcher/ui/dialogs/ProfileSelectDialog.h b/launcher/ui/dialogs/ProfileSelectDialog.h index 38aa42496..e56ba0527 100644 --- a/launcher/ui/dialogs/ProfileSelectDialog.h +++ b/launcher/ui/dialogs/ProfileSelectDialog.h @@ -21,17 +21,14 @@ #include "minecraft/auth/AccountList.h" -namespace Ui -{ +namespace Ui { class ProfileSelectDialog; } -class ProfileSelectDialog : public QDialog -{ +class ProfileSelectDialog : public QDialog { Q_OBJECT -public: - enum Flags - { + public: + enum Flags { NoFlags = 0, /*! @@ -52,7 +49,7 @@ public: * Constructs a new account select dialog with the given parent and message. * The message will be shown at the top of the dialog. It is an empty string by default. */ - explicit ProfileSelectDialog(const QString& message="", int flags=0, QWidget *parent = 0); + explicit ProfileSelectDialog(const QString& message = "", int flags = 0, QWidget* parent = 0); ~ProfileSelectDialog(); /*! @@ -73,18 +70,17 @@ public: */ bool useAsInstDefaullt() const; -public -slots: + public slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); -protected: + protected: shared_qobject_ptr m_accounts; //! The account that was selected when the user clicked OK. MinecraftAccountPtr m_selected; -private: - Ui::ProfileSelectDialog *ui; + private: + Ui::ProfileSelectDialog* ui; }; diff --git a/launcher/ui/dialogs/ProfileSetupDialog.cpp b/launcher/ui/dialogs/ProfileSetupDialog.cpp index 64c0b924c..4b0c5b768 100644 --- a/launcher/ui/dialogs/ProfileSetupDialog.cpp +++ b/launcher/ui/dialogs/ProfileSetupDialog.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -36,11 +36,11 @@ #include "ProfileSetupDialog.h" #include "ui_ProfileSetupDialog.h" -#include #include -#include -#include #include +#include +#include +#include #include "ui/dialogs/ProgressDialog.h" @@ -48,8 +48,7 @@ #include "minecraft/auth/AuthRequest.h" #include "minecraft/auth/Parsers.h" - -ProfileSetupDialog::ProfileSetupDialog(MinecraftAccountPtr accountToSetup, QWidget *parent) +ProfileSetupDialog::ProfileSetupDialog(MinecraftAccountPtr accountToSetup, QWidget* parent) : QDialog(parent), m_accountToSetup(accountToSetup), ui(new Ui::ProfileSetupDialog) { ui->setupUi(this); @@ -91,13 +90,11 @@ void ProfileSetupDialog::setNameStatus(ProfileSetupDialog::NameStatus status, QS { nameStatus = status; auto okButton = ui->buttonBox->button(QDialogButtonBox::Ok); - switch(nameStatus) - { + switch (nameStatus) { case NameStatus::Available: { validityAction->setIcon(goodIcon); okButton->setEnabled(true); - } - break; + } break; case NameStatus::NotSet: case NameStatus::Pending: validityAction->setIcon(yellowIcon); @@ -109,43 +106,44 @@ void ProfileSetupDialog::setNameStatus(ProfileSetupDialog::NameStatus status, QS okButton->setEnabled(false); break; } - if(!errorString.isEmpty()) { + if (!errorString.isEmpty()) { ui->errorLabel->setText(errorString); ui->errorLabel->setVisible(true); - } - else { + } else { ui->errorLabel->setVisible(false); } } void ProfileSetupDialog::nameEdited(const QString& name) { - if(!ui->nameEdit->hasAcceptableInput()) { + if (!ui->nameEdit->hasAcceptableInput()) { setNameStatus(NameStatus::NotSet, tr("Name is too short - must be between 3 and 16 characters long.")); return; } scheduleCheck(name); } -void ProfileSetupDialog::scheduleCheck(const QString& name) { +void ProfileSetupDialog::scheduleCheck(const QString& name) +{ queuedCheck = name; setNameStatus(NameStatus::Pending); checkStartTimer.start(1000); } -void ProfileSetupDialog::startCheck() { - if(isChecking) { +void ProfileSetupDialog::startCheck() +{ + if (isChecking) { return; } - if(queuedCheck.isNull()) { + if (queuedCheck.isNull()) { return; } checkName(queuedCheck); } - -void ProfileSetupDialog::checkName(const QString &name) { - if(isChecking) { +void ProfileSetupDialog::checkName(const QString& name) +{ + if (isChecking) { return; } @@ -160,44 +158,40 @@ void ProfileSetupDialog::checkName(const QString &name) { request.setRawHeader("Accept", "application/json"); request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toUtf8()); - AuthRequest *requestor = new AuthRequest(this); + AuthRequest* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &ProfileSetupDialog::checkFinished); requestor->get(request); } -void ProfileSetupDialog::checkFinished( - QNetworkReply::NetworkError error, - QByteArray data, - QList headers -) { - auto requestor = qobject_cast(QObject::sender()); +void ProfileSetupDialog::checkFinished(QNetworkReply::NetworkError error, + QByteArray profileData, + [[maybe_unused]] QList headers) +{ + auto requestor = qobject_cast(QObject::sender()); requestor->deleteLater(); - if(error == QNetworkReply::NoError) { - auto doc = QJsonDocument::fromJson(data); + if (error == QNetworkReply::NoError) { + auto doc = QJsonDocument::fromJson(profileData); auto root = doc.object(); auto statusValue = root.value("status").toString("INVALID"); - if(statusValue == "AVAILABLE") { + if (statusValue == "AVAILABLE") { setNameStatus(NameStatus::Available); - } - else if (statusValue == "DUPLICATE") { + } else if (statusValue == "DUPLICATE") { setNameStatus(NameStatus::Exists, tr("Minecraft profile with name %1 already exists.").arg(currentCheck)); - } - else if (statusValue == "NOT_ALLOWED") { + } else if (statusValue == "NOT_ALLOWED") { setNameStatus(NameStatus::Exists, tr("The name %1 is not allowed.").arg(currentCheck)); - } - else { + } else { setNameStatus(NameStatus::Error, tr("Unhandled profile name status: %1").arg(statusValue)); } - } - else { + } else { setNameStatus(NameStatus::Error, tr("Failed to check name availability.")); } isChecking = false; } -void ProfileSetupDialog::setupProfile(const QString &profileName) { - if(isWorking) { +void ProfileSetupDialog::setupProfile(const QString& profileName) +{ + if (isWorking) { return; } @@ -210,11 +204,11 @@ void ProfileSetupDialog::setupProfile(const QString &profileName) { request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toUtf8()); QString payloadTemplate("{\"profileName\":\"%1\"}"); - auto data = payloadTemplate.arg(profileName).toUtf8(); + auto profileData = payloadTemplate.arg(profileName).toUtf8(); - AuthRequest *requestor = new AuthRequest(this); + AuthRequest* requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &ProfileSetupDialog::setupProfileFinished); - requestor->post(request, data); + requestor->post(request, profileData); isWorking = true; auto button = ui->buttonBox->button(QDialogButtonBox::Cancel); @@ -223,8 +217,9 @@ void ProfileSetupDialog::setupProfile(const QString &profileName) { namespace { -struct MojangError{ - static MojangError fromJSON(QByteArray data) { +struct MojangError { + static MojangError fromJSON(QByteArray data) + { MojangError out; out.error = QString::fromUtf8(data); auto doc = QJsonDocument::fromJson(data, &out.parseError); @@ -247,26 +242,24 @@ struct MojangError{ QString errorMessage; }; -} +} // namespace -void ProfileSetupDialog::setupProfileFinished( - QNetworkReply::NetworkError error, - QByteArray data, - QList headers -) { - auto requestor = qobject_cast(QObject::sender()); +void ProfileSetupDialog::setupProfileFinished(QNetworkReply::NetworkError error, + QByteArray errorData, + [[maybe_unused]] QList headers) +{ + auto requestor = qobject_cast(QObject::sender()); requestor->deleteLater(); isWorking = false; - if(error == QNetworkReply::NoError) { + if (error == QNetworkReply::NoError) { /* * data contains the profile in the response * ... we could parse it and update the account, but let's just return back to the normal login flow instead... */ accept(); - } - else { - auto parsedError = MojangError::fromJSON(data); + } else { + auto parsedError = MojangError::fromJSON(errorData); ui->errorLabel->setVisible(true); ui->errorLabel->setText(tr("The server returned the following error:") + "\n\n" + parsedError.errorMessage); qDebug() << parsedError.rawError; diff --git a/launcher/ui/dialogs/ProfileSetupDialog.h b/launcher/ui/dialogs/ProfileSetupDialog.h index 6f413ebdf..09f8124e2 100644 --- a/launcher/ui/dialogs/ProfileSetupDialog.h +++ b/launcher/ui/dialogs/ProfileSetupDialog.h @@ -17,65 +17,48 @@ #include #include -#include #include +#include -#include #include +#include -namespace Ui -{ +namespace Ui { class ProfileSetupDialog; } -class ProfileSetupDialog : public QDialog -{ +class ProfileSetupDialog : public QDialog { Q_OBJECT -public: - - explicit ProfileSetupDialog(MinecraftAccountPtr accountToSetup, QWidget *parent = 0); + public: + explicit ProfileSetupDialog(MinecraftAccountPtr accountToSetup, QWidget* parent = 0); ~ProfileSetupDialog(); - enum class NameStatus - { - NotSet, - Pending, - Available, - Exists, - Error - } nameStatus = NameStatus::NotSet; + enum class NameStatus { NotSet, Pending, Available, Exists, Error } nameStatus = NameStatus::NotSet; -private slots: + private slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); - void nameEdited(const QString &name); - void checkFinished( - QNetworkReply::NetworkError error, - QByteArray data, - QList headers - ); + void nameEdited(const QString& name); + void checkFinished(QNetworkReply::NetworkError error, QByteArray data, QList headers); void startCheck(); - void setupProfileFinished( - QNetworkReply::NetworkError error, - QByteArray data, - QList headers - ); -protected: - void scheduleCheck(const QString &name); - void checkName(const QString &name); + void setupProfileFinished(QNetworkReply::NetworkError error, QByteArray data, QList headers); + + protected: + void scheduleCheck(const QString& name); + void checkName(const QString& name); void setNameStatus(NameStatus status, QString errorString); - void setupProfile(const QString & profileName); + void setupProfile(const QString& profileName); -private: + private: MinecraftAccountPtr m_accountToSetup; - Ui::ProfileSetupDialog *ui; + Ui::ProfileSetupDialog* ui; QIcon goodIcon; QIcon yellowIcon; QIcon badIcon; - QAction * validityAction = nullptr; + QAction* validityAction = nullptr; QString queuedCheck; @@ -85,4 +68,3 @@ private: QTimer checkStartTimer; }; - diff --git a/launcher/ui/dialogs/ProgressDialog.cpp b/launcher/ui/dialogs/ProgressDialog.cpp index 4243e2917..0ca3a1bd9 100644 --- a/launcher/ui/dialogs/ProgressDialog.cpp +++ b/launcher/ui/dialogs/ProgressDialog.cpp @@ -37,18 +37,17 @@ #include #include "ui_ProgressDialog.h" -#include #include #include +#include #include "tasks/Task.h" #include "ui/widgets/SubTaskProgressBar.h" - // map a value in a numeric range of an arbitrary type to between 0 and INT_MAX // for getting the best precision out of the qt progress bar -template, bool> = true> +template , bool> = true> std::tuple map_int_zero_max(T current, T range_max, T range_min) { int int_max = std::numeric_limits::max(); @@ -57,10 +56,9 @@ std::tuple map_int_zero_max(T current, T range_max, T range_min) double percentage = static_cast(current - range_min) / static_cast(type_range); int mapped_current = percentage * int_max; - return {mapped_current, int_max}; + return { mapped_current, int_max }; } - ProgressDialog::ProgressDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ProgressDialog) { ui->setupUi(this); @@ -87,7 +85,7 @@ void ProgressDialog::on_skipButton_clicked(bool checked) { Q_UNUSED(checked); if (ui->skipButton->isEnabled()) // prevent other triggers from aborting - task->abort(); + m_task->abort(); } ProgressDialog::~ProgressDialog() @@ -96,7 +94,7 @@ ProgressDialog::~ProgressDialog() } void ProgressDialog::updateSize(bool recenterParent) -{ +{ QSize lastSize = this->size(); QPoint lastPos = this->pos(); int minHeight = ui->globalStatusDetailsLabel->minimumSize().height() + (ui->verticalLayout->spacing() * 2); @@ -118,28 +116,25 @@ void ProgressDialog::updateSize(bool recenterParent) auto newX = std::max(0, parent->x() + ((parent->width() - newSize.width()) / 2)); auto newY = std::max(0, parent->y() + ((parent->height() - newSize.height()) / 2)); this->move(newX, newY); - } - else if (lastSize != newSize) - { + } else if (lastSize != newSize) { // center on old position after resize - QSize sizeDiff = lastSize - newSize; // last size was smaller, the results should be negative + QSize sizeDiff = lastSize - newSize; // last size was smaller, the results should be negative auto newX = std::max(0, lastPos.x() + (sizeDiff.width() / 2)); auto newY = std::max(0, lastPos.y() + (sizeDiff.height() / 2)); this->move(newX, newY); } - } int ProgressDialog::execWithTask(Task* task) { - this->task = task; + this->m_task = task; if (!task) { qDebug() << "Programmer error: Progress dialog created with null task."; return QDialog::DialogCode::Accepted; } - QDialog::DialogCode result {}; + QDialog::DialogCode result{}; if (handleImmediateResult(result)) { return result; } @@ -184,8 +179,8 @@ int ProgressDialog::execWithTask(std::unique_ptr& task) bool ProgressDialog::handleImmediateResult(QDialog::DialogCode& result) { - if (task->isFinished()) { - if (task->wasSuccessful()) { + if (m_task->isFinished()) { + if (m_task->wasSuccessful()) { result = QDialog::Accepted; } else { result = QDialog::Rejected; @@ -197,12 +192,12 @@ bool ProgressDialog::handleImmediateResult(QDialog::DialogCode& result) Task* ProgressDialog::getTask() { - return task; + return m_task; } void ProgressDialog::onTaskStarted() {} -void ProgressDialog::onTaskFailed(QString failure) +void ProgressDialog::onTaskFailed([[maybe_unused]] QString failure) { reject(); hide(); @@ -214,11 +209,11 @@ void ProgressDialog::onTaskSucceeded() hide(); } -void ProgressDialog::changeStatus(const QString& status) +void ProgressDialog::changeStatus([[maybe_unused]] const QString& status) { - ui->globalStatusLabel->setText(task->getStatus()); + ui->globalStatusLabel->setText(m_task->getStatus()); ui->globalStatusLabel->adjustSize(); - ui->globalStatusDetailsLabel->setText(task->getDetails()); + ui->globalStatusDetailsLabel->setText(m_task->getDetails()); ui->globalStatusDetailsLabel->adjustSize(); updateSize(); @@ -234,16 +229,15 @@ void ProgressDialog::addTaskProgress(TaskStepProgress const& progress) void ProgressDialog::changeStepProgress(TaskStepProgress const& task_progress) { m_is_multi_step = true; - if(ui->taskProgressScrollArea->isHidden()) { + if (ui->taskProgressScrollArea->isHidden()) { ui->taskProgressScrollArea->setHidden(false); updateSize(); } - + if (!taskProgress.contains(task_progress.uid)) addTaskProgress(task_progress); auto task_bar = taskProgress.value(task_progress.uid); - auto const [mapped_current, mapped_total] = map_int_zero_max(task_progress.current, task_progress.total, 0); if (task_progress.total <= 0) { task_bar->setRange(0, 0); @@ -258,14 +252,12 @@ void ProgressDialog::changeStepProgress(TaskStepProgress const& task_progress) if (task_progress.isDone()) { task_bar->setVisible(false); } - } void ProgressDialog::changeProgress(qint64 current, qint64 total) { ui->globalProgressBar->setMaximum(total); ui->globalProgressBar->setValue(current); - } void ProgressDialog::keyPressEvent(QKeyEvent* e) @@ -287,7 +279,7 @@ void ProgressDialog::keyPressEvent(QKeyEvent* e) void ProgressDialog::closeEvent(QCloseEvent* e) { - if (task && task->isRunning()) { + if (m_task && m_task->isRunning()) { e->ignore(); } else { QDialog::closeEvent(e); diff --git a/launcher/ui/dialogs/ProgressDialog.h b/launcher/ui/dialogs/ProgressDialog.h index f062be084..15eadf4e7 100644 --- a/launcher/ui/dialogs/ProgressDialog.h +++ b/launcher/ui/dialogs/ProgressDialog.h @@ -33,13 +33,12 @@ * limitations under the License. */ - #pragma once #include -#include #include #include +#include #include "QObjectPtr.h" #include "tasks/Task.h" @@ -49,60 +48,52 @@ class Task; class SequentialTask; -namespace Ui -{ +namespace Ui { class ProgressDialog; } -class ProgressDialog : public QDialog -{ +class ProgressDialog : public QDialog { Q_OBJECT -public: - explicit ProgressDialog(QWidget *parent = 0); + public: + explicit ProgressDialog(QWidget* parent = 0); ~ProgressDialog(); void updateSize(bool recenterParent = false); int execWithTask(Task* task); - int execWithTask(std::unique_ptr &&task); - int execWithTask(std::unique_ptr &task); + int execWithTask(std::unique_ptr&& task); + int execWithTask(std::unique_ptr& task); void setSkipButton(bool present, QString label = QString()); - Task *getTask(); + Task* getTask(); -public -slots: + public slots: void onTaskStarted(); void onTaskFailed(QString failure); void onTaskSucceeded(); - void changeStatus(const QString &status); + void changeStatus(const QString& status); void changeProgress(qint64 current, qint64 total); void changeStepProgress(TaskStepProgress const& task_progress); - -private -slots: + private slots: void on_skipButton_clicked(bool checked); -protected: - virtual void keyPressEvent(QKeyEvent *e); - virtual void closeEvent(QCloseEvent *e); + protected: + virtual void keyPressEvent(QKeyEvent* e); + virtual void closeEvent(QCloseEvent* e); -private: - bool handleImmediateResult(QDialog::DialogCode &result); + private: + bool handleImmediateResult(QDialog::DialogCode& result); void addTaskProgress(TaskStepProgress const& progress); -private: - Ui::ProgressDialog *ui; + private: + Ui::ProgressDialog* ui; - Task *task; + Task* m_task; bool m_is_multi_step = false; QHash taskProgress; - - }; - diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index b17eced35..4b82c0c5c 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -2,7 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2023 TheKodeToad * * 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 @@ -209,15 +209,17 @@ bool ResourceDownloadDialog::selectPage(QString pageId) return m_container->selectPage(pageId); } -ResourcePage* ResourceDownloadDialog::getSelectedPage() +ResourcePage* ResourceDownloadDialog::selectedPage() { - return m_selectedPage; + ResourcePage* result = dynamic_cast(m_container->selectedPage()); + Q_ASSERT(result != nullptr); + return result; } void ResourceDownloadDialog::addResource(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& ver) { removeResource(pack->name); - m_selectedPage->addResourceToPage(pack, ver, getBaseModel()); + selectedPage()->addResourceToPage(pack, ver, getBaseModel()); setButtonStatus(); } @@ -257,14 +259,8 @@ void ResourceDownloadDialog::selectedPageChanged(BasePage* previous, BasePage* s return; } - m_selectedPage = dynamic_cast(selected); - if (!m_selectedPage) { - qCritical() << "Page '" << selected->displayName() << "' in ResourceDownloadDialog is not a ResourcePage!"; - return; - } - // Same effect as having a global search bar - m_selectedPage->setSearchTerm(prev_page->getSearchTerm()); + selectedPage()->setSearchTerm(prev_page->getSearchTerm()); } ModDownloadDialog::ModDownloadDialog(QWidget* parent, const std::shared_ptr& mods, BaseInstance* instance) @@ -290,8 +286,6 @@ QList ModDownloadDialog::getPages() if (APPLICATION->capabilities() & Application::SupportsFlame && FlameAPI::validateModLoaders(loaders)) pages.append(FlameModPage::create(this, *m_instance)); - m_selectedPage = dynamic_cast(pages[0]); - return pages; } @@ -306,7 +300,7 @@ GetModDependenciesTask::Ptr ModDownloadDialog::getModDependenciesTask() return makeShared(this, m_instance, model, selectedVers); } return nullptr; -}; +} ResourcePackDownloadDialog::ResourcePackDownloadDialog(QWidget* parent, const std::shared_ptr& resource_packs, @@ -330,8 +324,6 @@ QList ResourcePackDownloadDialog::getPages() if (APPLICATION->capabilities() & Application::SupportsFlame) pages.append(FlameResourcePackPage::create(this, *m_instance)); - m_selectedPage = dynamic_cast(pages[0]); - return pages; } @@ -357,8 +349,6 @@ QList TexturePackDownloadDialog::getPages() if (APPLICATION->capabilities() & Application::SupportsFlame) pages.append(FlameTexturePackPage::create(this, *m_instance)); - m_selectedPage = dynamic_cast(pages[0]); - return pages; } @@ -379,11 +369,7 @@ ShaderPackDownloadDialog::ShaderPackDownloadDialog(QWidget* parent, QList ShaderPackDownloadDialog::getPages() { QList pages; - pages.append(ModrinthShaderPackPage::create(this, *m_instance)); - - m_selectedPage = dynamic_cast(pages[0]); - return pages; } diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.h b/launcher/ui/dialogs/ResourceDownloadDialog.h index f65daaa3f..e9d2cfbe6 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.h +++ b/launcher/ui/dialogs/ResourceDownloadDialog.h @@ -2,7 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2023 TheKodeToad * * 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 @@ -61,7 +61,7 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { QString dialogTitle() override { return tr("Download %1").arg(resourcesString()); }; bool selectPage(QString pageId); - ResourcePage* getSelectedPage(); + ResourcePage* selectedPage(); void addResource(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&); void removeResource(const QString&); @@ -88,7 +88,6 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { const std::shared_ptr m_base_model; PageContainer* m_container = nullptr; - ResourcePage* m_selectedPage = nullptr; QDialogButtonBox m_buttons; QVBoxLayout m_vertical_layout; diff --git a/launcher/ui/dialogs/ReviewMessageBox.cpp b/launcher/ui/dialogs/ReviewMessageBox.cpp index 7b33765fd..78c2542fd 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.cpp +++ b/launcher/ui/dialogs/ReviewMessageBox.cpp @@ -5,7 +5,7 @@ #include -ReviewMessageBox::ReviewMessageBox(QWidget* parent, QString const& title, QString const& icon) +ReviewMessageBox::ReviewMessageBox(QWidget* parent, [[maybe_unused]] QString const& title, [[maybe_unused]] QString const& icon) : QDialog(parent), ui(new Ui::ReviewMessageBox) { ui->setupUi(this); diff --git a/launcher/ui/dialogs/ScrollMessageBox.cpp b/launcher/ui/dialogs/ScrollMessageBox.cpp index afdc4bae1..c04d87842 100644 --- a/launcher/ui/dialogs/ScrollMessageBox.cpp +++ b/launcher/ui/dialogs/ScrollMessageBox.cpp @@ -1,15 +1,16 @@ #include "ScrollMessageBox.h" #include "ui_ScrollMessageBox.h" - -ScrollMessageBox::ScrollMessageBox(QWidget *parent, const QString &title, const QString &text, const QString &body) : - QDialog(parent), ui(new Ui::ScrollMessageBox) { +ScrollMessageBox::ScrollMessageBox(QWidget* parent, const QString& title, const QString& text, const QString& body) + : QDialog(parent), ui(new Ui::ScrollMessageBox) +{ ui->setupUi(this); this->setWindowTitle(title); ui->label->setText(text); ui->textBrowser->setText(body); } -ScrollMessageBox::~ScrollMessageBox() { +ScrollMessageBox::~ScrollMessageBox() +{ delete ui; } diff --git a/launcher/ui/dialogs/ScrollMessageBox.h b/launcher/ui/dialogs/ScrollMessageBox.h index 84aa253a9..8fd6769c4 100644 --- a/launcher/ui/dialogs/ScrollMessageBox.h +++ b/launcher/ui/dialogs/ScrollMessageBox.h @@ -2,19 +2,20 @@ #include - QT_BEGIN_NAMESPACE -namespace Ui { class ScrollMessageBox; } +namespace Ui { +class ScrollMessageBox; +} QT_END_NAMESPACE class ScrollMessageBox : public QDialog { -Q_OBJECT + Q_OBJECT -public: - ScrollMessageBox(QWidget *parent, const QString &title, const QString &text, const QString &body); + public: + ScrollMessageBox(QWidget* parent, const QString& title, const QString& text, const QString& body); ~ScrollMessageBox() override; -private: - Ui::ScrollMessageBox *ui; + private: + Ui::ScrollMessageBox* ui; }; diff --git a/launcher/ui/dialogs/SkinUploadDialog.cpp b/launcher/ui/dialogs/SkinUploadDialog.cpp index 8180ac1f0..5b3ebfa23 100644 --- a/launcher/ui/dialogs/SkinUploadDialog.cpp +++ b/launcher/ui/dialogs/SkinUploadDialog.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -33,20 +33,20 @@ * limitations under the License. */ -#include #include +#include #include #include -#include #include +#include #include +#include "CustomMessageBox.h" +#include "ProgressDialog.h" #include "SkinUploadDialog.h" #include "ui_SkinUploadDialog.h" -#include "ProgressDialog.h" -#include "CustomMessageBox.h" void SkinUploadDialog::on_buttonBox_rejected() { @@ -64,71 +64,51 @@ void SkinUploadDialog::on_buttonBox_accepted() QRegularExpression urlPrefixMatcher(QRegularExpression::anchoredPattern("^([a-z]+)://.+$")); bool isLocalFile = false; // it has an URL prefix -> it is an URL - if(urlPrefixMatcher.match(input).hasMatch()) - { + if (urlPrefixMatcher.match(input).hasMatch()) { QUrl fileURL = input; - if(fileURL.isValid()) - { + if (fileURL.isValid()) { // local? - if(fileURL.isLocalFile()) - { + if (fileURL.isLocalFile()) { isLocalFile = true; fileName = fileURL.toLocalFile(); - } - else - { - CustomMessageBox::selectable( - this, - tr("Skin Upload"), - tr("Using remote URLs for setting skins is not implemented yet."), - QMessageBox::Warning - )->exec(); + } else { + CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Using remote URLs for setting skins is not implemented yet."), + QMessageBox::Warning) + ->exec(); close(); return; } - } - else - { - CustomMessageBox::selectable( - this, - tr("Skin Upload"), - tr("You cannot use an invalid URL for uploading skins."), - QMessageBox::Warning - )->exec(); + } else { + CustomMessageBox::selectable(this, tr("Skin Upload"), tr("You cannot use an invalid URL for uploading skins."), + QMessageBox::Warning) + ->exec(); close(); return; } - } - else - { + } else { // just assume it's a path then isLocalFile = true; fileName = ui->skinPathTextBox->text(); } - if (isLocalFile && !QFile::exists(fileName)) - { + if (isLocalFile && !QFile::exists(fileName)) { CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Skin file does not exist!"), QMessageBox::Warning)->exec(); close(); return; } SkinUpload::Model model = SkinUpload::STEVE; - if (ui->steveBtn->isChecked()) - { + if (ui->steveBtn->isChecked()) { model = SkinUpload::STEVE; - } - else if (ui->alexBtn->isChecked()) - { + } else if (ui->alexBtn->isChecked()) { model = SkinUpload::ALEX; } skinUpload.addTask(shared_qobject_ptr(new SkinUpload(this, m_acct->accessToken(), FS::read(fileName), model))); } auto selectedCape = ui->capeCombo->currentData().toString(); - if(selectedCape != m_acct->accountData()->minecraftProfile.currentCape) { + if (selectedCape != m_acct->accountData()->minecraftProfile.currentCape) { skinUpload.addTask(shared_qobject_ptr(new CapeChange(this, m_acct->accessToken(), selectedCape))); } - if (prog.execWithTask(&skinUpload) != QDialog::Accepted) - { + if (prog.execWithTask(&skinUpload) != QDialog::Accepted) { CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Failed to upload skin!"), QMessageBox::Warning)->exec(); close(); return; @@ -141,45 +121,43 @@ void SkinUploadDialog::on_skinBrowseBtn_clicked() { auto filter = QMimeDatabase().mimeTypeForName("image/png").filterString(); QString raw_path = QFileDialog::getOpenFileName(this, tr("Select Skin Texture"), QString(), filter); - if (raw_path.isEmpty() || !QFileInfo::exists(raw_path)) - { + if (raw_path.isEmpty() || !QFileInfo::exists(raw_path)) { return; } QString cooked_path = FS::NormalizePath(raw_path); ui->skinPathTextBox->setText(cooked_path); } -SkinUploadDialog::SkinUploadDialog(MinecraftAccountPtr acct, QWidget *parent) - :QDialog(parent), m_acct(acct), ui(new Ui::SkinUploadDialog) +SkinUploadDialog::SkinUploadDialog(MinecraftAccountPtr acct, QWidget* parent) : QDialog(parent), m_acct(acct), ui(new Ui::SkinUploadDialog) { ui->setupUi(this); // FIXME: add a model for this, download/refresh the capes on demand - auto &data = *acct->accountData(); + auto& accountData = *acct->accountData(); int index = 0; ui->capeCombo->addItem(tr("No Cape"), QVariant()); - auto currentCape = data.minecraftProfile.currentCape; - if(currentCape.isEmpty()) { + auto currentCape = accountData.minecraftProfile.currentCape; + if (currentCape.isEmpty()) { ui->capeCombo->setCurrentIndex(index); } - for(auto & cape: data.minecraftProfile.capes) { + for (auto& cape : accountData.minecraftProfile.capes) { index++; - if(cape.data.size()) { + if (cape.data.size()) { QPixmap capeImage; - if(capeImage.loadFromData(cape.data, "PNG")) { + if (capeImage.loadFromData(cape.data, "PNG")) { QPixmap preview = QPixmap(10, 16); QPainter painter(&preview); painter.drawPixmap(0, 0, capeImage.copy(1, 1, 10, 16)); ui->capeCombo->addItem(capeImage, cape.alias, cape.id); - if(currentCape == cape.id) { + if (currentCape == cape.id) { ui->capeCombo->setCurrentIndex(index); } continue; } } ui->capeCombo->addItem(cape.alias, cape.id); - if(currentCape == cape.id) { + if (currentCape == cape.id) { ui->capeCombo->setCurrentIndex(index); } } diff --git a/launcher/ui/dialogs/SkinUploadDialog.h b/launcher/ui/dialogs/SkinUploadDialog.h index 84d17dc6f..81d6140cc 100644 --- a/launcher/ui/dialogs/SkinUploadDialog.h +++ b/launcher/ui/dialogs/SkinUploadDialog.h @@ -1,29 +1,28 @@ #pragma once -#include #include +#include -namespace Ui -{ - class SkinUploadDialog; +namespace Ui { +class SkinUploadDialog; } class SkinUploadDialog : public QDialog { Q_OBJECT -public: - explicit SkinUploadDialog(MinecraftAccountPtr acct, QWidget *parent = 0); - virtual ~SkinUploadDialog() {}; + public: + explicit SkinUploadDialog(MinecraftAccountPtr acct, QWidget* parent = 0); + virtual ~SkinUploadDialog(){}; -public slots: + public slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); void on_skinBrowseBtn_clicked(); -protected: + protected: MinecraftAccountPtr m_acct; -private: - Ui::SkinUploadDialog *ui; + private: + Ui::SkinUploadDialog* ui; }; diff --git a/launcher/ui/dialogs/SkinUploadDialog.ui b/launcher/ui/dialogs/SkinUploadDialog.ui index c7b166455..2c81a7fed 100644 --- a/launcher/ui/dialogs/SkinUploadDialog.ui +++ b/launcher/ui/dialogs/SkinUploadDialog.ui @@ -42,7 +42,7 @@ - ... + Browse diff --git a/launcher/ui/dialogs/VersionSelectDialog.cpp b/launcher/ui/dialogs/VersionSelectDialog.cpp index 5feb70d20..c61d10578 100644 --- a/launcher/ui/dialogs/VersionSelectDialog.cpp +++ b/launcher/ui/dialogs/VersionSelectDialog.cpp @@ -35,27 +35,26 @@ #include "VersionSelectDialog.h" +#include #include #include #include #include #include -#include #include "ui/widgets/VersionSelectWidget.h" #include "BaseVersion.h" #include "BaseVersionList.h" -VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent, bool cancelable) - : QDialog(parent) +VersionSelectDialog::VersionSelectDialog(BaseVersionList* vlist, QString title, QWidget* parent, bool cancelable) : QDialog(parent) { setObjectName(QStringLiteral("VersionSelectDialog")); resize(400, 347); m_verticalLayout = new QVBoxLayout(this); m_verticalLayout->setObjectName(QStringLiteral("verticalLayout")); - m_versionWidget = new VersionSelectWidget(true, parent); + m_versionWidget = new VersionSelectWidget(parent); m_verticalLayout->addWidget(m_versionWidget); m_horizontalLayout = new QHBoxLayout(); @@ -68,15 +67,16 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, m_buttonBox = new QDialogButtonBox(this); m_buttonBox->setObjectName(QStringLiteral("buttonBox")); m_buttonBox->setOrientation(Qt::Horizontal); - m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); m_horizontalLayout->addWidget(m_buttonBox); m_verticalLayout->addLayout(m_horizontalLayout); retranslate(); - QObject::connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(accept())); - QObject::connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(m_versionWidget->view(), &QAbstractItemView::doubleClicked, this, &QDialog::accept); + connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); QMetaObject::connectSlotsByName(this); setWindowModality(Qt::WindowModal); @@ -84,8 +84,7 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, m_vlist = vlist; - if (!cancelable) - { + if (!cancelable) { m_buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); } } @@ -123,8 +122,8 @@ int VersionSelectDialog::exec() { QDialog::open(); m_versionWidget->initialize(m_vlist); - if(resizeOnColumn != -1) - { + m_versionWidget->selectSearch(); + if (resizeOnColumn != -1) { m_versionWidget->setResizeOn(resizeOnColumn); } return QDialog::exec(); @@ -150,6 +149,11 @@ void VersionSelectDialog::setExactFilter(BaseVersionList::ModelRoles role, QStri m_versionWidget->setExactFilter(role, filter); } +void VersionSelectDialog::setExactIfPresentFilter(BaseVersionList::ModelRoles role, QString filter) +{ + m_versionWidget->setExactIfPresentFilter(role, filter); +} + void VersionSelectDialog::setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter) { m_versionWidget->setFuzzyFilter(role, filter); diff --git a/launcher/ui/dialogs/VersionSelectDialog.h b/launcher/ui/dialogs/VersionSelectDialog.h index 18a50cdb0..0ccd45e74 100644 --- a/launcher/ui/dialogs/VersionSelectDialog.h +++ b/launcher/ui/dialogs/VersionSelectDialog.h @@ -18,7 +18,6 @@ #include #include - #include "BaseVersionList.h" class QVBoxLayout; @@ -27,52 +26,51 @@ class QDialogButtonBox; class VersionSelectWidget; class QPushButton; -namespace Ui -{ +namespace Ui { class VersionSelectDialog; } class VersionProxyModel; -class VersionSelectDialog : public QDialog -{ +class VersionSelectDialog : public QDialog { Q_OBJECT -public: - explicit VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent = 0, bool cancelable = true); - virtual ~VersionSelectDialog() {}; + public: + explicit VersionSelectDialog(BaseVersionList* vlist, QString title, QWidget* parent = 0, bool cancelable = true); + virtual ~VersionSelectDialog(){}; int exec() override; BaseVersion::Ptr selectedVersion() const; - void setCurrentVersion(const QString & version); + void setCurrentVersion(const QString& version); void setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter); void setExactFilter(BaseVersionList::ModelRoles role, QString filter); + void setExactIfPresentFilter(BaseVersionList::ModelRoles role, QString filter); void setEmptyString(QString emptyString); void setEmptyErrorString(QString emptyErrorString); void setResizeOn(int column); -private slots: + private slots: void on_refreshButton_clicked(); -private: + private: void retranslate(); void selectRecommended(); -private: + private: QString m_currentVersion; - VersionSelectWidget *m_versionWidget = nullptr; - QVBoxLayout *m_verticalLayout = nullptr; - QHBoxLayout *m_horizontalLayout = nullptr; - QPushButton *m_refreshButton = nullptr; - QDialogButtonBox *m_buttonBox = nullptr; + VersionSelectWidget* m_versionWidget = nullptr; + QVBoxLayout* m_verticalLayout = nullptr; + QHBoxLayout* m_horizontalLayout = nullptr; + QPushButton* m_refreshButton = nullptr; + QDialogButtonBox* m_buttonBox = nullptr; - BaseVersionList *m_vlist = nullptr; + BaseVersionList* m_vlist = nullptr; - VersionProxyModel *m_proxyModel = nullptr; + VersionProxyModel* m_proxyModel = nullptr; int resizeOnColumn = -1; - Task * loadTask = nullptr; + Task* loadTask = nullptr; }; diff --git a/launcher/ui/instanceview/AccessibleInstanceView.cpp b/launcher/ui/instanceview/AccessibleInstanceView.cpp index 2e7b83000..c99fe541a 100644 --- a/launcher/ui/instanceview/AccessibleInstanceView.cpp +++ b/launcher/ui/instanceview/AccessibleInstanceView.cpp @@ -1,42 +1,40 @@ -#include "InstanceView.h" #include "AccessibleInstanceView.h" #include "AccessibleInstanceView_p.h" +#include "InstanceView.h" -#include #include #include +#include #ifndef QT_NO_ACCESSIBILITY -QAccessibleInterface *groupViewAccessibleFactory(const QString &classname, QObject *object) +QAccessibleInterface* groupViewAccessibleFactory(const QString& classname, QObject* object) { - QAccessibleInterface *iface = 0; + QAccessibleInterface* iface = 0; if (!object || !object->isWidgetType()) return iface; - QWidget *widget = static_cast(object); + QWidget* widget = static_cast(object); if (classname == QLatin1String("InstanceView")) { - iface = new AccessibleInstanceView((InstanceView *)widget); + iface = new AccessibleInstanceView((InstanceView*)widget); } return iface; } - -QAbstractItemView *AccessibleInstanceView::view() const +QAbstractItemView* AccessibleInstanceView::view() const { return qobject_cast(object()); } -int AccessibleInstanceView::logicalIndex(const QModelIndex &index) const +int AccessibleInstanceView::logicalIndex(const QModelIndex& index) const { if (!view()->model() || !index.isValid()) return -1; return index.row() * (index.model()->columnCount()) + index.column(); } -AccessibleInstanceView::AccessibleInstanceView(QWidget *w) - : QAccessibleObject(w) +AccessibleInstanceView::AccessibleInstanceView(QWidget* w) : QAccessibleObject(w) { Q_ASSERT(view()); } @@ -53,7 +51,7 @@ AccessibleInstanceView::~AccessibleInstanceView() } } -QAccessibleInterface *AccessibleInstanceView::cellAt(int row, int column) const +QAccessibleInterface* AccessibleInstanceView::cellAt(int row, int column) const { if (!view()->model()) { return 0; @@ -68,7 +66,7 @@ QAccessibleInterface *AccessibleInstanceView::cellAt(int row, int column) const return child(logicalIndex(index)); } -QAccessibleInterface *AccessibleInstanceView::caption() const +QAccessibleInterface* AccessibleInstanceView::caption() const { return 0; } @@ -123,14 +121,14 @@ QString AccessibleInstanceView::rowDescription(int row) const return view()->model()->headerData(row, Qt::Vertical).toString(); } -QList AccessibleInstanceView::selectedCells() const +QList AccessibleInstanceView::selectedCells() const { QList cells; if (!view()->selectionModel()) return cells; const QModelIndexList selectedIndexes = view()->selectionModel()->selectedIndexes(); cells.reserve(selectedIndexes.size()); - for (const QModelIndex &index : selectedIndexes) + for (const QModelIndex& index : selectedIndexes) cells.append(child(logicalIndex(index))); return cells; } @@ -145,7 +143,7 @@ QList AccessibleInstanceView::selectedColumns() const QList columns; columns.reserve(selectedColumns.size()); - for (const QModelIndex &index : selectedColumns) { + for (const QModelIndex& index : selectedColumns) { columns.append(index.column()); } @@ -163,14 +161,14 @@ QList AccessibleInstanceView::selectedRows() const const QModelIndexList selectedRows = view()->selectionModel()->selectedRows(); rows.reserve(selectedRows.size()); - for (const QModelIndex &index : selectedRows) { + for (const QModelIndex& index : selectedRows) { rows.append(index.row()); } return rows; } -QAccessibleInterface *AccessibleInstanceView::summary() const +QAccessibleInterface* AccessibleInstanceView::summary() const { return 0; } @@ -209,16 +207,17 @@ bool AccessibleInstanceView::selectRow(int row) return false; } case QAbstractItemView::SingleSelection: { - if (view()->selectionBehavior() != QAbstractItemView::SelectRows && columnCount() > 1 ) + if (view()->selectionBehavior() != QAbstractItemView::SelectRows && columnCount() > 1) return false; view()->clearSelection(); break; } case QAbstractItemView::ContiguousSelection: { - if ((!row || !view()->selectionModel()->isRowSelected(row - 1, view()->rootIndex())) && !view()->selectionModel()->isRowSelected(row + 1, view()->rootIndex())) { + if ((!row || !view()->selectionModel()->isRowSelected(row - 1, view()->rootIndex())) && + !view()->selectionModel()->isRowSelected(row + 1, view()->rootIndex())) { view()->clearSelection(); } - break; + break; } default: { break; @@ -251,7 +250,8 @@ bool AccessibleInstanceView::selectColumn(int column) } /* fallthrough */ case QAbstractItemView::ContiguousSelection: { - if ((!column || !view()->selectionModel()->isColumnSelected(column - 1, view()->rootIndex())) && !view()->selectionModel()->isColumnSelected(column + 1, view()->rootIndex())) { + if ((!column || !view()->selectionModel()->isColumnSelected(column - 1, view()->rootIndex())) && + !view()->selectionModel()->isColumnSelected(column + 1, view()->rootIndex())) { view()->clearSelection(); } break; @@ -292,10 +292,10 @@ bool AccessibleInstanceView::unselectRow(int row) return false; } - - if ((!row || selectionModel->isRowSelected(row - 1, view()->rootIndex())) && selectionModel->isRowSelected(row + 1, view()->rootIndex())) { - //If there are rows selected both up the current row and down the current rown, - //the ones which are down the current row will be deselected + if ((!row || selectionModel->isRowSelected(row - 1, view()->rootIndex())) && + selectionModel->isRowSelected(row + 1, view()->rootIndex())) { + // If there are rows selected both up the current row and down the current rown, + // the ones which are down the current row will be deselected selection = QItemSelection(index, view()->model()->index(rowCount() - 1, 0, view()->rootIndex())); } } @@ -324,8 +324,8 @@ bool AccessibleInstanceView::unselectColumn(int column) switch (view()->selectionMode()) { case QAbstractItemView::SingleSelection: { - //In SingleSelection and ContiguousSelection once an item - //is selected, there's no way for the user to unselect all items + // In SingleSelection and ContiguousSelection once an item + // is selected, there's no way for the user to unselect all items if (selectedColumnCount() == 1) { return false; } @@ -336,10 +336,10 @@ bool AccessibleInstanceView::unselectColumn(int column) return false; } - if ((!column || view()->selectionModel()->isColumnSelected(column - 1, view()->rootIndex())) - && view()->selectionModel()->isColumnSelected(column + 1, view()->rootIndex())) { - //If there are columns selected both at the left of the current row and at the right - //of the current row, the ones which are at the right will be deselected + if ((!column || view()->selectionModel()->isColumnSelected(column - 1, view()->rootIndex())) && + view()->selectionModel()->isColumnSelected(column + 1, view()->rootIndex())) { + // If there are columns selected both at the left of the current row and at the right + // of the current row, the ones which are at the right will be deselected selection = QItemSelection(index, model->index(0, columnCount() - 1, view()->rootIndex())); } default: @@ -360,9 +360,9 @@ QAccessible::State AccessibleInstanceView::state() const return QAccessible::State(); } -QAccessibleInterface *AccessibleInstanceView::childAt(int x, int y) const +QAccessibleInterface* AccessibleInstanceView::childAt(int x, int y) const { - QPoint viewportOffset = view()->viewport()->mapTo(view(), QPoint(0,0)); + QPoint viewportOffset = view()->viewport()->mapTo(view(), QPoint(0, 0)); QPoint indexPosition = view()->mapFromGlobal(QPoint(x, y) - viewportOffset); // FIXME: if indexPosition < 0 in one coordinate, return header @@ -381,22 +381,23 @@ int AccessibleInstanceView::childCount() const return (view()->model()->rowCount()) * (view()->model()->columnCount()); } -int AccessibleInstanceView::indexOfChild(const QAccessibleInterface *iface) const +int AccessibleInstanceView::indexOfChild(const QAccessibleInterface* iface) const { if (!view()->model()) return -1; - QAccessibleInterface *parent = iface->parent(); + QAccessibleInterface* parent = iface->parent(); if (parent->object() != view()) return -1; - Q_ASSERT(iface->role() != QAccessible::TreeItem); // should be handled by tree class + Q_ASSERT(iface->role() != QAccessible::TreeItem); // should be handled by tree class if (iface->role() == QAccessible::Cell || iface->role() == QAccessible::ListItem) { const AccessibleInstanceViewItem* cell = static_cast(iface); return logicalIndex(cell->m_index); } else if (iface->role() == QAccessible::Pane) { - return 0; // corner button + return 0; // corner button } else { - qWarning() << "AccessibleInstanceView::indexOfChild has a child with unknown role..." << iface->role() << iface->text(QAccessible::Name); + qWarning() << "AccessibleInstanceView::indexOfChild has a child with unknown role..." << iface->role() + << iface->text(QAccessible::Name); } // FIXME: we are in denial of our children. this should stop. return -1; @@ -417,7 +418,7 @@ QRect AccessibleInstanceView::rect() const return QRect(pos.x(), pos.y(), view()->width(), view()->height()); } -QAccessibleInterface *AccessibleInstanceView::parent() const +QAccessibleInterface* AccessibleInstanceView::parent() const { if (view() && view()->parent()) { if (qstrcmp("QComboBoxPrivateContainer", view()->parent()->metaObject()->className()) == 0) { @@ -428,7 +429,7 @@ QAccessibleInterface *AccessibleInstanceView::parent() const return 0; } -QAccessibleInterface *AccessibleInstanceView::child(int logicalIndex) const +QAccessibleInterface* AccessibleInstanceView::child(int logicalIndex) const { if (!view()->model()) return 0; @@ -442,7 +443,7 @@ QAccessibleInterface *AccessibleInstanceView::child(int logicalIndex) const int row = logicalIndex / columns; int column = logicalIndex % columns; - QAccessibleInterface *iface = 0; + QAccessibleInterface* iface = 0; QModelIndex index = view()->model()->index(row, column, view()->rootIndex()); if (Q_UNLIKELY(!index.isValid())) { @@ -456,14 +457,14 @@ QAccessibleInterface *AccessibleInstanceView::child(int logicalIndex) const return iface; } -void *AccessibleInstanceView::interface_cast(QAccessible::InterfaceType t) +void* AccessibleInstanceView::interface_cast(QAccessible::InterfaceType t) { if (t == QAccessible::TableInterface) - return static_cast(this); - return 0; + return static_cast(this); + return 0; } -void AccessibleInstanceView::modelChange(QAccessibleTableModelChangeEvent *event) +void AccessibleInstanceView::modelChange(QAccessibleTableModelChangeEvent* event) { // if there is no cache yet, we don't update anything if (childToId.isEmpty()) @@ -479,13 +480,12 @@ void AccessibleInstanceView::modelChange(QAccessibleTableModelChangeEvent *event // rows are inserted: move every row after that case QAccessibleTableModelChangeEvent::RowsInserted: case QAccessibleTableModelChangeEvent::ColumnsInserted: { - ChildCache newCache; ChildCache::ConstIterator iter = childToId.constBegin(); while (iter != childToId.constEnd()) { QAccessible::Id id = iter.value(); - QAccessibleInterface *iface = QAccessible::accessibleInterface(id); + QAccessibleInterface* iface = QAccessible::accessibleInterface(id); Q_ASSERT(iface); if (indexOfChild(iface) >= 0) { newCache.insert(indexOfChild(iface), id); @@ -507,11 +507,11 @@ void AccessibleInstanceView::modelChange(QAccessibleTableModelChangeEvent *event ChildCache::ConstIterator iter = childToId.constBegin(); while (iter != childToId.constEnd()) { QAccessible::Id id = iter.value(); - QAccessibleInterface *iface = QAccessible::accessibleInterface(id); + QAccessibleInterface* iface = QAccessible::accessibleInterface(id); Q_ASSERT(iface); if (iface->role() == QAccessible::Cell || iface->role() == QAccessible::ListItem) { Q_ASSERT(iface->tableCellInterface()); - AccessibleInstanceViewItem *cell = static_cast(iface->tableCellInterface()); + AccessibleInstanceViewItem* cell = static_cast(iface->tableCellInterface()); // Since it is a QPersistentModelIndex, we only need to check if it is valid if (cell->m_index.isValid()) newCache.insert(indexOfChild(cell), id); @@ -532,14 +532,13 @@ void AccessibleInstanceView::modelChange(QAccessibleTableModelChangeEvent *event // TABLE CELL -AccessibleInstanceViewItem::AccessibleInstanceViewItem(QAbstractItemView *view_, const QModelIndex &index_) - : view(view_), m_index(index_) +AccessibleInstanceViewItem::AccessibleInstanceViewItem(QAbstractItemView* view_, const QModelIndex& index_) : view(view_), m_index(index_) { if (Q_UNLIKELY(!index_.isValid())) qWarning() << "AccessibleInstanceViewItem::AccessibleInstanceViewItem with invalid index: " << index_; } -void *AccessibleInstanceViewItem::interface_cast(QAccessible::InterfaceType t) +void* AccessibleInstanceViewItem::interface_cast(QAccessible::InterfaceType t) { if (t == QAccessible::TableCellInterface) return static_cast(this); @@ -548,8 +547,14 @@ void *AccessibleInstanceViewItem::interface_cast(QAccessible::InterfaceType t) return 0; } -int AccessibleInstanceViewItem::columnExtent() const { return 1; } -int AccessibleInstanceViewItem::rowExtent() const { return 1; } +int AccessibleInstanceViewItem::columnExtent() const +{ + return 1; +} +int AccessibleInstanceViewItem::rowExtent() const +{ + return 1; +} QList AccessibleInstanceViewItem::rowHeaderCells() const { @@ -600,19 +605,17 @@ void AccessibleInstanceViewItem::doAction(const QString& actionName) if (actionName == toggleAction()) { if (isSelected()) { unselectCell(); - } - else { + } else { selectCell(); } } } -QStringList AccessibleInstanceViewItem::keyBindingsForAction(const QString &) const +QStringList AccessibleInstanceViewItem::keyBindingsForAction(const QString&) const { return QStringList(); } - void AccessibleInstanceViewItem::selectCell() { if (!isValid()) { @@ -624,7 +627,7 @@ void AccessibleInstanceViewItem::selectCell() } Q_ASSERT(table()); - QAccessibleTableInterface *cellTable = table()->tableInterface(); + QAccessibleTableInterface* cellTable = table()->tableInterface(); switch (view->selectionBehavior()) { case QAbstractItemView::SelectItems: @@ -654,7 +657,7 @@ void AccessibleInstanceViewItem::unselectCell() if (selectionMode == QAbstractItemView::NoSelection) return; - QAccessibleTableInterface *cellTable = table()->tableInterface(); + QAccessibleTableInterface* cellTable = table()->tableInterface(); switch (view->selectionBehavior()) { case QAbstractItemView::SelectItems: @@ -669,15 +672,16 @@ void AccessibleInstanceViewItem::unselectCell() return; } - //If the mode is not MultiSelection or ExtendedSelection and only - //one cell is selected it cannot be unselected by the user - if ((selectionMode != QAbstractItemView::MultiSelection) && (selectionMode != QAbstractItemView::ExtendedSelection) && (view->selectionModel()->selectedIndexes().count() <= 1)) + // If the mode is not MultiSelection or ExtendedSelection and only + // one cell is selected it cannot be unselected by the user + if ((selectionMode != QAbstractItemView::MultiSelection) && (selectionMode != QAbstractItemView::ExtendedSelection) && + (view->selectionModel()->selectedIndexes().count() <= 1)) return; view->selectionModel()->select(m_index, QItemSelectionModel::Deselect); } -QAccessibleInterface *AccessibleInstanceViewItem::table() const +QAccessibleInterface* AccessibleInstanceViewItem::table() const { return QAccessible::queryAccessibleInterface(view); } @@ -694,7 +698,7 @@ QAccessible::State AccessibleInstanceViewItem::state() const return st; QRect globalRect = view->rect(); - globalRect.translate(view->mapToGlobal(QPoint(0,0))); + globalRect.translate(view->mapToGlobal(QPoint(0, 0))); if (!globalRect.intersects(rect())) st.invisible = true; @@ -717,7 +721,6 @@ QAccessible::State AccessibleInstanceViewItem::state() const return st; } - QRect AccessibleInstanceViewItem::rect() const { QRect r; @@ -726,7 +729,7 @@ QRect AccessibleInstanceViewItem::rect() const r = view->visualRect(m_index); if (!r.isNull()) { - r.translate(view->viewport()->mapTo(view, QPoint(0,0))); + r.translate(view->viewport()->mapTo(view, QPoint(0, 0))); r.translate(view->mapToGlobal(QPoint(0, 0))); } return r; @@ -737,7 +740,7 @@ QString AccessibleInstanceViewItem::text(QAccessible::Text t) const QString value; if (!isValid()) return value; - QAbstractItemModel *model = view->model(); + QAbstractItemModel* model = view->model(); switch (t) { case QAccessible::Name: value = model->data(m_index, Qt::AccessibleTextRole).toString(); @@ -753,7 +756,7 @@ QString AccessibleInstanceViewItem::text(QAccessible::Text t) const return value; } -void AccessibleInstanceViewItem::setText(QAccessible::Text /*t*/, const QString &text) +void AccessibleInstanceViewItem::setText(QAccessible::Text /*t*/, const QString& text) { if (!isValid() || !(m_index.flags() & Qt::ItemIsEditable)) return; @@ -765,12 +768,12 @@ bool AccessibleInstanceViewItem::isValid() const return view && view->model() && m_index.isValid(); } -QAccessibleInterface *AccessibleInstanceViewItem::parent() const +QAccessibleInterface* AccessibleInstanceViewItem::parent() const { return QAccessible::queryAccessibleInterface(view); } -QAccessibleInterface *AccessibleInstanceViewItem::child(int) const +QAccessibleInterface* AccessibleInstanceViewItem::child(int) const { return 0; } diff --git a/launcher/ui/instanceview/AccessibleInstanceView.h b/launcher/ui/instanceview/AccessibleInstanceView.h index 9bfd17453..19522805a 100644 --- a/launcher/ui/instanceview/AccessibleInstanceView.h +++ b/launcher/ui/instanceview/AccessibleInstanceView.h @@ -1,6 +1,7 @@ #pragma once +#include #include class QAccessibleInterface; -QAccessibleInterface *groupViewAccessibleFactory(const QString &classname, QObject *object); +QAccessibleInterface* groupViewAccessibleFactory(const QString& classname, QObject* object); diff --git a/launcher/ui/instanceview/AccessibleInstanceView_p.h b/launcher/ui/instanceview/AccessibleInstanceView_p.h index 26462f51e..e99f85069 100644 --- a/launcher/ui/instanceview/AccessibleInstanceView_p.h +++ b/launcher/ui/instanceview/AccessibleInstanceView_p.h @@ -1,9 +1,9 @@ #pragma once -#include "QtCore/qpointer.h" #include -#include #include +#include +#include "QtCore/qpointer.h" #ifndef QT_NO_ACCESSIBILITY #include "InstanceView.h" // #include @@ -11,10 +11,9 @@ class QAccessibleTableCell; class QAccessibleTableHeaderCell; -class AccessibleInstanceView :public QAccessibleTableInterface, public QAccessibleObject -{ -public: - explicit AccessibleInstanceView(QWidget *w); +class AccessibleInstanceView : public QAccessibleTableInterface, public QAccessibleObject { + public: + explicit AccessibleInstanceView(QWidget* w); bool isValid() const override; QAccessible::Role role() const override; @@ -22,19 +21,19 @@ public: QString text(QAccessible::Text t) const override; QRect rect() const override; - QAccessibleInterface *childAt(int x, int y) const override; + QAccessibleInterface* childAt(int x, int y) const override; int childCount() const override; - int indexOfChild(const QAccessibleInterface *) const override; + int indexOfChild(const QAccessibleInterface*) const override; - QAccessibleInterface *parent() const override; - QAccessibleInterface *child(int index) const override; + QAccessibleInterface* parent() const override; + QAccessibleInterface* child(int index) const override; - void *interface_cast(QAccessible::InterfaceType t) override; + void* interface_cast(QAccessible::InterfaceType t) override; // table interface - QAccessibleInterface *cellAt(int row, int column) const override; - QAccessibleInterface *caption() const override; - QAccessibleInterface *summary() const override; + QAccessibleInterface* cellAt(int row, int column) const override; + QAccessibleInterface* caption() const override; + QAccessibleInterface* summary() const override; QString columnDescription(int column) const override; QString rowDescription(int row) const override; int columnCount() const override; @@ -54,42 +53,41 @@ public: bool unselectRow(int row) override; bool unselectColumn(int column) override; - QAbstractItemView *view() const; + QAbstractItemView* view() const; - void modelChange(QAccessibleTableModelChangeEvent *event) override; + void modelChange(QAccessibleTableModelChangeEvent* event) override; -protected: + protected: // maybe vector typedef QHash ChildCache; mutable ChildCache childToId; virtual ~AccessibleInstanceView(); -private: - inline int logicalIndex(const QModelIndex &index) const; + private: + inline int logicalIndex(const QModelIndex& index) const; }; -class AccessibleInstanceViewItem: public QAccessibleInterface, public QAccessibleTableCellInterface, public QAccessibleActionInterface -{ -public: - AccessibleInstanceViewItem(QAbstractItemView *view, const QModelIndex &m_index); +class AccessibleInstanceViewItem : public QAccessibleInterface, public QAccessibleTableCellInterface, public QAccessibleActionInterface { + public: + AccessibleInstanceViewItem(QAbstractItemView* view, const QModelIndex& m_index); - void *interface_cast(QAccessible::InterfaceType t) override; - QObject *object() const override { return nullptr; } + void* interface_cast(QAccessible::InterfaceType t) override; + QObject* object() const override { return nullptr; } QAccessible::Role role() const override; QAccessible::State state() const override; QRect rect() const override; bool isValid() const override; - QAccessibleInterface *childAt(int, int) const override { return nullptr; } + QAccessibleInterface* childAt(int, int) const override { return nullptr; } int childCount() const override { return 0; } - int indexOfChild(const QAccessibleInterface *) const override { return -1; } + int indexOfChild(const QAccessibleInterface*) const override { return -1; } QString text(QAccessible::Text t) const override; - void setText(QAccessible::Text t, const QString &text) override; + void setText(QAccessible::Text t, const QString& text) override; - QAccessibleInterface *parent() const override; - QAccessibleInterface *child(int) const override; + QAccessibleInterface* parent() const override; + QAccessibleInterface* child(int) const override; // cell interface int columnExtent() const override; @@ -101,13 +99,13 @@ public: bool isSelected() const override; QAccessibleInterface* table() const override; - //action interface + // action interface QStringList actionNames() const override; - void doAction(const QString &actionName) override; - QStringList keyBindingsForAction(const QString &actionName) const override; + void doAction(const QString& actionName) override; + QStringList keyBindingsForAction(const QString& actionName) const override; -private: - QPointer view; + private: + QPointer view; QPersistentModelIndex m_index; void selectCell(); diff --git a/launcher/ui/instanceview/InstanceDelegate.cpp b/launcher/ui/instanceview/InstanceDelegate.cpp index 137cc8d58..d947163bc 100644 --- a/launcher/ui/instanceview/InstanceDelegate.cpp +++ b/launcher/ui/instanceview/InstanceDelegate.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -34,29 +34,27 @@ */ #include "InstanceDelegate.h" -#include -#include -#include #include -#include #include +#include +#include +#include +#include -#include "InstanceView.h" -#include "BaseInstance.h" -#include "InstanceList.h" #include #include +#include "BaseInstance.h" +#include "InstanceList.h" +#include "InstanceView.h" // Origin: Qt -static void viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height, - qreal &widthUsed) +static void viewItemTextLayout(QTextLayout& textLayout, int lineWidth, qreal& height, qreal& widthUsed) { height = 0; widthUsed = 0; textLayout.beginLayout(); QString str = textLayout.text(); - while (true) - { + while (true) { QTextLine line = textLayout.createLine(); if (!line.isValid()) break; @@ -70,24 +68,20 @@ static void viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &he textLayout.endLayout(); } -ListViewDelegate::ListViewDelegate(QObject *parent) : QStyledItemDelegate(parent) -{ -} +ListViewDelegate::ListViewDelegate(QObject* parent) : QStyledItemDelegate(parent) {} -void drawSelectionRect(QPainter *painter, const QStyleOptionViewItem &option, - const QRect &rect) +void drawSelectionRect(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect) { if ((option.state & QStyle::State_Selected)) painter->fillRect(rect, option.palette.brush(QPalette::Highlight)); - else - { + else { QColor backgroundColor = option.palette.color(QPalette::Window); backgroundColor.setAlpha(160); painter->fillRect(rect, QBrush(backgroundColor)); } } -void drawFocusRect(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect) +void drawFocusRect(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect) { if (!(option.state & QStyle::State_HasFocus)) return; @@ -103,7 +97,7 @@ void drawFocusRect(QPainter *painter, const QStyleOptionViewItem &option, const // Apparently some widget styles expect this hint to not be set painter->setRenderHint(QPainter::Antialiasing, false); - QStyle *style = option.widget ? option.widget->style() : QApplication::style(); + QStyle* style = option.widget ? option.widget->style() : QApplication::style(); style->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, painter, option.widget); @@ -111,11 +105,9 @@ void drawFocusRect(QPainter *painter, const QStyleOptionViewItem &option, const } // TODO this can be made a lot prettier -void drawProgressOverlay(QPainter *painter, const QStyleOptionViewItem &option, - const int value, const int maximum) +void drawProgressOverlay(QPainter* painter, const QStyleOptionViewItem& option, const int value, const int maximum) { - if (maximum == 0 || value == maximum) - { + if (maximum == 0 || value == maximum) { return; } @@ -131,19 +123,15 @@ void drawProgressOverlay(QPainter *painter, const QStyleOptionViewItem &option, painter->restore(); } -void drawBadges(QPainter *painter, const QStyleOptionViewItem &option, BaseInstance *instance, QIcon::Mode mode, QIcon::State state) +void drawBadges(QPainter* painter, const QStyleOptionViewItem& option, BaseInstance* instance, QIcon::Mode mode, QIcon::State state) { QList pixmaps; - if (instance->isRunning()) - { + if (instance->isRunning()) { pixmaps.append("status-running"); - } - else if (instance->hasCrashed() || instance->hasVersionBroken()) - { + } else if (instance->hasCrashed() || instance->hasVersionBroken()) { pixmaps.append("status-bad"); } - if (instance->hasUpdateAvailable()) - { + if (instance->hasUpdateAvailable()) { pixmaps.append("checkupdate"); } @@ -153,12 +141,9 @@ void drawBadges(QPainter *painter, const QStyleOptionViewItem &option, BaseInsta const int rows = qCeil((double)pixmaps.size() / (double)itemsPerRow); QListIterator it(pixmaps); painter->translate(option.rect.topLeft()); - for (int y = 0; y < rows; ++y) - { - for (int x = 0; x < itemsPerRow; ++x) - { - if (!it.hasNext()) - { + for (int y = 0; y < rows; ++y) { + for (int x = 0; x < itemsPerRow; ++x) { + if (!it.hasNext()) { return; } // FIXME: inject this. @@ -166,21 +151,17 @@ void drawBadges(QPainter *painter, const QStyleOptionViewItem &option, BaseInsta // opt.icon.paint(painter, iconbox, Qt::AlignCenter, mode, state); const QPixmap pixmap; // itemSide - QRect badgeRect( - option.rect.width() - x * itemSide + qMax(x - 1, 0) * spacing - itemSide, - y * itemSide + qMax(y - 1, 0) * spacing, - itemSide, - itemSide - ); + QRect badgeRect(option.rect.width() - x * itemSide + qMax(x - 1, 0) * spacing - itemSide, + y * itemSide + qMax(y - 1, 0) * spacing, itemSide, itemSide); icon.paint(painter, badgeRect, Qt::AlignCenter, mode, state); } } painter->translate(-option.rect.topLeft()); } -static QSize viewItemTextSize(const QStyleOptionViewItem *option) +static QSize viewItemTextSize(const QStyleOptionViewItem* option) { - QStyle *style = option->widget ? option->widget->style() : QApplication::style(); + QStyle* style = option->widget ? option->widget->style() : QApplication::style(); QTextOption textOption; textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); QTextLayout textLayout; @@ -195,8 +176,7 @@ static QSize viewItemTextSize(const QStyleOptionViewItem *option) return QSize(size.width() + 2 * textMargin, size.height()); } -void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const +void ListViewDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); @@ -208,7 +188,7 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti opt.textElideMode = Qt::ElideRight; opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; - QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); + QStyle* style = opt.widget ? opt.widget->style() : QApplication::style(); // const int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize); const int iconSize = 48; @@ -296,16 +276,12 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti opt.icon.paint(painter, iconbox, Qt::AlignCenter, mode, state); } // set the text colors - QPalette::ColorGroup cg = - opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; + QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) cg = QPalette::Inactive; - if (opt.state & QStyle::State_Selected) - { + if (opt.state & QStyle::State_Selected) { painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); - } - else - { + } else { painter->setPen(opt.palette.color(cg, QPalette::Text)); } @@ -324,20 +300,16 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti const int lineCount = textLayout.lineCount(); - const QRect layoutRect = QStyle::alignedRect( - opt.direction, opt.displayAlignment, QSize(textRect.width(), int(height)), textRect); + const QRect layoutRect = QStyle::alignedRect(opt.direction, opt.displayAlignment, QSize(textRect.width(), int(height)), textRect); const QPointF position = layoutRect.topLeft(); - for (int i = 0; i < lineCount; ++i) - { + for (int i = 0; i < lineCount; ++i) { const QTextLine line = textLayout.lineAt(i); line.draw(painter, position); } // FIXME: this really has no business of being here. Make generic. - auto instance = (BaseInstance*)index.data(InstanceList::InstancePointerRole) - .value(); - if (instance) - { + auto instance = (BaseInstance*)index.data(InstanceList::InstancePointerRole).value(); + if (instance) { drawBadges(painter, opt, instance, mode, state); } @@ -347,8 +319,7 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti painter->restore(); } -QSize ListViewDelegate::sizeHint(const QStyleOptionViewItem &option, - const QModelIndex &index) const +QSize ListViewDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); @@ -357,9 +328,9 @@ QSize ListViewDelegate::sizeHint(const QStyleOptionViewItem &option, opt.textElideMode = Qt::ElideRight; opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; - QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); + QStyle* style = opt.widget ? opt.widget->style() : QApplication::style(); const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, opt.widget) + 1; - int height = 48 + textMargin * 2 + 5; // TODO: turn constants into variables + int height = 48 + textMargin * 2 + 5; // TODO: turn constants into variables QSize szz = viewItemTextSize(&opt); height += szz.height(); // FIXME: maybe the icon items could scale and keep proportions? @@ -367,40 +338,38 @@ QSize ListViewDelegate::sizeHint(const QStyleOptionViewItem &option, return sz; } -class NoReturnTextEdit: public QTextEdit -{ +class NoReturnTextEdit : public QTextEdit { Q_OBJECT -public: - explicit NoReturnTextEdit(QWidget *parent) : QTextEdit(parent) + public: + explicit NoReturnTextEdit(QWidget* parent) : QTextEdit(parent) { setTextInteractionFlags(Qt::TextEditorInteraction); setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); } - bool event(QEvent * event) override + bool event(QEvent* event) override { auto eventType = event->type(); - if(eventType == QEvent::KeyPress || eventType == QEvent::KeyRelease) - { - QKeyEvent *keyEvent = static_cast(event); + if (eventType == QEvent::KeyPress || eventType == QEvent::KeyRelease) { + QKeyEvent* keyEvent = static_cast(event); auto key = keyEvent->key(); - if ((key == Qt::Key_Return || key == Qt::Key_Enter) && eventType == QEvent::KeyPress) - { + if ((key == Qt::Key_Return || key == Qt::Key_Enter) && eventType == QEvent::KeyPress) { emit editingDone(); return true; } - if(key == Qt::Key_Tab) - { + if (key == Qt::Key_Tab) { return true; } } return QTextEdit::event(event); } -signals: + signals: void editingDone(); }; -void ListViewDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const +void ListViewDelegate::updateEditorGeometry(QWidget* editor, + const QStyleOptionViewItem& option, + [[maybe_unused]] const QModelIndex& index) const { const int iconSize = 48; QRect textRect = option.rect; @@ -412,28 +381,29 @@ void ListViewDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionV void ListViewDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const { auto text = index.data(Qt::EditRole).toString(); - QTextEdit * realeditor = qobject_cast(editor); - realeditor->setAlignment(Qt::AlignHCenter | Qt::AlignTop); - realeditor->append(text); - realeditor->selectAll(); - realeditor->document()->clearUndoRedoStacks(); + QTextEdit* realEditor = qobject_cast(editor); + realEditor->setAlignment(Qt::AlignHCenter | Qt::AlignTop); + realEditor->append(text); + realEditor->selectAll(); + realEditor->document()->clearUndoRedoStacks(); } void ListViewDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { - QTextEdit * realeditor = qobject_cast(editor); - QString text = realeditor->toPlainText(); + QTextEdit* realEditor = qobject_cast(editor); + QString text = realEditor->toPlainText(); text.replace(QChar('\n'), QChar(' ')); text = text.trimmed(); // Prevent instance names longer than 128 chars text.truncate(128); - if(text.size() != 0) - { + if (text.size() != 0) { model->setData(index, text); } } -QWidget * ListViewDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const +QWidget* ListViewDelegate::createEditor(QWidget* parent, + [[maybe_unused]] const QStyleOptionViewItem& option, + [[maybe_unused]] const QModelIndex& index) const { auto editor = new NoReturnTextEdit(parent); connect(editor, &NoReturnTextEdit::editingDone, this, &ListViewDelegate::editingDone); @@ -442,7 +412,7 @@ QWidget * ListViewDelegate::createEditor(QWidget* parent, const QStyleOptionView void ListViewDelegate::editingDone() { - NoReturnTextEdit *editor = qobject_cast(sender()); + NoReturnTextEdit* editor = qobject_cast(sender()); emit commitData(editor); emit closeEditor(editor); } diff --git a/launcher/ui/instanceview/InstanceDelegate.h b/launcher/ui/instanceview/InstanceDelegate.h index d95279f32..69dd32ba7 100644 --- a/launcher/ui/instanceview/InstanceDelegate.h +++ b/launcher/ui/instanceview/InstanceDelegate.h @@ -15,25 +15,24 @@ #pragma once -#include #include +#include -class ListViewDelegate : public QStyledItemDelegate -{ +class ListViewDelegate : public QStyledItemDelegate { Q_OBJECT -public: - explicit ListViewDelegate(QObject *parent = 0); + public: + explicit ListViewDelegate(QObject* parent = 0); virtual ~ListViewDelegate() {} - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; - void updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index) const override; - QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const override; + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override; - void setEditorData(QWidget * editor, const QModelIndex & index) const override; - void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override; + void setEditorData(QWidget* editor, const QModelIndex& index) const override; + void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override; -private slots: + private slots: void editingDone(); }; diff --git a/launcher/ui/instanceview/InstanceProxyModel.cpp b/launcher/ui/instanceview/InstanceProxyModel.cpp index d8de93edc..ab6bef696 100644 --- a/launcher/ui/instanceview/InstanceProxyModel.cpp +++ b/launcher/ui/instanceview/InstanceProxyModel.cpp @@ -15,57 +15,54 @@ #include "InstanceProxyModel.h" -#include "InstanceView.h" -#include "Application.h" #include #include +#include "Application.h" +#include "InstanceView.h" #include -InstanceProxyModel::InstanceProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { +InstanceProxyModel::InstanceProxyModel(QObject* parent) : QSortFilterProxyModel(parent) +{ m_naturalSort.setNumericMode(true); m_naturalSort.setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive); // FIXME: use loaded translation as source of locale instead, hook this up to translation changes m_naturalSort.setLocale(QLocale::system()); } -QVariant InstanceProxyModel::data(const QModelIndex & index, int role) const +QVariant InstanceProxyModel::data(const QModelIndex& index, int role) const { QVariant data = QSortFilterProxyModel::data(index, role); - if(role == Qt::DecorationRole) - { + if (role == Qt::DecorationRole) { return QVariant(APPLICATION->icons()->getIcon(data.toString())); } return data; } -bool InstanceProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { +bool InstanceProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right) const +{ const QString leftCategory = left.data(InstanceViewRoles::GroupRole).toString(); const QString rightCategory = right.data(InstanceViewRoles::GroupRole).toString(); if (leftCategory == rightCategory) { return subSortLessThan(left, right); - } - else { + } else { // FIXME: real group sorting happens in InstanceView::updateGeometries(), see LocaleString auto result = leftCategory.localeAwareCompare(rightCategory); - if(result == 0) { + if (result == 0) { return subSortLessThan(left, right); } return result < 0; } } -bool InstanceProxyModel::subSortLessThan(const QModelIndex &left, const QModelIndex &right) const +bool InstanceProxyModel::subSortLessThan(const QModelIndex& left, const QModelIndex& right) const { - BaseInstance *pdataLeft = static_cast(left.internalPointer()); - BaseInstance *pdataRight = static_cast(right.internalPointer()); + BaseInstance* pdataLeft = static_cast(left.internalPointer()); + BaseInstance* pdataRight = static_cast(right.internalPointer()); QString sortMode = APPLICATION->settings()->get("InstSortMode").toString(); - if (sortMode == "LastLaunch") - { + if (sortMode == "LastLaunch") { return pdataLeft->lastLaunch() > pdataRight->lastLaunch(); - } - else - { + } else { return m_naturalSort.compare(pdataLeft->name(), pdataRight->name()) < 0; } } diff --git a/launcher/ui/instanceview/InstanceProxyModel.h b/launcher/ui/instanceview/InstanceProxyModel.h index bba8d2b50..13fec1bca 100644 --- a/launcher/ui/instanceview/InstanceProxyModel.h +++ b/launcher/ui/instanceview/InstanceProxyModel.h @@ -15,21 +15,20 @@ #pragma once -#include #include +#include -class InstanceProxyModel : public QSortFilterProxyModel -{ +class InstanceProxyModel : public QSortFilterProxyModel { Q_OBJECT -public: - InstanceProxyModel(QObject *parent = 0); + public: + InstanceProxyModel(QObject* parent = 0); -protected: - QVariant data(const QModelIndex & index, int role) const override; - bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; - bool subSortLessThan(const QModelIndex &left, const QModelIndex &right) const; + protected: + QVariant data(const QModelIndex& index, int role) const override; + bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; + bool subSortLessThan(const QModelIndex& left, const QModelIndex& right) const; -private: + private: QCollator m_naturalSort; }; diff --git a/launcher/ui/instanceview/InstanceView.cpp b/launcher/ui/instanceview/InstanceView.cpp index 1911dd59a..690dc917b 100644 --- a/launcher/ui/instanceview/InstanceView.cpp +++ b/launcher/ui/instanceview/InstanceView.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,40 +35,36 @@ #include "InstanceView.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "VisualGroup.h" #include "ui/themes/ThemeManager.h" -#include #include #include - -template bool listsIntersect(const QList &l1, const QList t2) +template +bool listsIntersect(const QList& l1, const QList t2) { - for (auto &item : l1) - { - if (t2.contains(item)) - { + for (auto& item : l1) { + if (t2.contains(item)) { return true; } } return false; } -InstanceView::InstanceView(QWidget *parent) - : QAbstractItemView(parent) +InstanceView::InstanceView(QWidget* parent) : QAbstractItemView(parent) { setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); @@ -83,23 +79,25 @@ InstanceView::~InstanceView() m_groups.clear(); } -void InstanceView::setModel(QAbstractItemModel *model) +void InstanceView::setModel(QAbstractItemModel* model) { QAbstractItemView::setModel(model); connect(model, &QAbstractItemModel::modelReset, this, &InstanceView::modelReset); connect(model, &QAbstractItemModel::rowsRemoved, this, &InstanceView::rowsRemoved); } -void InstanceView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) +void InstanceView::dataChanged([[maybe_unused]] const QModelIndex& topLeft, + [[maybe_unused]] const QModelIndex& bottomRight, + [[maybe_unused]] const QVector& roles) { scheduleDelayedItemsLayout(); } -void InstanceView::rowsInserted(const QModelIndex &parent, int start, int end) +void InstanceView::rowsInserted([[maybe_unused]] const QModelIndex& parent, [[maybe_unused]] int start, [[maybe_unused]] int end) { scheduleDelayedItemsLayout(); } -void InstanceView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +void InstanceView::rowsAboutToBeRemoved([[maybe_unused]] const QModelIndex& parent, [[maybe_unused]] int start, [[maybe_unused]] int end) { scheduleDelayedItemsLayout(); } @@ -117,7 +115,8 @@ void InstanceView::rowsRemoved() void InstanceView::currentChanged(const QModelIndex& current, const QModelIndex& previous) { QAbstractItemView::currentChanged(current, previous); - // TODO: for accessibility support, implement+register a factory, steal QAccessibleTable from Qt and return an instance of it for InstanceView. + // TODO: for accessibility support, implement+register a factory, steal QAccessibleTable from Qt and return an instance of it for + // InstanceView. #ifndef QT_NO_ACCESSIBILITY if (QAccessible::isActive() && current.isValid()) { QAccessibleEvent event(this, QAccessible::Focus); @@ -127,19 +126,13 @@ void InstanceView::currentChanged(const QModelIndex& current, const QModelIndex& #endif /* !QT_NO_ACCESSIBILITY */ } - -class LocaleString : public QString -{ -public: - LocaleString(const char *s) : QString(s) - { - } - LocaleString(const QString &s) : QString(s) - { - } +class LocaleString : public QString { + public: + LocaleString(const char* s) : QString(s) {} + LocaleString(const QString& s) : QString(s) {} }; -inline bool operator<(const LocaleString &lhs, const LocaleString &rhs) +inline bool operator<(const LocaleString& lhs, const LocaleString& rhs) { return (QString::localeAwareCompare(lhs, rhs) < 0); } @@ -147,33 +140,28 @@ inline bool operator<(const LocaleString &lhs, const LocaleString &rhs) void InstanceView::updateScrollbar() { int previousScroll = verticalScrollBar()->value(); - if (m_groups.isEmpty()) - { + if (m_groups.isEmpty()) { verticalScrollBar()->setRange(0, 0); - } - else - { + } else { int totalHeight = 0; // top margin totalHeight += m_categoryMargin; int itemScroll = 0; - for (auto category : m_groups) - { + for (auto category : m_groups) { category->m_verticalPosition = totalHeight; totalHeight += category->totalHeight() + m_categoryMargin; - if(!itemScroll && category->totalHeight() != 0) - { + if (!itemScroll && category->totalHeight() != 0) { itemScroll = category->contentHeight() / category->numRows(); } } // do not divide by zero - if(itemScroll == 0) + if (itemScroll == 0) itemScroll = 64; totalHeight += m_bottomMargin; - verticalScrollBar()->setSingleStep ( itemScroll ); - const int rowsPerPage = qMax ( viewport()->height() / itemScroll, 1 ); - verticalScrollBar()->setPageStep ( rowsPerPage * itemScroll ); + verticalScrollBar()->setSingleStep(itemScroll); + const int rowsPerPage = qMax(viewport()->height() / itemScroll, 1); + verticalScrollBar()->setPageStep(rowsPerPage * itemScroll); verticalScrollBar()->setRange(0, totalHeight - height()); } @@ -185,24 +173,19 @@ void InstanceView::updateGeometries() { geometryCache.clear(); - QMap cats; + QMap cats; - for (int i = 0; i < model()->rowCount(); ++i) - { + for (int i = 0; i < model()->rowCount(); ++i) { const QString groupName = model()->index(i, 0).data(InstanceViewRoles::GroupRole).toString(); - if (!cats.contains(groupName)) - { - VisualGroup *old = this->category(groupName); - if (old) - { + if (!cats.contains(groupName)) { + VisualGroup* old = this->category(groupName); + if (old) { auto cat = new VisualGroup(old); cats.insert(groupName, cat); cat->update(); - } - else - { + } else { auto cat = new VisualGroup(groupName, this); - if(fVisibility) { + if (fVisibility) { cat->collapsed = fVisibility(groupName); } cats.insert(groupName, cat); @@ -217,43 +200,36 @@ void InstanceView::updateGeometries() viewport()->update(); } -bool InstanceView::isIndexHidden(const QModelIndex &index) const +bool InstanceView::isIndexHidden(const QModelIndex& index) const { - VisualGroup *cat = category(index); - if (cat) - { + VisualGroup* cat = category(index); + if (cat) { return cat->collapsed; - } - else - { + } else { return false; } } -VisualGroup *InstanceView::category(const QModelIndex &index) const +VisualGroup* InstanceView::category(const QModelIndex& index) const { return category(index.data(InstanceViewRoles::GroupRole).toString()); } -VisualGroup *InstanceView::category(const QString &cat) const +VisualGroup* InstanceView::category(const QString& cat) const { - for (auto group : m_groups) - { - if (group->text == cat) - { + for (auto group : m_groups) { + if (group->text == cat) { return group; } } return nullptr; } -VisualGroup *InstanceView::categoryAt(const QPoint &pos, VisualGroup::HitResults & result) const +VisualGroup* InstanceView::categoryAt(const QPoint& pos, VisualGroup::HitResults& result) const { - for (auto group : m_groups) - { + for (auto group : m_groups) { result = group->hitScan(pos); - if(result != VisualGroup::NoHit) - { + if (result != VisualGroup::NoHit) { return group; } } @@ -261,14 +237,13 @@ VisualGroup *InstanceView::categoryAt(const QPoint &pos, VisualGroup::HitResults return nullptr; } -QString InstanceView::groupNameAt(const QPoint &point) +QString InstanceView::groupNameAt(const QPoint& point) { executeDelayedItemsLayout(); - VisualGroup::HitResults hitresult; - auto group = categoryAt(point + offset(), hitresult); - if(group && (hitresult & (VisualGroup::HeaderHit | VisualGroup::BodyHit))) - { + VisualGroup::HitResults hitResult; + auto group = categoryAt(point + offset(), hitResult); + if (group && (hitResult & (VisualGroup::HeaderHit | VisualGroup::BodyHit))) { return group->text; } return QString(); @@ -289,7 +264,7 @@ int InstanceView::itemWidth() const return m_itemWidth; } -void InstanceView::mousePressEvent(QMouseEvent *event) +void InstanceView::mousePressEvent(QMouseEvent* event) { executeDelayedItemsLayout(); @@ -302,19 +277,16 @@ void InstanceView::mousePressEvent(QMouseEvent *event) m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex); m_pressedPosition = geometryPos; - VisualGroup::HitResults hitresult; - m_pressedCategory = categoryAt(geometryPos, hitresult); - if (m_pressedCategory && hitresult & VisualGroup::CheckboxHit) - { + VisualGroup::HitResults hitResult; + m_pressedCategory = categoryAt(geometryPos, hitResult); + if (m_pressedCategory && hitResult & VisualGroup::CheckboxHit) { setState(m_pressedCategory->collapsed ? ExpandingState : CollapsingState); event->accept(); return; } - if (index.isValid() && (index.flags() & Qt::ItemIsEnabled)) - { - if(index != currentIndex()) - { + if (index.isValid() && (index.flags() & Qt::ItemIsEnabled)) { + if (index != currentIndex()) { // FIXME: better! m_currentCursorColumn = -1; } @@ -330,15 +302,13 @@ void InstanceView::mousePressEvent(QMouseEvent *event) // signal handlers may change the model emit pressed(index); - } - else - { + } else { // Forces a finalize() even if mouse is pressed, but not on a item selectionModel()->select(QModelIndex(), QItemSelectionModel::Select); } } -void InstanceView::mouseMoveEvent(QMouseEvent *event) +void InstanceView::mouseMoveEvent(QMouseEvent* event) { executeDelayedItemsLayout(); @@ -346,16 +316,13 @@ void InstanceView::mouseMoveEvent(QMouseEvent *event) QPoint visualPos = event->pos(); QPoint geometryPos = event->pos() + offset(); - if (state() == ExpandingState || state() == CollapsingState) - { + if (state() == ExpandingState || state() == CollapsingState) { return; } - if (state() == DraggingState) - { + if (state() == DraggingState) { topLeft = m_pressedPosition - offset(); - if ((topLeft - event->pos()).manhattanLength() > QApplication::startDragDistance()) - { + if ((topLeft - event->pos()).manhattanLength() > QApplication::startDragDistance()) { m_pressedIndex = QModelIndex(); startDrag(model()->supportedDragActions()); setState(NoState); @@ -364,39 +331,31 @@ void InstanceView::mouseMoveEvent(QMouseEvent *event) return; } - if (selectionMode() != SingleSelection) - { + if (selectionMode() != SingleSelection) { topLeft = m_pressedPosition - offset(); - } - else - { + } else { topLeft = geometryPos; } - if (m_pressedIndex.isValid() && (state() != DragSelectingState) && - (event->buttons() != Qt::NoButton) && !selectedIndexes().isEmpty()) - { + if (m_pressedIndex.isValid() && (state() != DragSelectingState) && (event->buttons() != Qt::NoButton) && !selectedIndexes().isEmpty()) { setState(DraggingState); return; } - if ((event->buttons() & Qt::LeftButton) && selectionModel()) - { + if ((event->buttons() & Qt::LeftButton) && selectionModel()) { setState(DragSelectingState); setSelection(QRect(visualPos, visualPos), QItemSelectionModel::ClearAndSelect); QModelIndex index = indexAt(visualPos); // set at the end because it might scroll the view - if (index.isValid() && (index != selectionModel()->currentIndex()) && - (index.flags() & Qt::ItemIsEnabled)) - { + if (index.isValid() && (index != selectionModel()->currentIndex()) && (index.flags() & Qt::ItemIsEnabled)) { selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); } } } -void InstanceView::mouseReleaseEvent(QMouseEvent *event) +void InstanceView::mouseReleaseEvent(QMouseEvent* event) { executeDelayedItemsLayout(); @@ -404,15 +363,13 @@ void InstanceView::mouseReleaseEvent(QMouseEvent *event) QPoint geometryPos = event->pos() + offset(); QPersistentModelIndex index = indexAt(visualPos); - VisualGroup::HitResults hitresult; + VisualGroup::HitResults hitResult; - bool click = (index == m_pressedIndex && index.isValid()) || - (m_pressedCategory && m_pressedCategory == categoryAt(geometryPos, hitresult)); + bool click = + (index == m_pressedIndex && index.isValid()) || (m_pressedCategory && m_pressedCategory == categoryAt(geometryPos, hitResult)); - if (click && m_pressedCategory) - { - if (state() == ExpandingState) - { + if (click && m_pressedCategory) { + if (state() == ExpandingState) { m_pressedCategory->collapsed = false; emit groupStateChanged(m_pressedCategory->text, false); @@ -422,9 +379,7 @@ void InstanceView::mouseReleaseEvent(QMouseEvent *event) m_pressedCategory = nullptr; setState(NoState); return; - } - else if (state() == CollapsingState) - { + } else if (state() == CollapsingState) { m_pressedCategory->collapsed = true; emit groupStateChanged(m_pressedCategory->text, true); @@ -441,10 +396,8 @@ void InstanceView::mouseReleaseEvent(QMouseEvent *event) setState(NoState); - if (click) - { - if (event->button() == Qt::LeftButton) - { + if (click) { + if (event->button() == Qt::LeftButton) { emit clicked(index); } #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) @@ -453,34 +406,24 @@ void InstanceView::mouseReleaseEvent(QMouseEvent *event) #else QStyleOptionViewItem option = viewOptions(); #endif - if (m_pressedAlreadySelected) - { + if (m_pressedAlreadySelected) { option.state |= QStyle::State_Selected; } if ((model()->flags(index) & Qt::ItemIsEnabled) && - style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this)) - { + style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this)) { emit activated(index); } } } -void InstanceView::mouseDoubleClickEvent(QMouseEvent *event) +void InstanceView::mouseDoubleClickEvent(QMouseEvent* event) { executeDelayedItemsLayout(); QModelIndex index = indexAt(event->pos()); - if (!index.isValid() || !(index.flags() & Qt::ItemIsEnabled) || (m_pressedIndex != index)) - { - QMouseEvent me( - QEvent::MouseButtonPress, - event->localPos(), - event->windowPos(), - event->screenPos(), - event->button(), - event->buttons(), - event->modifiers() - ); + if (!index.isValid() || !(index.flags() & Qt::ItemIsEnabled) || (m_pressedIndex != index)) { + QMouseEvent me(QEvent::MouseButtonPress, event->localPos(), event->windowPos(), event->screenPos(), event->button(), + event->buttons(), event->modifiers()); mousePressEvent(&me); return; } @@ -494,8 +437,7 @@ void InstanceView::mouseDoubleClickEvent(QMouseEvent *event) #else QStyleOptionViewItem option = viewOptions(); #endif - if ((model()->flags(index) & Qt::ItemIsEnabled) && !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this)) - { + if ((model()->flags(index) & Qt::ItemIsEnabled) && !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this)) { emit activated(index); } } @@ -504,12 +446,12 @@ void InstanceView::setPaintCat(bool visible) { m_catVisible = visible; if (visible) - m_catPixmap.load(QString(":/backgrounds/%1").arg(ThemeManager::getCatImage())); + m_catPixmap.load(APPLICATION->themeManager()->getCatPack()); else m_catPixmap = QPixmap(); } -void InstanceView::paintEvent(QPaintEvent* event) +void InstanceView::paintEvent([[maybe_unused]] QPaintEvent* event) { executeDelayedItemsLayout(); @@ -538,9 +480,8 @@ void InstanceView::paintEvent(QPaintEvent* event) int wpWidth = viewport()->width(); option.rect.setWidth(wpWidth); - for (int i = 0; i < m_groups.size(); ++i) - { - VisualGroup *category = m_groups.at(i); + for (int i = 0; i < m_groups.size(); ++i) { + VisualGroup* category = m_groups.at(i); int y = category->verticalPosition(); y -= verticalOffset(); QRect backup = option.rect; @@ -554,28 +495,21 @@ void InstanceView::paintEvent(QPaintEvent* event) option.rect = backup; } - for (int i = 0; i < model()->rowCount(); ++i) - { + for (int i = 0; i < model()->rowCount(); ++i) { const QModelIndex index = model()->index(i, 0); - if (isIndexHidden(index)) - { + if (isIndexHidden(index)) { continue; } Qt::ItemFlags flags = index.flags(); option.rect = visualRect(index); option.features |= QStyleOptionViewItem::WrapText; - if (flags & Qt::ItemIsSelectable && selectionModel()->isSelected(index)) - { - option.state |= selectionModel()->isSelected(index) ? QStyle::State_Selected - : QStyle::State_None; - } - else - { + if (flags & Qt::ItemIsSelectable && selectionModel()->isSelected(index)) { + option.state |= selectionModel()->isSelected(index) ? QStyle::State_Selected : QStyle::State_None; + } else { option.state &= ~QStyle::State_Selected; } option.state |= (index == currentIndex()) ? QStyle::State_HasFocus : QStyle::State_None; - if (!(flags & Qt::ItemIsEnabled)) - { + if (!(flags & Qt::ItemIsEnabled)) { option.state &= ~QStyle::State_Enabled; } itemDelegate()->paint(&painter, option, index); @@ -613,27 +547,23 @@ void InstanceView::paintEvent(QPaintEvent* event) #endif } -void InstanceView::resizeEvent(QResizeEvent *event) +void InstanceView::resizeEvent([[maybe_unused]] QResizeEvent* event) { int newItemsPerRow = calculateItemsPerRow(); - if(newItemsPerRow != m_currentItemsPerRow) - { + if (newItemsPerRow != m_currentItemsPerRow) { m_currentCursorColumn = -1; m_currentItemsPerRow = newItemsPerRow; updateGeometries(); - } - else - { + } else { updateScrollbar(); } } -void InstanceView::dragEnterEvent(QDragEnterEvent *event) +void InstanceView::dragEnterEvent(QDragEnterEvent* event) { executeDelayedItemsLayout(); - if (!isDragEventAccepted(event)) - { + if (!isDragEventAccepted(event)) { return; } m_lastDragPosition = event->pos() + offset(); @@ -641,12 +571,11 @@ void InstanceView::dragEnterEvent(QDragEnterEvent *event) event->accept(); } -void InstanceView::dragMoveEvent(QDragMoveEvent *event) +void InstanceView::dragMoveEvent(QDragMoveEvent* event) { executeDelayedItemsLayout(); - if (!isDragEventAccepted(event)) - { + if (!isDragEventAccepted(event)) { return; } m_lastDragPosition = event->pos() + offset(); @@ -654,7 +583,7 @@ void InstanceView::dragMoveEvent(QDragMoveEvent *event) event->accept(); } -void InstanceView::dragLeaveEvent(QDragLeaveEvent *event) +void InstanceView::dragLeaveEvent([[maybe_unused]] QDragLeaveEvent* event) { executeDelayedItemsLayout(); @@ -662,7 +591,7 @@ void InstanceView::dragLeaveEvent(QDragLeaveEvent *event) viewport()->update(); } -void InstanceView::dropEvent(QDropEvent *event) +void InstanceView::dropEvent(QDropEvent* event) { executeDelayedItemsLayout(); @@ -673,16 +602,13 @@ void InstanceView::dropEvent(QDropEvent *event) auto mimedata = event->mimeData(); - if (event->source() == this) - { - if(event->possibleActions() & Qt::MoveAction) - { - std::pair dropPos = rowDropPos(event->pos()); - const VisualGroup *group = dropPos.first; - auto hitresult = dropPos.second; + if (event->source() == this) { + if (event->possibleActions() & Qt::MoveAction) { + std::pair dropPos = rowDropPos(event->pos()); + const VisualGroup* group = dropPos.first; + auto hitResult = dropPos.second; - if (hitresult == VisualGroup::HitResult::NoHit) - { + if (hitResult == VisualGroup::HitResult::NoHit) { viewport()->update(); return; } @@ -699,14 +625,12 @@ void InstanceView::dropEvent(QDropEvent *event) } // check if the action is supported - if (!mimedata) - { + if (!mimedata) { return; } // files dropped from outside? - if (mimedata->hasUrls()) - { + if (mimedata->hasUrls()) { auto urls = mimedata->urls(); event->accept(); emit droppedURLs(urls); @@ -718,57 +642,52 @@ void InstanceView::startDrag(Qt::DropActions supportedActions) executeDelayedItemsLayout(); QModelIndexList indexes = selectionModel()->selectedIndexes(); - if(indexes.count() == 0) + if (indexes.count() == 0) return; - QMimeData *data = model()->mimeData(indexes); - if (!data) - { + QMimeData* mimeData = model()->mimeData(indexes); + if (!mimeData) { return; } QRect rect; QPixmap pixmap = renderToPixmap(indexes, &rect); - QDrag *drag = new QDrag(this); + QDrag* drag = new QDrag(this); drag->setPixmap(pixmap); - drag->setMimeData(data); + drag->setMimeData(mimeData); drag->setHotSpot(m_pressedPosition - rect.topLeft()); Qt::DropAction defaultDropAction = Qt::IgnoreAction; - if (this->defaultDropAction() != Qt::IgnoreAction && (supportedActions & this->defaultDropAction())) - { + if (this->defaultDropAction() != Qt::IgnoreAction && (supportedActions & this->defaultDropAction())) { defaultDropAction = this->defaultDropAction(); } /*auto action = */ drag->exec(supportedActions, defaultDropAction); } -QRect InstanceView::visualRect(const QModelIndex &index) const +QRect InstanceView::visualRect(const QModelIndex& index) const { const_cast(this)->executeDelayedItemsLayout(); return geometryRect(index).translated(-offset()); } -QRect InstanceView::geometryRect(const QModelIndex &index) const +QRect InstanceView::geometryRect(const QModelIndex& index) const { const_cast(this)->executeDelayedItemsLayout(); - if (!index.isValid() || isIndexHidden(index) || index.column() > 0) - { + if (!index.isValid() || isIndexHidden(index) || index.column() > 0) { return QRect(); } int row = index.row(); - if(geometryCache.contains(row)) - { + if (geometryCache.contains(row)) { return *geometryCache[row]; } - const VisualGroup *cat = category(index); + const VisualGroup* cat = category(index); QPair pos = cat->positionOf(index); int x = pos.first; // int y = pos.second; - #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QStyleOptionViewItem option; initViewItemOption(&option); @@ -784,43 +703,38 @@ QRect InstanceView::geometryRect(const QModelIndex &index) const return out; } -QModelIndex InstanceView::indexAt(const QPoint &point) const +QModelIndex InstanceView::indexAt(const QPoint& point) const { const_cast(this)->executeDelayedItemsLayout(); - for (int i = 0; i < model()->rowCount(); ++i) - { + for (int i = 0; i < model()->rowCount(); ++i) { QModelIndex index = model()->index(i, 0); - if (visualRect(index).contains(point)) - { + if (visualRect(index).contains(point)) { return index; } } return QModelIndex(); } -void InstanceView::setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands) +void InstanceView::setSelection(const QRect& rect, const QItemSelectionModel::SelectionFlags commands) { executeDelayedItemsLayout(); - for (int i = 0; i < model()->rowCount(); ++i) - { + for (int i = 0; i < model()->rowCount(); ++i) { QModelIndex index = model()->index(i, 0); QRect itemRect = visualRect(index); - if (itemRect.intersects(rect)) - { + if (itemRect.intersects(rect)) { selectionModel()->select(index, commands); update(itemRect.translated(-offset())); } } } -QPixmap InstanceView::renderToPixmap(const QModelIndexList &indices, QRect *r) const +QPixmap InstanceView::renderToPixmap(const QModelIndexList& indices, QRect* r) const { Q_ASSERT(r); auto paintPairs = draggablePaintPairs(indices, r); - if (paintPairs.isEmpty()) - { + if (paintPairs.isEmpty()) { return QPixmap(); } QPixmap pixmap(r->size()); @@ -833,23 +747,21 @@ QPixmap InstanceView::renderToPixmap(const QModelIndexList &indices, QRect *r) c QStyleOptionViewItem option = viewOptions(); #endif option.state |= QStyle::State_Selected; - for (int j = 0; j < paintPairs.count(); ++j) - { + for (int j = 0; j < paintPairs.count(); ++j) { option.rect = paintPairs.at(j).first.translated(-r->topLeft()); - const QModelIndex ¤t = paintPairs.at(j).second; + const QModelIndex& current = paintPairs.at(j).second; itemDelegate()->paint(&painter, option, current); } return pixmap; } -QList> InstanceView::draggablePaintPairs(const QModelIndexList &indices, QRect *r) const +QList> InstanceView::draggablePaintPairs(const QModelIndexList& indices, QRect* r) const { Q_ASSERT(r); - QRect &rect = *r; + QRect& rect = *r; QList> ret; - for (int i = 0; i < indices.count(); ++i) - { - const QModelIndex &index = indices.at(i); + for (int i = 0; i < indices.count(); ++i) { + const QModelIndex& index = indices.at(i); const QRect current = geometryRect(index); ret += std::make_pair(current, index); rect |= current; @@ -857,16 +769,16 @@ QList> InstanceView::draggablePaintPairs(const QMo return ret; } -bool InstanceView::isDragEventAccepted(QDropEvent *event) +bool InstanceView::isDragEventAccepted([[maybe_unused]] QDropEvent* event) { return true; } -std::pair InstanceView::rowDropPos(const QPoint &pos) +std::pair InstanceView::rowDropPos(const QPoint& pos) { - VisualGroup::HitResults hitresult; - auto group = categoryAt(pos + offset(), hitresult); - return std::make_pair(group, hitresult); + VisualGroup::HitResults hitResult; + auto group = categoryAt(pos + offset(), hitResult); + return std::make_pair(group, hitResult); } QPoint InstanceView::offset() const @@ -874,146 +786,118 @@ QPoint InstanceView::offset() const return QPoint(horizontalOffset(), verticalOffset()); } -QRegion InstanceView::visualRegionForSelection(const QItemSelection &selection) const +QRegion InstanceView::visualRegionForSelection(const QItemSelection& selection) const { QRegion region; - for (auto &range : selection) - { + for (auto& range : selection) { int start_row = range.top(); int end_row = range.bottom(); - for (int row = start_row; row <= end_row; ++row) - { + for (int row = start_row; row <= end_row; ++row) { int start_column = range.left(); int end_column = range.right(); - for (int column = start_column; column <= end_column; ++column) - { + for (int column = start_column; column <= end_column; ++column) { QModelIndex index = model()->index(row, column, rootIndex()); - region += visualRect(index); // OK + region += visualRect(index); // OK } } } return region; } -QModelIndex InstanceView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) +QModelIndex InstanceView::moveCursor(QAbstractItemView::CursorAction cursorAction, [[maybe_unused]] Qt::KeyboardModifiers modifiers) { auto current = currentIndex(); - if(!current.isValid()) - { + if (!current.isValid()) { return current; } auto cat = category(current); int group_index = m_groups.indexOf(cat); - if(group_index < 0) + if (group_index < 0) return current; QPair pos = cat->positionOf(current); int column = pos.first; int row = pos.second; - if(m_currentCursorColumn < 0) - { + if (m_currentCursorColumn < 0) { m_currentCursorColumn = column; } - switch(cursorAction) - { - case MoveUp: - { - if(row == 0) - { - int prevgroupindex = group_index-1; - while(prevgroupindex >= 0) - { - auto prevgroup = m_groups[prevgroupindex]; - if(prevgroup->collapsed) - { - prevgroupindex--; + switch (cursorAction) { + case MoveUp: { + if (row == 0) { + int prevGroupIndex = group_index - 1; + while (prevGroupIndex >= 0) { + auto prevGroup = m_groups[prevGroupIndex]; + if (prevGroup->collapsed) { + prevGroupIndex--; continue; } - int newRow = prevgroup->numRows() - 1; - int newRowSize = prevgroup->rows[newRow].size(); + int newRow = prevGroup->numRows() - 1; + int newRowSize = prevGroup->rows[newRow].size(); int newColumn = m_currentCursorColumn; - if (m_currentCursorColumn >= newRowSize) - { + if (m_currentCursorColumn >= newRowSize) { newColumn = newRowSize - 1; } - return prevgroup->rows[newRow][newColumn]; + return prevGroup->rows[newRow][newColumn]; } - } - else - { + } else { int newRow = row - 1; int newRowSize = cat->rows[newRow].size(); int newColumn = m_currentCursorColumn; - if (m_currentCursorColumn >= newRowSize) - { + if (m_currentCursorColumn >= newRowSize) { newColumn = newRowSize - 1; } return cat->rows[newRow][newColumn]; } return current; } - case MoveDown: - { - if(row == cat->rows.size() - 1) - { - int nextgroupindex = group_index+1; - while (nextgroupindex < m_groups.size()) - { - auto nextgroup = m_groups[nextgroupindex]; - if(nextgroup->collapsed) - { - nextgroupindex++; + case MoveDown: { + if (row == cat->rows.size() - 1) { + int nextGroupIndex = group_index + 1; + while (nextGroupIndex < m_groups.size()) { + auto nextGroup = m_groups[nextGroupIndex]; + if (nextGroup->collapsed) { + nextGroupIndex++; continue; } - int newRowSize = nextgroup->rows[0].size(); + int newRowSize = nextGroup->rows[0].size(); int newColumn = m_currentCursorColumn; - if (m_currentCursorColumn >= newRowSize) - { + if (m_currentCursorColumn >= newRowSize) { newColumn = newRowSize - 1; } - return nextgroup->rows[0][newColumn]; + return nextGroup->rows[0][newColumn]; } - } - else - { + } else { int newRow = row + 1; int newRowSize = cat->rows[newRow].size(); int newColumn = m_currentCursorColumn; - if (m_currentCursorColumn >= newRowSize) - { + if (m_currentCursorColumn >= newRowSize) { newColumn = newRowSize - 1; } return cat->rows[newRow][newColumn]; } return current; } - case MoveLeft: - { - if(column > 0) - { + case MoveLeft: { + if (column > 0) { m_currentCursorColumn = column - 1; return cat->rows[row][column - 1]; } // TODO: moving to previous line return current; } - case MoveRight: - { - if(column < cat->rows[row].size() - 1) - { + case MoveRight: { + if (column < cat->rows[row].size() - 1) { m_currentCursorColumn = column + 1; return cat->rows[row][column + 1]; } // TODO: moving to next line return current; } - case MoveHome: - { + case MoveHome: { m_currentCursorColumn = 0; return cat->rows[row][0]; } - case MoveEnd: - { + case MoveEnd: { auto last = cat->rows[row].size() - 1; m_currentCursorColumn = last; return cat->rows[row][last]; @@ -1040,14 +924,13 @@ void InstanceView::scrollContentsBy(int dx, int dy) viewport()->scroll(dx, dy); } -void InstanceView::scrollTo(const QModelIndex &index, ScrollHint hint) +void InstanceView::scrollTo(const QModelIndex& index, ScrollHint hint) { if (!index.isValid()) return; const QRect rect = visualRect(index); - if (hint == EnsureVisible && viewport()->rect().contains(rect)) - { + if (hint == EnsureVisible && viewport()->rect().contains(rect)) { viewport()->update(rect); return; } @@ -1055,7 +938,7 @@ void InstanceView::scrollTo(const QModelIndex &index, ScrollHint hint) verticalScrollBar()->setValue(verticalScrollToValue(index, rect, hint)); } -int InstanceView::verticalScrollToValue(const QModelIndex &index, const QRect &rect, QListView::ScrollHint hint) const +int InstanceView::verticalScrollToValue([[maybe_unused]] const QModelIndex& index, const QRect& rect, QListView::ScrollHint hint) const { const QRect area = viewport()->rect(); const bool above = (hint == QListView::EnsureVisible && rect.top() < area.top()); diff --git a/launcher/ui/instanceview/InstanceView.h b/launcher/ui/instanceview/InstanceView.h index 364056751..3d4d56208 100644 --- a/launcher/ui/instanceview/InstanceView.h +++ b/launcher/ui/instanceview/InstanceView.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,95 +35,86 @@ #pragma once -#include -#include -#include #include -#include "VisualGroup.h" +#include +#include +#include #include +#include "VisualGroup.h" -struct InstanceViewRoles -{ - enum - { - GroupRole = Qt::UserRole, - ProgressValueRole, - ProgressMaximumRole - }; +struct InstanceViewRoles { + enum { GroupRole = Qt::UserRole, ProgressValueRole, ProgressMaximumRole }; }; -class InstanceView : public QAbstractItemView -{ +class InstanceView : public QAbstractItemView { Q_OBJECT -public: - InstanceView(QWidget *parent = 0); + public: + InstanceView(QWidget* parent = 0); ~InstanceView(); - void setModel(QAbstractItemModel *model) override; + void setModel(QAbstractItemModel* model) override; - using visibilityFunction = std::function; - void setSourceOfGroupCollapseStatus(visibilityFunction f) { - fVisibility = f; - } + using visibilityFunction = std::function; + void setSourceOfGroupCollapseStatus(visibilityFunction f) { fVisibility = f; } /// return geometry rectangle occupied by the specified model item - QRect geometryRect(const QModelIndex &index) const; + QRect geometryRect(const QModelIndex& index) const; /// return visual rectangle occupied by the specified model item - virtual QRect visualRect(const QModelIndex &index) const override; + virtual QRect visualRect(const QModelIndex& index) const override; /// get the model index at the specified visual point - virtual QModelIndex indexAt(const QPoint &point) const override; - QString groupNameAt(const QPoint &point); - void setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands) override; + virtual QModelIndex indexAt(const QPoint& point) const override; + QString groupNameAt(const QPoint& point); + void setSelection(const QRect& rect, const QItemSelectionModel::SelectionFlags commands) override; virtual int horizontalOffset() const override; virtual int verticalOffset() const override; virtual void scrollContentsBy(int dx, int dy) override; - virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override; + virtual void scrollTo(const QModelIndex& index, ScrollHint hint = EnsureVisible) override; virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override; - virtual QRegion visualRegionForSelection(const QItemSelection &selection) const override; + virtual QRegion visualRegionForSelection(const QItemSelection& selection) const override; int spacing() const { return m_spacing; }; void setPaintCat(bool visible); -public slots: + public slots: virtual void updateGeometries() override; -protected slots: - virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) override; - virtual void rowsInserted(const QModelIndex &parent, int start, int end) override; - virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) override; + protected slots: + virtual void dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles) override; + virtual void rowsInserted(const QModelIndex& parent, int start, int end) override; + virtual void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override; void modelReset(); void rowsRemoved(); - void currentChanged(const QModelIndex ¤t, const QModelIndex &previous) override; + void currentChanged(const QModelIndex& current, const QModelIndex& previous) override; -signals: + signals: void droppedURLs(QList urls); void groupStateChanged(QString group, bool collapsed); -protected: - bool isIndexHidden(const QModelIndex &index) const override; - void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void mouseDoubleClickEvent(QMouseEvent *event) override; - void paintEvent(QPaintEvent *event) override; - void resizeEvent(QResizeEvent *event) override; + protected: + bool isIndexHidden(const QModelIndex& index) const override; + void mousePressEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseDoubleClickEvent(QMouseEvent* event) override; + void paintEvent(QPaintEvent* event) override; + void resizeEvent(QResizeEvent* event) override; - void dragEnterEvent(QDragEnterEvent *event) override; - void dragMoveEvent(QDragMoveEvent *event) override; - void dragLeaveEvent(QDragLeaveEvent *event) override; - void dropEvent(QDropEvent *event) override; + void dragEnterEvent(QDragEnterEvent* event) override; + void dragMoveEvent(QDragMoveEvent* event) override; + void dragLeaveEvent(QDragLeaveEvent* event) override; + void dropEvent(QDropEvent* event) override; void startDrag(Qt::DropActions supportedActions) override; void updateScrollbar(); -private: + private: friend struct VisualGroup; - QList m_groups; + QList m_groups; visibilityFunction fVisibility; @@ -135,7 +126,7 @@ private: int m_spacing = 5; int m_itemWidth = 100; int m_currentItemsPerRow = -1; - int m_currentCursorColumn= -1; + int m_currentCursorColumn = -1; mutable QCache geometryCache; bool m_catVisible = false; QPixmap m_catPixmap; @@ -144,30 +135,27 @@ private: QPoint m_pressedPosition; QPersistentModelIndex m_pressedIndex; bool m_pressedAlreadySelected; - VisualGroup *m_pressedCategory; + VisualGroup* m_pressedCategory; QItemSelectionModel::SelectionFlag m_ctrlDragSelectionFlag; QPoint m_lastDragPosition; - VisualGroup *category(const QModelIndex &index) const; - VisualGroup *category(const QString &cat) const; - VisualGroup *categoryAt(const QPoint &pos, VisualGroup::HitResults & result) const; + VisualGroup* category(const QModelIndex& index) const; + VisualGroup* category(const QString& cat) const; + VisualGroup* categoryAt(const QPoint& pos, VisualGroup::HitResults& result) const; - int itemsPerRow() const - { - return m_currentItemsPerRow; - }; + int itemsPerRow() const { return m_currentItemsPerRow; }; int contentWidth() const; -private: /* methods */ + private: /* methods */ int itemWidth() const; int calculateItemsPerRow() const; - int verticalScrollToValue(const QModelIndex &index, const QRect &rect, QListView::ScrollHint hint) const; - QPixmap renderToPixmap(const QModelIndexList &indices, QRect *r) const; - QList> draggablePaintPairs(const QModelIndexList &indices, QRect *r) const; + int verticalScrollToValue(const QModelIndex& index, const QRect& rect, QListView::ScrollHint hint) const; + QPixmap renderToPixmap(const QModelIndexList& indices, QRect* r) const; + QList> draggablePaintPairs(const QModelIndexList& indices, QRect* r) const; - bool isDragEventAccepted(QDropEvent *event); + bool isDragEventAccepted(QDropEvent* event); - std::pair rowDropPos(const QPoint &pos); + std::pair rowDropPos(const QPoint& pos); QPoint offset() const; }; diff --git a/launcher/ui/instanceview/VisualGroup.cpp b/launcher/ui/instanceview/VisualGroup.cpp index e6bca17d6..aaf31941d 100644 --- a/launcher/ui/instanceview/VisualGroup.cpp +++ b/launcher/ui/instanceview/VisualGroup.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 Tayou * * 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 @@ -35,22 +36,18 @@ #include "VisualGroup.h" +#include +#include #include #include #include -#include -#include +#include #include "InstanceView.h" -VisualGroup::VisualGroup(const QString &text, InstanceView *view) : view(view), text(text), collapsed(false) -{ -} +VisualGroup::VisualGroup(QString text, InstanceView* view) : view(view), text(std::move(text)), collapsed(false) {} -VisualGroup::VisualGroup(const VisualGroup *other) - : view(other->view), text(other->text), collapsed(other->collapsed) -{ -} +VisualGroup::VisualGroup(const VisualGroup* other) : view(other->view), text(other->text), collapsed(other->collapsed) {} void VisualGroup::update() { @@ -64,13 +61,11 @@ void VisualGroup::update() int positionInRow = 0; int currentRow = 0; int offsetFromTop = 0; - for (auto item: temp_items) - { - if(positionInRow == itemsPerRow) - { + for (auto item : temp_items) { + if (positionInRow == itemsPerRow) { rows[currentRow].height = maxRowHeight; rows[currentRow].top = offsetFromTop; - currentRow ++; + currentRow++; offsetFromTop += maxRowHeight + 5; positionInRow = 0; maxRowHeight = 0; @@ -83,8 +78,7 @@ void VisualGroup::update() #endif auto itemHeight = view->itemDelegate()->sizeHint(viewItemOption, item).height(); - if(itemHeight > maxRowHeight) - { + if (itemHeight > maxRowHeight) { maxRowHeight = itemHeight; } rows[currentRow].items.append(item); @@ -94,16 +88,13 @@ void VisualGroup::update() rows[currentRow].top = offsetFromTop; } -QPair VisualGroup::positionOf(const QModelIndex &index) const +QPair VisualGroup::positionOf(const QModelIndex& index) const { int y = 0; - for (auto & row: rows) - { - for(auto x = 0; x < row.items.size(); x++) - { - if(row.items[x] == index) - { - return qMakePair(x,y); + for (auto& row : rows) { + for (auto x = 0; x < row.items.size(); x++) { + if (row.items[x] == index) { + return qMakePair(x, y); } } y++; @@ -112,193 +103,109 @@ QPair VisualGroup::positionOf(const QModelIndex &index) const return qMakePair(0, 0); } -int VisualGroup::rowTopOf(const QModelIndex &index) const +int VisualGroup::rowTopOf(const QModelIndex& index) const { auto position = positionOf(index); return rows[position.second].top; } -int VisualGroup::rowHeightOf(const QModelIndex &index) const +int VisualGroup::rowHeightOf(const QModelIndex& index) const { auto position = positionOf(index); return rows[position.second].height; } -VisualGroup::HitResults VisualGroup::hitScan(const QPoint &pos) const +VisualGroup::HitResults VisualGroup::hitScan(const QPoint& pos) const { VisualGroup::HitResults results = VisualGroup::NoHit; int y_start = verticalPosition(); int body_start = y_start + headerHeight(); - int body_end = body_start + contentHeight() + 5; // FIXME: wtf is this 5? + int body_end = body_start + contentHeight(); int y = pos.y(); // int x = pos.x(); - if (y < y_start) - { + if (y < y_start) { results = VisualGroup::NoHit; - } - else if (y < body_start) - { + } else if (y < body_start) { results = VisualGroup::HeaderHit; int collapseSize = headerHeight() - 4; // the icon - QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y_start, collapseSize, collapseSize); - if (iconRect.contains(pos)) - { + QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y_start, view->width() - 4, collapseSize); + if (iconRect.contains(pos)) { results |= VisualGroup::CheckboxHit; } - } - else if (y < body_end) - { + } else if (y < body_end) { results |= VisualGroup::BodyHit; } return results; } -void VisualGroup::drawHeader(QPainter *painter, const QStyleOptionViewItem &option) +void VisualGroup::drawHeader(QPainter* painter, const QStyleOptionViewItem& option) const { - painter->setRenderHint(QPainter::Antialiasing); - - const QRect optRect = option.rect; + QRect optRect = option.rect; + optRect.setTop(optRect.top() + 7); QFont font(QApplication::font()); font.setBold(true); const QFontMetrics fontMetrics = QFontMetrics(font); + painter->setFont(font); - QColor outlineColor = option.palette.text().color(); - outlineColor.setAlphaF(0.35); + QPen pen; + pen.setWidth(2); + QColor penColor = option.palette.text().color(); + penColor.setAlphaF(0.6); + pen.setColor(penColor); + painter->setPen(pen); + painter->setRenderHint(QPainter::Antialiasing); - //BEGIN: top left corner + // sizes and offsets, to keep things consistent below + int arrowOffsetLeft = fontMetrics.height() / 2 + 7; + int textOffsetLeft = arrowOffsetLeft * 2; + int arrowSize = 6; + int centerHeight = optRect.top() + fontMetrics.height() / 2; + + // BEGIN: arrow { - painter->save(); - painter->setPen(outlineColor); - const QPointF topLeft(optRect.topLeft()); - QRectF arc(topLeft, QSizeF(4, 4)); - arc.translate(0.5, 0.5); - painter->drawArc(arc, 1440, 1440); - painter->restore(); - } - //END: top left corner - - //BEGIN: left vertical line - { - QPoint start(optRect.topLeft()); - start.ry() += 3; - QPoint verticalGradBottom(optRect.topLeft()); - verticalGradBottom.ry() += fontMetrics.height() + 5; - QLinearGradient gradient(start, verticalGradBottom); - gradient.setColorAt(0, outlineColor); - gradient.setColorAt(1, Qt::transparent); - painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); - } - //END: left vertical line - - //BEGIN: horizontal line - { - QPoint start(optRect.topLeft()); - start.rx() += 3; - QPoint horizontalGradTop(optRect.topLeft()); - horizontalGradTop.rx() += optRect.width() - 6; - painter->fillRect(QRect(start, QSize(optRect.width() - 6, 1)), outlineColor); - } - //END: horizontal line - - //BEGIN: top right corner - { - painter->save(); - painter->setPen(outlineColor); - QPointF topRight(optRect.topRight()); - topRight.rx() -= 4; - QRectF arc(topRight, QSizeF(4, 4)); - arc.translate(0.5, 0.5); - painter->drawArc(arc, 0, 1440); - painter->restore(); - } - //END: top right corner - - //BEGIN: right vertical line - { - QPoint start(optRect.topRight()); - start.ry() += 3; - QPoint verticalGradBottom(optRect.topRight()); - verticalGradBottom.ry() += fontMetrics.height() + 5; - QLinearGradient gradient(start, verticalGradBottom); - gradient.setColorAt(0, outlineColor); - gradient.setColorAt(1, Qt::transparent); - painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); - } - //END: right vertical line - - //BEGIN: checkboxy thing - { - painter->save(); - painter->setRenderHint(QPainter::Antialiasing, false); - painter->setFont(font); - QColor penColor(option.palette.text().color()); - penColor.setAlphaF(0.6); - painter->setPen(penColor); - QRect iconSubRect(option.rect); - iconSubRect.setTop(iconSubRect.top() + 7); - iconSubRect.setLeft(iconSubRect.left() + 7); - - int sizing = fontMetrics.height(); - int even = ( (sizing - 1) % 2 ); - - iconSubRect.setHeight(sizing - even); - iconSubRect.setWidth(sizing - even); - painter->drawRect(iconSubRect); - - - /* - if(collapsed) - painter->drawText(iconSubRect, Qt::AlignHCenter | Qt::AlignVCenter, "+"); - else - painter->drawText(iconSubRect, Qt::AlignHCenter | Qt::AlignVCenter, "-"); - */ - painter->setBrush(option.palette.text()); - painter->fillRect(iconSubRect.x(), iconSubRect.y() + iconSubRect.height() / 2, - iconSubRect.width(), 2, penColor); - if (collapsed) - { - painter->fillRect(iconSubRect.x() + iconSubRect.width() / 2, iconSubRect.y(), 2, - iconSubRect.height(), penColor); + QPolygon arrowPolygon; + if (collapsed) { + arrowPolygon << QPoint(arrowOffsetLeft - arrowSize / 2, centerHeight - arrowSize) + << QPoint(arrowOffsetLeft + arrowSize / 2, centerHeight) + << QPoint(arrowOffsetLeft - arrowSize / 2, centerHeight + arrowSize); + painter->drawPolyline(arrowPolygon); + } else { + arrowPolygon << QPoint(arrowOffsetLeft - arrowSize, centerHeight - arrowSize / 2) + << QPoint(arrowOffsetLeft, centerHeight + arrowSize / 2) + << QPoint(arrowOffsetLeft + arrowSize, centerHeight - arrowSize / 2); + painter->drawPolyline(arrowPolygon); } - - painter->restore(); } - //END: checkboxy thing + // END: arrow - //BEGIN: text + // BEGIN: text { - QRect textRect(option.rect); - textRect.setTop(textRect.top() + 7); - textRect.setLeft(textRect.left() + 7 + fontMetrics.height() + 7); + QRect textRect(optRect); + textRect.setTop(textRect.top()); + textRect.setLeft(textOffsetLeft); textRect.setHeight(fontMetrics.height()); textRect.setRight(textRect.right() - 7); - painter->save(); - painter->setFont(font); - QColor penColor(option.palette.text().color()); - penColor.setAlphaF(0.6); - painter->setPen(penColor); - painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, text); - painter->restore(); + painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, !text.isEmpty() ? text : QObject::tr("Ungrouped")); } - //END: text + // END: text } int VisualGroup::totalHeight() const { - return headerHeight() + 5 + contentHeight(); // FIXME: wtf is that '5'? + return headerHeight() + contentHeight(); } -int VisualGroup::headerHeight() const +int VisualGroup::headerHeight() { QFont font(QApplication::font()); font.setBold(true); QFontMetrics fontMetrics(font); const int height = fontMetrics.height() + 1 /* 1 pixel-width gradient */ - + 11 /* top and bottom separation */; + + 11 /* top and bottom separation */; return height; /* int raw = view->viewport()->fontMetrics().height() + 4; @@ -311,8 +218,7 @@ int VisualGroup::headerHeight() const int VisualGroup::contentHeight() const { - if (collapsed) - { + if (collapsed) { return 0; } auto last = rows[numRows() - 1]; @@ -321,7 +227,7 @@ int VisualGroup::contentHeight() const int VisualGroup::numRows() const { - return rows.size(); + return (int)rows.size(); } int VisualGroup::verticalPosition() const @@ -332,11 +238,9 @@ int VisualGroup::verticalPosition() const QList VisualGroup::items() const { QList indices; - for (int i = 0; i < view->model()->rowCount(); ++i) - { + for (int i = 0; i < view->model()->rowCount(); ++i) { const QModelIndex index = view->model()->index(i, 0); - if (index.data(InstanceViewRoles::GroupRole).toString() == text) - { + if (index.data(InstanceViewRoles::GroupRole).toString() == text) { indices.append(index); } } diff --git a/launcher/ui/instanceview/VisualGroup.h b/launcher/ui/instanceview/VisualGroup.h index 5a743aa18..8c6f06bcc 100644 --- a/launcher/ui/instanceview/VisualGroup.h +++ b/launcher/ui/instanceview/VisualGroup.h @@ -1,70 +1,82 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Tayou * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ #pragma once -#include #include -#include +#include #include +#include class InstanceView; class QPainter; class QModelIndex; -struct VisualRow -{ +struct VisualRow { QList items; int height = 0; int top = 0; - inline int size() const - { - return items.size(); - } - inline QModelIndex &operator[](int i) - { - return items[i]; - } + inline int size() const { return items.size(); } + inline QModelIndex& operator[](int i) { return items[i]; } }; -struct VisualGroup -{ -/* constructors */ - VisualGroup(const QString &text, InstanceView *view); - VisualGroup(const VisualGroup *other); +struct VisualGroup { + /* constructors */ + VisualGroup(QString text, InstanceView* view); + explicit VisualGroup(const VisualGroup* other); -/* data */ - InstanceView *view = nullptr; + /* data */ + InstanceView* view = nullptr; QString text; bool collapsed = false; QVector rows; int firstItemIndex = 0; int m_verticalPosition = 0; -/* logic */ + /* logic */ /// update the internal list of items and flow them into the rows. void update(); /// draw the header at y-position. - void drawHeader(QPainter *painter, const QStyleOptionViewItem &option); + void drawHeader(QPainter* painter, const QStyleOptionViewItem& option) const; /// height of the group, in total. includes a small bit of padding. int totalHeight() const; /// height of the group header, in pixels - int headerHeight() const; + static int headerHeight(); /// height of the group content, in pixels int contentHeight() const; @@ -79,26 +91,19 @@ struct VisualGroup int verticalPosition() const; /// relative geometry - top of the row of the given item - int rowTopOf(const QModelIndex &index) const; + int rowTopOf(const QModelIndex& index) const; /// height of the row of the given item - int rowHeightOf(const QModelIndex &index) const; + int rowHeightOf(const QModelIndex& index) const; /// x/y position of the given item inside the group (in items!) - QPair positionOf(const QModelIndex &index) const; + QPair positionOf(const QModelIndex& index) const; - enum HitResult - { - NoHit = 0x0, - TextHit = 0x1, - CheckboxHit = 0x2, - HeaderHit = 0x4, - BodyHit = 0x8 - }; + enum HitResult { NoHit = 0x0, TextHit = 0x1, CheckboxHit = 0x2, HeaderHit = 0x4, BodyHit = 0x8 }; Q_DECLARE_FLAGS(HitResults, HitResult) /// shoot! BANG! what did we hit? - HitResults hitScan (const QPoint &pos) const; + HitResults hitScan(const QPoint& pos) const; QList items() const; }; diff --git a/launcher/ui/pagedialog/PageDialog.cpp b/launcher/ui/pagedialog/PageDialog.cpp index 18d61dc2f..6514217cd 100644 --- a/launcher/ui/pagedialog/PageDialog.cpp +++ b/launcher/ui/pagedialog/PageDialog.cpp @@ -16,9 +16,9 @@ #include "PageDialog.h" #include +#include #include #include -#include #include "Application.h" #include "settings/SettingsObject.h" @@ -26,19 +26,18 @@ #include "ui/widgets/IconLabel.h" #include "ui/widgets/PageContainer.h" -PageDialog::PageDialog(BasePageProvider *pageProvider, QString defaultId, QWidget *parent) - : QDialog(parent) +PageDialog::PageDialog(BasePageProvider* pageProvider, QString defaultId, QWidget* parent) : QDialog(parent) { setWindowTitle(pageProvider->dialogTitle()); m_container = new PageContainer(pageProvider, defaultId, this); - QVBoxLayout *mainLayout = new QVBoxLayout; + QVBoxLayout* mainLayout = new QVBoxLayout; mainLayout->addWidget(m_container); mainLayout->setSpacing(0); mainLayout->setContentsMargins(0, 0, 0, 0); setLayout(mainLayout); - QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Close); + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Close); buttons->button(QDialogButtonBox::Close)->setDefault(true); buttons->setContentsMargins(6, 0, 6, 0); m_container->addButtons(buttons); @@ -49,11 +48,10 @@ PageDialog::PageDialog(BasePageProvider *pageProvider, QString defaultId, QWidge restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("PagedGeometry").toByteArray())); } -void PageDialog::closeEvent(QCloseEvent *event) +void PageDialog::closeEvent(QCloseEvent* event) { qDebug() << "Paged dialog close requested"; - if (m_container->prepareToClose()) - { + if (m_container->prepareToClose()) { qDebug() << "Paged dialog close approved"; APPLICATION->settings()->set("PagedGeometry", saveGeometry().toBase64()); qDebug() << "Paged dialog geometry saved"; diff --git a/launcher/ui/pagedialog/PageDialog.h b/launcher/ui/pagedialog/PageDialog.h index 00d8b725b..aa50bc5e1 100644 --- a/launcher/ui/pagedialog/PageDialog.h +++ b/launcher/ui/pagedialog/PageDialog.h @@ -19,17 +19,15 @@ #include "ui/pages/BasePageProvider.h" class PageContainer; -class PageDialog : public QDialog -{ +class PageDialog : public QDialog { Q_OBJECT -public: - explicit PageDialog(BasePageProvider *pageProvider, QString defaultId = QString(), QWidget *parent = 0); + public: + explicit PageDialog(BasePageProvider* pageProvider, QString defaultId = QString(), QWidget* parent = 0); virtual ~PageDialog() {} -private -slots: - virtual void closeEvent(QCloseEvent *event); + private slots: + virtual void closeEvent(QCloseEvent* event); -private: - PageContainer * m_container; + private: + PageContainer* m_container; }; diff --git a/launcher/ui/pages/BasePage.h b/launcher/ui/pages/BasePage.h index dc2bde992..d35206a08 100644 --- a/launcher/ui/pages/BasePage.h +++ b/launcher/ui/pages/BasePage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/ui/pages/BasePageContainer.h b/launcher/ui/pages/BasePageContainer.h index b41fe12af..a497ef7b3 100644 --- a/launcher/ui/pages/BasePageContainer.h +++ b/launcher/ui/pages/BasePageContainer.h @@ -2,11 +2,11 @@ class BasePage; -class BasePageContainer -{ -public: +class BasePageContainer { + public: virtual ~BasePageContainer(){}; virtual bool selectPage(QString pageId) = 0; + virtual BasePage* selectedPage() const = 0; virtual BasePage* getPage(QString pageId) { return nullptr; }; virtual void refreshContainer() = 0; virtual bool requestClose() = 0; diff --git a/launcher/ui/pages/BasePageProvider.h b/launcher/ui/pages/BasePageProvider.h index 873e8dce2..4c3ecd6c1 100644 --- a/launcher/ui/pages/BasePageProvider.h +++ b/launcher/ui/pages/BasePageProvider.h @@ -15,54 +15,43 @@ #pragma once -#include "ui/pages/BasePage.h" -#include #include +#include +#include "ui/pages/BasePage.h" -class BasePageProvider -{ -public: - virtual QList getPages() = 0; +class BasePageProvider { + public: + virtual QList getPages() = 0; virtual QString dialogTitle() = 0; }; -class GenericPageProvider : public BasePageProvider -{ - typedef std::function PageCreator; -public: - explicit GenericPageProvider(const QString &dialogTitle) - : m_dialogTitle(dialogTitle) - { - } +class GenericPageProvider : public BasePageProvider { + typedef std::function PageCreator; + + public: + explicit GenericPageProvider(const QString& dialogTitle) : m_dialogTitle(dialogTitle) {} virtual ~GenericPageProvider() {} - QList getPages() override + QList getPages() override { - QList pages; - for (PageCreator creator : m_creators) - { + QList pages; + for (PageCreator creator : m_creators) { pages.append(creator()); } return pages; } QString dialogTitle() override { return m_dialogTitle; } - void setDialogTitle(const QString &title) - { - m_dialogTitle = title; - } - void addPageCreator(PageCreator page) - { - m_creators.append(page); - } + void setDialogTitle(const QString& title) { m_dialogTitle = title; } + void addPageCreator(PageCreator page) { m_creators.append(page); } - template + template void addPage() { - addPageCreator([](){return new PageClass();}); + addPageCreator([]() { return new PageClass(); }); } -private: + private: QList m_creators; QString m_dialogTitle; }; diff --git a/launcher/ui/pages/global/APIPage.cpp b/launcher/ui/pages/global/APIPage.cpp index 668aa0078..82aa76a4f 100644 --- a/launcher/ui/pages/global/APIPage.cpp +++ b/launcher/ui/pages/global/APIPage.cpp @@ -39,37 +39,30 @@ #include "APIPage.h" #include "ui_APIPage.h" -#include #include +#include #include #include #include #include #include +#include "Application.h" +#include "BuildConfig.h" +#include "net/PasteUpload.h" #include "settings/SettingsObject.h" #include "tools/BaseProfiler.h" -#include "Application.h" -#include "net/PasteUpload.h" -#include "BuildConfig.h" -APIPage::APIPage(QWidget *parent) : - QWidget(parent), - ui(new Ui::APIPage) +APIPage::APIPage(QWidget* parent) : QWidget(parent), ui(new Ui::APIPage) { // This is here so you can reorder the entries in the combobox without messing stuff up - int comboBoxEntries[] = { - PasteUpload::PasteType::Mclogs, - PasteUpload::PasteType::NullPointer, - PasteUpload::PasteType::PasteGG, - PasteUpload::PasteType::Hastebin - }; + int comboBoxEntries[] = { PasteUpload::PasteType::Mclogs, PasteUpload::PasteType::NullPointer, PasteUpload::PasteType::PasteGG, + PasteUpload::PasteType::Hastebin }; static QRegularExpression validUrlRegExp("https?://.+"); - static QRegularExpression validMSAClientID(QRegularExpression::anchoredPattern( - "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}")); - static QRegularExpression validFlameKey(QRegularExpression::anchoredPattern( - "\\$2[ayb]\\$.{56}")); + static QRegularExpression validMSAClientID( + QRegularExpression::anchoredPattern("[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}")); + static QRegularExpression validFlameKey(QRegularExpression::anchoredPattern("\\$2[ayb]\\$.{56}")); ui->setupUi(this); @@ -77,7 +70,7 @@ APIPage::APIPage(QWidget *parent) : ui->pasteTypeComboBox->addItem(PasteUpload::PasteTypes.at(pasteType).name, pasteType); } - void (QComboBox::*currentIndexChangedSignal)(int) (&QComboBox::currentIndexChanged); + void (QComboBox::*currentIndexChangedSignal)(int)(&QComboBox::currentIndexChanged); connect(ui->pasteTypeComboBox, currentIndexChangedSignal, this, &APIPage::updateBaseURLPlaceholder); // This function needs to be called even when the ComboBox's index is still in its default state. updateBaseURLPlaceholder(ui->pasteTypeComboBox->currentIndex()); @@ -110,12 +103,9 @@ void APIPage::resetBaseURLNote() void APIPage::updateBaseURLNote(int index) { - if (baseURLPasteType == index) - { + if (baseURLPasteType == index) { ui->baseURLNote->hide(); - } - else if (!ui->baseURLEntry->text().isEmpty()) - { + } else if (!ui->baseURLEntry->text().isEmpty()) { ui->baseURLNote->show(); } } @@ -136,8 +126,7 @@ void APIPage::loadSettings() ui->baseURLEntry->setText(pastebinURL); int pasteTypeIndex = ui->pasteTypeComboBox->findData(pasteType); - if (pasteTypeIndex == -1) - { + if (pasteTypeIndex == -1) { pasteTypeIndex = ui->pasteTypeComboBox->findData(PasteUpload::PasteType::Mclogs); ui->baseURLEntry->clear(); } @@ -167,15 +156,13 @@ void APIPage::applySettings() s->set("MSAClientIDOverride", msaClientID); QUrl metaURL(ui->metaURL->text()); // Add required trailing slash - if (!metaURL.isEmpty() && !metaURL.path().endsWith('/')) - { + if (!metaURL.isEmpty() && !metaURL.path().endsWith('/')) { QString path = metaURL.path(); path.append('/'); metaURL.setPath(path); } // Don't allow HTTP, since meta is basically RCE with all the jar files. - if(!metaURL.isEmpty() && metaURL.scheme() == "http") - { + if (!metaURL.isEmpty() && metaURL.scheme() == "http") { metaURL.setScheme("https"); } diff --git a/launcher/ui/pages/global/APIPage.h b/launcher/ui/pages/global/APIPage.h index 17e62ae7f..d4ed92900 100644 --- a/launcher/ui/pages/global/APIPage.h +++ b/launcher/ui/pages/global/APIPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2022 Jamie Mansfield * Copyright (c) 2022 Lenny McLennington @@ -39,41 +39,28 @@ #include -#include "ui/pages/BasePage.h" #include +#include "ui/pages/BasePage.h" namespace Ui { class APIPage; } -class APIPage : public QWidget, public BasePage -{ +class APIPage : public QWidget, public BasePage { Q_OBJECT -public: - explicit APIPage(QWidget *parent = 0); + public: + explicit APIPage(QWidget* parent = 0); ~APIPage(); - QString displayName() const override - { - return tr("APIs"); - } - QIcon icon() const override - { - return APPLICATION->getThemedIcon("worlds"); - } - QString id() const override - { - return "apis"; - } - QString helpPage() const override - { - return "APIs"; - } + QString displayName() const override { return tr("APIs"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("worlds"); } + QString id() const override { return "apis"; } + QString helpPage() const override { return "APIs"; } virtual bool apply() override; void retranslate() override; -private: + private: int baseURLPasteType; void resetBaseURLNote(); void updateBaseURLNote(int index); @@ -81,7 +68,6 @@ private: void loadSettings(); void applySettings(); -private: - Ui::APIPage *ui; + private: + Ui::APIPage* ui; }; - diff --git a/launcher/ui/pages/global/APIPage.ui b/launcher/ui/pages/global/APIPage.ui index 40b89d914..492741ba4 100644 --- a/launcher/ui/pages/global/APIPage.ui +++ b/launcher/ui/pages/global/APIPage.ui @@ -30,7 +30,7 @@ - Services + Services diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index 278f45c49..5c6fb092b 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2022 Jamie Mansfield * @@ -44,29 +44,27 @@ #include "net/NetJob.h" -#include "ui/dialogs/ProgressDialog.h" -#include "ui/dialogs/OfflineLoginDialog.h" +#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/LoginDialog.h" #include "ui/dialogs/MSALoginDialog.h" -#include "ui/dialogs/CustomMessageBox.h" +#include "ui/dialogs/OfflineLoginDialog.h" +#include "ui/dialogs/ProgressDialog.h" #include "ui/dialogs/SkinUploadDialog.h" -#include "tasks/Task.h" #include "minecraft/auth/AccountTask.h" #include "minecraft/services/SkinDelete.h" +#include "tasks/Task.h" #include "Application.h" #include "BuildConfig.h" -AccountListPage::AccountListPage(QWidget *parent) - : QMainWindow(parent), ui(new Ui::AccountListPage) +AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new Ui::AccountListPage) { ui->setupUi(this); - ui->listView->setEmptyString(tr( - "Welcome!\n" - "If you're new here, you can click the \"Add\" button to add your Mojang or Minecraft account." - )); + ui->listView->setEmptyString( + tr("Welcome!\n" + "If you're new here, you can click the \"Add\" button to add your Mojang or Minecraft account.")); ui->listView->setEmptyMode(VersionListView::String); ui->listView->setContextMenuPolicy(Qt::CustomContextMenu); @@ -82,11 +80,10 @@ AccountListPage::AccountListPage(QWidget *parent) // Expand the account column - QItemSelectionModel *selectionModel = ui->listView->selectionModel(); + QItemSelectionModel* selectionModel = ui->listView->selectionModel(); - connect(selectionModel, &QItemSelectionModel::selectionChanged, [this](const QItemSelection &sel, const QItemSelection &dsel) { - updateButtonStates(); - }); + connect(selectionModel, &QItemSelectionModel::selectionChanged, + [this]([[maybe_unused]] const QItemSelection& sel, [[maybe_unused]] const QItemSelection& dsel) { updateButtonStates(); }); connect(ui->listView, &VersionListView::customContextMenuRequested, this, &AccountListPage::ShowContextMenu); connect(m_accounts.get(), &AccountList::listChanged, this, &AccountListPage::listChanged); @@ -121,21 +118,19 @@ void AccountListPage::ShowContextMenu(const QPoint& pos) void AccountListPage::changeEvent(QEvent* event) { - if (event->type() == QEvent::LanguageChange) - { + if (event->type() == QEvent::LanguageChange) { ui->retranslateUi(this); } QMainWindow::changeEvent(event); } -QMenu * AccountListPage::createPopupMenu() +QMenu* AccountListPage::createPopupMenu() { QMenu* filteredMenu = QMainWindow::createPopupMenu(); - filteredMenu->removeAction(ui->toolBar->toggleViewAction() ); + filteredMenu->removeAction(ui->toolBar->toggleViewAction()); return filteredMenu; } - void AccountListPage::listChanged() { updateButtonStates(); @@ -143,13 +138,10 @@ void AccountListPage::listChanged() void AccountListPage::on_actionAddMojang_triggered() { - MinecraftAccountPtr account = LoginDialog::newAccount( - this, - tr("Please enter your Mojang account email and password to add your account.") - ); + MinecraftAccountPtr account = + LoginDialog::newAccount(this, tr("Please enter your Mojang account email and password to add your account.")); - if (account) - { + if (account) { m_accounts->addAccount(account); if (m_accounts->count() == 1) { m_accounts->setDefaultAccount(account); @@ -159,26 +151,10 @@ void AccountListPage::on_actionAddMojang_triggered() void AccountListPage::on_actionAddMicrosoft_triggered() { - if(BuildConfig.BUILD_PLATFORM == "osx64") { - CustomMessageBox::selectable( - this, - tr("Microsoft Accounts not available"), - //: %1 refers to the launcher itself - tr( - "Microsoft accounts are only usable on macOS 10.13 or newer, with fully updated %1.\n\n" - "Please update both your operating system and %1." - ).arg(BuildConfig.LAUNCHER_DISPLAYNAME), - QMessageBox::Warning - )->exec(); - return; - } - MinecraftAccountPtr account = MSALoginDialog::newAccount( - this, - tr("Please enter your Mojang account email and password to add your account.") - ); + MinecraftAccountPtr account = + MSALoginDialog::newAccount(this, tr("Please enter your Mojang account email and password to add your account.")); - if (account) - { + if (account) { m_accounts->addAccount(account); if (m_accounts->count() == 1) { m_accounts->setDefaultAccount(account); @@ -189,25 +165,17 @@ 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." - "

    " - "If you have lost your account you can contact Microsoft for support." - ) - ); + QMessageBox::warning(this, tr("Error"), + tr("You must add a Microsoft or Mojang account that owns Minecraft before you can add an offline account." + "

    " + "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.") - ); + MinecraftAccountPtr account = + OfflineLoginDialog::newAccount(this, tr("Please enter your desired username to add your offline account.")); - if (account) - { + if (account) { m_accounts->addAccount(account); if (m_accounts->count() == 1) { m_accounts->setDefaultAccount(account); @@ -218,14 +186,14 @@ void AccountListPage::on_actionAddOffline_triggered() void AccountListPage::on_actionRemove_triggered() { QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes(); - if (selection.size() > 0) - { + if (selection.size() > 0) { QModelIndex selected = selection.first(); m_accounts->removeAccount(selected); } } -void AccountListPage::on_actionRefresh_triggered() { +void AccountListPage::on_actionRefresh_triggered() +{ QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes(); if (selection.size() > 0) { QModelIndex selected = selection.first(); @@ -234,12 +202,10 @@ void AccountListPage::on_actionRefresh_triggered() { } } - void AccountListPage::on_actionSetDefault_triggered() { QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes(); - if (selection.size() > 0) - { + if (selection.size() > 0) { QModelIndex selected = selection.first(); MinecraftAccountPtr account = selected.data(AccountList::PointerRole).value(); m_accounts->setDefaultAccount(account); @@ -258,8 +224,7 @@ void AccountListPage::updateButtonStates() bool hasSelection = !selection.empty(); bool accountIsReady = false; bool accountIsOnline = false; - if (hasSelection) - { + if (hasSelection) { QModelIndex selected = selection.first(); MinecraftAccountPtr account = selected.data(AccountList::PointerRole).value(); accountIsReady = !account->isActive(); @@ -271,11 +236,10 @@ void AccountListPage::updateButtonStates() ui->actionDeleteSkin->setEnabled(accountIsReady && accountIsOnline); ui->actionRefresh->setEnabled(accountIsReady && accountIsOnline); - if(m_accounts->defaultAccount().get() == nullptr) { + if (m_accounts->defaultAccount().get() == nullptr) { ui->actionNoDefault->setEnabled(false); ui->actionNoDefault->setChecked(true); - } - else { + } else { ui->actionNoDefault->setEnabled(true); ui->actionNoDefault->setChecked(false); } @@ -284,8 +248,7 @@ void AccountListPage::updateButtonStates() void AccountListPage::on_actionUploadSkin_triggered() { QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes(); - if (selection.size() > 0) - { + if (selection.size() > 0) { QModelIndex selected = selection.first(); MinecraftAccountPtr account = selected.data(AccountList::PointerRole).value(); SkinUploadDialog dialog(account, this); diff --git a/launcher/ui/pages/global/AccountListPage.h b/launcher/ui/pages/global/AccountListPage.h index 9395e92bf..add0f4aa0 100644 --- a/launcher/ui/pages/global/AccountListPage.h +++ b/launcher/ui/pages/global/AccountListPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2022 Jamie Mansfield * @@ -41,47 +41,35 @@ #include "ui/pages/BasePage.h" -#include "minecraft/auth/AccountList.h" #include "Application.h" +#include "minecraft/auth/AccountList.h" -namespace Ui -{ +namespace Ui { class AccountListPage; } class AuthenticateTask; -class AccountListPage : public QMainWindow, public BasePage -{ +class AccountListPage : public QMainWindow, public BasePage { Q_OBJECT -public: - explicit AccountListPage(QWidget *parent = 0); + public: + explicit AccountListPage(QWidget* parent = 0); ~AccountListPage(); - QString displayName() const override - { - return tr("Accounts"); - } + QString displayName() const override { return tr("Accounts"); } QIcon icon() const override { auto icon = APPLICATION->getThemedIcon("accounts"); - if(icon.isNull()) - { + if (icon.isNull()) { icon = APPLICATION->getThemedIcon("noaccount"); } return icon; } - QString id() const override - { - return "accounts"; - } - QString helpPage() const override - { - return "Getting-Started#adding-an-account"; - } + QString id() const override { return "accounts"; } + QString helpPage() const override { return "Getting-Started#adding-an-account"; } void retranslate() override; -public slots: + public slots: void on_actionAddMojang_triggered(); void on_actionAddMicrosoft_triggered(); void on_actionAddOffline_triggered(); @@ -97,12 +85,12 @@ public slots: //! Updates the states of the dialog's buttons. void updateButtonStates(); -protected slots: - void ShowContextMenu(const QPoint &pos); + protected slots: + void ShowContextMenu(const QPoint& pos); -private: - void changeEvent(QEvent * event) override; - QMenu * createPopupMenu() override; + private: + void changeEvent(QEvent* event) override; + QMenu* createPopupMenu() override; shared_qobject_ptr m_accounts; - Ui::AccountListPage *ui; + Ui::AccountListPage* ui; }; diff --git a/launcher/ui/pages/global/CustomCommandsPage.cpp b/launcher/ui/pages/global/CustomCommandsPage.cpp index df1420ca6..cc8518c2f 100644 --- a/launcher/ui/pages/global/CustomCommandsPage.cpp +++ b/launcher/ui/pages/global/CustomCommandsPage.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu * @@ -35,13 +35,12 @@ */ #include "CustomCommandsPage.h" -#include -#include #include +#include +#include -CustomCommandsPage::CustomCommandsPage(QWidget* parent): QWidget(parent) +CustomCommandsPage::CustomCommandsPage(QWidget* parent) : QWidget(parent) { - auto verticalLayout = new QVBoxLayout(this); verticalLayout->setObjectName(QStringLiteral("verticalLayout")); verticalLayout->setContentsMargins(0, 0, 0, 0); @@ -56,9 +55,7 @@ CustomCommandsPage::CustomCommandsPage(QWidget* parent): QWidget(parent) loadSettings(); } -CustomCommandsPage::~CustomCommandsPage() -{ -} +CustomCommandsPage::~CustomCommandsPage() {} bool CustomCommandsPage::apply() { @@ -77,13 +74,8 @@ void CustomCommandsPage::applySettings() void CustomCommandsPage::loadSettings() { auto s = APPLICATION->settings(); - commands->initialize( - false, - true, - s->get("PreLaunchCommand").toString(), - s->get("WrapperCommand").toString(), - s->get("PostExitCommand").toString() - ); + commands->initialize(false, true, s->get("PreLaunchCommand").toString(), s->get("WrapperCommand").toString(), + s->get("PostExitCommand").toString()); } void CustomCommandsPage::retranslate() diff --git a/launcher/ui/pages/global/CustomCommandsPage.h b/launcher/ui/pages/global/CustomCommandsPage.h index 865503ff7..ec1204ffe 100644 --- a/launcher/ui/pages/global/CustomCommandsPage.h +++ b/launcher/ui/pages/global/CustomCommandsPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -35,42 +35,29 @@ #pragma once -#include #include +#include -#include "ui/pages/BasePage.h" #include +#include "ui/pages/BasePage.h" #include "ui/widgets/CustomCommands.h" -class CustomCommandsPage : public QWidget, public BasePage -{ +class CustomCommandsPage : public QWidget, public BasePage { Q_OBJECT -public: - explicit CustomCommandsPage(QWidget *parent = 0); + public: + explicit CustomCommandsPage(QWidget* parent = 0); ~CustomCommandsPage(); - QString displayName() const override - { - return tr("Custom Commands"); - } - QIcon icon() const override - { - return APPLICATION->getThemedIcon("custom-commands"); - } - QString id() const override - { - return "custom-commands"; - } - QString helpPage() const override - { - return "Custom-commands"; - } + QString displayName() const override { return tr("Custom Commands"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("custom-commands"); } + QString id() const override { return "custom-commands"; } + QString helpPage() const override { return "Custom-commands"; } bool apply() override; void retranslate() override; -private: + private: void applySettings(); void loadSettings(); - CustomCommands * commands; + CustomCommands* commands; }; diff --git a/launcher/ui/pages/global/ExternalToolsPage.cpp b/launcher/ui/pages/global/ExternalToolsPage.cpp index 5ba0ebc28..33e9c5388 100644 --- a/launcher/ui/pages/global/ExternalToolsPage.cpp +++ b/launcher/ui/pages/global/ExternalToolsPage.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -36,20 +36,18 @@ #include "ExternalToolsPage.h" #include "ui_ExternalToolsPage.h" -#include #include +#include #include #include +#include +#include +#include "Application.h" #include "settings/SettingsObject.h" #include "tools/BaseProfiler.h" -#include -#include "Application.h" -#include -ExternalToolsPage::ExternalToolsPage(QWidget *parent) : - QWidget(parent), - ui(new Ui::ExternalToolsPage) +ExternalToolsPage::ExternalToolsPage(QWidget* parent) : QWidget(parent), ui(new Ui::ExternalToolsPage) { ui->setupUi(this); ui->tabWidget->tabBar()->hide(); @@ -87,12 +85,9 @@ void ExternalToolsPage::applySettings() // Editors QString jsonEditor = ui->jsonEditorTextBox->text(); - if (!jsonEditor.isEmpty() && - (!QFileInfo(jsonEditor).exists() || !QFileInfo(jsonEditor).isExecutable())) - { + if (!jsonEditor.isEmpty() && (!QFileInfo(jsonEditor).exists() || !QFileInfo(jsonEditor).isExecutable())) { QString found = QStandardPaths::findExecutable(jsonEditor); - if (!found.isEmpty()) - { + if (!found.isEmpty()) { jsonEditor = found; } } @@ -103,21 +98,16 @@ void ExternalToolsPage::on_jprofilerPathBtn_clicked() { QString raw_dir = ui->jprofilerPathEdit->text(); QString error; - do - { + do { raw_dir = QFileDialog::getExistingDirectory(this, tr("JProfiler Folder"), raw_dir); - if (raw_dir.isEmpty()) - { + if (raw_dir.isEmpty()) { break; } QString cooked_dir = FS::NormalizePath(raw_dir); - if (!APPLICATION->profilers()["jprofiler"]->check(cooked_dir, &error)) - { + if (!APPLICATION->profilers()["jprofiler"]->check(cooked_dir, &error)) { QMessageBox::critical(this, tr("Error"), tr("Error while checking JProfiler install:\n%1").arg(error)); continue; - } - else - { + } else { ui->jprofilerPathEdit->setText(cooked_dir); break; } @@ -126,12 +116,9 @@ void ExternalToolsPage::on_jprofilerPathBtn_clicked() void ExternalToolsPage::on_jprofilerCheckBtn_clicked() { QString error; - if (!APPLICATION->profilers()["jprofiler"]->check(ui->jprofilerPathEdit->text(), &error)) - { + if (!APPLICATION->profilers()["jprofiler"]->check(ui->jprofilerPathEdit->text(), &error)) { QMessageBox::critical(this, tr("Error"), tr("Error while checking JProfiler install:\n%1").arg(error)); - } - else - { + } else { QMessageBox::information(this, tr("OK"), tr("JProfiler setup seems to be OK")); } } @@ -140,21 +127,16 @@ void ExternalToolsPage::on_jvisualvmPathBtn_clicked() { QString raw_dir = ui->jvisualvmPathEdit->text(); QString error; - do - { + do { raw_dir = QFileDialog::getOpenFileName(this, tr("JVisualVM Executable"), raw_dir); - if (raw_dir.isEmpty()) - { + if (raw_dir.isEmpty()) { break; } QString cooked_dir = FS::NormalizePath(raw_dir); - if (!APPLICATION->profilers()["jvisualvm"]->check(cooked_dir, &error)) - { + if (!APPLICATION->profilers()["jvisualvm"]->check(cooked_dir, &error)) { QMessageBox::critical(this, tr("Error"), tr("Error while checking JVisualVM install:\n%1").arg(error)); continue; - } - else - { + } else { ui->jvisualvmPathEdit->setText(cooked_dir); break; } @@ -163,12 +145,9 @@ void ExternalToolsPage::on_jvisualvmPathBtn_clicked() void ExternalToolsPage::on_jvisualvmCheckBtn_clicked() { QString error; - if (!APPLICATION->profilers()["jvisualvm"]->check(ui->jvisualvmPathEdit->text(), &error)) - { + if (!APPLICATION->profilers()["jvisualvm"]->check(ui->jvisualvmPathEdit->text(), &error)) { QMessageBox::critical(this, tr("Error"), tr("Error while checking JVisualVM install:\n%1").arg(error)); - } - else - { + } else { QMessageBox::information(this, tr("OK"), tr("JVisualVM setup seems to be OK")); } } @@ -177,25 +156,20 @@ void ExternalToolsPage::on_mceditPathBtn_clicked() { QString raw_dir = ui->mceditPathEdit->text(); QString error; - do - { + do { #ifdef Q_OS_OSX raw_dir = QFileDialog::getOpenFileName(this, tr("MCEdit Application"), raw_dir); #else raw_dir = QFileDialog::getExistingDirectory(this, tr("MCEdit Folder"), raw_dir); #endif - if (raw_dir.isEmpty()) - { + if (raw_dir.isEmpty()) { break; } QString cooked_dir = FS::NormalizePath(raw_dir); - if (!APPLICATION->mcedit()->check(cooked_dir, error)) - { + if (!APPLICATION->mcedit()->check(cooked_dir, error)) { QMessageBox::critical(this, tr("Error"), tr("Error while checking MCEdit install:\n%1").arg(error)); continue; - } - else - { + } else { ui->mceditPathEdit->setText(cooked_dir); break; } @@ -204,43 +178,34 @@ void ExternalToolsPage::on_mceditPathBtn_clicked() void ExternalToolsPage::on_mceditCheckBtn_clicked() { QString error; - if (!APPLICATION->mcedit()->check(ui->mceditPathEdit->text(), error)) - { + if (!APPLICATION->mcedit()->check(ui->mceditPathEdit->text(), error)) { QMessageBox::critical(this, tr("Error"), tr("Error while checking MCEdit install:\n%1").arg(error)); - } - else - { + } else { QMessageBox::information(this, tr("OK"), tr("MCEdit setup seems to be OK")); } } void ExternalToolsPage::on_jsonEditorBrowseBtn_clicked() { - QString raw_file = QFileDialog::getOpenFileName( - this, tr("JSON Editor"), - ui->jsonEditorTextBox->text().isEmpty() + QString raw_file = QFileDialog::getOpenFileName(this, tr("JSON Editor"), + ui->jsonEditorTextBox->text().isEmpty() #if defined(Q_OS_LINUX) - ? QString("/usr/bin") + ? QString("/usr/bin") #else - ? QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation).first() + ? QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation).first() #endif - : ui->jsonEditorTextBox->text()); + : ui->jsonEditorTextBox->text()); - if (raw_file.isEmpty()) - { + if (raw_file.isEmpty()) { return; } QString cooked_file = FS::NormalizePath(raw_file); // it has to exist and be an executable - if (QFileInfo(cooked_file).exists() && QFileInfo(cooked_file).isExecutable()) - { + if (QFileInfo(cooked_file).exists() && QFileInfo(cooked_file).isExecutable()) { ui->jsonEditorTextBox->setText(cooked_file); - } - else - { - QMessageBox::warning(this, tr("Invalid"), - tr("The file chosen does not seem to be an executable")); + } else { + QMessageBox::warning(this, tr("Invalid"), tr("The file chosen does not seem to be an executable")); } } diff --git a/launcher/ui/pages/global/ExternalToolsPage.h b/launcher/ui/pages/global/ExternalToolsPage.h index 8bd38a196..7248f0c99 100644 --- a/launcher/ui/pages/global/ExternalToolsPage.h +++ b/launcher/ui/pages/global/ExternalToolsPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -37,54 +37,42 @@ #include -#include "ui/pages/BasePage.h" #include +#include "ui/pages/BasePage.h" namespace Ui { class ExternalToolsPage; } -class ExternalToolsPage : public QWidget, public BasePage -{ +class ExternalToolsPage : public QWidget, public BasePage { Q_OBJECT -public: - explicit ExternalToolsPage(QWidget *parent = 0); + public: + explicit ExternalToolsPage(QWidget* parent = 0); ~ExternalToolsPage(); - QString displayName() const override - { - return tr("External Tools"); - } + QString displayName() const override { return tr("External Tools"); } QIcon icon() const override { auto icon = APPLICATION->getThemedIcon("externaltools"); - if(icon.isNull()) - { + if (icon.isNull()) { icon = APPLICATION->getThemedIcon("loadermods"); } return icon; } - QString id() const override - { - return "external-tools"; - } - QString helpPage() const override - { - return "Tools"; - } + QString id() const override { return "external-tools"; } + QString helpPage() const override { return "Tools"; } virtual bool apply() override; void retranslate() override; -private: + private: void loadSettings(); void applySettings(); -private: - Ui::ExternalToolsPage *ui; + private: + Ui::ExternalToolsPage* ui; -private -slots: + private slots: void on_jprofilerPathBtn_clicked(); void on_jprofilerCheckBtn_clicked(); void on_jvisualvmPathBtn_clicked(); diff --git a/launcher/ui/pages/global/ExternalToolsPage.ui b/launcher/ui/pages/global/ExternalToolsPage.ui index 3643094df..47c77842a 100644 --- a/launcher/ui/pages/global/ExternalToolsPage.ui +++ b/launcher/ui/pages/global/ExternalToolsPage.ui @@ -47,7 +47,7 @@ - ... + Browse @@ -84,7 +84,7 @@ - ... + Browse @@ -121,7 +121,7 @@ - ... + Browse @@ -166,7 +166,7 @@ - ... + Browse diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 81dd4cc12..45d8f0180 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu * @@ -38,22 +38,22 @@ #include "JavaCommon.h" #include "ui_JavaPage.h" +#include #include #include -#include #include #include "ui/dialogs/VersionSelectDialog.h" -#include "java/JavaUtils.h" #include "java/JavaInstallList.h" +#include "java/JavaUtils.h" -#include "settings/SettingsObject.h" #include -#include "Application.h" #include +#include "Application.h" +#include "settings/SettingsObject.h" -JavaPage::JavaPage(QWidget *parent) : QWidget(parent), ui(new Ui::JavaPage) +JavaPage::JavaPage(QWidget* parent) : QWidget(parent), ui(new Ui::JavaPage) { ui->setupUi(this); ui->tabWidget->tabBar()->hide(); @@ -80,13 +80,10 @@ void JavaPage::applySettings() // Memory int min = ui->minMemSpinBox->value(); int max = ui->maxMemSpinBox->value(); - if(min < max) - { + if (min < max) { s->set("MinMemAlloc", min); s->set("MaxMemAlloc", max); - } - else - { + } else { s->set("MinMemAlloc", max); s->set("MaxMemAlloc", min); } @@ -105,13 +102,10 @@ void JavaPage::loadSettings() // Memory int min = s->get("MinMemAlloc").toInt(); int max = s->get("MaxMemAlloc").toInt(); - if(min < max) - { + if (min < max) { ui->minMemSpinBox->setValue(min); ui->maxMemSpinBox->setValue(max); - } - else - { + } else { ui->minMemSpinBox->setValue(max); ui->maxMemSpinBox->setValue(min); } @@ -137,8 +131,7 @@ void JavaPage::on_javaDetectBtn_clicked() vselect.setResizeOn(2); vselect.exec(); - if (vselect.result() == QDialog::Accepted && vselect.selectedVersion()) - { + if (vselect.result() == QDialog::Accepted && vselect.selectedVersion()) { java = std::dynamic_pointer_cast(vselect.selectedVersion()); ui->javaPathTextBox->setText(java->path); } @@ -149,15 +142,14 @@ void JavaPage::on_javaBrowseBtn_clicked() QString raw_path = QFileDialog::getOpenFileName(this, tr("Find Java executable")); // do not allow current dir - it's dirty. Do not allow dirs that don't exist - if(raw_path.isEmpty()) - { + if (raw_path.isEmpty()) { return; } QString cooked_path = FS::NormalizePath(raw_path); - QFileInfo javaInfo(cooked_path);; - if(!javaInfo.exists() || !javaInfo.isExecutable()) - { + QFileInfo javaInfo(cooked_path); + ; + if (!javaInfo.exists() || !javaInfo.isExecutable()) { return; } ui->javaPathTextBox->setText(cooked_path); @@ -165,18 +157,16 @@ void JavaPage::on_javaBrowseBtn_clicked() void JavaPage::on_javaTestBtn_clicked() { - if(checker) - { + if (checker) { return; } - checker.reset(new JavaCommon::TestCheck( - this, ui->javaPathTextBox->text(), ui->jvmArgsTextBox->toPlainText().replace("\n", " "), - ui->minMemSpinBox->value(), ui->maxMemSpinBox->value(), ui->permGenSpinBox->value())); + checker.reset(new JavaCommon::TestCheck(this, ui->javaPathTextBox->text(), ui->jvmArgsTextBox->toPlainText().replace("\n", " "), + ui->minMemSpinBox->value(), ui->maxMemSpinBox->value(), ui->permGenSpinBox->value())); connect(checker.get(), SIGNAL(finished()), SLOT(checkerFinished())); checker->run(); } -void JavaPage::on_maxMemSpinBox_valueChanged(int i) +void JavaPage::on_maxMemSpinBox_valueChanged([[maybe_unused]] int i) { updateThresholds(); } diff --git a/launcher/ui/pages/global/JavaPage.h b/launcher/ui/pages/global/JavaPage.h index 2ef6d7493..1a1bd96e1 100644 --- a/launcher/ui/pages/global/JavaPage.h +++ b/launcher/ui/pages/global/JavaPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -35,62 +35,47 @@ #pragma once -#include -#include -#include "ui/pages/BasePage.h" -#include "JavaCommon.h" #include #include +#include +#include +#include "JavaCommon.h" +#include "ui/pages/BasePage.h" class SettingsObject; -namespace Ui -{ +namespace Ui { class JavaPage; } -class JavaPage : public QWidget, public BasePage -{ +class JavaPage : public QWidget, public BasePage { Q_OBJECT -public: - explicit JavaPage(QWidget *parent = 0); + public: + explicit JavaPage(QWidget* parent = 0); ~JavaPage(); - QString displayName() const override - { - return tr("Java"); - } - QIcon icon() const override - { - return APPLICATION->getThemedIcon("java"); - } - QString id() const override - { - return "java-settings"; - } - QString helpPage() const override - { - return "Java-settings"; - } + QString displayName() const override { return tr("Java"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("java"); } + QString id() const override { return "java-settings"; } + QString helpPage() const override { return "Java-settings"; } bool apply() override; void retranslate() override; void updateThresholds(); -private: + private: void applySettings(); void loadSettings(); -private -slots: + private slots: void on_javaDetectBtn_clicked(); void on_javaTestBtn_clicked(); void on_javaBrowseBtn_clicked(); void on_maxMemSpinBox_valueChanged(int i); void checkerFinished(); -private: - Ui::JavaPage *ui; + private: + Ui::JavaPage* ui; unique_qobject_ptr checker; }; diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index 6749cbe41..5a547637c 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -58,7 +58,7 @@ - &PermGen: + &PermGen: permGenSpinBox @@ -160,117 +160,7 @@ Java Runtime - - - - - 0 - 0 - - - - &Auto-detect... - - - - - - - - 0 - 0 - - - - JVM arguments: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - &Java path: - - - javaPathTextBox - - - - - - - - 0 - 0 - - - - &Test - - - - - - - - - - - - - 0 - 0 - - - - - 28 - 16777215 - - - - ... - - - - - - - - - - 0 - 0 - - - - If enabled, the launcher will not check if an instance is compatible with the selected Java version. - - - &Skip Java compatibility checks - - - - - - - If enabled, the launcher will not prompt you to choose a Java version if one isn't found. - - - Skip Java &Wizard - - - - + true @@ -289,6 +179,114 @@ + + + + + 0 + 0 + + + + If enabled, the launcher will not check if an instance is compatible with the selected Java version. + + + &Skip Java compatibility checks + + + + + + + + + + 0 + 0 + + + + &Auto-detect... + + + + + + + + 0 + 0 + + + + &Test + + + + + + + + + + 0 + 0 + + + + JVM arguments: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + 0 + 0 + + + + &Java path: + + + javaPathTextBox + + + + + + + + + + + 0 + 0 + + + + Browse + + + + + + + + + If enabled, the launcher will not prompt you to choose a Java version if one isn't found. + + + Skip Java &Wizard + + + @@ -317,8 +315,6 @@ permGenSpinBox javaBrowseBtn javaPathTextBox - javaDetectBtn - javaTestBtn tabWidget diff --git a/launcher/ui/pages/global/LanguagePage.cpp b/launcher/ui/pages/global/LanguagePage.cpp index fcd174bdb..af6fc1727 100644 --- a/launcher/ui/pages/global/LanguagePage.cpp +++ b/launcher/ui/pages/global/LanguagePage.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu * @@ -36,23 +36,20 @@ #include "LanguagePage.h" -#include "ui/widgets/LanguageSelectionWidget.h" #include +#include "ui/widgets/LanguageSelectionWidget.h" -LanguagePage::LanguagePage(QWidget* parent) : - QWidget(parent) +LanguagePage::LanguagePage(QWidget* parent) : QWidget(parent) { setObjectName(QStringLiteral("languagePage")); auto layout = new QVBoxLayout(this); mainWidget = new LanguageSelectionWidget(this); - layout->setContentsMargins(0,0,0,0); + layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(mainWidget); retranslate(); } -LanguagePage::~LanguagePage() -{ -} +LanguagePage::~LanguagePage() {} bool LanguagePage::apply() { diff --git a/launcher/ui/pages/global/LanguagePage.h b/launcher/ui/pages/global/LanguagePage.h index 2fd4ab0de..ff7ce7ddc 100644 --- a/launcher/ui/pages/global/LanguagePage.h +++ b/launcher/ui/pages/global/LanguagePage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu * @@ -36,45 +36,32 @@ #pragma once -#include -#include "ui/pages/BasePage.h" #include #include +#include +#include "ui/pages/BasePage.h" class LanguageSelectionWidget; -class LanguagePage : public QWidget, public BasePage -{ +class LanguagePage : public QWidget, public BasePage { Q_OBJECT -public: - explicit LanguagePage(QWidget *parent = 0); + public: + explicit LanguagePage(QWidget* parent = 0); virtual ~LanguagePage(); - QString displayName() const override - { - return tr("Language"); - } - QIcon icon() const override - { - return APPLICATION->getThemedIcon("language"); - } - QString id() const override - { - return "language-settings"; - } - QString helpPage() const override - { - return "Language-settings"; - } + QString displayName() const override { return tr("Language"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("language"); } + QString id() const override { return "language-settings"; } + QString helpPage() const override { return "Language-settings"; } bool apply() override; void retranslate() override; -private: + private: void applySettings(); void loadSettings(); -private: - LanguageSelectionWidget *mainWidget; + private: + LanguageSelectionWidget* mainWidget; }; diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index 816dde723..7f22fdb50 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -3,7 +3,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (c) 2022 dada513 - * Copyright (C) 2022 Tayou + * Copyright (C) 2022 Tayou * * 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 @@ -38,17 +38,17 @@ #include "LauncherPage.h" #include "ui_LauncherPage.h" -#include -#include #include -#include +#include #include +#include +#include -#include "settings/SettingsObject.h" #include #include "Application.h" #include "BuildConfig.h" #include "DesktopServices.h" +#include "settings/SettingsObject.h" #include "ui/themes/ITheme.h" #include "updater/ExternalUpdater.h" @@ -56,15 +56,14 @@ #include // FIXME: possibly move elsewhere -enum InstSortMode -{ +enum InstSortMode { // Sort alphabetically by name. Sort_Name, // Sort by which instance was launched most recently. Sort_LastLaunch }; -LauncherPage::LauncherPage(QWidget *parent) : QWidget(parent), ui(new Ui::LauncherPage) +LauncherPage::LauncherPage(QWidget* parent) : QWidget(parent), ui(new Ui::LauncherPage) { ui->setupUi(this); auto origForeground = ui->fontPreview->palette().color(ui->fontPreview->foregroundRole()); @@ -104,46 +103,39 @@ void LauncherPage::on_instDirBrowseBtn_clicked() QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Instance Folder"), ui->instDirTextBox->text()); // do not allow current dir - it's dirty. Do not allow dirs that don't exist - if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) - { + if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) { QString cooked_dir = FS::NormalizePath(raw_dir); - if (FS::checkProblemticPathJava(QDir(cooked_dir))) - { + if (FS::checkProblemticPathJava(QDir(cooked_dir))) { QMessageBox warning; - warning.setText(tr("You're trying to specify an instance folder which\'s path " - "contains at least one \'!\'. " - "Java is known to cause problems if that is the case, your " - "instances (probably) won't start!")); + warning.setText( + tr("You're trying to specify an instance folder which\'s path " + "contains at least one \'!\'. " + "Java is known to cause problems if that is the case, your " + "instances (probably) won't start!")); warning.setInformativeText( tr("Do you really want to use this path? " "Selecting \"No\" will close this and not alter your instance path.")); warning.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); int result = warning.exec(); - if (result == QMessageBox::Ok) - { + if (result == QMessageBox::Ok) { ui->instDirTextBox->setText(cooked_dir); } - } - else if(DesktopServices::isFlatpak() && raw_dir.startsWith("/run/user")) - { + } else if (DesktopServices::isFlatpak() && raw_dir.startsWith("/run/user")) { QMessageBox warning; warning.setText(tr("You're trying to specify an instance folder " - "which was granted temporarily via Flatpak.\n" - "This is known to cause problems. " - "After a restart the launcher might break, " - "because it will no longer have access to that directory.\n\n" - "Granting %1 access to it via Flatseal is recommended.").arg(BuildConfig.LAUNCHER_DISPLAYNAME)); - warning.setInformativeText( - tr("Do you want to proceed anyway?")); + "which was granted temporarily via Flatpak.\n" + "This is known to cause problems. " + "After a restart the launcher might break, " + "because it will no longer have access to that directory.\n\n" + "Granting %1 access to it via Flatseal is recommended.") + .arg(BuildConfig.LAUNCHER_DISPLAYNAME)); + warning.setInformativeText(tr("Do you want to proceed anyway?")); warning.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); int result = warning.exec(); - if (result == QMessageBox::Ok) - { + if (result == QMessageBox::Ok) { ui->instDirTextBox->setText(cooked_dir); } - } - else - { + } else { ui->instDirTextBox->setText(cooked_dir); } } @@ -154,8 +146,7 @@ void LauncherPage::on_iconsDirBrowseBtn_clicked() QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Icons Folder"), ui->iconsDirTextBox->text()); // do not allow current dir - it's dirty. Do not allow dirs that don't exist - if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) - { + if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) { QString cooked_dir = FS::NormalizePath(raw_dir); ui->iconsDirTextBox->setText(cooked_dir); } @@ -166,8 +157,7 @@ void LauncherPage::on_modsDirBrowseBtn_clicked() QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Mods Folder"), ui->modsDirTextBox->text()); // do not allow current dir - it's dirty. Do not allow dirs that don't exist - if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) - { + if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) { QString cooked_dir = FS::NormalizePath(raw_dir); ui->modsDirTextBox->setText(cooked_dir); } @@ -177,8 +167,7 @@ void LauncherPage::on_downloadsDirBrowseBtn_clicked() { QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Downloads Folder"), ui->downloadsDirTextBox->text()); - if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) - { + if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) { QString cooked_dir = FS::NormalizePath(raw_dir); ui->downloadsDirTextBox->setText(cooked_dir); } @@ -194,8 +183,7 @@ void LauncherPage::applySettings() auto s = APPLICATION->settings(); // Updates - if (APPLICATION->updater()) - { + if (APPLICATION->updater()) { APPLICATION->updater()->setAutomaticallyChecksForUpdates(ui->autoUpdateCheckBox->isChecked()); } @@ -220,15 +208,14 @@ void LauncherPage::applySettings() s->set("DownloadsDirWatchRecursive", ui->downloadsDirWatchRecursiveCheckBox->isChecked()); auto sortMode = (InstSortMode)ui->sortingModeGroup->checkedId(); - switch (sortMode) - { - case Sort_LastLaunch: - s->set("InstSortMode", "LastLaunch"); - break; - case Sort_Name: - default: - s->set("InstSortMode", "Name"); - break; + switch (sortMode) { + case Sort_LastLaunch: + s->set("InstSortMode", "LastLaunch"); + break; + case Sort_Name: + default: + s->set("InstSortMode", "Name"); + break; } // Mods @@ -238,12 +225,10 @@ void LauncherPage::loadSettings() { auto s = APPLICATION->settings(); // Updates - if (APPLICATION->updater()) - { + if (APPLICATION->updater()) { ui->autoUpdateCheckBox->setChecked(APPLICATION->updater()->getAutomaticallyChecksForUpdates()); } - // Toolbar/menu bar settings (not applicable if native menu bar is present) ui->toolsBox->setEnabled(!QMenuBar().isNativeMenuBar()); #ifdef Q_OS_MACOS @@ -261,8 +246,7 @@ void LauncherPage::loadSettings() bool conversionOk = true; int fontSize = APPLICATION->settings()->get("ConsoleFontSize").toInt(&conversionOk); - if(!conversionOk) - { + if (!conversionOk) { fontSize = 11; } ui->fontSizeBox->setValue(fontSize); @@ -279,12 +263,9 @@ void LauncherPage::loadSettings() QString sortMode = s->get("InstSortMode").toString(); - if (sortMode == "LastLaunch") - { + if (sortMode == "LastLaunch") { ui->sortLastLaunchedBtn->setChecked(true); - } - else - { + } else { ui->sortByNameBtn->setChecked(true); } diff --git a/launcher/ui/pages/global/LauncherPage.h b/launcher/ui/pages/global/LauncherPage.h index 33f66f1be..e733224d2 100644 --- a/launcher/ui/pages/global/LauncherPage.h +++ b/launcher/ui/pages/global/LauncherPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -35,56 +35,41 @@ #pragma once -#include #include +#include -#include "java/JavaChecker.h" -#include "ui/pages/BasePage.h" #include -#include "ui/ColorCache.h" #include +#include "java/JavaChecker.h" +#include "ui/ColorCache.h" +#include "ui/pages/BasePage.h" class QTextCharFormat; class SettingsObject; -namespace Ui -{ +namespace Ui { class LauncherPage; } -class LauncherPage : public QWidget, public BasePage -{ +class LauncherPage : public QWidget, public BasePage { Q_OBJECT -public: - explicit LauncherPage(QWidget *parent = 0); + public: + explicit LauncherPage(QWidget* parent = 0); ~LauncherPage(); - QString displayName() const override - { - return "Launcher"; - } - QIcon icon() const override - { - return APPLICATION->getThemedIcon("launcher"); - } - QString id() const override - { - return "launcher-settings"; - } - QString helpPage() const override - { - return "Launcher-settings"; - } + QString displayName() const override { return tr("Launcher"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("launcher"); } + QString id() const override { return "launcher-settings"; } + QString helpPage() const override { return "Launcher-settings"; } bool apply() override; void retranslate() override; -private: + private: void applySettings(); void loadSettings(); -private -slots: + private slots: void on_instDirBrowseBtn_clicked(); void on_modsDirBrowseBtn_clicked(); void on_iconsDirBrowseBtn_clicked(); @@ -96,8 +81,8 @@ slots: */ void refreshFontPreview(); -private: - Ui::LauncherPage *ui; + private: + Ui::LauncherPage* ui; /*! * Stores the currently selected update channel. @@ -105,7 +90,7 @@ private: QString m_currentUpdateChannel; // default format for the font preview... - QTextCharFormat *defaultFormat; + QTextCharFormat* defaultFormat; std::unique_ptr m_colors; diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index 26408f44f..bc259a9b8 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -99,7 +99,7 @@ - ... + Browse @@ -109,7 +109,7 @@ - ... + Browse @@ -126,14 +126,14 @@ - ... + Browse - ... + Browse diff --git a/launcher/ui/pages/global/MinecraftPage.cpp b/launcher/ui/pages/global/MinecraftPage.cpp index eca3e8657..866a4121c 100644 --- a/launcher/ui/pages/global/MinecraftPage.cpp +++ b/launcher/ui/pages/global/MinecraftPage.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield + * Copyright (C) 2023 seth * * 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 @@ -36,14 +37,14 @@ #include "MinecraftPage.h" #include "ui_MinecraftPage.h" -#include #include +#include #include -#include "settings/SettingsObject.h" #include "Application.h" +#include "settings/SettingsObject.h" -MinecraftPage::MinecraftPage(QWidget *parent) : QWidget(parent), ui(new Ui::MinecraftPage) +MinecraftPage::MinecraftPage(QWidget* parent) : QWidget(parent), ui(new Ui::MinecraftPage) { ui->setupUi(this); loadSettings(); @@ -99,6 +100,9 @@ void MinecraftPage::applySettings() // Miscellaneous s->set("CloseAfterLaunch", ui->closeAfterLaunchCheck->isChecked()); s->set("QuitAfterGameStop", ui->quitAfterGameStopCheck->isChecked()); + + // Mod loader settings + s->set("DisableQuiltBeacon", ui->disableQuiltBeaconCheckBox->isChecked()); } void MinecraftPage::loadSettings() @@ -137,6 +141,8 @@ void MinecraftPage::loadSettings() ui->closeAfterLaunchCheck->setChecked(s->get("CloseAfterLaunch").toBool()); ui->quitAfterGameStopCheck->setChecked(s->get("QuitAfterGameStop").toBool()); + + ui->disableQuiltBeaconCheckBox->setChecked(s->get("DisableQuiltBeacon").toBool()); } void MinecraftPage::retranslate() diff --git a/launcher/ui/pages/global/MinecraftPage.h b/launcher/ui/pages/global/MinecraftPage.h index cf5f95eb7..28c31b5d8 100644 --- a/launcher/ui/pages/global/MinecraftPage.h +++ b/launcher/ui/pages/global/MinecraftPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -35,57 +35,41 @@ #pragma once -#include #include +#include +#include #include "java/JavaChecker.h" #include "ui/pages/BasePage.h" -#include class SettingsObject; -namespace Ui -{ +namespace Ui { class MinecraftPage; } -class MinecraftPage : public QWidget, public BasePage -{ +class MinecraftPage : public QWidget, public BasePage { Q_OBJECT -public: - explicit MinecraftPage(QWidget *parent = 0); + public: + explicit MinecraftPage(QWidget* parent = 0); ~MinecraftPage(); - QString displayName() const override - { - return tr("Minecraft"); - } - QIcon icon() const override - { - return APPLICATION->getThemedIcon("minecraft"); - } - QString id() const override - { - return "minecraft-settings"; - } - QString helpPage() const override - { - return "Minecraft-settings"; - } + QString displayName() const override { return tr("Minecraft"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("minecraft"); } + QString id() const override { return "minecraft-settings"; } + QString helpPage() const override { return "Minecraft-settings"; } bool apply() override; void retranslate() override; -private: + private: void updateCheckboxStuff(); void applySettings(); void loadSettings(); -private -slots: + private slots: void on_maximizedCheckBox_clicked(bool checked); -private: - Ui::MinecraftPage *ui; - + private: + Ui::MinecraftPage* ui; }; diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui index 8f5de725d..393b0f358 100644 --- a/launcher/ui/pages/global/MinecraftPage.ui +++ b/launcher/ui/pages/global/MinecraftPage.ui @@ -39,7 +39,7 @@ - General + General @@ -190,6 +190,25 @@ Tweaks + + + + Mod loader settings + + + + + + Disable Quilt Loader Beacon + + + Disable Quilt loader's beacon for counting monthly active users + + + + + + diff --git a/launcher/ui/pages/global/ProxyPage.cpp b/launcher/ui/pages/global/ProxyPage.cpp index ffff8456c..9caffcb37 100644 --- a/launcher/ui/pages/global/ProxyPage.cpp +++ b/launcher/ui/pages/global/ProxyPage.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu * @@ -40,18 +40,17 @@ #include #include -#include "settings/SettingsObject.h" #include "Application.h" +#include "settings/SettingsObject.h" -ProxyPage::ProxyPage(QWidget *parent) : QWidget(parent), ui(new Ui::ProxyPage) +ProxyPage::ProxyPage(QWidget* parent) : QWidget(parent), ui(new Ui::ProxyPage) { ui->setupUi(this); ui->tabWidget->tabBar()->hide(); loadSettings(); updateCheckboxStuff(); - connect(ui->proxyGroup, QOverload::of(&QButtonGroup::buttonClicked), - this, &ProxyPage::proxyGroupChanged); + connect(ui->proxyGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &ProxyPage::proxyGroupChanged); } ProxyPage::~ProxyPage() @@ -67,13 +66,12 @@ bool ProxyPage::apply() void ProxyPage::updateCheckboxStuff() { - bool enableEditing = ui->proxyHTTPBtn->isChecked() - || ui->proxySOCKS5Btn->isChecked(); + bool enableEditing = ui->proxyHTTPBtn->isChecked() || ui->proxySOCKS5Btn->isChecked(); ui->proxyAddrBox->setEnabled(enableEditing); ui->proxyAuthBox->setEnabled(enableEditing); } -void ProxyPage::proxyGroupChanged(QAbstractButton *button) +void ProxyPage::proxyGroupChanged([[maybe_unused]] QAbstractButton* button) { updateCheckboxStuff(); } @@ -99,13 +97,8 @@ void ProxyPage::applySettings() s->set("ProxyUser", ui->proxyUserEdit->text()); s->set("ProxyPass", ui->proxyPassEdit->text()); - APPLICATION->updateProxySettings( - proxyType, - ui->proxyAddrEdit->text(), - ui->proxyPortEdit->value(), - ui->proxyUserEdit->text(), - ui->proxyPassEdit->text() - ); + APPLICATION->updateProxySettings(proxyType, ui->proxyAddrEdit->text(), ui->proxyPortEdit->value(), ui->proxyUserEdit->text(), + ui->proxyPassEdit->text()); } void ProxyPage::loadSettings() { diff --git a/launcher/ui/pages/global/ProxyPage.h b/launcher/ui/pages/global/ProxyPage.h index 279a90295..26118f181 100644 --- a/launcher/ui/pages/global/ProxyPage.h +++ b/launcher/ui/pages/global/ProxyPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu * @@ -36,53 +36,39 @@ #pragma once -#include #include #include +#include -#include "ui/pages/BasePage.h" #include +#include "ui/pages/BasePage.h" -namespace Ui -{ +namespace Ui { class ProxyPage; } -class ProxyPage : public QWidget, public BasePage -{ +class ProxyPage : public QWidget, public BasePage { Q_OBJECT -public: - explicit ProxyPage(QWidget *parent = 0); + public: + explicit ProxyPage(QWidget* parent = 0); ~ProxyPage(); - QString displayName() const override - { - return tr("Proxy"); - } - QIcon icon() const override - { - return APPLICATION->getThemedIcon("proxy"); - } - QString id() const override - { - return "proxy-settings"; - } - QString helpPage() const override - { - return "Proxy-settings"; - } + QString displayName() const override { return tr("Proxy"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("proxy"); } + QString id() const override { return "proxy-settings"; } + QString helpPage() const override { return "Proxy-settings"; } bool apply() override; void retranslate() override; -private slots: - void proxyGroupChanged(QAbstractButton *button); + private slots: + void proxyGroupChanged(QAbstractButton* button); -private: + private: void updateCheckboxStuff(); void applySettings(); void loadSettings(); -private: - Ui::ProxyPage *ui; + private: + Ui::ProxyPage* ui; }; diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 12038f88f..719db8799 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -305,7 +305,7 @@ bool ExternalResourcesPage::current(const QModelIndex& current, const QModelInde return onSelectionChanged(current, previous); } -bool ExternalResourcesPage::onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) +bool ExternalResourcesPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { auto sourceCurrent = m_filterModel->mapToSource(current); int row = sourceCurrent.row(); diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.h b/launcher/ui/pages/instance/ExternalResourcesPage.h index 97d922d88..d29be0fc3 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.h +++ b/launcher/ui/pages/instance/ExternalResourcesPage.h @@ -4,8 +4,8 @@ #include #include "Application.h" -#include "settings/Setting.h" #include "minecraft/MinecraftInstance.h" +#include "settings/Setting.h" #include "ui/pages/BasePage.h" class ResourceFolderModel; @@ -52,7 +52,7 @@ class ExternalResourcesPage : public QMainWindow, public BasePage { virtual void addItem(); void removeItem(); - virtual void removeItems(const QItemSelection &selection); + virtual void removeItems(const QItemSelection& selection); virtual void enableItem(); virtual void disableItem(); diff --git a/launcher/ui/pages/instance/GameOptionsPage.cpp b/launcher/ui/pages/instance/GameOptionsPage.cpp index 63443166b..8db392b1d 100644 --- a/launcher/ui/pages/instance/GameOptionsPage.cpp +++ b/launcher/ui/pages/instance/GameOptionsPage.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -34,23 +34,20 @@ */ #include "GameOptionsPage.h" -#include "ui_GameOptionsPage.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/gameoptions/GameOptions.h" +#include "ui_GameOptionsPage.h" -GameOptionsPage::GameOptionsPage(MinecraftInstance * inst, QWidget* parent) - : QWidget(parent), ui(new Ui::GameOptionsPage) +GameOptionsPage::GameOptionsPage(MinecraftInstance* inst, QWidget* parent) : QWidget(parent), ui(new Ui::GameOptionsPage) { ui->setupUi(this); ui->tabWidget->tabBar()->hide(); m_model = inst->gameOptionsModel(); ui->optionsView->setModel(m_model.get()); auto head = ui->optionsView->header(); - if(head->count()) - { + if (head->count()) { head->setSectionResizeMode(0, QHeaderView::ResizeToContents); - for(int i = 1; i < head->count(); i++) - { + for (int i = 1; i < head->count(); i++) { head->setSectionResizeMode(i, QHeaderView::Stretch); } } diff --git a/launcher/ui/pages/instance/GameOptionsPage.h b/launcher/ui/pages/instance/GameOptionsPage.h index de8c421e7..a132843e7 100644 --- a/launcher/ui/pages/instance/GameOptionsPage.h +++ b/launcher/ui/pages/instance/GameOptionsPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -35,50 +35,36 @@ #pragma once -#include #include +#include -#include "ui/pages/BasePage.h" #include +#include "ui/pages/BasePage.h" -namespace Ui -{ +namespace Ui { class GameOptionsPage; } class GameOptions; class MinecraftInstance; -class GameOptionsPage : public QWidget, public BasePage -{ +class GameOptionsPage : public QWidget, public BasePage { Q_OBJECT -public: - explicit GameOptionsPage(MinecraftInstance *inst, QWidget *parent = 0); + public: + explicit GameOptionsPage(MinecraftInstance* inst, QWidget* parent = 0); virtual ~GameOptionsPage(); void openedImpl() override; void closedImpl() override; - virtual QString displayName() const override - { - return tr("Game Options"); - } - virtual QIcon icon() const override - { - return APPLICATION->getThemedIcon("settings"); - } - virtual QString id() const override - { - return "gameoptions"; - } - virtual QString helpPage() const override - { - return "Game-Options-management"; - } + virtual QString displayName() const override { return tr("Game Options"); } + virtual QIcon icon() const override { return APPLICATION->getThemedIcon("settings"); } + virtual QString id() const override { return "gameoptions"; } + virtual QString helpPage() const override { return "Game-Options-management"; } void retranslate() override; -private: // data - Ui::GameOptionsPage *ui = nullptr; + private: // data + Ui::GameOptionsPage* ui = nullptr; std::shared_ptr m_model; }; diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 943ff17f1..f7be91684 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 seth * * 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 @@ -37,8 +38,8 @@ #include "InstanceSettingsPage.h" #include "ui_InstanceSettingsPage.h" -#include #include +#include #include #include @@ -46,15 +47,15 @@ #include "ui/dialogs/VersionSelectDialog.h" #include "ui/widgets/CustomCommands.h" -#include "JavaCommon.h" #include "Application.h" +#include "JavaCommon.h" #include "minecraft/auth/AccountList.h" +#include "FileSystem.h" #include "java/JavaInstallList.h" #include "java/JavaUtils.h" -#include "FileSystem.h" -InstanceSettingsPage::InstanceSettingsPage(BaseInstance *inst, QWidget *parent) +InstanceSettingsPage::InstanceSettingsPage(BaseInstance* inst, QWidget* parent) : QWidget(parent), ui(new Ui::InstanceSettingsPage), m_instance(inst) { m_settings = inst->settings(); @@ -77,7 +78,7 @@ InstanceSettingsPage::~InstanceSettingsPage() void InstanceSettingsPage::globalSettingsButtonClicked(bool) { - switch(ui->settingsTabs->currentIndex()) { + switch (ui->settingsTabs->currentIndex()) { case 0: APPLICATION->ShowGlobalSettings(this, "java-settings"); return; @@ -103,13 +104,10 @@ void InstanceSettingsPage::applySettings() // Miscellaneous bool miscellaneous = ui->miscellaneousSettingsBox->isChecked(); m_settings->set("OverrideMiscellaneous", miscellaneous); - if (miscellaneous) - { + if (miscellaneous) { m_settings->set("CloseAfterLaunch", ui->closeAfterLaunchCheck->isChecked()); m_settings->set("QuitAfterGameStop", ui->quitAfterGameStopCheck->isChecked()); - } - else - { + } else { m_settings->reset("CloseAfterLaunch"); m_settings->reset("QuitAfterGameStop"); } @@ -117,14 +115,11 @@ void InstanceSettingsPage::applySettings() // Console bool console = ui->consoleSettingsBox->isChecked(); m_settings->set("OverrideConsole", console); - if (console) - { + if (console) { m_settings->set("ShowConsole", ui->showConsoleCheck->isChecked()); m_settings->set("AutoCloseConsole", ui->autoCloseConsoleCheck->isChecked()); m_settings->set("ShowConsoleOnError", ui->showConsoleErrorCheck->isChecked()); - } - else - { + } else { m_settings->reset("ShowConsole"); m_settings->reset("AutoCloseConsole"); m_settings->reset("ShowConsoleOnError"); @@ -133,14 +128,11 @@ void InstanceSettingsPage::applySettings() // Window Size bool window = ui->windowSizeGroupBox->isChecked(); m_settings->set("OverrideWindow", window); - if (window) - { + if (window) { m_settings->set("LaunchMaximized", ui->maximizedCheckBox->isChecked()); m_settings->set("MinecraftWinWidth", ui->windowWidthSpinBox->value()); m_settings->set("MinecraftWinHeight", ui->windowHeightSpinBox->value()); - } - else - { + } else { m_settings->reset("LaunchMaximized"); m_settings->reset("MinecraftWinWidth"); m_settings->reset("MinecraftWinHeight"); @@ -149,24 +141,18 @@ void InstanceSettingsPage::applySettings() // Memory bool memory = ui->memoryGroupBox->isChecked(); m_settings->set("OverrideMemory", memory); - if (memory) - { + if (memory) { int min = ui->minMemSpinBox->value(); int max = ui->maxMemSpinBox->value(); - if(min < max) - { + if (min < max) { m_settings->set("MinMemAlloc", min); m_settings->set("MaxMemAlloc", max); - } - else - { + } else { m_settings->set("MinMemAlloc", max); m_settings->set("MaxMemAlloc", min); } m_settings->set("PermGen", ui->permGenSpinBox->value()); - } - else - { + } else { m_settings->reset("MinMemAlloc"); m_settings->reset("MaxMemAlloc"); m_settings->reset("PermGen"); @@ -175,13 +161,10 @@ void InstanceSettingsPage::applySettings() // Java Install Settings bool javaInstall = ui->javaSettingsGroupBox->isChecked(); m_settings->set("OverrideJavaLocation", javaInstall); - if (javaInstall) - { + if (javaInstall) { m_settings->set("JavaPath", ui->javaPathTextBox->text()); m_settings->set("IgnoreJavaCompatibility", ui->skipCompatibilityCheckbox->isChecked()); - } - else - { + } else { m_settings->reset("JavaPath"); m_settings->reset("IgnoreJavaCompatibility"); } @@ -189,12 +172,9 @@ void InstanceSettingsPage::applySettings() // Java arguments bool javaArgs = ui->javaArgumentsGroupBox->isChecked(); m_settings->set("OverrideJavaArgs", javaArgs); - if(javaArgs) - { + if (javaArgs) { m_settings->set("JvmArgs", ui->jvmArgsTextBox->toPlainText().replace("\n", " ")); - } - else - { + } else { m_settings->reset("JvmArgs"); } @@ -204,14 +184,11 @@ void InstanceSettingsPage::applySettings() // Custom Commands bool custcmd = ui->customCommands->checked(); m_settings->set("OverrideCommands", custcmd); - if (custcmd) - { + if (custcmd) { m_settings->set("PreLaunchCommand", ui->customCommands->prelaunchCommand()); m_settings->set("WrapperCommand", ui->customCommands->wrapperCommand()); m_settings->set("PostExitCommand", ui->customCommands->postexitCommand()); - } - else - { + } else { m_settings->reset("PreLaunchCommand"); m_settings->reset("WrapperCommand"); m_settings->reset("PostExitCommand"); @@ -220,13 +197,10 @@ void InstanceSettingsPage::applySettings() // Workarounds bool workarounds = ui->nativeWorkaroundsGroupBox->isChecked(); m_settings->set("OverrideNativeWorkarounds", workarounds); - if(workarounds) - { + if (workarounds) { m_settings->set("UseNativeOpenAL", ui->useNativeOpenALCheck->isChecked()); m_settings->set("UseNativeGLFW", ui->useNativeGLFWCheck->isChecked()); - } - else - { + } else { m_settings->reset("UseNativeOpenAL"); m_settings->reset("UseNativeGLFW"); } @@ -234,14 +208,11 @@ void InstanceSettingsPage::applySettings() // Performance bool performance = ui->perfomanceGroupBox->isChecked(); m_settings->set("OverridePerformance", performance); - if(performance) - { + if (performance) { m_settings->set("EnableFeralGamemode", ui->enableFeralGamemodeCheck->isChecked()); m_settings->set("EnableMangoHud", ui->enableMangoHud->isChecked()); m_settings->set("UseDiscreteGpu", ui->useDiscreteGpuCheck->isChecked()); - } - else - { + } else { m_settings->reset("EnableFeralGamemode"); m_settings->reset("EnableMangoHud"); m_settings->reset("UseDiscreteGpu"); @@ -250,13 +221,10 @@ void InstanceSettingsPage::applySettings() // Game time bool gameTime = ui->gameTimeGroupBox->isChecked(); m_settings->set("OverrideGameTime", gameTime); - if (gameTime) - { + if (gameTime) { m_settings->set("ShowGameTime", ui->showGameTime->isChecked()); m_settings->set("RecordGameTime", ui->recordGameTime->isChecked()); - } - else - { + } else { m_settings->reset("ShowGameTime"); m_settings->reset("RecordGameTime"); } @@ -264,12 +232,9 @@ void InstanceSettingsPage::applySettings() // Join server on launch bool joinServerOnLaunch = ui->serverJoinGroupBox->isChecked(); m_settings->set("JoinServerOnLaunch", joinServerOnLaunch); - if (joinServerOnLaunch) - { + if (joinServerOnLaunch) { m_settings->set("JoinServerOnLaunchAddress", ui->serverJoinAddress->text()); - } - else - { + } else { m_settings->reset("JoinServerOnLaunchAddress"); } @@ -280,6 +245,14 @@ void InstanceSettingsPage::applySettings() m_settings->reset("InstanceAccountId"); } + bool overrideModLoaderSettings = ui->modLoaderSettingsGroupBox->isChecked(); + m_settings->set("OverrideModLoaderSettings", overrideModLoaderSettings); + if (overrideModLoaderSettings) { + m_settings->set("DisableQuiltBeacon", ui->disableQuiltBeaconCheckBox->isChecked()); + } else { + m_settings->reset("DisableQuiltBeacon"); + } + // FIXME: This should probably be called by a signal instead m_instance->updateRuntimeContext(); } @@ -307,13 +280,10 @@ void InstanceSettingsPage::loadSettings() ui->memoryGroupBox->setChecked(m_settings->get("OverrideMemory").toBool()); int min = m_settings->get("MinMemAlloc").toInt(); int max = m_settings->get("MaxMemAlloc").toInt(); - if(min < max) - { + if (min < max) { ui->minMemSpinBox->setValue(min); ui->maxMemSpinBox->setValue(max); - } - else - { + } else { ui->minMemSpinBox->setValue(max); ui->maxMemSpinBox->setValue(min); } @@ -323,7 +293,6 @@ void InstanceSettingsPage::loadSettings() ui->labelPermGen->setVisible(permGenVisible); ui->labelPermgenNote->setVisible(permGenVisible); - // Java Settings bool overrideJava = m_settings->get("OverrideJava").toBool(); bool overrideLocation = m_settings->get("OverrideJavaLocation").toBool() || overrideJava; @@ -337,13 +306,8 @@ void InstanceSettingsPage::loadSettings() ui->jvmArgsTextBox->setPlainText(m_settings->get("JvmArgs").toString()); // Custom commands - ui->customCommands->initialize( - true, - m_settings->get("OverrideCommands").toBool(), - m_settings->get("PreLaunchCommand").toString(), - m_settings->get("WrapperCommand").toString(), - m_settings->get("PostExitCommand").toString() - ); + ui->customCommands->initialize(true, m_settings->get("OverrideCommands").toBool(), m_settings->get("PreLaunchCommand").toString(), + m_settings->get("WrapperCommand").toString(), m_settings->get("PostExitCommand").toString()); // Workarounds ui->nativeWorkaroundsGroupBox->setChecked(m_settings->get("OverrideNativeWorkarounds").toBool()); @@ -380,6 +344,10 @@ void InstanceSettingsPage::loadSettings() ui->instanceAccountGroupBox->setChecked(m_settings->get("UseAccountForInstance").toBool()); updateAccountsMenu(); + + // Mod loader specific settings + ui->modLoaderSettingsGroupBox->setChecked(m_settings->get("OverrideModLoaderSettings").toBool()); + ui->disableQuiltBeaconCheckBox->setChecked(m_settings->get("DisableQuiltBeacon").toBool()); } void InstanceSettingsPage::on_javaDetectBtn_clicked() @@ -395,8 +363,7 @@ void InstanceSettingsPage::on_javaDetectBtn_clicked() vselect.setResizeOn(2); vselect.exec(); - if (vselect.result() == QDialog::Accepted && vselect.selectedVersion()) - { + if (vselect.result() == QDialog::Accepted && vselect.selectedVersion()) { java = std::dynamic_pointer_cast(vselect.selectedVersion()); ui->javaPathTextBox->setText(java->path); bool visible = java->id.requiresPermGen() && m_settings->get("OverrideMemory").toBool(); @@ -412,15 +379,13 @@ void InstanceSettingsPage::on_javaBrowseBtn_clicked() QString raw_path = QFileDialog::getOpenFileName(this, tr("Find Java executable")); // do not allow current dir - it's dirty. Do not allow dirs that don't exist - if(raw_path.isEmpty()) - { + if (raw_path.isEmpty()) { return; } QString cooked_path = FS::NormalizePath(raw_path); QFileInfo javaInfo(cooked_path); - if(!javaInfo.exists() || !javaInfo.isExecutable()) - { + if (!javaInfo.exists() || !javaInfo.isExecutable()) { return; } ui->javaPathTextBox->setText(cooked_path); @@ -434,13 +399,11 @@ void InstanceSettingsPage::on_javaBrowseBtn_clicked() void InstanceSettingsPage::on_javaTestBtn_clicked() { - if(checker) - { + if (checker) { return; } - checker.reset(new JavaCommon::TestCheck( - this, ui->javaPathTextBox->text(), ui->jvmArgsTextBox->toPlainText().replace("\n", " "), - ui->minMemSpinBox->value(), ui->maxMemSpinBox->value(), ui->permGenSpinBox->value())); + checker.reset(new JavaCommon::TestCheck(this, ui->javaPathTextBox->text(), ui->jvmArgsTextBox->toPlainText().replace("\n", " "), + ui->minMemSpinBox->value(), ui->maxMemSpinBox->value(), ui->permGenSpinBox->value())); connect(checker.get(), SIGNAL(finished()), SLOT(checkerFinished())); checker->run(); } @@ -457,7 +420,6 @@ void InstanceSettingsPage::updateAccountsMenu() if (i == accountIndex) ui->instanceAccountSelector->setCurrentIndex(i); } - } QIcon InstanceSettingsPage::getFaceForAccount(MinecraftAccountPtr account) @@ -478,7 +440,7 @@ void InstanceSettingsPage::changeInstanceAccount(int index) } } -void InstanceSettingsPage::on_maxMemSpinBox_valueChanged(int i) +void InstanceSettingsPage::on_maxMemSpinBox_valueChanged([[maybe_unused]] int i) { updateThresholds(); } diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.h b/launcher/ui/pages/instance/InstanceSettingsPage.h index 036b41818..21ecbaf8e 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.h +++ b/launcher/ui/pages/instance/InstanceSettingsPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -46,35 +46,21 @@ #include "ui/pages/BasePage.h" class JavaChecker; -namespace Ui -{ +namespace Ui { class InstanceSettingsPage; } -class InstanceSettingsPage : public QWidget, public BasePage -{ +class InstanceSettingsPage : public QWidget, public BasePage { Q_OBJECT -public: - explicit InstanceSettingsPage(BaseInstance *inst, QWidget *parent = 0); + public: + explicit InstanceSettingsPage(BaseInstance* inst, QWidget* parent = 0); virtual ~InstanceSettingsPage(); - virtual QString displayName() const override - { - return tr("Settings"); - } - virtual QIcon icon() const override - { - return APPLICATION->getThemedIcon("instance-settings"); - } - virtual QString id() const override - { - return "settings"; - } + virtual QString displayName() const override { return tr("Settings"); } + virtual QIcon icon() const override { return APPLICATION->getThemedIcon("instance-settings"); } + virtual QString id() const override { return "settings"; } virtual bool apply() override; - virtual QString helpPage() const override - { - return "Instance-settings"; - } + virtual QString helpPage() const override { return "Instance-settings"; } void retranslate() override; void updateThresholds(); @@ -96,9 +82,9 @@ public: QIcon getFaceForAccount(MinecraftAccountPtr account); void changeInstanceAccount(int index); -private: - Ui::InstanceSettingsPage *ui; - BaseInstance *m_instance; + private: + Ui::InstanceSettingsPage* ui; + BaseInstance* m_instance; SettingsObjectPtr m_settings; unique_qobject_ptr checker; }; diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 8427965de..380d8c88c 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -61,31 +61,7 @@ false - - - - - - - Auto-detect... - - - - - - - Browse... - - - - - - - Test - - - - + If enabled, the launcher will not check if an instance is compatible with the selected Java version. @@ -95,6 +71,38 @@ + + + + + + + + + Browse + + + + + + + + + + + Auto-detect... + + + + + + + Test + + + + + @@ -116,7 +124,7 @@ - PermGen: + PermGen: @@ -541,6 +549,31 @@ Miscellaneous + + + + true + + + false + + + Mod loader settings + + + + + + Disable Quilt Loader Beacon + + + Disable Quilt loader's beacon for counting monthly active users + + + + + + @@ -674,10 +707,6 @@ openGlobalJavaSettingsButton settingsTabs javaSettingsGroupBox - javaPathTextBox - javaDetectBtn - javaBrowseBtn - javaTestBtn memoryGroupBox minMemSpinBox maxMemSpinBox diff --git a/launcher/ui/pages/instance/LogPage.cpp b/launcher/ui/pages/instance/LogPage.cpp index 639cd7118..8e1e53762 100644 --- a/launcher/ui/pages/instance/LogPage.cpp +++ b/launcher/ui/pages/instance/LogPage.cpp @@ -47,56 +47,42 @@ #include "launch/LaunchTask.h" #include "settings/Setting.h" -#include "ui/GuiUtil.h" #include "ui/ColorCache.h" +#include "ui/GuiUtil.h" #include -class LogFormatProxyModel : public QIdentityProxyModel -{ -public: - LogFormatProxyModel(QObject* parent = nullptr) : QIdentityProxyModel(parent) +class LogFormatProxyModel : public QIdentityProxyModel { + public: + LogFormatProxyModel(QObject* parent = nullptr) : QIdentityProxyModel(parent) {} + QVariant data(const QModelIndex& index, int role) const override { - } - QVariant data(const QModelIndex &index, int role) const override - { - switch(role) - { + switch (role) { case Qt::FontRole: return m_font; - case Qt::ForegroundRole: - { - MessageLevel::Enum level = (MessageLevel::Enum) QIdentityProxyModel::data(index, LogModel::LevelRole).toInt(); + case Qt::ForegroundRole: { + MessageLevel::Enum level = (MessageLevel::Enum)QIdentityProxyModel::data(index, LogModel::LevelRole).toInt(); return m_colors->getFront(level); } - case Qt::BackgroundRole: - { - MessageLevel::Enum level = (MessageLevel::Enum) QIdentityProxyModel::data(index, LogModel::LevelRole).toInt(); + case Qt::BackgroundRole: { + MessageLevel::Enum level = (MessageLevel::Enum)QIdentityProxyModel::data(index, LogModel::LevelRole).toInt(); return m_colors->getBack(level); } default: return QIdentityProxyModel::data(index, role); - } + } } - void setFont(QFont font) - { - m_font = font; - } + void setFont(QFont font) { m_font = font; } - void setColors(LogColorCache* colors) - { - m_colors.reset(colors); - } + void setColors(LogColorCache* colors) { m_colors.reset(colors); } - QModelIndex find(const QModelIndex &start, const QString &value, bool reverse) const + QModelIndex find(const QModelIndex& start, const QString& value, bool reverse) const { QModelIndex parentIndex = parent(start); - auto compare = [&](int r) -> QModelIndex - { + auto compare = [&](int r) -> QModelIndex { QModelIndex idx = index(r, start.column(), parentIndex); - if (!idx.isValid() || idx == start) - { + if (!idx.isValid() || idx == start) { return QModelIndex(); } QVariant v = data(idx, Qt::DisplayRole); @@ -105,35 +91,28 @@ public: return idx; return QModelIndex(); }; - if(reverse) - { + if (reverse) { int from = start.row(); int to = 0; - for (int i = 0; i < 2; ++i) - { - for (int r = from; (r >= to); --r) - { + for (int i = 0; i < 2; ++i) { + for (int r = from; (r >= to); --r) { auto idx = compare(r); - if(idx.isValid()) + if (idx.isValid()) return idx; } // prepare for the next iteration from = rowCount() - 1; to = start.row(); } - } - else - { + } else { int from = start.row(); int to = rowCount(parentIndex); - for (int i = 0; i < 2; ++i) - { - for (int r = from; (r < to); ++r) - { + for (int i = 0; i < 2; ++i) { + for (int r = from; (r < to); ++r) { auto idx = compare(r); - if(idx.isValid()) + if (idx.isValid()) return idx; } // prepare for the next iteration @@ -143,13 +122,13 @@ public: } return QModelIndex(); } -private: + + private: QFont m_font; std::unique_ptr m_colors; }; -LogPage::LogPage(InstancePtr instance, QWidget *parent) - : QWidget(parent), ui(new Ui::LogPage), m_instance(instance) +LogPage::LogPage(InstancePtr instance, QWidget* parent) : QWidget(parent), ui(new Ui::LogPage), m_instance(instance) { ui->setupUi(this); ui->tabWidget->tabBar()->hide(); @@ -167,8 +146,7 @@ LogPage::LogPage(InstancePtr instance, QWidget *parent) QString fontFamily = APPLICATION->settings()->get("ConsoleFont").toString(); bool conversionOk = false; int fontSize = APPLICATION->settings()->get("ConsoleFontSize").toInt(&conversionOk); - if(!conversionOk) - { + if (!conversionOk) { fontSize = 11; } m_proxy->setFont(QFont(fontFamily, fontSize)); @@ -179,8 +157,7 @@ LogPage::LogPage(InstancePtr instance, QWidget *parent) // set up instance and launch process recognition { auto launchTask = m_instance->getLaunchTask(); - if(launchTask) - { + if (launchTask) { setInstanceLaunchTaskChanged(launchTask, true); } connect(m_instance.get(), &BaseInstance::launchTaskChanged, this, &LogPage::onInstanceLaunchTaskChanged); @@ -202,30 +179,23 @@ LogPage::~LogPage() void LogPage::modelStateToUI() { - if(m_model->wrapLines()) - { + if (m_model->wrapLines()) { ui->text->setWordWrap(true); ui->wrapCheckbox->setCheckState(Qt::Checked); - } - else - { + } else { ui->text->setWordWrap(false); ui->wrapCheckbox->setCheckState(Qt::Unchecked); } - if(m_model->suspended()) - { + if (m_model->suspended()) { ui->trackLogCheckbox->setCheckState(Qt::Unchecked); - } - else - { + } else { ui->trackLogCheckbox->setCheckState(Qt::Checked); } } void LogPage::UIToModelState() { - if(!m_model) - { + if (!m_model) { return; } m_model->setLineWrap(ui->wrapCheckbox->checkState() == Qt::Checked); @@ -235,21 +205,15 @@ void LogPage::UIToModelState() void LogPage::setInstanceLaunchTaskChanged(shared_qobject_ptr proc, bool initial) { m_process = proc; - if(m_process) - { + if (m_process) { m_model = proc->getLogModel(); m_proxy->setSourceModel(m_model.get()); - if(initial) - { + if (initial) { modelStateToUI(); - } - else - { + } else { UIToModelState(); } - } - else - { + } else { m_proxy->setSourceModel(nullptr); m_model.reset(); } @@ -272,34 +236,25 @@ bool LogPage::shouldDisplay() const void LogPage::on_btnPaste_clicked() { - if(!m_model) + if (!m_model) return; - //FIXME: turn this into a proper task and move the upload logic out of GuiUtil! - m_model->append( - MessageLevel::Launcher, - QString("Log upload triggered at: %1").arg( - QDateTime::currentDateTime().toString(Qt::RFC2822Date) - ) - ); + // FIXME: turn this into a proper task and move the upload logic out of GuiUtil! + m_model->append(MessageLevel::Launcher, + QString("Log upload triggered at: %1").arg(QDateTime::currentDateTime().toString(Qt::RFC2822Date))); auto url = GuiUtil::uploadPaste(tr("Minecraft Log"), m_model->toPlainText(), this); - if(!url.has_value()) - { + if (!url.has_value()) { m_model->append(MessageLevel::Error, QString("Log upload canceled")); - } - else if (url->isNull()) - { + } else if (url->isNull()) { m_model->append(MessageLevel::Error, QString("Log upload failed!")); - } - else - { + } else { m_model->append(MessageLevel::Launcher, QString("Log uploaded to: %1").arg(url.value())); } } void LogPage::on_btnCopy_clicked() { - if(!m_model) + if (!m_model) return; m_model->append(MessageLevel::Launcher, QString("Clipboard copy at: %1").arg(QDateTime::currentDateTime().toString(Qt::RFC2822Date))); GuiUtil::setClipboardText(m_model->toPlainText()); @@ -307,7 +262,7 @@ void LogPage::on_btnCopy_clicked() void LogPage::on_btnClear_clicked() { - if(!m_model) + if (!m_model) return; m_model->clear(); m_container->refreshContainer(); @@ -320,7 +275,7 @@ void LogPage::on_btnBottom_clicked() void LogPage::on_trackLogCheckbox_clicked(bool checked) { - if(!m_model) + if (!m_model) return; m_model->suspend(!checked); } @@ -328,7 +283,7 @@ void LogPage::on_trackLogCheckbox_clicked(bool checked) void LogPage::on_wrapCheckbox_clicked(bool checked) { ui->text->setWordWrap(checked); - if(!m_model) + if (!m_model) return; m_model->setLineWrap(checked); } @@ -353,8 +308,7 @@ void LogPage::findPreviousActivated() void LogPage::findActivated() { // focus the search bar if it doesn't have focus - if (!ui->searchBar->hasFocus()) - { + if (!ui->searchBar->hasFocus()) { ui->searchBar->setFocus(); ui->searchBar->selectAll(); } diff --git a/launcher/ui/pages/instance/LogPage.h b/launcher/ui/pages/instance/LogPage.h index f6fe87c41..6c259891d 100644 --- a/launcher/ui/pages/instance/LogPage.h +++ b/launcher/ui/pages/instance/LogPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -37,46 +37,32 @@ #include +#include #include "BaseInstance.h" #include "launch/LaunchTask.h" #include "ui/pages/BasePage.h" -#include -namespace Ui -{ +namespace Ui { class LogPage; } class QTextCharFormat; class LogFormatProxyModel; -class LogPage : public QWidget, public BasePage -{ +class LogPage : public QWidget, public BasePage { Q_OBJECT -public: - explicit LogPage(InstancePtr instance, QWidget *parent = 0); + public: + explicit LogPage(InstancePtr instance, QWidget* parent = 0); virtual ~LogPage(); - virtual QString displayName() const override - { - return tr("Minecraft Log"); - } - virtual QIcon icon() const override - { - return APPLICATION->getThemedIcon("log"); - } - virtual QString id() const override - { - return "console"; - } + virtual QString displayName() const override { return tr("Minecraft Log"); } + virtual QIcon icon() const override { return APPLICATION->getThemedIcon("log"); } + virtual QString id() const override { return "console"; } virtual bool apply() override; - virtual QString helpPage() const override - { - return "Minecraft-Logs"; - } + virtual QString helpPage() const override { return "Minecraft-Logs"; } virtual bool shouldDisplay() const override; void retranslate() override; -private slots: + private slots: void on_btnPaste_clicked(); void on_btnCopy_clicked(); void on_btnClear_clicked(); @@ -92,16 +78,16 @@ private slots: void onInstanceLaunchTaskChanged(shared_qobject_ptr proc); -private: + private: void modelStateToUI(); void UIToModelState(); void setInstanceLaunchTaskChanged(shared_qobject_ptr proc, bool initial); -private: - Ui::LogPage *ui; + private: + Ui::LogPage* ui; InstancePtr m_instance; shared_qobject_ptr m_process; - LogFormatProxyModel * m_proxy; - shared_qobject_ptr m_model; + LogFormatProxyModel* m_proxy; + shared_qobject_ptr m_model; }; diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index 0fc0c9867..8fdaf065d 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -3,6 +3,9 @@ // SPDX-License-Identifier: GPL-3.0-only #include "ManagedPackPage.h" +#include +#include +#include #include "ui_ManagedPackPage.h" #include @@ -23,6 +26,8 @@ #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" +#include "net/ApiDownload.h" + /** This is just to override the combo box popup behavior so that the combo box doesn't take the whole screen. * ... thanks Qt. */ @@ -103,6 +108,19 @@ ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_wi // Pretend we're opening the page again openedImpl(); }); + + connect(ui->changelogTextBrowser, &QTextBrowser::anchorClicked, this, [](const QUrl url) { + if (url.scheme().isEmpty()) { + auto querry = + QUrlQuery(url.query()).queryItemValue("remoteUrl", QUrl::FullyDecoded); // curseforge workaround for linkout?remoteUrl= + auto decoded = QUrl::fromPercentEncoding(querry.toUtf8()); + auto newUrl = QUrl(decoded); + if (newUrl.isValid() && (newUrl.scheme() == "http" || newUrl.scheme() == "https")) + QDesktopServices ::openUrl(newUrl); + return; + } + QDesktopServices::openUrl(url); + }); } ManagedPackPage::~ManagedPackPage() @@ -226,7 +244,7 @@ void ModrinthManagedPackPage::parseManagedPack() QString id = m_inst->getManagedPackID(); m_fetch_job->addNetAction( - Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); + Net::ApiDownload::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] { QJsonParseError parse_error{}; @@ -376,7 +394,7 @@ void FlameManagedPackPage::parseManagedPack() QString id = m_inst->getManagedPackID(); - m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/mods/%2/files").arg(BuildConfig.FLAME_BASE_URL, id), response)); + m_fetch_job->addNetAction(Net::ApiDownload::makeByteArray(QString("%1/mods/%2/files").arg(BuildConfig.FLAME_BASE_URL, id), response)); QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] { QJsonParseError parse_error{}; diff --git a/launcher/ui/pages/instance/ManagedPackPage.ui b/launcher/ui/pages/instance/ManagedPackPage.ui index bbe44a940..05e91bbca 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.ui +++ b/launcher/ui/pages/instance/ManagedPackPage.ui @@ -168,10 +168,13 @@ - + No changelog available for this version! + + false + @@ -188,6 +191,13 @@ + + + ProjectDescriptionPage + QTextBrowser +
    ui/widgets/ProjectDescriptionPage.h
    +
    +
    diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index cef292bd9..0f5e29cb6 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -127,7 +127,7 @@ bool ModFolderPage::shouldDisplay() const return true; } -bool ModFolderPage::onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) +bool ModFolderPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { auto sourceCurrent = m_filterModel->mapToSource(current); int row = sourceCurrent.row(); diff --git a/launcher/ui/pages/instance/NotesPage.cpp b/launcher/ui/pages/instance/NotesPage.cpp index 95a9fad22..a86369f87 100644 --- a/launcher/ui/pages/instance/NotesPage.cpp +++ b/launcher/ui/pages/instance/NotesPage.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -34,11 +34,10 @@ */ #include "NotesPage.h" -#include "ui_NotesPage.h" #include +#include "ui_NotesPage.h" -NotesPage::NotesPage(BaseInstance *inst, QWidget *parent) - : QWidget(parent), ui(new Ui::NotesPage), m_inst(inst) +NotesPage::NotesPage(BaseInstance* inst, QWidget* parent) : QWidget(parent), ui(new Ui::NotesPage), m_inst(inst) { ui->setupUi(this); ui->noteEditor->setText(m_inst->notes()); diff --git a/launcher/ui/pages/instance/NotesPage.h b/launcher/ui/pages/instance/NotesPage.h index 80a7279bc..3351d25fc 100644 --- a/launcher/ui/pages/instance/NotesPage.h +++ b/launcher/ui/pages/instance/NotesPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -37,45 +37,34 @@ #include +#include #include "BaseInstance.h" #include "ui/pages/BasePage.h" -#include -namespace Ui -{ +namespace Ui { class NotesPage; } -class NotesPage : public QWidget, public BasePage -{ +class NotesPage : public QWidget, public BasePage { Q_OBJECT -public: - explicit NotesPage(BaseInstance *inst, QWidget *parent = 0); + public: + explicit NotesPage(BaseInstance* inst, QWidget* parent = 0); virtual ~NotesPage(); - virtual QString displayName() const override - { - return tr("Notes"); - } + virtual QString displayName() const override { return tr("Notes"); } virtual QIcon icon() const override { auto icon = APPLICATION->getThemedIcon("notes"); - if(icon.isNull()) + if (icon.isNull()) icon = APPLICATION->getThemedIcon("news"); return icon; } - virtual QString id() const override - { - return "notes"; - } + virtual QString id() const override { return "notes"; } virtual bool apply() override; - virtual QString helpPage() const override - { - return "Notes"; - } + virtual QString helpPage() const override { return "Notes"; } void retranslate() override; -private: - Ui::NotesPage *ui; - BaseInstance *m_inst; + private: + Ui::NotesPage* ui; + BaseInstance* m_inst; }; diff --git a/launcher/ui/pages/instance/OtherLogsPage.cpp b/launcher/ui/pages/instance/OtherLogsPage.cpp index bbdd73248..ab5d98289 100644 --- a/launcher/ui/pages/instance/OtherLogsPage.cpp +++ b/launcher/ui/pages/instance/OtherLogsPage.cpp @@ -41,14 +41,13 @@ #include "ui/GuiUtil.h" -#include "RecursiveFileSystemWatcher.h" -#include #include +#include #include +#include "RecursiveFileSystemWatcher.h" -OtherLogsPage::OtherLogsPage(QString path, IPathMatcher::Ptr fileFilter, QWidget *parent) - : QWidget(parent), ui(new Ui::OtherLogsPage), m_path(path), m_fileFilter(fileFilter), - m_watcher(new RecursiveFileSystemWatcher(this)) +OtherLogsPage::OtherLogsPage(QString path, IPathMatcher::Ptr fileFilter, QWidget* parent) + : QWidget(parent), ui(new Ui::OtherLogsPage), m_path(path), m_fileFilter(fileFilter), m_watcher(new RecursiveFileSystemWatcher(this)) { ui->setupUi(this); ui->tabWidget->tabBar()->hide(); @@ -94,21 +93,15 @@ void OtherLogsPage::populateSelectLogBox() { ui->selectLogBox->clear(); ui->selectLogBox->addItems(m_watcher->files()); - if (m_currentFile.isEmpty()) - { + if (m_currentFile.isEmpty()) { setControlsEnabled(false); ui->selectLogBox->setCurrentIndex(-1); - } - else - { + } else { const int index = ui->selectLogBox->findText(m_currentFile); - if (index != -1) - { + if (index != -1) { ui->selectLogBox->setCurrentIndex(index); setControlsEnabled(true); - } - else - { + } else { setControlsEnabled(false); } } @@ -117,19 +110,15 @@ void OtherLogsPage::populateSelectLogBox() void OtherLogsPage::on_selectLogBox_currentIndexChanged(const int index) { QString file; - if (index != -1) - { + if (index != -1) { file = ui->selectLogBox->itemText(index); } - if (file.isEmpty() || !QFile::exists(FS::PathCombine(m_path, file))) - { + if (file.isEmpty() || !QFile::exists(FS::PathCombine(m_path, file))) { m_currentFile = QString(); ui->text->clear(); setControlsEnabled(false); - } - else - { + } else { m_currentFile = file; on_btnReload_clicked(); setControlsEnabled(true); @@ -138,64 +127,49 @@ void OtherLogsPage::on_selectLogBox_currentIndexChanged(const int index) void OtherLogsPage::on_btnReload_clicked() { - if(m_currentFile.isEmpty()) - { + if (m_currentFile.isEmpty()) { setControlsEnabled(false); return; } QFile file(FS::PathCombine(m_path, m_currentFile)); - if (!file.open(QFile::ReadOnly)) - { + if (!file.open(QFile::ReadOnly)) { setControlsEnabled(false); - ui->btnReload->setEnabled(true); // allow reload + ui->btnReload->setEnabled(true); // allow reload m_currentFile = QString(); - QMessageBox::critical(this, tr("Error"), tr("Unable to open %1 for reading: %2") - .arg(m_currentFile, file.errorString())); - } - else - { - auto setPlainText = [&](const QString & text) - { + QMessageBox::critical(this, tr("Error"), tr("Unable to open %1 for reading: %2").arg(m_currentFile, file.errorString())); + } else { + auto setPlainText = [&](const QString& text) { QString fontFamily = APPLICATION->settings()->get("ConsoleFont").toString(); bool conversionOk = false; int fontSize = APPLICATION->settings()->get("ConsoleFontSize").toInt(&conversionOk); - if(!conversionOk) - { + if (!conversionOk) { fontSize = 11; } - QTextDocument *doc = ui->text->document(); + QTextDocument* doc = ui->text->document(); doc->setDefaultFont(QFont(fontFamily, fontSize)); ui->text->setPlainText(text); }; - auto showTooBig = [&]() - { - setPlainText( - tr("The file (%1) is too big. You may want to open it in a viewer optimized " - "for large files.").arg(file.fileName())); + auto showTooBig = [&]() { + setPlainText(tr("The file (%1) is too big. You may want to open it in a viewer optimized " + "for large files.") + .arg(file.fileName())); }; - if(file.size() > (1024ll * 1024ll * 12ll)) - { + if (file.size() > (1024ll * 1024ll * 12ll)) { showTooBig(); return; } QString content; - if(file.fileName().endsWith(".gz")) - { + if (file.fileName().endsWith(".gz")) { QByteArray temp; - if(!GZip::unzip(file.readAll(), temp)) - { - setPlainText( - tr("The file (%1) is not readable.").arg(file.fileName())); + if (!GZip::unzip(file.readAll(), temp)) { + setPlainText(tr("The file (%1) is not readable.").arg(file.fileName())); return; } content = QString::fromUtf8(temp); - } - else - { + } else { content = QString::fromUtf8(file.readAll()); } - if (content.size() >= 50000000ll) - { + if (content.size() >= 50000000ll) { showTooBig(); return; } @@ -215,8 +189,7 @@ void OtherLogsPage::on_btnCopy_clicked() void OtherLogsPage::on_btnDelete_clicked() { - if(m_currentFile.isEmpty()) - { + if (m_currentFile.isEmpty()) { setControlsEnabled(false); return; } @@ -230,36 +203,27 @@ void OtherLogsPage::on_btnDelete_clicked() } QFile file(FS::PathCombine(m_path, m_currentFile)); - if (FS::trash(file.fileName())) - { + if (FS::trash(file.fileName())) { return; } - if (!file.remove()) - { - QMessageBox::critical(this, tr("Error"), tr("Unable to delete %1: %2") - .arg(m_currentFile, file.errorString())); + if (!file.remove()) { + QMessageBox::critical(this, tr("Error"), tr("Unable to delete %1: %2").arg(m_currentFile, file.errorString())); } } - - void OtherLogsPage::on_btnClean_clicked() { auto toDelete = m_watcher->files(); - if(toDelete.isEmpty()) - { + if (toDelete.isEmpty()) { return; } - QMessageBox *messageBox = new QMessageBox(this); + QMessageBox* messageBox = new QMessageBox(this); messageBox->setWindowTitle(tr("Confirm Cleanup")); - if(toDelete.size() > 5) - { + if (toDelete.size() > 5) { messageBox->setText(tr("Are you sure you want to delete all log files?")); messageBox->setDetailedText(toDelete.join('\n')); - } - else - { + } else { messageBox->setText(tr("Are you sure you want to delete all these files?\n%1").arg(toDelete.join('\n'))); } messageBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); @@ -268,46 +232,37 @@ void OtherLogsPage::on_btnClean_clicked() messageBox->setIcon(QMessageBox::Question); messageBox->setTextInteractionFlags(Qt::TextBrowserInteraction); - if (messageBox->exec() != QMessageBox::Ok) - { + if (messageBox->exec() != QMessageBox::Ok) { return; } QStringList failed; - for(auto item: toDelete) - { + for (auto item : toDelete) { QFile file(FS::PathCombine(m_path, item)); - if (FS::trash(file.fileName())) - { + if (FS::trash(file.fileName())) { continue; } - if (!file.remove()) - { + if (!file.remove()) { failed.push_back(item); } } - if(!failed.empty()) - { - QMessageBox *messageBox = new QMessageBox(this); - messageBox->setWindowTitle(tr("Error")); - if(failed.size() > 5) - { - messageBox->setText(tr("Couldn't delete some files!")); - messageBox->setDetailedText(failed.join('\n')); + if (!failed.empty()) { + QMessageBox* messageBoxFailure = new QMessageBox(this); + messageBoxFailure->setWindowTitle(tr("Error")); + if (failed.size() > 5) { + messageBoxFailure->setText(tr("Couldn't delete some files!")); + messageBoxFailure->setDetailedText(failed.join('\n')); + } else { + messageBoxFailure->setText(tr("Couldn't delete some files:\n%1").arg(failed.join('\n'))); } - else - { - messageBox->setText(tr("Couldn't delete some files:\n%1").arg(failed.join('\n'))); - } - messageBox->setStandardButtons(QMessageBox::Ok); - messageBox->setDefaultButton(QMessageBox::Ok); - messageBox->setTextInteractionFlags(Qt::TextSelectableByMouse); - messageBox->setIcon(QMessageBox::Critical); - messageBox->setTextInteractionFlags(Qt::TextBrowserInteraction); - messageBox->exec(); + messageBoxFailure->setStandardButtons(QMessageBox::Ok); + messageBoxFailure->setDefaultButton(QMessageBox::Ok); + messageBoxFailure->setTextInteractionFlags(Qt::TextSelectableByMouse); + messageBoxFailure->setIcon(QMessageBox::Critical); + messageBoxFailure->setTextInteractionFlags(Qt::TextBrowserInteraction); + messageBoxFailure->exec(); } } - void OtherLogsPage::setControlsEnabled(const bool enabled) { ui->btnReload->setEnabled(enabled); @@ -319,7 +274,7 @@ void OtherLogsPage::setControlsEnabled(const bool enabled) } // FIXME: HACK, use LogView instead? -static void findNext(QPlainTextEdit * _this, const QString& what, bool reverse) +static void findNext(QPlainTextEdit* _this, const QString& what, bool reverse) { _this->find(what, reverse ? QTextDocument::FindFlag::FindBackward : QTextDocument::FindFlag(0)); } @@ -344,8 +299,7 @@ void OtherLogsPage::findPreviousActivated() void OtherLogsPage::findActivated() { // focus the search bar if it doesn't have focus - if (!ui->searchBar->hasFocus()) - { + if (!ui->searchBar->hasFocus()) { ui->searchBar->setFocus(); ui->searchBar->selectAll(); } diff --git a/launcher/ui/pages/instance/OtherLogsPage.h b/launcher/ui/pages/instance/OtherLogsPage.h index 95591638b..4b3b122b0 100644 --- a/launcher/ui/pages/instance/OtherLogsPage.h +++ b/launcher/ui/pages/instance/OtherLogsPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -37,47 +37,33 @@ #include -#include "ui/pages/BasePage.h" #include #include +#include "ui/pages/BasePage.h" -namespace Ui -{ +namespace Ui { class OtherLogsPage; } class RecursiveFileSystemWatcher; -class OtherLogsPage : public QWidget, public BasePage -{ +class OtherLogsPage : public QWidget, public BasePage { Q_OBJECT -public: - explicit OtherLogsPage(QString path, IPathMatcher::Ptr fileFilter, QWidget *parent = 0); + public: + explicit OtherLogsPage(QString path, IPathMatcher::Ptr fileFilter, QWidget* parent = 0); ~OtherLogsPage(); - QString id() const override - { - return "logs"; - } - QString displayName() const override - { - return tr("Other logs"); - } - QIcon icon() const override - { - return APPLICATION->getThemedIcon("log"); - } - QString helpPage() const override - { - return "Minecraft-Logs"; - } + QString id() const override { return "logs"; } + QString displayName() const override { return tr("Other logs"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("log"); } + QString helpPage() const override { return "Minecraft-Logs"; } void retranslate() override; void openedImpl() override; void closedImpl() override; -private slots: + private slots: void populateSelectLogBox(); void on_selectLogBox_currentIndexChanged(const int index); void on_btnReload_clicked(); @@ -91,13 +77,13 @@ private slots: void findNextActivated(); void findPreviousActivated(); -private: + private: void setControlsEnabled(const bool enabled); -private: - Ui::OtherLogsPage *ui; + private: + Ui::OtherLogsPage* ui; QString m_path; QString m_currentFile; IPathMatcher::Ptr m_fileFilter; - RecursiveFileSystemWatcher *m_watcher; + RecursiveFileSystemWatcher* m_watcher; }; diff --git a/launcher/ui/pages/instance/ResourcePackPage.cpp b/launcher/ui/pages/instance/ResourcePackPage.cpp index 12b371df4..26c14ca4d 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.cpp +++ b/launcher/ui/pages/instance/ResourcePackPage.cpp @@ -55,7 +55,7 @@ ResourcePackPage::ResourcePackPage(MinecraftInstance* instance, std::shared_ptr< ui->actionViewConfigs->setVisible(false); } -bool ResourcePackPage::onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) +bool ResourcePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { auto sourceCurrent = m_filterModel->mapToSource(current); int row = sourceCurrent.row(); diff --git a/launcher/ui/pages/instance/ResourcePackPage.h b/launcher/ui/pages/instance/ResourcePackPage.h index b04aa2e96..cb84ca96d 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.h +++ b/launcher/ui/pages/instance/ResourcePackPage.h @@ -42,11 +42,10 @@ #include "minecraft/mod/ResourcePackFolderModel.h" -class ResourcePackPage : public ExternalResourcesPage -{ +class ResourcePackPage : public ExternalResourcesPage { Q_OBJECT -public: - explicit ResourcePackPage(MinecraftInstance *instance, std::shared_ptr model, QWidget *parent = 0); + public: + explicit ResourcePackPage(MinecraftInstance* instance, std::shared_ptr model, QWidget* parent = 0); QString displayName() const override { return tr("Resource packs"); } QIcon icon() const override { return APPLICATION->getThemedIcon("resourcepacks"); } @@ -55,12 +54,10 @@ public: virtual bool shouldDisplay() const override { - return !m_instance->traits().contains("no-texturepacks") && - !m_instance->traits().contains("texturepacks"); + return !m_instance->traits().contains("no-texturepacks") && !m_instance->traits().contains("texturepacks"); } public slots: bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override; void downloadRPs(); }; - diff --git a/launcher/ui/pages/instance/ScreenshotsPage.cpp b/launcher/ui/pages/instance/ScreenshotsPage.cpp index bcce5f572..29c835fc1 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.cpp +++ b/launcher/ui/pages/instance/ScreenshotsPage.cpp @@ -39,52 +39,50 @@ #include "BuildConfig.h" #include "ui_ScreenshotsPage.h" -#include -#include -#include -#include +#include +#include #include #include -#include -#include -#include -#include -#include #include +#include +#include #include +#include +#include +#include #include +#include +#include #include -#include "ui/dialogs/ProgressDialog.h" #include "ui/dialogs/CustomMessageBox.h" +#include "ui/dialogs/ProgressDialog.h" #include "net/NetJob.h" -#include "screenshots/ImgurUpload.h" #include "screenshots/ImgurAlbumCreation.h" +#include "screenshots/ImgurUpload.h" #include "tasks/SequentialTask.h" -#include "RWStorage.h" -#include #include +#include +#include "RWStorage.h" typedef RWStorage SharedIconCache; typedef std::shared_ptr SharedIconCachePtr; -class ThumbnailingResult : public QObject -{ +class ThumbnailingResult : public QObject { Q_OBJECT -public slots: - inline void emitResultsReady(const QString &path) { emit resultsReady(path); } - inline void emitResultsFailed(const QString &path) { emit resultsFailed(path); } -signals: - void resultsReady(const QString &path); - void resultsFailed(const QString &path); + public slots: + inline void emitResultsReady(const QString& path) { emit resultsReady(path); } + inline void emitResultsFailed(const QString& path) { emit resultsFailed(path); } + signals: + void resultsReady(const QString& path); + void resultsFailed(const QString& path); }; -class ThumbnailRunnable : public QRunnable -{ -public: +class ThumbnailRunnable : public QRunnable { + public: ThumbnailRunnable(QString path, SharedIconCachePtr cache) { m_path = path; @@ -129,57 +127,50 @@ public: // this is about as elegant and well written as a bag of bricks with scribbles done by insane // asylum patients. -class FilterModel : public QIdentityProxyModel -{ +class FilterModel : public QIdentityProxyModel { Q_OBJECT -public: - explicit FilterModel(QObject *parent = 0) : QIdentityProxyModel(parent) + public: + explicit FilterModel(QObject* parent = 0) : QIdentityProxyModel(parent) { m_thumbnailingPool.setMaxThreadCount(4); m_thumbnailCache = std::make_shared(); m_thumbnailCache->add("placeholder", APPLICATION->getThemedIcon("screenshot-placeholder")); connect(&watcher, SIGNAL(fileChanged(QString)), SLOT(fileChanged(QString))); } - virtual ~FilterModel() { + virtual ~FilterModel() + { m_thumbnailingPool.clear(); if (!m_thumbnailingPool.waitForDone(500)) qDebug() << "Thumbnail pool took longer than 500ms to finish"; } - virtual QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const + virtual QVariant data(const QModelIndex& proxyIndex, int role = Qt::DisplayRole) const { auto model = sourceModel(); if (!model) return QVariant(); - if (role == Qt::DisplayRole || role == Qt::EditRole) - { + if (role == Qt::DisplayRole || role == Qt::EditRole) { QVariant result = sourceModel()->data(mapToSource(proxyIndex), role); return result.toString().remove(QRegularExpression("\\.png$")); } - if (role == Qt::DecorationRole) - { - QVariant result = - sourceModel()->data(mapToSource(proxyIndex), QFileSystemModel::FilePathRole); + if (role == Qt::DecorationRole) { + QVariant result = sourceModel()->data(mapToSource(proxyIndex), QFileSystemModel::FilePathRole); QString filePath = result.toString(); QIcon temp; - if (!watched.contains(filePath)) - { - ((QFileSystemWatcher &)watcher).addPath(filePath); - ((QSet &)watched).insert(filePath); + if (!watched.contains(filePath)) { + ((QFileSystemWatcher&)watcher).addPath(filePath); + ((QSet&)watched).insert(filePath); } - if (m_thumbnailCache->get(filePath, temp)) - { + if (m_thumbnailCache->get(filePath, temp)) { return temp; } - if (!m_failed.contains(filePath)) - { - ((FilterModel *)this)->thumbnailImage(filePath); + if (!m_failed.contains(filePath)) { + ((FilterModel*)this)->thumbnailImage(filePath); } return (m_thumbnailCache->get("placeholder")); } return sourceModel()->data(mapToSource(proxyIndex), role); } - virtual bool setData(const QModelIndex &index, const QVariant &value, - int role = Qt::EditRole) + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) { auto model = sourceModel(); if (!model) @@ -189,23 +180,21 @@ public: // FIXME: this is a workaround for a bug in QFileSystemModel, where it doesn't // sort after renames { - ((QFileSystemModel *)model)->setNameFilterDisables(true); - ((QFileSystemModel *)model)->setNameFilterDisables(false); + ((QFileSystemModel*)model)->setNameFilterDisables(true); + ((QFileSystemModel*)model)->setNameFilterDisables(false); } return model->setData(mapToSource(index), value.toString() + ".png", role); } -private: + private: void thumbnailImage(QString path) { auto runnable = new ThumbnailRunnable(path, m_thumbnailCache); - connect(&(runnable->m_resultEmitter), SIGNAL(resultsReady(QString)), - SLOT(thumbnailReady(QString))); - connect(&(runnable->m_resultEmitter), SIGNAL(resultsFailed(QString)), - SLOT(thumbnailFailed(QString))); - ((QThreadPool &)m_thumbnailingPool).start(runnable); + connect(&(runnable->m_resultEmitter), SIGNAL(resultsReady(QString)), SLOT(thumbnailReady(QString))); + connect(&(runnable->m_resultEmitter), SIGNAL(resultsFailed(QString)), SLOT(thumbnailFailed(QString))); + ((QThreadPool&)m_thumbnailingPool).start(runnable); } -private slots: + private slots: void thumbnailReady(QString path) { emit layoutChanged(); } void thumbnailFailed(QString path) { m_failed.insert(path); } void fileChanged(QString filepath) @@ -219,7 +208,7 @@ private slots: } } -private: + private: SharedIconCachePtr m_thumbnailCache; QThreadPool m_thumbnailingPool; QSet m_failed; @@ -227,18 +216,15 @@ private: QFileSystemWatcher watcher; }; -class CenteredEditingDelegate : public QStyledItemDelegate -{ -public: - explicit CenteredEditingDelegate(QObject *parent = 0) : QStyledItemDelegate(parent) {} +class CenteredEditingDelegate : public QStyledItemDelegate { + public: + explicit CenteredEditingDelegate(QObject* parent = 0) : QStyledItemDelegate(parent) {} virtual ~CenteredEditingDelegate() {} - virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, - const QModelIndex &index) const + virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { auto widget = QStyledItemDelegate::createEditor(parent, option, index); - auto foo = dynamic_cast(widget); - if (foo) - { + auto foo = dynamic_cast(widget); + if (foo) { foo->setAlignment(Qt::AlignHCenter); foo->setFrame(true); foo->setMaximumWidth(192); @@ -247,15 +233,14 @@ public: } }; -ScreenshotsPage::ScreenshotsPage(QString path, QWidget *parent) - : QMainWindow(parent), ui(new Ui::ScreenshotsPage) +ScreenshotsPage::ScreenshotsPage(QString path, QWidget* parent) : QMainWindow(parent), ui(new Ui::ScreenshotsPage) { m_model.reset(new QFileSystemModel()); m_filterModel.reset(new FilterModel()); m_filterModel->setSourceModel(m_model.get()); m_model->setFilter(QDir::Files); m_model->setReadOnly(false); - m_model->setNameFilters({"*.png"}); + m_model->setNameFilters({ "*.png" }); m_model->setNameFilterDisables(false); m_folder = path; m_valid = FS::ensureFolderPathExists(m_folder); @@ -278,31 +263,29 @@ ScreenshotsPage::ScreenshotsPage(QString path, QWidget *parent) connect(ui->listView, SIGNAL(activated(QModelIndex)), SLOT(onItemActivated(QModelIndex))); } -bool ScreenshotsPage::eventFilter(QObject *obj, QEvent *evt) +bool ScreenshotsPage::eventFilter(QObject* obj, QEvent* evt) { if (obj != ui->listView) return QWidget::eventFilter(obj, evt); - if (evt->type() != QEvent::KeyPress) - { + if (evt->type() != QEvent::KeyPress) { return QWidget::eventFilter(obj, evt); } - QKeyEvent *keyEvent = static_cast(evt); + QKeyEvent* keyEvent = static_cast(evt); if (keyEvent->matches(QKeySequence::Copy)) { on_actionCopy_File_s_triggered(); return true; } - switch (keyEvent->key()) - { - case Qt::Key_Delete: - on_actionDelete_triggered(); - return true; - case Qt::Key_F2: - on_actionRename_triggered(); - return true; - default: - break; + switch (keyEvent->key()) { + case Qt::Key_Delete: + on_actionDelete_triggered(); + return true; + case Qt::Key_F2: + on_actionRename_triggered(); + return true; + default: + break; } return QWidget::eventFilter(obj, evt); } @@ -322,17 +305,17 @@ void ScreenshotsPage::ShowContextMenu(const QPoint& pos) auto menu = ui->toolBar->createContextMenu(this, tr("Context menu")); if (ui->listView->selectionModel()->selectedRows().size() > 1) { - menu->removeAction( ui->actionCopy_Image ); + menu->removeAction(ui->actionCopy_Image); } menu->exec(ui->listView->mapToGlobal(pos)); delete menu; } -QMenu * ScreenshotsPage::createPopupMenu() +QMenu* ScreenshotsPage::createPopupMenu() { QMenu* filteredMenu = QMainWindow::createPopupMenu(); - filteredMenu->removeAction( ui->toolBar->toggleViewAction() ); + filteredMenu->removeAction(ui->toolBar->toggleViewAction()); return filteredMenu; } @@ -345,13 +328,12 @@ void ScreenshotsPage::onItemActivated(QModelIndex index) DesktopServices::openFile(info.absoluteFilePath()); } -void ScreenshotsPage::onCurrentSelectionChanged(const QItemSelection &selected) +void ScreenshotsPage::onCurrentSelectionChanged(const QItemSelection& selected) { bool allReadable = !selected.isEmpty(); bool allWritable = !selected.isEmpty(); - for (auto index : selected.indexes()) - { + for (auto index : selected.indexes()) { if (!index.isValid()) break; auto info = m_model->fileInfo(index); @@ -401,8 +383,7 @@ void ScreenshotsPage::on_actionUpload_triggered() QList uploaded; auto job = NetJob::Ptr(new NetJob("Screenshot Upload", APPLICATION->network())); - if(selection.size() < 2) - { + if (selection.size() < 2) { auto item = selection.at(0); auto info = m_model->fileInfo(item); auto screenshot = std::make_shared(info); @@ -411,31 +392,24 @@ void ScreenshotsPage::on_actionUpload_triggered() m_uploadActive = true; ProgressDialog dialog(this); - if(dialog.execWithTask(job.get()) != QDialog::Accepted) - { - CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), - tr("Unknown error"), QMessageBox::Warning)->exec(); - } - else - { + if (dialog.execWithTask(job.get()) != QDialog::Accepted) { + CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), tr("Unknown error"), QMessageBox::Warning)->exec(); + } else { auto link = screenshot->m_url; - QClipboard *clipboard = QApplication::clipboard(); + QClipboard* clipboard = QApplication::clipboard(); clipboard->setText(link); CustomMessageBox::selectable( - this, - tr("Upload finished"), - tr("The link to the uploaded screenshot has been placed in your clipboard.") - .arg(link), - QMessageBox::Information - )->exec(); + this, tr("Upload finished"), + tr("The link to the uploaded screenshot has been placed in your clipboard.").arg(link), + QMessageBox::Information) + ->exec(); } m_uploadActive = false; return; } - for (auto item : selection) - { + for (auto item : selection) { auto info = m_model->fileInfo(item); auto screenshot = std::make_shared(info); uploaded.push_back(screenshot); @@ -449,26 +423,16 @@ void ScreenshotsPage::on_actionUpload_triggered() task.addTask(albumTask); m_uploadActive = true; ProgressDialog prog(this); - if (prog.execWithTask(&task) != QDialog::Accepted) - { - CustomMessageBox::selectable( - this, - tr("Failed to upload screenshots!"), - tr("Unknown error"), - QMessageBox::Warning - )->exec(); - } - else - { + if (prog.execWithTask(&task) != QDialog::Accepted) { + CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), tr("Unknown error"), QMessageBox::Warning)->exec(); + } else { auto link = QString("https://imgur.com/a/%1").arg(imgurAlbum->id()); - QClipboard *clipboard = QApplication::clipboard(); + QClipboard* clipboard = QApplication::clipboard(); clipboard->setText(link); - CustomMessageBox::selectable( - this, - tr("Upload finished"), - tr("The link to the uploaded album has been placed in your clipboard.") .arg(link), - QMessageBox::Information - )->exec(); + CustomMessageBox::selectable(this, tr("Upload finished"), + tr("The link to the uploaded album has been placed in your clipboard.").arg(link), + QMessageBox::Information) + ->exec(); } m_uploadActive = false; } @@ -476,8 +440,7 @@ void ScreenshotsPage::on_actionUpload_triggered() void ScreenshotsPage::on_actionCopy_Image_triggered() { auto selection = ui->listView->selectionModel()->selectedRows(); - if(selection.size() < 1) - { + if (selection.size() < 1) { return; } @@ -492,15 +455,13 @@ void ScreenshotsPage::on_actionCopy_Image_triggered() void ScreenshotsPage::on_actionCopy_File_s_triggered() { auto selection = ui->listView->selectionModel()->selectedRows(); - if(selection.size() < 1) - { + if (selection.size() < 1) { // Don't do anything so we don't empty the users clipboard return; } QString buf = ""; - for (auto item : selection) - { + for (auto item : selection) { auto info = m_model->fileInfo(item); buf += "file:///" + info.absoluteFilePath() + "\r\n"; } @@ -532,8 +493,7 @@ void ScreenshotsPage::on_actionDelete_triggered() if (response != QMessageBox::Yes) return; - for (auto item : selected) - { + for (auto item : selected) { if (FS::trash(m_model->filePath(item))) continue; @@ -552,23 +512,19 @@ void ScreenshotsPage::on_actionRename_triggered() void ScreenshotsPage::openedImpl() { - if(!m_valid) - { + if (!m_valid) { m_valid = FS::ensureFolderPathExists(m_folder); } - if (m_valid) - { + if (m_valid) { QString path = QDir(m_folder).absolutePath(); auto idx = m_model->setRootPath(path); - if(idx.isValid()) - { + if (idx.isValid()) { ui->listView->setModel(m_filterModel.get()); - connect(ui->listView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ScreenshotsPage::onCurrentSelectionChanged); - onCurrentSelectionChanged(ui->listView->selectionModel()->selection()); // set initial button enable states + connect(ui->listView->selectionModel(), &QItemSelectionModel::selectionChanged, this, + &ScreenshotsPage::onCurrentSelectionChanged); + onCurrentSelectionChanged(ui->listView->selectionModel()->selection()); // set initial button enable states ui->listView->setRootIndex(m_filterModel->mapFromSource(idx)); - } - else - { + } else { ui->listView->setModel(nullptr); } } diff --git a/launcher/ui/pages/instance/ScreenshotsPage.h b/launcher/ui/pages/instance/ScreenshotsPage.h index 89611b6d1..bb127b429 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.h +++ b/launcher/ui/pages/instance/ScreenshotsPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -37,16 +37,15 @@ #include -#include "ui/pages/BasePage.h" #include +#include "ui/pages/BasePage.h" #include "settings/Setting.h" class QFileSystemModel; class QIdentityProxyModel; class QItemSelection; -namespace Ui -{ +namespace Ui { class ScreenshotsPage; } @@ -54,49 +53,30 @@ struct ScreenShot; class ScreenshotList; class ImgurAlbumCreation; -class ScreenshotsPage : public QMainWindow, public BasePage -{ +class ScreenshotsPage : public QMainWindow, public BasePage { Q_OBJECT -public: - explicit ScreenshotsPage(QString path, QWidget *parent = 0); + public: + explicit ScreenshotsPage(QString path, QWidget* parent = 0); virtual ~ScreenshotsPage(); void openedImpl() override; void closedImpl() override; - enum - { - NothingDone = 0x42 - }; + enum { NothingDone = 0x42 }; - virtual bool eventFilter(QObject *, QEvent *) override; - virtual QString displayName() const override - { - return tr("Screenshots"); - } - virtual QIcon icon() const override - { - return APPLICATION->getThemedIcon("screenshots"); - } - virtual QString id() const override - { - return "screenshots"; - } - virtual QString helpPage() const override - { - return "Screenshots-management"; - } - virtual bool apply() override - { - return !m_uploadActive; - } + virtual bool eventFilter(QObject*, QEvent*) override; + virtual QString displayName() const override { return tr("Screenshots"); } + virtual QIcon icon() const override { return APPLICATION->getThemedIcon("screenshots"); } + virtual QString id() const override { return "screenshots"; } + virtual QString helpPage() const override { return "Screenshots-management"; } + virtual bool apply() override { return !m_uploadActive; } void retranslate() override; -protected: - QMenu * createPopupMenu() override; + protected: + QMenu* createPopupMenu() override; -private slots: + private slots: void on_actionUpload_triggered(); void on_actionCopy_Image_triggered(); void on_actionCopy_File_s_triggered(); @@ -104,11 +84,11 @@ private slots: void on_actionRename_triggered(); void on_actionView_Folder_triggered(); void onItemActivated(QModelIndex); - void onCurrentSelectionChanged(const QItemSelection &selected); - void ShowContextMenu(const QPoint &pos); + void onCurrentSelectionChanged(const QItemSelection& selected); + void ShowContextMenu(const QPoint& pos); -private: - Ui::ScreenshotsPage *ui; + private: + Ui::ScreenshotsPage* ui; std::shared_ptr m_model; std::shared_ptr m_filterModel; QString m_folder; diff --git a/launcher/ui/pages/instance/ServersPage.cpp b/launcher/ui/pages/instance/ServersPage.cpp index 4b1fa08a3..da49f4a7d 100644 --- a/launcher/ui/pages/instance/ServersPage.cpp +++ b/launcher/ui/pages/instance/ServersPage.cpp @@ -40,36 +40,27 @@ #include "ui_ServersPage.h" #include -#include #include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include #include #include #include -static const int COLUMN_COUNT = 2; // 3 , TBD: latency and other nice things. +static const int COLUMN_COUNT = 2; // 3 , TBD: latency and other nice things. -struct Server -{ +struct Server { // Types - enum class AcceptsTextures : int - { - ASK = 0, - ALWAYS = 1, - NEVER = 2 - }; + enum class AcceptsTextures : int { ASK = 0, ALWAYS = 1, NEVER = 2 }; // Methods - Server() - { - m_name = QObject::tr("Minecraft Server"); - } - Server(const QString & name, const QString & address) + Server() { m_name = QObject::tr("Minecraft Server"); } + Server(const QString& name, const QString& address) { m_name = name; m_address = address; @@ -82,21 +73,16 @@ struct Server std::string nameStr(server["name"]); m_name = QString::fromUtf8(nameStr.c_str()); - if(server["icon"]) - { + if (server["icon"]) { std::string base64str(server["icon"]); m_icon = QByteArray::fromBase64(base64str.c_str()); } - if(server.has_key("acceptTextures", nbt::tag_type::Byte)) - { + if (server.has_key("acceptTextures", nbt::tag_type::Byte)) { bool value = server["acceptTextures"].as().get(); - if(value) - { + if (value) { m_acceptsTextures = AcceptsTextures::ALWAYS; - } - else - { + } else { m_acceptsTextures = AcceptsTextures::NEVER; } } @@ -106,12 +92,10 @@ struct Server { server.insert("name", m_name.trimmed().toUtf8().toStdString()); server.insert("ip", m_address.trimmed().toUtf8().toStdString()); - if(m_icon.size()) - { + if (m_icon.size()) { server.insert("icon", m_icon.toBase64().toStdString()); } - if(m_acceptsTextures != AcceptsTextures::ASK) - { + if (m_acceptsTextures != AcceptsTextures::ASK) { server.insert("acceptTextures", nbt::tag_byte(m_acceptsTextures == AcceptsTextures::ALWAYS)); } } @@ -127,64 +111,54 @@ struct Server // Data - temporary bool m_checked = false; bool m_up = false; - QString m_motd; // https://mctools.org/motd-creator + QString m_motd; // https://mctools.org/motd-creator int m_ping = 0; int m_currentPlayers = 0; int m_maxPlayers = 0; }; -static std::unique_ptr parseServersDat(const QString& filename) +static std::unique_ptr parseServersDat(const QString& filename) { - try - { + try { QByteArray input = FS::read(filename); std::istringstream foo(std::string(input.constData(), input.size())); auto pair = nbt::io::read_compound(foo); - if(pair.first != "") + if (pair.first != "") return nullptr; - if(pair.second == nullptr) + if (pair.second == nullptr) return nullptr; return std::move(pair.second); - } - catch (...) - { + } catch (...) { return nullptr; } } -static bool serializeServerDat(const QString& filename, nbt::tag_compound * levelInfo) +static bool serializeServerDat(const QString& filename, nbt::tag_compound* levelInfo) { - try - { - if(!FS::ensureFilePathExists(filename)) - { + try { + if (!FS::ensureFilePathExists(filename)) { return false; } std::ostringstream s; nbt::io::write_tag("", *levelInfo, s); - QByteArray val(s.str().data(), (int) s.str().size() ); + QByteArray val(s.str().data(), (int)s.str().size()); FS::write(filename, val); return true; - } - catch (...) - { + } catch (...) { return false; } } -class ServersModel: public QAbstractListModel -{ +class ServersModel : public QAbstractListModel { Q_OBJECT -public: - enum Roles - { + public: + enum Roles { ServerPtrRole = Qt::UserRole, }; - explicit ServersModel(const QString &path, QObject *parent = 0) - : QAbstractListModel(parent) + explicit ServersModel(const QString& path, QObject* parent = 0) : QAbstractListModel(parent) { m_path = path; m_watcher = new QFileSystemWatcher(this); @@ -194,18 +168,16 @@ public: m_saveTimer.setInterval(5000); connect(&m_saveTimer, &QTimer::timeout, this, &ServersModel::save_internal); } - virtual ~ServersModel() {}; + virtual ~ServersModel(){}; void observe() { - if(m_observed) - { + if (m_observed) { return; } m_observed = true; - if(!m_loaded) - { + if (!m_loaded) { load(); } @@ -214,8 +186,7 @@ public: void unobserve() { - if(!m_observed) - { + if (!m_observed) { return; } m_observed = false; @@ -225,8 +196,7 @@ public: void lock() { - if(m_locked) - { + if (m_locked) { return; } saveNow(); @@ -237,8 +207,7 @@ public: void unlock() { - if(!m_locked) - { + if (!m_locked) { return; } m_locked = false; @@ -248,12 +217,10 @@ public: int addEmptyRow(int position) { - if(m_locked) - { + if (m_locked) { return -1; } - if(position < 0 || position >= rowCount()) - { + if (position < 0 || position >= rowCount()) { position = rowCount(); } beginInsertRows(QModelIndex(), position, position); @@ -265,36 +232,32 @@ public: bool removeRow(int row) { - if(m_locked) - { + if (m_locked) { return false; } - if(row < 0 || row >= rowCount()) - { + if (row < 0 || row >= rowCount()) { return false; } beginRemoveRows(QModelIndex(), row, row); m_servers.removeAt(row); - endRemoveRows(); // does absolutely nothing, the selected server stays as the next line... + endRemoveRows(); // does absolutely nothing, the selected server stays as the next line... scheduleSave(); return true; } bool moveUp(int row) { - if(m_locked) - { + if (m_locked) { return false; } - if(row <= 0) - { + if (row <= 0) { return false; } beginMoveRows(QModelIndex(), row, row, QModelIndex(), row - 1); #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) - m_servers.swapItemsAt(row-1, row); + m_servers.swapItemsAt(row - 1, row); #else - m_servers.swap(row-1, row); + m_servers.swap(row - 1, row); #endif endMoveRows(); scheduleSave(); @@ -303,20 +266,18 @@ public: bool moveDown(int row) { - if(m_locked) - { + if (m_locked) { return false; } int count = rowCount(); - if(row + 1 >= count) - { + if (row + 1 >= count) { return false; } beginMoveRows(QModelIndex(), row, row, QModelIndex(), row + 2); #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) - m_servers.swapItemsAt(row+1, row); + m_servers.swapItemsAt(row + 1, row); #else - m_servers.swap(row+1, row); + m_servers.swap(row + 1, row); #endif endMoveRows(); scheduleSave(); @@ -328,10 +289,8 @@ public: if (section < 0 || section >= COLUMN_COUNT) return QVariant(); - if(role == Qt::DisplayRole) - { - switch(section) - { + if (role == Qt::DisplayRole) { + switch (section) { case 0: return tr("Name"); case 1: @@ -344,90 +303,81 @@ public: return QAbstractListModel::headerData(section, orientation, role); } - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override { if (!index.isValid()) return QVariant(); int row = index.row(); int column = index.column(); - if(column < 0 || column >= COLUMN_COUNT) + if (column < 0 || column >= COLUMN_COUNT) return QVariant(); if (row < 0 || row >= m_servers.size()) return QVariant(); - switch(column) - { + switch (column) { case 0: - switch (role) - { - case Qt::DecorationRole: - { - auto & bytes = m_servers[row].m_icon; - if(bytes.size()) - { - QPixmap px; - if(px.loadFromData(bytes)) - return QIcon(px); + switch (role) { + case Qt::DecorationRole: { + auto& bytes = m_servers[row].m_icon; + if (bytes.size()) { + QPixmap px; + if (px.loadFromData(bytes)) + return QIcon(px); + } + return APPLICATION->getThemedIcon("unknown_server"); } - return APPLICATION->getThemedIcon("unknown_server"); - } - case Qt::DisplayRole: - return m_servers[row].m_name; - case ServerPtrRole: - return QVariant::fromValue((void *)&m_servers[row]); - default: - return QVariant(); + case Qt::DisplayRole: + return m_servers[row].m_name; + case ServerPtrRole: + return QVariant::fromValue((void*)&m_servers[row]); + default: + return QVariant(); } case 1: - switch (role) - { - case Qt::DisplayRole: - return m_servers[row].m_address; - default: - return QVariant(); + switch (role) { + case Qt::DisplayRole: + return m_servers[row].m_address; + default: + return QVariant(); } case 2: - switch (role) - { - case Qt::DisplayRole: - return m_servers[row].m_ping; - default: - return QVariant(); + switch (role) { + case Qt::DisplayRole: + return m_servers[row].m_ping; + default: + return QVariant(); } default: return QVariant(); } } - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override { return parent.isValid() ? 0 : m_servers.size(); } - int columnCount(const QModelIndex & parent) const override + int columnCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : COLUMN_COUNT; } - Server * at(int index) + Server* at(int index) { - if(index < 0 || index >= rowCount()) - { + if (index < 0 || index >= rowCount()) { return nullptr; } return &m_servers[index]; } - void setName(int row, const QString & name) + void setName(int row, const QString& name) { - if(m_locked) - { + if (m_locked) { return; } auto server = at(row); - if(!server || server->m_name == name) - { + if (!server || server->m_name == name) { return; } server->m_name = name; @@ -435,15 +385,13 @@ public: scheduleSave(); } - void setAddress(int row, const QString & address) + void setAddress(int row, const QString& address) { - if(m_locked) - { + if (m_locked) { return; } auto server = at(row); - if(!server || server->m_address == address) - { + if (!server || server->m_address == address) { return; } server->m_address = address; @@ -453,13 +401,11 @@ public: void setAcceptsTextures(int row, Server::AcceptsTextures textures) { - if(m_locked) - { + if (m_locked) { return; } auto server = at(row); - if(!server || server->m_acceptsTextures == textures) - { + if (!server || server->m_acceptsTextures == textures) { return; } server->m_acceptsTextures = textures; @@ -473,12 +419,10 @@ public: beginResetModel(); QList servers; auto serversDat = parseServersDat(serversPath()); - if(serversDat) - { - auto &serversList = serversDat->at("servers").as(); - for(auto iter = serversList.begin(); iter != serversList.end(); iter++) - { - auto & serverTag = (*iter).as(); + if (serversDat) { + auto& serversList = serversDat->at("servers").as(); + for (auto iter = serversList.begin(); iter != serversList.end(); iter++) { + auto& serverTag = (*iter).as(); Server s(serverTag); servers.append(s); } @@ -490,14 +434,12 @@ public: void saveNow() { - if(saveIsScheduled()) - { + if (saveIsScheduled()) { save_internal(); } } - -public slots: + public slots: void dirChanged(const QString& path) { qDebug() << "Changed:" << path; @@ -508,7 +450,7 @@ public slots: qDebug() << "Changed:" << path; } -private slots: + private slots: void save_internal() { cancelSave(); @@ -517,31 +459,27 @@ private slots: nbt::tag_compound out; nbt::tag_list list; - for(auto & server: m_servers) - { + for (auto& server : m_servers) { nbt::tag_compound serverNbt; server.serialize(serverNbt); list.push_back(std::move(serverNbt)); } out.insert("servers", nbt::value(std::move(list))); - if(!serializeServerDat(path, &out)) - { + if (!serializeServerDat(path, &out)) { qDebug() << "Failed to save server list:" << path << "Will try again."; scheduleSave(); } } -private: + private: void scheduleSave() { - if(!m_loaded) - { + if (!m_loaded) { qDebug() << "Server list should never save if it didn't successfully load, path:" << m_path; return; } - if(!m_dirty) - { + if (!m_dirty) { m_dirty = true; qDebug() << "Server list save is scheduled for" << m_path; } @@ -562,24 +500,17 @@ private: void updateFSObserver() { bool observingFS = m_watcher->directories().contains(m_path); - if(m_observed && m_locked) - { - if(!observingFS) - { + if (m_observed && m_locked) { + if (!observingFS) { qWarning() << "Will watch" << m_path; - if(!m_watcher->addPath(m_path)) - { + if (!m_watcher->addPath(m_path)) { qWarning() << "Failed to start watching" << m_path; } } - } - else - { - if(observingFS) - { + } else { + if (observingFS) { qWarning() << "Will stop watching" << m_path; - if(!m_watcher->removePath(m_path)) - { + if (!m_watcher->removePath(m_path)) { qWarning() << "Failed to stop watching" << m_path; } } @@ -592,34 +523,31 @@ private: return foo.filePath(); } -private: + private: bool m_loaded = false; bool m_locked = false; bool m_observed = false; bool m_dirty = false; QString m_path; QList m_servers; - QFileSystemWatcher *m_watcher = nullptr; + QFileSystemWatcher* m_watcher = nullptr; QTimer m_saveTimer; }; -ServersPage::ServersPage(InstancePtr inst, QWidget* parent) - : QMainWindow(parent), ui(new Ui::ServersPage) +ServersPage::ServersPage(InstancePtr inst, QWidget* parent) : QMainWindow(parent), ui(new Ui::ServersPage) { ui->setupUi(this); m_inst = inst; m_model = new ServersModel(inst->gameRoot(), this); - ui->serversView->setIconSize(QSize(64,64)); + ui->serversView->setIconSize(QSize(64, 64)); ui->serversView->setModel(m_model); ui->serversView->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->serversView, &QTreeView::customContextMenuRequested, this, &ServersPage::ShowContextMenu); auto head = ui->serversView->header(); - if(head->count()) - { + if (head->count()) { head->setSectionResizeMode(0, QHeaderView::Stretch); - for(int i = 1; i < head->count(); i++) - { + for (int i = 1; i < head->count(); i++) { head->setSectionResizeMode(i, QHeaderView::ResizeToContents); } } @@ -633,8 +561,7 @@ ServersPage::ServersPage(InstancePtr inst, QWidget* parent) connect(m_model, &QAbstractItemModel::rowsRemoved, this, &ServersPage::rowsRemoved); m_locked = m_inst->isRunning(); - if(m_locked) - { + if (m_locked) { m_model->lock(); } @@ -659,40 +586,33 @@ void ServersPage::ShowContextMenu(const QPoint& pos) delete menu; } -QMenu * ServersPage::createPopupMenu() +QMenu* ServersPage::createPopupMenu() { QMenu* filteredMenu = QMainWindow::createPopupMenu(); - filteredMenu->removeAction( ui->toolBar->toggleViewAction() ); + filteredMenu->removeAction(ui->toolBar->toggleViewAction()); return filteredMenu; } void ServersPage::runningStateChanged(bool running) { - if(m_locked == running) - { + if (m_locked == running) { return; } m_locked = running; - if(m_locked) - { + if (m_locked) { m_model->lock(); - } - else - { + } else { m_model->unlock(); } updateState(); } -void ServersPage::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +void ServersPage::currentChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { int nextServer = -1; - if (!current.isValid()) - { + if (!current.isValid()) { nextServer = -1; - } - else - { + } else { nextServer = current.row(); } currentServer = nextServer; @@ -700,20 +620,15 @@ void ServersPage::currentChanged(const QModelIndex ¤t, const QModelIndex & } // WARNING: this is here because currentChanged is not accurate when removing rows. the current item needs to be fixed up after removal. -void ServersPage::rowsRemoved(const QModelIndex& parent, int first, int last) +void ServersPage::rowsRemoved([[maybe_unused]] const QModelIndex& parent, int first, int last) { - if(currentServer < first) - { + if (currentServer < first) { // current was before the removal return; - } - else if(currentServer >= first && currentServer <= last) - { + } else if (currentServer >= first && currentServer <= last) { // current got removed... return; - } - else - { + } else { // current was past the removal int count = last - first + 1; currentServer -= count; @@ -749,14 +664,11 @@ void ServersPage::updateState() ui->actionRemove->setEnabled(serverEditEnabled); ui->actionJoin->setEnabled(serverEditEnabled); - if(server) - { + if (server) { ui->addressLine->setText(server->m_address); ui->nameLine->setText(server->m_name); ui->resourceComboBox->setCurrentIndex(int(server->m_acceptsTextures)); - } - else - { + } else { ui->addressLine->setText(QString()); ui->nameLine->setText(QString()); ui->resourceComboBox->setCurrentIndex(0); @@ -788,27 +700,25 @@ void ServersPage::closedImpl() void ServersPage::on_actionAdd_triggered() { int position = m_model->addEmptyRow(currentServer + 1); - if(position < 0) - { + if (position < 0) { return; } // select the new row ui->serversView->selectionModel()->setCurrentIndex( - m_model->index(position), - QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear | QItemSelectionModel::Rows - ); + m_model->index(position), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear | QItemSelectionModel::Rows); currentServer = position; } void ServersPage::on_actionRemove_triggered() { - auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), - tr("You are about to remove \"%1\".\n" - "This is permanent and the server will be gone from your list forever (A LONG TIME).\n\n" - "Are you sure?") - .arg(m_model->at(currentServer)->m_name), - QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) - ->exec(); + auto response = + CustomMessageBox::selectable(this, tr("Confirm Removal"), + tr("You are about to remove \"%1\".\n" + "This is permanent and the server will be gone from your list forever (A LONG TIME).\n\n" + "Are you sure?") + .arg(m_model->at(currentServer)->m_name), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); if (response != QMessageBox::Yes) return; @@ -818,23 +728,21 @@ void ServersPage::on_actionRemove_triggered() void ServersPage::on_actionMove_Up_triggered() { - if(m_model->moveUp(currentServer)) - { - currentServer --; + if (m_model->moveUp(currentServer)) { + currentServer--; } } void ServersPage::on_actionMove_Down_triggered() { - if(m_model->moveDown(currentServer)) - { - currentServer ++; + if (m_model->moveDown(currentServer)) { + currentServer++; } } void ServersPage::on_actionJoin_triggered() { - const auto &address = m_model->at(currentServer)->m_address; + const auto& address = m_model->at(currentServer)->m_address; APPLICATION->launch(m_inst, true, false, nullptr, std::make_shared(MinecraftServerTarget::parse(address))); } diff --git a/launcher/ui/pages/instance/ServersPage.h b/launcher/ui/pages/instance/ServersPage.h index 476e7d70d..a27d1d297 100644 --- a/launcher/ui/pages/instance/ServersPage.h +++ b/launcher/ui/pages/instance/ServersPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu * @@ -39,13 +39,12 @@ #include #include -#include "ui/pages/BasePage.h" #include +#include "ui/pages/BasePage.h" #include "settings/Setting.h" -namespace Ui -{ +namespace Ui { class ServersPage; } @@ -53,46 +52,33 @@ struct Server; class ServersModel; class MinecraftInstance; -class ServersPage : public QMainWindow, public BasePage -{ +class ServersPage : public QMainWindow, public BasePage { Q_OBJECT -public: - explicit ServersPage(InstancePtr inst, QWidget *parent = 0); + public: + explicit ServersPage(InstancePtr inst, QWidget* parent = 0); virtual ~ServersPage(); void openedImpl() override; void closedImpl() override; - virtual QString displayName() const override - { - return tr("Servers"); - } - virtual QIcon icon() const override - { - return APPLICATION->getThemedIcon("server"); - } - virtual QString id() const override - { - return "servers"; - } - virtual QString helpPage() const override - { - return "Servers-management"; - } + virtual QString displayName() const override { return tr("Servers"); } + virtual QIcon icon() const override { return APPLICATION->getThemedIcon("server"); } + virtual QString id() const override { return "servers"; } + virtual QString helpPage() const override { return "Servers-management"; } void retranslate() override; -protected: - QMenu * createPopupMenu() override; + protected: + QMenu* createPopupMenu() override; -private: + private: void updateState(); void scheduleSave(); bool saveIsScheduled() const; -private slots: - void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); - void rowsRemoved(const QModelIndex &parent, int first, int last); + private slots: + void currentChanged(const QModelIndex& current, const QModelIndex& previous); + void rowsRemoved(const QModelIndex& parent, int first, int last); void on_actionAdd_triggered(); void on_actionRemove_triggered(); @@ -102,19 +88,18 @@ private slots: void runningStateChanged(bool running); - void nameEdited(const QString & name); - void addressEdited(const QString & address); - void resourceIndexChanged(int index);\ + void nameEdited(const QString& name); + void addressEdited(const QString& address); + void resourceIndexChanged(int index); - void ShowContextMenu(const QPoint &pos); + void ShowContextMenu(const QPoint& pos); -private: // data + private: // data int currentServer = -1; bool m_locked = true; - Ui::ServersPage *ui = nullptr; - ServersModel * m_model = nullptr; + Ui::ServersPage* ui = nullptr; + ServersModel* m_model = nullptr; InstancePtr m_inst = nullptr; std::shared_ptr m_wide_bar_setting = nullptr; }; - diff --git a/launcher/ui/pages/instance/ShaderPackPage.h b/launcher/ui/pages/instance/ShaderPackPage.h index a779fd8cc..7c43a3756 100644 --- a/launcher/ui/pages/instance/ShaderPackPage.h +++ b/launcher/ui/pages/instance/ShaderPackPage.h @@ -39,11 +39,10 @@ #include "ExternalResourcesPage.h" -class ShaderPackPage : public ExternalResourcesPage -{ +class ShaderPackPage : public ExternalResourcesPage { Q_OBJECT -public: - explicit ShaderPackPage(MinecraftInstance *instance, std::shared_ptr model, QWidget *parent = nullptr); + public: + explicit ShaderPackPage(MinecraftInstance* instance, std::shared_ptr model, QWidget* parent = nullptr); ~ShaderPackPage() override = default; QString displayName() const override { return tr("Shader packs"); } diff --git a/launcher/ui/pages/instance/TexturePackPage.cpp b/launcher/ui/pages/instance/TexturePackPage.cpp index e477ceda3..fa478a9a2 100644 --- a/launcher/ui/pages/instance/TexturePackPage.cpp +++ b/launcher/ui/pages/instance/TexturePackPage.cpp @@ -57,7 +57,7 @@ TexturePackPage::TexturePackPage(MinecraftInstance* instance, std::shared_ptractionViewConfigs->setVisible(false); } -bool TexturePackPage::onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) +bool TexturePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { auto sourceCurrent = m_filterModel->mapToSource(current); int row = sourceCurrent.row(); diff --git a/launcher/ui/pages/instance/TexturePackPage.h b/launcher/ui/pages/instance/TexturePackPage.h index 47a8fa60d..9c4f24b70 100644 --- a/launcher/ui/pages/instance/TexturePackPage.h +++ b/launcher/ui/pages/instance/TexturePackPage.h @@ -42,21 +42,17 @@ #include "minecraft/mod/TexturePackFolderModel.h" -class TexturePackPage : public ExternalResourcesPage -{ +class TexturePackPage : public ExternalResourcesPage { Q_OBJECT -public: - explicit TexturePackPage(MinecraftInstance *instance, std::shared_ptr model, QWidget* parent = nullptr); + public: + explicit TexturePackPage(MinecraftInstance* instance, std::shared_ptr model, QWidget* parent = nullptr); QString displayName() const override { return tr("Texture packs"); } QIcon icon() const override { return APPLICATION->getThemedIcon("resourcepacks"); } QString id() const override { return "texturepacks"; } QString helpPage() const override { return "Texture-packs"; } - virtual bool shouldDisplay() const override - { - return m_instance->traits().contains("texturepacks"); - } + virtual bool shouldDisplay() const override { return m_instance->traits().contains("texturepacks"); } public slots: bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override; diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index a180c8041..ef029d1d9 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -6,7 +6,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022-2023 Sefa Eyeoglu - * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2023 TheKodeToad * * 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 @@ -51,6 +51,7 @@ #include #include "VersionPage.h" +#include "ui/dialogs/InstallLoaderDialog.h" #include "ui_VersionPage.h" #include "ui/dialogs/CustomMessageBox.h" @@ -188,7 +189,7 @@ void VersionPage::showContextMenu(const QPoint& pos) delete menu; } -void VersionPage::packageCurrent(const QModelIndex& current, const QModelIndex& previous) +void VersionPage::packageCurrent(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { if (!current.isValid()) { ui->frame->clear(); @@ -226,18 +227,6 @@ void VersionPage::packageCurrent(const QModelIndex& current, const QModelIndex& void VersionPage::updateVersionControls() { - // FIXME: this is a dirty hack - auto minecraftVersion = Version(m_profile->getComponentVersion("net.minecraft")); - - bool supportsFabric = minecraftVersion >= Version("1.14"); - ui->actionInstall_Fabric->setEnabled(supportsFabric); - - bool supportsQuilt = minecraftVersion >= Version("1.14"); - ui->actionInstall_Quilt->setEnabled(supportsQuilt); - - bool supportsLiteLoader = minecraftVersion <= Version("1.12.2"); - ui->actionInstall_LiteLoader->setEnabled(supportsLiteLoader); - updateButtons(); } @@ -389,20 +378,14 @@ void VersionPage::on_actionChange_version_triggered() return; } auto uid = list->uid(); - // FIXME: this is a horrible HACK. Get version filtering information from the actual metadata... - if (uid == "net.minecraftforge") { - on_actionInstall_Forge_triggered(); - return; - } else if (uid == "com.mumfrey.liteloader") { - on_actionInstall_LiteLoader_triggered(); - return; - } + VersionSelectDialog vselect(list.get(), tr("Change %1 version").arg(name), this); if (uid == "net.fabricmc.intermediary" || uid == "org.quiltmc.hashed") { vselect.setEmptyString(tr("No intermediary mappings versions are currently available.")); vselect.setEmptyErrorString(tr("Couldn't load or download the intermediary mappings version lists!")); - vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft")); } + vselect.setExactIfPresentFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft")); + auto currentVersion = patch->getVersion(); if (!currentVersion.isEmpty()) { vselect.setCurrentVersion(currentVersion); @@ -443,79 +426,11 @@ void VersionPage::on_actionDownload_All_triggered() m_container->refreshContainer(); } -void VersionPage::on_actionInstall_Forge_triggered() +void VersionPage::on_actionInstall_Loader_triggered() { - auto vlist = APPLICATION->metadataIndex()->get("net.minecraftforge"); - if (!vlist) { - return; - } - VersionSelectDialog vselect(vlist.get(), tr("Select Forge version"), this); - vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft")); - vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") + - m_profile->getComponentVersion("net.minecraft")); - vselect.setEmptyErrorString(tr("Couldn't load or download the Forge version lists!")); - - auto currentVersion = m_profile->getComponentVersion("net.minecraftforge"); - if (!currentVersion.isEmpty()) { - vselect.setCurrentVersion(currentVersion); - } - - if (vselect.exec() && vselect.selectedVersion()) { - auto vsn = vselect.selectedVersion(); - m_profile->setComponentVersion("net.minecraftforge", vsn->descriptor()); - m_profile->resolve(Net::Mode::Online); - // m_profile->installVersion(); - preselect(m_profile->rowCount(QModelIndex()) - 1); - m_container->refreshContainer(); - } -} - -void VersionPage::on_actionInstall_Fabric_triggered() -{ - auto vlist = APPLICATION->metadataIndex()->get("net.fabricmc.fabric-loader"); - if (!vlist) { - return; - } - VersionSelectDialog vselect(vlist.get(), tr("Select Fabric Loader version"), this); - vselect.setEmptyString(tr("No Fabric Loader versions are currently available.")); - vselect.setEmptyErrorString(tr("Couldn't load or download the Fabric Loader version lists!")); - - auto currentVersion = m_profile->getComponentVersion("net.fabricmc.fabric-loader"); - if (!currentVersion.isEmpty()) { - vselect.setCurrentVersion(currentVersion); - } - - if (vselect.exec() && vselect.selectedVersion()) { - auto vsn = vselect.selectedVersion(); - m_profile->setComponentVersion("net.fabricmc.fabric-loader", vsn->descriptor()); - m_profile->resolve(Net::Mode::Online); - preselect(m_profile->rowCount(QModelIndex()) - 1); - m_container->refreshContainer(); - } -} - -void VersionPage::on_actionInstall_Quilt_triggered() -{ - auto vlist = APPLICATION->metadataIndex()->get("org.quiltmc.quilt-loader"); - if (!vlist) { - return; - } - VersionSelectDialog vselect(vlist.get(), tr("Select Quilt Loader version"), this); - vselect.setEmptyString(tr("No Quilt Loader versions are currently available.")); - vselect.setEmptyErrorString(tr("Couldn't load or download the Quilt Loader version lists!")); - - auto currentVersion = m_profile->getComponentVersion("org.quiltmc.quilt-loader"); - if (!currentVersion.isEmpty()) { - vselect.setCurrentVersion(currentVersion); - } - - if (vselect.exec() && vselect.selectedVersion()) { - auto vsn = vselect.selectedVersion(); - m_profile->setComponentVersion("org.quiltmc.quilt-loader", vsn->descriptor()); - m_profile->resolve(Net::Mode::Online); - preselect(m_profile->rowCount(QModelIndex()) - 1); - m_container->refreshContainer(); - } + InstallLoaderDialog dialog(m_inst->getPackProfile(), QString(), this); + dialog.exec(); + m_container->refreshContainer(); } void VersionPage::on_actionAdd_Empty_triggered() @@ -534,33 +449,6 @@ void VersionPage::on_actionAdd_Empty_triggered() } } -void VersionPage::on_actionInstall_LiteLoader_triggered() -{ - auto vlist = APPLICATION->metadataIndex()->get("com.mumfrey.liteloader"); - if (!vlist) { - return; - } - VersionSelectDialog vselect(vlist.get(), tr("Select LiteLoader version"), this); - vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft")); - vselect.setEmptyString(tr("No LiteLoader versions are currently available for Minecraft ") + - m_profile->getComponentVersion("net.minecraft")); - vselect.setEmptyErrorString(tr("Couldn't load or download the LiteLoader version lists!")); - - auto currentVersion = m_profile->getComponentVersion("com.mumfrey.liteloader"); - if (!currentVersion.isEmpty()) { - vselect.setCurrentVersion(currentVersion); - } - - if (vselect.exec() && vselect.selectedVersion()) { - auto vsn = vselect.selectedVersion(); - m_profile->setComponentVersion("com.mumfrey.liteloader", vsn->descriptor()); - m_profile->resolve(Net::Mode::Online); - // m_profile->installVersion(vselect.selectedVersion()); - preselect(m_profile->rowCount(QModelIndex()) - 1); - m_container->refreshContainer(); - } -} - void VersionPage::on_actionLibrariesFolder_triggered() { DesktopServices::openDirectory(m_inst->getLocalLibraryPath(), true); @@ -571,7 +459,7 @@ void VersionPage::on_actionMinecraftFolder_triggered() DesktopServices::openDirectory(m_inst->gameRoot(), true); } -void VersionPage::versionCurrent(const QModelIndex& current, const QModelIndex& previous) +void VersionPage::versionCurrent(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { currentIdx = current.row(); updateButtons(currentIdx); diff --git a/launcher/ui/pages/instance/VersionPage.h b/launcher/ui/pages/instance/VersionPage.h index 45d383f41..6915be883 100644 --- a/launcher/ui/pages/instance/VersionPage.h +++ b/launcher/ui/pages/instance/VersionPage.h @@ -68,11 +68,8 @@ class VersionPage : public QMainWindow, public BasePage { private slots: void on_actionChange_version_triggered(); - void on_actionInstall_Forge_triggered(); - void on_actionInstall_Fabric_triggered(); - void on_actionInstall_Quilt_triggered(); + void on_actionInstall_Loader_triggered(); void on_actionAdd_Empty_triggered(); - void on_actionInstall_LiteLoader_triggered(); void on_actionReload_triggered(); void on_actionRemove_triggered(); void on_actionMove_up_triggered(); diff --git a/launcher/ui/pages/instance/VersionPage.ui b/launcher/ui/pages/instance/VersionPage.ui index a73c42d6c..9be21d499 100644 --- a/launcher/ui/pages/instance/VersionPage.ui +++ b/launcher/ui/pages/instance/VersionPage.ui @@ -98,11 +98,7 @@ - - - - - + @@ -116,26 +112,26 @@
    - Change version + Change Version - Change version of the selected package. + Change version of the selected component. - Move up + Move Up - Make the selected package apply sooner. + Make the selected component apply sooner. - Move down + Move Down - Make the selected package apply later. + Make the selected component apply later. @@ -143,7 +139,7 @@ Remove - Remove selected package from the instance. + Remove selected component from the instance. @@ -151,7 +147,7 @@ Customize - Customize selected package. + Customize selected component. @@ -159,7 +155,7 @@ Edit - Edit selected package. + Edit selected component. @@ -167,39 +163,15 @@ Revert - Revert the selected package to default. + Revert the selected component to default. - + - Install Forge + Install Loader - Install the Minecraft Forge package. - - - - - Install Fabric - - - Install the Fabric Loader package. - - - - - Install Quilt - - - Install the Quilt Loader package. - - - - - Install LiteLoader - - - Install the LiteLoader package. + Install a mod loader. @@ -228,7 +200,7 @@ Add Empty - Add an empty custom package. + Add an empty custom component. @@ -236,12 +208,12 @@ Reload - Reload all packages. + Reload all components. - Download All + Download all Download the files needed to launch the instance now. diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp index b2200b1a7..587bb6ce6 100644 --- a/launcher/ui/pages/instance/WorldListPage.cpp +++ b/launcher/ui/pages/instance/WorldListPage.cpp @@ -36,45 +36,42 @@ */ #include "WorldListPage.h" +#include "minecraft/WorldList.h" #include "ui/dialogs/CustomMessageBox.h" #include "ui_WorldListPage.h" -#include "minecraft/WorldList.h" -#include -#include -#include #include +#include +#include +#include +#include #include #include #include -#include #include -#include "tools/MCEditTool.h" #include "FileSystem.h" +#include "tools/MCEditTool.h" -#include "ui/GuiUtil.h" #include "DesktopServices.h" +#include "ui/GuiUtil.h" #include "Application.h" - -class WorldListProxyModel : public QSortFilterProxyModel -{ +class WorldListProxyModel : public QSortFilterProxyModel { Q_OBJECT -public: - WorldListProxyModel(QObject *parent) : QSortFilterProxyModel(parent) {} + public: + WorldListProxyModel(QObject* parent) : QSortFilterProxyModel(parent) {} - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const { QModelIndex sourceIndex = mapToSource(index); - if (index.column() == 0 && role == Qt::DecorationRole) - { - WorldList *worlds = qobject_cast(sourceModel()); + if (index.column() == 0 && role == Qt::DecorationRole) { + WorldList* worlds = qobject_cast(sourceModel()); auto iconFile = worlds->data(sourceIndex, WorldList::IconFileRole).toString(); - if(iconFile.isNull()) { + if (iconFile.isNull()) { // NOTE: Minecraft uses the same placeholder for servers AND worlds return APPLICATION->getThemedIcon("unknown_server"); } @@ -85,15 +82,14 @@ public: } }; - -WorldListPage::WorldListPage(BaseInstance *inst, std::shared_ptr worlds, QWidget *parent) +WorldListPage::WorldListPage(BaseInstance* inst, std::shared_ptr worlds, QWidget* parent) : QMainWindow(parent), m_inst(inst), ui(new Ui::WorldListPage), m_worlds(worlds) { ui->setupUi(this); ui->toolBar->insertSpacer(ui->actionRefresh); - WorldListProxyModel * proxy = new WorldListProxyModel(this); + WorldListProxyModel* proxy = new WorldListProxyModel(this); proxy->setSortCaseSensitivity(Qt::CaseInsensitive); proxy->setSourceModel(m_worlds.get()); proxy->setSortRole(Qt::UserRole); @@ -101,7 +97,7 @@ WorldListPage::WorldListPage(BaseInstance *inst, std::shared_ptr worl ui->worldTreeView->setModel(proxy); ui->worldTreeView->installEventFilter(this); ui->worldTreeView->setContextMenuPolicy(Qt::CustomContextMenu); - ui->worldTreeView->setIconSize(QSize(64,64)); + ui->worldTreeView->setIconSize(QSize(64, 64)); connect(ui->worldTreeView, &QTreeView::customContextMenuRequested, this, &WorldListPage::ShowContextMenu); auto head = ui->worldTreeView->header(); @@ -146,10 +142,10 @@ void WorldListPage::ShowContextMenu(const QPoint& pos) delete menu; } -QMenu * WorldListPage::createPopupMenu() +QMenu* WorldListPage::createPopupMenu() { QMenu* filteredMenu = QMainWindow::createPopupMenu(); - filteredMenu->removeAction( ui->toolBar->toggleViewAction() ); + filteredMenu->removeAction(ui->toolBar->toggleViewAction()); return filteredMenu; } @@ -163,26 +159,24 @@ void WorldListPage::retranslate() ui->retranslateUi(this); } -bool WorldListPage::worldListFilter(QKeyEvent *keyEvent) +bool WorldListPage::worldListFilter(QKeyEvent* keyEvent) { - switch (keyEvent->key()) - { - case Qt::Key_Delete: - on_actionRemove_triggered(); - return true; - default: - break; + switch (keyEvent->key()) { + case Qt::Key_Delete: + on_actionRemove_triggered(); + return true; + default: + break; } return QWidget::eventFilter(ui->worldTreeView, keyEvent); } -bool WorldListPage::eventFilter(QObject *obj, QEvent *ev) +bool WorldListPage::eventFilter(QObject* obj, QEvent* ev) { - if (ev->type() != QEvent::KeyPress) - { + if (ev->type() != QEvent::KeyPress) { return QWidget::eventFilter(obj, ev); } - QKeyEvent *keyEvent = static_cast(ev); + QKeyEvent* keyEvent = static_cast(ev); if (obj == ui->worldTreeView) return worldListFilter(keyEvent); return QWidget::eventFilter(obj, ev); @@ -192,7 +186,7 @@ void WorldListPage::on_actionRemove_triggered() { auto proxiedIndex = getSelectedWorld(); - if(!proxiedIndex.isValid()) + if (!proxiedIndex.isValid()) return; auto result = CustomMessageBox::selectable(this, tr("Confirm Deletion"), @@ -203,8 +197,7 @@ void WorldListPage::on_actionRemove_triggered() QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ->exec(); - if(result != QMessageBox::Yes) - { + if (result != QMessageBox::Yes) { return; } m_worlds->stopWatching(); @@ -221,12 +214,11 @@ void WorldListPage::on_actionDatapacks_triggered() { QModelIndex index = getSelectedWorld(); - if (!index.isValid()) - { + if (!index.isValid()) { return; } - if(!worldSafetyNagQuestion(tr("Open World Datapacks Folder"))) + if (!worldSafetyNagQuestion(tr("Open World Datapacks Folder"))) return; auto fullPath = m_worlds->data(index, WorldList::FolderRole).toString(); @@ -234,25 +226,23 @@ void WorldListPage::on_actionDatapacks_triggered() DesktopServices::openDirectory(FS::PathCombine(fullPath, "datapacks"), true); } - void WorldListPage::on_actionReset_Icon_triggered() { auto proxiedIndex = getSelectedWorld(); - if(!proxiedIndex.isValid()) + if (!proxiedIndex.isValid()) return; - if(m_worlds->resetIcon(proxiedIndex.row())) { + if (m_worlds->resetIcon(proxiedIndex.row())) { ui->actionReset_Icon->setEnabled(false); } } - QModelIndex WorldListPage::getSelectedWorld() { auto index = ui->worldTreeView->selectionModel()->currentIndex(); - auto proxy = (QSortFilterProxyModel *) ui->worldTreeView->model(); + auto proxy = (QSortFilterProxyModel*)ui->worldTreeView->model(); return proxy->mapToSource(index); } @@ -260,8 +250,7 @@ void WorldListPage::on_actionCopy_Seed_triggered() { QModelIndex index = getSelectedWorld(); - if (!index.isValid()) - { + if (!index.isValid()) { return; } int64_t seed = m_worlds->data(index, WorldList::SeedRole).toLongLong(); @@ -270,7 +259,7 @@ void WorldListPage::on_actionCopy_Seed_triggered() void WorldListPage::on_actionMCEdit_triggered() { - if(m_mceditStarting) + if (m_mceditStarting) return; auto mcedit = APPLICATION->mcedit(); @@ -279,81 +268,66 @@ void WorldListPage::on_actionMCEdit_triggered() QModelIndex index = getSelectedWorld(); - if (!index.isValid()) - { + if (!index.isValid()) { return; } - if(!worldSafetyNagQuestion(tr("Open World in MCEdit"))) + if (!worldSafetyNagQuestion(tr("Open World in MCEdit"))) return; auto fullPath = m_worlds->data(index, WorldList::FolderRole).toString(); auto program = mcedit->getProgramPath(); - if(program.size()) - { + if (program.size()) { #ifdef Q_OS_WIN32 - if(!QProcess::startDetached(program, {fullPath}, mceditPath)) - { + if (!QProcess::startDetached(program, { fullPath }, mceditPath)) { mceditError(); } #else m_mceditProcess.reset(new LoggedProcess()); m_mceditProcess->setDetachable(true); connect(m_mceditProcess.get(), &LoggedProcess::stateChanged, this, &WorldListPage::mceditState); - m_mceditProcess->start(program, {fullPath}); + m_mceditProcess->start(program, { fullPath }); m_mceditProcess->setWorkingDirectory(mceditPath); m_mceditStarting = true; #endif - } - else - { - QMessageBox::warning( - this->parentWidget(), - tr("No MCEdit found or set up!"), - tr("You do not have MCEdit set up or it was moved.\nYou can set it up in the global settings.") - ); + } else { + QMessageBox::warning(this->parentWidget(), tr("No MCEdit found or set up!"), + tr("You do not have MCEdit set up or it was moved.\nYou can set it up in the global settings.")); } } void WorldListPage::mceditError() { - QMessageBox::warning( - this->parentWidget(), - tr("MCEdit failed to start!"), - tr("MCEdit failed to start.\nIt may be necessary to reinstall it.") - ); + QMessageBox::warning(this->parentWidget(), tr("MCEdit failed to start!"), + tr("MCEdit failed to start.\nIt may be necessary to reinstall it.")); } void WorldListPage::mceditState(LoggedProcess::State state) { bool failed = false; - switch(state) - { + switch (state) { case LoggedProcess::NotRunning: case LoggedProcess::Starting: return; case LoggedProcess::FailedToStart: case LoggedProcess::Crashed: - case LoggedProcess::Aborted: - { + case LoggedProcess::Aborted: { failed = true; } /* fallthrough */ case LoggedProcess::Running: - case LoggedProcess::Finished: - { + case LoggedProcess::Finished: { m_mceditStarting = false; break; } } - if(failed) - { + if (failed) { mceditError(); } } -void WorldListPage::worldChanged(const QModelIndex ¤t, const QModelIndex &previous) +void WorldListPage::worldChanged([[maybe_unused]] const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { QModelIndex index = getSelectedWorld(); bool enable = index.isValid(); @@ -369,15 +343,11 @@ void WorldListPage::worldChanged(const QModelIndex ¤t, const QModelIndex & void WorldListPage::on_actionAdd_triggered() { - auto list = GuiUtil::BrowseForFiles( - displayName(), - tr("Select a Minecraft world zip"), - tr("Minecraft World Zip File (*.zip)"), QString(), this->parentWidget()); - if (!list.empty()) - { + auto list = GuiUtil::BrowseForFiles(displayName(), tr("Select a Minecraft world zip"), tr("Minecraft World Zip File (*.zip)"), + QString(), this->parentWidget()); + if (!list.empty()) { m_worlds->stopWatching(); - for (auto filename : list) - { + for (auto filename : list) { m_worlds->installWorld(QFileInfo(filename)); } m_worlds->startWatching(); @@ -389,38 +359,35 @@ bool WorldListPage::isWorldSafe(QModelIndex) return !m_inst->isRunning(); } -bool WorldListPage::worldSafetyNagQuestion(const QString &actionType) +bool WorldListPage::worldSafetyNagQuestion(const QString& actionType) { - if(!isWorldSafe(getSelectedWorld())) - { - auto result = QMessageBox::question(this, actionType, tr("Changing a world while Minecraft is running is potentially unsafe.\nDo you wish to proceed?")); - if(result == QMessageBox::No) - { + if (!isWorldSafe(getSelectedWorld())) { + auto result = QMessageBox::question( + this, actionType, tr("Changing a world while Minecraft is running is potentially unsafe.\nDo you wish to proceed?")); + if (result == QMessageBox::No) { return false; } } return true; } - void WorldListPage::on_actionCopy_triggered() { QModelIndex index = getSelectedWorld(); - if (!index.isValid()) - { + if (!index.isValid()) { return; } - if(!worldSafetyNagQuestion(tr("Copy World"))) + if (!worldSafetyNagQuestion(tr("Copy World"))) return; auto worldVariant = m_worlds->data(index, WorldList::ObjectRole); - auto world = (World *) worldVariant.value(); + auto world = (World*)worldVariant.value(); bool ok = false; - QString name = QInputDialog::getText(this, tr("World name"), tr("Enter a new name for the copy."), QLineEdit::Normal, world->name(), &ok); + QString name = + QInputDialog::getText(this, tr("World name"), tr("Enter a new name for the copy."), QLineEdit::Normal, world->name(), &ok); - if (ok && name.length() > 0) - { + if (ok && name.length() > 0) { world->install(m_worlds->dir().absolutePath(), name); } } @@ -428,22 +395,20 @@ void WorldListPage::on_actionCopy_triggered() void WorldListPage::on_actionRename_triggered() { QModelIndex index = getSelectedWorld(); - if (!index.isValid()) - { + if (!index.isValid()) { return; } - if(!worldSafetyNagQuestion(tr("Rename World"))) + if (!worldSafetyNagQuestion(tr("Rename World"))) return; auto worldVariant = m_worlds->data(index, WorldList::ObjectRole); - auto world = (World *) worldVariant.value(); + auto world = (World*)worldVariant.value(); bool ok = false; QString name = QInputDialog::getText(this, tr("World name"), tr("Enter a new world name."), QLineEdit::Normal, world->name(), &ok); - if (ok && name.length() > 0) - { + if (ok && name.length() > 0) { world->rename(name); } } diff --git a/launcher/ui/pages/instance/WorldListPage.h b/launcher/ui/pages/instance/WorldListPage.h index 925521bec..4f83002f4 100644 --- a/launcher/ui/pages/instance/WorldListPage.h +++ b/launcher/ui/pages/instance/WorldListPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -37,76 +37,58 @@ #include -#include "minecraft/MinecraftInstance.h" -#include "ui/pages/BasePage.h" #include #include +#include "minecraft/MinecraftInstance.h" +#include "ui/pages/BasePage.h" #include "settings/Setting.h" class WorldList; -namespace Ui -{ +namespace Ui { class WorldListPage; } -class WorldListPage : public QMainWindow, public BasePage -{ +class WorldListPage : public QMainWindow, public BasePage { Q_OBJECT -public: - explicit WorldListPage( - BaseInstance *inst, - std::shared_ptr worlds, - QWidget *parent = 0 - ); + public: + explicit WorldListPage(BaseInstance* inst, std::shared_ptr worlds, QWidget* parent = 0); virtual ~WorldListPage(); - virtual QString displayName() const override - { - return tr("Worlds"); - } - virtual QIcon icon() const override - { - return APPLICATION->getThemedIcon("worlds"); - } - virtual QString id() const override - { - return "worlds"; - } - virtual QString helpPage() const override - { - return "Worlds"; - } + virtual QString displayName() const override { return tr("Worlds"); } + virtual QIcon icon() const override { return APPLICATION->getThemedIcon("worlds"); } + virtual QString id() const override { return "worlds"; } + virtual QString helpPage() const override { return "Worlds"; } virtual bool shouldDisplay() const override; void retranslate() override; virtual void openedImpl() override; virtual void closedImpl() override; -protected: - bool eventFilter(QObject *obj, QEvent *ev) override; - bool worldListFilter(QKeyEvent *ev); - QMenu * createPopupMenu() override; + protected: + bool eventFilter(QObject* obj, QEvent* ev) override; + bool worldListFilter(QKeyEvent* ev); + QMenu* createPopupMenu() override; -protected: - BaseInstance *m_inst; + protected: + BaseInstance* m_inst; -private: + private: QModelIndex getSelectedWorld(); bool isWorldSafe(QModelIndex index); - bool worldSafetyNagQuestion(const QString &actionType); + bool worldSafetyNagQuestion(const QString& actionType); void mceditError(); -private: - Ui::WorldListPage *ui; + private: + Ui::WorldListPage* ui; std::shared_ptr m_worlds; unique_qobject_ptr m_mceditProcess; bool m_mceditStarting = false; std::shared_ptr m_wide_bar_setting = nullptr; -private slots: + private slots: void on_actionCopy_Seed_triggered(); void on_actionMCEdit_triggered(); void on_actionRemove_triggered(); @@ -117,8 +99,8 @@ private slots: void on_actionView_Folder_triggered(); void on_actionDatapacks_triggered(); void on_actionReset_Icon_triggered(); - void worldChanged(const QModelIndex ¤t, const QModelIndex &previous); + void worldChanged(const QModelIndex& current, const QModelIndex& previous); void mceditState(LoggedProcess::State state); - void ShowContextMenu(const QPoint &pos); + void ShowContextMenu(const QPoint& pos); }; diff --git a/launcher/ui/pages/modplatform/CustomPage.cpp b/launcher/ui/pages/modplatform/CustomPage.cpp index e164171a4..4ac21b012 100644 --- a/launcher/ui/pages/modplatform/CustomPage.cpp +++ b/launcher/ui/pages/modplatform/CustomPage.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -46,8 +46,7 @@ #include "minecraft/VanillaInstanceCreationTask.h" #include "ui/dialogs/NewInstanceDialog.h" -CustomPage::CustomPage(NewInstanceDialog *dialog, QWidget *parent) - : QWidget(parent), dialog(dialog), ui(new Ui::CustomPage) +CustomPage::CustomPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), dialog(dialog), ui(new Ui::CustomPage) { ui->setupUi(this); ui->tabWidget->tabBar()->hide(); @@ -68,19 +67,15 @@ CustomPage::CustomPage(NewInstanceDialog *dialog, QWidget *parent) connect(ui->quiltFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged); connect(ui->liteLoaderFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged); connect(ui->loaderRefreshBtn, &QPushButton::clicked, this, &CustomPage::loaderRefresh); - } void CustomPage::openedImpl() { - if(!initialized) - { + if (!initialized) { auto vlist = APPLICATION->metadataIndex()->get("net.minecraft"); ui->versionList->initialize(vlist.get()); initialized = true; - } - else - { + } else { suggestCurrent(); } } @@ -92,7 +87,7 @@ void CustomPage::refresh() void CustomPage::loaderRefresh() { - if(ui->noneFilter->isChecked()) + if (ui->noneFilter->isChecked()) return; ui->loaderVersionList->loadList(); } @@ -100,17 +95,17 @@ void CustomPage::loaderRefresh() void CustomPage::filterChanged() { QStringList out; - if(ui->alphaFilter->isChecked()) + if (ui->alphaFilter->isChecked()) out << "(old_alpha)"; - if(ui->betaFilter->isChecked()) + if (ui->betaFilter->isChecked()) out << "(old_beta)"; - if(ui->snapshotFilter->isChecked()) + if (ui->snapshotFilter->isChecked()) out << "(snapshot)"; - if(ui->oldSnapshotFilter->isChecked()) + if (ui->oldSnapshotFilter->isChecked()) out << "(old_snapshot)"; - if(ui->releaseFilter->isChecked()) + if (ui->releaseFilter->isChecked()) out << "(release)"; - if(ui->experimentsFilter->isChecked()) + if (ui->experimentsFilter->isChecked()) out << "(experiment)"; auto regexp = out.join('|'); ui->versionList->setFilter(BaseVersionList::TypeRole, new RegexpFilter(regexp, false)); @@ -119,49 +114,37 @@ void CustomPage::filterChanged() void CustomPage::loaderFilterChanged() { QString minecraftVersion; - if (m_selectedVersion) - { + if (m_selectedVersion) { minecraftVersion = m_selectedVersion->descriptor(); - } - else - { - ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // empty list + } else { + ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // empty list ui->loaderVersionList->setEmptyString(tr("No Minecraft version is selected.")); ui->loaderVersionList->setEmptyMode(VersionListView::String); return; } - if(ui->noneFilter->isChecked()) - { - ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // empty list + if (ui->noneFilter->isChecked()) { + ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // empty list ui->loaderVersionList->setEmptyString(tr("No mod loader is selected.")); ui->loaderVersionList->setEmptyMode(VersionListView::String); return; - } - else if(ui->forgeFilter->isChecked()) - { + } else if (ui->forgeFilter->isChecked()) { ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, minecraftVersion); m_selectedLoader = "net.minecraftforge"; - } - else if(ui->fabricFilter->isChecked()) - { + } else if (ui->fabricFilter->isChecked()) { // FIXME: dirty hack because the launcher is unaware of Fabric's dependencies - if (Version(minecraftVersion) >= Version("1.14")) // Fabric/Quilt supported + if (Version(minecraftVersion) >= Version("1.14")) // Fabric/Quilt supported ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, ""); - else // Fabric/Quilt unsupported - ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // clear list + else // Fabric/Quilt unsupported + ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // clear list m_selectedLoader = "net.fabricmc.fabric-loader"; - } - else if(ui->quiltFilter->isChecked()) - { + } else if (ui->quiltFilter->isChecked()) { // FIXME: dirty hack because the launcher is unaware of Quilt's dependencies (same as Fabric) - if (Version(minecraftVersion) >= Version("1.14")) // Fabric/Quilt supported + if (Version(minecraftVersion) >= Version("1.14")) // Fabric/Quilt supported ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, ""); - else // Fabric/Quilt unsupported - ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // clear list + else // Fabric/Quilt unsupported + ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // clear list m_selectedLoader = "org.quiltmc.quilt-loader"; - } - else if(ui->liteLoaderFilter->isChecked()) - { + } else if (ui->liteLoaderFilter->isChecked()) { ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, minecraftVersion); m_selectedLoader = "com.mumfrey.liteloader"; } @@ -204,25 +187,21 @@ QString CustomPage::selectedLoader() const void CustomPage::suggestCurrent() { - if (!isOpened) - { + if (!isOpened) { return; } - - if(!m_selectedVersion) - { + + if (!m_selectedVersion) { dialog->setSuggestedPack(); return; } // There isn't a selected version if the version list is empty - if(ui->loaderVersionList->selectedVersion() == nullptr) + if (ui->loaderVersionList->selectedVersion() == nullptr) dialog->setSuggestedPack(m_selectedVersion->descriptor(), new VanillaCreationTask(m_selectedVersion)); - else - { + else { dialog->setSuggestedPack(m_selectedVersion->descriptor(), - new VanillaCreationTask(m_selectedVersion, m_selectedLoader, - m_selectedLoaderVersion)); + new VanillaCreationTask(m_selectedVersion, m_selectedLoader, m_selectedLoaderVersion)); } dialog->setSuggestedIcon("default"); } diff --git a/launcher/ui/pages/modplatform/CustomPage.h b/launcher/ui/pages/modplatform/CustomPage.h index 8b5a5011c..c5d6d5af5 100644 --- a/launcher/ui/pages/modplatform/CustomPage.h +++ b/launcher/ui/pages/modplatform/CustomPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -37,40 +37,26 @@ #include -#include "ui/pages/BasePage.h" #include #include "tasks/Task.h" +#include "ui/pages/BasePage.h" -namespace Ui -{ +namespace Ui { class CustomPage; } class NewInstanceDialog; -class CustomPage : public QWidget, public BasePage -{ +class CustomPage : public QWidget, public BasePage { Q_OBJECT -public: - explicit CustomPage(NewInstanceDialog *dialog, QWidget *parent = 0); + public: + explicit CustomPage(NewInstanceDialog* dialog, QWidget* parent = 0); virtual ~CustomPage(); - virtual QString displayName() const override - { - return tr("Custom"); - } - virtual QIcon icon() const override - { - return APPLICATION->getThemedIcon("minecraft"); - } - virtual QString id() const override - { - return "vanilla"; - } - virtual QString helpPage() const override - { - return "Vanilla-platform"; - } + virtual QString displayName() const override { return tr("Custom"); } + virtual QIcon icon() const override { return APPLICATION->getThemedIcon("minecraft"); } + virtual QString id() const override { return "vanilla"; } + virtual QString helpPage() const override { return "Vanilla-platform"; } virtual bool shouldDisplay() const override; void retranslate() override; @@ -80,23 +66,23 @@ public: BaseVersion::Ptr selectedLoaderVersion() const; QString selectedLoader() const; -public slots: + public slots: void setSelectedVersion(BaseVersion::Ptr version); void setSelectedLoaderVersion(BaseVersion::Ptr version); -private slots: + private slots: void filterChanged(); void loaderFilterChanged(); -private: + private: void refresh(); void loaderRefresh(); void suggestCurrent(); -private: + private: bool initialized = false; - NewInstanceDialog *dialog = nullptr; - Ui::CustomPage *ui = nullptr; + NewInstanceDialog* dialog = nullptr; + Ui::CustomPage* ui = nullptr; bool m_versionSetByUser = false; BaseVersion::Ptr m_selectedVersion; BaseVersion::Ptr m_selectedLoaderVersion; diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp index 30196aad6..3e3c36b7b 100644 --- a/launcher/ui/pages/modplatform/ImportPage.cpp +++ b/launcher/ui/pages/modplatform/ImportPage.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu * @@ -35,41 +35,41 @@ */ #include "ImportPage.h" + +#include "ui/dialogs/ProgressDialog.h" #include "ui_ImportPage.h" #include #include +#include +#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/NewInstanceDialog.h" +#include "modplatform/flame/FlameAPI.h" + +#include "Json.h" + #include "InstanceImportTask.h" - -class UrlValidator : public QValidator -{ -public: +class UrlValidator : public QValidator { + public: using QValidator::QValidator; - State validate(QString &in, int &pos) const + State validate(QString& in, [[maybe_unused]] int& pos) const { const QUrl url(in); - if (url.isValid() && !url.isRelative() && !url.isEmpty()) - { + if (url.isValid() && !url.isRelative() && !url.isEmpty()) { return Acceptable; - } - else if (QFile::exists(in)) - { + } else if (QFile::exists(in)) { return Acceptable; - } - else - { + } else { return Intermediate; } } }; -ImportPage::ImportPage(NewInstanceDialog* dialog, QWidget *parent) - : QWidget(parent), ui(new Ui::ImportPage), dialog(dialog) +ImportPage::ImportPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), ui(new Ui::ImportPage), dialog(dialog) { ui->setupUi(this); ui->modpackEdit->setValidator(new UrlValidator(ui->modpackEdit)); @@ -98,16 +98,13 @@ void ImportPage::openedImpl() void ImportPage::updateState() { - if(!isOpened) - { + if (!isOpened) { return; } - if(ui->modpackEdit->hasAcceptableInput()) - { + if (ui->modpackEdit->hasAcceptableInput()) { QString input = ui->modpackEdit->text(); auto url = QUrl::fromUserInput(input); - if(url.isLocalFile()) - { + if (url.isLocalFile()) { // FIXME: actually do some validation of what's inside here... this is fake AF QFileInfo fi(input); @@ -116,28 +113,75 @@ void ImportPage::updateState() // mrpack is a modrinth pack bool isMRPack = fi.suffix() == "mrpack"; - if(fi.exists() && (isZip || isMRPack)) - { - QFileInfo fi(url.fileName()); - dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url,this)); + if (fi.exists() && (isZip || isMRPack)) { + auto extra_info = QMap(m_extra_info); + qDebug() << "Pack Extra Info" << extra_info << m_extra_info; + dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url, this, std::move(extra_info))); dialog->setSuggestedIcon("default"); } - } - else - { - if(input.endsWith("?client=y")) { + } else if (url.scheme() == "curseforge") { + // need to find the download link for the modpack + // format of url curseforge://install?addonId=IDHERE&fileId=IDHERE + QUrlQuery query(url); + auto addonId = query.allQueryItemValues("addonId")[0]; + auto fileId = query.allQueryItemValues("fileId")[0]; + auto array = std::make_shared(); + + auto api = FlameAPI(); + auto job = api.getFile(addonId, fileId, array); + + connect(job.get(), &NetJob::failed, this, + [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); + connect(job.get(), &NetJob::succeeded, this, [this, array, addonId, fileId] { + qDebug() << "Returned CFURL Json:\n" << array->toStdString().c_str(); + auto doc = Json::requireDocument(*array); + auto data = Json::ensureObject(Json::ensureObject(doc.object()), "data"); + // No way to find out if it's a mod or a modpack before here + // And also we need to check if it ends with .zip, instead of any better way + auto fileName = Json::ensureString(data, "fileName"); + if (fileName.endsWith(".zip")) { + // Have to use ensureString then use QUrl to get proper url encoding + auto dl_url = QUrl(Json::ensureString(data, "downloadUrl", "", "downloadUrl")); + if (!dl_url.isValid()) { + CustomMessageBox::selectable( + this, tr("Error"), + tr("The modpack %1 is blocked for third-parties! Please download it manually.").arg(fileName), + QMessageBox::Critical) + ->show(); + return; + } + + QFileInfo dl_file(dl_url.fileName()); + QString pack_name = Json::ensureString(data, "displayName", dl_file.completeBaseName(), "displayName"); + + QMap extra_info; + extra_info.insert("pack_id", addonId); + extra_info.insert("pack_version_id", fileId); + + dialog->setSuggestedPack(pack_name, new InstanceImportTask(dl_url, this, std::move(extra_info))); + dialog->setSuggestedIcon("default"); + + } else { + CustomMessageBox::selectable(this, tr("Error"), tr("This url isn't a valid modpack !"), QMessageBox::Critical)->show(); + } + }); + ProgressDialog dlUrlDialod(this); + dlUrlDialod.setSkipButton(true, tr("Abort")); + dlUrlDialod.execWithTask(job.get()); + return; + } else { + if (input.endsWith("?client=y")) { input.chop(9); input.append("/file"); url = QUrl::fromUserInput(input); } // hook, line and sinker. QFileInfo fi(url.fileName()); - dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url,this)); + auto extra_info = QMap(m_extra_info); + dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url, this, std::move(extra_info))); dialog->setSuggestedIcon("default"); } - } - else - { + } else { dialog->setSuggestedPack(); } } @@ -148,35 +192,33 @@ void ImportPage::setUrl(const QString& url) updateState(); } +void ImportPage::setExtraInfo(const QMap& extra_info) +{ + m_extra_info = extra_info; + updateState(); +} + void ImportPage::on_modpackBtn_clicked() { auto filter = QMimeDatabase().mimeTypeForName("application/zip").filterString(); //: Option for filtering for *.mrpack files when importing filter += ";;" + tr("Modrinth pack") + " (*.mrpack)"; const QUrl url = QFileDialog::getOpenFileUrl(this, tr("Choose modpack"), modpackUrl(), filter); - if (url.isValid()) - { - if (url.isLocalFile()) - { + if (url.isValid()) { + if (url.isLocalFile()) { ui->modpackEdit->setText(url.toLocalFile()); - } - else - { + } else { ui->modpackEdit->setText(url.toString()); } } } - QUrl ImportPage::modpackUrl() const { const QUrl url(ui->modpackEdit->text()); - if (url.isValid() && !url.isRelative() && !url.host().isEmpty()) - { + if (url.isValid() && !url.isRelative() && !url.host().isEmpty()) { return url; - } - else - { + } else { return QUrl::fromLocalFile(ui->modpackEdit->text()); } } diff --git a/launcher/ui/pages/modplatform/ImportPage.h b/launcher/ui/pages/modplatform/ImportPage.h index c2acb92da..70d7736eb 100644 --- a/launcher/ui/pages/modplatform/ImportPage.h +++ b/launcher/ui/pages/modplatform/ImportPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -37,55 +37,41 @@ #include -#include "ui/pages/BasePage.h" #include #include "tasks/Task.h" +#include "ui/pages/BasePage.h" -namespace Ui -{ +namespace Ui { class ImportPage; } class NewInstanceDialog; -class ImportPage : public QWidget, public BasePage -{ +class ImportPage : public QWidget, public BasePage { Q_OBJECT -public: - explicit ImportPage(NewInstanceDialog* dialog, QWidget *parent = 0); + public: + explicit ImportPage(NewInstanceDialog* dialog, QWidget* parent = 0); virtual ~ImportPage(); - virtual QString displayName() const override - { - return tr("Import"); - } - virtual QIcon icon() const override - { - return APPLICATION->getThemedIcon("viewfolder"); - } - virtual QString id() const override - { - return "import"; - } - virtual QString helpPage() const override - { - return "Zip-import"; - } + virtual QString displayName() const override { return tr("Import"); } + virtual QIcon icon() const override { return APPLICATION->getThemedIcon("viewfolder"); } + virtual QString id() const override { return "import"; } + virtual QString helpPage() const override { return "Zip-import"; } virtual bool shouldDisplay() const override; void retranslate() override; - void setUrl(const QString & url); + void setUrl(const QString& url); void openedImpl() override; - -private slots: + void setExtraInfo(const QMap& extra_info); + private slots: void on_modpackBtn_clicked(); void updateState(); -private: + private: QUrl modpackUrl() const; -private: - Ui::ImportPage *ui = nullptr; + private: + Ui::ImportPage* ui = nullptr; NewInstanceDialog* dialog = nullptr; + QMap m_extra_info = {}; }; - diff --git a/launcher/ui/pages/modplatform/ImportPage.ui b/launcher/ui/pages/modplatform/ImportPage.ui index 3583cf90a..9a9736b8a 100644 --- a/launcher/ui/pages/modplatform/ImportPage.ui +++ b/launcher/ui/pages/modplatform/ImportPage.ui @@ -40,7 +40,7 @@ - - CurseForge modpacks (ZIP) + - CurseForge modpacks (ZIP / curseforge:// URL) Qt::AlignCenter diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 49405a02b..0a7edb7b7 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -17,7 +17,7 @@ #include "BuildConfig.h" #include "Json.h" -#include "net/Download.h" +#include "net/ApiDownload.h" #include "net/NetJob.h" #include "modplatform/ModIndex.h" @@ -102,7 +102,7 @@ QHash ResourceModel::roleNames() const return roles; } -bool ResourceModel::setData(const QModelIndex& index, const QVariant& value, int role) +bool ResourceModel::setData(const QModelIndex& index, const QVariant& value, [[maybe_unused]] int role) { int pos = index.row(); if (pos >= m_packs.size() || pos < 0 || !index.isValid()) @@ -281,7 +281,7 @@ std::optional ResourceModel::getIcon(QModelIndex& index, const QUrl& url) auto cache_entry = APPLICATION->metacache()->resolveEntry( metaEntryBase(), QString("logos/%1").arg(QString(QCryptographicHash::hash(url.toEncoded(), QCryptographicHash::Algorithm::Sha1).toHex()))); - auto icon_fetch_action = Net::Download::makeCached(url, cache_entry); + auto icon_fetch_action = Net::ApiDownload::makeCached(url, cache_entry); auto full_file_path = cache_entry->getFullPath(); connect(icon_fetch_action.get(), &NetAction::succeeded, this, [=] { @@ -310,7 +310,7 @@ std::optional ResourceModel::getIcon(QModelIndex& index, const QUrl& url) #define NEED_FOR_CALLBACK_ASSERT(name) \ Q_ASSERT_X(0 != 0, #name, "You NEED to re-implement this if you intend on using the default callbacks.") -QJsonArray ResourceModel::documentToArray(QJsonDocument& doc) const +QJsonArray ResourceModel::documentToArray([[maybe_unused]] QJsonDocument& doc) const { NEED_FOR_CALLBACK_ASSERT("documentToArray"); return {}; @@ -372,7 +372,7 @@ void ResourceModel::searchRequestSucceeded(QJsonDocument& doc) endInsertRows(); } -void ResourceModel::searchRequestFailed(QString reason, int network_error_code) +void ResourceModel::searchRequestFailed([[maybe_unused]] QString reason, int network_error_code) { switch (network_error_code) { default: diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h index 6533d9c6a..cc813d6e6 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.h +++ b/launcher/ui/pages/modplatform/ResourceModel.h @@ -42,7 +42,10 @@ class ResourceModel : public QAbstractListModel { [[nodiscard]] virtual auto debugName() const -> QString; [[nodiscard]] virtual auto metaEntryBase() const -> QString = 0; - [[nodiscard]] inline int rowCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : m_packs.size(); } + [[nodiscard]] inline int rowCount(const QModelIndex& parent) const override + { + return parent.isValid() ? 0 : static_cast(m_packs.size()); + } [[nodiscard]] inline int columnCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : 1; } [[nodiscard]] inline auto flags(const QModelIndex& index) const -> Qt::ItemFlags override { return QAbstractListModel::flags(index); } diff --git a/launcher/ui/pages/modplatform/ResourcePackModel.cpp b/launcher/ui/pages/modplatform/ResourcePackModel.cpp index 18c14bf81..d436f320f 100644 --- a/launcher/ui/pages/modplatform/ResourcePackModel.cpp +++ b/launcher/ui/pages/modplatform/ResourcePackModel.cpp @@ -9,7 +9,8 @@ namespace ResourceDownload { ResourcePackResourceModel::ResourcePackResourceModel(BaseInstance const& base_inst, ResourceAPI* api) - : ResourceModel(api), m_base_instance(base_inst){}; + : ResourceModel(api), m_base_instance(base_inst) +{} /******** Make data requests ********/ diff --git a/launcher/ui/pages/modplatform/ResourcePackPage.cpp b/launcher/ui/pages/modplatform/ResourcePackPage.cpp index 52fb4802c..2a4d02815 100644 --- a/launcher/ui/pages/modplatform/ResourcePackPage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePackPage.cpp @@ -13,8 +13,7 @@ namespace ResourceDownload { -ResourcePackResourcePage::ResourcePackResourcePage(ResourceDownloadDialog* dialog, BaseInstance& instance) - : ResourcePage(dialog, instance) +ResourcePackResourcePage::ResourcePackResourcePage(ResourceDownloadDialog* dialog, BaseInstance& instance) : ResourcePage(dialog, instance) { connect(m_ui->searchButton, &QPushButton::clicked, this, &ResourcePackResourcePage::triggerSearch); connect(m_ui->packView, &QListView::doubleClicked, this, &ResourcePackResourcePage::onResourceSelected); @@ -38,7 +37,8 @@ QMap ResourcePackResourcePage::urlHandlers() const { QMap map; map.insert(QRegularExpression::anchoredPattern("(?:www\\.)?modrinth\\.com\\/resourcepack\\/([^\\/]+)\\/?"), "modrinth"); - map.insert(QRegularExpression::anchoredPattern("(?:www\\.)?curseforge\\.com\\/minecraft\\/texture-packs\\/([^\\/]+)\\/?"), "curseforge"); + map.insert(QRegularExpression::anchoredPattern("(?:www\\.)?curseforge\\.com\\/minecraft\\/texture-packs\\/([^\\/]+)\\/?"), + "curseforge"); map.insert(QRegularExpression::anchoredPattern("minecraft\\.curseforge\\.com\\/projects\\/([^\\/]+)\\/?"), "curseforge"); return map; } diff --git a/launcher/ui/pages/modplatform/ResourcePackPage.h b/launcher/ui/pages/modplatform/ResourcePackPage.h index 8c5cf08b7..6015aec0b 100644 --- a/launcher/ui/pages/modplatform/ResourcePackPage.h +++ b/launcher/ui/pages/modplatform/ResourcePackPage.h @@ -4,8 +4,8 @@ #pragma once -#include "ui/pages/modplatform/ResourcePage.h" #include "ui/pages/modplatform/ResourcePackModel.h" +#include "ui/pages/modplatform/ResourcePage.h" namespace Ui { class ResourcePage; diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index aab2ee89a..c087e2be3 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -4,7 +4,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2023 TheKodeToad * * 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 @@ -104,6 +104,7 @@ void ResourcePage::openedImpl() updateSelectionButton(); triggerSearch(); + m_ui->searchEdit->setFocus(); } auto ResourcePage::eventFilter(QObject* watched, QEvent* event) -> bool @@ -279,7 +280,7 @@ void ResourcePage::updateVersionList() updateSelectionButton(); } -void ResourcePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) +void ResourcePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelIndex prev) { if (!curr.isValid()) { return; @@ -306,9 +307,9 @@ void ResourcePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) updateUi(); } -void ResourcePage::onVersionSelectionChanged(QString data) +void ResourcePage::onVersionSelectionChanged(QString versionData) { - if (data.isNull() || data.isEmpty()) { + if (versionData.isNull() || versionData.isEmpty()) { m_selected_version_index = -1; return; } @@ -394,7 +395,7 @@ void ResourcePage::openUrl(const QUrl& url) if (auto current_pack = getCurrentPack(); current_pack && slug != current_pack->slug) { m_parent_dialog->selectPage(page); - auto newPage = m_parent_dialog->getSelectedPage(); + auto newPage = m_parent_dialog->selectedPage(); QLineEdit* searchEdit = newPage->m_ui->searchEdit; auto model = newPage->m_model; diff --git a/launcher/ui/pages/modplatform/ResourcePage.h b/launcher/ui/pages/modplatform/ResourcePage.h index b4a87f573..7bec0a375 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.h +++ b/launcher/ui/pages/modplatform/ResourcePage.h @@ -97,7 +97,11 @@ class ResourcePage : public QWidget, public BasePage { virtual void openUrl(const QUrl&); /** Whether the version is opted out or not. Currently only makes sense in CF. */ - virtual bool optedOut(ModPlatform::IndexedVersion& ver) const { return false; }; + virtual bool optedOut(ModPlatform::IndexedVersion& ver) const + { + Q_UNUSED(ver); + return false; + }; public: BaseInstance& m_base_instance; diff --git a/launcher/ui/pages/modplatform/ShaderPackModel.cpp b/launcher/ui/pages/modplatform/ShaderPackModel.cpp index aabd3be6e..8c913657a 100644 --- a/launcher/ui/pages/modplatform/ShaderPackModel.cpp +++ b/launcher/ui/pages/modplatform/ShaderPackModel.cpp @@ -9,7 +9,8 @@ namespace ResourceDownload { ShaderPackResourceModel::ShaderPackResourceModel(BaseInstance const& base_inst, ResourceAPI* api) - : ResourceModel(api), m_base_instance(base_inst){}; + : ResourceModel(api), m_base_instance(base_inst) +{} /******** Make data requests ********/ diff --git a/launcher/ui/pages/modplatform/TexturePackPage.h b/launcher/ui/pages/modplatform/TexturePackPage.h index 0bdce2f90..948e5286b 100644 --- a/launcher/ui/pages/modplatform/TexturePackPage.h +++ b/launcher/ui/pages/modplatform/TexturePackPage.h @@ -4,10 +4,10 @@ #pragma once -#include "ui_ResourcePage.h" #include "ui/dialogs/ResourceDownloadDialog.h" #include "ui/pages/modplatform/ResourcePackPage.h" #include "ui/pages/modplatform/TexturePackModel.h" +#include "ui_ResourcePage.h" namespace Ui { class ResourcePage; @@ -39,8 +39,7 @@ class TexturePackResourcePage : public ResourcePackResourcePage { [[nodiscard]] inline QString resourceString() const override { return tr("texture pack"); } protected: - TexturePackResourcePage(TexturePackDownloadDialog* dialog, BaseInstance& instance) - : ResourcePackResourcePage(dialog, instance) + TexturePackResourcePage(TexturePackDownloadDialog* dialog, BaseInstance& instance) : ResourcePackResourcePage(dialog, instance) { connect(m_ui->searchButton, &QPushButton::clicked, this, &TexturePackResourcePage::triggerSearch); connect(m_ui->packView, &QListView::doubleClicked, this, &TexturePackResourcePage::onResourceSelected); diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp index 0887ebeef..9cd5eed53 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp @@ -18,14 +18,14 @@ #include -#include #include +#include #include "StringUtils.h" namespace Atl { -FilterModel::FilterModel(QObject *parent) : QSortFilterProxyModel(parent) +FilterModel::FilterModel(QObject* parent) : QSortFilterProxyModel(parent) { currentSorting = Sorting::ByPopularity; sortings.insert(tr("Sort by Popularity"), Sorting::ByPopularity); @@ -62,7 +62,7 @@ void FilterModel::setSearchTerm(const QString term) invalidate(); } -bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const { if (searchTerm.isEmpty()) { return true; @@ -73,20 +73,18 @@ bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParen return pack.name.contains(searchTerm, Qt::CaseInsensitive); } -bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +bool FilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) const { ATLauncher::IndexedPack leftPack = sourceModel()->data(left, Qt::UserRole).value(); ATLauncher::IndexedPack rightPack = sourceModel()->data(right, Qt::UserRole).value(); if (currentSorting == ByPopularity) { return leftPack.position > rightPack.position; - } - else if (currentSorting == ByGameVersion) { + } else if (currentSorting == ByGameVersion) { Version lv(leftPack.versions.at(0).minecraft); Version rv(rightPack.versions.at(0).minecraft); return lv < rv; - } - else if (currentSorting == ByName) { + } else if (currentSorting == ByName) { return StringUtils::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0; } @@ -95,4 +93,4 @@ bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) co return true; } -} +} // namespace Atl diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.h b/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.h index 5235ccdbf..0ab46ed2d 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.h +++ b/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.h @@ -20,10 +20,9 @@ namespace Atl { -class FilterModel : public QSortFilterProxyModel -{ +class FilterModel : public QSortFilterProxyModel { Q_OBJECT -public: + public: FilterModel(QObject* parent = Q_NULLPTR); enum Sorting { ByPopularity, @@ -36,15 +35,14 @@ public: Sorting getCurrentSorting(); void setSearchTerm(QString term); -protected: - bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; - bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; + protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; + bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; -private: + private: QMap sortings; Sorting currentSorting; QString searchTerm; - }; -} +} // namespace Atl diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp index c6b087d67..39f4f346a 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp @@ -20,6 +20,8 @@ #include #include +#include "net/ApiDownload.h" + namespace Atl { ListModel::ListModel(QObject* parent) : QAbstractListModel(parent) {} @@ -75,7 +77,7 @@ void ListModel::request() auto netJob = makeShared("Atl::Request", APPLICATION->network()); auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/json/packsnew.json"); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(url), response)); jobPtr = netJob; jobPtr->start(); @@ -137,8 +139,7 @@ void ListModel::requestFailed(QString reason) void ListModel::getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback) { if (m_logoMap.contains(logo)) { - callback( - APPLICATION->metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath()); + callback(APPLICATION->metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(logo))->getFullPath()); } else { requestLogo(logo, logoUrl); } @@ -168,9 +169,9 @@ void ListModel::requestLogo(QString file, QString url) return; } - MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(file.section(".", 0, 0))); + MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(file)); auto job = new NetJob(QString("ATLauncher Icon Download %1").arg(file), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); QObject::connect(job, &NetJob::succeeded, this, [this, file, fullPath, job] { diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp index 7b61daa71..6fb867733 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -38,15 +38,15 @@ #include #include +#include "Application.h" #include "BuildConfig.h" #include "Json.h" #include "modplatform/atlauncher/ATLShareCode.h" -#include "Application.h" + +#include "net/ApiDownload.h" AtlOptionalModListModel::AtlOptionalModListModel(QWidget* parent, ATLauncher::PackVersion version, QVector mods) - : QAbstractListModel(parent) - , m_version(version) - , m_mods(mods) + : QAbstractListModel(parent), m_version(version), m_mods(mods) { // fill mod index for (int i = 0; i < m_mods.size(); i++) { @@ -62,7 +62,8 @@ AtlOptionalModListModel::AtlOptionalModListModel(QWidget* parent, ATLauncher::Pa } } -QVector AtlOptionalModListModel::getResult() { +QVector AtlOptionalModListModel::getResult() +{ QVector result; for (const auto& mod : m_mods) { @@ -74,16 +75,19 @@ QVector AtlOptionalModListModel::getResult() { return result; } -int AtlOptionalModListModel::rowCount(const QModelIndex &parent) const { +int AtlOptionalModListModel::rowCount(const QModelIndex& parent) const +{ return parent.isValid() ? 0 : m_mods.size(); } -int AtlOptionalModListModel::columnCount(const QModelIndex &parent) const { +int AtlOptionalModListModel::columnCount(const QModelIndex& parent) const +{ // Enabled, Name, Description return parent.isValid() ? 0 : 3; } -QVariant AtlOptionalModListModel::data(const QModelIndex &index, int role) const { +QVariant AtlOptionalModListModel::data(const QModelIndex& index, int role) const +{ auto row = index.row(); auto mod = m_mods.at(row); @@ -94,18 +98,15 @@ QVariant AtlOptionalModListModel::data(const QModelIndex &index, int role) const if (index.column() == DescriptionColumn) { return mod.description; } - } - else if (role == Qt::ToolTipRole) { + } else if (role == Qt::ToolTipRole) { if (index.column() == DescriptionColumn) { return mod.description; } - } - else if (role == Qt::ForegroundRole) { + } else if (role == Qt::ForegroundRole) { if (!mod.colour.isEmpty() && m_version.colours.contains(mod.colour)) { return QColor(QString("#%1").arg(m_version.colours[mod.colour])); } - } - else if (role == Qt::CheckStateRole) { + } else if (role == Qt::CheckStateRole) { if (index.column() == EnabledColumn) { return m_selection[mod.name] ? Qt::Checked : Qt::Unchecked; } @@ -114,7 +115,8 @@ QVariant AtlOptionalModListModel::data(const QModelIndex &index, int role) const return {}; } -bool AtlOptionalModListModel::setData(const QModelIndex &index, const QVariant &value, int role) { +bool AtlOptionalModListModel::setData(const QModelIndex& index, [[maybe_unused]] const QVariant& value, int role) +{ if (role == Qt::CheckStateRole) { auto row = index.row(); auto mod = m_mods.at(row); @@ -126,7 +128,8 @@ bool AtlOptionalModListModel::setData(const QModelIndex &index, const QVariant & return false; } -QVariant AtlOptionalModListModel::headerData(int section, Qt::Orientation orientation, int role) const { +QVariant AtlOptionalModListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case EnabledColumn: @@ -141,7 +144,8 @@ QVariant AtlOptionalModListModel::headerData(int section, Qt::Orientation orient return {}; } -Qt::ItemFlags AtlOptionalModListModel::flags(const QModelIndex &index) const { +Qt::ItemFlags AtlOptionalModListModel::flags(const QModelIndex& index) const +{ auto flags = QAbstractListModel::flags(index); if (index.isValid() && index.column() == EnabledColumn) { flags |= Qt::ItemIsUserCheckable; @@ -149,23 +153,23 @@ Qt::ItemFlags AtlOptionalModListModel::flags(const QModelIndex &index) const { return flags; } -void AtlOptionalModListModel::useShareCode(const QString& code) { +void AtlOptionalModListModel::useShareCode(const QString& code) +{ m_jobPtr.reset(new NetJob("Atl::Request", APPLICATION->network())); auto url = QString(BuildConfig.ATL_API_BASE_URL + "share-codes/" + code); - m_jobPtr->addNetAction(Net::Download::makeByteArray(QUrl(url), m_response)); + m_jobPtr->addNetAction(Net::ApiDownload::makeByteArray(QUrl(url), m_response)); - connect(m_jobPtr.get(), &NetJob::succeeded, - this, &AtlOptionalModListModel::shareCodeSuccess); - connect(m_jobPtr.get(), &NetJob::failed, - this, &AtlOptionalModListModel::shareCodeFailure); + connect(m_jobPtr.get(), &NetJob::succeeded, this, &AtlOptionalModListModel::shareCodeSuccess); + connect(m_jobPtr.get(), &NetJob::failed, this, &AtlOptionalModListModel::shareCodeFailure); m_jobPtr->start(); } -void AtlOptionalModListModel::shareCodeSuccess() { +void AtlOptionalModListModel::shareCodeSuccess() +{ m_jobPtr.reset(); - QJsonParseError parse_error {}; + QJsonParseError parse_error{}; auto doc = QJsonDocument::fromJson(*m_response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from ATL at " << parse_error.offset << " reason: " << parse_error.errorString(); @@ -177,8 +181,7 @@ void AtlOptionalModListModel::shareCodeSuccess() { ATLauncher::ShareCodeResponse response; try { ATLauncher::loadShareCodeResponse(response, obj); - } - catch (const JSONValidationError& e) { + } catch (const JSONValidationError& e) { qDebug() << QString::fromUtf8(*m_response); qWarning() << "Error while reading response from ATLauncher: " << e.cause(); return; @@ -202,44 +205,44 @@ void AtlOptionalModListModel::shareCodeSuccess() { m_selection[mod.name] = mod.selected; } - emit dataChanged(AtlOptionalModListModel::index(0, EnabledColumn), - AtlOptionalModListModel::index(m_mods.size() - 1, EnabledColumn)); + emit dataChanged(AtlOptionalModListModel::index(0, EnabledColumn), AtlOptionalModListModel::index(m_mods.size() - 1, EnabledColumn)); } -void AtlOptionalModListModel::shareCodeFailure(const QString& reason) { +void AtlOptionalModListModel::shareCodeFailure([[maybe_unused]] const QString& reason) +{ m_jobPtr.reset(); // fixme: plumb in an error message } -void AtlOptionalModListModel::selectRecommended() { +void AtlOptionalModListModel::selectRecommended() +{ for (const auto& mod : m_mods) { m_selection[mod.name] = mod.recommended; } - emit dataChanged(AtlOptionalModListModel::index(0, EnabledColumn), - AtlOptionalModListModel::index(m_mods.size() - 1, EnabledColumn)); + emit dataChanged(AtlOptionalModListModel::index(0, EnabledColumn), AtlOptionalModListModel::index(m_mods.size() - 1, EnabledColumn)); } -void AtlOptionalModListModel::clearAll() { +void AtlOptionalModListModel::clearAll() +{ for (const auto& mod : m_mods) { m_selection[mod.name] = false; } - emit dataChanged(AtlOptionalModListModel::index(0, EnabledColumn), - AtlOptionalModListModel::index(m_mods.size() - 1, EnabledColumn)); + emit dataChanged(AtlOptionalModListModel::index(0, EnabledColumn), AtlOptionalModListModel::index(m_mods.size() - 1, EnabledColumn)); } -void AtlOptionalModListModel::toggleMod(ATLauncher::VersionMod mod, int index) { +void AtlOptionalModListModel::toggleMod(ATLauncher::VersionMod mod, int index) +{ auto enable = !m_selection[mod.name]; // If there is a warning for the mod, display that first (if we would be enabling the mod) if (enable && !mod.warning.isEmpty() && m_version.warnings.contains(mod.warning)) { - auto message = QString("%1

    %2") - .arg(m_version.warnings[mod.warning], tr("Are you sure that you want to enable this mod?")); + auto message = QString("%1

    %2").arg(m_version.warnings[mod.warning], tr("Are you sure that you want to enable this mod?")); // fixme: avoid casting here - auto result = QMessageBox::warning((QWidget*) this->parent(), tr("Warning"), message, QMessageBox::Yes | QMessageBox::No); + auto result = QMessageBox::warning((QWidget*)this->parent(), tr("Warning"), message, QMessageBox::Yes | QMessageBox::No); if (result != QMessageBox::Yes) { return; } @@ -248,15 +251,18 @@ void AtlOptionalModListModel::toggleMod(ATLauncher::VersionMod mod, int index) { setMod(mod, index, enable); } -void AtlOptionalModListModel::setMod(ATLauncher::VersionMod mod, int index, bool enable, bool shouldEmit) { - if (m_selection[mod.name] == enable) return; +void AtlOptionalModListModel::setMod(ATLauncher::VersionMod mod, int index, bool enable, bool shouldEmit) +{ + if (m_selection[mod.name] == enable) + return; m_selection[mod.name] = enable; // disable other mods in the group, if applicable if (enable && !mod.group.isEmpty()) { for (int i = 0; i < m_mods.size(); i++) { - if (index == i) continue; + if (index == i) + continue; auto other = m_mods.at(i); if (mod.group == other.group) { @@ -277,16 +283,15 @@ void AtlOptionalModListModel::setMod(ATLauncher::VersionMod mod, int index, bool // if the dependency is 'effectively hidden', then track which mods // depend on it - so we can efficiently disable it when no more dependents // depend on it. - auto dependants = m_dependants[dependencyName]; + auto dependents = m_dependents[dependencyName]; if (enable) { - dependants.append(mod.name); - } - else { - dependants.removeAll(mod.name); + dependents.append(mod.name); + } else { + dependents.removeAll(mod.name); // if there are no longer any dependents, let's disable the mod - if (dependencyMod.effectively_hidden && dependants.isEmpty()) { + if (dependencyMod.effectively_hidden && dependents.isEmpty()) { setMod(dependencyMod, dependencyIndex, false, shouldEmit); } } @@ -294,8 +299,8 @@ void AtlOptionalModListModel::setMod(ATLauncher::VersionMod mod, int index, bool // disable mods that depend on this one, if disabling if (!enable) { - auto dependants = m_dependants[mod.name]; - for (const auto& dependencyName : dependants) { + auto dependents = m_dependents[mod.name]; + for (const auto& dependencyName : dependents) { auto dependencyIndex = m_index[dependencyName]; auto dependencyMod = m_mods.at(dependencyIndex); @@ -304,14 +309,12 @@ void AtlOptionalModListModel::setMod(ATLauncher::VersionMod mod, int index, bool } if (shouldEmit) { - emit dataChanged(AtlOptionalModListModel::index(index, EnabledColumn), - AtlOptionalModListModel::index(index, EnabledColumn)); + emit dataChanged(AtlOptionalModListModel::index(index, EnabledColumn), AtlOptionalModListModel::index(index, EnabledColumn)); } } AtlOptionalModDialog::AtlOptionalModDialog(QWidget* parent, ATLauncher::PackVersion version, QVector mods) - : QDialog(parent) - , ui(new Ui::AtlOptionalModDialog) + : QDialog(parent), ui(new Ui::AtlOptionalModDialog) { ui->setupUi(this); @@ -319,35 +322,24 @@ AtlOptionalModDialog::AtlOptionalModDialog(QWidget* parent, ATLauncher::PackVers ui->treeView->setModel(listModel); ui->treeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - ui->treeView->header()->setSectionResizeMode( - AtlOptionalModListModel::NameColumn, QHeaderView::ResizeToContents); - ui->treeView->header()->setSectionResizeMode( - AtlOptionalModListModel::DescriptionColumn, QHeaderView::Stretch); + ui->treeView->header()->setSectionResizeMode(AtlOptionalModListModel::NameColumn, QHeaderView::ResizeToContents); + ui->treeView->header()->setSectionResizeMode(AtlOptionalModListModel::DescriptionColumn, QHeaderView::Stretch); - connect(ui->shareCodeButton, &QPushButton::clicked, - this, &AtlOptionalModDialog::useShareCode); - connect(ui->selectRecommendedButton, &QPushButton::clicked, - listModel, &AtlOptionalModListModel::selectRecommended); - connect(ui->clearAllButton, &QPushButton::clicked, - listModel, &AtlOptionalModListModel::clearAll); - connect(ui->installButton, &QPushButton::clicked, - this, &QDialog::accept); + connect(ui->shareCodeButton, &QPushButton::clicked, this, &AtlOptionalModDialog::useShareCode); + connect(ui->selectRecommendedButton, &QPushButton::clicked, listModel, &AtlOptionalModListModel::selectRecommended); + connect(ui->clearAllButton, &QPushButton::clicked, listModel, &AtlOptionalModListModel::clearAll); + connect(ui->installButton, &QPushButton::clicked, this, &QDialog::accept); } -AtlOptionalModDialog::~AtlOptionalModDialog() { +AtlOptionalModDialog::~AtlOptionalModDialog() +{ delete ui; } -void AtlOptionalModDialog::useShareCode() { +void AtlOptionalModDialog::useShareCode() +{ bool ok; - auto shareCode = QInputDialog::getText( - this, - tr("Select a share code"), - tr("Share code:"), - QLineEdit::Normal, - "", - &ok - ); + auto shareCode = QInputDialog::getText(this, tr("Select a share code"), tr("Share code:"), QLineEdit::Normal, "", &ok); if (!ok) { // If the user cancels the dialog, we don't need to show any error dialogs. diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h index 639f0d489..55903003b 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h +++ b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -35,8 +35,8 @@ #pragma once -#include #include +#include #include "modplatform/atlauncher/ATLPackIndex.h" #include "net/NetJob.h" @@ -48,37 +48,36 @@ class AtlOptionalModDialog; class AtlOptionalModListModel : public QAbstractListModel { Q_OBJECT -public: - enum Columns - { + public: + enum Columns { EnabledColumn = 0, NameColumn, DescriptionColumn, }; - AtlOptionalModListModel(QWidget *parent, ATLauncher::PackVersion version, QVector mods); + AtlOptionalModListModel(QWidget* parent, ATLauncher::PackVersion version, QVector mods); QVector getResult(); - int rowCount(const QModelIndex &parent) const override; - int columnCount(const QModelIndex &parent) const override; + int rowCount(const QModelIndex& parent) const override; + int columnCount(const QModelIndex& parent) const override; - QVariant data(const QModelIndex &index, int role) const override; - bool setData(const QModelIndex &index, const QVariant &value, int role) override; + QVariant data(const QModelIndex& index, int role) const override; + bool setData(const QModelIndex& index, const QVariant& value, int role) override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - Qt::ItemFlags flags(const QModelIndex &index) const override; + Qt::ItemFlags flags(const QModelIndex& index) const override; void useShareCode(const QString& code); -public slots: + public slots: void shareCodeSuccess(); void shareCodeFailure(const QString& reason); void selectRecommended(); void clearAll(); -private: + private: void toggleMod(ATLauncher::VersionMod mod, int index); void setMod(ATLauncher::VersionMod mod, int index, bool enable, bool shouldEmit = true); @@ -91,24 +90,22 @@ private: QMap m_selection; QMap m_index; - QMap> m_dependants; + QMap> m_dependents; }; class AtlOptionalModDialog : public QDialog { Q_OBJECT -public: - AtlOptionalModDialog(QWidget *parent, ATLauncher::PackVersion version, QVector mods); + public: + AtlOptionalModDialog(QWidget* parent, ATLauncher::PackVersion version, QVector mods); ~AtlOptionalModDialog() override; - QVector getResult() { - return listModel->getResult(); - } + QVector getResult() { return listModel->getResult(); } void useShareCode(); -private: - Ui::AtlOptionalModDialog *ui; + private: + Ui::AtlOptionalModDialog* ui; - AtlOptionalModListModel *listModel; + AtlOptionalModListModel* listModel; }; diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp index 875444451..5e3b9ecf1 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -46,10 +46,7 @@ #include -AtlPage::AtlPage(NewInstanceDialog* dialog, QWidget* parent) - : QWidget(parent) - , ui(new Ui::AtlPage) - , dialog(dialog) +AtlPage::AtlPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), ui(new Ui::AtlPage), dialog(dialog) { ui->setupUi(this); @@ -65,8 +62,7 @@ AtlPage::AtlPage(NewInstanceDialog* dialog, QWidget* parent) ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300); - for(int i = 0; i < filterModel->getAvailableSortings().size(); i++) - { + for (int i = 0; i < filterModel->getAvailableSortings().size(); i++) { ui->sortByBox->addItem(filterModel->getAvailableSortings().keys().at(i)); } ui->sortByBox->setCurrentText(filterModel->translateCurrentSorting()); @@ -94,8 +90,7 @@ void AtlPage::retranslate() void AtlPage::openedImpl() { - if(!initialized) - { + if (!initialized) { listModel->request(); initialized = true; } @@ -105,13 +100,11 @@ void AtlPage::openedImpl() void AtlPage::suggestCurrent() { - if(!isOpened) - { + if (!isOpened) { return; } - if (selectedVersion.isEmpty()) - { + if (selectedVersion.isEmpty()) { dialog->setSuggestedPack(); return; } @@ -121,10 +114,8 @@ void AtlPage::suggestCurrent() auto editedLogoName = selected.safeName; auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(selected.safeName.toLower()); - listModel->getLogo(selected.safeName, url, [this, editedLogoName](QString logo) - { - dialog->setSuggestedIconFromFile(logo, editedLogoName); - }); + listModel->getLogo(selected.safeName, url, + [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); } void AtlPage::triggerSearch() @@ -132,20 +123,18 @@ void AtlPage::triggerSearch() filterModel->setSearchTerm(ui->searchEdit->text()); } -void AtlPage::onSortingSelectionChanged(QString data) +void AtlPage::onSortingSelectionChanged(QString sort) { - auto toSet = filterModel->getAvailableSortings().value(data); + auto toSet = filterModel->getAvailableSortings().value(sort); filterModel->setSorting(toSet); } -void AtlPage::onSelectionChanged(QModelIndex first, QModelIndex second) +void AtlPage::onSelectionChanged(QModelIndex first, [[maybe_unused]] QModelIndex second) { ui->versionSelectionBox->clear(); - if(!first.isValid()) - { - if(isOpened) - { + if (!first.isValid()) { + if (isOpened) { dialog->setSuggestedPack(); } return; @@ -155,21 +144,20 @@ void AtlPage::onSelectionChanged(QModelIndex first, QModelIndex second) ui->packDescription->setHtml(selected.description.replace("\n", "
    ")); - for(const auto& version : selected.versions) { + for (const auto& version : selected.versions) { ui->versionSelectionBox->addItem(version.version); } suggestCurrent(); } -void AtlPage::onVersionSelectionChanged(QString data) +void AtlPage::onVersionSelectionChanged(QString version) { - if(data.isNull() || data.isEmpty()) - { + if (version.isNull() || version.isEmpty()) { selectedVersion = ""; return; } - selectedVersion = data; + selectedVersion = version; suggestCurrent(); } diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.h b/launcher/ui/pages/modplatform/atlauncher/AtlPage.h index 1b3b15c1a..6bc449649 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.h +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -38,52 +38,38 @@ #include "AtlFilterModel.h" #include "AtlListModel.h" -#include #include +#include #include "Application.h" -#include "ui/pages/BasePage.h" #include "tasks/Task.h" +#include "ui/pages/BasePage.h" -namespace Ui -{ - class AtlPage; +namespace Ui { +class AtlPage; } class NewInstanceDialog; -class AtlPage : public QWidget, public BasePage -{ -Q_OBJECT +class AtlPage : public QWidget, public BasePage { + Q_OBJECT -public: - explicit AtlPage(NewInstanceDialog* dialog, QWidget *parent = 0); + public: + explicit AtlPage(NewInstanceDialog* dialog, QWidget* parent = 0); virtual ~AtlPage(); - virtual QString displayName() const override - { - return "ATLauncher"; - } - virtual QIcon icon() const override - { - return APPLICATION->getThemedIcon("atlauncher"); - } - virtual QString id() const override - { - return "atl"; - } - virtual QString helpPage() const override - { - return "ATL-platform"; - } + virtual QString displayName() const override { return "ATLauncher"; } + virtual QIcon icon() const override { return APPLICATION->getThemedIcon("atlauncher"); } + virtual QString id() const override { return "atl"; } + virtual QString helpPage() const override { return "ATL-platform"; } virtual bool shouldDisplay() const override; void retranslate() override; void openedImpl() override; -private: + private: void suggestCurrent(); -private slots: + private slots: void triggerSearch(); void onSortingSelectionChanged(QString data); @@ -91,8 +77,8 @@ private slots: void onSelectionChanged(QModelIndex first, QModelIndex second); void onVersionSelectionChanged(QString data); -private: - Ui::AtlPage *ui = nullptr; + private: + Ui::AtlPage* ui = nullptr; NewInstanceDialog* dialog = nullptr; Atl::ListModel* listModel = nullptr; Atl::FilterModel* filterModel = nullptr; diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp index 3d2d568a1..0c7257859 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -33,17 +33,16 @@ * limitations under the License. */ -#include #include "AtlUserInteractionSupportImpl.h" +#include #include "AtlOptionalModDialog.h" #include "ui/dialogs/VersionSelectDialog.h" -AtlUserInteractionSupportImpl::AtlUserInteractionSupportImpl(QWidget *parent) : m_parent(parent) -{ -} +AtlUserInteractionSupportImpl::AtlUserInteractionSupportImpl(QWidget* parent) : m_parent(parent) {} -std::optional> AtlUserInteractionSupportImpl::chooseOptionalMods(ATLauncher::PackVersion version, QVector mods) +std::optional> AtlUserInteractionSupportImpl::chooseOptionalMods(ATLauncher::PackVersion version, + QVector mods) { AtlOptionalModDialog optionalModDialog(m_parent, version, mods); auto result = optionalModDialog.exec(); @@ -59,8 +58,7 @@ QString AtlUserInteractionSupportImpl::chooseVersion(Meta::VersionList::Ptr vlis if (minecraftVersion != nullptr) { vselect.setExactFilter(BaseVersionList::ParentVersionRole, minecraftVersion); vselect.setEmptyString(tr("No versions are currently available for Minecraft %1").arg(minecraftVersion)); - } - else { + } else { vselect.setEmptyString(tr("No versions are currently available")); } vselect.setEmptyErrorString(tr("Couldn't load or download the version lists!")); @@ -72,9 +70,7 @@ QString AtlUserInteractionSupportImpl::chooseVersion(Meta::VersionList::Ptr vlis // filter by minecraft version, if the loader depends on a certain version. if (minecraftVersion != nullptr) { - auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Meta::Require& req) { - return req.uid == "net.minecraft"; - }); + auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Meta::Require& req) { return req.uid == "net.minecraft"; }); if (iter == reqs.end()) continue; if (iter->equalsVersion != minecraftVersion) diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h b/launcher/ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h index adeb53cbc..52ced2615 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h +++ b/launcher/ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp index fa55aa686..ff21d0109 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp @@ -3,6 +3,8 @@ #include "Application.h" #include "ui/widgets/ProjectItem.h" +#include "net/ApiDownload.h" + #include #include @@ -40,14 +42,16 @@ QVariant ListModel::data(const QModelIndex& index, int role) const return edit; } return pack.description; - } case Qt::DecorationRole: { + } + case 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; - } case Qt::UserRole: { + } + case Qt::UserRole: { QVariant v; v.setValue(pack); return v; @@ -68,7 +72,7 @@ QVariant ListModel::data(const QModelIndex& index, int role) const return QVariant(); } -bool ListModel::setData(const QModelIndex &index, const QVariant &value, int role) +bool ListModel::setData(const QModelIndex& index, const QVariant& value, [[maybe_unused]] int role) { int pos = index.row(); if (pos >= modpacks.size() || pos < 0 || !index.isValid()) @@ -102,9 +106,9 @@ void ListModel::requestLogo(QString logo, QString url) return; } - MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FlamePacks", QString("logos/%1").arg(logo.section(".", 0, 0))); + MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FlamePacks", QString("logos/%1").arg(logo)); auto job = new NetJob(QString("Flame Icon Download %1").arg(logo), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job] { @@ -128,7 +132,7 @@ void ListModel::requestLogo(QString logo, QString url) void ListModel::getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback) { if (m_logoMap.contains(logo)) { - callback(APPLICATION->metacache()->resolveEntry("FlamePacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath()); + callback(APPLICATION->metacache()->resolveEntry("FlamePacks", QString("logos/%1").arg(logo))->getFullPath()); } else { requestLogo(logo, logoUrl); } @@ -139,7 +143,7 @@ Qt::ItemFlags ListModel::flags(const QModelIndex& index) const return QAbstractListModel::flags(index); } -bool ListModel::canFetchMore(const QModelIndex& parent) const +bool ListModel::canFetchMore([[maybe_unused]] const QModelIndex& parent) const { return searchState == CanPossiblyFetchMore; } @@ -171,7 +175,7 @@ void ListModel::performPaginatedSearch() .arg(currentSearchTerm) .arg(currentSort + 1); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response)); jobPtr = netJob; jobPtr->start(); QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::searchRequestFinished); diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index cef26bb6b..183e16f90 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -42,9 +42,11 @@ #include "FlameModel.h" #include "InstanceImportTask.h" #include "Json.h" +#include "modplatform/flame/FlameAPI.h" #include "ui/dialogs/NewInstanceDialog.h" #include "ui/widgets/ProjectItem.h" -#include "modplatform/flame/FlameAPI.h" + +#include "net/ApiDownload.h" static FlameAPI api; @@ -114,7 +116,7 @@ void FlamePage::triggerSearch() listModel->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex()); } -void FlamePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) +void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelIndex prev) { ui->versionSelectionBox->clear(); @@ -132,7 +134,8 @@ void FlamePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) auto netJob = new NetJob(QString("Flame::PackVersions(%1)").arg(current.name), APPLICATION->network()); auto response = std::make_shared(); int addonId = current.addonId; - netJob->addNetAction(Net::Download::makeByteArray(QString("https://api.curseforge.com/v1/mods/%1/files").arg(addonId), response)); + netJob->addNetAction( + Net::ApiDownload::makeByteArray(QString("https://api.curseforge.com/v1/mods/%1/files").arg(addonId), response)); QObject::connect(netJob, &NetJob::succeeded, this, [this, response, addonId, curr] { if (addonId != current.addonId) { @@ -207,17 +210,17 @@ void FlamePage::suggestCurrent() dialog->setSuggestedPack(current.name, new InstanceImportTask(version.downloadUrl, this, std::move(extra_info))); QString editedLogoName; - editedLogoName = "curseforge_" + current.logoName.section(".", 0, 0); + editedLogoName = "curseforge_" + current.logoName; listModel->getLogo(current.logoName, current.logoUrl, [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); } -void FlamePage::onVersionSelectionChanged(QString data) +void FlamePage::onVersionSelectionChanged(QString version) { bool is_blocked = false; ui->versionSelectionBox->currentData().toInt(&is_blocked); - if (data.isNull() || data.isEmpty() || is_blocked) { + if (version.isNull() || version.isEmpty() || is_blocked) { m_selected_version_index = -1; return; } @@ -252,10 +255,8 @@ void FlamePage::updateUi() text += "
    " + tr(" by ") + authorStrs.join(", "); } - if(current.extraInfoLoaded) { - if (!current.extra.issuesUrl.isEmpty() - || !current.extra.sourceUrl.isEmpty() - || !current.extra.wikiUrl.isEmpty()) { + if (current.extraInfoLoaded) { + if (!current.extra.issuesUrl.isEmpty() || !current.extra.sourceUrl.isEmpty() || !current.extra.wikiUrl.isEmpty()) { text += "

    " + tr("External links:") + "
    "; } @@ -267,7 +268,6 @@ void FlamePage::updateUi() text += "- " + tr("Source code: %1").arg(current.extra.sourceUrl) + "
    "; } - text += "
    "; text += api.getModDescription(current.addonId).toUtf8(); diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.h b/launcher/ui/pages/modplatform/flame/FlamePage.h index 8bdca38e8..ff5c79750 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.h +++ b/launcher/ui/pages/modplatform/flame/FlamePage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -37,45 +37,31 @@ #include -#include "ui/pages/BasePage.h" #include -#include "tasks/Task.h" #include +#include "tasks/Task.h" +#include "ui/pages/BasePage.h" -namespace Ui -{ +namespace Ui { class FlamePage; } class NewInstanceDialog; namespace Flame { - class ListModel; +class ListModel; } -class FlamePage : public QWidget, public BasePage -{ +class FlamePage : public QWidget, public BasePage { Q_OBJECT -public: - explicit FlamePage(NewInstanceDialog* dialog, QWidget *parent = 0); + public: + explicit FlamePage(NewInstanceDialog* dialog, QWidget* parent = 0); virtual ~FlamePage(); - virtual QString displayName() const override - { - return "CurseForge"; - } - virtual QIcon icon() const override - { - return APPLICATION->getThemedIcon("flame"); - } - virtual QString id() const override - { - return "flame"; - } - virtual QString helpPage() const override - { - return "Flame-platform"; - } + virtual QString displayName() const override { return "CurseForge"; } + virtual QIcon icon() const override { return APPLICATION->getThemedIcon("flame"); } + virtual QString id() const override { return "flame"; } + virtual QString helpPage() const override { return "Flame-platform"; } virtual bool shouldDisplay() const override; void retranslate() override; @@ -83,18 +69,18 @@ public: void openedImpl() override; - bool eventFilter(QObject * watched, QEvent * event) override; + bool eventFilter(QObject* watched, QEvent* event) override; -private: + private: void suggestCurrent(); -private slots: + private slots: void triggerSearch(); void onSelectionChanged(QModelIndex first, QModelIndex second); void onVersionSelectionChanged(QString data); -private: - Ui::FlamePage *ui = nullptr; + private: + Ui::FlamePage* ui = nullptr; NewInstanceDialog* dialog = nullptr; Flame::ListModel* listModel = nullptr; Flame::IndexedPack current; diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp index 0fb67c507..2b020c48d 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp @@ -32,7 +32,7 @@ void FlameModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonAr auto FlameModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion { return FlameMod::loadDependencyVersions(m, arr); -}; +} auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray { diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index f93e27e64..dc17e705a 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -49,8 +49,7 @@ static bool isOptedOut(ModPlatform::IndexedVersion const& ver) return ver.downloadUrl.isEmpty(); } -FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) - : ModPage(dialog, instance) +FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ModPage(dialog, instance) { m_model = new FlameModModel(instance); m_ui->packView->setModel(m_model); @@ -67,7 +66,9 @@ FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) m_ui->packDescription->setMetaEntry(metaEntryBase()); } -auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders) const -> bool +auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver, + QString mineVer, + std::optional loaders) const -> bool { Q_UNUSED(loaders); return ver.mcVersion.contains(mineVer) && !ver.downloadUrl.isEmpty(); @@ -86,7 +87,7 @@ void FlameModPage::openUrl(const QUrl& url) if (query.startsWith("remoteUrl=")) { // attempt to resolve url from warning page query.remove(0, 10); - ModPage::openUrl({QUrl::fromPercentEncoding(query.toUtf8())}); // double decoding is necessary + ModPage::openUrl({ QUrl::fromPercentEncoding(query.toUtf8()) }); // double decoding is necessary return; } } @@ -125,7 +126,7 @@ void FlameResourcePackPage::openUrl(const QUrl& url) if (query.startsWith("remoteUrl=")) { // attempt to resolve url from warning page query.remove(0, 10); - ResourcePackResourcePage::openUrl({QUrl::fromPercentEncoding(query.toUtf8())}); // double decoding is necessary + ResourcePackResourcePage::openUrl({ QUrl::fromPercentEncoding(query.toUtf8()) }); // double decoding is necessary return; } } @@ -164,7 +165,7 @@ void FlameTexturePackPage::openUrl(const QUrl& url) if (query.startsWith("remoteUrl=")) { // attempt to resolve url from warning page query.remove(0, 10); - ResourcePackResourcePage::openUrl({QUrl::fromPercentEncoding(query.toUtf8())}); // double decoding is necessary + ResourcePackResourcePage::openUrl({ QUrl::fromPercentEncoding(query.toUtf8()) }); // double decoding is necessary return; } } @@ -175,8 +176,17 @@ void FlameTexturePackPage::openUrl(const QUrl& url) // I don't know why, but doing this on the parent class makes it so that // other mod providers start loading before being selected, at least with // my Qt, so we need to implement this in every derived class... -auto FlameModPage::shouldDisplay() const -> bool { return true; } -auto FlameResourcePackPage::shouldDisplay() const -> bool { return true; } -auto FlameTexturePackPage::shouldDisplay() const -> bool { return true; } +auto FlameModPage::shouldDisplay() const -> bool +{ + return true; +} +auto FlameResourcePackPage::shouldDisplay() const -> bool +{ + return true; +} +auto FlameTexturePackPage::shouldDisplay() const -> bool +{ + return true; +} } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h index 103a6bb92..c6ebc1eac 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h @@ -49,12 +49,27 @@ namespace ResourceDownload { namespace Flame { -static inline QString displayName() { return "CurseForge"; } -static inline QIcon icon() { return APPLICATION->getThemedIcon("flame"); } -static inline QString id() { return "curseforge"; } -static inline QString debugName() { return "Flame"; } -static inline QString metaEntryBase() { return "FlameMods"; } +static inline QString displayName() +{ + return "CurseForge"; } +static inline QIcon icon() +{ + return APPLICATION->getThemedIcon("flame"); +} +static inline QString id() +{ + return "curseforge"; +} +static inline QString debugName() +{ + return "Flame"; +} +static inline QString metaEntryBase() +{ + return "FlameMods"; +} +} // namespace Flame class FlameModPage : public ModPage { Q_OBJECT @@ -78,7 +93,9 @@ class FlameModPage : public ModPage { [[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; } - bool validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders = {}) const override; + bool validateVersion(ModPlatform::IndexedVersion& ver, + QString mineVer, + std::optional loaders = {}) const override; bool optedOut(ModPlatform::IndexedVersion& ver) const override; void openUrl(const QUrl& url) override; diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp new file mode 100644 index 000000000..5c9ff63b2 --- /dev/null +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ImportFTBPage.h" +#include "ui_ImportFTBPage.h" + +#include +#include "FileSystem.h" +#include "ListModel.h" +#include "modplatform/import_ftb/PackInstallTask.h" +#include "ui/dialogs/NewInstanceDialog.h" + +namespace FTBImportAPP { + +ImportFTBPage::ImportFTBPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), dialog(dialog), ui(new Ui::ImportFTBPage) +{ + ui->setupUi(this); + + { + listModel = new ListModel(this); + + ui->modpackList->setModel(listModel); + ui->modpackList->setSortingEnabled(true); + ui->modpackList->header()->hide(); + ui->modpackList->setIndentation(0); + ui->modpackList->setIconSize(QSize(42, 42)); + } + + connect(ui->modpackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &ImportFTBPage::onPublicPackSelectionChanged); + + ui->modpackList->selectionModel()->reset(); +} + +ImportFTBPage::~ImportFTBPage() +{ + delete ui; +} + +void ImportFTBPage::openedImpl() +{ + if (!initialized) { + listModel->update(); + initialized = true; + } + suggestCurrent(); +} + +void ImportFTBPage::retranslate() +{ + ui->retranslateUi(this); +} + +void ImportFTBPage::suggestCurrent() +{ + if (!isOpened) + return; + + if (selected.path.isEmpty()) { + dialog->setSuggestedPack(); + return; + } + + dialog->setSuggestedPack(selected.name, new PackInstallTask(selected)); + QString editedLogoName = QString("ftb_%1").arg(selected.id); + dialog->setSuggestedIconFromFile(FS::PathCombine(selected.path, "folder.jpg"), editedLogoName); +} + +void ImportFTBPage::onPublicPackSelectionChanged(QModelIndex now, QModelIndex prev) +{ + if (!now.isValid()) { + onPackSelectionChanged(); + return; + } + Modpack selectedPack = listModel->data(now, Qt::UserRole).value(); + onPackSelectionChanged(&selectedPack); +} + +void ImportFTBPage::onPackSelectionChanged(Modpack* pack) +{ + if (pack) { + selected = *pack; + suggestCurrent(); + return; + } + if (isOpened) + dialog->setSuggestedPack(); +} + +} // namespace FTBImportAPP diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.h b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.h new file mode 100644 index 000000000..54c49f7b7 --- /dev/null +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.h @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include "modplatform/import_ftb/PackHelpers.h" +#include "ui/pages/BasePage.h" +#include "ui/pages/modplatform/import_ftb/ListModel.h" + +class NewInstanceDialog; + +namespace FTBImportAPP { +namespace Ui { +class ImportFTBPage; +} + +class ImportFTBPage : public QWidget, public BasePage { + Q_OBJECT + + public: + explicit ImportFTBPage(NewInstanceDialog* dialog, QWidget* parent = 0); + virtual ~ImportFTBPage(); + QString displayName() const override { return tr("FTB App Import"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("ftb_logo"); } + QString id() const override { return "import_ftb"; } + QString helpPage() const override { return "FTB-platform"; } + bool shouldDisplay() const override { return true; } + void openedImpl() override; + void retranslate() override; + + private: + void suggestCurrent(); + void onPackSelectionChanged(Modpack* pack = nullptr); + private slots: + void onPublicPackSelectionChanged(QModelIndex first, QModelIndex second); + + private: + bool initialized = false; + Modpack selected; + ListModel* listModel = nullptr; + + NewInstanceDialog* dialog = nullptr; + Ui::ImportFTBPage* ui = nullptr; +}; + +} // namespace FTBImportAPP diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui new file mode 100644 index 000000000..32d548b0d --- /dev/null +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui @@ -0,0 +1,28 @@ + + + FTBImportAPP::ImportFTBPage + + + + 0 + 0 + 1461 + 1011 + + + + + + + + 16777215 + 16777215 + + + + + + + + + diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp new file mode 100644 index 000000000..dc78f451c --- /dev/null +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ListModel.h" +#include +#include +#include +#include +#include +#include "FileSystem.h" +#include "modplatform/import_ftb/PackHelpers.h" + +namespace FTBImportAPP { + +QString getPath() +{ + QString partialPath; +#if defined(Q_OS_OSX) + partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support"); +#elif defined(Q_OS_WIN32) + partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); +#else + partialPath = QDir::homePath(); +#endif + return FS::PathCombine(partialPath, ".ftba"); +} + +const QString ListModel::FTB_APP_PATH = getPath(); + +void ListModel::update() +{ + beginResetModel(); + modpacks.clear(); + + QString instancesPath = FS::PathCombine(FTB_APP_PATH, "instances"); + if (auto instancesInfo = QFileInfo(instancesPath); instancesInfo.exists() && instancesInfo.isDir()) { + QDirIterator directoryIterator(instancesPath, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable | QDir::Hidden, + QDirIterator::FollowSymlinks); + while (directoryIterator.hasNext()) { + auto modpack = parseDirectory(directoryIterator.next()); + if (!modpack.path.isEmpty()) + modpacks.append(modpack); + } + } else { + qDebug() << "Couldn't find ftb instances folder: " << instancesPath; + } + + endResetModel(); +} + +QVariant ListModel::data(const QModelIndex& index, int role) const +{ + int pos = index.row(); + if (pos >= modpacks.size() || pos < 0 || !index.isValid()) { + return QVariant(); + } + + auto pack = modpacks.at(pos); + if (role == Qt::DisplayRole) { + return pack.name; + } else if (role == Qt::DecorationRole) { + return pack.icon; + } else if (role == Qt::UserRole) { + QVariant v; + v.setValue(pack); + return v; + } else if (role == Qt::ToolTipRole) { + return tr("Minecraft %1").arg(pack.mcVersion); + } + + return QVariant(); +} +} // namespace FTBImportAPP \ No newline at end of file diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.h b/launcher/ui/pages/modplatform/import_ftb/ListModel.h new file mode 100644 index 000000000..c67aa8963 --- /dev/null +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.h @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include "modplatform/import_ftb/PackHelpers.h" + +namespace FTBImportAPP { + +class ListModel : public QAbstractListModel { + Q_OBJECT + + public: + ListModel(QObject* parent) : QAbstractListModel(parent) {} + virtual ~ListModel() = default; + + int rowCount(const QModelIndex& parent) const { return modpacks.size(); } + int columnCount(const QModelIndex& parent) const { return 1; } + QVariant data(const QModelIndex& index, int role) const; + + void update(); + + static const QString FTB_APP_PATH; + + private: + ModpackList modpacks; +}; +} // namespace FTBImportAPP \ No newline at end of file diff --git a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp index 330dd4fb8..356d919d9 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,6 +35,7 @@ #include "ListModel.h" #include "Application.h" +#include "net/ApiDownload.h" #include "net/HttpMetaCache.h" #include "net/NetJob.h" @@ -76,7 +77,7 @@ bool FilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) co return true; } -bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const +bool FilterModel::filterAcceptsRow([[maybe_unused]] int sourceRow, [[maybe_unused]] const QModelIndex& sourceParent) const { return true; } @@ -173,10 +174,10 @@ QVariant ListModel::data(const QModelIndex& index, int role) const return QVariant(); } -void ListModel::fill(ModpackList modpacks) +void ListModel::fill(ModpackList modpacks_) { beginResetModel(); - this->modpacks = modpacks; + this->modpacks = modpacks_; endResetModel(); } @@ -229,9 +230,9 @@ void ListModel::requestLogo(QString file) return; } - MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FTBPacks", QString("logos/%1").arg(file.section(".", 0, 0))); + MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FTBPacks", QString("logos/%1").arg(file)); NetJob* job = new NetJob(QString("FTB Icon Download for %1").arg(file), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/%1").arg(file)), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/%1").arg(file)), entry)); auto fullPath = entry->getFullPath(); QObject::connect(job, &NetJob::finished, this, [this, file, fullPath, job] { @@ -255,7 +256,7 @@ void ListModel::requestLogo(QString file) void ListModel::getLogo(const QString& logo, LogoCallback callback) { if (m_logoMap.contains(logo)) { - callback(APPLICATION->metacache()->resolveEntry("FTBPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath()); + callback(APPLICATION->metacache()->resolveEntry("FTBPacks", QString("logos/%1").arg(logo))->getFullPath()); } else { requestLogo(logo); } diff --git a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.h b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.h index c55df0009..51a58d991 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.h +++ b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.h @@ -1,13 +1,13 @@ #pragma once -#include #include +#include #include -#include -#include #include +#include #include +#include #include @@ -16,34 +16,28 @@ namespace LegacyFTB { typedef QMap FTBLogoMap; typedef std::function LogoCallback; -class FilterModel : public QSortFilterProxyModel -{ +class FilterModel : public QSortFilterProxyModel { Q_OBJECT -public: + public: FilterModel(QObject* parent = Q_NULLPTR); - enum Sorting { - ByName, - ByGameVersion - }; + enum Sorting { ByName, ByGameVersion }; const QMap getAvailableSortings(); QString translateCurrentSorting(); void setSorting(Sorting sorting); Sorting getCurrentSorting(); -protected: - bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; - bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; + protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; + bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; -private: + private: QMap sortings; Sorting currentSorting; - }; -class ListModel : public QAbstractListModel -{ +class ListModel : public QAbstractListModel { Q_OBJECT -private: + private: ModpackList modpacks; QStringList m_failedLogos; QStringList m_loadingLogos; @@ -53,18 +47,17 @@ private: void requestLogo(QString file); QString translatePackType(PackType type) const; - -private slots: + private slots: void logoFailed(QString logo); void logoLoaded(QString logo, QIcon out); -public: - ListModel(QObject *parent); + public: + ListModel(QObject* parent); ~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; + 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; void fill(ModpackList modpacks); void addPack(Modpack modpack); @@ -72,7 +65,7 @@ public: void remove(int row); Modpack at(int row); - void getLogo(const QString &logo, LogoCallback callback); + void getLogo(const QString& logo, LogoCallback callback); }; -} +} // namespace LegacyFTB diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp index b3f6261fe..0103bbaa2 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu * @@ -44,15 +44,14 @@ #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/NewInstanceDialog.h" +#include "ListModel.h" #include "modplatform/legacy_ftb/PackFetchTask.h" #include "modplatform/legacy_ftb/PackInstallTask.h" #include "modplatform/legacy_ftb/PrivatePackManager.h" -#include "ListModel.h" namespace LegacyFTB { -Page::Page(NewInstanceDialog* dialog, QWidget *parent) - : QWidget(parent), dialog(dialog), ui(new Ui::Page) +Page::Page(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), dialog(dialog), ui(new Ui::Page) { ftbFetchTask.reset(new PackFetchTask(APPLICATION->network())); ftbPrivatePacks.reset(new PrivatePackManager()); @@ -70,8 +69,7 @@ Page::Page(NewInstanceDialog* dialog, QWidget *parent) ui->publicPackList->setIndentation(0); ui->publicPackList->setIconSize(QSize(42, 42)); - for(int i = 0; i < publicFilterModel->getAvailableSortings().size(); i++) - { + for (int i = 0; i < publicFilterModel->getAvailableSortings().size(); i++) { ui->sortByBox->addItem(publicFilterModel->getAvailableSortings().keys().at(i)); } @@ -142,8 +140,7 @@ bool Page::shouldDisplay() const void Page::openedImpl() { - if(!initialized) - { + if (!initialized) { connect(ftbFetchTask.get(), &PackFetchTask::finished, this, &Page::ftbPackDataDownloadSuccessfully); connect(ftbFetchTask.get(), &PackFetchTask::failed, this, &Page::ftbPackDataDownloadFailed); connect(ftbFetchTask.get(), &PackFetchTask::aborted, this, &Page::ftbPackDataDownloadAborted); @@ -166,50 +163,34 @@ void Page::retranslate() void Page::suggestCurrent() { - if(!isOpened) - { + if (!isOpened) { return; } - if(selected.broken || selectedVersion.isEmpty()) - { + if (selected.broken || selectedVersion.isEmpty()) { dialog->setSuggestedPack(); return; } dialog->setSuggestedPack(selected.name, selectedVersion, new PackInstallTask(APPLICATION->network(), selected, selectedVersion)); QString editedLogoName; - if(selected.logo.toLower().startsWith("ftb")) - { + if (selected.logo.toLower().startsWith("ftb")) { editedLogoName = selected.logo; - } - else - { + } else { editedLogoName = "ftb_" + selected.logo; } editedLogoName = editedLogoName.left(editedLogoName.lastIndexOf(".png")); - if(selected.type == PackType::Public) - { - publicListModel->getLogo(selected.logo, [this, editedLogoName](QString logo) - { - dialog->setSuggestedIconFromFile(logo, editedLogoName); - }); - } - else if (selected.type == PackType::ThirdParty) - { - thirdPartyModel->getLogo(selected.logo, [this, editedLogoName](QString logo) - { - dialog->setSuggestedIconFromFile(logo, editedLogoName); - }); - } - else if (selected.type == PackType::Private) - { - privateListModel->getLogo(selected.logo, [this, editedLogoName](QString logo) - { - dialog->setSuggestedIconFromFile(logo, editedLogoName); - }); + if (selected.type == PackType::Public) { + publicListModel->getLogo(selected.logo, + [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); + } else if (selected.type == PackType::ThirdParty) { + thirdPartyModel->getLogo(selected.logo, + [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); + } else if (selected.type == PackType::Private) { + privateListModel->getLogo(selected.logo, + [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); } } @@ -234,23 +215,18 @@ void Page::ftbPrivatePackDataDownloadSuccessfully(Modpack pack) privateListModel->addPack(pack); } -void Page::ftbPrivatePackDataDownloadFailed(QString reason, QString packCode) +void Page::ftbPrivatePackDataDownloadFailed([[maybe_unused]] QString reason, QString packCode) { - auto reply = QMessageBox::question( - this, - tr("FTB private packs"), - tr("Failed to download pack information for code %1.\nShould it be removed now?").arg(packCode) - ); - if(reply == QMessageBox::Yes) - { + auto reply = QMessageBox::question(this, tr("FTB private packs"), + tr("Failed to download pack information for code %1.\nShould it be removed now?").arg(packCode)); + if (reply == QMessageBox::Yes) { ftbPrivatePacks->remove(packCode); } } -void Page::onPublicPackSelectionChanged(QModelIndex now, QModelIndex prev) +void Page::onPublicPackSelectionChanged(QModelIndex now, [[maybe_unused]] QModelIndex prev) { - if(!now.isValid()) - { + if (!now.isValid()) { onPackSelectionChanged(); return; } @@ -258,10 +234,9 @@ void Page::onPublicPackSelectionChanged(QModelIndex now, QModelIndex prev) onPackSelectionChanged(&selectedPack); } -void Page::onThirdPartyPackSelectionChanged(QModelIndex now, QModelIndex prev) +void Page::onThirdPartyPackSelectionChanged(QModelIndex now, [[maybe_unused]] QModelIndex prev) { - if(!now.isValid()) - { + if (!now.isValid()) { onPackSelectionChanged(); return; } @@ -269,10 +244,9 @@ void Page::onThirdPartyPackSelectionChanged(QModelIndex now, QModelIndex prev) onPackSelectionChanged(&selectedPack); } -void Page::onPrivatePackSelectionChanged(QModelIndex now, QModelIndex prev) +void Page::onPrivatePackSelectionChanged(QModelIndex now, [[maybe_unused]] QModelIndex prev) { - if(!now.isValid()) - { + if (!now.isValid()) { onPackSelectionChanged(); return; } @@ -283,34 +257,26 @@ void Page::onPrivatePackSelectionChanged(QModelIndex now, QModelIndex prev) void Page::onPackSelectionChanged(Modpack* pack) { ui->versionSelectionBox->clear(); - if(pack) - { - currentModpackInfo->setHtml("Pack by " + pack->author + "" + - "
    Minecraft " + pack->mcVersion + "
    " + "
    " + pack->description + "
    • " + pack->mods.replace(";", "
    • ") - + "
    "); + if (pack) { + currentModpackInfo->setHtml("Pack by " + pack->author + "" + "
    Minecraft " + pack->mcVersion + "
    " + "
    " + + pack->description + "
    • " + pack->mods.replace(";", "
    • ") + "
    "); bool currentAdded = false; - for(int i = 0; i < pack->oldVersions.size(); i++) - { - if(pack->currentVersion == pack->oldVersions.at(i)) - { + for (int i = 0; i < pack->oldVersions.size(); i++) { + if (pack->currentVersion == pack->oldVersions.at(i)) { currentAdded = true; } ui->versionSelectionBox->addItem(pack->oldVersions.at(i)); } - if(!currentAdded) - { + if (!currentAdded) { ui->versionSelectionBox->addItem(pack->currentVersion); } selected = *pack; - } - else - { + } else { currentModpackInfo->setHtml(""); ui->versionSelectionBox->clear(); - if(isOpened) - { + if (isOpened) { dialog->setSuggestedPack(); } return; @@ -318,21 +284,20 @@ void Page::onPackSelectionChanged(Modpack* pack) suggestCurrent(); } -void Page::onVersionSelectionItemChanged(QString data) +void Page::onVersionSelectionItemChanged(QString version) { - if(data.isNull() || data.isEmpty()) - { + if (version.isNull() || version.isEmpty()) { selectedVersion = ""; return; } - selectedVersion = data; + selectedVersion = version; suggestCurrent(); } -void Page::onSortingSelectionChanged(QString data) +void Page::onSortingSelectionChanged(QString sort) { - FilterModel::Sorting toSet = publicFilterModel->getAvailableSortings().value(data); + FilterModel::Sorting toSet = publicFilterModel->getAvailableSortings().value(sort); publicFilterModel->setSorting(toSet); thirdPartyFilterModel->setSorting(toSet); privateFilterModel->setSorting(toSet); @@ -340,20 +305,15 @@ void Page::onSortingSelectionChanged(QString data) void Page::onTabChanged(int tab) { - if(tab == 1) - { + if (tab == 1) { currentModel = thirdPartyFilterModel; currentList = ui->thirdPartyPackList; currentModpackInfo = ui->thirdPartyPackDescription; - } - else if(tab == 2) - { + } else if (tab == 2) { currentModel = privateFilterModel; currentList = ui->privatePackList; currentModpackInfo = ui->privatePackDescription; - } - else - { + } else { currentModel = publicFilterModel; currentList = ui->publicPackList; currentModpackInfo = ui->publicPackDescription; @@ -361,13 +321,10 @@ void Page::onTabChanged(int tab) currentList->selectionModel()->reset(); QModelIndex idx = currentList->currentIndex(); - if(idx.isValid()) - { + if (idx.isValid()) { auto pack = currentModel->data(idx, Qt::UserRole).value(); onPackSelectionChanged(&pack); - } - else - { + } else { onPackSelectionChanged(); } } @@ -375,38 +332,24 @@ void Page::onTabChanged(int tab) void Page::onAddPackClicked() { bool ok; - QString text = QInputDialog::getText( - this, - tr("Add FTB pack"), - tr("Enter pack code:"), - QLineEdit::Normal, - QString(), - &ok - ); - if(ok && !text.isEmpty()) - { + QString text = QInputDialog::getText(this, tr("Add FTB pack"), tr("Enter pack code:"), QLineEdit::Normal, QString(), &ok); + if (ok && !text.isEmpty()) { ftbPrivatePacks->add(text); - ftbFetchTask->fetchPrivate({text}); + ftbFetchTask->fetchPrivate({ text }); } } void Page::onRemovePackClicked() { auto index = ui->privatePackList->currentIndex(); - if(!index.isValid()) - { + if (!index.isValid()) { return; } auto row = index.row(); Modpack pack = privateListModel->at(row); - auto answer = QMessageBox::question( - this, - tr("Remove pack"), - tr("Are you sure you want to remove pack %1?").arg(pack.name), - QMessageBox::Yes | QMessageBox::No - ); - if(answer != QMessageBox::Yes) - { + auto answer = QMessageBox::question(this, tr("Remove pack"), tr("Are you sure you want to remove pack %1?").arg(pack.name), + QMessageBox::Yes | QMessageBox::No); + if (answer != QMessageBox::Yes) { return; } @@ -415,4 +358,4 @@ void Page::onRemovePackClicked() onPackSelectionChanged(); } -} +} // namespace LegacyFTB diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.h b/launcher/ui/pages/modplatform/legacy_ftb/Page.h index 1de8b40ab..a12b0745d 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.h +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -35,23 +35,22 @@ #pragma once -#include -#include #include +#include +#include -#include "ui/pages/BasePage.h" #include -#include "tasks/Task.h" -#include "modplatform/legacy_ftb/PackHelpers.h" -#include "modplatform/legacy_ftb/PackFetchTask.h" #include "QObjectPtr.h" +#include "modplatform/legacy_ftb/PackFetchTask.h" +#include "modplatform/legacy_ftb/PackHelpers.h" +#include "tasks/Task.h" +#include "ui/pages/BasePage.h" class NewInstanceDialog; namespace LegacyFTB { -namespace Ui -{ +namespace Ui { class Page; } @@ -61,38 +60,25 @@ class PrivatePackListModel; class PrivatePackFilterModel; class PrivatePackManager; -class Page : public QWidget, public BasePage -{ +class Page : public QWidget, public BasePage { Q_OBJECT -public: - explicit Page(NewInstanceDialog * dialog, QWidget *parent = 0); + public: + explicit Page(NewInstanceDialog* dialog, QWidget* parent = 0); virtual ~Page(); - QString displayName() const override - { - return "FTB Legacy"; - } - QIcon icon() const override - { - return APPLICATION->getThemedIcon("ftb_logo"); - } - QString id() const override - { - return "legacy_ftb"; - } - QString helpPage() const override - { - return "FTB-platform"; - } + QString displayName() const override { return "FTB Legacy"; } + QIcon icon() const override { return APPLICATION->getThemedIcon("ftb_logo"); } + QString id() const override { return "legacy_ftb"; } + QString helpPage() const override { return "FTB-platform"; } bool shouldDisplay() const override; void openedImpl() override; void retranslate() override; -private: + private: void suggestCurrent(); - void onPackSelectionChanged(Modpack *pack = nullptr); + void onPackSelectionChanged(Modpack* pack = nullptr); -private slots: + private slots: void ftbPackDataDownloadSuccessfully(ModpackList publicPacks, ModpackList thirdPartyPacks); void ftbPackDataDownloadFailed(QString reason); void ftbPackDataDownloadAborted(); @@ -112,7 +98,7 @@ private slots: void onAddPackClicked(); void onRemovePackClicked(); -private: + private: FilterModel* currentModel = nullptr; QTreeView* currentList = nullptr; QTextBrowser* currentModpackInfo = nullptr; @@ -124,18 +110,18 @@ private: ListModel* publicListModel = nullptr; FilterModel* publicFilterModel = nullptr; - ListModel *thirdPartyModel = nullptr; - FilterModel *thirdPartyFilterModel = nullptr; + ListModel* thirdPartyModel = nullptr; + FilterModel* thirdPartyFilterModel = nullptr; - ListModel *privateListModel = nullptr; - FilterModel *privateFilterModel = nullptr; + ListModel* privateListModel = nullptr; + FilterModel* privateFilterModel = nullptr; unique_qobject_ptr ftbFetchTask; std::unique_ptr ftbPrivatePacks; NewInstanceDialog* dialog = nullptr; - Ui::Page *ui = nullptr; + Ui::Page* ui = nullptr; }; -} +} // namespace LegacyFTB diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index e0046d887..ebc5556c6 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * @@ -42,6 +42,8 @@ #include "minecraft/PackProfile.h" #include "ui/widgets/ProjectItem.h" +#include "net/ApiDownload.h" + #include namespace Modrinth { @@ -115,7 +117,7 @@ auto ModpackListModel::data(const QModelIndex& index, int role) const -> QVarian return {}; } -bool ModpackListModel::setData(const QModelIndex &index, const QVariant &value, int role) +bool ModpackListModel::setData(const QModelIndex& index, const QVariant& value, [[maybe_unused]] int role) { int pos = index.row(); if (pos >= modpacks.size() || pos < 0 || !index.isValid()) @@ -142,7 +144,7 @@ void ModpackListModel::performPaginatedSearch() .arg(currentSearchTerm) .arg(currentSort); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchAllUrl), m_all_response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchAllUrl), m_all_response)); QObject::connect(netJob.get(), &NetJob::succeeded, this, [this] { QJsonParseError parse_error_all{}; @@ -181,26 +183,24 @@ void ModpackListModel::refresh() static auto sortFromIndex(int index) -> QString { - switch(index){ - default: - case 0: - return "relevance"; - case 1: - return "downloads"; - case 2: - return "follows"; - case 3: - return "newest"; - case 4: - return "updated"; + switch (index) { + default: + case 0: + return "relevance"; + case 1: + return "downloads"; + case 2: + return "follows"; + case 3: + return "newest"; + case 4: + return "updated"; } - - return {}; } void ModpackListModel::searchWithTerm(const QString& term, const int sort) { - if(sort > 5 || sort < 0) + if (sort > 5 || sort < 0) return; auto sort_str = sortFromIndex(sort); @@ -218,9 +218,7 @@ void ModpackListModel::searchWithTerm(const QString& term, const int sort) void ModpackListModel::getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback) { if (m_logoMap.contains(logo)) { - callback(APPLICATION->metacache() - ->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo.section(".", 0, 0))) - ->getFullPath()); + callback(APPLICATION->metacache()->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo))->getFullPath()); } else { requestLogo(logo, logoUrl); } @@ -232,10 +230,9 @@ void ModpackListModel::requestLogo(QString logo, QString url) return; } - MetaEntryPtr entry = - APPLICATION->metacache()->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo.section(".", 0, 0))); + MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo)); auto job = new NetJob(QString("%1 Icon Download %2").arg(m_parent->debugName()).arg(logo), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job] { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h index b9e9c3da5..721c69f55 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify @@ -47,7 +47,7 @@ class Version; namespace Modrinth { using LogoMap = QMap; -using LogoCallback = std::function; +using LogoCallback = std::function; class ModpackListModel : public QAbstractListModel { Q_OBJECT @@ -64,7 +64,7 @@ class ModpackListModel : public QAbstractListModel { /* Retrieve information from the model at a given index with the given role */ auto data(const QModelIndex& index, int role) const -> QVariant override; - bool setData(const QModelIndex &index, const QVariant &value, int role) override; + bool setData(const QModelIndex& index, const QVariant& value, int role) override; inline void setActiveJob(NetJob::Ptr ptr) { jobPtr = ptr; } @@ -75,7 +75,10 @@ class ModpackListModel : public QAbstractListModel { void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback); - inline auto canFetchMore(const QModelIndex& parent) const -> bool override { return parent.isValid() ? false : searchState == CanPossiblyFetchMore; }; + inline auto canFetchMore(const QModelIndex& parent) const -> bool override + { + return parent.isValid() ? false : searchState == CanPossiblyFetchMore; + }; public slots: void searchRequestFinished(QJsonDocument& doc_all); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index c71dd9038..41fd5003f 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify @@ -46,6 +46,8 @@ #include "ui/widgets/ProjectItem.h" +#include "net/ApiDownload.h" + #include #include #include @@ -105,7 +107,7 @@ bool ModrinthPage::eventFilter(QObject* watched, QEvent* event) return QObject::eventFilter(watched, event); } -void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev) +void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelIndex prev) { ui->versionSelectionBox->clear(); @@ -127,7 +129,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev) QString id = current.id; - netJob->addNetAction(Net::Download::makeByteArray(QString("%1/project/%2").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QString("%1/project/%2").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); QObject::connect(netJob, &NetJob::succeeded, this, [this, response, id, curr] { if (id != current.id) { @@ -176,7 +178,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev) QString id = current.id; netJob->addNetAction( - Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); + Net::ApiDownload::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); QObject::connect(netJob, &NetJob::succeeded, this, [this, response, id, curr] { if (id != current.id) { @@ -309,9 +311,9 @@ void ModrinthPage::triggerSearch() m_model->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex()); } -void ModrinthPage::onVersionSelectionChanged(QString data) +void ModrinthPage::onVersionSelectionChanged(QString version) { - if (data.isNull() || data.isEmpty()) { + if (version.isNull() || version.isEmpty()) { selectedVersion = ""; return; } diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h index db5e1a3d6..b7054c886 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp index 8aa649898..ce7475b6e 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -45,7 +45,7 @@ void ModrinthModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJso auto ModrinthModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion { return ::Modrinth::loadDependencyVersions(m, arr); -}; +} auto ModrinthModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h index d7c858f88..15cd58544 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index dd143700c..616c1a815 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -46,8 +46,7 @@ namespace ResourceDownload { -ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance& instance) - : ModPage(dialog, instance) +ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ModPage(dialog, instance) { m_model = new ModrinthModModel(instance); m_ui->packView->setModel(m_model); @@ -64,14 +63,15 @@ ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance& instan m_ui->packDescription->setMetaEntry(metaEntryBase()); } -auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders) const -> bool +auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver, + QString mineVer, + std::optional loaders) const -> bool { auto loaderCompatible = !loaders.has_value(); if (!loaderCompatible) { auto loaderStrings = ModrinthAPI::getModLoaderStrings(loaders.value()); - for (auto remoteLoader : ver.loaders) - { + for (auto remoteLoader : ver.loaders) { if (loaderStrings.contains(remoteLoader)) { loaderCompatible = true; break; @@ -139,9 +139,21 @@ ModrinthShaderPackPage::ModrinthShaderPackPage(ShaderPackDownloadDialog* dialog, // I don't know why, but doing this on the parent class makes it so that // other mod providers start loading before being selected, at least with // my Qt, so we need to implement this in every derived class... -auto ModrinthModPage::shouldDisplay() const -> bool { return true; } -auto ModrinthResourcePackPage::shouldDisplay() const -> bool { return true; } -auto ModrinthTexturePackPage::shouldDisplay() const -> bool { return true; } -auto ModrinthShaderPackPage::shouldDisplay() const -> bool { return true; } +auto ModrinthModPage::shouldDisplay() const -> bool +{ + return true; +} +auto ModrinthResourcePackPage::shouldDisplay() const -> bool +{ + return true; +} +auto ModrinthTexturePackPage::shouldDisplay() const -> bool +{ + return true; +} +auto ModrinthShaderPackPage::shouldDisplay() const -> bool +{ + return true; +} } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h index f4eb5ce09..86ba1ccb2 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -43,18 +43,33 @@ #include "ui/pages/modplatform/ModPage.h" #include "ui/pages/modplatform/ResourcePackPage.h" -#include "ui/pages/modplatform/TexturePackPage.h" #include "ui/pages/modplatform/ShaderPackPage.h" +#include "ui/pages/modplatform/TexturePackPage.h" namespace ResourceDownload { namespace Modrinth { -static inline QString displayName() { return "Modrinth"; } -static inline QIcon icon() { return APPLICATION->getThemedIcon("modrinth"); } -static inline QString id() { return "modrinth"; } -static inline QString debugName() { return "Modrinth"; } -static inline QString metaEntryBase() { return "ModrinthPacks"; } +static inline QString displayName() +{ + return "Modrinth"; } +static inline QIcon icon() +{ + return APPLICATION->getThemedIcon("modrinth"); +} +static inline QString id() +{ + return "modrinth"; +} +static inline QString debugName() +{ + return "Modrinth"; +} +static inline QString metaEntryBase() +{ + return "ModrinthPacks"; +} +} // namespace Modrinth class ModrinthModPage : public ModPage { Q_OBJECT @@ -78,7 +93,8 @@ class ModrinthModPage : public ModPage { [[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; } - auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders = {}) const -> bool override; + auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders = {}) const + -> bool override; }; class ModrinthResourcePackPage : public ResourcePackResourcePage { diff --git a/launcher/ui/pages/modplatform/technic/TechnicData.h b/launcher/ui/pages/modplatform/technic/TechnicData.h index cd2ea8e14..fc7fa4d39 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicData.h +++ b/launcher/ui/pages/modplatform/technic/TechnicData.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2021-2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -63,6 +63,6 @@ struct Modpack { QString recommended; QVector versions; }; -} +} // namespace Technic Q_DECLARE_METATYPE(Technic::Modpack) diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp index f08eb2897..e8c5ac922 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2021 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -38,6 +38,8 @@ #include "BuildConfig.h" #include "Json.h" +#include "net/ApiDownload.h" + #include Technic::ListModel::ListModel(QObject* parent) : QAbstractListModel(parent) {} @@ -116,7 +118,7 @@ void Technic::ListModel::performSearch() QString("%1search?build=%2&q=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD, currentSearchTerm); searchMode = List; } - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response)); jobPtr = netJob; jobPtr->start(); QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::searchRequestFinished); @@ -157,7 +159,7 @@ void Technic::ListModel::searchRequestFinished() pack.logoName = "null"; } else { pack.logoUrl = rawURL; - pack.logoName = rawURL.section(QLatin1Char('/'), -1).section(QLatin1Char('.'), 0, 0); + pack.logoName = rawURL.section(QLatin1Char('/'), -1); } pack.broken = false; newList.append(pack); @@ -179,7 +181,7 @@ void Technic::ListModel::searchRequestFinished() auto iconUrl = Json::requireString(iconObj, "url"); pack.logoUrl = iconUrl; - pack.logoName = iconUrl.section(QLatin1Char('/'), -1).section(QLatin1Char('.'), 0, 0); + pack.logoName = iconUrl.section(QLatin1Char('/'), -1); } else { pack.logoUrl = "null"; pack.logoName = "null"; @@ -254,7 +256,7 @@ void Technic::ListModel::requestLogo(QString logo, QString url) MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("TechnicPacks", QString("logos/%1").arg(logo)); auto job = new NetJob(QString("Technic Icon Download %1").arg(logo), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.h b/launcher/ui/pages/modplatform/technic/TechnicModel.h index 0f1a814e2..d7a635d41 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.h +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2021 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp index fc678fa20..54b86feba 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2021-2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -41,16 +41,17 @@ #include "ui/dialogs/NewInstanceDialog.h" #include "BuildConfig.h" +#include "Json.h" #include "TechnicModel.h" #include "modplatform/technic/SingleZipPackInstallTask.h" #include "modplatform/technic/SolderPackInstallTask.h" -#include "Json.h" #include "Application.h" #include "modplatform/technic/SolderPackManifest.h" -TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget *parent) - : QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog) +#include "net/ApiDownload.h" + +TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog) { ui->setupUi(this); connect(ui->searchButton, &QPushButton::clicked, this, &TechnicPage::triggerSearch); @@ -96,18 +97,17 @@ void TechnicPage::openedImpl() triggerSearch(); } -void TechnicPage::triggerSearch() { +void TechnicPage::triggerSearch() +{ model->searchWithTerm(ui->searchEdit->text()); } -void TechnicPage::onSelectionChanged(QModelIndex first, QModelIndex second) +void TechnicPage::onSelectionChanged(QModelIndex first, [[maybe_unused]] QModelIndex second) { ui->versionSelectionBox->clear(); - if(!first.isValid()) - { - if(isOpened) - { + if (!first.isValid()) { + if (isOpened) { dialog->setSuggestedPack(); } return; @@ -119,74 +119,60 @@ void TechnicPage::onSelectionChanged(QModelIndex first, QModelIndex second) void TechnicPage::suggestCurrent() { - if (!isOpened) - { + if (!isOpened) { return; } - if (current.broken) - { + if (current.broken) { dialog->setSuggestedPack(); return; } - QString editedLogoName = "technic_" + current.logoName.section(".", 0, 0); - model->getLogo(current.logoName, current.logoUrl, [this, editedLogoName](QString logo) - { - dialog->setSuggestedIconFromFile(logo, editedLogoName); - }); + QString editedLogoName = "technic_" + current.logoName; + model->getLogo(current.logoName, current.logoUrl, + [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); - if (current.metadataLoaded) - { + if (current.metadataLoaded) { metadataLoaded(); return; } auto netJob = makeShared(QString("Technic::PackMeta(%1)").arg(current.name), APPLICATION->network()); QString slug = current.slug; - netJob->addNetAction(Net::Download::makeByteArray(QString("%1modpack/%2?build=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, slug, BuildConfig.TECHNIC_API_BUILD), response)); - QObject::connect(netJob.get(), &NetJob::succeeded, this, [this, slug] - { + netJob->addNetAction(Net::ApiDownload::makeByteArray( + QString("%1modpack/%2?build=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, slug, BuildConfig.TECHNIC_API_BUILD), response)); + QObject::connect(netJob.get(), &NetJob::succeeded, this, [this, slug] { jobPtr.reset(); - if (current.slug != slug) - { + if (current.slug != slug) { return; } - QJsonParseError parse_error {}; + QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); QJsonObject obj = doc.object(); - if(parse_error.error != QJsonParseError::NoError) - { - qWarning() << "Error while parsing JSON response from Technic at " << parse_error.offset << " reason: " << parse_error.errorString(); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from Technic at " << parse_error.offset + << " reason: " << parse_error.errorString(); qWarning() << *response; return; } - if (!obj.contains("url")) - { + if (!obj.contains("url")) { qWarning() << "Json doesn't contain an url key"; return; } QJsonValueRef url = obj["url"]; - if (url.isString()) - { + if (url.isString()) { current.url = url.toString(); - } - else - { - if (!obj.contains("solder")) - { + } else { + if (!obj.contains("solder")) { qWarning() << "Json doesn't contain a valid url or solder key"; return; } QJsonValueRef solderUrl = obj["solder"]; - if (solderUrl.isString()) - { + if (solderUrl.isString()) { current.url = solderUrl.toString(); current.isSolder = true; - } - else - { + } else { qWarning() << "Json doesn't contain a valid url or solder key"; return; } @@ -227,29 +213,28 @@ void TechnicPage::metadataLoaded() // Strip trailing forward-slashes from Solder URL's if (current.isSolder) { - while (current.url.endsWith('/')) current.url.chop(1); + while (current.url.endsWith('/')) + current.url.chop(1); } // Display versions from Solder if (!current.isSolder) { // If the pack isn't a Solder pack, it only has the single version ui->versionSelectionBox->addItem(current.currentVersion); - } - else if (current.versionsLoaded) { + } else if (current.versionsLoaded) { // reverse foreach, so that the newest versions are first for (auto i = current.versions.size(); i--;) { ui->versionSelectionBox->addItem(current.versions.at(i)); } ui->versionSelectionBox->setCurrentText(current.recommended); - } - else { + } else { // For now, until the versions are pulled from the Solder instance, display the current // version so we can display something quicker ui->versionSelectionBox->addItem(current.currentVersion); auto netJob = makeShared(QString("Technic::SolderMeta(%1)").arg(current.name), APPLICATION->network()); auto url = QString("%1/modpack/%2").arg(current.url, current.slug); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(url), response)); QObject::connect(netJob.get(), &NetJob::succeeded, this, &TechnicPage::onSolderLoaded); @@ -260,7 +245,8 @@ void TechnicPage::metadataLoaded() selectVersion(); } -void TechnicPage::selectVersion() { +void TechnicPage::selectVersion() +{ if (!isOpened) { return; } @@ -269,17 +255,18 @@ void TechnicPage::selectVersion() { return; } - if (!current.isSolder) - { - dialog->setSuggestedPack(current.name, selectedVersion, new Technic::SingleZipPackInstallTask(current.url, current.minecraftVersion)); - } - else - { - dialog->setSuggestedPack(current.name, selectedVersion, new Technic::SolderPackInstallTask(APPLICATION->network(), current.url, current.slug, selectedVersion, current.minecraftVersion)); + if (!current.isSolder) { + dialog->setSuggestedPack(current.name, selectedVersion, + new Technic::SingleZipPackInstallTask(current.url, current.minecraftVersion)); + } else { + dialog->setSuggestedPack(current.name, selectedVersion, + new Technic::SolderPackInstallTask(APPLICATION->network(), current.url, current.slug, selectedVersion, + current.minecraftVersion)); } } -void TechnicPage::onSolderLoaded() { +void TechnicPage::onSolderLoaded() +{ jobPtr.reset(); auto fallback = [this]() { @@ -319,12 +306,13 @@ void TechnicPage::onSolderLoaded() { metadataLoaded(); } -void TechnicPage::onVersionSelectionChanged(QString data) { - if (data.isNull() || data.isEmpty()) { +void TechnicPage::onVersionSelectionChanged(QString version) +{ + if (version.isNull() || version.isEmpty()) { selectedVersion = ""; return; } - selectedVersion = data; + selectedVersion = version; selectVersion(); } diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.h b/launcher/ui/pages/modplatform/technic/TechnicPage.h index 753261b30..91b61eaf2 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.h +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2021-2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify @@ -37,46 +37,32 @@ #include -#include "ui/pages/BasePage.h" #include +#include "TechnicData.h" #include "net/NetJob.h" #include "tasks/Task.h" -#include "TechnicData.h" +#include "ui/pages/BasePage.h" -namespace Ui -{ +namespace Ui { class TechnicPage; } class NewInstanceDialog; namespace Technic { - class ListModel; +class ListModel; } -class TechnicPage : public QWidget, public BasePage -{ +class TechnicPage : public QWidget, public BasePage { Q_OBJECT -public: - explicit TechnicPage(NewInstanceDialog* dialog, QWidget *parent = 0); + public: + explicit TechnicPage(NewInstanceDialog* dialog, QWidget* parent = 0); virtual ~TechnicPage(); - virtual QString displayName() const override - { - return "Technic"; - } - virtual QIcon icon() const override - { - return APPLICATION->getThemedIcon("technic"); - } - virtual QString id() const override - { - return "technic"; - } - virtual QString helpPage() const override - { - return "Technic-platform"; - } + virtual QString displayName() const override { return "Technic"; } + virtual QIcon icon() const override { return APPLICATION->getThemedIcon("technic"); } + virtual QString id() const override { return "technic"; } + virtual QString helpPage() const override { return "Technic-platform"; } virtual bool shouldDisplay() const override; void retranslate() override; @@ -84,19 +70,19 @@ public: bool eventFilter(QObject* watched, QEvent* event) override; -private: + private: void suggestCurrent(); void metadataLoaded(); void selectVersion(); -private slots: + private slots: void triggerSearch(); void onSelectionChanged(QModelIndex first, QModelIndex second); void onSolderLoaded(); void onVersionSelectionChanged(QString data); -private: - Ui::TechnicPage *ui = nullptr; + private: + Ui::TechnicPage* ui = nullptr; NewInstanceDialog* dialog = nullptr; Technic::ListModel* model = nullptr; diff --git a/launcher/ui/setupwizard/BaseWizardPage.h b/launcher/ui/setupwizard/BaseWizardPage.h index 72dbecfd5..80cc64969 100644 --- a/launcher/ui/setupwizard/BaseWizardPage.h +++ b/launcher/ui/setupwizard/BaseWizardPage.h @@ -1,31 +1,21 @@ #pragma once -#include #include +#include -class BaseWizardPage : public QWizardPage -{ -public: - explicit BaseWizardPage(QWidget *parent = Q_NULLPTR) - : QWizardPage(parent) - { - } - virtual ~BaseWizardPage() {}; +class BaseWizardPage : public QWizardPage { + public: + explicit BaseWizardPage(QWidget* parent = Q_NULLPTR) : QWizardPage(parent) {} + virtual ~BaseWizardPage(){}; - virtual bool wantsRefreshButton() - { - return false; - } - virtual void refresh() - { - } + virtual bool wantsRefreshButton() { return false; } + virtual void refresh() {} -protected: + protected: virtual void retranslate() = 0; - void changeEvent(QEvent * event) override + void changeEvent(QEvent* event) override { - if (event->type() == QEvent::LanguageChange) - { + if (event->type() == QEvent::LanguageChange) { retranslate(); } QWizardPage::changeEvent(event); diff --git a/launcher/ui/setupwizard/JavaWizardPage.cpp b/launcher/ui/setupwizard/JavaWizardPage.cpp index 2b70c47c3..e2c444373 100644 --- a/launcher/ui/setupwizard/JavaWizardPage.cpp +++ b/launcher/ui/setupwizard/JavaWizardPage.cpp @@ -1,29 +1,27 @@ #include "JavaWizardPage.h" #include "Application.h" -#include +#include #include -#include #include #include #include +#include #include -#include +#include #include #include "FileSystem.h" +#include "JavaCommon.h" #include "java/JavaInstall.h" #include "java/JavaUtils.h" -#include "JavaCommon.h" -#include "ui/widgets/VersionSelectWidget.h" #include "ui/dialogs/CustomMessageBox.h" #include "ui/widgets/JavaSettingsWidget.h" +#include "ui/widgets/VersionSelectWidget.h" - -JavaWizardPage::JavaWizardPage(QWidget *parent) - :BaseWizardPage(parent) +JavaWizardPage::JavaWizardPage(QWidget* parent) : BaseWizardPage(parent) { setupUi(); } @@ -31,7 +29,7 @@ JavaWizardPage::JavaWizardPage(QWidget *parent) void JavaWizardPage::setupUi() { setObjectName(QStringLiteral("javaPage")); - QVBoxLayout * layout = new QVBoxLayout(this); + QVBoxLayout* layout = new QVBoxLayout(this); m_java_widget = new JavaSettingsWidget(this); layout->addWidget(m_java_widget); @@ -59,30 +57,23 @@ bool JavaWizardPage::validatePage() { auto settings = APPLICATION->settings(); auto result = m_java_widget->validate(); - switch(result) - { + switch (result) { default: - case JavaSettingsWidget::ValidationStatus::Bad: - { + case JavaSettingsWidget::ValidationStatus::Bad: { return false; } - case JavaSettingsWidget::ValidationStatus::AllOK: - { + case JavaSettingsWidget::ValidationStatus::AllOK: { settings->set("JavaPath", m_java_widget->javaPath()); return true; } - case JavaSettingsWidget::ValidationStatus::JavaBad: - { + case JavaSettingsWidget::ValidationStatus::JavaBad: { // Memory auto s = APPLICATION->settings(); s->set("MinMemAlloc", m_java_widget->minHeapSize()); s->set("MaxMemAlloc", m_java_widget->maxHeapSize()); - if (m_java_widget->permGenEnabled()) - { + if (m_java_widget->permGenEnabled()) { s->set("PermGen", m_java_widget->permGenSize()); - } - else - { + } else { s->reset("PermGen"); } return true; @@ -93,7 +84,8 @@ bool JavaWizardPage::validatePage() void JavaWizardPage::retranslate() { setTitle(tr("Java")); - setSubTitle(tr("You do not have a working Java set up yet or it went missing.\n" - "Please select one of the following or browse for a Java executable.")); + setSubTitle( + tr("You do not have a working Java set up yet or it went missing.\n" + "Please select one of the following or browse for a Java executable.")); m_java_widget->retranslate(); } diff --git a/launcher/ui/setupwizard/JavaWizardPage.h b/launcher/ui/setupwizard/JavaWizardPage.h index 0d749039c..6c083dc96 100644 --- a/launcher/ui/setupwizard/JavaWizardPage.h +++ b/launcher/ui/setupwizard/JavaWizardPage.h @@ -4,26 +4,22 @@ class JavaSettingsWidget; -class JavaWizardPage : public BaseWizardPage -{ +class JavaWizardPage : public BaseWizardPage { Q_OBJECT -public: - explicit JavaWizardPage(QWidget *parent = Q_NULLPTR); + public: + explicit JavaWizardPage(QWidget* parent = Q_NULLPTR); - virtual ~JavaWizardPage() - { - }; + virtual ~JavaWizardPage(){}; bool wantsRefreshButton() override; void refresh() override; void initializePage() override; bool validatePage() override; -protected: /* methods */ + protected: /* methods */ void setupUi(); void retranslate() override; -private: /* data */ - JavaSettingsWidget *m_java_widget = nullptr; + private: /* data */ + JavaSettingsWidget* m_java_widget = nullptr; }; - diff --git a/launcher/ui/setupwizard/LanguageWizardPage.cpp b/launcher/ui/setupwizard/LanguageWizardPage.cpp index 6bd19b6ff..09cdb807e 100644 --- a/launcher/ui/setupwizard/LanguageWizardPage.cpp +++ b/launcher/ui/setupwizard/LanguageWizardPage.cpp @@ -2,25 +2,22 @@ #include #include -#include "ui/widgets/LanguageSelectionWidget.h" -#include #include +#include +#include "ui/widgets/LanguageSelectionWidget.h" -LanguageWizardPage::LanguageWizardPage(QWidget *parent) - : BaseWizardPage(parent) +LanguageWizardPage::LanguageWizardPage(QWidget* parent) : BaseWizardPage(parent) { setObjectName(QStringLiteral("languagePage")); auto layout = new QVBoxLayout(this); mainWidget = new LanguageSelectionWidget(this); - layout->setContentsMargins(0,0,0,0); + layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(mainWidget); retranslate(); } -LanguageWizardPage::~LanguageWizardPage() -{ -} +LanguageWizardPage::~LanguageWizardPage() {} bool LanguageWizardPage::wantsRefreshButton() { diff --git a/launcher/ui/setupwizard/LanguageWizardPage.h b/launcher/ui/setupwizard/LanguageWizardPage.h index 45a0e5c04..44a062399 100644 --- a/launcher/ui/setupwizard/LanguageWizardPage.h +++ b/launcher/ui/setupwizard/LanguageWizardPage.h @@ -4,11 +4,10 @@ class LanguageSelectionWidget; -class LanguageWizardPage : public BaseWizardPage -{ +class LanguageWizardPage : public BaseWizardPage { Q_OBJECT -public: - explicit LanguageWizardPage(QWidget *parent = Q_NULLPTR); + public: + explicit LanguageWizardPage(QWidget* parent = Q_NULLPTR); virtual ~LanguageWizardPage(); @@ -18,9 +17,9 @@ public: bool validatePage() override; -protected: + protected: void retranslate() override; -private: - LanguageSelectionWidget *mainWidget = nullptr; + private: + LanguageSelectionWidget* mainWidget = nullptr; }; diff --git a/launcher/ui/setupwizard/PasteWizardPage.cpp b/launcher/ui/setupwizard/PasteWizardPage.cpp index 0f47da4b1..777fd3a44 100644 --- a/launcher/ui/setupwizard/PasteWizardPage.cpp +++ b/launcher/ui/setupwizard/PasteWizardPage.cpp @@ -4,9 +4,7 @@ #include "Application.h" #include "net/PasteUpload.h" -PasteWizardPage::PasteWizardPage(QWidget *parent) : - BaseWizardPage(parent), - ui(new Ui::PasteWizardPage) +PasteWizardPage::PasteWizardPage(QWidget* parent) : BaseWizardPage(parent), ui(new Ui::PasteWizardPage) { ui->setupUi(this); } @@ -16,17 +14,14 @@ PasteWizardPage::~PasteWizardPage() delete ui; } -void PasteWizardPage::initializePage() -{ -} +void PasteWizardPage::initializePage() {} bool PasteWizardPage::validatePage() { auto s = APPLICATION->settings(); QString prevPasteURL = s->get("PastebinURL").toString(); s->reset("PastebinURL"); - if (ui->previousSettingsRadioButton->isChecked()) - { + if (ui->previousSettingsRadioButton->isChecked()) { bool usingDefaultBase = prevPasteURL == PasteUpload::PasteTypes.at(PasteUpload::PasteType::NullPointer).defaultBase; s->set("PastebinType", PasteUpload::PasteType::NullPointer); if (!usingDefaultBase) diff --git a/launcher/ui/setupwizard/PasteWizardPage.h b/launcher/ui/setupwizard/PasteWizardPage.h index 513a14cb5..dece81c42 100644 --- a/launcher/ui/setupwizard/PasteWizardPage.h +++ b/launcher/ui/setupwizard/PasteWizardPage.h @@ -8,20 +8,19 @@ namespace Ui { class PasteWizardPage; } -class PasteWizardPage : public BaseWizardPage -{ +class PasteWizardPage : public BaseWizardPage { Q_OBJECT -public: - explicit PasteWizardPage(QWidget *parent = nullptr); + public: + explicit PasteWizardPage(QWidget* parent = nullptr); ~PasteWizardPage(); void initializePage() override; bool validatePage() override; void retranslate() override; -private: - Ui::PasteWizardPage *ui; + private: + Ui::PasteWizardPage* ui; }; -#endif // PASTEDEFAULTSCONFIRMATIONWIZARD_H +#endif // PASTEDEFAULTSCONFIRMATIONWIZARD_H diff --git a/launcher/ui/setupwizard/SetupWizard.cpp b/launcher/ui/setupwizard/SetupWizard.cpp index 0a47334fc..4e5bd1dca 100644 --- a/launcher/ui/setupwizard/SetupWizard.cpp +++ b/launcher/ui/setupwizard/SetupWizard.cpp @@ -1,16 +1,16 @@ #include "SetupWizard.h" -#include "LanguageWizardPage.h" #include "JavaWizardPage.h" +#include "LanguageWizardPage.h" -#include "translations/TranslationsModel.h" #include #include +#include "translations/TranslationsModel.h" -#include #include +#include -SetupWizard::SetupWizard(QWidget *parent) : QWizard(parent) +SetupWizard::SetupWizard(QWidget* parent) : QWizard(parent) { setObjectName(QStringLiteral("SetupWizard")); resize(620, 660); @@ -33,17 +33,17 @@ void SetupWizard::retranslate() setWindowTitle(tr("%1 Quick Setup").arg(BuildConfig.LAUNCHER_DISPLAYNAME)); } -BaseWizardPage * SetupWizard::getBasePage(int id) +BaseWizardPage* SetupWizard::getBasePage(int id) { - if(id == -1) + if (id == -1) return nullptr; auto pagePtr = page(id); - if(!pagePtr) + if (!pagePtr) return nullptr; - return dynamic_cast(pagePtr); + return dynamic_cast(pagePtr); } -BaseWizardPage * SetupWizard::getCurrentBasePage() +BaseWizardPage* SetupWizard::getCurrentBasePage() { return getBasePage(currentId()); } @@ -51,38 +51,29 @@ BaseWizardPage * SetupWizard::getCurrentBasePage() void SetupWizard::pageChanged(int id) { auto basePagePtr = getBasePage(id); - if(!basePagePtr) - { + if (!basePagePtr) { return; } - if(basePagePtr->wantsRefreshButton()) - { - setButtonLayout({QWizard::CustomButton1, QWizard::Stretch, QWizard::BackButton, QWizard::NextButton, QWizard::FinishButton}); + if (basePagePtr->wantsRefreshButton()) { + setButtonLayout({ QWizard::CustomButton1, QWizard::Stretch, QWizard::BackButton, QWizard::NextButton, QWizard::FinishButton }); auto customButton = button(QWizard::CustomButton1); - connect(customButton, &QAbstractButton::clicked, [&](){ + connect(customButton, &QAbstractButton::clicked, [&]() { auto basePagePtr = getCurrentBasePage(); - if(basePagePtr) - { + if (basePagePtr) { basePagePtr->refresh(); } }); - } - else - { - setButtonLayout({QWizard::Stretch, QWizard::BackButton, QWizard::NextButton, QWizard::FinishButton}); + } else { + setButtonLayout({ QWizard::Stretch, QWizard::BackButton, QWizard::NextButton, QWizard::FinishButton }); } } - -void SetupWizard::changeEvent(QEvent *event) +void SetupWizard::changeEvent(QEvent* event) { - if (event->type() == QEvent::LanguageChange) - { + if (event->type() == QEvent::LanguageChange) { retranslate(); } QWizard::changeEvent(event); } -SetupWizard::~SetupWizard() -{ -} +SetupWizard::~SetupWizard() {} diff --git a/launcher/ui/setupwizard/SetupWizard.h b/launcher/ui/setupwizard/SetupWizard.h index 9b8adb4df..c26c59fdb 100644 --- a/launcher/ui/setupwizard/SetupWizard.h +++ b/launcher/ui/setupwizard/SetupWizard.h @@ -17,29 +17,26 @@ #include -namespace Ui -{ +namespace Ui { class SetupWizard; } class BaseWizardPage; -class SetupWizard : public QWizard -{ +class SetupWizard : public QWizard { Q_OBJECT -public: /* con/destructors */ - explicit SetupWizard(QWidget *parent = 0); + public: /* con/destructors */ + explicit SetupWizard(QWidget* parent = 0); virtual ~SetupWizard(); - void changeEvent(QEvent * event) override; - BaseWizardPage *getBasePage(int id); - BaseWizardPage *getCurrentBasePage(); + void changeEvent(QEvent* event) override; + BaseWizardPage* getBasePage(int id); + BaseWizardPage* getCurrentBasePage(); -private slots: + private slots: void pageChanged(int id); -private: /* methods */ + private: /* methods */ void retranslate(); }; - diff --git a/launcher/ui/setupwizard/ThemeWizardPage.cpp b/launcher/ui/setupwizard/ThemeWizardPage.cpp index 42826aba1..fe11ed9ae 100644 --- a/launcher/ui/setupwizard/ThemeWizardPage.cpp +++ b/launcher/ui/setupwizard/ThemeWizardPage.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Tayou + * Copyright (C) 2022 Tayou * * 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 @@ -61,7 +61,7 @@ void ThemeWizardPage::updateIcons() void ThemeWizardPage::updateCat() { qDebug() << "Setting Cat"; - ui->catImagePreviewButton->setIcon(QIcon(QString(R"(:/backgrounds/%1)").arg(ThemeManager::getCatImage()))); + ui->catImagePreviewButton->setIcon(QIcon(QString(R"(%1)").arg(APPLICATION->themeManager()->getCatPack()))); } void ThemeWizardPage::retranslate() diff --git a/launcher/ui/setupwizard/ThemeWizardPage.h b/launcher/ui/setupwizard/ThemeWizardPage.h index 61a3d0c01..f3d40b6d8 100644 --- a/launcher/ui/setupwizard/ThemeWizardPage.h +++ b/launcher/ui/setupwizard/ThemeWizardPage.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Tayou + * Copyright (C) 2022 Tayou * * 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 diff --git a/launcher/ui/themes/BrightTheme.cpp b/launcher/ui/themes/BrightTheme.cpp index 696ffdfb4..ffccdaab1 100644 --- a/launcher/ui/themes/BrightTheme.cpp +++ b/launcher/ui/themes/BrightTheme.cpp @@ -20,18 +20,18 @@ bool BrightTheme::hasColorScheme() QPalette BrightTheme::colorScheme() { QPalette brightPalette; - brightPalette.setColor(QPalette::Window, QColor(255,255,255)); - brightPalette.setColor(QPalette::WindowText, QColor(17,17,17)); - brightPalette.setColor(QPalette::Base, QColor(250,250,250)); - brightPalette.setColor(QPalette::AlternateBase, QColor(240,240,240)); - brightPalette.setColor(QPalette::ToolTipBase, QColor(17,17,17)); - brightPalette.setColor(QPalette::ToolTipText, QColor(255,255,255)); - brightPalette.setColor(QPalette::Text, Qt::black); - brightPalette.setColor(QPalette::Button, QColor(249,249,249)); + brightPalette.setColor(QPalette::Window, QColor(255, 255, 255)); + brightPalette.setColor(QPalette::WindowText, QColor(17, 17, 17)); + brightPalette.setColor(QPalette::Base, QColor(250, 250, 250)); + brightPalette.setColor(QPalette::AlternateBase, QColor(240, 240, 240)); + brightPalette.setColor(QPalette::ToolTipBase, QColor(17, 17, 17)); + brightPalette.setColor(QPalette::ToolTipText, QColor(255, 255, 255)); + brightPalette.setColor(QPalette::Text, Qt::black); + brightPalette.setColor(QPalette::Button, QColor(249, 249, 249)); brightPalette.setColor(QPalette::ButtonText, Qt::black); brightPalette.setColor(QPalette::BrightText, Qt::red); - brightPalette.setColor(QPalette::Link, QColor(37,137,164)); - brightPalette.setColor(QPalette::Highlight, QColor(137,207,84)); + brightPalette.setColor(QPalette::Link, QColor(37, 137, 164)); + brightPalette.setColor(QPalette::Highlight, QColor(137, 207, 84)); brightPalette.setColor(QPalette::HighlightedText, Qt::black); return fadeInactive(brightPalette, fadeAmount(), fadeColor()); } @@ -43,7 +43,7 @@ double BrightTheme::fadeAmount() QColor BrightTheme::fadeColor() { - return QColor(255,255,255); + return QColor(255, 255, 255); } bool BrightTheme::hasStyleSheet() @@ -55,4 +55,3 @@ QString BrightTheme::appStyleSheet() { return QString(); } - diff --git a/launcher/ui/themes/BrightTheme.h b/launcher/ui/themes/BrightTheme.h index c61f52d58..44a767492 100644 --- a/launcher/ui/themes/BrightTheme.h +++ b/launcher/ui/themes/BrightTheme.h @@ -2,9 +2,8 @@ #include "FusionTheme.h" -class BrightTheme: public FusionTheme -{ -public: +class BrightTheme : public FusionTheme { + public: virtual ~BrightTheme() {} QString id() override; @@ -16,4 +15,3 @@ public: double fadeAmount() override; QColor fadeColor() override; }; - diff --git a/launcher/ui/themes/CatPack.cpp b/launcher/ui/themes/CatPack.cpp new file mode 100644 index 000000000..f0d8ddd55 --- /dev/null +++ b/launcher/ui/themes/CatPack.cpp @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ui/themes/CatPack.h" +#include +#include +#include +#include "FileSystem.h" +#include "Json.h" + +QString BasicCatPack::path() +{ + const auto now = QDate::currentDate(); + const auto birthday = QDate(now.year(), 11, 30); + const auto xmas = QDate(now.year(), 12, 25); + const auto halloween = QDate(now.year(), 10, 31); + + QString cat = QString(":/backgrounds/%1").arg(m_id); + if (std::abs(now.daysTo(xmas)) <= 4) { + cat += "-xmas"; + } else if (std::abs(now.daysTo(halloween)) <= 4) { + cat += "-spooky"; + } else if (std::abs(now.daysTo(birthday)) <= 12) { + cat += "-bday"; + } + return cat; +} + +JsonCatPack::PartialDate partialDate(QJsonObject date) +{ + auto month = Json::ensureInteger(date, "month", 1); + if (month > 12) + month = 12; + else if (month <= 0) + month = 1; + auto day = Json::ensureInteger(date, "day", 1); + if (day > 31) + day = 31; + else if (day <= 0) + day = 1; + return { month, day }; +}; + +JsonCatPack::JsonCatPack(QFileInfo& manifestInfo) : BasicCatPack(manifestInfo.dir().dirName()) +{ + QString path = manifestInfo.path(); + auto doc = Json::requireDocument(manifestInfo.absoluteFilePath(), "CatPack JSON file"); + const auto root = doc.object(); + m_name = Json::requireString(root, "name", "Catpack name"); + m_defaultPath = FS::PathCombine(path, Json::requireString(root, "default", "Default Cat")); + auto variants = Json::ensureArray(root, "variants", QJsonArray(), "Catpack Variants"); + for (auto v : variants) { + auto variant = Json::ensureObject(v, QJsonObject(), "Cat variant"); + m_variants << Variant{ FS::PathCombine(path, Json::requireString(variant, "path", "Variant path")), + partialDate(Json::requireObject(variant, "startTime", "Variant startTime")), + partialDate(Json::requireObject(variant, "endTime", "Variant endTime")) }; + } +} + +QDate ensureDay(int year, int month, int day) +{ + QDate date(year, month, 1); + if (day > date.daysInMonth()) + day = date.daysInMonth(); + return QDate(year, month, day); +} + +QString JsonCatPack::path() +{ + const QDate now = QDate::currentDate(); + for (auto var : m_variants) { + QDate startDate = ensureDay(now.year(), var.startTime.month, var.startTime.day); + QDate endDate = ensureDay(now.year(), var.endTime.month, var.endTime.day); + if (startDate > endDate) { // it's spans over multiple years + if (endDate <= now) // end date is in the past so jump one year into the future for endDate + endDate = endDate.addYears(1); + else // end date is in the future so jump one year into the past for startDate + startDate = startDate.addYears(-1); + } + + if (startDate >= now && now >= endDate) + return var.path; + } + return m_defaultPath; +} diff --git a/launcher/ui/themes/CatPack.h b/launcher/ui/themes/CatPack.h new file mode 100644 index 000000000..fdd117a7f --- /dev/null +++ b/launcher/ui/themes/CatPack.h @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +class CatPack { + public: + virtual ~CatPack() {} + virtual QString id() = 0; + virtual QString name() = 0; + virtual QString path() = 0; +}; + +class BasicCatPack : public CatPack { + public: + BasicCatPack(QString id, QString name) : m_id(id), m_name(name) {} + BasicCatPack(QString id) : BasicCatPack(id, id) {} + virtual QString id() { return m_id; } + virtual QString name() { return m_name; } + virtual QString path(); + + protected: + QString m_id; + QString m_name; +}; + +class FileCatPack : public BasicCatPack { + public: + FileCatPack(QString id, QFileInfo& fileInfo) : BasicCatPack(id), m_path(fileInfo.absoluteFilePath()) {} + FileCatPack(QFileInfo& fileInfo) : FileCatPack(fileInfo.baseName(), fileInfo) {} + virtual QString path() { return m_path; } + + private: + QString m_path; +}; + +class JsonCatPack : public BasicCatPack { + public: + struct PartialDate { + int month; + int day; + }; + struct Variant { + QString path; + PartialDate startTime; + PartialDate endTime; + }; + JsonCatPack(QFileInfo& manifestInfo); + virtual QString path(); + + private: + QString m_defaultPath; + QList m_variants; +}; diff --git a/launcher/ui/themes/CustomTheme.cpp b/launcher/ui/themes/CustomTheme.cpp index 198e76ba1..29ecf6254 100644 --- a/launcher/ui/themes/CustomTheme.cpp +++ b/launcher/ui/themes/CustomTheme.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Tayou + * Copyright (C) 2022 Tayou * * 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 @@ -148,7 +148,7 @@ static bool writeThemeJson(const QString& path, try { Json::write(rootObj, path); return true; - } catch (const Exception& e) { + } catch ([[maybe_unused]] const Exception& e) { themeWarningLog() << "Failed to write theme json to" << path; return false; } @@ -183,7 +183,8 @@ CustomTheme::CustomTheme(ITheme* baseTheme, QFileInfo& fileInfo, bool isManifest return; } - // FIXME: This is kinda jank, it only actually checks if the qss file path is not present. It should actually check for any relevant missing data (e.g. name, colors) + // FIXME: This is kinda jank, it only actually checks if the qss file path is not present. It should actually check for any relevant + // missing data (e.g. name, colors) if (jsonDataIncomplete) { writeThemeJson(fileInfo.absoluteFilePath(), m_palette, m_fadeAmount, m_fadeColor, m_name, m_widgets, m_qssFilePath); } diff --git a/launcher/ui/themes/CustomTheme.h b/launcher/ui/themes/CustomTheme.h index f2b1b06ed..3ec4cafa2 100644 --- a/launcher/ui/themes/CustomTheme.h +++ b/launcher/ui/themes/CustomTheme.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Tayou + * Copyright (C) 2022 Tayou * * 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 diff --git a/launcher/ui/themes/DarkTheme.cpp b/launcher/ui/themes/DarkTheme.cpp index 48231b53e..c3a68a2d4 100644 --- a/launcher/ui/themes/DarkTheme.cpp +++ b/launcher/ui/themes/DarkTheme.cpp @@ -20,21 +20,21 @@ bool DarkTheme::hasColorScheme() QPalette DarkTheme::colorScheme() { QPalette darkPalette; - darkPalette.setColor(QPalette::Window, QColor(49,49,49)); + darkPalette.setColor(QPalette::Window, QColor(49, 49, 49)); darkPalette.setColor(QPalette::WindowText, Qt::white); - darkPalette.setColor(QPalette::Base, QColor(34,34,34)); - darkPalette.setColor(QPalette::AlternateBase, QColor(42,42,42)); + darkPalette.setColor(QPalette::Base, QColor(34, 34, 34)); + darkPalette.setColor(QPalette::AlternateBase, QColor(42, 42, 42)); darkPalette.setColor(QPalette::ToolTipBase, Qt::white); darkPalette.setColor(QPalette::ToolTipText, Qt::white); darkPalette.setColor(QPalette::Text, Qt::white); - darkPalette.setColor(QPalette::Button, QColor(48,48,48)); + darkPalette.setColor(QPalette::Button, QColor(48, 48, 48)); darkPalette.setColor(QPalette::ButtonText, Qt::white); darkPalette.setColor(QPalette::BrightText, Qt::red); - darkPalette.setColor(QPalette::Link, QColor(47,163,198)); - darkPalette.setColor(QPalette::Highlight, QColor(150,219,89)); + darkPalette.setColor(QPalette::Link, QColor(47, 163, 198)); + darkPalette.setColor(QPalette::Highlight, QColor(150, 219, 89)); darkPalette.setColor(QPalette::HighlightedText, Qt::black); darkPalette.setColor(QPalette::PlaceholderText, Qt::darkGray); - return fadeInactive(darkPalette, fadeAmount(), fadeColor()); + return fadeInactive(darkPalette, fadeAmount(), fadeColor()); } double DarkTheme::fadeAmount() @@ -44,7 +44,7 @@ double DarkTheme::fadeAmount() QColor DarkTheme::fadeColor() { - return QColor(49,49,49); + return QColor(49, 49, 49); } bool DarkTheme::hasStyleSheet() diff --git a/launcher/ui/themes/DarkTheme.h b/launcher/ui/themes/DarkTheme.h index 9bd2f3439..431e9a735 100644 --- a/launcher/ui/themes/DarkTheme.h +++ b/launcher/ui/themes/DarkTheme.h @@ -2,9 +2,8 @@ #include "FusionTheme.h" -class DarkTheme: public FusionTheme -{ -public: +class DarkTheme : public FusionTheme { + public: virtual ~DarkTheme() {} QString id() override; diff --git a/launcher/ui/themes/FusionTheme.h b/launcher/ui/themes/FusionTheme.h index ee34245ac..826fae1db 100644 --- a/launcher/ui/themes/FusionTheme.h +++ b/launcher/ui/themes/FusionTheme.h @@ -2,9 +2,8 @@ #include "ITheme.h" -class FusionTheme: public ITheme -{ -public: +class FusionTheme : public ITheme { + public: virtual ~FusionTheme() {} QString qtTheme() override; diff --git a/launcher/ui/themes/ITheme.cpp b/launcher/ui/themes/ITheme.cpp index 8f0757e1a..316b0f2ed 100644 --- a/launcher/ui/themes/ITheme.cpp +++ b/launcher/ui/themes/ITheme.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Tayou + * Copyright (C) 2022 Tayou * * 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 @@ -33,10 +33,10 @@ * limitations under the License. */ #include "ITheme.h" -#include "rainbow.h" -#include #include +#include #include "Application.h" +#include "rainbow.h" void ITheme::apply(bool) { @@ -51,8 +51,7 @@ void ITheme::apply(bool) QPalette ITheme::fadeInactive(QPalette in, qreal bias, QColor color) { - auto blend = [&in, bias, color](QPalette::ColorRole role) - { + auto blend = [&in, bias, color](QPalette::ColorRole role) { QColor from = in.color(QPalette::Active, role); QColor blended = Rainbow::mix(from, color, bias); in.setColor(QPalette::Disabled, role, blended); diff --git a/launcher/ui/themes/ITheme.h b/launcher/ui/themes/ITheme.h index a0a638bd0..d85e7f983 100644 --- a/launcher/ui/themes/ITheme.h +++ b/launcher/ui/themes/ITheme.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Tayou + * Copyright (C) 2022 Tayou * * 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 diff --git a/launcher/ui/themes/IconTheme.cpp b/launcher/ui/themes/IconTheme.cpp new file mode 100644 index 000000000..4bd889854 --- /dev/null +++ b/launcher/ui/themes/IconTheme.cpp @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 TheKodeToad + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "IconTheme.h" + +#include +#include + +IconTheme::IconTheme(const QString& id, const QString& path) : m_id(id), m_path(path) {} + +bool IconTheme::load() +{ + const QString path = m_path + "/index.theme"; + + if (!QFile::exists(path)) + return false; + + QSettings settings(path, QSettings::IniFormat); + settings.beginGroup("Icon Theme"); + m_name = settings.value("Name").toString(); + settings.endGroup(); + return !m_name.isNull(); +} + +QString IconTheme::id() +{ + return m_id; +} + +QString IconTheme::path() +{ + return m_path; +} + +QString IconTheme::name() +{ + return m_name; +} diff --git a/launcher/ui/themes/IconTheme.h b/launcher/ui/themes/IconTheme.h new file mode 100644 index 000000000..4e466c6ae --- /dev/null +++ b/launcher/ui/themes/IconTheme.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 TheKodeToad + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +class IconTheme { + public: + IconTheme(const QString& id, const QString& path); + IconTheme() = default; + + bool load(); + QString id(); + QString path(); + QString name(); + + private: + QString m_id; + QString m_path; + QString m_name; +}; diff --git a/launcher/ui/themes/SystemTheme.cpp b/launcher/ui/themes/SystemTheme.cpp index 3a746d027..7ad144c7a 100644 --- a/launcher/ui/themes/SystemTheme.cpp +++ b/launcher/ui/themes/SystemTheme.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Tayou + * Copyright (C) 2022 Tayou * * 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 @@ -65,7 +65,7 @@ void SystemTheme::apply(bool initial) // See https://github.com/MultiMC/Launcher/issues/1790 // or https://github.com/PrismLauncher/PrismLauncher/issues/490 if (initial) - return; + return; ITheme::apply(initial); } diff --git a/launcher/ui/themes/SystemTheme.h b/launcher/ui/themes/SystemTheme.h index 05f31233e..4f7d83e57 100644 --- a/launcher/ui/themes/SystemTheme.h +++ b/launcher/ui/themes/SystemTheme.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Tayou + * Copyright (C) 2022 Tayou * * 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 diff --git a/launcher/ui/themes/ThemeManager.cpp b/launcher/ui/themes/ThemeManager.cpp index 94ac8a245..0bcac100c 100644 --- a/launcher/ui/themes/ThemeManager.cpp +++ b/launcher/ui/themes/ThemeManager.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Tayou + * Copyright (C) 2022 Tayou + * Copyright (C) 2023 TheKodeToad * * 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 @@ -21,17 +22,20 @@ #include #include #include +#include +#include "Exception.h" #include "ui/themes/BrightTheme.h" +#include "ui/themes/CatPack.h" #include "ui/themes/CustomTheme.h" #include "ui/themes/DarkTheme.h" #include "ui/themes/SystemTheme.h" #include "Application.h" -ThemeManager::ThemeManager(MainWindow* mainWindow) +ThemeManager::ThemeManager() { - m_mainWindow = mainWindow; initializeThemes(); + initializeCatPacks(); } /// @brief Adds the Theme to the list of themes @@ -40,7 +44,10 @@ ThemeManager::ThemeManager(MainWindow* mainWindow) QString ThemeManager::addTheme(std::unique_ptr theme) { QString id = theme->id(); - m_themes.emplace(id, std::move(theme)); + if (m_themes.find(id) == m_themes.end()) + m_themes.emplace(id, std::move(theme)); + else + themeWarningLog() << "Theme(" << id << ") not added to prevent id duplication"; return id; } @@ -52,53 +59,110 @@ ITheme* ThemeManager::getTheme(QString themeId) return m_themes[themeId].get(); } +QString ThemeManager::addIconTheme(IconTheme theme) +{ + QString id = theme.id(); + if (m_icons.find(id) == m_icons.end()) + m_icons.emplace(id, std::move(theme)); + else + themeWarningLog() << "IconTheme(" << id << ") not added to prevent id duplication"; + return id; +} + void ThemeManager::initializeThemes() { // Icon themes - { - // TODO: icon themes and instance icons do not mesh well together. Rearrange and fix discrepancies! - // set icon theme search path! - auto searchPaths = QIcon::themeSearchPaths(); - searchPaths.append("iconthemes"); - QIcon::setThemeSearchPaths(searchPaths); - themeDebugLog() << "<> Icon themes initialized."; - } + initializeIcons(); // Initialize widget themes - { - themeDebugLog() << "<> Initializing Widget Themes"; - themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique()); - auto darkThemeId = addTheme(std::make_unique()); - themeDebugLog() << "Loading Built-in Theme:" << darkThemeId; - themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique()); + initializeWidgets(); +} - // TODO: need some way to differentiate same name themes in different subdirectories (maybe smaller grey text next to theme name in - // dropdown?) - QString themeFolder = QDir("./themes/").absoluteFilePath(""); - themeDebugLog() << "Theme Folder Path: " << themeFolder; +void ThemeManager::initializeIcons() +{ + // TODO: icon themes and instance icons do not mesh well together. Rearrange and fix discrepancies! + // set icon theme search path! + themeDebugLog() << "<> Initializing Icon Themes"; - QDirIterator directoryIterator(themeFolder, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); - while (directoryIterator.hasNext()) { - QDir dir(directoryIterator.next()); - QFileInfo themeJson(dir.absoluteFilePath("theme.json")); - if (themeJson.exists()) { - // Load "theme.json" based themes - themeDebugLog() << "Loading JSON Theme from:" << themeJson.absoluteFilePath(); - addTheme(std::make_unique(getTheme(darkThemeId), themeJson, true)); - } else { - // Load pure QSS Themes - QDirIterator stylesheetFileIterator(dir.absoluteFilePath(""), { "*.qss", "*.css" }, QDir::Files); - while (stylesheetFileIterator.hasNext()) { - QFile customThemeFile(stylesheetFileIterator.next()); - QFileInfo customThemeFileInfo(customThemeFile); - themeDebugLog() << "Loading QSS Theme from:" << customThemeFileInfo.absoluteFilePath(); - addTheme(std::make_unique(getTheme(darkThemeId), customThemeFileInfo, false)); - } - } + auto searchPaths = QIcon::themeSearchPaths(); + searchPaths.append(m_iconThemeFolder.path()); + QIcon::setThemeSearchPaths(searchPaths); + + for (const QString& id : builtinIcons) { + IconTheme theme(id, QString(":/icons/%1").arg(id)); + if (!theme.load()) { + themeWarningLog() << "Couldn't load built-in icon theme" << id; + continue; } - themeDebugLog() << "<> Widget themes initialized."; + addIconTheme(std::move(theme)); + themeDebugLog() << "Loaded Built-In Icon Theme" << id; } + + if (!m_iconThemeFolder.mkpath(".")) + themeWarningLog() << "Couldn't create icon theme folder"; + themeDebugLog() << "Icon Theme Folder Path: " << m_iconThemeFolder.absolutePath(); + + QDirIterator directoryIterator(m_iconThemeFolder.path(), QDir::Dirs | QDir::NoDotAndDotDot); + while (directoryIterator.hasNext()) { + QDir dir(directoryIterator.next()); + IconTheme theme(dir.dirName(), dir.path()); + if (!theme.load()) + continue; + + addIconTheme(std::move(theme)); + themeDebugLog() << "Loaded Custom Icon Theme from" << dir.path(); + } + + themeDebugLog() << "<> Icon themes initialized."; +} + +void ThemeManager::initializeWidgets() +{ + themeDebugLog() << "<> Initializing Widget Themes"; + themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique()); + auto darkThemeId = addTheme(std::make_unique()); + themeDebugLog() << "Loading Built-in Theme:" << darkThemeId; + themeDebugLog() << "Loading Built-in Theme:" << addTheme(std::make_unique()); + + // TODO: need some way to differentiate same name themes in different subdirectories (maybe smaller grey text next to theme name in + // dropdown?) + + if (!m_applicationThemeFolder.mkpath(".")) + themeWarningLog() << "Couldn't create theme folder"; + themeDebugLog() << "Theme Folder Path: " << m_applicationThemeFolder.absolutePath(); + + QDirIterator directoryIterator(m_applicationThemeFolder.path(), QDir::Dirs | QDir::NoDotAndDotDot); + while (directoryIterator.hasNext()) { + QDir dir(directoryIterator.next()); + QFileInfo themeJson(dir.absoluteFilePath("theme.json")); + if (themeJson.exists()) { + // Load "theme.json" based themes + themeDebugLog() << "Loading JSON Theme from:" << themeJson.absoluteFilePath(); + addTheme(std::make_unique(getTheme(darkThemeId), themeJson, true)); + } else { + // Load pure QSS Themes + QDirIterator stylesheetFileIterator(dir.absoluteFilePath(""), { "*.qss", "*.css" }, QDir::Files); + while (stylesheetFileIterator.hasNext()) { + QFile customThemeFile(stylesheetFileIterator.next()); + QFileInfo customThemeFileInfo(customThemeFile); + themeDebugLog() << "Loading QSS Theme from:" << customThemeFileInfo.absoluteFilePath(); + addTheme(std::make_unique(getTheme(darkThemeId), customThemeFileInfo, false)); + } + } + } + + themeDebugLog() << "<> Widget themes initialized."; +} + +QList ThemeManager::getValidIconThemes() +{ + QList ret; + ret.reserve(m_icons.size()); + for (auto&& [id, theme] : m_icons) { + ret.append(&theme); + } + return ret; } QList ThemeManager::getValidApplicationThemes() @@ -111,17 +175,49 @@ QList ThemeManager::getValidApplicationThemes() return ret; } -void ThemeManager::setIconTheme(const QString& name) +QList ThemeManager::getValidCatPacks() { - QIcon::setThemeName(name); + QList ret; + ret.reserve(m_catPacks.size()); + for (auto&& [id, theme] : m_catPacks) { + ret.append(theme.get()); + } + return ret; } -void ThemeManager::applyCurrentlySelectedTheme(bool initial) +bool ThemeManager::isValidIconTheme(const QString& id) { - setIconTheme(APPLICATION->settings()->get("IconTheme").toString()); - themeDebugLog() << "<> Icon theme set."; - setApplicationTheme(APPLICATION->settings()->get("ApplicationTheme").toString(), initial); - themeDebugLog() << "<> Application theme set."; + return !id.isEmpty() && m_icons.find(id) != m_icons.end(); +} + +bool ThemeManager::isValidApplicationTheme(const QString& id) +{ + return !id.isEmpty() && m_themes.find(id) != m_themes.end(); +} + +QDir ThemeManager::getIconThemesFolder() +{ + return m_iconThemeFolder; +} + +QDir ThemeManager::getApplicationThemesFolder() +{ + return m_applicationThemeFolder; +} + +QDir ThemeManager::getCatPacksFolder() +{ + return m_catPacksFolder; +} + +void ThemeManager::setIconTheme(const QString& name) +{ + if (m_icons.find(name) == m_icons.end()) { + themeWarningLog() << "Tried to set invalid icon theme:" << name; + return; + } + + QIcon::setThemeName(name); } void ThemeManager::setApplicationTheme(const QString& name, bool initial) @@ -137,19 +233,83 @@ void ThemeManager::setApplicationTheme(const QString& name, bool initial) } } -QString ThemeManager::getCatImage(QString catName) +void ThemeManager::applyCurrentlySelectedTheme(bool initial) { - QDateTime now = QDateTime::currentDateTime(); - QDateTime birthday(QDate(now.date().year(), 11, 30), QTime(0, 0)); - QDateTime xmas(QDate(now.date().year(), 12, 25), QTime(0, 0)); - QDateTime halloween(QDate(now.date().year(), 10, 31), QTime(0, 0)); - QString cat = !catName.isEmpty() ? catName : APPLICATION->settings()->get("BackgroundCat").toString(); - if (std::abs(now.daysTo(xmas)) <= 4) { - cat += "-xmas"; - } else if (std::abs(now.daysTo(halloween)) <= 4) { - cat += "-spooky"; - } else if (std::abs(now.daysTo(birthday)) <= 12) { - cat += "-bday"; - } - return cat; + auto settings = APPLICATION->settings(); + setIconTheme(settings->get("IconTheme").toString()); + themeDebugLog() << "<> Icon theme set."; + setApplicationTheme(settings->get("ApplicationTheme").toString(), initial); + themeDebugLog() << "<> Application theme set."; +} + +QString ThemeManager::getCatPack(QString catName) +{ + auto catIter = m_catPacks.find(!catName.isEmpty() ? catName : APPLICATION->settings()->get("BackgroundCat").toString()); + if (catIter != m_catPacks.end()) { + auto& catPack = catIter->second; + themeDebugLog() << "applying catpack" << catPack->id(); + return catPack->path(); + } else { + themeWarningLog() << "Tried to get invalid catPack:" << catName; + } + + return m_catPacks.begin()->second->path(); +} + +QString ThemeManager::addCatPack(std::unique_ptr catPack) +{ + QString id = catPack->id(); + if (m_catPacks.find(id) == m_catPacks.end()) + m_catPacks.emplace(id, std::move(catPack)); + else + themeWarningLog() << "CatPack(" << id << ") not added to prevent id duplication"; + return id; +} + +void ThemeManager::initializeCatPacks() +{ + QList> defaultCats{ { "kitteh", QObject::tr("Background Cat (from MultiMC)") }, + { "rory", QObject::tr("Rory ID 11 (drawn by Ashtaka)") }, + { "rory-flat", QObject::tr("Rory ID 11 (flat edition, drawn by Ashtaka)") }, + { "teawie", QObject::tr("Teawie (drawn by SympathyTea)") } }; + for (auto [id, name] : defaultCats) { + addCatPack(std::unique_ptr(new BasicCatPack(id, name))); + } + if (!m_catPacksFolder.mkpath(".")) + themeWarningLog() << "Couldn't create catpacks folder"; + themeDebugLog() << "CatPacks Folder Path:" << m_catPacksFolder.absolutePath(); + + QStringList supportedImageFormats; + for (auto format : QImageReader::supportedImageFormats()) { + supportedImageFormats.append("*." + format); + } + auto loadFiles = [this, supportedImageFormats](QDir dir) { + // Load image files directly + QDirIterator ImageFileIterator(dir.absoluteFilePath(""), supportedImageFormats, QDir::Files); + while (ImageFileIterator.hasNext()) { + QFile customCatFile(ImageFileIterator.next()); + QFileInfo customCatFileInfo(customCatFile); + themeDebugLog() << "Loading CatPack from:" << customCatFileInfo.absoluteFilePath(); + addCatPack(std::unique_ptr(new FileCatPack(customCatFileInfo))); + } + }; + + loadFiles(m_catPacksFolder); + + QDirIterator directoryIterator(m_catPacksFolder.path(), QDir::Dirs | QDir::NoDotAndDotDot); + while (directoryIterator.hasNext()) { + QDir dir(directoryIterator.next()); + QFileInfo manifest(dir.absoluteFilePath("catpack.json")); + if (manifest.isFile()) { + try { + // Load background manifest + themeDebugLog() << "Loading background manifest from:" << manifest.absoluteFilePath(); + addCatPack(std::unique_ptr(new JsonCatPack(manifest))); + } catch (const Exception& e) { + themeWarningLog() << "Couldn't load catpack json:" << e.cause(); + } + } else { + loadFiles(dir); + } + } } diff --git a/launcher/ui/themes/ThemeManager.h b/launcher/ui/themes/ThemeManager.h index 87f36d9c1..b5c66677b 100644 --- a/launcher/ui/themes/ThemeManager.h +++ b/launcher/ui/themes/ThemeManager.h @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Tayou + * Copyright (C) 2022 Tayou + * Copyright (C) 2023 TheKodeToad * * 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 @@ -19,7 +20,9 @@ #include +#include "IconTheme.h" #include "ui/MainWindow.h" +#include "ui/themes/CatPack.h" #include "ui/themes/ITheme.h" inline auto themeDebugLog() @@ -33,25 +36,42 @@ inline auto themeWarningLog() class ThemeManager { public: - ThemeManager(MainWindow* mainWindow); + ThemeManager(); + QList getValidIconThemes(); QList getValidApplicationThemes(); - void setIconTheme(const QString& name); + bool isValidIconTheme(const QString& id); + bool isValidApplicationTheme(const QString& id); + QDir getIconThemesFolder(); + QDir getApplicationThemesFolder(); + QDir getCatPacksFolder(); void applyCurrentlySelectedTheme(bool initial = false); + void setIconTheme(const QString& name); void setApplicationTheme(const QString& name, bool initial = false); - /// - /// Returns the cat based on selected cat and with events (Birthday, XMas, etc.) - /// - /// Optional, if you need a specific cat. - /// - static QString getCatImage(QString catName = ""); + /// @brief Returns the background based on selected and with events (Birthday, XMas, etc.) + /// @param catName Optional, if you need a specific background. + /// @return + QString getCatPack(QString catName = ""); + QList getValidCatPacks(); private: std::map> m_themes; - MainWindow* m_mainWindow; + std::map m_icons; + QDir m_iconThemeFolder{ "iconthemes" }; + QDir m_applicationThemeFolder{ "themes" }; + QDir m_catPacksFolder{ "catpacks" }; + std::map> m_catPacks; void initializeThemes(); + void initializeCatPacks(); QString addTheme(std::unique_ptr theme); ITheme* getTheme(QString themeId); + QString addIconTheme(IconTheme theme); + QString addCatPack(std::unique_ptr catPack); + void initializeIcons(); + void initializeWidgets(); + + const QStringList builtinIcons{ "pe_colored", "pe_light", "pe_dark", "pe_blue", "breeze_light", "breeze_dark", + "OSX", "iOS", "flat", "flat_white", "multimc" }; }; diff --git a/launcher/ui/widgets/CustomCommands.cpp b/launcher/ui/widgets/CustomCommands.cpp index 5ab903950..9b98d7409 100644 --- a/launcher/ui/widgets/CustomCommands.cpp +++ b/launcher/ui/widgets/CustomCommands.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -41,9 +41,7 @@ CustomCommands::~CustomCommands() delete ui; } -CustomCommands::CustomCommands(QWidget* parent): - QWidget(parent), - ui(new Ui::CustomCommands) +CustomCommands::CustomCommands(QWidget* parent) : QWidget(parent), ui(new Ui::CustomCommands) { ui->setupUi(this); } @@ -51,8 +49,7 @@ CustomCommands::CustomCommands(QWidget* parent): void CustomCommands::initialize(bool checkable, bool checked, const QString& prelaunch, const QString& wrapper, const QString& postexit) { ui->customCommandsGroupBox->setCheckable(checkable); - if(checkable) - { + if (checkable) { ui->customCommandsGroupBox->setChecked(checked); } ui->preLaunchCmdTextBox->setText(prelaunch); @@ -60,14 +57,14 @@ void CustomCommands::initialize(bool checkable, bool checked, const QString& pre ui->postExitCmdTextBox->setText(postexit); } - -void CustomCommands::retranslate() { +void CustomCommands::retranslate() +{ ui->retranslateUi(this); } bool CustomCommands::checked() const { - if(!ui->customCommandsGroupBox->isCheckable()) + if (!ui->customCommandsGroupBox->isCheckable()) return true; return ui->customCommandsGroupBox->isChecked(); } diff --git a/launcher/ui/widgets/CustomCommands.h b/launcher/ui/widgets/CustomCommands.h index ed10ba956..5b410ae9a 100644 --- a/launcher/ui/widgets/CustomCommands.h +++ b/launcher/ui/widgets/CustomCommands.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -37,19 +37,17 @@ #include -namespace Ui -{ +namespace Ui { class CustomCommands; } -class CustomCommands : public QWidget -{ +class CustomCommands : public QWidget { Q_OBJECT -public: - explicit CustomCommands(QWidget *parent = 0); + public: + explicit CustomCommands(QWidget* parent = 0); virtual ~CustomCommands(); - void initialize(bool checkable, bool checked, const QString & prelaunch, const QString & wrapper, const QString & postexit); + void initialize(bool checkable, bool checked, const QString& prelaunch, const QString& wrapper, const QString& postexit); void retranslate(); bool checked() const; @@ -57,8 +55,6 @@ public: QString wrapperCommand() const; QString postexitCommand() const; -private: - Ui::CustomCommands *ui; + private: + Ui::CustomCommands* ui; }; - - diff --git a/launcher/ui/widgets/DropLabel.cpp b/launcher/ui/widgets/DropLabel.cpp index a900e57cf..b1473b358 100644 --- a/launcher/ui/widgets/DropLabel.cpp +++ b/launcher/ui/widgets/DropLabel.cpp @@ -1,34 +1,33 @@ #include "DropLabel.h" -#include #include +#include -DropLabel::DropLabel(QWidget *parent) : QLabel(parent) +DropLabel::DropLabel(QWidget* parent) : QLabel(parent) { setAcceptDrops(true); } -void DropLabel::dragEnterEvent(QDragEnterEvent *event) +void DropLabel::dragEnterEvent(QDragEnterEvent* event) { event->acceptProposedAction(); } -void DropLabel::dragMoveEvent(QDragMoveEvent *event) +void DropLabel::dragMoveEvent(QDragMoveEvent* event) { event->acceptProposedAction(); } -void DropLabel::dragLeaveEvent(QDragLeaveEvent *event) +void DropLabel::dragLeaveEvent(QDragLeaveEvent* event) { event->accept(); } -void DropLabel::dropEvent(QDropEvent *event) +void DropLabel::dropEvent(QDropEvent* event) { - const QMimeData *mimeData = event->mimeData(); + const QMimeData* mimeData = event->mimeData(); - if (!mimeData) - { + if (!mimeData) { return; } diff --git a/launcher/ui/widgets/DropLabel.h b/launcher/ui/widgets/DropLabel.h index c5ca0bcca..0027f48b1 100644 --- a/launcher/ui/widgets/DropLabel.h +++ b/launcher/ui/widgets/DropLabel.h @@ -2,19 +2,18 @@ #include -class DropLabel : public QLabel -{ +class DropLabel : public QLabel { Q_OBJECT -public: - explicit DropLabel(QWidget *parent = nullptr); + public: + explicit DropLabel(QWidget* parent = nullptr); -signals: + signals: void droppedURLs(QList urls); -protected: - void dropEvent(QDropEvent *event) override; - void dragEnterEvent(QDragEnterEvent *event) override; - void dragMoveEvent(QDragMoveEvent *event) override; - void dragLeaveEvent(QDragLeaveEvent *event) override; + protected: + void dropEvent(QDropEvent* event) override; + void dragEnterEvent(QDragEnterEvent* event) override; + void dragMoveEvent(QDragMoveEvent* event) override; + void dragLeaveEvent(QDragLeaveEvent* event) override; }; diff --git a/launcher/ui/widgets/ErrorFrame.cpp b/launcher/ui/widgets/ErrorFrame.cpp index b3e410361..213c26b76 100644 --- a/launcher/ui/widgets/ErrorFrame.cpp +++ b/launcher/ui/widgets/ErrorFrame.cpp @@ -27,9 +27,7 @@ void ErrorFrame::clear() setDescription(QString()); } -ErrorFrame::ErrorFrame(QWidget *parent) : - QFrame(parent), - ui(new Ui::ErrorFrame) +ErrorFrame::ErrorFrame(QWidget* parent) : QFrame(parent), ui(new Ui::ErrorFrame) { ui->setupUi(this); ui->label_Description->setHidden(true); @@ -44,24 +42,18 @@ ErrorFrame::~ErrorFrame() void ErrorFrame::updateHiddenState() { - if(ui->label_Description->isHidden() && ui->label_Title->isHidden()) - { + if (ui->label_Description->isHidden() && ui->label_Title->isHidden()) { setHidden(true); - } - else - { + } else { setHidden(false); } } void ErrorFrame::setTitle(QString text) { - if(text.isEmpty()) - { + if (text.isEmpty()) { ui->label_Title->setHidden(true); - } - else - { + } else { ui->label_Title->setText(text); ui->label_Title->setHidden(false); } @@ -70,14 +62,11 @@ void ErrorFrame::setTitle(QString text) void ErrorFrame::setDescription(QString text) { - if(text.isEmpty()) - { + if (text.isEmpty()) { ui->label_Description->setHidden(true); updateHiddenState(); return; - } - else - { + } else { ui->label_Description->setHidden(false); updateHiddenState(); } @@ -87,9 +76,8 @@ void ErrorFrame::setDescription(QString text) QChar rem('\n'); QString finaltext; finaltext.reserve(intermediatetext.size()); - foreach(const QChar& c, intermediatetext) - { - if(c == rem && prev){ + foreach (const QChar& c, intermediatetext) { + if (c == rem && prev) { continue; } prev = c == rem; @@ -97,33 +85,27 @@ void ErrorFrame::setDescription(QString text) } QString labeltext; labeltext.reserve(300); - if(finaltext.length() > 290) - { + if (finaltext.length() > 290) { ui->label_Description->setOpenExternalLinks(false); ui->label_Description->setTextFormat(Qt::TextFormat::RichText); desc = text; // This allows injecting HTML here. labeltext.append("" + finaltext.left(287) + "..."); QObject::connect(ui->label_Description, &QLabel::linkActivated, this, &ErrorFrame::ellipsisHandler); - } - else - { + } else { ui->label_Description->setTextFormat(Qt::TextFormat::PlainText); labeltext.append(finaltext); } ui->label_Description->setText(labeltext); } -void ErrorFrame::ellipsisHandler(const QString &link) +void ErrorFrame::ellipsisHandler(const QString& link) { - if(!currentBox) - { + if (!currentBox) { currentBox = CustomMessageBox::selectable(this, QString(), desc); connect(currentBox, &QMessageBox::finished, this, &ErrorFrame::boxClosed); currentBox->show(); - } - else - { + } else { currentBox->setText(desc); } } diff --git a/launcher/ui/widgets/ErrorFrame.h b/launcher/ui/widgets/ErrorFrame.h index d5069a14b..1aea6a1d8 100644 --- a/launcher/ui/widgets/ErrorFrame.h +++ b/launcher/ui/widgets/ErrorFrame.h @@ -17,17 +17,15 @@ #include -namespace Ui -{ +namespace Ui { class ErrorFrame; } -class ErrorFrame : public QFrame -{ +class ErrorFrame : public QFrame { Q_OBJECT -public: - explicit ErrorFrame(QWidget *parent = 0); + public: + explicit ErrorFrame(QWidget* parent = 0); ~ErrorFrame(); void setTitle(QString text); @@ -35,15 +33,15 @@ public: void clear(); -public slots: - void ellipsisHandler(const QString& link ); + public slots: + void ellipsisHandler(const QString& link); void boxClosed(int result); -private: + private: void updateHiddenState(); -private: - Ui::ErrorFrame *ui; + private: + Ui::ErrorFrame* ui; QString desc; - class QMessageBox * currentBox = nullptr; + class QMessageBox* currentBox = nullptr; }; diff --git a/launcher/ui/widgets/FocusLineEdit.cpp b/launcher/ui/widgets/FocusLineEdit.cpp index b272100c0..6570227bb 100644 --- a/launcher/ui/widgets/FocusLineEdit.cpp +++ b/launcher/ui/widgets/FocusLineEdit.cpp @@ -1,23 +1,22 @@ #include "FocusLineEdit.h" #include -FocusLineEdit::FocusLineEdit(QWidget *parent) : QLineEdit(parent) +FocusLineEdit::FocusLineEdit(QWidget* parent) : QLineEdit(parent) { _selectOnMousePress = false; } -void FocusLineEdit::focusInEvent(QFocusEvent *e) +void FocusLineEdit::focusInEvent(QFocusEvent* e) { QLineEdit::focusInEvent(e); selectAll(); _selectOnMousePress = true; } -void FocusLineEdit::mousePressEvent(QMouseEvent *me) +void FocusLineEdit::mousePressEvent(QMouseEvent* me) { QLineEdit::mousePressEvent(me); - if (_selectOnMousePress) - { + if (_selectOnMousePress) { selectAll(); _selectOnMousePress = false; } diff --git a/launcher/ui/widgets/FocusLineEdit.h b/launcher/ui/widgets/FocusLineEdit.h index 71b4f140a..f5ea6602e 100644 --- a/launcher/ui/widgets/FocusLineEdit.h +++ b/launcher/ui/widgets/FocusLineEdit.h @@ -1,17 +1,14 @@ #include -class FocusLineEdit : public QLineEdit -{ +class FocusLineEdit : public QLineEdit { Q_OBJECT -public: - FocusLineEdit(QWidget *parent); - virtual ~FocusLineEdit() - { - } + public: + FocusLineEdit(QWidget* parent); + virtual ~FocusLineEdit() {} -protected: - void focusInEvent(QFocusEvent *e); - void mousePressEvent(QMouseEvent *me); + protected: + void focusInEvent(QFocusEvent* e); + void mousePressEvent(QMouseEvent* me); bool _selectOnMousePress; }; diff --git a/launcher/ui/widgets/IconLabel.cpp b/launcher/ui/widgets/IconLabel.cpp index bf1c23584..28776686b 100644 --- a/launcher/ui/widgets/IconLabel.cpp +++ b/launcher/ui/widgets/IconLabel.cpp @@ -1,13 +1,12 @@ #include "IconLabel.h" -#include -#include #include #include #include +#include +#include -IconLabel::IconLabel(QWidget *parent, QIcon icon, QSize size) - : QWidget(parent), m_size(size), m_icon(icon) +IconLabel::IconLabel(QWidget* parent, QIcon icon, QSize size) : QWidget(parent), m_size(size), m_icon(icon) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); } @@ -23,19 +22,16 @@ void IconLabel::setIcon(QIcon icon) update(); } -void IconLabel::paintEvent(QPaintEvent *) +void IconLabel::paintEvent(QPaintEvent*) { QPainter p(this); QRect rect = contentsRect(); int width = rect.width(); int height = rect.height(); - if(width < height) - { + if (width < height) { rect.setHeight(width); rect.translate(0, (height - width) / 2); - } - else if (width > height) - { + } else if (width > height) { rect.setWidth(height); rect.translate((width - height) / 2, 0); } diff --git a/launcher/ui/widgets/IconLabel.h b/launcher/ui/widgets/IconLabel.h index 6d212c4ca..41d97f694 100644 --- a/launcher/ui/widgets/IconLabel.h +++ b/launcher/ui/widgets/IconLabel.h @@ -1,26 +1,25 @@ #pragma once -#include #include +#include class QStyleOption; /** * This is a trivial widget that paints a QIcon of the specified size. */ -class IconLabel : public QWidget -{ +class IconLabel : public QWidget { Q_OBJECT -public: + public: /// Create a line separator. orientation is the orientation of the line. - explicit IconLabel(QWidget *parent, QIcon icon, QSize size); + explicit IconLabel(QWidget* parent, QIcon icon, QSize size); virtual QSize sizeHint() const; - virtual void paintEvent(QPaintEvent *); + virtual void paintEvent(QPaintEvent*); void setIcon(QIcon icon); -private: + private: QSize m_size; QIcon m_icon; }; diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index a0fda952f..1f03f9eaf 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -348,7 +348,7 @@ void InfoFrame::setImage(QPixmap img) } } -void InfoFrame::descriptionEllipsisHandler(QString link) +void InfoFrame::descriptionEllipsisHandler([[maybe_unused]] QString link) { if (!m_current_box) { m_current_box = CustomMessageBox::selectable(this, "", m_description); @@ -359,7 +359,7 @@ void InfoFrame::descriptionEllipsisHandler(QString link) } } -void InfoFrame::licenseEllipsisHandler(QString link) +void InfoFrame::licenseEllipsisHandler([[maybe_unused]] QString link) { if (!m_current_box) { m_current_box = CustomMessageBox::selectable(this, "", m_license); @@ -370,7 +370,7 @@ void InfoFrame::licenseEllipsisHandler(QString link) } } -void InfoFrame::boxClosed(int result) +void InfoFrame::boxClosed([[maybe_unused]] int result) { m_current_box = nullptr; } diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index c94fdd8d5..42279a663 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -1,20 +1,20 @@ #include "JavaSettingsWidget.h" -#include +#include #include -#include #include #include #include +#include #include -#include +#include #include +#include "FileSystem.h" #include "JavaCommon.h" #include "java/JavaInstall.h" #include "java/JavaUtils.h" -#include "FileSystem.h" #include "ui/dialogs/CustomMessageBox.h" #include "ui/widgets/VersionSelectWidget.h" @@ -46,7 +46,7 @@ void JavaSettingsWidget::setupUi() m_verticalLayout = new QVBoxLayout(this); m_verticalLayout->setObjectName(QStringLiteral("verticalLayout")); - m_versionWidget = new VersionSelectWidget(true, this); + m_versionWidget = new VersionSelectWidget(this); m_verticalLayout->addWidget(m_versionWidget); m_horizontalLayout = new QHBoxLayout(); @@ -126,6 +126,7 @@ void JavaSettingsWidget::setupUi() void JavaSettingsWidget::initialize() { m_versionWidget->initialize(APPLICATION->javalist().get()); + m_versionWidget->selectSearch(); m_versionWidget->setResizeOn(2); auto s = APPLICATION->settings(); // Memory @@ -149,40 +150,30 @@ void JavaSettingsWidget::refresh() JavaSettingsWidget::ValidationStatus JavaSettingsWidget::validate() { - switch(javaStatus) - { + switch (javaStatus) { default: case JavaStatus::NotSet: case JavaStatus::DoesNotExist: case JavaStatus::DoesNotStart: - case JavaStatus::ReturnedInvalidData: - { - int button = CustomMessageBox::selectable( - this, - tr("No Java version selected"), - tr("You didn't select a Java version or selected something that doesn't work.\n" - "%1 will not be able to start Minecraft.\n" - "Do you wish to proceed without any Java?" - "\n\n" - "You can change the Java version in the settings later.\n" - ).arg(BuildConfig.LAUNCHER_DISPLAYNAME), - QMessageBox::Warning, - QMessageBox::Yes | QMessageBox::No, - QMessageBox::NoButton - )->exec(); - if(button == QMessageBox::No) - { + case JavaStatus::ReturnedInvalidData: { + int button = CustomMessageBox::selectable(this, tr("No Java version selected"), + tr("You didn't select a Java version or selected something that doesn't work.\n" + "%1 will not be able to start Minecraft.\n" + "Do you wish to proceed without any Java?" + "\n\n" + "You can change the Java version in the settings later.\n") + .arg(BuildConfig.LAUNCHER_DISPLAYNAME), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton) + ->exec(); + if (button == QMessageBox::No) { return ValidationStatus::Bad; } return ValidationStatus::JavaBad; - } - break; - case JavaStatus::Pending: - { + } break; + case JavaStatus::Pending: { return ValidationStatus::Bad; } - case JavaStatus::Good: - { + case JavaStatus::Good: { return ValidationStatus::AllOK; } } @@ -219,34 +210,26 @@ void JavaSettingsWidget::memoryValueChanged(int) unsigned int min = m_minMemSpinBox->value(); unsigned int max = m_maxMemSpinBox->value(); unsigned int permgen = m_permGenSpinBox->value(); - QObject *obj = sender(); - if (obj == m_minMemSpinBox && min != observedMinMemory) - { + QObject* obj = sender(); + if (obj == m_minMemSpinBox && min != observedMinMemory) { observedMinMemory = min; actuallyChanged = true; - if (min > max) - { + if (min > max) { observedMaxMemory = min; m_maxMemSpinBox->setValue(min); } - } - else if (obj == m_maxMemSpinBox && max != observedMaxMemory) - { + } else if (obj == m_maxMemSpinBox && max != observedMaxMemory) { observedMaxMemory = max; actuallyChanged = true; - if (min > max) - { + if (min > max) { observedMinMemory = max; m_minMemSpinBox->setValue(max); } - } - else if (obj == m_permGenSpinBox && permgen != observedPermGenMemory) - { + } else if (obj == m_permGenSpinBox && permgen != observedPermGenMemory) { observedPermGenMemory = permgen; actuallyChanged = true; } - if(actuallyChanged) - { + if (actuallyChanged) { checkJavaPathOnEdit(m_javaPathTextBox->text()); updateThresholds(); } @@ -255,8 +238,7 @@ void JavaSettingsWidget::memoryValueChanged(int) void JavaSettingsWidget::javaVersionSelected(BaseVersion::Ptr version) { auto java = std::dynamic_pointer_cast(version); - if(!java) - { + if (!java) { return; } auto visible = java->id.requiresPermGen(); @@ -275,8 +257,7 @@ void JavaSettingsWidget::on_javaBrowseBtn_clicked() filter = "Java (java)"; #endif QString raw_path = QFileDialog::getOpenFileName(this, tr("Find Java executable"), QString(), filter); - if(raw_path.isEmpty()) - { + if (raw_path.isEmpty()) { return; } QString cooked_path = FS::NormalizePath(raw_path); @@ -288,8 +269,7 @@ void JavaSettingsWidget::on_javaStatusBtn_clicked() { QString text; bool failed = false; - switch(javaStatus) - { + switch (javaStatus) { case JavaStatus::NotSet: checkJavaPath(m_javaPathTextBox->text()); return; @@ -297,24 +277,20 @@ void JavaSettingsWidget::on_javaStatusBtn_clicked() text += QObject::tr("The specified file either doesn't exist or is not a proper executable."); failed = true; break; - case JavaStatus::DoesNotStart: - { + case JavaStatus::DoesNotStart: { text += QObject::tr("The specified Java binary didn't start properly.
    "); auto htmlError = m_result.errorLog; - if(!htmlError.isEmpty()) - { + if (!htmlError.isEmpty()) { htmlError.replace('\n', "
    "); text += QString("%1").arg(htmlError); } failed = true; break; } - case JavaStatus::ReturnedInvalidData: - { + case JavaStatus::ReturnedInvalidData: { text += QObject::tr("The specified Java binary returned unexpected results:
    "); auto htmlOut = m_result.outLog; - if(!htmlOut.isEmpty()) - { + if (!htmlOut.isEmpty()) { htmlOut.replace('\n', "
    "); text += QString("%1").arg(htmlOut); } @@ -322,26 +298,24 @@ void JavaSettingsWidget::on_javaStatusBtn_clicked() break; } case JavaStatus::Good: - text += QObject::tr("Java test succeeded!
    Platform reported: %1
    Java version " - "reported: %2
    ").arg(m_result.realPlatform, m_result.javaVersion.toString()); + text += QObject::tr( + "Java test succeeded!
    Platform reported: %1
    Java version " + "reported: %2
    ") + .arg(m_result.realPlatform, m_result.javaVersion.toString()); break; case JavaStatus::Pending: // TODO: abort here? return; } - CustomMessageBox::selectable( - this, - failed ? QObject::tr("Java test failure") : QObject::tr("Java test success"), - text, - failed ? QMessageBox::Critical : QMessageBox::Information - )->show(); + CustomMessageBox::selectable(this, failed ? QObject::tr("Java test failure") : QObject::tr("Java test success"), text, + failed ? QMessageBox::Critical : QMessageBox::Information) + ->show(); } void JavaSettingsWidget::setJavaStatus(JavaSettingsWidget::JavaStatus status) { javaStatus = status; - switch(javaStatus) - { + switch (javaStatus) { case JavaStatus::Good: m_javaStatusBtn->setIcon(goodIcon); break; @@ -364,29 +338,23 @@ void JavaSettingsWidget::checkJavaPathOnEdit(const QString& path) { auto realPath = FS::ResolveExecutable(path); QFileInfo pathInfo(realPath); - if (pathInfo.baseName().toLower().contains("java")) - { + if (pathInfo.baseName().toLower().contains("java")) { checkJavaPath(path); - } - else - { - if(!m_checker) - { + } else { + if (!m_checker) { setJavaStatus(JavaStatus::NotSet); } } } -void JavaSettingsWidget::checkJavaPath(const QString &path) +void JavaSettingsWidget::checkJavaPath(const QString& path) { - if(m_checker) - { + if (m_checker) { queuedCheck = path; return; } auto realPath = FS::ResolveExecutable(path); - if(realPath.isNull()) - { + if (realPath.isNull()) { setJavaStatus(JavaStatus::DoesNotExist); return; } @@ -395,8 +363,7 @@ void JavaSettingsWidget::checkJavaPath(const QString &path) m_checker->m_path = path; m_checker->m_minMem = m_minMemSpinBox->value(); m_checker->m_maxMem = m_maxMemSpinBox->value(); - if(m_permGenSpinBox->isVisible()) - { + if (m_permGenSpinBox->isVisible()) { m_checker->m_permGen = m_permGenSpinBox->value(); } connect(m_checker.get(), &JavaChecker::checkFinished, this, &JavaSettingsWidget::checkFinished); @@ -406,27 +373,22 @@ void JavaSettingsWidget::checkJavaPath(const QString &path) void JavaSettingsWidget::checkFinished(JavaCheckResult result) { m_result = result; - switch(result.validity) - { - case JavaCheckResult::Validity::Valid: - { + switch (result.validity) { + case JavaCheckResult::Validity::Valid: { setJavaStatus(JavaStatus::Good); break; } - case JavaCheckResult::Validity::ReturnedInvalidData: - { + case JavaCheckResult::Validity::ReturnedInvalidData: { setJavaStatus(JavaStatus::ReturnedInvalidData); break; } - case JavaCheckResult::Validity::Errored: - { + case JavaCheckResult::Validity::Errored: { setJavaStatus(JavaStatus::DoesNotStart); break; } } m_checker.reset(); - if(!queuedCheck.isNull()) - { + if (!queuedCheck.isNull()) { checkJavaPath(queuedCheck); queuedCheck.clear(); } diff --git a/launcher/ui/widgets/JavaSettingsWidget.h b/launcher/ui/widgets/JavaSettingsWidget.h index e4b7c7129..6ea73da60 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.h +++ b/launcher/ui/widgets/JavaSettingsWidget.h @@ -1,9 +1,9 @@ #pragma once #include -#include #include #include +#include #include class QLineEdit; @@ -20,30 +20,16 @@ class QToolButton; /** * This is a widget for all the Java settings dialogs and pages. */ -class JavaSettingsWidget : public QWidget -{ +class JavaSettingsWidget : public QWidget { Q_OBJECT -public: - explicit JavaSettingsWidget(QWidget *parent); - virtual ~JavaSettingsWidget() {}; + public: + explicit JavaSettingsWidget(QWidget* parent); + virtual ~JavaSettingsWidget(){}; - enum class JavaStatus - { - NotSet, - Pending, - Good, - DoesNotExist, - DoesNotStart, - ReturnedInvalidData - } javaStatus = JavaStatus::NotSet; + enum class JavaStatus { NotSet, Pending, Good, DoesNotExist, DoesNotStart, ReturnedInvalidData } javaStatus = JavaStatus::NotSet; - enum class ValidationStatus - { - Bad, - JavaBad, - AllOK - }; + enum class ValidationStatus { Bad, JavaBad, AllOK }; void refresh(); void initialize(); @@ -58,39 +44,38 @@ public: void updateThresholds(); - -protected slots: + protected slots: void memoryValueChanged(int); - void javaPathEdited(const QString &path); + void javaPathEdited(const QString& path); void javaVersionSelected(BaseVersion::Ptr version); void on_javaBrowseBtn_clicked(); void on_javaStatusBtn_clicked(); void checkFinished(JavaCheckResult result); -protected: /* methods */ - void checkJavaPathOnEdit(const QString &path); - void checkJavaPath(const QString &path); + protected: /* methods */ + void checkJavaPathOnEdit(const QString& path); + void checkJavaPath(const QString& path); void setJavaStatus(JavaStatus status); void setupUi(); -private: /* data */ - VersionSelectWidget *m_versionWidget = nullptr; - QVBoxLayout *m_verticalLayout = nullptr; + private: /* data */ + VersionSelectWidget* m_versionWidget = nullptr; + QVBoxLayout* m_verticalLayout = nullptr; - QLineEdit * m_javaPathTextBox = nullptr; - QPushButton * m_javaBrowseBtn = nullptr; - QToolButton * m_javaStatusBtn = nullptr; - QHBoxLayout *m_horizontalLayout = nullptr; + QLineEdit* m_javaPathTextBox = nullptr; + QPushButton* m_javaBrowseBtn = nullptr; + QToolButton* m_javaStatusBtn = nullptr; + QHBoxLayout* m_horizontalLayout = nullptr; - QGroupBox *m_memoryGroupBox = nullptr; - QGridLayout *m_gridLayout_2 = nullptr; - QSpinBox *m_maxMemSpinBox = nullptr; - QLabel *m_labelMinMem = nullptr; - QLabel *m_labelMaxMem = nullptr; - QLabel *m_labelMaxMemIcon = nullptr; - QSpinBox *m_minMemSpinBox = nullptr; - QLabel *m_labelPermGen = nullptr; - QSpinBox *m_permGenSpinBox = nullptr; + QGroupBox* m_memoryGroupBox = nullptr; + QGridLayout* m_gridLayout_2 = nullptr; + QSpinBox* m_maxMemSpinBox = nullptr; + QLabel* m_labelMinMem = nullptr; + QLabel* m_labelMaxMem = nullptr; + QLabel* m_labelMaxMemIcon = nullptr; + QSpinBox* m_minMemSpinBox = nullptr; + QLabel* m_labelPermGen = nullptr; + QSpinBox* m_permGenSpinBox = nullptr; QIcon goodIcon; QIcon yellowIcon; QIcon badIcon; diff --git a/launcher/ui/widgets/LabeledToolButton.cpp b/launcher/ui/widgets/LabeledToolButton.cpp index f52e49c98..46114e047 100644 --- a/launcher/ui/widgets/LabeledToolButton.cpp +++ b/launcher/ui/widgets/LabeledToolButton.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -33,31 +33,29 @@ * limitations under the License. */ -#include -#include -#include -#include #include "LabeledToolButton.h" #include #include +#include +#include +#include +#include /* - * + * * Tool Button with a label on it, instead of the normal text rendering - * + * */ -LabeledToolButton::LabeledToolButton(QWidget * parent) - : QToolButton(parent) - , m_label(new QLabel(this)) +LabeledToolButton::LabeledToolButton(QWidget* parent) : QToolButton(parent), m_label(new QLabel(this)) { - //QToolButton::setText(" "); + // QToolButton::setText(" "); m_label->setWordWrap(true); m_label->setMouseTracking(false); m_label->setAlignment(Qt::AlignCenter); m_label->setTextInteractionFlags(Qt::NoTextInteraction); // somehow, this makes word wrap work in the QLabel. yay. - //m_label->setMinimumWidth(100); + // m_label->setMinimumWidth(100); } QString LabeledToolButton::text() const @@ -65,7 +63,7 @@ QString LabeledToolButton::text() const return m_label->text(); } -void LabeledToolButton::setText(const QString & text) +void LabeledToolButton::setText(const QString& text) { m_label->setText(text); } @@ -76,7 +74,6 @@ void LabeledToolButton::setIcon(QIcon icon) resetIcon(); } - /*! \reimp */ @@ -92,24 +89,21 @@ QSize LabeledToolButton::sizeHint() const int w = 0, h = 0; QStyleOptionToolButton opt; initStyleOption(&opt); - QSize sz =m_label->sizeHint(); + QSize sz = m_label->sizeHint(); w = sz.width(); h = sz.height(); - opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height + opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height if (popupMode() == MenuButtonPopup) w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this); - + return style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), this); } - - -void LabeledToolButton::resizeEvent(QResizeEvent * event) +void LabeledToolButton::resizeEvent(QResizeEvent* event) { - m_label->setGeometry(QRect(4, 4, width()-8, height()-8)); - if(!m_icon.isNull()) - { + m_label->setGeometry(QRect(4, 4, width() - 8, height() - 8)); + if (!m_icon.isNull()) { resetIcon(); } QWidget::resizeEvent(event); @@ -120,14 +114,14 @@ void LabeledToolButton::resetIcon() auto iconSz = m_icon.actualSize(QSize(160, 80)); float w = iconSz.width(); float h = iconSz.height(); - float ar = w/h; + float ar = w / h; // FIXME: hardcoded max size of 160x80 int newW = 80 * ar; - if(newW > 160) + if (newW > 160) newW = 160; - QSize newSz (newW, 80); + QSize newSz(newW, 80); auto pixmap = m_icon.pixmap(newSz); m_label->setPixmap(pixmap); m_label->setMinimumHeight(80); - m_label->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Preferred ); + m_label->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); } diff --git a/launcher/ui/widgets/LabeledToolButton.h b/launcher/ui/widgets/LabeledToolButton.h index 51f99e9ba..0bb5e28de 100644 --- a/launcher/ui/widgets/LabeledToolButton.h +++ b/launcher/ui/widgets/LabeledToolButton.h @@ -20,21 +20,21 @@ class QLabel; -class LabeledToolButton : public QToolButton -{ +class LabeledToolButton : public QToolButton { Q_OBJECT - QLabel * m_label; + QLabel* m_label; QIcon m_icon; -public: - LabeledToolButton(QWidget * parent = 0); + public: + LabeledToolButton(QWidget* parent = 0); QString text() const; - void setText(const QString & text); + void setText(const QString& text); void setIcon(QIcon icon); virtual QSize sizeHint() const; -protected: - void resizeEvent(QResizeEvent * event); + + protected: + void resizeEvent(QResizeEvent* event); void resetIcon(); }; diff --git a/launcher/ui/widgets/LineSeparator.cpp b/launcher/ui/widgets/LineSeparator.cpp index d03e67623..2d6239a2f 100644 --- a/launcher/ui/widgets/LineSeparator.cpp +++ b/launcher/ui/widgets/LineSeparator.cpp @@ -1,11 +1,11 @@ #include "LineSeparator.h" -#include -#include #include #include +#include +#include -void LineSeparator::initStyleOption(QStyleOption *option) const +void LineSeparator::initStyleOption(QStyleOption* option) const { option->initFrom(this); // in a horizontal layout, the line is vertical (and vice versa) @@ -13,8 +13,7 @@ void LineSeparator::initStyleOption(QStyleOption *option) const option->state |= QStyle::State_Horizontal; } -LineSeparator::LineSeparator(QWidget *parent, Qt::Orientation orientation) - : QWidget(parent), m_orientation(orientation) +LineSeparator::LineSeparator(QWidget* parent, Qt::Orientation orientation) : QWidget(parent), m_orientation(orientation) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); } @@ -23,12 +22,11 @@ QSize LineSeparator::sizeHint() const { QStyleOption opt; initStyleOption(&opt); - const int extent = - style()->pixelMetric(QStyle::PM_ToolBarSeparatorExtent, &opt, parentWidget()); + const int extent = style()->pixelMetric(QStyle::PM_ToolBarSeparatorExtent, &opt, parentWidget()); return QSize(extent, extent); } -void LineSeparator::paintEvent(QPaintEvent *) +void LineSeparator::paintEvent(QPaintEvent*) { QPainter p(this); QStyleOption opt; diff --git a/launcher/ui/widgets/LineSeparator.h b/launcher/ui/widgets/LineSeparator.h index 22927b68d..719facb99 100644 --- a/launcher/ui/widgets/LineSeparator.h +++ b/launcher/ui/widgets/LineSeparator.h @@ -3,16 +3,16 @@ class QStyleOption; -class LineSeparator : public QWidget -{ +class LineSeparator : public QWidget { Q_OBJECT -public: + public: /// Create a line separator. orientation is the orientation of the line. - explicit LineSeparator(QWidget *parent, Qt::Orientation orientation = Qt::Horizontal); + explicit LineSeparator(QWidget* parent, Qt::Orientation orientation = Qt::Horizontal); QSize sizeHint() const; - void paintEvent(QPaintEvent *); - void initStyleOption(QStyleOption *option) const; -private: + void paintEvent(QPaintEvent*); + void initStyleOption(QStyleOption* option) const; + + private: Qt::Orientation m_orientation = Qt::Horizontal; }; diff --git a/launcher/ui/widgets/LogView.cpp b/launcher/ui/widgets/LogView.cpp index 9c46438d6..4096889d3 100644 --- a/launcher/ui/widgets/LogView.cpp +++ b/launcher/ui/widgets/LogView.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -34,8 +34,8 @@ */ #include "LogView.h" -#include #include +#include LogView::LogView(QWidget* parent) : QPlainTextEdit(parent) { @@ -50,13 +50,10 @@ LogView::~LogView() void LogView::setWordWrap(bool wrapping) { - if(wrapping) - { + if (wrapping) { setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setLineWrapMode(QPlainTextEdit::WidgetWidth); - } - else - { + } else { setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); setLineWrapMode(QPlainTextEdit::NoWrap); } @@ -64,16 +61,14 @@ void LogView::setWordWrap(bool wrapping) void LogView::setModel(QAbstractItemModel* model) { - if(m_model) - { + if (m_model) { disconnect(m_model, &QAbstractItemModel::modelReset, this, &LogView::repopulate); disconnect(m_model, &QAbstractItemModel::rowsInserted, this, &LogView::rowsInserted); disconnect(m_model, &QAbstractItemModel::rowsAboutToBeInserted, this, &LogView::rowsAboutToBeInserted); disconnect(m_model, &QAbstractItemModel::rowsRemoved, this, &LogView::rowsRemoved); } m_model = model; - if(m_model) - { + if (m_model) { connect(m_model, &QAbstractItemModel::modelReset, this, &LogView::repopulate); connect(m_model, &QAbstractItemModel::rowsInserted, this, &LogView::rowsInserted); connect(m_model, &QAbstractItemModel::rowsAboutToBeInserted, this, &LogView::rowsAboutToBeInserted); @@ -83,15 +78,14 @@ void LogView::setModel(QAbstractItemModel* model) repopulate(); } -QAbstractItemModel * LogView::model() const +QAbstractItemModel* LogView::model() const { return m_model; } void LogView::modelDestroyed(QObject* model) { - if(m_model == model) - { + if (m_model == model) { setModel(nullptr); } } @@ -100,8 +94,7 @@ void LogView::repopulate() { auto doc = document(); doc->clear(); - if(!m_model) - { + if (!m_model) { return; } rowsInserted(QModelIndex(), 0, m_model->rowCount() - 1); @@ -112,39 +105,32 @@ void LogView::rowsAboutToBeInserted(const QModelIndex& parent, int first, int la Q_UNUSED(parent) Q_UNUSED(first) Q_UNUSED(last) - QScrollBar *bar = verticalScrollBar(); + QScrollBar* bar = verticalScrollBar(); int max_bar = bar->maximum(); int val_bar = bar->value(); - if (m_scroll) - { + if (m_scroll) { m_scroll = (max_bar - val_bar) <= 1; - } - else - { + } else { m_scroll = val_bar == max_bar; } } void LogView::rowsInserted(const QModelIndex& parent, int first, int last) { - for(int i = first; i <= last; i++) - { + for (int i = first; i <= last; i++) { auto idx = m_model->index(i, 0, parent); auto text = m_model->data(idx, Qt::DisplayRole).toString(); QTextCharFormat format(*m_defaultFormat); auto font = m_model->data(idx, Qt::FontRole); - if(font.isValid()) - { + if (font.isValid()) { format.setFont(font.value()); } auto fg = m_model->data(idx, Qt::ForegroundRole); - if(fg.isValid()) - { + if (fg.isValid()) { format.setForeground(fg.value()); } auto bg = m_model->data(idx, Qt::BackgroundRole); - if(bg.isValid()) - { + if (bg.isValid()) { format.setBackground(bg.value()); } auto workCursor = textCursor(); @@ -152,10 +138,9 @@ void LogView::rowsInserted(const QModelIndex& parent, int first, int last) workCursor.insertText(text, format); workCursor.insertBlock(); } - if(m_scroll && !m_scrolling) - { + if (m_scroll && !m_scrolling) { m_scrolling = true; - QMetaObject::invokeMethod( this, "scrollToBottom", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, "scrollToBottom", Qt::QueuedConnection); } } diff --git a/launcher/ui/widgets/LogView.h b/launcher/ui/widgets/LogView.h index 3143360aa..dde5f8f76 100644 --- a/launcher/ui/widgets/LogView.h +++ b/launcher/ui/widgets/LogView.h @@ -1,36 +1,35 @@ #pragma once -#include #include +#include class QAbstractItemModel; -class LogView: public QPlainTextEdit -{ +class LogView : public QPlainTextEdit { Q_OBJECT -public: - explicit LogView(QWidget *parent = nullptr); + public: + explicit LogView(QWidget* parent = nullptr); virtual ~LogView(); - virtual void setModel(QAbstractItemModel *model); - QAbstractItemModel *model() const; + virtual void setModel(QAbstractItemModel* model); + QAbstractItemModel* model() const; -public slots: + public slots: void setWordWrap(bool wrapping); - void findNext(const QString & what, bool reverse); + void findNext(const QString& what, bool reverse); void scrollToBottom(); -protected slots: + protected slots: void repopulate(); // note: this supports only appending - void rowsInserted(const QModelIndex &parent, int first, int last); - void rowsAboutToBeInserted(const QModelIndex &parent, int first, int last); + void rowsInserted(const QModelIndex& parent, int first, int last); + void rowsAboutToBeInserted(const QModelIndex& parent, int first, int last); // note: this supports only removing from front - void rowsRemoved(const QModelIndex &parent, int first, int last); - void modelDestroyed(QObject * model); + void rowsRemoved(const QModelIndex& parent, int first, int last); + void modelDestroyed(QObject* model); -protected: - QAbstractItemModel *m_model = nullptr; - QTextCharFormat *m_defaultFormat = nullptr; + protected: + QAbstractItemModel* m_model = nullptr; + QTextCharFormat* m_defaultFormat = nullptr; bool m_scroll = false; bool m_scrolling = false; }; diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index ea052c41a..c2c099eeb 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -18,9 +18,8 @@ unique_qobject_ptr ModFilterWidget::create(Version default_vers auto task = filter_widget->versionList()->getLoadTask(); - connect(task.get(), &Task::failed, [filter_widget]{ - filter_widget->disableVersionButton(VersionButtonID::Major, tr("failed to get version index")); - }); + connect(task.get(), &Task::failed, + [filter_widget] { filter_widget->disableVersionButton(VersionButtonID::Major, tr("failed to get version index")); }); connect(task.get(), &Task::finished, &load_version_list_loop, &QEventLoop::quit); if (!task->isRunning()) @@ -34,16 +33,15 @@ unique_qobject_ptr ModFilterWidget::create(Version default_vers return unique_qobject_ptr(filter_widget); } -ModFilterWidget::ModFilterWidget(Version def, QWidget* parent) - : QTabWidget(parent), m_filter(new Filter()), ui(new Ui::ModFilterWidget) +ModFilterWidget::ModFilterWidget(Version def, QWidget* parent) : QTabWidget(parent), m_filter(new Filter()), ui(new Ui::ModFilterWidget) { ui->setupUi(this); - m_mcVersion_buttons.addButton(ui->strictVersionButton, VersionButtonID::Strict); + m_mcVersion_buttons.addButton(ui->strictVersionButton, VersionButtonID::Strict); ui->strictVersionButton->click(); - m_mcVersion_buttons.addButton(ui->majorVersionButton, VersionButtonID::Major); - m_mcVersion_buttons.addButton(ui->allVersionsButton, VersionButtonID::All); - //m_mcVersion_buttons.addButton(ui->betweenVersionsButton, VersionButtonID::Between); + m_mcVersion_buttons.addButton(ui->majorVersionButton, VersionButtonID::Major); + m_mcVersion_buttons.addButton(ui->allVersionsButton, VersionButtonID::All); + // m_mcVersion_buttons.addButton(ui->betweenVersionsButton, VersionButtonID::Between); connect(&m_mcVersion_buttons, SIGNAL(idClicked(int)), this, SLOT(onVersionFilterChanged(int))); @@ -57,25 +55,19 @@ void ModFilterWidget::setInstance(MinecraftInstance* instance) { m_instance = instance; - ui->strictVersionButton->setText( - tr("Strict match (= %1)").arg(mcVersionStr())); + ui->strictVersionButton->setText(tr("Strict match (= %1)").arg(mcVersionStr())); // we can't do this for snapshots sadly - if(mcVersionStr().contains('.')) - { + if (mcVersionStr().contains('.')) { auto mcVersionSplit = mcVersionStr().split("."); - ui->majorVersionButton->setText( - tr("Major version match (= %1.%2.x)").arg(mcVersionSplit[0], mcVersionSplit[1])); - } - else - { + ui->majorVersionButton->setText(tr("Major version match (= %1.%2.x)").arg(mcVersionSplit[0], mcVersionSplit[1])); + } else { ui->majorVersionButton->setText(tr("Major version match (unsupported)")); disableVersionButton(Major); } - ui->allVersionsButton->setText( - tr("Any version")); - //ui->betweenVersionsButton->setText( - // tr("Between two versions")); + ui->allVersionsButton->setText(tr("Any version")); + // ui->betweenVersionsButton->setText( + // tr("Between two versions")); } auto ModFilterWidget::getFilter() -> std::shared_ptr @@ -89,19 +81,19 @@ void ModFilterWidget::disableVersionButton(VersionButtonID id, QString reason) { QAbstractButton* btn = nullptr; - switch(id){ - case(VersionButtonID::Strict): - btn = ui->strictVersionButton; - break; - case(VersionButtonID::Major): - btn = ui->majorVersionButton; - break; - case(VersionButtonID::All): - btn = ui->allVersionsButton; - break; - case(VersionButtonID::Between): - default: - break; + switch (id) { + case (VersionButtonID::Strict): + btn = ui->strictVersionButton; + break; + case (VersionButtonID::Major): + btn = ui->majorVersionButton; + break; + case (VersionButtonID::All): + btn = ui->allVersionsButton; + break; + case (VersionButtonID::Between): + default: + break; } if (btn) { @@ -113,12 +105,12 @@ void ModFilterWidget::disableVersionButton(VersionButtonID id, QString reason) void ModFilterWidget::onVersionFilterChanged(int id) { - //ui->lowerVersionComboBox->setEnabled(id == VersionButtonID::Between); - //ui->upperVersionComboBox->setEnabled(id == VersionButtonID::Between); + // ui->lowerVersionComboBox->setEnabled(id == VersionButtonID::Between); + // ui->upperVersionComboBox->setEnabled(id == VersionButtonID::Between); int index = 1; - auto cast_id = (VersionButtonID) id; + auto cast_id = (VersionButtonID)id; if (cast_id != m_version_id) { m_version_id = cast_id; } else { @@ -127,32 +119,32 @@ void ModFilterWidget::onVersionFilterChanged(int id) m_filter->versions.clear(); - switch(cast_id){ - case(VersionButtonID::Strict): - m_filter->versions.push_front(mcVersion()); - break; - case(VersionButtonID::Major): { - auto versionSplit = mcVersionStr().split("."); + switch (cast_id) { + case (VersionButtonID::Strict): + m_filter->versions.push_front(mcVersion()); + break; + case (VersionButtonID::Major): { + auto versionSplit = mcVersionStr().split("."); - auto major_version = QString("%1.%2").arg(versionSplit[0], versionSplit[1]); - QString version_str = major_version; + auto major_version = QString("%1.%2").arg(versionSplit[0], versionSplit[1]); + QString version_str = major_version; - while (m_version_list->hasVersion(version_str)) { - m_filter->versions.emplace_back(version_str); - version_str = QString("%1.%2").arg(major_version, QString::number(index++)); + while (m_version_list->hasVersion(version_str)) { + m_filter->versions.emplace_back(version_str); + version_str = QString("%1.%2").arg(major_version, QString::number(index++)); + } + + break; } - - break; - } - case(VersionButtonID::All): - // Empty list to avoid enumerating all versions :P - break; - case(VersionButtonID::Between): - // TODO - break; + case (VersionButtonID::All): + // Empty list to avoid enumerating all versions :P + break; + case (VersionButtonID::Between): + // TODO + break; } - if(changed()) + if (changed()) emit filterChanged(); else emit filterUnchanged(); diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index 706ffd21a..ed6cd0ea7 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include "Version.h" @@ -17,16 +17,10 @@ namespace Ui { class ModFilterWidget; } -class ModFilterWidget : public QTabWidget -{ +class ModFilterWidget : public QTabWidget { Q_OBJECT -public: - enum VersionButtonID { - Strict = 0, - Major = 1, - All = 2, - Between = 3 - }; + public: + enum VersionButtonID { Strict = 0, Major = 1, All = 2, Between = 3 }; struct Filter { std::list versions; @@ -37,7 +31,7 @@ public: std::shared_ptr m_filter; -public: + public: static unique_qobject_ptr create(Version default_version, QWidget* parent = nullptr); ~ModFilterWidget(); @@ -51,26 +45,29 @@ public: Meta::VersionList::Ptr versionList() { return m_version_list; } -private: + private: ModFilterWidget(Version def, QWidget* parent = nullptr); - inline auto mcVersionStr() const -> QString { return m_instance ? m_instance->getPackProfile()->getComponentVersion("net.minecraft") : ""; } + inline auto mcVersionStr() const -> QString + { + return m_instance ? m_instance->getPackProfile()->getComponentVersion("net.minecraft") : ""; + } inline auto mcVersion() const -> Version { return { mcVersionStr() }; } -private slots: + private slots: void onVersionFilterChanged(int id); -public: signals: + public: + signals: void filterChanged(); void filterUnchanged(); -private: + private: Ui::ModFilterWidget* ui; MinecraftInstance* m_instance = nullptr; - -/* Version stuff */ + /* Version stuff */ QButtonGroup m_mcVersion_buttons; Meta::VersionList::Ptr m_version_list; diff --git a/launcher/ui/widgets/ModListView.cpp b/launcher/ui/widgets/ModListView.cpp index 80a918b64..c72d4c522 100644 --- a/launcher/ui/widgets/ModListView.cpp +++ b/launcher/ui/widgets/ModListView.cpp @@ -14,61 +14,55 @@ */ #include "ModListView.h" +#include #include #include #include -#include #include -ModListView::ModListView ( QWidget* parent ) - :QTreeView ( parent ) +ModListView::ModListView(QWidget* parent) : QTreeView(parent) { - setAllColumnsShowFocus ( true ); - setExpandsOnDoubleClick ( false ); - setRootIsDecorated ( false ); - setSortingEnabled ( true ); - setAlternatingRowColors ( true ); - setSelectionMode ( QAbstractItemView::ExtendedSelection ); - setHeaderHidden ( false ); + setAllColumnsShowFocus(true); + setExpandsOnDoubleClick(false); + setRootIsDecorated(false); + setSortingEnabled(true); + setAlternatingRowColors(true); + setSelectionMode(QAbstractItemView::ExtendedSelection); + setHeaderHidden(false); setSelectionBehavior(QAbstractItemView::SelectRows); - setHorizontalScrollBarPolicy ( Qt::ScrollBarAsNeeded ); + setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); setDropIndicatorShown(true); setDragEnabled(true); setDragDropMode(QAbstractItemView::DropOnly); viewport()->setAcceptDrops(true); } -void ModListView::setModel ( QAbstractItemModel* model ) +void ModListView::setModel(QAbstractItemModel* model) { - QTreeView::setModel ( model ); + QTreeView::setModel(model); auto head = header(); head->setStretchLastSection(false); // HACK: this is true for the checkbox column of mod lists - auto string = model->headerData(0,head->orientation()).toString(); - if(head->count() < 1) - { + auto string = model->headerData(0, head->orientation()).toString(); + if (head->count() < 1) { return; } - if(!string.size()) - { + if (!string.size()) { head->setSectionResizeMode(0, QHeaderView::ResizeToContents); head->setSectionResizeMode(1, QHeaderView::Stretch); - for(int i = 2; i < head->count(); i++) + for (int i = 2; i < head->count(); i++) head->setSectionResizeMode(i, QHeaderView::ResizeToContents); - } - else - { + } else { head->setSectionResizeMode(0, QHeaderView::Stretch); - for(int i = 1; i < head->count(); i++) + for (int i = 1; i < head->count(); i++) head->setSectionResizeMode(i, QHeaderView::ResizeToContents); } } -void ModListView::setResizeModes(const QList &modes) +void ModListView::setResizeModes(const QList& modes) { auto head = header(); - for(int i = 0; i < modes.count(); i++) { + for (int i = 0; i < modes.count(); i++) { head->setSectionResizeMode(i, modes[i]); } } - diff --git a/launcher/ui/widgets/ModListView.h b/launcher/ui/widgets/ModListView.h index 3f0b3b0e1..86316459f 100644 --- a/launcher/ui/widgets/ModListView.h +++ b/launcher/ui/widgets/ModListView.h @@ -17,11 +17,10 @@ #include #include -class ModListView: public QTreeView -{ +class ModListView : public QTreeView { Q_OBJECT -public: - explicit ModListView ( QWidget* parent = 0 ); - virtual void setModel ( QAbstractItemModel* model ); - virtual void setResizeModes (const QList& modes); + public: + explicit ModListView(QWidget* parent = 0); + virtual void setModel(QAbstractItemModel* model); + virtual void setResizeModes(const QList& modes); }; diff --git a/launcher/ui/widgets/PageContainer.cpp b/launcher/ui/widgets/PageContainer.cpp index b98c9796f..514e1d25c 100644 --- a/launcher/ui/widgets/PageContainer.cpp +++ b/launcher/ui/widgets/PageContainer.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2022 Jamie Mansfield + * Copyright (C) 2023 TheKodeToad * * 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 @@ -38,36 +39,33 @@ #include "BuildConfig.h" #include "PageContainer_p.h" -#include -#include -#include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include "settings/SettingsObject.h" #include "ui/widgets/IconLabel.h" -#include "DesktopServices.h" #include "Application.h" +#include "DesktopServices.h" -class PageEntryFilterModel : public QSortFilterProxyModel -{ -public: - explicit PageEntryFilterModel(QObject *parent = 0) : QSortFilterProxyModel(parent) - { - } +class PageEntryFilterModel : public QSortFilterProxyModel { + public: + explicit PageEntryFilterModel(QObject* parent = 0) : QSortFilterProxyModel(parent) {} -protected: - bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const + protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const { const QString pattern = filterRegularExpression().pattern(); - const auto model = static_cast(sourceModel()); + const auto model = static_cast(sourceModel()); const auto page = model->pages().at(sourceRow); if (!page->shouldDisplay()) return false; @@ -76,18 +74,15 @@ protected: } }; -PageContainer::PageContainer(BasePageProvider *pageProvider, QString defaultId, - QWidget *parent) - : QWidget(parent) +PageContainer::PageContainer(BasePageProvider* pageProvider, QString defaultId, QWidget* parent) : QWidget(parent) { createUI(); m_model = new PageModel(this); m_proxyModel = new PageEntryFilterModel(this); int counter = 0; auto pages = pageProvider->getPages(); - for (auto page : pages) - { - auto widget = dynamic_cast(page); + for (auto page : pages) { + auto widget = dynamic_cast(page); widget->setParent(this); page->stackIndex = m_pageStack->addWidget(widget); page->listIndex = counter; @@ -108,8 +103,7 @@ PageContainer::PageContainer(BasePageProvider *pageProvider, QString defaultId, m_pageList->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); m_pageList->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); m_pageList->setModel(m_proxyModel); - connect(m_pageList->selectionModel(), SIGNAL(currentRowChanged(QModelIndex, QModelIndex)), - this, SLOT(currentChanged(QModelIndex))); + connect(m_pageList->selectionModel(), SIGNAL(currentRowChanged(QModelIndex, QModelIndex)), this, SLOT(currentChanged(QModelIndex))); m_pageStack->setStackingMode(QStackedLayout::StackOne); m_pageList->setFocus(); selectPage(defaultId); @@ -120,16 +114,13 @@ bool PageContainer::selectPage(QString pageId) // now find what we want to have selected... auto page = m_model->findPageEntryById(pageId); QModelIndex index; - if (page) - { + if (page) { index = m_proxyModel->mapFromSource(m_model->index(page->listIndex)); } - if(!index.isValid()) - { + if (!index.isValid()) { index = m_proxyModel->index(0, 0); } - if (index.isValid()) - { + if (index.isValid()) { m_pageList->setCurrentIndex(index); return true; } @@ -141,7 +132,12 @@ BasePage* PageContainer::getPage(QString pageId) return m_model->findPageEntryById(pageId); } -const QList PageContainer::getPages() const +BasePage* PageContainer::selectedPage() const +{ + return m_currentPage; +} + +const QList& PageContainer::getPages() const { return m_model->pages(); } @@ -149,15 +145,11 @@ const QList PageContainer::getPages() const void PageContainer::refreshContainer() { m_proxyModel->invalidate(); - if(!m_currentPage->shouldDisplay()) - { + if (!m_currentPage->shouldDisplay()) { auto index = m_proxyModel->index(0, 0); - if(index.isValid()) - { + if (index.isValid()) { m_pageList->setCurrentIndex(index); - } - else - { + } else { // FIXME: unhandled corner case: what to do when there's no page to select? } } @@ -177,7 +169,7 @@ void PageContainer::createUI() headerLabelFont.setPointSize(pointSize + 2); m_header->setFont(headerLabelFont); - QHBoxLayout *headerHLayout = new QHBoxLayout; + QHBoxLayout* headerHLayout = new QHBoxLayout; const int leftMargin = APPLICATION->style()->pixelMetric(QStyle::PM_LayoutLeftMargin); headerHLayout->addSpacerItem(new QSpacerItem(leftMargin, 0, QSizePolicy::Fixed, QSizePolicy::Ignored)); headerHLayout->addWidget(m_header); @@ -195,7 +187,7 @@ void PageContainer::createUI() m_layout->addWidget(m_pageList, 0, 0, 2, 1); m_layout->addLayout(m_pageStack, 1, 1, 1, 1); m_layout->setColumnStretch(1, 4); - m_layout->setContentsMargins(0,0,0,6); + m_layout->setContentsMargins(0, 0, 0, 6); setLayout(m_layout); } @@ -208,39 +200,32 @@ void PageContainer::retranslate() page->retranslate(); } -void PageContainer::addButtons(QWidget *buttons) +void PageContainer::addButtons(QWidget* buttons) { m_layout->addWidget(buttons, 2, 0, 1, 2); } -void PageContainer::addButtons(QLayout *buttons) +void PageContainer::addButtons(QLayout* buttons) { m_layout->addLayout(buttons, 2, 0, 1, 2); } void PageContainer::showPage(int row) { - if (m_currentPage) - { + if (m_currentPage) { m_currentPage->closed(); } - if (row != -1) - { + if (row != -1) { m_currentPage = m_model->pages().at(row); - } - else - { + } else { m_currentPage = nullptr; } - if (m_currentPage) - { + if (m_currentPage) { m_pageStack->setCurrentIndex(m_currentPage->stackIndex); m_header->setText(m_currentPage->displayName()); m_iconHeader->setIcon(m_currentPage->icon()); m_currentPage->opened(); - } - else - { + } else { m_pageStack->setCurrentIndex(0); m_header->setText(QString()); m_iconHeader->setIcon(APPLICATION->getThemedIcon("bug")); @@ -249,8 +234,7 @@ void PageContainer::showPage(int row) void PageContainer::help() { - if (m_currentPage) - { + if (m_currentPage) { QString pageId = m_currentPage->helpPage(); if (pageId.isEmpty()) return; @@ -258,7 +242,7 @@ void PageContainer::help() } } -void PageContainer::currentChanged(const QModelIndex ¤t) +void PageContainer::currentChanged(const QModelIndex& current) { int selected_index = current.isValid() ? m_proxyModel->mapToSource(current).row() : -1; @@ -272,12 +256,10 @@ void PageContainer::currentChanged(const QModelIndex ¤t) bool PageContainer::prepareToClose() { - if(!saveAll()) - { + if (!saveAll()) { return false; } - if (m_currentPage) - { + if (m_currentPage) { m_currentPage->closed(); } return true; @@ -285,8 +267,7 @@ bool PageContainer::prepareToClose() bool PageContainer::saveAll() { - for (auto page : m_model->pages()) - { + for (auto page : m_model->pages()) { if (!page->apply()) return false; } diff --git a/launcher/ui/widgets/PageContainer.h b/launcher/ui/widgets/PageContainer.h index ad74d43a2..05be1c3a5 100644 --- a/launcher/ui/widgets/PageContainer.h +++ b/launcher/ui/widgets/PageContainer.h @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield + * Copyright (C) 2023 TheKodeToad * * 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 @@ -35,11 +36,11 @@ #pragma once -#include #include +#include -#include "ui/pages/BasePageProvider.h" #include "ui/pages/BasePageContainer.h" +#include "ui/pages/BasePageProvider.h" class QLayout; class IconLabel; @@ -51,16 +52,14 @@ class QLineEdit; class QStackedLayout; class QGridLayout; -class PageContainer : public QWidget, public BasePageContainer -{ +class PageContainer : public QWidget, public BasePageContainer { Q_OBJECT -public: - explicit PageContainer(BasePageProvider *pageProvider, QString defaultId = QString(), - QWidget *parent = 0); + public: + explicit PageContainer(BasePageProvider* pageProvider, QString defaultId = QString(), QWidget* parent = 0); virtual ~PageContainer() {} - void addButtons(QWidget * buttons); - void addButtons(QLayout * buttons); + void addButtons(QWidget* buttons); + void addButtons(QLayout* buttons); /* * Save any unsaved state and prepare to be closed. * @return true if everything can be saved, false if there is something that requires attention @@ -71,48 +70,45 @@ public: /* request close - used by individual pages */ bool requestClose() override { - if(m_container) - { + if (m_container) { return m_container->requestClose(); } return false; } - virtual bool selectPage(QString pageId) override; + bool selectPage(QString pageId) override; + BasePage* selectedPage() const override; BasePage* getPage(QString pageId) override; - const QList getPages() const; + const QList& getPages() const; void refreshContainer() override; - virtual void setParentContainer(BasePageContainer * container) - { - m_container = container; - }; + virtual void setParentContainer(BasePageContainer* container) { m_container = container; }; void changeEvent(QEvent*) override; -private: + private: void createUI(); void retranslate(); -public slots: + public slots: void help(); -signals: + signals: /** Emitted when the currently selected page is changed */ void selectedPageChanged(BasePage* previous, BasePage* selected); -private slots: - void currentChanged(const QModelIndex ¤t); + private slots: + void currentChanged(const QModelIndex& current); void showPage(int row); -private: - BasePageContainer * m_container = nullptr; - BasePage * m_currentPage = 0; - QSortFilterProxyModel *m_proxyModel; - PageModel *m_model; - QStackedLayout *m_pageStack; - QListView *m_pageList; - QLabel *m_header; - IconLabel *m_iconHeader; - QGridLayout *m_layout; + private: + BasePageContainer* m_container = nullptr; + BasePage* m_currentPage = 0; + QSortFilterProxyModel* m_proxyModel; + PageModel* m_model; + QStackedLayout* m_pageStack; + QListView* m_pageList; + QLabel* m_header; + IconLabel* m_iconHeader; + QGridLayout* m_layout; }; diff --git a/launcher/ui/widgets/PageContainer_p.h b/launcher/ui/widgets/PageContainer_p.h index da1a66f46..e61f6e154 100644 --- a/launcher/ui/widgets/PageContainer_p.h +++ b/launcher/ui/widgets/PageContainer_p.h @@ -15,21 +15,18 @@ #pragma once -#include -#include #include +#include #include +#include class BasePage; const int pageIconSize = 24; -class PageViewDelegate : public QStyledItemDelegate -{ -public: - PageViewDelegate(QObject *parent) : QStyledItemDelegate(parent) - { - } - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +class PageViewDelegate : public QStyledItemDelegate { + public: + PageViewDelegate(QObject* parent) : QStyledItemDelegate(parent) {} + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { QSize size = QStyledItemDelegate::sizeHint(option, index); size.setHeight(qMax(size.height(), 32)); @@ -37,10 +34,9 @@ public: } }; -class PageModel : public QAbstractListModel -{ -public: - PageModel(QObject *parent = 0) : QAbstractListModel(parent) +class PageModel : public QAbstractListModel { + public: + PageModel(QObject* parent = 0) : QAbstractListModel(parent) { QPixmap empty(pageIconSize, pageIconSize); empty.fill(Qt::transparent); @@ -48,61 +44,53 @@ public: } virtual ~PageModel() {} - int rowCount(const QModelIndex &parent = QModelIndex()) const + int rowCount(const QModelIndex& parent = QModelIndex()) const { return parent.isValid() ? 0 : m_pages.size(); } + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const { - return parent.isValid() ? 0 : m_pages.size(); - } - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const - { - switch (role) - { - case Qt::DisplayRole: - return m_pages.at(index.row())->displayName(); - case Qt::DecorationRole: - { - QIcon icon = m_pages.at(index.row())->icon(); - if (icon.isNull()) - icon = m_emptyIcon; - // HACK: fixes icon stretching on windows. TODO: report Qt bug for this - return QIcon(icon.pixmap(QSize(48,48))); - } + switch (role) { + case Qt::DisplayRole: + return m_pages.at(index.row())->displayName(); + case Qt::DecorationRole: { + QIcon icon = m_pages.at(index.row())->icon(); + if (icon.isNull()) + icon = m_emptyIcon; + // HACK: fixes icon stretching on windows. TODO: report Qt bug for this + return QIcon(icon.pixmap(QSize(48, 48))); + } } return QVariant(); } - void setPages(const QList &pages) + void setPages(const QList& pages) { beginResetModel(); m_pages = pages; endResetModel(); } - const QList &pages() const - { - return m_pages; - } + const QList& pages() const { return m_pages; } - BasePage * findPageEntryById(QString id) + BasePage* findPageEntryById(QString id) { - for(auto page: m_pages) - { + for (auto page : m_pages) { if (page->id() == id) return page; } return nullptr; } - QList m_pages; + QList m_pages; QIcon m_emptyIcon; }; -class PageView : public QListView -{ -public: - PageView(QWidget *parent = 0) : QListView(parent) +class PageView : public QListView { + public: + PageView(QWidget* parent = 0) : QListView(parent) { setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding); setItemDelegate(new PageViewDelegate(this)); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + // Adjust margins when using Breeze theme + setProperty("_kde_side_panel_view", true); } virtual QSize sizeHint() const @@ -113,10 +101,9 @@ public: return QSize(width, 100); } - virtual bool eventFilter(QObject *obj, QEvent *event) + virtual bool eventFilter(QObject* obj, QEvent* event) { - if (obj == verticalScrollBar() && - (event->type() == QEvent::Show || event->type() == QEvent::Hide)) + if (obj == verticalScrollBar() && (event->type() == QEvent::Show || event->type() == QEvent::Hide)) updateGeometry(); return QListView::eventFilter(obj, event); } diff --git a/launcher/ui/widgets/ProjectItem.cpp b/launcher/ui/widgets/ProjectItem.cpp index 0085d6b2b..1481c1b6b 100644 --- a/launcher/ui/widgets/ProjectItem.cpp +++ b/launcher/ui/widgets/ProjectItem.cpp @@ -116,7 +116,6 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o } int description_x = rect.x(); - // Have the y-value be set based on the number of lines in the description, to centralize the // description text with the space between the base and the title. @@ -127,8 +126,8 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o description_y -= opt.fontMetrics.height(); // On the bottom, aligned to the left after the icon, and featuring at most two lines of text (with some margin space to spare) - painter->drawText(description_x, description_y, remaining_width, - cut_text.size() * opt.fontMetrics.height(), Qt::TextWordWrap, description); + painter->drawText(description_x, description_y, remaining_width, cut_text.size() * opt.fontMetrics.height(), Qt::TextWordWrap, + description); } painter->restore(); diff --git a/launcher/ui/widgets/ProjectItem.h b/launcher/ui/widgets/ProjectItem.h index 196055ea4..c3d0dce70 100644 --- a/launcher/ui/widgets/ProjectItem.h +++ b/launcher/ui/widgets/ProjectItem.h @@ -18,9 +18,8 @@ enum UserDataTypes { class ProjectItemDelegate final : public QStyledItemDelegate { Q_OBJECT - public: - ProjectItemDelegate(QWidget* parent); - - void paint(QPainter*, const QStyleOptionViewItem&, const QModelIndex&) const override; + public: + ProjectItemDelegate(QWidget* parent); + void paint(QPainter*, const QStyleOptionViewItem&, const QModelIndex&) const override; }; diff --git a/launcher/ui/widgets/SubTaskProgressBar.cpp b/launcher/ui/widgets/SubTaskProgressBar.cpp index 84ea5f207..b0e62e0f5 100644 --- a/launcher/ui/widgets/SubTaskProgressBar.cpp +++ b/launcher/ui/widgets/SubTaskProgressBar.cpp @@ -26,12 +26,11 @@ unique_qobject_ptr SubTaskProgressBar::create(QWidget* paren return unique_qobject_ptr(progress_bar); } -SubTaskProgressBar::SubTaskProgressBar(QWidget* parent) - : ui(new Ui::SubTaskProgressBar) +SubTaskProgressBar::SubTaskProgressBar(QWidget* parent) : QWidget(parent), ui(new Ui::SubTaskProgressBar) { ui->setupUi(this); } -SubTaskProgressBar::~SubTaskProgressBar() +SubTaskProgressBar::~SubTaskProgressBar() { delete ui; } @@ -55,4 +54,3 @@ void SubTaskProgressBar::setDetails(QString details) { ui->statusDetailsLabel->setText(details); } - diff --git a/launcher/ui/widgets/SubTaskProgressBar.h b/launcher/ui/widgets/SubTaskProgressBar.h index 8f8aeea2a..cd08809f1 100644 --- a/launcher/ui/widgets/SubTaskProgressBar.h +++ b/launcher/ui/widgets/SubTaskProgressBar.h @@ -25,11 +25,10 @@ namespace Ui { class SubTaskProgressBar; } -class SubTaskProgressBar : public QWidget -{ +class SubTaskProgressBar : public QWidget { Q_OBJECT -public: + public: static unique_qobject_ptr create(QWidget* parent = nullptr); SubTaskProgressBar(QWidget* parent = nullptr); @@ -40,9 +39,6 @@ public: void setStatus(QString status); void setDetails(QString details); - - -private: + private: Ui::SubTaskProgressBar* ui; - }; diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.cpp b/launcher/ui/widgets/ThemeCustomizationWidget.cpp index dcf13303c..0de97441f 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.cpp +++ b/launcher/ui/widgets/ThemeCustomizationWidget.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Tayou + * Copyright (C) 2022 Tayou * * 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 @@ -19,17 +19,26 @@ #include "ui_ThemeCustomizationWidget.h" #include "Application.h" +#include "DesktopServices.h" #include "ui/themes/ITheme.h" #include "ui/themes/ThemeManager.h" -ThemeCustomizationWidget::ThemeCustomizationWidget(QWidget *parent) : QWidget(parent), ui(new Ui::ThemeCustomizationWidget) +ThemeCustomizationWidget::ThemeCustomizationWidget(QWidget* parent) : QWidget(parent), ui(new Ui::ThemeCustomizationWidget) { ui->setupUi(this); loadSettings(); connect(ui->iconsComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyIconTheme); - connect(ui->widgetStyleComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyWidgetTheme); + connect(ui->widgetStyleComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, + &ThemeCustomizationWidget::applyWidgetTheme); connect(ui->backgroundCatComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyCatTheme); + + connect(ui->iconsFolder, &QPushButton::clicked, this, + [] { DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path()); }); + connect(ui->widgetStyleFolder, &QPushButton::clicked, this, + [] { DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path()); }); + connect(ui->catPackFolder, &QPushButton::clicked, this, + [] { DesktopServices::openDirectory(APPLICATION->themeManager()->getCatPacksFolder().path()); }); } ThemeCustomizationWidget::~ThemeCustomizationWidget() @@ -40,7 +49,7 @@ ThemeCustomizationWidget::~ThemeCustomizationWidget() /// /// The layout was not quite right, so currently this just disables the UI elements, which should be hidden instead /// TODO FIXME -/// +/// /// Original Method One: /// ui->iconsComboBox->setVisible(features& ThemeFields::ICONS); /// ui->iconsLabel->setVisible(features& ThemeFields::ICONS); @@ -48,7 +57,7 @@ ThemeCustomizationWidget::~ThemeCustomizationWidget() /// ui->widgetThemeLabel->setVisible(features& ThemeFields::WIDGETS); /// ui->backgroundCatComboBox->setVisible(features& ThemeFields::CAT); /// ui->backgroundCatLabel->setVisible(features& ThemeFields::CAT); -/// +/// /// original Method Two: /// if (!(features & ThemeFields::ICONS)) { /// ui->formLayout->setRowVisible(0, false); @@ -61,43 +70,50 @@ ThemeCustomizationWidget::~ThemeCustomizationWidget() /// } /// /// -void ThemeCustomizationWidget::showFeatures(ThemeFields features) { +void ThemeCustomizationWidget::showFeatures(ThemeFields features) +{ ui->iconsComboBox->setEnabled(features & ThemeFields::ICONS); ui->iconsLabel->setEnabled(features & ThemeFields::ICONS); ui->widgetStyleComboBox->setEnabled(features & ThemeFields::WIDGETS); - ui->widgetThemeLabel->setEnabled(features & ThemeFields::WIDGETS); + ui->widgetStyleLabel->setEnabled(features & ThemeFields::WIDGETS); ui->backgroundCatComboBox->setEnabled(features & ThemeFields::CAT); ui->backgroundCatLabel->setEnabled(features & ThemeFields::CAT); } -void ThemeCustomizationWidget::applyIconTheme(int index) { +void ThemeCustomizationWidget::applyIconTheme(int index) +{ auto settings = APPLICATION->settings(); auto originalIconTheme = settings->get("IconTheme").toString(); - auto& newIconTheme = m_iconThemeOptions[index].first; - settings->set("IconTheme", newIconTheme); - + auto newIconTheme = ui->iconsComboBox->currentData().toString(); if (originalIconTheme != newIconTheme) { - APPLICATION->applyCurrentlySelectedTheme(); + settings->set("IconTheme", newIconTheme); + APPLICATION->themeManager()->applyCurrentlySelectedTheme(); } emit currentIconThemeChanged(index); } -void ThemeCustomizationWidget::applyWidgetTheme(int index) { +void ThemeCustomizationWidget::applyWidgetTheme(int index) +{ auto settings = APPLICATION->settings(); auto originalAppTheme = settings->get("ApplicationTheme").toString(); auto newAppTheme = ui->widgetStyleComboBox->currentData().toString(); if (originalAppTheme != newAppTheme) { settings->set("ApplicationTheme", newAppTheme); - APPLICATION->applyCurrentlySelectedTheme(); + APPLICATION->themeManager()->applyCurrentlySelectedTheme(); } emit currentWidgetThemeChanged(index); } -void ThemeCustomizationWidget::applyCatTheme(int index) { +void ThemeCustomizationWidget::applyCatTheme(int index) +{ auto settings = APPLICATION->settings(); - settings->set("BackgroundCat", m_catOptions[index].first); + auto originalCat = settings->get("BackgroundCat").toString(); + auto newCat = ui->backgroundCatComboBox->currentData().toString(); + if (originalCat != newCat) { + settings->set("BackgroundCat", newCat); + } emit currentCatChanged(index); } @@ -112,18 +128,23 @@ void ThemeCustomizationWidget::loadSettings() { auto settings = APPLICATION->settings(); - auto iconTheme = settings->get("IconTheme").toString(); - for (auto& iconThemeFromList : m_iconThemeOptions) { - QIcon iconForComboBox = QIcon(QString(":/icons/%1/scalable/settings").arg(iconThemeFromList.first)); - ui->iconsComboBox->addItem(iconForComboBox, iconThemeFromList.second); - if (iconTheme == iconThemeFromList.first) { - ui->iconsComboBox->setCurrentIndex(ui->iconsComboBox->count() - 1); + { + auto currentIconTheme = settings->get("IconTheme").toString(); + auto iconThemes = APPLICATION->themeManager()->getValidIconThemes(); + int idx = 0; + for (auto iconTheme : iconThemes) { + QIcon iconForComboBox = QIcon(iconTheme->path() + "/scalable/settings"); + ui->iconsComboBox->addItem(iconForComboBox, iconTheme->name(), iconTheme->id()); + if (currentIconTheme == iconTheme->id()) { + ui->iconsComboBox->setCurrentIndex(idx); + } + idx++; } } { auto currentTheme = settings->get("ApplicationTheme").toString(); - auto themes = APPLICATION->getValidApplicationThemes(); + auto themes = APPLICATION->themeManager()->getValidApplicationThemes(); int idx = 0; for (auto& theme : themes) { ui->widgetStyleComboBox->addItem(theme->name(), theme->id()); @@ -135,10 +156,10 @@ void ThemeCustomizationWidget::loadSettings() } auto cat = settings->get("BackgroundCat").toString(); - for (auto& catFromList : m_catOptions) { - QIcon catIcon = QIcon(QString(":/backgrounds/%1").arg(ThemeManager::getCatImage(catFromList.first))); - ui->backgroundCatComboBox->addItem(catIcon, catFromList.second); - if (cat == catFromList.first) { + for (auto& catFromList : APPLICATION->themeManager()->getValidCatPacks()) { + QIcon catIcon = QIcon(QString("%1").arg(catFromList->path())); + ui->backgroundCatComboBox->addItem(catIcon, catFromList->name(), catFromList->id()); + if (cat == catFromList->id()) { ui->backgroundCatComboBox->setCurrentIndex(ui->backgroundCatComboBox->count() - 1); } } diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.h b/launcher/ui/widgets/ThemeCustomizationWidget.h index d955a2665..cef5fb6c6 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.h +++ b/launcher/ui/widgets/ThemeCustomizationWidget.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Tayou + * Copyright (C) 2022 Tayou * * 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 @@ -31,7 +31,7 @@ class ThemeCustomizationWidget : public QWidget { public: explicit ThemeCustomizationWidget(QWidget* parent = nullptr); - ~ThemeCustomizationWidget(); + ~ThemeCustomizationWidget() override; void showFeatures(ThemeFields features); @@ -52,26 +52,4 @@ class ThemeCustomizationWidget : public QWidget { private: Ui::ThemeCustomizationWidget* ui; - - //TODO finish implementing - QList> m_iconThemeOptions{ - { "pe_colored", QObject::tr("Simple (Colored Icons)") }, - { "pe_light", QObject::tr("Simple (Light Icons)") }, - { "pe_dark", QObject::tr("Simple (Dark Icons)") }, - { "pe_blue", QObject::tr("Simple (Blue Icons)") }, - { "breeze_light", QObject::tr("Breeze Light") }, - { "breeze_dark", QObject::tr("Breeze Dark") }, - { "OSX", QObject::tr("OSX") }, - { "iOS", QObject::tr("iOS") }, - { "flat", QObject::tr("Flat") }, - { "flat_white", QObject::tr("Flat (White)") }, - { "multimc", QObject::tr("Legacy") }, - { "custom", QObject::tr("Custom") } - }; - QList> m_catOptions{ - { "kitteh", QObject::tr("Background Cat (from MultiMC)") }, - { "rory", QObject::tr("Rory ID 11 (drawn by Ashtaka)") }, - { "rory-flat", QObject::tr("Rory ID 11 (flat edition, drawn by Ashtaka)") }, - { "teawie", QObject::tr("Teawie (drawn by SympathyTea)") } - }; }; diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.ui b/launcher/ui/widgets/ThemeCustomizationWidget.ui index f216a610e..4503181c2 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.ui +++ b/launcher/ui/widgets/ThemeCustomizationWidget.ui @@ -40,22 +40,43 @@
    - - - - 0 - 0 - - - - Qt::StrongFocus - - + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + + + + + View icon themes folder. + + + + + + + .. + + + true + + + + - + - &Colors + &Widgets widgetStyleComboBox @@ -63,17 +84,38 @@ - - - - 0 - 0 - - - - Qt::StrongFocus - - + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + + + + + View widget themes folder. + + + + + + + .. + + + true + + + + @@ -89,7 +131,7 @@ - + @@ -107,15 +149,15 @@ - + - The cat appears in the background and is not shown by default. It is only made visible when pressing the Cat button in the Toolbar. + View cat packs folder. - + .. diff --git a/launcher/ui/widgets/VariableSizedImageObject.cpp b/launcher/ui/widgets/VariableSizedImageObject.cpp index 991b4a047..f655fc38d 100644 --- a/launcher/ui/widgets/VariableSizedImageObject.cpp +++ b/launcher/ui/widgets/VariableSizedImageObject.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify @@ -25,6 +25,7 @@ #include "Application.h" +#include "net/ApiDownload.h" #include "net/NetJob.h" enum FormatProperties { ImageData = QTextFormat::UserProperty + 1 }; @@ -97,7 +98,7 @@ void VariableSizedImageObject::loadImage(QTextDocument* doc, const QUrl& source, QString("images/%1").arg(QString(QCryptographicHash::hash(source.toEncoded(), QCryptographicHash::Algorithm::Sha1).toHex()))); auto job = new NetJob(QString("Load Image: %1").arg(source.fileName()), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(source, entry)); + job->addNetAction(Net::ApiDownload::makeCached(source, entry)); auto full_entry_path = entry->getFullPath(); auto source_url = source; diff --git a/launcher/ui/widgets/VariableSizedImageObject.h b/launcher/ui/widgets/VariableSizedImageObject.h index 137487eee..ca67af0c9 100644 --- a/launcher/ui/widgets/VariableSizedImageObject.h +++ b/launcher/ui/widgets/VariableSizedImageObject.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/ui/widgets/VersionListView.cpp b/launcher/ui/widgets/VersionListView.cpp index 06e58d221..7701d1271 100644 --- a/launcher/ui/widgets/VersionListView.cpp +++ b/launcher/ui/widgets/VersionListView.cpp @@ -34,35 +34,33 @@ * limitations under the License. */ -#include -#include -#include -#include -#include #include "VersionListView.h" +#include +#include +#include +#include +#include -VersionListView::VersionListView(QWidget *parent) - :QTreeView ( parent ) +VersionListView::VersionListView(QWidget* parent) : QTreeView(parent) { m_emptyString = tr("No versions are currently available."); } -void VersionListView::rowsInserted(const QModelIndex &parent, int start, int end) +void VersionListView::rowsInserted(const QModelIndex& parent, int start, int end) { - m_itemCount += end-start+1; + m_itemCount += end - start + 1; updateEmptyViewPort(); QTreeView::rowsInserted(parent, start, end); } - -void VersionListView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +void VersionListView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) { - m_itemCount -= end-start+1; + m_itemCount -= end - start + 1; updateEmptyViewPort(); QTreeView::rowsInserted(parent, start, end); } -void VersionListView::setModel(QAbstractItemModel *model) +void VersionListView::setModel(QAbstractItemModel* model) { m_itemCount = model->rowCount(); updateEmptyViewPort(); @@ -71,11 +69,9 @@ void VersionListView::setModel(QAbstractItemModel *model) void VersionListView::reset() { - if(model()) - { + if (model()) { m_itemCount = model()->rowCount(); - } - else { + } else { m_itemCount = 0; } updateEmptyViewPort(); @@ -106,28 +102,23 @@ void VersionListView::updateEmptyViewPort() setAccessibleDescription(currentEmptyString()); #endif /* !QT_NO_ACCESSIBILITY */ - if(!m_itemCount) - { + if (!m_itemCount) { viewport()->update(); } } -void VersionListView::paintEvent(QPaintEvent *event) +void VersionListView::paintEvent(QPaintEvent* event) { - if(m_itemCount) - { + if (m_itemCount) { QTreeView::paintEvent(event); - } - else - { + } else { paintInfoLabel(event); } } QString VersionListView::currentEmptyString() const { - switch(m_emptyMode) - { + switch (m_emptyMode) { default: case VersionListView::String: return m_emptyString; @@ -136,12 +127,11 @@ QString VersionListView::currentEmptyString() const } } - -void VersionListView::paintInfoLabel(QPaintEvent *event) const +void VersionListView::paintInfoLabel(QPaintEvent* event) const { QString emptyString = currentEmptyString(); - //calculate the rect for the overlay + // calculate the rect for the overlay QPainter painter(viewport()); painter.setRenderHint(QPainter::Antialiasing, true); QFont font("sans", 20); @@ -163,7 +153,7 @@ void VersionListView::paintInfoLabel(QPaintEvent *event) const auto wrapRect = textRect; wrapRect.adjust(-10, -10, 10, 10); - //check if we are allowed to draw in our area + // check if we are allowed to draw in our area if (!event->rect().intersects(wrapRect)) { return; } diff --git a/launcher/ui/widgets/VersionListView.h b/launcher/ui/widgets/VersionListView.h index 4153b314a..07792db52 100644 --- a/launcher/ui/widgets/VersionListView.h +++ b/launcher/ui/widgets/VersionListView.h @@ -16,39 +16,32 @@ #pragma once #include -class VersionListView : public QTreeView -{ +class VersionListView : public QTreeView { Q_OBJECT -public: - - explicit VersionListView(QWidget *parent = 0); - virtual void paintEvent(QPaintEvent *event) override; + public: + explicit VersionListView(QWidget* parent = 0); + virtual void paintEvent(QPaintEvent* event) override; virtual void setModel(QAbstractItemModel* model) override; - enum EmptyMode - { - Empty, - String, - ErrorString - }; + enum EmptyMode { Empty, String, ErrorString }; void setEmptyString(QString emptyString); void setEmptyErrorString(QString emptyErrorString); void setEmptyMode(EmptyMode mode); -public slots: + public slots: virtual void reset() override; -protected slots: - virtual void rowsAboutToBeRemoved(const QModelIndex & parent, int start, int end) override; - virtual void rowsInserted(const QModelIndex &parent, int start, int end) override; + protected slots: + virtual void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override; + virtual void rowsInserted(const QModelIndex& parent, int start, int end) override; -private: /* methods */ - void paintInfoLabel(QPaintEvent *event) const; + private: /* methods */ + void paintInfoLabel(QPaintEvent* event) const; void updateEmptyViewPort(); QString currentEmptyString() const; -private: /* variables */ + private: /* variables */ int m_itemCount = 0; QString m_emptyString; QString m_emptyErrorString; diff --git a/launcher/ui/widgets/VersionSelectWidget.cpp b/launcher/ui/widgets/VersionSelectWidget.cpp index a956ddb3c..a24630b31 100644 --- a/launcher/ui/widgets/VersionSelectWidget.cpp +++ b/launcher/ui/widgets/VersionSelectWidget.cpp @@ -11,10 +11,7 @@ #include "ui/dialogs/CustomMessageBox.h" -VersionSelectWidget::VersionSelectWidget(QWidget* parent) : VersionSelectWidget(false, parent) {} - -VersionSelectWidget::VersionSelectWidget(bool focusSearch, QWidget* parent) - : QWidget(parent), focusSearch(focusSearch) +VersionSelectWidget::VersionSelectWidget(QWidget* parent) : QWidget(parent) { setObjectName(QStringLiteral("VersionSelectWidget")); verticalLayout = new QVBoxLayout(this); @@ -81,9 +78,7 @@ void VersionSelectWidget::setEmptyMode(VersionListView::EmptyMode mode) listView->setEmptyMode(mode); } -VersionSelectWidget::~VersionSelectWidget() -{ -} +VersionSelectWidget::~VersionSelectWidget() {} void VersionSelectWidget::setResizeOn(int column) { @@ -92,7 +87,8 @@ void VersionSelectWidget::setResizeOn(int column) listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch); } -bool VersionSelectWidget::eventFilter(QObject *watched, QEvent *event) { +bool VersionSelectWidget::eventFilter(QObject* watched, QEvent* event) +{ if (watched == search && event->type() == QEvent::KeyPress) { const QKeyEvent* keyEvent = (QKeyEvent*)event; const bool up = keyEvent->key() == Qt::Key_Up; @@ -109,31 +105,24 @@ bool VersionSelectWidget::eventFilter(QObject *watched, QEvent *event) { return QObject::eventFilter(watched, event); } -void VersionSelectWidget::initialize(BaseVersionList *vlist) +void VersionSelectWidget::initialize(BaseVersionList* vlist) { m_vlist = vlist; m_proxyModel->setSourceModel(vlist); listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch); - if (focusSearch) - search->setFocus(); - - if (!m_vlist->isLoaded()) - { + if (!m_vlist->isLoaded()) { loadList(); - } - else - { - if (m_proxyModel->rowCount() == 0) - { + } else { + if (m_proxyModel->rowCount() == 0) { listView->setEmptyMode(VersionListView::String); } preselect(); } } -void VersionSelectWidget::closeEvent(QCloseEvent * event) +void VersionSelectWidget::closeEvent(QCloseEvent* event) { QWidget::closeEvent(event); } @@ -141,16 +130,14 @@ void VersionSelectWidget::closeEvent(QCloseEvent * event) void VersionSelectWidget::loadList() { auto newTask = m_vlist->getLoadTask(); - if (!newTask) - { + if (!newTask) { return; } loadTask = newTask.get(); connect(loadTask, &Task::succeeded, this, &VersionSelectWidget::onTaskSucceeded); connect(loadTask, &Task::failed, this, &VersionSelectWidget::onTaskFailed); connect(loadTask, &Task::progress, this, &VersionSelectWidget::changeProgress); - if(!loadTask->isRunning()) - { + if (!loadTask->isRunning()) { loadTask->start(); } sneakyProgressBar->setHidden(false); @@ -158,8 +145,7 @@ void VersionSelectWidget::loadList() void VersionSelectWidget::onTaskSucceeded() { - if (m_proxyModel->rowCount() == 0) - { + if (m_proxyModel->rowCount() == 0) { listView->setEmptyMode(VersionListView::String); } sneakyProgressBar->setHidden(true); @@ -187,36 +173,43 @@ void VersionSelectWidget::currentRowChanged(const QModelIndex& current, const QM void VersionSelectWidget::preselect() { - if(preselectedAlready) + if (preselectedAlready) return; selectCurrent(); - if(preselectedAlready) + if (preselectedAlready) return; selectRecommended(); } void VersionSelectWidget::selectCurrent() { - if(m_currentVersion.isEmpty()) - { + if (m_currentVersion.isEmpty()) { return; } auto idx = m_proxyModel->getVersion(m_currentVersion); - if(idx.isValid()) - { + if (idx.isValid()) { preselectedAlready = true; - listView->selectionModel()->setCurrentIndex(idx,QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + listView->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); listView->scrollTo(idx, QAbstractItemView::PositionAtCenter); } } +void VersionSelectWidget::selectSearch() +{ + search->setFocus(); +} + +VersionListView* VersionSelectWidget::view() +{ + return listView; +} + void VersionSelectWidget::selectRecommended() { auto idx = m_proxyModel->getRecommended(); - if(idx.isValid()) - { + if (idx.isValid()) { preselectedAlready = true; - listView->selectionModel()->setCurrentIndex(idx,QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + listView->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); listView->scrollTo(idx, QAbstractItemView::PositionAtCenter); } } @@ -233,17 +226,22 @@ BaseVersion::Ptr VersionSelectWidget::selectedVersion() const return variant.value(); } -void VersionSelectWidget::setExactFilter(BaseVersionList::ModelRoles role, QString filter) -{ - m_proxyModel->setFilter(role, new ExactFilter(filter)); -} - void VersionSelectWidget::setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter) { m_proxyModel->setFilter(role, new ContainsFilter(filter)); } -void VersionSelectWidget::setFilter(BaseVersionList::ModelRoles role, Filter *filter) +void VersionSelectWidget::setExactFilter(BaseVersionList::ModelRoles role, QString filter) +{ + m_proxyModel->setFilter(role, new ExactFilter(filter)); +} + +void VersionSelectWidget::setExactIfPresentFilter(BaseVersionList::ModelRoles role, QString filter) +{ + m_proxyModel->setFilter(role, new ExactIfPresentFilter(filter)); +} + +void VersionSelectWidget::setFilter(BaseVersionList::ModelRoles role, Filter* filter) { m_proxyModel->setFilter(role, filter); } diff --git a/launcher/ui/widgets/VersionSelectWidget.h b/launcher/ui/widgets/VersionSelectWidget.h index be4ba768d..99729fbdf 100644 --- a/launcher/ui/widgets/VersionSelectWidget.h +++ b/launcher/ui/widgets/VersionSelectWidget.h @@ -35,9 +35,9 @@ #pragma once -#include -#include #include +#include +#include #include "BaseVersionList.h" #include "VersionListView.h" @@ -47,16 +47,14 @@ class QVBoxLayout; class QProgressBar; class Filter; -class VersionSelectWidget: public QWidget -{ +class VersionSelectWidget : public QWidget { Q_OBJECT -public: - explicit VersionSelectWidget(QWidget *parent); - explicit VersionSelectWidget(bool focusSearch = false, QWidget *parent = 0); + public: + explicit VersionSelectWidget(QWidget* parent); ~VersionSelectWidget(); //! loads the list if needed. - void initialize(BaseVersionList *vlist); + void initialize(BaseVersionList* vlist); //! Starts a task that loads the list. void loadList(); @@ -65,43 +63,46 @@ public: BaseVersion::Ptr selectedVersion() const; void selectRecommended(); void selectCurrent(); + void selectSearch(); + VersionListView* view(); - void setCurrentVersion(const QString & version); + void setCurrentVersion(const QString& version); void setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter); void setExactFilter(BaseVersionList::ModelRoles role, QString filter); - void setFilter(BaseVersionList::ModelRoles role, Filter *filter); + void setExactIfPresentFilter(BaseVersionList::ModelRoles role, QString filter); + void setFilter(BaseVersionList::ModelRoles role, Filter* filter); void setEmptyString(QString emptyString); void setEmptyErrorString(QString emptyErrorString); void setEmptyMode(VersionListView::EmptyMode mode); void setResizeOn(int column); + bool eventFilter(QObject* watched, QEvent* event) override; -signals: + signals: void selectedVersionChanged(BaseVersion::Ptr version); -protected: - virtual void closeEvent ( QCloseEvent* ); + protected: + virtual void closeEvent(QCloseEvent*); -private slots: + private slots: void onTaskSucceeded(); - void onTaskFailed(const QString &reason); + void onTaskFailed(const QString& reason); void changeProgress(qint64 current, qint64 total); - void currentRowChanged(const QModelIndex ¤t, const QModelIndex &); + void currentRowChanged(const QModelIndex& current, const QModelIndex&); -private: + private: void preselect(); -private: + private: QString m_currentVersion; - BaseVersionList *m_vlist = nullptr; - VersionProxyModel *m_proxyModel = nullptr; + BaseVersionList* m_vlist = nullptr; + VersionProxyModel* m_proxyModel = nullptr; int resizeOnColumn = 0; - Task * loadTask; + Task* loadTask; bool preselectedAlready = false; - bool focusSearch; - QVBoxLayout *verticalLayout = nullptr; - VersionListView *listView = nullptr; - QLineEdit *search; - QProgressBar *sneakyProgressBar = nullptr; + QVBoxLayout* verticalLayout = nullptr; + VersionListView* listView = nullptr; + QLineEdit* search; + QProgressBar* sneakyProgressBar = nullptr; }; diff --git a/launcher/ui/widgets/WideBar.cpp b/launcher/ui/widgets/WideBar.cpp index a77c45fee..46caaaef2 100644 --- a/launcher/ui/widgets/WideBar.cpp +++ b/launcher/ui/widgets/WideBar.cpp @@ -7,8 +7,8 @@ class ActionButton : public QToolButton { Q_OBJECT public: - ActionButton(QAction* action, QWidget* parent = nullptr, bool use_default_action = false) : QToolButton(parent), - m_action(action), m_use_default_action(use_default_action) + ActionButton(QAction* action, QWidget* parent = nullptr, bool use_default_action = false) + : QToolButton(parent), m_action(action), m_use_default_action(use_default_action) { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); setToolButtonStyle(Qt::ToolButtonTextBesideIcon); @@ -204,8 +204,10 @@ static void copyAction(QAction* from, QAction* to) void WideBar::showVisibilityMenu(QPoint const& position) { - if (!m_bar_menu) + if (!m_bar_menu) { m_bar_menu = std::make_unique(this); + m_bar_menu->setTearOffEnabled(true); + } if (m_menu_state == MenuState::Dirty) { for (auto* old_action : m_bar_menu->actions()) @@ -227,7 +229,7 @@ void WideBar::showVisibilityMenu(QPoint const& position) act->setCheckable(true); act->setChecked(entry.bar_action->isVisible()); - connect(act, &QAction::toggled, entry.bar_action, [this, &entry](bool toggled){ + connect(act, &QAction::toggled, entry.bar_action, [this, &entry](bool toggled) { entry.bar_action->setVisible(toggled); // NOTE: This is needed so that disabled actions get reflected on the button when it is made visible. @@ -243,7 +245,8 @@ void WideBar::showVisibilityMenu(QPoint const& position) m_bar_menu->popup(mapToGlobal(position)); } -void WideBar::addContextMenuAction(QAction* action) { +void WideBar::addContextMenuAction(QAction* action) +{ m_context_menu_actions.append(action); } @@ -306,5 +309,4 @@ bool WideBar::checkHash(QByteArray const& old_hash) const return old_hash == getHash(); } - #include "WideBar.moc" diff --git a/launcher/updater/ExternalUpdater.h b/launcher/updater/ExternalUpdater.h index a053e081f..daa5b1095 100644 --- a/launcher/updater/ExternalUpdater.h +++ b/launcher/updater/ExternalUpdater.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Kenneth Chew * * This program is free software: you can redistribute it and/or modify @@ -31,11 +31,10 @@ * The initializer of the new class should have the side effect of starting the automatic updater. That is, * once the class is initialized, the program should automatically check for updates if necessary. */ -class ExternalUpdater : public QObject -{ +class ExternalUpdater : public QObject { Q_OBJECT -public: + public: /*! * Check for updates manually, showing the user a progress bar and an alert if no updates are found. */ @@ -71,7 +70,7 @@ public: */ virtual void setBetaAllowed(bool allowed) = 0; -signals: + signals: /*! * Emits whenever the user's ability to check for updates changes. * @@ -84,4 +83,4 @@ signals: void canCheckForUpdatesChanged(bool canCheck); }; -#endif //LAUNCHER_EXTERNALUPDATER_H +#endif // LAUNCHER_EXTERNALUPDATER_H diff --git a/launcher/updater/MacSparkleUpdater.h b/launcher/updater/MacSparkleUpdater.h index cee19f7c7..5f9b1a28c 100644 --- a/launcher/updater/MacSparkleUpdater.h +++ b/launcher/updater/MacSparkleUpdater.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Kenneth Chew * * This program is free software: you can redistribute it and/or modify @@ -26,11 +26,10 @@ /*! * An implementation for the updater on macOS that uses the Sparkle framework. */ -class MacSparkleUpdater : public ExternalUpdater -{ +class MacSparkleUpdater : public ExternalUpdater { Q_OBJECT -public: + public: /*! * Start the Sparkle updater, which automatically checks for updates if necessary. */ @@ -115,10 +114,10 @@ public: */ void setBetaAllowed(bool allowed) override; -private: + private: class Private; - Private *priv; + Private* priv; }; -#endif //LAUNCHER_MACSPARKLEUPDATER_H +#endif // LAUNCHER_MACSPARKLEUPDATER_H diff --git a/launcher/updater/MacSparkleUpdater.mm b/launcher/updater/MacSparkleUpdater.mm index 07337176d..b2b631593 100644 --- a/launcher/updater/MacSparkleUpdater.mm +++ b/launcher/updater/MacSparkleUpdater.mm @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Kenneth Chew * * This program is free software: you can redistribute it and/or modify @@ -28,7 +28,7 @@ @property(nonatomic, readonly) SPUUpdater* updater; /// A callback to run when the state of `canCheckForUpdates` for the `updater` changes. -@property(nonatomic, copy) void (^callback) (bool); +@property(nonatomic, copy) void (^callback)(bool); - (id)initWithUpdater:(SPUUpdater*)updater; @@ -36,8 +36,7 @@ @implementation UpdaterObserver -- (id)initWithUpdater:(SPUUpdater*)updater -{ +- (id)initWithUpdater:(SPUUpdater*)updater { self = [super init]; _updater = updater; [self addObserver:self forKeyPath:@"updater.canCheckForUpdates" options:NSKeyValueObservingOptionNew context:nil]; @@ -45,13 +44,11 @@ return self; } -- (void)observeValueForKeyPath:(NSString *)keyPath +- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object - change:(NSDictionary *)change - context:(void *)context -{ - if ([keyPath isEqualToString:@"updater.canCheckForUpdates"]) - { + change:(NSDictionary*)change + context:(void*)context { + if ([keyPath isEqualToString:@"updater.canCheckForUpdates"]) { bool canCheck = [change[NSKeyValueChangeNewKey] boolValue]; self.callback(canCheck); } @@ -59,34 +56,29 @@ @end - @interface UpdaterDelegate : NSObject -@property(nonatomic, copy) NSSet *allowedChannels; +@property(nonatomic, copy) NSSet* allowedChannels; @end @implementation UpdaterDelegate -- (NSSet *)allowedChannelsForUpdater:(SPUUpdater *)updater -{ +- (NSSet*)allowedChannelsForUpdater:(SPUUpdater*)updater { return _allowedChannels; } @end - -class MacSparkleUpdater::Private -{ -public: - SPUStandardUpdaterController *updaterController; - UpdaterObserver *updaterObserver; - UpdaterDelegate *updaterDelegate; - NSAutoreleasePool *autoReleasePool; +class MacSparkleUpdater::Private { + public: + SPUStandardUpdaterController* updaterController; + UpdaterObserver* updaterObserver; + UpdaterDelegate* updaterDelegate; + NSAutoreleasePool* autoReleasePool; }; -MacSparkleUpdater::MacSparkleUpdater() -{ +MacSparkleUpdater::MacSparkleUpdater() { priv = new MacSparkleUpdater::Private(); // Enable Cocoa's memory management. @@ -98,18 +90,17 @@ MacSparkleUpdater::MacSparkleUpdater() // Controller is the interface for actually doing the updates. priv->updaterController = [[SPUStandardUpdaterController alloc] initWithStartingUpdater:true - updaterDelegate:priv->updaterDelegate - userDriverDelegate:nil]; + updaterDelegate:priv->updaterDelegate + userDriverDelegate:nil]; priv->updaterObserver = [[UpdaterObserver alloc] initWithUpdater:priv->updaterController.updater]; // Use KVO to run a callback that emits a Qt signal when `canCheckForUpdates` changes, so the UI can respond accordingly. priv->updaterObserver.callback = ^(bool canCheck) { - emit canCheckForUpdatesChanged(canCheck); + emit canCheckForUpdatesChanged(canCheck); }; } -MacSparkleUpdater::~MacSparkleUpdater() -{ +MacSparkleUpdater::~MacSparkleUpdater() { [priv->updaterObserver removeObserver:priv->updaterObserver forKeyPath:@"updater.canCheckForUpdates"]; [priv->updaterController release]; @@ -119,77 +110,63 @@ MacSparkleUpdater::~MacSparkleUpdater() delete priv; } -void MacSparkleUpdater::checkForUpdates() -{ +void MacSparkleUpdater::checkForUpdates() { [priv->updaterController checkForUpdates:nil]; } -bool MacSparkleUpdater::getAutomaticallyChecksForUpdates() -{ +bool MacSparkleUpdater::getAutomaticallyChecksForUpdates() { return priv->updaterController.updater.automaticallyChecksForUpdates; } -double MacSparkleUpdater::getUpdateCheckInterval() -{ +double MacSparkleUpdater::getUpdateCheckInterval() { return priv->updaterController.updater.updateCheckInterval; } -QSet MacSparkleUpdater::getAllowedChannels() -{ +QSet MacSparkleUpdater::getAllowedChannels() { // Convert NSSet -> QSet __block QSet channels; - [priv->updaterDelegate.allowedChannels enumerateObjectsUsingBlock:^(NSString *channel, BOOL *stop) - { - channels.insert(QString::fromNSString(channel)); + [priv->updaterDelegate.allowedChannels enumerateObjectsUsingBlock:^(NSString* channel, BOOL* stop) { + channels.insert(QString::fromNSString(channel)); }]; return channels; } -bool MacSparkleUpdater::getBetaAllowed() -{ +bool MacSparkleUpdater::getBetaAllowed() { return getAllowedChannels().contains("beta"); } -void MacSparkleUpdater::setAutomaticallyChecksForUpdates(bool check) -{ - priv->updaterController.updater.automaticallyChecksForUpdates = check ? YES : NO; // make clang-tidy happy +void MacSparkleUpdater::setAutomaticallyChecksForUpdates(bool check) { + priv->updaterController.updater.automaticallyChecksForUpdates = check ? YES : NO; // make clang-tidy happy } -void MacSparkleUpdater::setUpdateCheckInterval(double seconds) -{ +void MacSparkleUpdater::setUpdateCheckInterval(double seconds) { priv->updaterController.updater.updateCheckInterval = seconds; } -void MacSparkleUpdater::clearAllowedChannels() -{ +void MacSparkleUpdater::clearAllowedChannels() { priv->updaterDelegate.allowedChannels = [NSSet set]; } -void MacSparkleUpdater::setAllowedChannel(const QString &channel) -{ - if (channel.isEmpty()) - { +void MacSparkleUpdater::setAllowedChannel(const QString& channel) { + if (channel.isEmpty()) { clearAllowedChannels(); return; } - NSSet *nsChannels = [NSSet setWithObject:channel.toNSString()]; + NSSet* nsChannels = [NSSet setWithObject:channel.toNSString()]; priv->updaterDelegate.allowedChannels = nsChannels; } -void MacSparkleUpdater::setAllowedChannels(const QSet &channels) -{ - if (channels.isEmpty()) - { +void MacSparkleUpdater::setAllowedChannels(const QSet& channels) { + if (channels.isEmpty()) { clearAllowedChannels(); return; } QString channelsConfig = ""; // Convert QSet -> NSSet - NSMutableSet *nsChannels = [NSMutableSet setWithCapacity:channels.count()]; - foreach (const QString channel, channels) - { + NSMutableSet* nsChannels = [NSMutableSet setWithCapacity:channels.count()]; + foreach (const QString channel, channels) { [nsChannels addObject:channel.toNSString()]; channelsConfig += channel + " "; } @@ -197,14 +174,10 @@ void MacSparkleUpdater::setAllowedChannels(const QSet &channels) priv->updaterDelegate.allowedChannels = nsChannels; } -void MacSparkleUpdater::setBetaAllowed(bool allowed) -{ - if (allowed) - { +void MacSparkleUpdater::setBetaAllowed(bool allowed) { + if (allowed) { setAllowedChannel("beta"); - } - else - { + } else { clearAllowedChannels(); } } diff --git a/libraries/LocalPeer/include/LocalPeer.h b/libraries/LocalPeer/include/LocalPeer.h index 3619ed5d1..c370102fd 100644 --- a/libraries/LocalPeer/include/LocalPeer.h +++ b/libraries/LocalPeer/include/LocalPeer.h @@ -43,56 +43,46 @@ #include #include - class QLocalServer; class LockedFile; -class ApplicationId -{ -public: /* methods */ +class ApplicationId { + public: /* methods */ // traditional app = installed system wide and used in a multi-user environment static ApplicationId fromTraditionalApp(); // ID based on a path with all the application data (no two instances with the same data path should run) - static ApplicationId fromPathAndVersion(const QString & dataPath, const QString & version); + static ApplicationId fromPathAndVersion(const QString& dataPath, const QString& version); // custom ID - static ApplicationId fromCustomId(const QString & id); + static ApplicationId fromCustomId(const QString& id); // custom ID, based on a raw string previously acquired from 'toString' - static ApplicationId fromRawString(const QString & id); + static ApplicationId fromRawString(const QString& id); + QString toString() { return m_id; } - QString toString() - { - return m_id; - } + private: /* methods */ + ApplicationId(const QString& value) { m_id = value; } -private: /* methods */ - ApplicationId(const QString & value) - { - m_id = value; - } - -private: /* data */ + private: /* data */ QString m_id; }; -class LocalPeer : public QObject -{ +class LocalPeer : public QObject { Q_OBJECT -public: - LocalPeer(QObject *parent, const ApplicationId &appId); + public: + LocalPeer(QObject* parent, const ApplicationId& appId); ~LocalPeer(); bool isClient(); - bool sendMessage(const QByteArray &message, int timeout); + bool sendMessage(const QByteArray& message, int timeout); ApplicationId applicationId() const; -Q_SIGNALS: - void messageReceived(const QByteArray &message); + Q_SIGNALS: + void messageReceived(const QByteArray& message); -protected Q_SLOTS: + protected Q_SLOTS: void receiveConnection(); -protected: + protected: ApplicationId id; QString socketName; std::unique_ptr server; diff --git a/libraries/LocalPeer/src/LocalPeer.cpp b/libraries/LocalPeer/src/LocalPeer.cpp index b7149c408..ab528c2a1 100644 --- a/libraries/LocalPeer/src/LocalPeer.cpp +++ b/libraries/LocalPeer/src/LocalPeer.cpp @@ -38,21 +38,20 @@ ** ****************************************************************************/ - #include "LocalPeer.h" #include #include -#include +#include #include #include -#include #include +#include #include "LockedFile.h" #if defined(Q_OS_WIN) -#include #include -typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); +#include +typedef BOOL(WINAPI* PProcessIdToSessionId)(DWORD, DWORD*); static PProcessIdToSessionId pProcessIdToSessionId = 0; #endif #if defined(Q_OS_UNIX) @@ -60,9 +59,9 @@ static PProcessIdToSessionId pProcessIdToSessionId = 0; #include #endif +#include #include #include -#include static const char* ack = "ack"; @@ -79,13 +78,11 @@ ApplicationId ApplicationId::fromTraditionalApp() quint16 idNum = qChecksum(idc.constData(), idc.size()); auto socketName = QLatin1String("qtsingleapp-") + prefix + QLatin1Char('-') + QString::number(idNum, 16); #if defined(Q_OS_WIN) - if (!pProcessIdToSessionId) - { + if (!pProcessIdToSessionId) { QLibrary lib("kernel32"); pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); } - if (pProcessIdToSessionId) - { + if (pProcessIdToSessionId) { DWORD sessionId = 0; pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); socketName += QLatin1Char('-') + QString::number(sessionId, 16); @@ -114,8 +111,7 @@ ApplicationId ApplicationId::fromRawString(const QString& id) return ApplicationId(id); } -LocalPeer::LocalPeer(QObject * parent, const ApplicationId &appId) - : QObject(parent), id(appId) +LocalPeer::LocalPeer(QObject* parent, const ApplicationId& appId) : QObject(parent), id(appId) { socketName = id.toString(); server.reset(new QLocalServer()); @@ -124,9 +120,7 @@ LocalPeer::LocalPeer(QObject * parent, const ApplicationId &appId) lockFile->open(QIODevice::ReadWrite); } -LocalPeer::~LocalPeer() -{ -} +LocalPeer::~LocalPeer() {} ApplicationId LocalPeer::applicationId() const { @@ -145,7 +139,7 @@ bool LocalPeer::isClient() #if defined(Q_OS_UNIX) // ### Workaround if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { - QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName); + QFile::remove(QDir::cleanPath(QDir::tempPath()) + QLatin1Char('/') + socketName); res = server->listen(socketName); } #endif @@ -155,8 +149,7 @@ bool LocalPeer::isClient() return false; } - -bool LocalPeer::sendMessage(const QByteArray &message, int timeout) +bool LocalPeer::sendMessage(const QByteArray& message, int timeout) { if (!isClient()) return false; @@ -164,17 +157,15 @@ bool LocalPeer::sendMessage(const QByteArray &message, int timeout) QLocalSocket socket; bool connOk = false; int tries = 2; - for(int i = 0; i < tries; i++) { + for (int i = 0; i < tries; i++) { // Try twice, in case the other instance is just starting up socket.connectToServer(socketName); - connOk = socket.waitForConnected(timeout/2); - if (!connOk && i < (tries - 1)) - { + connOk = socket.waitForConnected(timeout / 2); + if (!connOk && i < (tries - 1)) { std::this_thread::sleep_for(std::chrono::milliseconds(250)); } } - if (!connOk) - { + if (!connOk) { return false; } @@ -182,36 +173,30 @@ bool LocalPeer::sendMessage(const QByteArray &message, int timeout) QDataStream ds(&socket); ds.writeBytes(uMsg.constData(), uMsg.size()); - if(!socket.waitForBytesWritten(timeout)) - { + if (!socket.waitForBytesWritten(timeout)) { return false; } // wait for 'ack' - if(!socket.waitForReadyRead(timeout)) - { + if (!socket.waitForReadyRead(timeout)) { return false; } // make sure we got 'ack' - if(!(socket.read(qstrlen(ack)) == ack)) - { + if (!(socket.read(qstrlen(ack)) == ack)) { return false; } return true; } - void LocalPeer::receiveConnection() { QLocalSocket* socket = server->nextPendingConnection(); - if (!socket) - { + if (!socket) { return; } - while (socket->bytesAvailable() < static_cast(sizeof(quint32))) - { + while (socket->bytesAvailable() < static_cast(sizeof(quint32))) { socket->waitForReadyRead(); } QDataStream ds(socket); @@ -221,21 +206,19 @@ void LocalPeer::receiveConnection() uMsg.resize(remaining); int got = 0; char* uMsgBuf = uMsg.data(); - do - { + do { got = ds.readRawData(uMsgBuf, remaining); remaining -= got; uMsgBuf += got; } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); - if (got < 0) - { + if (got < 0) { qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); delete socket; return; } socket->write(ack, qstrlen(ack)); socket->waitForBytesWritten(1000); - socket->waitForDisconnected(1000); // make sure client reads ack + socket->waitForDisconnected(1000); // make sure client reads ack delete socket; - emit messageReceived(uMsg); //### (might take a long time to return) + emit messageReceived(uMsg); //### (might take a long time to return) } diff --git a/libraries/LocalPeer/src/LockedFile.cpp b/libraries/LocalPeer/src/LockedFile.cpp index 73294a16e..e93eabc96 100644 --- a/libraries/LocalPeer/src/LockedFile.cpp +++ b/libraries/LocalPeer/src/LockedFile.cpp @@ -80,8 +80,7 @@ \sa QFile::QFile() */ -LockedFile::LockedFile() - : QFile() +LockedFile::LockedFile() : QFile() { #ifdef Q_OS_WIN wmutex = 0; @@ -97,8 +96,7 @@ LockedFile::LockedFile() \sa QFile::QFile() */ -LockedFile::LockedFile(const QString &name) - : QFile(name) +LockedFile::LockedFile(const QString& name) : QFile(name) { #ifdef Q_OS_WIN wmutex = 0; diff --git a/libraries/LocalPeer/src/LockedFile.h b/libraries/LocalPeer/src/LockedFile.h index 2f29ee201..e8023251c 100644 --- a/libraries/LocalPeer/src/LockedFile.h +++ b/libraries/LocalPeer/src/LockedFile.h @@ -45,13 +45,12 @@ #include #endif -class LockedFile : public QFile -{ -public: +class LockedFile : public QFile { + public: enum LockMode { NoLock = 0, ReadLock, WriteLock }; LockedFile(); - LockedFile(const QString &name); + LockedFile(const QString& name); ~LockedFile(); bool open(OpenMode mode); @@ -61,8 +60,7 @@ public: bool isLocked() const; LockMode lockMode() const; - private: - + private: #ifdef Q_OS_WIN Qt::HANDLE wmutex; Qt::HANDLE rmutex; diff --git a/libraries/LocalPeer/src/LockedFile_unix.cpp b/libraries/LocalPeer/src/LockedFile_unix.cpp index 6becc89e2..d17e625e4 100644 --- a/libraries/LocalPeer/src/LockedFile_unix.cpp +++ b/libraries/LocalPeer/src/LockedFile_unix.cpp @@ -38,10 +38,10 @@ ** ****************************************************************************/ -#include #include -#include #include +#include +#include #include "LockedFile.h" @@ -75,12 +75,10 @@ bool LockedFile::lock(LockMode mode, bool block) return false; } - m_lock_mode = mode; return true; } - bool LockedFile::unlock() { if (!isOpen()) { diff --git a/libraries/LocalPeer/src/LockedFile_win.cpp b/libraries/LocalPeer/src/LockedFile_win.cpp index 93d2c73b3..a8dbead36 100644 --- a/libraries/LocalPeer/src/LockedFile_win.cpp +++ b/libraries/LocalPeer/src/LockedFile_win.cpp @@ -38,9 +38,9 @@ ** ****************************************************************************/ -#include "LockedFile.h" #include #include +#include "LockedFile.h" #define MUTEX_PREFIX "QtLockedFile mutex " // Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS @@ -50,8 +50,7 @@ Qt::HANDLE LockedFile::getMutexHandle(int idx, bool doCreate) { if (mutexname.isEmpty()) { QFileInfo fi(*this); - mutexname = QString::fromLatin1(MUTEX_PREFIX) - + fi.absoluteFilePath().toLower(); + mutexname = QString::fromLatin1(MUTEX_PREFIX) + fi.absoluteFilePath().toLower(); } QString mname(mutexname); if (idx >= 0) @@ -64,8 +63,7 @@ Qt::HANDLE LockedFile::getMutexHandle(int idx, bool doCreate) qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); return 0; } - } - else { + } else { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (LPCWSTR)mname.utf16()); if (!mutex) { if (GetLastError() != ERROR_FILE_NOT_FOUND) @@ -81,20 +79,18 @@ bool LockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock) Q_ASSERT(mutex); DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0); switch (res) { - case WAIT_OBJECT_0: - case WAIT_ABANDONED: - return true; - break; - case WAIT_TIMEOUT: - break; - default: - qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); + case WAIT_OBJECT_0: + case WAIT_ABANDONED: + return true; + break; + case WAIT_TIMEOUT: + break; + default: + qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); } return false; } - - bool LockedFile::lock(LockMode mode, bool block) { if (!isOpen()) { @@ -130,8 +126,7 @@ bool LockedFile::lock(LockMode mode, bool block) qWarning("QtLockedFile::lock(): too many readers"); rmutex = 0; ok = false; - } - else if (!rmutex) { + } else if (!rmutex) { rmutex = getMutexHandle(idx, true); if (!rmutex || !waitMutex(rmutex, false)) ok = false; @@ -143,8 +138,7 @@ bool LockedFile::lock(LockMode mode, bool block) ReleaseMutex(wmutex); if (!ok) return false; - } - else { + } else { Q_ASSERT(rmutexes.isEmpty()); for (int i = 0; i < MAX_READERS; i++) { Qt::HANDLE mutex = getMutexHandle(i, false); @@ -152,8 +146,7 @@ bool LockedFile::lock(LockMode mode, bool block) rmutexes.append(mutex); } if (rmutexes.size()) { - DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), - TRUE, block ? INFINITE : 0); + DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), TRUE, block ? INFINITE : 0); if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { if (res != WAIT_TIMEOUT) qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed"); @@ -182,9 +175,8 @@ bool LockedFile::unlock() ReleaseMutex(rmutex); CloseHandle(rmutex); rmutex = 0; - } - else { - foreach(Qt::HANDLE mutex, rmutexes) { + } else { + foreach (Qt::HANDLE mutex, rmutexes) { ReleaseMutex(mutex); CloseHandle(mutex); } diff --git a/libraries/README.md b/libraries/README.md index 4da11314d..e75a381ee 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -61,7 +61,7 @@ The `standard` and `legacy` launchers are available. Example (some parts have been censored): -``` +```text mod legacyjavafixer-1.0 mainClass net.minecraft.launchwrapper.Launch param --username diff --git a/libraries/gamemode/include/gamemode_client.h b/libraries/gamemode/include/gamemode_client.h index b6f7afd4c..b186cd489 100644 --- a/libraries/gamemode/include/gamemode_client.h +++ b/libraries/gamemode/include/gamemode_client.h @@ -94,7 +94,7 @@ static volatile int internal_libgamemode_loaded = 1; /* Typedefs for the functions to load */ typedef int (*api_call_return_int)(void); -typedef const char *(*api_call_return_cstring)(void); +typedef const char* (*api_call_return_cstring)(void); typedef int (*api_call_pid_return_int)(pid_t); /* Storage for functors */ @@ -111,26 +111,26 @@ static api_call_pid_return_int REAL_internal_gamemode_query_status_for = NULL; * * Returns 0 on success and -1 on failure */ -__attribute__((always_inline)) static inline int internal_bind_libgamemode_symbol( - void *handle, const char *name, void **out_func, size_t func_size, bool required) +__attribute__((always_inline)) static inline int internal_bind_libgamemode_symbol(void* handle, + const char* name, + void** out_func, + size_t func_size, + bool required) { - void *symbol_lookup = NULL; - char *dl_error = NULL; + void* symbol_lookup = NULL; + char* dl_error = NULL; - /* Safely look up the symbol */ - symbol_lookup = dlsym(handle, name); - dl_error = dlerror(); - if (required && (dl_error || !symbol_lookup)) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "dlsym failed - %s", - dl_error); - return -1; - } + /* Safely look up the symbol */ + symbol_lookup = dlsym(handle, name); + dl_error = dlerror(); + if (required && (dl_error || !symbol_lookup)) { + snprintf(internal_gamemode_client_error_string, sizeof(internal_gamemode_client_error_string), "dlsym failed - %s", dl_error); + return -1; + } - /* Have the symbol correctly, copy it to make it usable */ - memcpy(out_func, &symbol_lookup, func_size); - return 0; + /* Have the symbol correctly, copy it to make it usable */ + memcpy(out_func, &symbol_lookup, func_size); + return 0; } /** @@ -140,98 +140,74 @@ __attribute__((always_inline)) static inline int internal_bind_libgamemode_symbo */ __attribute__((always_inline)) static inline int internal_load_libgamemode(void) { - /* We start at 1, 0 is a success and -1 is a fail */ - if (internal_libgamemode_loaded != 1) { - return internal_libgamemode_loaded; - } + /* We start at 1, 0 is a success and -1 is a fail */ + if (internal_libgamemode_loaded != 1) { + return internal_libgamemode_loaded; + } - /* Anonymous struct type to define our bindings */ - struct binding { - const char *name; - void **functor; - size_t func_size; - bool required; - } bindings[] = { - { "real_gamemode_request_start", - (void **)&REAL_internal_gamemode_request_start, - sizeof(REAL_internal_gamemode_request_start), - true }, - { "real_gamemode_request_end", - (void **)&REAL_internal_gamemode_request_end, - sizeof(REAL_internal_gamemode_request_end), - true }, - { "real_gamemode_query_status", - (void **)&REAL_internal_gamemode_query_status, - sizeof(REAL_internal_gamemode_query_status), - false }, - { "real_gamemode_error_string", - (void **)&REAL_internal_gamemode_error_string, - sizeof(REAL_internal_gamemode_error_string), - true }, - { "real_gamemode_request_start_for", - (void **)&REAL_internal_gamemode_request_start_for, - sizeof(REAL_internal_gamemode_request_start_for), - false }, - { "real_gamemode_request_end_for", - (void **)&REAL_internal_gamemode_request_end_for, - sizeof(REAL_internal_gamemode_request_end_for), - false }, - { "real_gamemode_query_status_for", - (void **)&REAL_internal_gamemode_query_status_for, - sizeof(REAL_internal_gamemode_query_status_for), - false }, - }; + /* Anonymous struct type to define our bindings */ + struct binding { + const char* name; + void** functor; + size_t func_size; + bool required; + } bindings[] = { + { "real_gamemode_request_start", (void**)&REAL_internal_gamemode_request_start, sizeof(REAL_internal_gamemode_request_start), + true }, + { "real_gamemode_request_end", (void**)&REAL_internal_gamemode_request_end, sizeof(REAL_internal_gamemode_request_end), true }, + { "real_gamemode_query_status", (void**)&REAL_internal_gamemode_query_status, sizeof(REAL_internal_gamemode_query_status), false }, + { "real_gamemode_error_string", (void**)&REAL_internal_gamemode_error_string, sizeof(REAL_internal_gamemode_error_string), true }, + { "real_gamemode_request_start_for", (void**)&REAL_internal_gamemode_request_start_for, + sizeof(REAL_internal_gamemode_request_start_for), false }, + { "real_gamemode_request_end_for", (void**)&REAL_internal_gamemode_request_end_for, sizeof(REAL_internal_gamemode_request_end_for), + false }, + { "real_gamemode_query_status_for", (void**)&REAL_internal_gamemode_query_status_for, + sizeof(REAL_internal_gamemode_query_status_for), false }, + }; - void *libgamemode = NULL; + void* libgamemode = NULL; - /* Try and load libgamemode */ - libgamemode = dlopen("libgamemode.so.0", RTLD_NOW); - if (!libgamemode) { - /* Attempt to load unversioned library for compatibility with older - * versions (as of writing, there are no ABI changes between the two - - * this may need to change if ever ABI-breaking changes are made) */ - libgamemode = dlopen("libgamemode.so", RTLD_NOW); - if (!libgamemode) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "dlopen failed - %s", - dlerror()); - internal_libgamemode_loaded = -1; - return -1; - } - } + /* Try and load libgamemode */ + libgamemode = dlopen("libgamemode.so.0", RTLD_NOW); + if (!libgamemode) { + /* Attempt to load unversioned library for compatibility with older + * versions (as of writing, there are no ABI changes between the two - + * this may need to change if ever ABI-breaking changes are made) */ + libgamemode = dlopen("libgamemode.so", RTLD_NOW); + if (!libgamemode) { + snprintf(internal_gamemode_client_error_string, sizeof(internal_gamemode_client_error_string), "dlopen failed - %s", dlerror()); + internal_libgamemode_loaded = -1; + return -1; + } + } - /* Attempt to bind all symbols */ - for (size_t i = 0; i < sizeof(bindings) / sizeof(bindings[0]); i++) { - struct binding *binder = &bindings[i]; + /* Attempt to bind all symbols */ + for (size_t i = 0; i < sizeof(bindings) / sizeof(bindings[0]); i++) { + struct binding* binder = &bindings[i]; - if (internal_bind_libgamemode_symbol(libgamemode, - binder->name, - binder->functor, - binder->func_size, - binder->required)) { - internal_libgamemode_loaded = -1; - return -1; - }; - } + if (internal_bind_libgamemode_symbol(libgamemode, binder->name, binder->functor, binder->func_size, binder->required)) { + internal_libgamemode_loaded = -1; + return -1; + }; + } - /* Success */ - internal_libgamemode_loaded = 0; - return 0; + /* Success */ + internal_libgamemode_loaded = 0; + return 0; } /** * Redirect to the real libgamemode */ -__attribute__((always_inline)) static inline const char *gamemode_error_string(void) +__attribute__((always_inline)) static inline const char* gamemode_error_string(void) { - /* If we fail to load the system gamemode, or we have an error string already, return our error - * string instead of diverting to the system version */ - if (internal_load_libgamemode() < 0 || internal_gamemode_client_error_string[0] != '\0') { - return internal_gamemode_client_error_string; - } + /* If we fail to load the system gamemode, or we have an error string already, return our error + * string instead of diverting to the system version */ + if (internal_load_libgamemode() < 0 || internal_gamemode_client_error_string[0] != '\0') { + return internal_gamemode_client_error_string; + } - return REAL_internal_gamemode_error_string(); + return REAL_internal_gamemode_error_string(); } /** @@ -246,22 +222,22 @@ __attribute__((always_inline)) static inline #endif int gamemode_request_start(void) { - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { #ifdef GAMEMODE_AUTO - fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); + fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); #endif - return -1; - } + return -1; + } - if (REAL_internal_gamemode_request_start() < 0) { + if (REAL_internal_gamemode_request_start() < 0) { #ifdef GAMEMODE_AUTO - fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); + fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); #endif - return -1; - } + return -1; + } - return 0; + return 0; } /* Redirect to the real libgamemode */ @@ -272,94 +248,90 @@ __attribute__((always_inline)) static inline #endif int gamemode_request_end(void) { - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { #ifdef GAMEMODE_AUTO - fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); + fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); #endif - return -1; - } + return -1; + } - if (REAL_internal_gamemode_request_end() < 0) { + if (REAL_internal_gamemode_request_end() < 0) { #ifdef GAMEMODE_AUTO - fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); + fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); #endif - return -1; - } + return -1; + } - return 0; + return 0; } /* Redirect to the real libgamemode */ __attribute__((always_inline)) static inline int gamemode_query_status(void) { - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { - return -1; - } + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { + return -1; + } - if (REAL_internal_gamemode_query_status == NULL) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "gamemode_query_status missing (older host?)"); - return -1; - } + if (REAL_internal_gamemode_query_status == NULL) { + snprintf(internal_gamemode_client_error_string, sizeof(internal_gamemode_client_error_string), + "gamemode_query_status missing (older host?)"); + return -1; + } - return REAL_internal_gamemode_query_status(); + return REAL_internal_gamemode_query_status(); } /* Redirect to the real libgamemode */ __attribute__((always_inline)) static inline int gamemode_request_start_for(pid_t pid) { - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { - return -1; - } + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { + return -1; + } - if (REAL_internal_gamemode_request_start_for == NULL) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "gamemode_request_start_for missing (older host?)"); - return -1; - } + if (REAL_internal_gamemode_request_start_for == NULL) { + snprintf(internal_gamemode_client_error_string, sizeof(internal_gamemode_client_error_string), + "gamemode_request_start_for missing (older host?)"); + return -1; + } - return REAL_internal_gamemode_request_start_for(pid); + return REAL_internal_gamemode_request_start_for(pid); } /* Redirect to the real libgamemode */ __attribute__((always_inline)) static inline int gamemode_request_end_for(pid_t pid) { - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { - return -1; - } + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { + return -1; + } - if (REAL_internal_gamemode_request_end_for == NULL) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "gamemode_request_end_for missing (older host?)"); - return -1; - } + if (REAL_internal_gamemode_request_end_for == NULL) { + snprintf(internal_gamemode_client_error_string, sizeof(internal_gamemode_client_error_string), + "gamemode_request_end_for missing (older host?)"); + return -1; + } - return REAL_internal_gamemode_request_end_for(pid); + return REAL_internal_gamemode_request_end_for(pid); } /* Redirect to the real libgamemode */ __attribute__((always_inline)) static inline int gamemode_query_status_for(pid_t pid) { - /* Need to load gamemode */ - if (internal_load_libgamemode() < 0) { - return -1; - } + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { + return -1; + } - if (REAL_internal_gamemode_query_status_for == NULL) { - snprintf(internal_gamemode_client_error_string, - sizeof(internal_gamemode_client_error_string), - "gamemode_query_status_for missing (older host?)"); - return -1; - } + if (REAL_internal_gamemode_query_status_for == NULL) { + snprintf(internal_gamemode_client_error_string, sizeof(internal_gamemode_client_error_string), + "gamemode_query_status_for missing (older host?)"); + return -1; + } - return REAL_internal_gamemode_query_status_for(pid); + return REAL_internal_gamemode_query_status_for(pid); } -#endif // CLIENT_GAMEMODE_H +#endif // CLIENT_GAMEMODE_H diff --git a/libraries/javacheck/JavaCheck.java b/libraries/javacheck/JavaCheck.java index 4bf43a544..7cde86cf2 100644 --- a/libraries/javacheck/JavaCheck.java +++ b/libraries/javacheck/JavaCheck.java @@ -1,10 +1,5 @@ public final class JavaCheck { - - private static final String[] CHECKED_PROPERTIES = new String[] { - "os.arch", - "java.version", - "java.vendor" - }; + private static final String[] CHECKED_PROPERTIES = new String[] {"os.arch", "java.version", "java.vendor"}; public static void main(String[] args) { int returnCode = 0; @@ -21,5 +16,4 @@ public final class JavaCheck { System.exit(returnCode); } - } diff --git a/libraries/katabasis/include/katabasis/Bits.h b/libraries/katabasis/include/katabasis/Bits.h index f11f25d25..15da2a5a8 100644 --- a/libraries/katabasis/include/katabasis/Bits.h +++ b/libraries/katabasis/include/katabasis/Bits.h @@ -1,8 +1,8 @@ #pragma once -#include #include #include +#include #include namespace Katabasis { @@ -11,17 +11,13 @@ enum class Activity { LoggingIn, LoggingOut, Refreshing, - FailedSoft, //!< soft failure. this generally means the user auth details haven't been invalidated - FailedHard, //!< hard failure. auth is invalid - FailedGone, //!< hard failure. auth is invalid, and the account no longer exists + FailedSoft, //!< soft failure. this generally means the user auth details haven't been invalidated + FailedHard, //!< hard failure. auth is invalid + FailedGone, //!< hard failure. auth is invalid, and the account no longer exists Succeeded }; -enum class Validity { - None, - Assumed, - Certain -}; +enum class Validity { None, Assumed, Certain }; struct Token { QDateTime issueInstant; @@ -34,4 +30,4 @@ struct Token { bool persistent = true; }; -} +} // namespace Katabasis diff --git a/libraries/katabasis/include/katabasis/DeviceFlow.h b/libraries/katabasis/include/katabasis/DeviceFlow.h index 0401df3c0..98724d81b 100644 --- a/libraries/katabasis/include/katabasis/DeviceFlow.h +++ b/libraries/katabasis/include/katabasis/DeviceFlow.h @@ -2,13 +2,13 @@ #include #include -#include #include +#include #include +#include "Bits.h" #include "Reply.h" #include "RequestParameter.h" -#include "Bits.h" namespace Katabasis { @@ -16,14 +16,12 @@ class ReplyServer; class PollServer; /// Simple OAuth2 Device Flow authenticator. -class DeviceFlow: public QObject -{ +class DeviceFlow : public QObject { Q_OBJECT -public: + public: Q_ENUMS(GrantFlow) -public: - + public: struct Options { QString userAgent = QStringLiteral("Katabasis/1.0"); QString responseType = QStringLiteral("code"); @@ -34,7 +32,7 @@ public: QUrl accessTokenUrl; }; -public: + public: /// Are we authenticated? bool linked(); @@ -44,21 +42,21 @@ public: /// Provider-specific extra tokens, available after a successful authentication QVariantMap extraTokens(); -public: + public: // TODO: put in `Options` /// User-defined extra parameters to append to request URL QVariantMap extraRequestParams(); - void setExtraRequestParams(const QVariantMap &value); + void setExtraRequestParams(const QVariantMap& value); // TODO: split up the class into multiple, each implementing one OAuth2 flow /// Grant type (if non-standard) QString grantType(); - void setGrantType(const QString &value); + void setGrantType(const QString& value); -public: + public: /// Constructor. /// @param parent Parent object. - explicit DeviceFlow(Options & opts, Token & token, QObject *parent = 0, QNetworkAccessManager *manager = 0); + explicit DeviceFlow(Options& opts, Token& token, QObject* parent = 0, QNetworkAccessManager* manager = 0); /// Get refresh token. QString refreshToken(); @@ -66,7 +64,7 @@ public: /// Get token expiration time QDateTime expires(); -public slots: + public slots: /// Authenticate. void login(); @@ -79,24 +77,24 @@ public slots: /// Handle situation where reply server has opted to close its connection void serverHasClosed(bool paramsfound = false); -signals: + signals: /// Emitted when client needs to open a web browser window, with the given URL. - void openBrowser(const QUrl &url); + void openBrowser(const QUrl& url); /// Emitted when client can close the browser window. void closeBrowser(); /// Emitted when client needs to show a verification uri and user code - void showVerificationUriAndCode(const QUrl &uri, const QString &code, int expiresIn); + void showVerificationUriAndCode(const QUrl& uri, const QString& code, int expiresIn); /// Emitted when the internal state changes void activityChanged(Activity activity); -public slots: + public slots: /// Handle verification response. void onVerificationReceived(QMap); -protected slots: + protected slots: /// Handle completion of a Device Authorization Request void onDeviceAuthReplyFinished(); @@ -104,20 +102,20 @@ protected slots: void onRefreshFinished(); /// Handle failure of a refresh request. - void onRefreshError(QNetworkReply::NetworkError error, QNetworkReply *reply); + void onRefreshError(QNetworkReply::NetworkError error, QNetworkReply* reply); -protected: + protected: /// Set refresh token. - void setRefreshToken(const QString &v); + void setRefreshToken(const QString& v); /// Set token expiration time. void setExpires(QDateTime v); /// Start polling authorization server - void startPollServer(const QVariantMap ¶ms, int expiresIn); + void startPollServer(const QVariantMap& params, int expiresIn); /// Set authentication token. - void setToken(const QString &v); + void setToken(const QString& v); /// Set the linked state void setLinked(bool v); @@ -126,26 +124,26 @@ protected: void setExtraTokens(QVariantMap extraTokens); /// Set local poll server - void setPollServer(PollServer *server); + void setPollServer(PollServer* server); - PollServer * pollServer() const; + PollServer* pollServer() const; void updateActivity(Activity activity); -protected: + protected: Options options_; QVariantMap extraReqParams_; - QNetworkAccessManager *manager_ = nullptr; + QNetworkAccessManager* manager_ = nullptr; ReplyList timedReplies_; QString grantType_; -protected: - Token &token_; + protected: + Token& token_; -private: - PollServer *pollServer_ = nullptr; + private: + PollServer* pollServer_ = nullptr; Activity activity_ = Activity::Idle; }; -} +} // namespace Katabasis diff --git a/libraries/katabasis/include/katabasis/Globals.h b/libraries/katabasis/include/katabasis/Globals.h index 512745d3c..02fe1cf45 100644 --- a/libraries/katabasis/include/katabasis/Globals.h +++ b/libraries/katabasis/include/katabasis/Globals.h @@ -45,7 +45,7 @@ const char OAUTH2_EXPIRES_IN[] = "expires_in"; const char OAUTH2_DEVICE_CODE[] = "device_code"; const char OAUTH2_USER_CODE[] = "user_code"; const char OAUTH2_VERIFICATION_URI[] = "verification_uri"; -const char OAUTH2_VERIFICATION_URL[] = "verification_url"; // Google sign-in +const char OAUTH2_VERIFICATION_URL[] = "verification_url"; // Google sign-in const char OAUTH2_VERIFICATION_URI_COMPLETE[] = "verification_uri_complete"; const char OAUTH2_INTERVAL[] = "interval"; @@ -56,4 +56,4 @@ const char AUTHORIZATION_CODE[] = "authorization_code"; const char HTTP_HTTP_HEADER[] = "HTTP"; const char HTTP_AUTHORIZATION_HEADER[] = "Authorization"; -} +} // namespace Katabasis diff --git a/libraries/katabasis/include/katabasis/PollServer.h b/libraries/katabasis/include/katabasis/PollServer.h index 77103867c..fd6a5351c 100644 --- a/libraries/katabasis/include/katabasis/PollServer.h +++ b/libraries/katabasis/include/katabasis/PollServer.h @@ -12,32 +12,35 @@ class QNetworkAccessManager; namespace Katabasis { /// Poll an authorization server for token -class PollServer : public QObject -{ +class PollServer : public QObject { Q_OBJECT -public: - explicit PollServer(QNetworkAccessManager * manager, const QNetworkRequest &request, const QByteArray & payload, int expiresIn, QObject *parent = 0); + public: + explicit PollServer(QNetworkAccessManager* manager, + const QNetworkRequest& request, + const QByteArray& payload, + int expiresIn, + QObject* parent = 0); /// Seconds to wait between polling requests Q_PROPERTY(int interval READ interval WRITE setInterval) int interval() const; void setInterval(int interval); -signals: + signals: void verificationReceived(QMap); - void serverClosed(bool); // whether it has found parameters + void serverClosed(bool); // whether it has found parameters -public slots: + public slots: void startPolling(); -protected slots: + protected slots: void onPollTimeout(); void onExpiration(); void onReplyFinished(); -protected: - QNetworkAccessManager *manager_; + protected: + QNetworkAccessManager* manager_; const QNetworkRequest request_; const QByteArray payload_; const int expiresIn_; @@ -45,4 +48,4 @@ protected: QTimer pollTimer; }; -} +} // namespace Katabasis diff --git a/libraries/katabasis/include/katabasis/Reply.h b/libraries/katabasis/include/katabasis/Reply.h index 415cf4ec8..89ee90e98 100644 --- a/libraries/katabasis/include/katabasis/Reply.h +++ b/libraries/katabasis/include/katabasis/Reply.h @@ -1,38 +1,38 @@ #pragma once -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include namespace Katabasis { constexpr int defaultTimeout = 30 * 1000; /// A network request/reply pair that can time out. -class Reply: public QTimer { +class Reply : public QTimer { Q_OBJECT -public: - Reply(QNetworkReply *reply, int timeOut = defaultTimeout, QObject *parent = 0); + public: + Reply(QNetworkReply* reply, int timeOut = defaultTimeout, QObject* parent = 0); -signals: + signals: void error(QNetworkReply::NetworkError); -public slots: + public slots: /// When time out occurs, the QNetworkReply's error() signal is triggered. void onTimeOut(); -public: - QNetworkReply *reply; + public: + QNetworkReply* reply; bool timedOut = false; }; /// List of O2Replies. class ReplyList { -public: + public: ReplyList() { ignoreSslErrors_ = false; } /// Destructor. @@ -40,24 +40,24 @@ public: virtual ~ReplyList(); /// Create a new O2Reply from a QNetworkReply, and add it to this list. - void add(QNetworkReply *reply, int timeOut = defaultTimeout); + void add(QNetworkReply* reply, int timeOut = defaultTimeout); /// Add an O2Reply to the list, while taking ownership of it. - void add(Reply *reply); + void add(Reply* reply); /// Remove item from the list that corresponds to a QNetworkReply. - void remove(QNetworkReply *reply); + void remove(QNetworkReply* reply); /// Find an O2Reply in the list, corresponding to a QNetworkReply. /// @return Matching O2Reply or NULL. - Reply *find(QNetworkReply *reply); + Reply* find(QNetworkReply* reply); bool ignoreSslErrors(); void setIgnoreSslErrors(bool ignoreSslErrors); -protected: - QList replies_; + protected: + QList replies_; bool ignoreSslErrors_; }; -} +} // namespace Katabasis diff --git a/libraries/katabasis/include/katabasis/RequestParameter.h b/libraries/katabasis/include/katabasis/RequestParameter.h index ca36934a3..1d23cf0e1 100644 --- a/libraries/katabasis/include/katabasis/RequestParameter.h +++ b/libraries/katabasis/include/katabasis/RequestParameter.h @@ -4,12 +4,10 @@ namespace Katabasis { /// Request parameter (name-value pair) participating in authentication. struct RequestParameter { - RequestParameter(const QByteArray &n, const QByteArray &v): name(n), value(v) {} - bool operator <(const RequestParameter &other) const { - return (name == other.name)? (value < other.value): (name < other.name); - } + RequestParameter(const QByteArray& n, const QByteArray& v) : name(n), value(v) {} + bool operator<(const RequestParameter& other) const { return (name == other.name) ? (value < other.value) : (name < other.name); } QByteArray name; QByteArray value; }; -} +} // namespace Katabasis diff --git a/libraries/katabasis/src/DeviceFlow.cpp b/libraries/katabasis/src/DeviceFlow.cpp index f49fcb7d7..3b9d9c53f 100644 --- a/libraries/katabasis/src/DeviceFlow.cpp +++ b/libraries/katabasis/src/DeviceFlow.cpp @@ -1,26 +1,26 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include "katabasis/DeviceFlow.h" -#include "katabasis/PollServer.h" #include "katabasis/Globals.h" +#include "katabasis/PollServer.h" -#include "KatabasisLogging.h" #include "JsonResponse.h" +#include "KatabasisLogging.h" namespace { @@ -43,10 +43,11 @@ bool hasMandatoryDeviceAuthParams(const QVariantMap& params) return true; } -QByteArray createQueryParameters(const QList ¶meters) { +QByteArray createQueryParameters(const QList& parameters) +{ QByteArray ret; bool first = true; - for( auto & h: parameters) { + for (auto& h : parameters) { if (first) { first = false; } else { @@ -56,32 +57,35 @@ QByteArray createQueryParameters(const QList ¶m } return ret; } -} +} // namespace namespace Katabasis { -DeviceFlow::DeviceFlow(Options & opts, Token & token, QObject *parent, QNetworkAccessManager *manager) : QObject(parent), token_(token) { +DeviceFlow::DeviceFlow(Options& opts, Token& token, QObject* parent, QNetworkAccessManager* manager) : QObject(parent), token_(token) +{ manager_ = manager ? manager : new QNetworkAccessManager(this); qRegisterMetaType("QNetworkReply::NetworkError"); options_ = opts; } -bool DeviceFlow::linked() { +bool DeviceFlow::linked() +{ return token_.validity != Validity::None; } -void DeviceFlow::setLinked(bool v) { - qDebug() << "DeviceFlow::setLinked:" << (v? "true": "false"); +void DeviceFlow::setLinked(bool v) +{ + qDebug() << "DeviceFlow::setLinked:" << (v ? "true" : "false"); token_.validity = v ? Validity::Certain : Validity::None; } void DeviceFlow::updateActivity(Activity activity) { - if(activity_ == activity) { + if (activity_ == activity) { return; } activity_ = activity; - switch(activity) { + switch (activity) { case Katabasis::Activity::Idle: case Katabasis::Activity::LoggingIn: case Katabasis::Activity::LoggingOut: @@ -103,22 +107,26 @@ void DeviceFlow::updateActivity(Activity activity) emit activityChanged(activity_); } -QString DeviceFlow::token() { +QString DeviceFlow::token() +{ return token_.token; } -void DeviceFlow::setToken(const QString &v) { +void DeviceFlow::setToken(const QString& v) +{ token_.token = v; } -QVariantMap DeviceFlow::extraTokens() { +QVariantMap DeviceFlow::extraTokens() +{ return token_.extra; } -void DeviceFlow::setExtraTokens(QVariantMap extraTokens) { +void DeviceFlow::setExtraTokens(QVariantMap extraTokens) +{ token_.extra = extraTokens; } -void DeviceFlow::setPollServer(PollServer *server) +void DeviceFlow::setPollServer(PollServer* server) { if (pollServer_) pollServer_->deleteLater(); @@ -126,7 +134,7 @@ void DeviceFlow::setPollServer(PollServer *server) pollServer_ = server; } -PollServer *DeviceFlow::pollServer() const +PollServer* DeviceFlow::pollServer() const { return pollServer_; } @@ -136,7 +144,7 @@ QVariantMap DeviceFlow::extraRequestParams() return extraReqParams_; } -void DeviceFlow::setExtraRequestParams(const QVariantMap &value) +void DeviceFlow::setExtraRequestParams(const QVariantMap& value) { extraReqParams_ = value; } @@ -149,13 +157,14 @@ QString DeviceFlow::grantType() return OAUTH2_GRANT_TYPE_DEVICE; } -void DeviceFlow::setGrantType(const QString &value) +void DeviceFlow::setGrantType(const QString& value) { grantType_ = value; } // First get the URL and token to display to the user -void DeviceFlow::login() { +void DeviceFlow::login() +{ qDebug() << "DeviceFlow::link"; updateActivity(Activity::LoggingIn); @@ -173,7 +182,7 @@ void DeviceFlow::login() { QUrl url(options_.authorizationUrl); QNetworkRequest deviceRequest(url); deviceRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - QNetworkReply *tokenReply = manager_->post(deviceRequest, payload); + QNetworkReply* tokenReply = manager_->post(deviceRequest, payload); connect(tokenReply, &QNetworkReply::finished, this, &DeviceFlow::onDeviceAuthReplyFinished, Qt::QueuedConnection); } @@ -182,19 +191,18 @@ void DeviceFlow::login() { void DeviceFlow::onDeviceAuthReplyFinished() { qDebug() << "DeviceFlow::onDeviceAuthReplyFinished"; - QNetworkReply *tokenReply = qobject_cast(sender()); - if (!tokenReply) - { - qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: reply is null"; - return; + QNetworkReply* tokenReply = qobject_cast(sender()); + if (!tokenReply) { + qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: reply is null"; + return; } if (tokenReply->error() == QNetworkReply::NoError) { QByteArray replyData = tokenReply->readAll(); // Dump replyData // SENSITIVE DATA in RelWithDebInfo or Debug builds - //qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: replyData\n"; - //qDebug() << QString( replyData ); + // qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: replyData\n"; + // qDebug() << QString( replyData ); QVariantMap params = parseJsonResponse(replyData); @@ -202,7 +210,7 @@ void DeviceFlow::onDeviceAuthReplyFinished() qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: Tokens returned:\n"; foreach (QString key, params.keys()) { // SENSITIVE DATA in RelWithDebInfo or Debug builds, so it is truncated first - qDebug() << key << ": "<< params.value( key ).toString(); + qDebug() << key << ": " << params.value(key).toString(); } // Check for mandatory parameters @@ -237,7 +245,7 @@ void DeviceFlow::onDeviceAuthReplyFinished() } // Spin up polling for the user completing the login flow out of band -void DeviceFlow::startPollServer(const QVariantMap ¶ms, int expiresIn) +void DeviceFlow::startPollServer(const QVariantMap& params, int expiresIn) { qDebug() << "DeviceFlow::startPollServer: device_ and user_code expires in" << expiresIn << "seconds"; @@ -250,14 +258,14 @@ void DeviceFlow::startPollServer(const QVariantMap ¶ms, int expiresIn) QList parameters; parameters.append(RequestParameter(OAUTH2_CLIENT_ID, options_.clientIdentifier.toUtf8())); - if ( !options_.clientSecret.isEmpty() ) { + if (!options_.clientSecret.isEmpty()) { parameters.append(RequestParameter(OAUTH2_CLIENT_SECRET, options_.clientSecret.toUtf8())); } parameters.append(RequestParameter(OAUTH2_CODE, deviceCode.toUtf8())); parameters.append(RequestParameter(OAUTH2_GRANT_TYPE, grantType.toUtf8())); QByteArray payload = createQueryParameters(parameters); - PollServer * pollServer = new PollServer(manager_, authRequest, payload, expiresIn, this); + PollServer* pollServer = new PollServer(manager_, authRequest, payload, expiresIn, this); if (params.contains(OAUTH2_INTERVAL)) { bool ok = false; int interval = params[OAUTH2_INTERVAL].toInt(&ok); @@ -272,7 +280,8 @@ void DeviceFlow::startPollServer(const QVariantMap ¶ms, int expiresIn) } // Once the user completes the flow, update the internal state and report it to observers -void DeviceFlow::onVerificationReceived(const QMap response) { +void DeviceFlow::onVerificationReceived(const QMap response) +{ qDebug() << "DeviceFlow::onVerificationReceived: Emitting closeBrowser()"; emit closeBrowser(); @@ -307,7 +316,7 @@ void DeviceFlow::onVerificationReceived(const QMap response) { // Or if the flow fails or the polling times out, update the internal state with error and report it to observers void DeviceFlow::serverHasClosed(bool paramsfound) { - if ( !paramsfound ) { + if (!paramsfound) { // server has probably timed out after receiving first response updateActivity(Activity::FailedHard); } @@ -315,7 +324,8 @@ void DeviceFlow::serverHasClosed(bool paramsfound) setPollServer(NULL); } -void DeviceFlow::logout() { +void DeviceFlow::logout() +{ qDebug() << "DeviceFlow::unlink"; updateActivity(Activity::LoggingOut); // FIXME: implement logout flows... if they exist @@ -323,24 +333,29 @@ void DeviceFlow::logout() { updateActivity(Activity::FailedHard); } -QDateTime DeviceFlow::expires() { +QDateTime DeviceFlow::expires() +{ return token_.notAfter; } -void DeviceFlow::setExpires(QDateTime v) { +void DeviceFlow::setExpires(QDateTime v) +{ token_.notAfter = v; } -QString DeviceFlow::refreshToken() { +QString DeviceFlow::refreshToken() +{ return token_.refresh_token; } -void DeviceFlow::setRefreshToken(const QString &v) { +void DeviceFlow::setRefreshToken(const QString& v) +{ qCDebug(katabasisCredentials) << "new refresh token:" << v; token_.refresh_token = v; } namespace { -QByteArray buildRequestBody(const QMap ¶meters) { +QByteArray buildRequestBody(const QMap& parameters) +{ QByteArray body; bool first = true; foreach (QString key, parameters.keys()) { @@ -354,9 +369,10 @@ QByteArray buildRequestBody(const QMap ¶meters) { } return body; } -} +} // namespace -bool DeviceFlow::refresh() { +bool DeviceFlow::refresh() +{ qDebug() << "DeviceFlow::refresh: Token: ..." << refreshToken().right(7); updateActivity(Activity::Refreshing); @@ -376,21 +392,22 @@ bool DeviceFlow::refresh() { refreshRequest.setHeader(QNetworkRequest::ContentTypeHeader, MIME_TYPE_XFORM); QMap parameters; parameters.insert(OAUTH2_CLIENT_ID, options_.clientIdentifier); - if ( !options_.clientSecret.isEmpty() ) { + if (!options_.clientSecret.isEmpty()) { parameters.insert(OAUTH2_CLIENT_SECRET, options_.clientSecret); } parameters.insert(OAUTH2_REFRESH_TOKEN, refreshToken()); parameters.insert(OAUTH2_GRANT_TYPE, OAUTH2_REFRESH_TOKEN); QByteArray data = buildRequestBody(parameters); - QNetworkReply *refreshReply = manager_->post(refreshRequest, data); + QNetworkReply* refreshReply = manager_->post(refreshRequest, data); timedReplies_.add(refreshReply); connect(refreshReply, &QNetworkReply::finished, this, &DeviceFlow::onRefreshFinished, Qt::QueuedConnection); return true; } -void DeviceFlow::onRefreshFinished() { - QNetworkReply *refreshReply = qobject_cast(sender()); +void DeviceFlow::onRefreshFinished() +{ + QNetworkReply* refreshReply = qobject_cast(sender()); auto networkError = refreshReply->error(); if (networkError == QNetworkReply::NoError) { @@ -399,10 +416,9 @@ void DeviceFlow::onRefreshFinished() { setToken(tokens.value(OAUTH2_ACCESS_TOKEN).toString()); setExpires(QDateTime::currentDateTimeUtc().addSecs(tokens.value(OAUTH2_EXPIRES_IN).toInt())); QString refreshToken = tokens.value(OAUTH2_REFRESH_TOKEN).toString(); - if(!refreshToken.isEmpty()) { + if (!refreshToken.isEmpty()) { setRefreshToken(refreshToken); - } - else { + } else { qDebug() << "No new refresh token. Keep the old one."; } timedReplies_.remove(refreshReply); @@ -415,37 +431,37 @@ void DeviceFlow::onRefreshFinished() { } } -void DeviceFlow::onRefreshError(QNetworkReply::NetworkError error, QNetworkReply *refreshReply) { +void DeviceFlow::onRefreshError(QNetworkReply::NetworkError error, QNetworkReply* refreshReply) +{ QString errorString = "No Reply"; - if(refreshReply) { + if (refreshReply) { timedReplies_.remove(refreshReply); errorString = refreshReply->errorString(); } - switch (error) - { - // used for invalid credentials and similar errors. Fall through. - case QNetworkReply::AuthenticationRequiredError: - case QNetworkReply::ContentAccessDenied: - case QNetworkReply::ContentOperationNotPermittedError: - case QNetworkReply::ProtocolInvalidOperationError: - updateActivity(Activity::FailedHard); - break; - case QNetworkReply::ContentGoneError: { - updateActivity(Activity::FailedGone); - break; + switch (error) { + // used for invalid credentials and similar errors. Fall through. + case QNetworkReply::AuthenticationRequiredError: + case QNetworkReply::ContentAccessDenied: + case QNetworkReply::ContentOperationNotPermittedError: + case QNetworkReply::ProtocolInvalidOperationError: + updateActivity(Activity::FailedHard); + break; + case QNetworkReply::ContentGoneError: { + updateActivity(Activity::FailedGone); + break; + } + case QNetworkReply::TimeoutError: + case QNetworkReply::OperationCanceledError: + case QNetworkReply::SslHandshakeFailedError: + default: + updateActivity(Activity::FailedSoft); + return; } - case QNetworkReply::TimeoutError: - case QNetworkReply::OperationCanceledError: - case QNetworkReply::SslHandshakeFailedError: - default: - updateActivity(Activity::FailedSoft); - return; - } - if(refreshReply) { + if (refreshReply) { refreshReply->deleteLater(); } qDebug() << "DeviceFlow::onRefreshFinished: Error" << static_cast(error) << " - " << errorString; } -} +} // namespace Katabasis diff --git a/libraries/katabasis/src/JsonResponse.cpp b/libraries/katabasis/src/JsonResponse.cpp index 63384d121..6840627ac 100644 --- a/libraries/katabasis/src/JsonResponse.cpp +++ b/libraries/katabasis/src/JsonResponse.cpp @@ -7,7 +7,8 @@ namespace Katabasis { -QVariantMap parseJsonResponse(const QByteArray &data) { +QVariantMap parseJsonResponse(const QByteArray& data) +{ QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (err.error != QJsonParseError::NoError) { @@ -23,4 +24,4 @@ QVariantMap parseJsonResponse(const QByteArray &data) { return doc.object().toVariantMap(); } -} +} // namespace Katabasis diff --git a/libraries/katabasis/src/JsonResponse.h b/libraries/katabasis/src/JsonResponse.h index e7fe7e30d..ff3471752 100644 --- a/libraries/katabasis/src/JsonResponse.h +++ b/libraries/katabasis/src/JsonResponse.h @@ -6,7 +6,7 @@ class QByteArray; namespace Katabasis { - /// Parse JSON data into a QVariantMap -QVariantMap parseJsonResponse(const QByteArray &data); +/// Parse JSON data into a QVariantMap +QVariantMap parseJsonResponse(const QByteArray& data); -} +} // namespace Katabasis diff --git a/libraries/katabasis/src/PollServer.cpp b/libraries/katabasis/src/PollServer.cpp index 1083c5994..c1c316df9 100644 --- a/libraries/katabasis/src/PollServer.cpp +++ b/libraries/katabasis/src/PollServer.cpp @@ -1,30 +1,28 @@ #include #include -#include "katabasis/PollServer.h" #include "JsonResponse.h" +#include "katabasis/PollServer.h" namespace { -QMap toVerificationParams(const QVariantMap &map) +QMap toVerificationParams(const QVariantMap& map) { QMap params; - for (QVariantMap::const_iterator i = map.constBegin(); - i != map.constEnd(); ++i) - { + for (QVariantMap::const_iterator i = map.constBegin(); i != map.constEnd(); ++i) { params[i.key()] = i.value().toString(); } return params; } -} +} // namespace namespace Katabasis { -PollServer::PollServer(QNetworkAccessManager *manager, const QNetworkRequest &request, const QByteArray &payload, int expiresIn, QObject *parent) - : QObject(parent) - , manager_(manager) - , request_(request) - , payload_(payload) - , expiresIn_(expiresIn) +PollServer::PollServer(QNetworkAccessManager* manager, + const QNetworkRequest& request, + const QByteArray& payload, + int expiresIn, + QObject* parent) + : QObject(parent), manager_(manager), request_(request), payload_(payload), expiresIn_(expiresIn) { expirationTimer.setTimerType(Qt::VeryCoarseTimer); expirationTimer.setInterval(expiresIn * 1000); @@ -58,7 +56,7 @@ void PollServer::startPolling() void PollServer::onPollTimeout() { qDebug() << "PollServer::onPollTimeout: retrying"; - QNetworkReply * reply = manager_->post(request_, payload_); + QNetworkReply* reply = manager_->post(request_, payload_); connect(reply, SIGNAL(finished()), this, SLOT(onReplyFinished())); } @@ -70,7 +68,7 @@ void PollServer::onExpiration() void PollServer::onReplyFinished() { - QNetworkReply *reply = qobject_cast(sender()); + QNetworkReply* reply = qobject_cast(sender()); if (!reply) { qDebug() << "PollServer::onReplyFinished: reply is null"; @@ -93,8 +91,7 @@ void PollServer::onReplyFinished() // polling interval on each such connection timeout, is RECOMMENDED." setInterval(interval() * 2); pollTimer.start(); - } - else { + } else { QString error = params.value("error"); if (error == "slow_down") { // rfc8628#section-3.2 @@ -103,14 +100,12 @@ void PollServer::onReplyFinished() // be increased by 5 seconds for this and all subsequent requests." setInterval(interval() + 5); pollTimer.start(); - } - else if (error == "authorization_pending") { + } else if (error == "authorization_pending") { // keep trying - rfc8628#section-3.2 // "The authorization request is still pending as the end user hasn't // yet completed the user-interaction steps (Section 3.3)." pollTimer.start(); - } - else { + } else { expirationTimer.stop(); emit serverClosed(true); // let O2 handle the other cases @@ -120,4 +115,4 @@ void PollServer::onReplyFinished() reply->deleteLater(); } -} +} // namespace Katabasis diff --git a/libraries/katabasis/src/Reply.cpp b/libraries/katabasis/src/Reply.cpp index c26079005..4a5017e22 100644 --- a/libraries/katabasis/src/Reply.cpp +++ b/libraries/katabasis/src/Reply.cpp @@ -1,42 +1,48 @@ -#include #include +#include #include "katabasis/Reply.h" namespace Katabasis { -Reply::Reply(QNetworkReply *r, int timeOut, QObject *parent): QTimer(parent), reply(r) { +Reply::Reply(QNetworkReply* r, int timeOut, QObject* parent) : QTimer(parent), reply(r) +{ setSingleShot(true); connect(this, &Reply::timeout, this, &Reply::onTimeOut, Qt::QueuedConnection); start(timeOut); } -void Reply::onTimeOut() { +void Reply::onTimeOut() +{ timedOut = true; reply->abort(); } // ---------------------------- -ReplyList::~ReplyList() { - foreach (Reply *timedReply, replies_) { +ReplyList::~ReplyList() +{ + foreach (Reply* timedReply, replies_) { delete timedReply; } } -void ReplyList::add(QNetworkReply *reply, int timeOut) { +void ReplyList::add(QNetworkReply* reply, int timeOut) +{ if (reply && ignoreSslErrors()) { reply->ignoreSslErrors(); } add(new Reply(reply, timeOut)); } -void ReplyList::add(Reply *reply) { +void ReplyList::add(Reply* reply) +{ replies_.append(reply); } -void ReplyList::remove(QNetworkReply *reply) { - Reply *o2Reply = find(reply); +void ReplyList::remove(QNetworkReply* reply) +{ + Reply* o2Reply = find(reply); if (o2Reply) { o2Reply->stop(); (void)replies_.removeOne(o2Reply); @@ -45,8 +51,9 @@ void ReplyList::remove(QNetworkReply *reply) { } } -Reply *ReplyList::find(QNetworkReply *reply) { - foreach (Reply *timedReply, replies_) { +Reply* ReplyList::find(QNetworkReply* reply) +{ + foreach (Reply* timedReply, replies_) { if (timedReply->reply == reply) { return timedReply; } @@ -64,4 +71,4 @@ void ReplyList::setIgnoreSslErrors(bool ignoreSslErrors) ignoreSslErrors_ = ignoreSslErrors; } -} +} // namespace Katabasis diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index 646e2e3ea..10cfa2ac0 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -71,7 +71,6 @@ import java.util.Map; * recommended. */ public final class Launcher extends Applet implements AppletStub { - private static final long serialVersionUID = 1L; private final Map params = new HashMap<>(); @@ -207,12 +206,10 @@ public final class Launcher extends Applet implements AppletStub { } @Override - public void paint(Graphics graphics) { - } + public void paint(Graphics graphics) {} @Override - public void update(Graphics graphics) { - } + public void update(Graphics graphics) {} public void setParameter(String key, String value) { params.put(key, value); @@ -221,5 +218,4 @@ public final class Launcher extends Applet implements AppletStub { public void setParameter(String key, boolean value) { setParameter(key, value ? "true" : "false"); } - } diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 78804b3e8..4b59c1da6 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -54,10 +54,6 @@ package org.prismlauncher; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; - import org.prismlauncher.exception.ParseException; import org.prismlauncher.launcher.Launcher; import org.prismlauncher.launcher.impl.StandardLauncher; @@ -65,8 +61,11 @@ import org.prismlauncher.launcher.impl.legacy.LegacyLauncher; import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.logging.Log; -public final class EntryPoint { +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +public final class EntryPoint { public static void main(String[] args) { ExitCode code = listen(); @@ -160,19 +159,18 @@ public final class EntryPoint { return PreLaunchAction.PROCEED; } - private enum PreLaunchAction { - PROCEED, LAUNCH, ABORT - } + private enum PreLaunchAction { PROCEED, LAUNCH, ABORT } private enum ExitCode { - NORMAL(0), ABORT(1), ERROR(2), ILLEGAL_ARGUMENT(65); + NORMAL(0), + ABORT(1), + ERROR(2), + ILLEGAL_ARGUMENT(65); private final int numeric; ExitCode(int numeric) { this.numeric = numeric; } - } - } diff --git a/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java b/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java index 524076ff7..e9abc5ee3 100644 --- a/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java +++ b/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java @@ -38,11 +38,9 @@ package org.prismlauncher.exception; public final class ParameterNotFoundException extends IllegalArgumentException { - private static final long serialVersionUID = 1L; public ParameterNotFoundException(String key) { super(String.format("Required parameter '%s' was not found", key)); } - } diff --git a/libraries/launcher/org/prismlauncher/exception/ParseException.java b/libraries/launcher/org/prismlauncher/exception/ParseException.java index 4608fdd17..ae0578958 100644 --- a/libraries/launcher/org/prismlauncher/exception/ParseException.java +++ b/libraries/launcher/org/prismlauncher/exception/ParseException.java @@ -38,11 +38,9 @@ package org.prismlauncher.exception; public final class ParseException extends IllegalArgumentException { - private static final long serialVersionUID = 1L; public ParseException(String input, String format) { super(String.format("For input '%s' - should match '%s'", input, format)); } - } diff --git a/libraries/launcher/org/prismlauncher/launcher/Launcher.java b/libraries/launcher/org/prismlauncher/launcher/Launcher.java index 049a83d84..5dc0bb10d 100644 --- a/libraries/launcher/org/prismlauncher/launcher/Launcher.java +++ b/libraries/launcher/org/prismlauncher/launcher/Launcher.java @@ -38,7 +38,5 @@ package org.prismlauncher.launcher; public interface Launcher { - void launch() throws Throwable; - } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index 0c2153a9d..761837041 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -54,15 +54,14 @@ package org.prismlauncher.launcher.impl; -import java.util.ArrayList; -import java.util.List; - import org.prismlauncher.exception.ParseException; import org.prismlauncher.launcher.Launcher; import org.prismlauncher.utils.Parameters; -public abstract class AbstractLauncher implements Launcher { +import java.util.ArrayList; +import java.util.List; +public abstract class AbstractLauncher implements Launcher { private static final int DEFAULT_WINDOW_WIDTH = 854, DEFAULT_WINDOW_HEIGHT = 480; // parameters, separated from ParamBucket @@ -106,5 +105,4 @@ public abstract class AbstractLauncher implements Launcher { throw new ParseException(windowParams, "[width]x[height]"); } } - } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index 9436ff15e..186909123 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -54,13 +54,12 @@ package org.prismlauncher.launcher.impl; -import java.lang.invoke.MethodHandle; - import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.ReflectionUtils; -public final class StandardLauncher extends AbstractLauncher { +import java.lang.invoke.MethodHandle; +public final class StandardLauncher extends AbstractLauncher { public StandardLauncher(Parameters params) { super(params); } @@ -87,5 +86,4 @@ public final class StandardLauncher extends AbstractLauncher { MethodHandle method = ReflectionUtils.findMainMethod(mainClassName); method.invokeExact(gameArgs.toArray(new String[0])); } - } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java index c215e7fe0..6cfe35d86 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java @@ -54,6 +54,8 @@ package org.prismlauncher.launcher.impl.legacy; +import org.prismlauncher.utils.logging.Log; + import java.applet.Applet; import java.awt.Dimension; import java.awt.event.WindowAdapter; @@ -70,12 +72,9 @@ import java.util.List; import javax.imageio.ImageIO; import javax.swing.JFrame; -import org.prismlauncher.utils.logging.Log; - import net.minecraft.Launcher; public final class LegacyFrame extends JFrame { - private static final long serialVersionUID = 1L; private final Launcher launcher; @@ -96,8 +95,8 @@ public final class LegacyFrame extends JFrame { addWindowListener(new ForceExitHandler()); } - public void start(String user, String session, int width, int height, boolean maximize, String serverAddress, - String serverPort, boolean demo) { + public void start( + String user, String session, int width, int height, boolean maximize, String serverAddress, String serverPort, boolean demo) { // Implements support for launching in to multiplayer on classic servers using a // mpticket file generated by an external program and stored in the instance's // root folder. @@ -157,7 +156,6 @@ public final class LegacyFrame extends JFrame { } private final class ForceExitHandler extends WindowAdapter { - @Override public void windowClosing(WindowEvent event) { // FIXME better solution @@ -184,7 +182,5 @@ public final class LegacyFrame extends JFrame { // old minecraft versions can hang without this >_< System.exit(0); } - } - } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java index d349177b2..5c5f86d47 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java @@ -55,22 +55,21 @@ package org.prismlauncher.launcher.impl.legacy; +import org.prismlauncher.launcher.impl.AbstractLauncher; +import org.prismlauncher.utils.Parameters; +import org.prismlauncher.utils.ReflectionUtils; +import org.prismlauncher.utils.logging.Log; + import java.io.File; import java.lang.invoke.MethodHandle; import java.lang.reflect.Field; import java.util.Collections; import java.util.List; -import org.prismlauncher.launcher.impl.AbstractLauncher; -import org.prismlauncher.utils.Parameters; -import org.prismlauncher.utils.ReflectionUtils; -import org.prismlauncher.utils.logging.Log; - /** * Used to launch old versions that support applets. */ public final class LegacyLauncher extends AbstractLauncher { - private final String user, session; private final String title; private final String appletClass; @@ -109,8 +108,7 @@ public final class LegacyLauncher extends AbstractLauncher { try { LegacyFrame window = new LegacyFrame(title, ReflectionUtils.createAppletClass(appletClass)); - window.start(user, session, width, height, maximize, serverAddress, serverPort, - gameArgs.contains("--demo")); + window.start(user, session, width, height, maximize, serverAddress, serverPort, gameArgs.contains("--demo")); return; } catch (Throwable e) { Log.error("Running applet wrapper failed with exception; falling back to main class", e); @@ -122,5 +120,4 @@ public final class LegacyLauncher extends AbstractLauncher { MethodHandle method = ReflectionUtils.findMainMethod(main); method.invokeExact(gameArgs.toArray(new String[0])); } - } diff --git a/libraries/launcher/org/prismlauncher/utils/Parameters.java b/libraries/launcher/org/prismlauncher/utils/Parameters.java index 6365753e2..2b0aed22a 100644 --- a/libraries/launcher/org/prismlauncher/utils/Parameters.java +++ b/libraries/launcher/org/prismlauncher/utils/Parameters.java @@ -54,15 +54,14 @@ package org.prismlauncher.utils; +import org.prismlauncher.exception.ParameterNotFoundException; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.prismlauncher.exception.ParameterNotFoundException; - public final class Parameters { - private final Map> map = new HashMap<>(); public void add(String key, String value) { @@ -112,5 +111,4 @@ public final class Parameters { return params.get(0); } - } diff --git a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java index dd212ef93..ad222bd2a 100644 --- a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java @@ -54,6 +54,8 @@ package org.prismlauncher.utils; +import org.prismlauncher.utils.logging.Log; + import java.applet.Applet; import java.io.File; import java.lang.invoke.MethodHandle; @@ -62,10 +64,7 @@ import java.lang.invoke.MethodType; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import org.prismlauncher.utils.logging.Log; - public final class ReflectionUtils { - private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); private static final ClassLoader LOADER = ClassLoader.getSystemClassLoader(); @@ -146,9 +145,7 @@ public final class ReflectionUtils { * @throws NoSuchMethodException * @throws IllegalAccessException */ - public static MethodHandle findMainMethod(String clazz) - throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException { + public static MethodHandle findMainMethod(String clazz) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException { return findMainMethod(LOADER.loadClass(clazz)); } - } diff --git a/libraries/launcher/org/prismlauncher/utils/logging/Level.java b/libraries/launcher/org/prismlauncher/utils/logging/Level.java index 552b0b55e..7af9c34a0 100644 --- a/libraries/launcher/org/prismlauncher/utils/logging/Level.java +++ b/libraries/launcher/org/prismlauncher/utils/logging/Level.java @@ -55,5 +55,4 @@ public enum Level { this.name = name; this.stderr = stderr; } - } diff --git a/libraries/launcher/org/prismlauncher/utils/logging/Log.java b/libraries/launcher/org/prismlauncher/utils/logging/Log.java index e3aa538b2..992698e55 100644 --- a/libraries/launcher/org/prismlauncher/utils/logging/Log.java +++ b/libraries/launcher/org/prismlauncher/utils/logging/Log.java @@ -43,9 +43,8 @@ import java.io.PrintStream; * messages. */ public final class Log { - // original before possibly overridden by MC - private static final PrintStream OUT = new PrintStream(System.out), ERR = new PrintStream(System.err); + private static final PrintStream OUT = new PrintStream(System.out), ERR = new PrintStream(System.err); private static final boolean DEBUG = Boolean.getBoolean("org.prismlauncher.debug"); public static void launcher(String message) { @@ -100,5 +99,4 @@ public final class Log { else OUT.println(message); } - } diff --git a/libraries/libnbtplusplus b/libraries/libnbtplusplus index 2203af7ee..a5e8fd52b 160000 --- a/libraries/libnbtplusplus +++ b/libraries/libnbtplusplus @@ -1 +1 @@ -Subproject commit 2203af7eeb48c45398139b583615134efd8d407f +Subproject commit a5e8fd52b8bf4ab5d5bcc042b2a247867589985f diff --git a/libraries/murmur2/src/MurmurHash2.h b/libraries/murmur2/src/MurmurHash2.h index dc2c9681d..5d4f48713 100644 --- a/libraries/murmur2/src/MurmurHash2.h +++ b/libraries/murmur2/src/MurmurHash2.h @@ -16,11 +16,11 @@ //----------------------------------------------------------------------------- #define KiB 1024 -#define MiB 1024*KiB +#define MiB 1024 * KiB uint32_t MurmurHash2( std::ifstream&& file_stream, - std::size_t buffer_size = 4*MiB, + std::size_t buffer_size = 4 * MiB, std::function filter_out = [](char) { return false; }); struct IncrementalHashInfo { diff --git a/libraries/rainbow/include/rainbow.h b/libraries/rainbow/include/rainbow.h index 57be87f14..a75e53ee7 100644 --- a/libraries/rainbow/include/rainbow.h +++ b/libraries/rainbow/include/rainbow.h @@ -29,8 +29,7 @@ class QColor; /** * A set of methods used to work with colors. */ -namespace Rainbow -{ +namespace Rainbow { /** * Calculate the luma of a color. Luma is weighted sum of gamma-adjusted * R'G'B' components of a color. The result is similar to qGray. The range @@ -41,14 +40,13 @@ namespace Rainbow * * @see http://en.wikipedia.org/wiki/Luma_(video) */ -qreal luma(const QColor &); +qreal luma(const QColor&); /** * Calculate hue, chroma and luma of a color in one call. * @since 5.0 */ -void getHcy(const QColor &, qreal *hue, qreal *chroma, qreal *luma, - qreal *alpha = 0); +void getHcy(const QColor&, qreal* hue, qreal* chroma, qreal* luma, qreal* alpha = 0); /** * Calculate the contrast ratio between two colors, according to the @@ -62,7 +60,7 @@ void getHcy(const QColor &, qreal *hue, qreal *chroma, qreal *luma, * * @see Rainbow::luma */ -qreal contrastRatio(const QColor &, const QColor &); +qreal contrastRatio(const QColor&, const QColor&); /** * Adjust the luma of a color by changing its distance from white. @@ -79,8 +77,7 @@ qreal contrastRatio(const QColor &, const QColor &); * component of the color; 1.0 means no change, 0.0 maximizes chroma * @see Rainbow::shade */ -QColor -lighten(const QColor &, qreal amount = 0.5, qreal chromaInverseGain = 1.0); +QColor lighten(const QColor&, qreal amount = 0.5, qreal chromaInverseGain = 1.0); /** * Adjust the luma of a color by changing its distance from black. @@ -97,7 +94,7 @@ lighten(const QColor &, qreal amount = 0.5, qreal chromaInverseGain = 1.0); * component of the color; 1.0 means no change, 0.0 minimizes chroma * @see Rainbow::shade */ -QColor darken(const QColor &, qreal amount = 0.5, qreal chromaGain = 1.0); +QColor darken(const QColor&, qreal amount = 0.5, qreal chromaGain = 1.0); /** * Adjust the luma and chroma components of a color. The amount is added @@ -111,7 +108,7 @@ QColor darken(const QColor &, qreal amount = 0.5, qreal chromaGain = 1.0); * 1.0 maximizes chroma * @see Rainbow::luma */ -QColor shade(const QColor &, qreal lumaAmount, qreal chromaAmount = 0.0); +QColor shade(const QColor&, qreal lumaAmount, qreal chromaAmount = 0.0); /** * Create a new color by tinting one color with another. This function is @@ -125,7 +122,7 @@ QColor shade(const QColor &, qreal lumaAmount, qreal chromaAmount = 0.0); * @param amount how strongly to tint the base; 0.0 gives @p base, * 1.0 gives @p color */ -QColor tint(const QColor &base, const QColor &color, qreal amount = 0.3); +QColor tint(const QColor& base, const QColor& color, qreal amount = 0.3); /** * Blend two colors into a new color by linear combination. @@ -138,7 +135,7 @@ QColor tint(const QColor &base, const QColor &color, qreal amount = 0.3); * @p bias >= 1 gives @p c2. @p bias == 0.5 gives a 50% blend of @p c1 * and @p c2. */ -QColor mix(const QColor &c1, const QColor &c2, qreal bias = 0.5); +QColor mix(const QColor& c1, const QColor& c2, qreal bias = 0.5); /** * Blend two colors into a new color by painting the second color over the @@ -152,7 +149,5 @@ QColor mix(const QColor &c1, const QColor &c2, qreal bias = 0.5); * @param paint the color to be overlayed onto the base color. * @param comp the CompositionMode used to do the blending. */ -QColor -overlayColors(const QColor &base, const QColor &paint, - QPainter::CompositionMode comp = QPainter::CompositionMode_SourceOver); -} +QColor overlayColors(const QColor& base, const QColor& paint, QPainter::CompositionMode comp = QPainter::CompositionMode_SourceOver); +} // namespace Rainbow diff --git a/libraries/rainbow/src/rainbow.cpp b/libraries/rainbow/src/rainbow.cpp index 9054d71dd..5ef196520 100644 --- a/libraries/rainbow/src/rainbow.cpp +++ b/libraries/rainbow/src/rainbow.cpp @@ -25,11 +25,11 @@ #include #include -#include // qIsNaN +#include // qIsNaN #include -//BEGIN internal helper functions +// BEGIN internal helper functions static inline qreal wrap(qreal a, qreal d = 1.0) { @@ -44,23 +44,21 @@ static inline qreal normalize(qreal a) return (a < 1.0 ? (a > 0.0 ? a : 0.0) : 1.0); } - /////////////////////////////////////////////////////////////////////////////// // HCY color space -#define HCY_REC 709 // use 709 for now +#define HCY_REC 709 // use 709 for now #if HCY_REC == 601 -static const qreal yc[3] = {0.299, 0.587, 0.114}; +static const qreal yc[3] = { 0.299, 0.587, 0.114 }; #elif HCY_REC == 709 -static const qreal yc[3] = {0.2126, 0.7152, 0.0722}; -#else // use Qt values -static const qreal yc[3] = {0.34375, 0.5, 0.15625}; +static const qreal yc[3] = { 0.2126, 0.7152, 0.0722 }; +#else // use Qt values +static const qreal yc[3] = { 0.34375, 0.5, 0.15625 }; #endif -class KHCY -{ -public: - explicit KHCY(const QColor &color) +class KHCY { + public: + explicit KHCY(const QColor& color) { qreal r = gamma(color.redF()); qreal g = gamma(color.greenF()); @@ -74,30 +72,20 @@ public: qreal p = qMax(qMax(r, g), b); qreal n = qMin(qMin(r, g), b); qreal d = 6.0 * (p - n); - if (n == p) - { + if (n == p) { h = 0.0; - } - else if (r == p) - { + } else if (r == p) { h = ((g - b) / d); - } - else if (g == p) - { + } else if (g == p) { h = ((b - r) / d) + (1.0 / 3.0); - } - else - { + } else { h = ((r - g) / d) + (2.0 / 3.0); } // chroma component - if (r == g && g == b) - { + if (r == g && g == b) { c = 0.0; - } - else - { + } else { c = qMax((y - n) / y, (p - y) / (1 - y)); } } @@ -118,145 +106,103 @@ public: // calculate some needed variables qreal _hs = _h * 6.0, th, tm; - if (_hs < 1.0) - { + if (_hs < 1.0) { th = _hs; tm = yc[0] + yc[1] * th; - } - else if (_hs < 2.0) - { + } else if (_hs < 2.0) { th = 2.0 - _hs; tm = yc[1] + yc[0] * th; - } - else if (_hs < 3.0) - { + } else if (_hs < 3.0) { th = _hs - 2.0; tm = yc[1] + yc[2] * th; - } - else if (_hs < 4.0) - { + } else if (_hs < 4.0) { th = 4.0 - _hs; tm = yc[2] + yc[1] * th; - } - else if (_hs < 5.0) - { + } else if (_hs < 5.0) { th = _hs - 4.0; tm = yc[2] + yc[0] * th; - } - else - { + } else { th = 6.0 - _hs; tm = yc[0] + yc[2] * th; } // calculate RGB channels in sorted order qreal tn, to, tp; - if (tm >= _y) - { + if (tm >= _y) { tp = _y + _y * _c * (1.0 - tm) / tm; to = _y + _y * _c * (th - tm) / tm; tn = _y - (_y * _c); - } - else - { + } else { tp = _y + (1.0 - _y) * _c; to = _y + (1.0 - _y) * _c * (th - tm) / (1.0 - tm); tn = _y - (1.0 - _y) * _c * tm / (1.0 - tm); } // return RGB channels in appropriate order - if (_hs < 1.0) - { + if (_hs < 1.0) { return QColor::fromRgbF(igamma(tp), igamma(to), igamma(tn), a); - } - else if (_hs < 2.0) - { + } else if (_hs < 2.0) { return QColor::fromRgbF(igamma(to), igamma(tp), igamma(tn), a); - } - else if (_hs < 3.0) - { + } else if (_hs < 3.0) { return QColor::fromRgbF(igamma(tn), igamma(tp), igamma(to), a); - } - else if (_hs < 4.0) - { + } else if (_hs < 4.0) { return QColor::fromRgbF(igamma(tn), igamma(to), igamma(tp), a); - } - else if (_hs < 5.0) - { + } else if (_hs < 5.0) { return QColor::fromRgbF(igamma(to), igamma(tn), igamma(tp), a); - } - else - { + } else { return QColor::fromRgbF(igamma(tp), igamma(tn), igamma(to), a); } } qreal h, c, y, a; - static qreal luma(const QColor &color) - { - return lumag(gamma(color.redF()), gamma(color.greenF()), gamma(color.blueF())); - } + static qreal luma(const QColor& color) { return lumag(gamma(color.redF()), gamma(color.greenF()), gamma(color.blueF())); } -private: - static qreal gamma(qreal n) - { - return pow(normalize(n), 2.2); - } - static qreal igamma(qreal n) - { - return pow(normalize(n), 1.0 / 2.2); - } - static qreal lumag(qreal r, qreal g, qreal b) - { - return r * yc[0] + g * yc[1] + b * yc[2]; - } + private: + static qreal gamma(qreal n) { return pow(normalize(n), 2.2); } + static qreal igamma(qreal n) { return pow(normalize(n), 1.0 / 2.2); } + static qreal lumag(qreal r, qreal g, qreal b) { return r * yc[0] + g * yc[1] + b * yc[2]; } }; static inline qreal mixQreal(qreal a, qreal b, qreal bias) { return a + (b - a) * bias; } -//END internal helper functions +// END internal helper functions -qreal Rainbow::luma(const QColor &color) +qreal Rainbow::luma(const QColor& color) { return KHCY::luma(color); } -void Rainbow::getHcy(const QColor &color, qreal *h, qreal *c, qreal *y, qreal *a) +void Rainbow::getHcy(const QColor& color, qreal* h, qreal* c, qreal* y, qreal* a) { - if (!c || !h || !y) - { + if (!c || !h || !y) { return; } KHCY khcy(color); *c = khcy.c; *h = khcy.h; *y = khcy.y; - if (a) - { + if (a) { *a = khcy.a; } } static qreal contrastRatioForLuma(qreal y1, qreal y2) { - if (y1 > y2) - { + if (y1 > y2) { return (y1 + 0.05) / (y2 + 0.05); - } - else - { + } else { return (y2 + 0.05) / (y1 + 0.05); } } -qreal Rainbow::contrastRatio(const QColor &c1, const QColor &c2) +qreal Rainbow::contrastRatio(const QColor& c1, const QColor& c2) { return contrastRatioForLuma(luma(c1), luma(c2)); } -QColor Rainbow::lighten(const QColor &color, qreal ky, qreal kc) +QColor Rainbow::lighten(const QColor& color, qreal ky, qreal kc) { KHCY c(color); c.y = 1.0 - normalize((1.0 - c.y) * (1.0 - ky)); @@ -264,7 +210,7 @@ QColor Rainbow::lighten(const QColor &color, qreal ky, qreal kc) return c.qColor(); } -QColor Rainbow::darken(const QColor &color, qreal ky, qreal kc) +QColor Rainbow::darken(const QColor& color, qreal ky, qreal kc) { KHCY c(color); c.y = normalize(c.y * (1.0 - ky)); @@ -272,7 +218,7 @@ QColor Rainbow::darken(const QColor &color, qreal ky, qreal kc) return c.qColor(); } -QColor Rainbow::shade(const QColor &color, qreal ky, qreal kc) +QColor Rainbow::shade(const QColor& color, qreal ky, qreal kc) { KHCY c(color); c.y = normalize(c.y + ky); @@ -280,7 +226,7 @@ QColor Rainbow::shade(const QColor &color, qreal ky, qreal kc) return c.qColor(); } -static QColor tintHelper(const QColor &base, qreal baseLuma, const QColor &color, qreal amount) +static QColor tintHelper(const QColor& base, qreal baseLuma, const QColor& color, qreal amount) { KHCY result(Rainbow::mix(base, color, pow(amount, 0.3))); result.y = mixQreal(baseLuma, result.y, amount); @@ -288,55 +234,45 @@ static QColor tintHelper(const QColor &base, qreal baseLuma, const QColor &color return result.qColor(); } -QColor Rainbow::tint(const QColor &base, const QColor &color, qreal amount) +QColor Rainbow::tint(const QColor& base, const QColor& color, qreal amount) { - if (amount <= 0.0) - { + if (amount <= 0.0) { return base; } - if (amount >= 1.0) - { + if (amount >= 1.0) { return color; } - if (qIsNaN(amount)) - { + if (qIsNaN(amount)) { return base; } - qreal baseLuma = luma(base); // cache value because luma call is expensive + qreal baseLuma = luma(base); // cache value because luma call is expensive double ri = contrastRatioForLuma(baseLuma, luma(color)); double rg = 1.0 + ((ri + 1.0) * amount * amount * amount); double u = 1.0, l = 0.0; QColor result; - for (int i = 12; i; --i) - { + for (int i = 12; i; --i) { double a = 0.5 * (l + u); result = tintHelper(base, baseLuma, color, a); double ra = contrastRatioForLuma(baseLuma, luma(result)); - if (ra > rg) - { + if (ra > rg) { u = a; - } - else - { + } else { l = a; } } return result; } -QColor Rainbow::mix(const QColor &c1, const QColor &c2, qreal bias) +QColor Rainbow::mix(const QColor& c1, const QColor& c2, qreal bias) { - if (bias <= 0.0) - { + if (bias <= 0.0) { return c1; } - if (bias >= 1.0) - { + if (bias >= 1.0) { return c2; } - if (qIsNaN(bias)) - { + if (qIsNaN(bias)) { return c1; } @@ -348,15 +284,14 @@ QColor Rainbow::mix(const QColor &c1, const QColor &c2, qreal bias) return QColor::fromRgbF(r, g, b, a); } -QColor Rainbow::overlayColors(const QColor &base, const QColor &paint, - QPainter::CompositionMode comp) +QColor Rainbow::overlayColors(const QColor& base, const QColor& paint, QPainter::CompositionMode comp) { // This isn't the fastest way, but should be "fast enough". // It's also the only safe way to use QPainter::CompositionMode QImage img(1, 1, QImage::Format_ARGB32_Premultiplied); QPainter p(&img); QColor start = base; - start.setAlpha(255); // opaque + start.setAlpha(255); // opaque p.fillRect(0, 0, 1, 1, start); p.setCompositionMode(comp); p.fillRect(0, 0, 1, 1, paint); diff --git a/libraries/systeminfo/include/distroutils.h b/libraries/systeminfo/include/distroutils.h index 2e85f4336..caa2688cf 100644 --- a/libraries/systeminfo/include/distroutils.h +++ b/libraries/systeminfo/include/distroutils.h @@ -1,23 +1,22 @@ -#include "sys.h" #include +#include "sys.h" namespace Sys { -struct LsbInfo -{ +struct LsbInfo { QString distributor; QString version; QString description; QString codename; }; -bool main_lsb_info(LsbInfo & out); -bool fallback_lsb_info(Sys::LsbInfo & out); -void lsb_postprocess(Sys::LsbInfo & lsb, Sys::DistributionInfo & out); +bool main_lsb_info(LsbInfo& out); +bool fallback_lsb_info(Sys::LsbInfo& out); +void lsb_postprocess(Sys::LsbInfo& lsb, Sys::DistributionInfo& out); Sys::DistributionInfo read_lsb_release(); -QString _extract_distribution(const QString & x); -QString _extract_version(const QString & x); +QString _extract_distribution(const QString& x); +QString _extract_version(const QString& x); Sys::DistributionInfo read_legacy_release(); Sys::DistributionInfo read_os_release(); -} +} // namespace Sys diff --git a/libraries/systeminfo/include/sys.h b/libraries/systeminfo/include/sys.h index 6a6a7c82f..dfebbe90b 100644 --- a/libraries/systeminfo/include/sys.h +++ b/libraries/systeminfo/include/sys.h @@ -1,19 +1,12 @@ #pragma once #include -namespace Sys -{ +namespace Sys { const uint64_t mebibyte = 1024ull * 1024ull; -enum class KernelType { - Undetermined, - Windows, - Darwin, - Linux -}; +enum class KernelType { Undetermined, Windows, Darwin, Linux }; -struct KernelInfo -{ +struct KernelInfo { QString kernelName; QString kernelVersion; @@ -26,25 +19,18 @@ struct KernelInfo KernelInfo getKernelInfo(); -struct DistributionInfo -{ +struct DistributionInfo { DistributionInfo operator+(const DistributionInfo& rhs) const { DistributionInfo out; - if(!distributionName.isEmpty()) - { + if (!distributionName.isEmpty()) { out.distributionName = distributionName; - } - else - { + } else { out.distributionName = rhs.distributionName; } - if(!distributionVersion.isEmpty()) - { + if (!distributionVersion.isEmpty()) { out.distributionVersion = distributionVersion; - } - else - { + } else { out.distributionVersion = rhs.distributionVersion; } return out; @@ -56,4 +42,4 @@ struct DistributionInfo DistributionInfo getDistributionInfo(); uint64_t getSystemRam(); -} +} // namespace Sys diff --git a/libraries/systeminfo/src/distroutils.cpp b/libraries/systeminfo/src/distroutils.cpp index 05e1bb8cd..57e6c8320 100644 --- a/libraries/systeminfo/src/distroutils.cpp +++ b/libraries/systeminfo/src/distroutils.cpp @@ -29,14 +29,14 @@ SOFTWARE. #include "distroutils.h" -#include -#include -#include -#include -#include #include #include +#include +#include +#include #include +#include +#include #include @@ -46,38 +46,27 @@ Sys::DistributionInfo Sys::read_os_release() QStringList files = { "/etc/os-release", "/usr/lib/os-release" }; QString name; QString version; - for (auto &file: files) - { - if(!QFile::exists(file)) - { + for (auto& file : files) { + if (!QFile::exists(file)) { continue; } QSettings settings(file, QSettings::IniFormat); - if(settings.contains("ID")) - { + if (settings.contains("ID")) { name = settings.value("ID").toString().toLower(); - } - else if (settings.contains("NAME")) - { + } else if (settings.contains("NAME")) { name = settings.value("NAME").toString().toLower(); - } - else - { + } else { continue; } - if(settings.contains("VERSION_ID")) - { + if (settings.contains("VERSION_ID")) { version = settings.value("VERSION_ID").toString().toLower(); - } - else if(settings.contains("VERSION")) - { + } else if (settings.contains("VERSION")) { version = settings.value("VERSION").toString().toLower(); } break; } - if(name.isEmpty()) - { + if (name.isEmpty()) { return out; } out.distributionName = name; @@ -85,33 +74,31 @@ Sys::DistributionInfo Sys::read_os_release() return out; } -bool Sys::main_lsb_info(Sys::LsbInfo & out) +bool Sys::main_lsb_info(Sys::LsbInfo& out) { - int status=0; + int status = 0; QProcess lsbProcess; QStringList arguments; arguments << "-a"; - lsbProcess.start("lsb_release", arguments); + lsbProcess.start("lsb_release", arguments); lsbProcess.waitForFinished(); status = lsbProcess.exitStatus(); QString output = lsbProcess.readAllStandardOutput(); qDebug() << output; lsbProcess.close(); - if(status == 0) - { + if (status == 0) { auto lines = output.split('\n'); - for(auto line:lines) - { + for (auto line : lines) { int index = line.indexOf(':'); auto key = line.left(index).trimmed(); auto value = line.mid(index + 1).toLower().trimmed(); - if(key == "Distributor ID") + if (key == "Distributor ID") out.distributor = value; - else if(key == "Release") + else if (key == "Release") out.version = value; - else if(key == "Description") + else if (key == "Description") out.description = value; - else if(key == "Codename") + else if (key == "Codename") out.codename = value; } return !out.distributor.isEmpty(); @@ -119,7 +106,7 @@ bool Sys::main_lsb_info(Sys::LsbInfo & out) return false; } -bool Sys::fallback_lsb_info(Sys::LsbInfo & out) +bool Sys::fallback_lsb_info(Sys::LsbInfo& out) { // running lsb_release failed, try to read the file instead // /etc/lsb-release format, if the file even exists, is non-standard. @@ -127,15 +114,12 @@ bool Sys::fallback_lsb_info(Sys::LsbInfo & out) // distributions install an /etc/lsb-release as part of the base // distribution, but `lsb_release` remains optional. QString file = "/etc/lsb-release"; - if (QFile::exists(file)) - { + if (QFile::exists(file)) { QSettings settings(file, QSettings::IniFormat); - if(settings.contains("DISTRIB_ID")) - { + if (settings.contains("DISTRIB_ID")) { out.distributor = settings.value("DISTRIB_ID").toString().toLower(); } - if(settings.contains("DISTRIB_RELEASE")) - { + if (settings.contains("DISTRIB_RELEASE")) { out.version = settings.value("DISTRIB_RELEASE").toString().toLower(); } return !out.distributor.isEmpty(); @@ -143,48 +127,34 @@ bool Sys::fallback_lsb_info(Sys::LsbInfo & out) return false; } -void Sys::lsb_postprocess(Sys::LsbInfo & lsb, Sys::DistributionInfo & out) +void Sys::lsb_postprocess(Sys::LsbInfo& lsb, Sys::DistributionInfo& out) { QString dist = lsb.distributor; QString vers = lsb.version; - if(dist.startsWith("redhatenterprise")) - { + if (dist.startsWith("redhatenterprise")) { dist = "rhel"; - } - else if(dist == "archlinux") - { + } else if (dist == "archlinux") { dist = "arch"; - } - else if (dist.startsWith("suse")) - { - if(lsb.description.startsWith("opensuse")) - { + } else if (dist.startsWith("suse")) { + if (lsb.description.startsWith("opensuse")) { dist = "opensuse"; - } - else if (lsb.description.startsWith("suse linux enterprise")) - { + } else if (lsb.description.startsWith("suse linux enterprise")) { dist = "sles"; } - } - else if (dist == "debian" and vers == "testing") - { + } else if (dist == "debian" and vers == "testing") { vers = lsb.codename; - } - else - { + } else { // ubuntu, debian, gentoo, scientific, slackware, ... ? #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) auto parts = dist.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts); #else auto parts = dist.split(QRegularExpression("\\s+"), QString::SkipEmptyParts); #endif - if(parts.size()) - { + if (parts.size()) { dist = parts[0]; } } - if(!dist.isEmpty()) - { + if (!dist.isEmpty()) { out.distributionName = dist; out.distributionVersion = vers; } @@ -193,10 +163,8 @@ void Sys::lsb_postprocess(Sys::LsbInfo & lsb, Sys::DistributionInfo & out) Sys::DistributionInfo Sys::read_lsb_release() { LsbInfo lsb; - if(!main_lsb_info(lsb)) - { - if(!fallback_lsb_info(lsb)) - { + if (!main_lsb_info(lsb)) { + if (!fallback_lsb_info(lsb)) { return Sys::DistributionInfo(); } } @@ -205,15 +173,13 @@ Sys::DistributionInfo Sys::read_lsb_release() return out; } -QString Sys::_extract_distribution(const QString & x) +QString Sys::_extract_distribution(const QString& x) { QString release = x.toLower(); - if (release.startsWith("red hat enterprise")) - { + if (release.startsWith("red hat enterprise")) { return "rhel"; } - if (release.startsWith("suse linux enterprise")) - { + if (release.startsWith("suse linux enterprise")) { return "sles"; } #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) @@ -221,14 +187,13 @@ QString Sys::_extract_distribution(const QString & x) #else QStringList list = release.split(QRegularExpression("\\s+"), QString::SkipEmptyParts); #endif - if(list.size()) - { + if (list.size()) { return list[0]; } return QString(); } -QString Sys::_extract_version(const QString & x) +QString Sys::_extract_version(const QString& x) { QRegularExpression versionish_string(QRegularExpression::anchoredPattern("\\d+(?:\\.\\d+)*$")); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) @@ -236,11 +201,9 @@ QString Sys::_extract_version(const QString & x) #else QStringList list = x.split(QRegularExpression("\\s+"), QString::SkipEmptyParts); #endif - for(int i = list.size() - 1; i >= 0; --i) - { + for (int i = list.size() - 1; i >= 0; --i) { QString chunk = list[i]; - if(versionish_string.match(chunk).hasMatch()) - { + if (versionish_string.match(chunk).hasMatch()) { return chunk; } } @@ -249,43 +212,35 @@ QString Sys::_extract_version(const QString & x) Sys::DistributionInfo Sys::read_legacy_release() { - struct checkEntry - { + struct checkEntry { QString file; - std::function extract_distro; - std::function extract_version; + std::function extract_distro; + std::function extract_version; }; - QList checks = - { - {"/etc/arch-release", [](const QString &){ return "arch";}, [](const QString &){ return "rolling";}}, - {"/etc/slackware-version", &Sys::_extract_distribution, &Sys::_extract_version}, - {QString(), &Sys::_extract_distribution, &Sys::_extract_version}, - {"/etc/debian_version", [](const QString &){ return "debian";}, [](const QString & x){ return x;}}, + QList checks = { + { "/etc/arch-release", [](const QString&) { return "arch"; }, [](const QString&) { return "rolling"; } }, + { "/etc/slackware-version", &Sys::_extract_distribution, &Sys::_extract_version }, + { QString(), &Sys::_extract_distribution, &Sys::_extract_version }, + { "/etc/debian_version", [](const QString&) { return "debian"; }, [](const QString& x) { return x; } }, }; - for(auto & check: checks) - { + for (auto& check : checks) { QStringList files; - if(check.file.isNull()) - { + if (check.file.isNull()) { QDir etcDir("/etc"); - etcDir.setNameFilters({"*-release"}); + etcDir.setNameFilters({ "*-release" }); etcDir.setFilter(QDir::Files | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden); files = etcDir.entryList(); - } - else - { + } else { files.append(check.file); } - for (auto file : files) - { + for (auto file : files) { QFile relfile(file); - if(!relfile.open(QIODevice::ReadOnly | QIODevice::Text)) + if (!relfile.open(QIODevice::ReadOnly | QIODevice::Text)) continue; QString contents = QString::fromUtf8(relfile.readLine()).trimmed(); QString dist = check.extract_distro(contents); QString vers = check.extract_version(contents); - if(!dist.isEmpty()) - { + if (!dist.isEmpty()) { Sys::DistributionInfo out; out.distributionName = dist; out.distributionVersion = vers; @@ -295,4 +250,3 @@ Sys::DistributionInfo Sys::read_legacy_release() } return Sys::DistributionInfo(); } - diff --git a/libraries/systeminfo/src/sys_apple.cpp b/libraries/systeminfo/src/sys_apple.cpp index b6d62c1ae..5cf70f1aa 100644 --- a/libraries/systeminfo/src/sys_apple.cpp +++ b/libraries/systeminfo/src/sys_apple.cpp @@ -2,9 +2,9 @@ #include +#include #include #include -#include Sys::KernelInfo Sys::getKernelInfo() { @@ -22,18 +22,16 @@ Sys::KernelInfo Sys::getKernelInfo() out.kernelMinor = 0; out.kernelPatch = 0; auto sections = release.split('-'); - if(sections.size() >= 1) { + if (sections.size() >= 1) { auto versionParts = sections[0].split('.'); - if(versionParts.size() >= 3) { + if (versionParts.size() >= 3) { out.kernelMajor = versionParts[0].toInt(); out.kernelMinor = versionParts[1].toInt(); out.kernelPatch = versionParts[2].toInt(); - } - else { + } else { qWarning() << "Not enough version numbers in " << sections[0] << " found " << versionParts.size(); } - } - else { + } else { qWarning() << "Not enough '-' sections in " << release << " found " << sections.size(); } return out; @@ -45,12 +43,9 @@ uint64_t Sys::getSystemRam() { uint64_t memsize; size_t memsizesize = sizeof(memsize); - if(!sysctlbyname("hw.memsize", &memsize, &memsizesize, NULL, 0)) - { + if (!sysctlbyname("hw.memsize", &memsize, &memsizesize, NULL, 0)) { return memsize; - } - else - { + } else { return 0; } } diff --git a/libraries/systeminfo/src/sys_test.cpp b/libraries/systeminfo/src/sys_test.cpp index 9a5f9dfa2..50c75eb77 100644 --- a/libraries/systeminfo/src/sys_test.cpp +++ b/libraries/systeminfo/src/sys_test.cpp @@ -2,11 +2,9 @@ #include -class SysTest : public QObject -{ +class SysTest : public QObject { Q_OBJECT -private -slots: + private slots: void test_kernelNotNull() { @@ -14,15 +12,15 @@ slots: QVERIFY(!kinfo.kernelName.isEmpty()); QVERIFY(kinfo.kernelVersion != "0.0"); } -/* - void test_systemDistroNotNull() - { - auto kinfo = Sys::getDistributionInfo(); - QVERIFY(!kinfo.distributionName.isEmpty()); - QVERIFY(!kinfo.distributionVersion.isEmpty()); - qDebug() << "Distro: " << kinfo.distributionName << "version" << kinfo.distributionVersion; - } -*/ + /* + void test_systemDistroNotNull() + { + auto kinfo = Sys::getDistributionInfo(); + QVERIFY(!kinfo.distributionName.isEmpty()); + QVERIFY(!kinfo.distributionVersion.isEmpty()); + qDebug() << "Distro: " << kinfo.distributionName << "version" << kinfo.distributionVersion; + } + */ }; QTEST_GUILESS_MAIN(SysTest) diff --git a/libraries/systeminfo/src/sys_unix.cpp b/libraries/systeminfo/src/sys_unix.cpp index 3c63e73a4..4e075959a 100644 --- a/libraries/systeminfo/src/sys_unix.cpp +++ b/libraries/systeminfo/src/sys_unix.cpp @@ -6,9 +6,9 @@ #include #include +#include #include #include -#include Sys::KernelInfo Sys::getKernelInfo() { @@ -27,18 +27,16 @@ Sys::KernelInfo Sys::getKernelInfo() out.kernelMinor = 0; out.kernelPatch = 0; auto sections = release.split('-'); - if(sections.size() >= 1) { + if (sections.size() >= 1) { auto versionParts = sections[0].split('.'); - if(versionParts.size() >= 3) { + if (versionParts.size() >= 3) { out.kernelMajor = versionParts[0].toInt(); out.kernelMinor = versionParts[1].toInt(); out.kernelPatch = versionParts[2].toInt(); - } - else { + } else { qWarning() << "Not enough version numbers in " << sections[0] << " found " << versionParts.size(); } - } - else { + } else { qWarning() << "Not enough '-' sections in " << release << " found " << sections.size(); } return out; @@ -49,17 +47,12 @@ uint64_t Sys::getSystemRam() std::string token; #ifdef Q_OS_LINUX std::ifstream file("/proc/meminfo"); - while(file >> token) - { - if(token == "MemTotal:") - { + while (file >> token) { + if (token == "MemTotal:") { uint64_t mem; - if(file >> mem) - { + if (file >> mem) { return mem * 1024ull; - } - else - { + } else { return 0; } } @@ -68,18 +61,16 @@ uint64_t Sys::getSystemRam() } #elif defined(Q_OS_FREEBSD) char buff[512]; - FILE *fp = popen("sysctl hw.physmem", "r"); - if (fp != NULL) - { - while(fgets(buff, 512, fp) != NULL) - { - std::string str(buff); - uint64_t mem = std::stoull(str.substr(12, std::string::npos)); - return mem * 1024ull; - } + FILE* fp = popen("sysctl hw.physmem", "r"); + if (fp != NULL) { + while (fgets(buff, 512, fp) != NULL) { + std::string str(buff); + uint64_t mem = std::stoull(str.substr(12, std::string::npos)); + return mem * 1024ull; + } } #endif - return 0; // nothing found + return 0; // nothing found } Sys::DistributionInfo Sys::getDistributionInfo() @@ -88,18 +79,13 @@ Sys::DistributionInfo Sys::getDistributionInfo() DistributionInfo lsb_info = read_lsb_release(); DistributionInfo legacy_info = read_legacy_release(); DistributionInfo result = systemd_info + lsb_info + legacy_info; - if(result.distributionName.isNull()) - { + if (result.distributionName.isNull()) { result.distributionName = "unknown"; } - if(result.distributionVersion.isNull()) - { - if(result.distributionName == "arch") - { + if (result.distributionVersion.isNull()) { + if (result.distributionName == "arch") { result.distributionVersion = "rolling"; - } - else - { + } else { result.distributionVersion = "unknown"; } } diff --git a/libraries/systeminfo/src/sys_win32.cpp b/libraries/systeminfo/src/sys_win32.cpp index 5bf510cf8..2627761d1 100644 --- a/libraries/systeminfo/src/sys_win32.cpp +++ b/libraries/systeminfo/src/sys_win32.cpp @@ -22,7 +22,7 @@ uint64_t Sys::getSystemRam() { MEMORYSTATUSEX status; status.dwLength = sizeof(status); - GlobalMemoryStatusEx( &status ); + GlobalMemoryStatusEx(&status); // bytes return (uint64_t)status.ullTotalPhys; } diff --git a/nix/NIX.md b/nix/NIX.md deleted file mode 100644 index 980d20e87..000000000 --- a/nix/NIX.md +++ /dev/null @@ -1,83 +0,0 @@ -# Running on Nix - -## Putting it in your system configuration - -### On flakes-enabled nix - -#### Directly installing - -The `prismlauncher` flake provides a package which you can install along with -the rest of your packages - -```nix -# In your flake.nix: -{ - inputs = { - prismlauncher.url = "github:PrismLauncher/PrismLauncher"; - }; -} -``` - -```nix -# And in your system configuration: -environment.systemPackages = [ prismlauncher.packages.${pkgs.system}.prismlauncher ]; - -# Or in your home-manager configuration: -home.packages = [ prismlauncher.packages.${pkgs.system}.prismlauncher ]; -``` - -#### Using the overlay - -Alternatively, you can overlay the prismlauncher version in nixpkgs which will -allow you to install using `pkgs` as you normally would while also using the -latest version - -```nix -# In your flake.nix: -{ - inputs = { - prismlauncher.url = "github:PrismLauncher/PrismLauncher"; - }; -} -``` - -```nix -# And in your system configuration: -nixpkgs.overlays = [ inputs.prismlauncher.overlay ]; -environment.systemPackages = [ pkgs.prismlauncher ]; - -# Or in your home-manager configuration: -config.nixpkgs.overlays = [ inputs.prismlauncher.overlay ]; -home.packages = [ pkgs.prismlauncher ]; -``` - -### Without flakes-enabled nix - -#### Using channels - -```sh -nix-channel --add https://github.com/PrismLauncher/PrismLauncher/archive/master.tar.gz prismlauncher -nix-channel --update prismlauncher -nix-env -iA prismlauncher -``` - -#### Using the overlay - -```nix -# In your configuration.nix: -{ - nixpkgs.overlays = [ - (import (builtins.fetchTarball "https://github.com/PrismLauncher/PrismLauncher/archive/develop.tar.gz")).overlay - ]; - - environment.systemPackages = with pkgs; [ prismlauncher ]; -} -``` - -## Running ad-hoc - -If you're on a flakes-enabled nix you can run the launcher in one-line - -```sh -nix run github:PrismLauncher/PrismLauncher -``` diff --git a/nix/README.md b/nix/README.md new file mode 100644 index 000000000..7a422fc5b --- /dev/null +++ b/nix/README.md @@ -0,0 +1,193 @@ +# Prism Launcher Nix Packaging + +## Installing a stable release (nixpkgs) + +Prism Launcher is packaged in [nixpkgs](https://github.com/NixOS/nixpkgs/) since 22.11. + +See [Package variants](#package-variants) for a list of available packages. + +## Installing a development release (flake) + +We use [garnix](https://garnix.io/) to build and cache our development builds. +If you want to avoid rebuilds you may add the garnix cache to your substitutors. + +Example (NixOS): + +```nix +{...}: +{ + nix.settings = { + trusted-substituters = [ + "https://cache.garnix.io" + ]; + + trusted-public-keys = [ + "cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g=" + ]; + }; +} +``` + +### Using the overlay + +After adding `github:PrismLauncher/PrismLauncher` to your flake inputs, you can add the `default` overlay to your nixpkgs instance. + +Example: + +```nix +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + prismlauncher = { + url = "github:PrismLauncher/PrismLauncher"; + # Optional: Override the nixpkgs input of prismlauncher to use the same revision as the rest of your flake + # Note that overriding any input of prismlauncher may break reproducibility + # inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = {nixpkgs, prismlauncher}: { + nixosConfigurations.foo = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + + modules = [ + ({pkgs, ...}: { + nixpkgs.overlays = [prismlauncher.overlays.default]; + + environment.systemPackages = [pkgs.prismlauncher]; + }) + ]; + }; + } +} +``` + +### Installing the package directly + +Alternatively, if you don't want to use an overlay, you can install Prism Launcher directly by installing the `prismlauncher` package. +This way the installed package is fully reproducible. + +Example: + +```nix +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + prismlauncher = { + url = "github:PrismLauncher/PrismLauncher"; + # Optional: Override the nixpkgs input of prismlauncher to use the same revision as the rest of your flake + # Note that overriding any input of prismlauncher may break reproducibility + # inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = {nixpkgs, prismlauncher}: { + nixosConfigurations.foo = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + + modules = [ + ({pkgs, ...}: { + environment.systemPackages = [prismlauncher.packages.${pkgs.system}.prismlauncher]; + }) + ]; + }; + } +} +``` + +### Installing the package ad-hoc (`nix shell`, `nix run`, etc.) + +You can simply call the default package of this flake. + +Example: + +```shell +nix run github:PrismLauncher/PrismLauncher + +nix shell github:PrismLauncher/PrismLauncher + +nix profile install github:PrismLauncher/PrismLauncher +``` + +## Installing a development release (without flakes) + +We use [garnix](https://garnix.io/) to build and cache our development builds. +If you want to avoid rebuilds you may add the garnix cache to your substitutors. + +Example (NixOS): + +```nix +{...}: +{ + nix.settings = { + trusted-substituters = [ + "https://cache.garnix.io" + ]; + + trusted-public-keys = [ + "cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g=" + ]; + }; +} +``` + +### Using the overlay (`fetchTarball`) + +We use flake-compat to allow using this Flake on a system that doesn't use flakes. + +Example: + +```nix +{pkgs, ...}: { + nixpkgs.overlays = [(import (builtins.fetchTarball "https://github.com/PrismLauncher/PrismLauncher/archive/develop.tar.gz")).overlays.default]; + + environment.systemPackages = [pkgs.prismlauncher]; +} +``` + +### Installing the package directly (`fetchTarball`) + +Alternatively, if you don't want to use an overlay, you can install Prism Launcher directly by installing the `prismlauncher` package. +This way the installed package is fully reproducible. + +Example: + +```nix +{pkgs, ...}: { + environment.systemPackages = [(import (builtins.fetchTarball "https://github.com/PrismLauncher/PrismLauncher/archive/develop.tar.gz")).packages.${pkgs.system}.prismlauncher]; +} +``` + +### Installing the package ad-hoc (`nix-env`) + +You can add this repository as a channel and install its packages that way. + +Example: + +```shell +nix-channel --add https://github.com/PrismLauncher/PrismLauncher/archive/develop.tar.gz prismlauncher + +nix-channel --update prismlauncher + +nix-env -iA prismlauncher.prismlauncher +``` + +## Package variants + +Both Nixpkgs and this repository offer the following packages: + +- `prismlauncher` - Preferred build using Qt 6 +- `prismlauncher-qt5` - Legacy build using Qt 5 (i.e. for Qt 5 theming support) + +Both of these packages also have `-unwrapped` counterparts, that are not wrapped and can therefore be customized even further than what the wrapper packages offer. + +### Customizing wrapped packages + +The wrapped packages (`prismlauncher` and `prismlauncher-qt5`) offer some build parameters to further customize the launcher's environment. + +The following parameters can be overridden: + +- `msaClientID` (default: `null`, requires full rebuild!) Client ID used for Microsoft Authentication +- `gamemodeSupport` (default: `true`) Turn on/off support for [Feral GameMode](https://github.com/FeralInteractive/gamemode) +- `jdks` (default: `[ jdk17 jdk8 ]`) Java runtimes added to `PRISMLAUNCHER_JAVA_PATHS` variable +- `additionalLibs` (default: `[ ]`) Additional libraries that will be added to `LD_LIBRARY_PATH` diff --git a/nix/default.nix b/nix/default.nix index 7bad1440c..47172927a 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -24,9 +24,9 @@ # Supported systems. systems = [ "x86_64-linux" - "x86_64-darwin" "aarch64-linux" - # Disabled due to qtbase being currently broken for "aarch64-darwin." + # Disabled due to our packages not supporting darwin yet. + # "x86_64-darwin" # "aarch64-darwin" ]; } diff --git a/nix/dev.nix b/nix/dev.nix index 635c6bb41..c39e15653 100644 --- a/nix/dev.nix +++ b/nix/dev.nix @@ -19,9 +19,8 @@ nil.enable = true; clang-format = { - enable = - false; # As most of the codebase is **not** formatted, we don't want clang-format yet - types_or = ["c" "c++"]; + enable = true; + types_or = ["c" "c++" "java" "json" "objective-c"]; }; }; }; diff --git a/program_info/org.prismlauncher.PrismLauncher.desktop.in b/program_info/org.prismlauncher.PrismLauncher.desktop.in index 20fabe9d4..816c00595 100644 --- a/program_info/org.prismlauncher.PrismLauncher.desktop.in +++ b/program_info/org.prismlauncher.PrismLauncher.desktop.in @@ -10,4 +10,4 @@ Icon=org.prismlauncher.PrismLauncher Categories=Game;ActionGame;AdventureGame;Simulation; Keywords=game;minecraft;mc; StartupWMClass=PrismLauncher -MimeType=application/zip;application/x-modrinth-modpack+zip +MimeType=application/zip;application/x-modrinth-modpack+zip;x-scheme-handler/curseforge; diff --git a/program_info/win_install.nsi.in b/program_info/win_install.nsi.in index d3b5c256f..cb7b0935b 100644 --- a/program_info/win_install.nsi.in +++ b/program_info/win_install.nsi.in @@ -363,6 +363,10 @@ Section "@Launcher_DisplayName@" ; Write the installation path into the registry WriteRegStr HKCU Software\@Launcher_CommonName@ "InstallDir" "$INSTDIR" + ; Write the URL Handler into registry for curseforge + WriteRegStr HKCU Software\Classes\curseforge "URL Protocol" "" + WriteRegStr HKCU Software\Classes\curseforge\shell\open\command "" '"$INSTDIR\@Launcher_APP_BINARY_NAME@.exe" "%1"' + ; Write the uninstall keys for Windows ${GetParameters} $R0 ${GetOptions} $R0 "/NoUninstaller" $R1 diff --git a/renovate.json b/renovate.json index d97a8dc6c..71581e3b0 100644 --- a/renovate.json +++ b/renovate.json @@ -1,12 +1,12 @@ { - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:base" - ], - "nix": { - "enabled": true - }, - "lockFileMaintenance": { - "enabled": true - } + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base" + ], + "nix": { + "enabled": true + }, + "lockFileMaintenance": { + "enabled": true + } } diff --git a/tests/DataPackParse_test.cpp b/tests/DataPackParse_test.cpp index 61ce1e2b1..cd6ae8e8f 100644 --- a/tests/DataPackParse_test.cpp +++ b/tests/DataPackParse_test.cpp @@ -30,13 +30,13 @@ class DataPackParseTest : public QObject { Q_OBJECT - private slots: + private slots: void test_parseZIP() { QString source = QFINDTESTDATA("testdata/DataPackParse"); QString zip_dp = FS::PathCombine(source, "test_data_pack_boogaloo.zip"); - DataPack pack { QFileInfo(zip_dp) }; + DataPack pack{ QFileInfo(zip_dp) }; bool valid = DataPackUtils::processZIP(pack); @@ -50,7 +50,7 @@ class DataPackParseTest : public QObject { QString source = QFINDTESTDATA("testdata/DataPackParse"); QString folder_dp = FS::PathCombine(source, "test_folder"); - DataPack pack { QFileInfo(folder_dp) }; + DataPack pack{ QFileInfo(folder_dp) }; bool valid = DataPackUtils::processFolder(pack); @@ -64,7 +64,7 @@ class DataPackParseTest : public QObject { QString source = QFINDTESTDATA("testdata/DataPackParse"); QString folder_dp = FS::PathCombine(source, "another_test_folder"); - DataPack pack { QFileInfo(folder_dp) }; + DataPack pack{ QFileInfo(folder_dp) }; bool valid = DataPackUtils::process(pack); diff --git a/tests/DummyResourceAPI.h b/tests/DummyResourceAPI.h index 0cc909584..35de95151 100644 --- a/tests/DummyResourceAPI.h +++ b/tests/DummyResourceAPI.h @@ -32,7 +32,7 @@ class DummyResourceAPI : public ResourceAPI { } DummyResourceAPI() : ResourceAPI() {} - [[nodiscard]] auto getSortingMethods() const -> QList override { return {}; }; + [[nodiscard]] auto getSortingMethods() const -> QList override { return {}; } [[nodiscard]] Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&& callbacks) const override { diff --git a/tests/FileSystem_test.cpp b/tests/FileSystem_test.cpp index a41345c2e..e19410423 100644 --- a/tests/FileSystem_test.cpp +++ b/tests/FileSystem_test.cpp @@ -1,7 +1,7 @@ -#include #include -#include #include +#include +#include #include @@ -11,16 +11,16 @@ // Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header #ifdef __APPLE__ -#include // for deployment target to support pre-catalina targets without std::fs -#endif // __APPLE__ +#include // for deployment target to support pre-catalina targets without std::fs +#endif // __APPLE__ #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) #if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) #define GHC_USE_STD_FS #include namespace fs = std::filesystem; -#endif // MacOS min version check -#endif // Other OSes version check +#endif // MacOS min version check +#endif // Other OSes version check #ifndef GHC_USE_STD_FS #include @@ -29,8 +29,6 @@ namespace fs = ghc::filesystem; #include - - class LinkTask : public Task { Q_OBJECT @@ -42,41 +40,30 @@ class LinkTask : public Task { m_lnk->debug(true); } - ~LinkTask() { - delete m_lnk; - } + ~LinkTask() { delete m_lnk; } - void matcher(const IPathMatcher *filter) - { - m_lnk->matcher(filter); - } + void matcher(const IPathMatcher* filter) { m_lnk->matcher(filter); } - void linkRecursively(bool recursive) + void linkRecursively(bool recursive) { m_lnk->linkRecursively(recursive); m_linkRecursive = recursive; } - void whitelist(bool b) - { - m_lnk->whitelist(b); - } + void whitelist(bool b) { m_lnk->whitelist(b); } - void setMaxDepth(int depth) - { - m_lnk->setMaxDepth(depth); - } + void setMaxDepth(int depth) { m_lnk->setMaxDepth(depth); } private: void executeTask() override { - if(!(*m_lnk)()){ + if (!(*m_lnk)()) { #if defined Q_OS_WIN32 if (!m_useHard) { qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks"; qDebug() << "atempting to run with privelage"; - connect(m_lnk, &FS::create_link::finishedPrivileged, this, [&](bool gotResults){ + connect(m_lnk, &FS::create_link::finishedPrivileged, this, [&](bool gotResults) { if (gotResults) { emitSucceeded(); } else { @@ -87,32 +74,28 @@ class LinkTask : public Task { m_lnk->runPrivileged(); } else { qDebug() << "Link Failed!" << m_lnk->getOSError().value() << m_lnk->getOSError().message().c_str(); - } + } #else qDebug() << "Link Failed!" << m_lnk->getOSError().value() << m_lnk->getOSError().message().c_str(); #endif } else { emitSucceeded(); } - - }; + } - FS::create_link *m_lnk; - bool m_useHard = false; + FS::create_link* m_lnk; + [[maybe_unused]] bool m_useHard = false; bool m_linkRecursive = true; }; - -class FileSystemTest : public QObject -{ +class FileSystemTest : public QObject { Q_OBJECT const QString bothSlash = "/foo/"; const QString trailingSlash = "foo/"; const QString leadingSlash = "/foo"; -private -slots: + private slots: void test_pathCombine() { QCOMPARE(QString("/foo/foo"), FS::PathCombine(bothSlash, bothSlash)); @@ -130,12 +113,22 @@ slots: QTest::addColumn("path1"); QTest::addColumn("path2"); - QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc/def" << "ghi/jkl"; - QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/def/" << "ghi/jkl"; + QTest::newRow("qt 1") << "/abc/def/ghi/jkl" + << "/abc/def" + << "ghi/jkl"; + QTest::newRow("qt 2") << "/abc/def/ghi/jkl" + << "/abc/def/" + << "ghi/jkl"; #if defined(Q_OS_WIN) - QTest::newRow("win native, from C:") << "C:/abc" << "C:" << "abc"; - QTest::newRow("win native 1") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def" << "ghi\\jkl"; - QTest::newRow("win native 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def\\" << "ghi\\jkl"; + QTest::newRow("win native, from C:") << "C:/abc" + << "C:" + << "abc"; + QTest::newRow("win native 1") << "C:/abc/def/ghi/jkl" + << "C:\\abc\\def" + << "ghi\\jkl"; + QTest::newRow("win native 2") << "C:/abc/def/ghi/jkl" + << "C:\\abc\\def\\" + << "ghi\\jkl"; #endif } @@ -155,15 +148,39 @@ slots: QTest::addColumn("path2"); QTest::addColumn("path3"); - QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc" << "def" << "ghi/jkl"; - QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/" << "def" << "ghi/jkl"; - QTest::newRow("qt 3") << "/abc/def/ghi/jkl" << "/abc" << "def/" << "ghi/jkl"; - QTest::newRow("qt 4") << "/abc/def/ghi/jkl" << "/abc/" << "def/" << "ghi/jkl"; + QTest::newRow("qt 1") << "/abc/def/ghi/jkl" + << "/abc" + << "def" + << "ghi/jkl"; + QTest::newRow("qt 2") << "/abc/def/ghi/jkl" + << "/abc/" + << "def" + << "ghi/jkl"; + QTest::newRow("qt 3") << "/abc/def/ghi/jkl" + << "/abc" + << "def/" + << "ghi/jkl"; + QTest::newRow("qt 4") << "/abc/def/ghi/jkl" + << "/abc/" + << "def/" + << "ghi/jkl"; #if defined(Q_OS_WIN) - QTest::newRow("win 1") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def" << "ghi\\jkl"; - QTest::newRow("win 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl"; - QTest::newRow("win 3") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def\\" << "ghi\\jkl"; - QTest::newRow("win 4") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl"; + QTest::newRow("win 1") << "C:/abc/def/ghi/jkl" + << "C:\\abc" + << "def" + << "ghi\\jkl"; + QTest::newRow("win 2") << "C:/abc/def/ghi/jkl" + << "C:\\abc\\" + << "def" + << "ghi\\jkl"; + QTest::newRow("win 3") << "C:/abc/def/ghi/jkl" + << "C:\\abc" + << "def\\" + << "ghi\\jkl"; + QTest::newRow("win 4") << "C:/abc/def/ghi/jkl" + << "C:\\abc\\" + << "def" + << "ghi\\jkl"; #endif } @@ -180,8 +197,7 @@ slots: void test_copy() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); - auto f = [&folder]() - { + auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); @@ -192,8 +208,7 @@ slots: FS::copy c(folder, target_dir.path()); c(); - for(auto entry: target_dir.entryList()) - { + for (auto entry : target_dir.entryList()) { qDebug() << entry; } QVERIFY(target_dir.entryList().contains("pack.mcmeta")); @@ -213,8 +228,7 @@ slots: void test_copy_with_blacklist() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); - auto f = [&folder]() - { + auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); @@ -227,8 +241,7 @@ slots: c.matcher(&re); c(); - for(auto entry: target_dir.entryList()) - { + for (auto entry : target_dir.entryList()) { qDebug() << entry; } QVERIFY(!target_dir.entryList().contains("pack.mcmeta")); @@ -248,8 +261,7 @@ slots: void test_copy_with_whitelist() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); - auto f = [&folder]() - { + auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); @@ -263,8 +275,7 @@ slots: c.whitelist(true); c(); - for(auto entry: target_dir.entryList()) - { + for (auto entry : target_dir.entryList()) { qDebug() << entry; } QVERIFY(target_dir.entryList().contains("pack.mcmeta")); @@ -284,8 +295,7 @@ slots: void test_copy_with_dot_hidden() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); - auto f = [&folder]() - { + auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); @@ -298,7 +308,7 @@ slots: auto filter = QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden; - for (auto entry: target_dir.entryList(filter)) { + for (auto entry : target_dir.entryList(filter)) { qDebug() << entry; } @@ -335,7 +345,7 @@ slots: auto filter = QDir::Filter::Files; - for (auto entry: target_dir.entryList(filter)) { + for (auto entry : target_dir.entryList(filter)) { qDebug() << entry; } @@ -348,12 +358,10 @@ slots: QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); } - void test_link() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); - auto f = [&folder, this]() - { + auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); @@ -364,17 +372,13 @@ slots: LinkTask lnk_tsk(folder, target_dir.path()); lnk_tsk.linkRecursively(false); - QObject::connect(&lnk_tsk, &Task::finished, [&]{ - QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); - }); + QObject::connect(&lnk_tsk, &Task::finished, + [&] { QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); }); lnk_tsk.start(); - QVERIFY2(QTest::qWaitFor([&]() { - return lnk_tsk.isFinished(); - }, 100000), "Task didn't finish as it should."); + QVERIFY2(QTest::qWaitFor([&]() { return lnk_tsk.isFinished(); }, 100000), "Task didn't finish as it should."); - for(auto entry: target_dir.entryList()) - { + for (auto entry : target_dir.entryList()) { qDebug() << entry; QFileInfo entry_lnk_info(target_dir.filePath(entry)); if (!entry_lnk_info.isDir()) @@ -402,10 +406,9 @@ slots: void test_hard_link() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); - auto f = [&folder]() - { + auto f = [&folder]() { // use working dir to prevent makeing a hard link to a tmpfs or across devices - QTemporaryDir tempDir("./tmp"); + QTemporaryDir tempDir("./tmp"); tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); @@ -415,23 +418,20 @@ slots: FS::create_link lnk(folder, target_dir.path()); lnk.useHardLinks(true); lnk.debug(true); - if(!lnk()){ + if (!lnk()) { qDebug() << "Link Failed!" << lnk.getOSError().value() << lnk.getOSError().message().c_str(); } - for(auto entry: target_dir.entryList()) - { + for (auto entry : target_dir.entryList()) { qDebug() << entry; QFileInfo entry_lnk_info(target_dir.filePath(entry)); QVERIFY(!entry_lnk_info.isSymLink()); QFileInfo entry_orig_info(QDir(folder).filePath(entry)); if (!entry_lnk_info.isDir()) { qDebug() << "hard link equivalency?" << entry_lnk_info.absoluteFilePath() << "vs" << entry_orig_info.absoluteFilePath(); - QVERIFY(fs::equivalent( - fs::path(StringUtils::toStdString(entry_lnk_info.absoluteFilePath())), - fs::path(StringUtils::toStdString(entry_orig_info.absoluteFilePath())) - )); - } + QVERIFY(fs::equivalent(fs::path(StringUtils::toStdString(entry_lnk_info.absoluteFilePath())), + fs::path(StringUtils::toStdString(entry_orig_info.absoluteFilePath())))); + } } QFileInfo lnk_info(target_dir.path()); @@ -455,8 +455,7 @@ slots: void test_link_with_blacklist() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); - auto f = [&folder]() - { + auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); @@ -469,18 +468,13 @@ slots: RegexpMatcher re("[.]?mcmeta"); lnk_tsk.matcher(&re); lnk_tsk.linkRecursively(true); - QObject::connect(&lnk_tsk, &Task::finished, [&]{ - QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); - }); + QObject::connect(&lnk_tsk, &Task::finished, + [&] { QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); }); lnk_tsk.start(); - QVERIFY2(QTest::qWaitFor([&]() { - return lnk_tsk.isFinished(); - }, 100000), "Task didn't finish as it should."); + QVERIFY2(QTest::qWaitFor([&]() { return lnk_tsk.isFinished(); }, 100000), "Task didn't finish as it should."); - - for(auto entry: target_dir.entryList()) - { + for (auto entry : target_dir.entryList()) { qDebug() << entry; QFileInfo entry_lnk_info(target_dir.filePath(entry)); if (!entry_lnk_info.isDir()) @@ -507,8 +501,7 @@ slots: void test_link_with_whitelist() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); - auto f = [&folder]() - { + auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); @@ -522,17 +515,13 @@ slots: lnk_tsk.matcher(&re); lnk_tsk.linkRecursively(true); lnk_tsk.whitelist(true); - QObject::connect(&lnk_tsk, &Task::finished, [&]{ - QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); - }); + QObject::connect(&lnk_tsk, &Task::finished, + [&] { QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); }); lnk_tsk.start(); - QVERIFY2(QTest::qWaitFor([&]() { - return lnk_tsk.isFinished(); - }, 100000), "Task didn't finish as it should."); + QVERIFY2(QTest::qWaitFor([&]() { return lnk_tsk.isFinished(); }, 100000), "Task didn't finish as it should."); - for(auto entry: target_dir.entryList()) - { + for (auto entry : target_dir.entryList()) { qDebug() << entry; QFileInfo entry_lnk_info(target_dir.filePath(entry)); if (!entry_lnk_info.isDir()) @@ -559,8 +548,7 @@ slots: void test_link_with_dot_hidden() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); - auto f = [&folder]() - { + auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); @@ -571,18 +559,15 @@ slots: LinkTask lnk_tsk(folder, target_dir.path()); lnk_tsk.linkRecursively(true); - QObject::connect(&lnk_tsk, &Task::finished, [&]{ - QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); - }); + QObject::connect(&lnk_tsk, &Task::finished, + [&] { QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); }); lnk_tsk.start(); - QVERIFY2(QTest::qWaitFor([&]() { - return lnk_tsk.isFinished(); - }, 100000), "Task didn't finish as it should."); + QVERIFY2(QTest::qWaitFor([&]() { return lnk_tsk.isFinished(); }, 100000), "Task didn't finish as it should."); auto filter = QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden; - for (auto entry: target_dir.entryList(filter)) { + for (auto entry : target_dir.entryList(filter)) { qDebug() << entry; QFileInfo entry_lnk_info(target_dir.filePath(entry)); if (!entry_lnk_info.isDir()) @@ -621,19 +606,16 @@ slots: qDebug() << tempDir.path(); qDebug() << target_dir.path(); - LinkTask lnk_tsk(file, target_dir.filePath("pack.mcmeta")); - QObject::connect(&lnk_tsk, &Task::finished, [&]{ - QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); - }); + LinkTask lnk_tsk(file, target_dir.filePath("pack.mcmeta")); + QObject::connect(&lnk_tsk, &Task::finished, + [&] { QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); }); lnk_tsk.start(); - QVERIFY2(QTest::qWaitFor([&]() { - return lnk_tsk.isFinished(); - }, 100000), "Task didn't finish as it should."); + QVERIFY2(QTest::qWaitFor([&]() { return lnk_tsk.isFinished(); }, 100000), "Task didn't finish as it should."); auto filter = QDir::Filter::Files; - for (auto entry: target_dir.entryList(filter)) { + for (auto entry : target_dir.entryList(filter)) { qDebug() << entry; } @@ -648,8 +630,7 @@ slots: void test_link_with_max_depth() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); - auto f = [&folder, this]() - { + auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); @@ -661,22 +642,19 @@ slots: LinkTask lnk_tsk(folder, target_dir.path()); lnk_tsk.linkRecursively(true); lnk_tsk.setMaxDepth(0); - QObject::connect(&lnk_tsk, &Task::finished, [&]{ - QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); - }); + QObject::connect(&lnk_tsk, &Task::finished, + [&] { QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); }); lnk_tsk.start(); - QVERIFY2(QTest::qWaitFor([&]() { - return lnk_tsk.isFinished(); - }, 100000), "Task didn't finish as it should."); + QVERIFY2(QTest::qWaitFor([&]() { return lnk_tsk.isFinished(); }, 100000), "Task didn't finish as it should."); QVERIFY(!QFileInfo(target_dir.path()).isSymLink()); auto filter = QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden; - for(auto entry: target_dir.entryList(filter)) - { + for (auto entry : target_dir.entryList(filter)) { qDebug() << entry; - if (entry == "." || entry == "..") continue; + if (entry == "." || entry == "..") + continue; QFileInfo entry_lnk_info(target_dir.filePath(entry)); QVERIFY(entry_lnk_info.isSymLink()); } @@ -687,8 +665,6 @@ slots: QVERIFY(target_dir.entryList().contains("pack.mcmeta")); QVERIFY(target_dir.entryList().contains("assets")); - - }; // first try variant without trailing / @@ -704,8 +680,7 @@ slots: void test_link_with_no_max_depth() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); - auto f = [&folder]() - { + auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); @@ -717,24 +692,19 @@ slots: LinkTask lnk_tsk(folder, target_dir.path()); lnk_tsk.linkRecursively(true); lnk_tsk.setMaxDepth(-1); - QObject::connect(&lnk_tsk, &Task::finished, [&]{ - QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); - }); + QObject::connect(&lnk_tsk, &Task::finished, + [&] { QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); }); lnk_tsk.start(); - QVERIFY2(QTest::qWaitFor([&]() { - return lnk_tsk.isFinished(); - }, 100000), "Task didn't finish as it should."); - + QVERIFY2(QTest::qWaitFor([&]() { return lnk_tsk.isFinished(); }, 100000), "Task didn't finish as it should."); std::function verify_check = [&](QString check_path) { QDir check_dir(check_path); auto filter = QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden; - for(auto entry: check_dir.entryList(filter)) - { + for (auto entry : check_dir.entryList(filter)) { QFileInfo entry_lnk_info(check_dir.filePath(entry)); qDebug() << entry << check_dir.filePath(entry); - if (!entry_lnk_info.isDir()){ + if (!entry_lnk_info.isDir()) { QVERIFY(entry_lnk_info.isSymLink()); } else if (entry != "." && entry != "..") { qDebug() << "Decending tree to verify symlinks:" << check_dir.filePath(entry); @@ -742,9 +712,8 @@ slots: } } }; - + verify_check(target_dir.path()); - QFileInfo lnk_info(target_dir.path()); QVERIFY(lnk_info.exists()); @@ -763,7 +732,8 @@ slots: f(); } - void test_path_depth() { + void test_path_depth() + { QCOMPARE(FS::pathDepth(""), 0); QCOMPARE(FS::pathDepth("."), 0); QCOMPARE(FS::pathDepth("foo.txt"), 0); @@ -777,7 +747,8 @@ slots: QCOMPARE(FS::pathDepth("/baz/../bar/foo.txt"), 1); } - void test_path_trunc() { + void test_path_trunc() + { QCOMPARE(FS::pathTruncate("", 0), QDir::toNativeSeparators("")); QCOMPARE(FS::pathTruncate("foo.txt", 0), QDir::toNativeSeparators("")); QCOMPARE(FS::pathTruncate("foo.txt", 1), QDir::toNativeSeparators("")); diff --git a/tests/GZip_test.cpp b/tests/GZip_test.cpp index 82503d81d..c11ba2711 100644 --- a/tests/GZip_test.cpp +++ b/tests/GZip_test.cpp @@ -3,18 +3,16 @@ #include #include -void fib(int &prev, int &cur) +void fib(int& prev, int& cur) { auto ret = prev + cur; prev = cur; cur = ret; } -class GZipTest : public QObject -{ +class GZipTest : public QObject { Q_OBJECT -private -slots: + private slots: void test_Through() { @@ -27,9 +25,8 @@ slots: std::uniform_int_distribution idis(0, std::numeric_limits::max()); // initialize random buffer - for(int i = 0; i < size; i++) - { - random.append((char)idis(eng)); + for (int i = 0; i < size; i++) { + random.append(static_cast(idis(eng))); } // initialize fibonacci @@ -37,8 +34,7 @@ slots: int cur = 1; // test if fibonacci long random buffers pass through GZip - do - { + do { QByteArray copy = random; copy.resize(cur); compressed.clear(); diff --git a/tests/GradleSpecifier_test.cpp b/tests/GradleSpecifier_test.cpp index 850f83881..fa3c4ad9b 100644 --- a/tests/GradleSpecifier_test.cpp +++ b/tests/GradleSpecifier_test.cpp @@ -2,19 +2,11 @@ #include -class GradleSpecifierTest : public QObject -{ +class GradleSpecifierTest : public QObject { Q_OBJECT -private -slots: - void initTestCase() - { - - } - void cleanupTestCase() - { - - } + private slots: + void initTestCase() {} + void cleanupTestCase() {} void test_Positive_data() { @@ -40,8 +32,10 @@ slots: QTest::addColumn("spec"); QTest::addColumn("expected"); - QTest::newRow("3 parter") << "group.id:artifact:1.0" << "group/id/artifact/1.0/artifact-1.0.jar"; - QTest::newRow("doom") << "id.software:doom:1.666:demons@wad" << "id/software/doom/1.666/doom-1.666-demons.wad"; + QTest::newRow("3 parter") << "group.id:artifact:1.0" + << "group/id/artifact/1.0/artifact-1.0.jar"; + QTest::newRow("doom") << "id.software:doom:1.666:demons@wad" + << "id/software/doom/1.666/doom-1.666-demons.wad"; } void test_Path() { diff --git a/tests/Index_test.cpp b/tests/Index_test.cpp index 436b753e5..888021e24 100644 --- a/tests/Index_test.cpp +++ b/tests/Index_test.cpp @@ -3,14 +3,13 @@ #include #include -class IndexTest : public QObject -{ +class IndexTest : public QObject { Q_OBJECT -private -slots: + private slots: void test_hasUid_and_getList() { - Meta::Index windex({std::make_shared("list1"), std::make_shared("list2"), std::make_shared("list3")}); + Meta::Index windex({ std::make_shared("list1"), std::make_shared("list2"), + std::make_shared("list3") }); QVERIFY(windex.hasUid("list1")); QVERIFY(!windex.hasUid("asdf")); QVERIFY(windex.get("list2") != nullptr); @@ -20,13 +19,18 @@ slots: void test_merge() { - Meta::Index windex({std::make_shared("list1"), std::make_shared("list2"), std::make_shared("list3")}); + Meta::Index windex({ std::make_shared("list1"), std::make_shared("list2"), + std::make_shared("list3") }); QCOMPARE(windex.lists().size(), 3); - windex.merge(std::shared_ptr(new Meta::Index({std::make_shared("list1"), std::make_shared("list2"), std::make_shared("list3")}))); + windex.merge(std::shared_ptr( + new Meta::Index({ std::make_shared("list1"), std::make_shared("list2"), + std::make_shared("list3") }))); QCOMPARE(windex.lists().size(), 3); - windex.merge(std::shared_ptr(new Meta::Index({std::make_shared("list4"), std::make_shared("list2"), std::make_shared("list5")}))); + windex.merge(std::shared_ptr( + new Meta::Index({ std::make_shared("list4"), std::make_shared("list2"), + std::make_shared("list5") }))); QCOMPARE(windex.lists().size(), 5); - windex.merge(std::shared_ptr(new Meta::Index({std::make_shared("list6")}))); + windex.merge(std::shared_ptr(new Meta::Index({ std::make_shared("list6") }))); QCOMPARE(windex.lists().size(), 6); } }; diff --git a/tests/JavaVersion_test.cpp b/tests/JavaVersion_test.cpp index 76d9af2fe..fd2851954 100644 --- a/tests/JavaVersion_test.cpp +++ b/tests/JavaVersion_test.cpp @@ -2,11 +2,9 @@ #include -class JavaVersionTest : public QObject -{ +class JavaVersionTest : public QObject { Q_OBJECT -private -slots: + private slots: void test_Parse_data() { QTest::addColumn("string"); @@ -50,33 +48,54 @@ slots: QTest::addColumn("bigger"); // old format and new format equivalence - QTest::newRow("1.6.0_33 == 6.0.33") << "1.6.0_33" << "6.0.33" << false << true << false; + QTest::newRow("1.6.0_33 == 6.0.33") << "1.6.0_33" + << "6.0.33" << false << true << false; // old format major version - QTest::newRow("1.5.0_33 < 1.6.0_33") << "1.5.0_33" << "1.6.0_33" << true << false << false; + QTest::newRow("1.5.0_33 < 1.6.0_33") << "1.5.0_33" + << "1.6.0_33" << true << false << false; // new format - first release vs first security patch - QTest::newRow("9 < 9.0.1") << "9" << "9.0.1" << true << false << false; - QTest::newRow("9.0.1 > 9") << "9.0.1" << "9" << false << false << true; + QTest::newRow("9 < 9.0.1") << "9" + << "9.0.1" << true << false << false; + QTest::newRow("9.0.1 > 9") << "9.0.1" + << "9" << false << false << true; // new format - first minor vs first release/security patch - QTest::newRow("9.1 > 9.0.1") << "9.1" << "9.0.1" << false << false << true; - QTest::newRow("9.0.1 < 9.1") << "9.0.1" << "9.1" << true << false << false; - QTest::newRow("9.1 > 9") << "9.1" << "9" << false << false << true; - QTest::newRow("9 > 9.1") << "9" << "9.1" << true << false << false; + QTest::newRow("9.1 > 9.0.1") << "9.1" + << "9.0.1" << false << false << true; + QTest::newRow("9.0.1 < 9.1") << "9.0.1" + << "9.1" << true << false << false; + QTest::newRow("9.1 > 9") << "9.1" + << "9" << false << false << true; + QTest::newRow("9 > 9.1") << "9" + << "9.1" << true << false << false; // new format - omitted numbers - QTest::newRow("9 == 9.0") << "9" << "9.0" << false << true << false; - QTest::newRow("9 == 9.0.0") << "9" << "9.0.0" << false << true << false; - QTest::newRow("9.0 == 9.0.0") << "9.0" << "9.0.0" << false << true << false; + QTest::newRow("9 == 9.0") << "9" + << "9.0" << false << true << false; + QTest::newRow("9 == 9.0.0") << "9" + << "9.0.0" << false << true << false; + QTest::newRow("9.0 == 9.0.0") << "9.0" + << "9.0.0" << false << true << false; // early access and prereleases compared to final release - QTest::newRow("9-ea < 9") << "9-ea" << "9" << true << false << false; - QTest::newRow("9 < 9.0.1-ea") << "9" << "9.0.1-ea" << true << false << false; - QTest::newRow("9.0.1-ea > 9") << "9.0.1-ea" << "9" << false << false << true; + QTest::newRow("9-ea < 9") << "9-ea" + << "9" << true << false << false; + QTest::newRow("9 < 9.0.1-ea") << "9" + << "9.0.1-ea" << true << false << false; + QTest::newRow("9.0.1-ea > 9") << "9.0.1-ea" + << "9" << false << false << true; // prerelease difference only testing - QTest::newRow("9-1 == 9-1") << "9-1" << "9-1" << false << true << false; - QTest::newRow("9-1 < 9-2") << "9-1" << "9-2" << true << false << false; - QTest::newRow("9-5 < 9-20") << "9-5" << "9-20" << true << false << false; - QTest::newRow("9-rc1 < 9-rc2") << "9-rc1" << "9-rc2" << true << false << false; - QTest::newRow("9-rc5 < 9-rc20") << "9-rc5" << "9-rc20" << true << false << false; - QTest::newRow("9-rc < 9-rc2") << "9-rc" << "9-rc2" << true << false << false; - QTest::newRow("9-ea < 9-rc") << "9-ea" << "9-rc" << true << false << false; + QTest::newRow("9-1 == 9-1") << "9-1" + << "9-1" << false << true << false; + QTest::newRow("9-1 < 9-2") << "9-1" + << "9-2" << true << false << false; + QTest::newRow("9-5 < 9-20") << "9-5" + << "9-20" << true << false << false; + QTest::newRow("9-rc1 < 9-rc2") << "9-rc1" + << "9-rc2" << true << false << false; + QTest::newRow("9-rc5 < 9-rc20") << "9-rc5" + << "9-rc20" << true << false << false; + QTest::newRow("9-rc < 9-rc2") << "9-rc" + << "9-rc2" << true << false << false; + QTest::newRow("9-ea < 9-rc") << "9-ea" + << "9-rc" << true << false << false; } void test_Sort() { diff --git a/tests/Library_test.cpp b/tests/Library_test.cpp index db8380c7f..8b8d4c55c 100644 --- a/tests/Library_test.cpp +++ b/tests/Library_test.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify @@ -35,17 +35,16 @@ #include -#include -#include -#include -#include #include #include +#include +#include +#include +#include -class LibraryTest : public QObject -{ +class LibraryTest : public QObject { Q_OBJECT -private: + private: LibraryPtr readMojangJson(const QString path) { QFile jsonFile(path); @@ -56,20 +55,17 @@ private: return MojangVersionFormat::libraryFromJson(problems, QJsonDocument::fromJson(data).object(), path); } // get absolute path to expected storage, assuming default cache prefix - QStringList getStorage(QString relative) - { - return {FS::PathCombine(cache->getBasePath("libraries"), relative)}; - } + QStringList getStorage(QString relative) { return { FS::PathCombine(cache->getBasePath("libraries"), relative) }; } - RuntimeContext dummyContext(QString system = "linux", QString arch = "64", QString realArch = "amd64") { + RuntimeContext dummyContext(QString system = "linux", QString arch = "64", QString realArch = "amd64") + { RuntimeContext r; r.javaArchitecture = arch; r.javaRealArchitecture = realArch; r.system = system; return r; } -private -slots: + private slots: void initTestCase() { cache.reset(new HttpMetaCache()); @@ -111,7 +107,7 @@ slots: test.setHint("local"); auto downloads = test.getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(downloads.size(), 0); - QCOMPARE(failedFiles, {"testname-testversion.jar"}); + QCOMPARE(failedFiles, { "testname-testversion.jar" }); } void test_legacy_url_local_override() { @@ -127,7 +123,7 @@ slots: QStringList jar, native, native32, native64; test.getApplicableFiles(r, jar, native, native32, native64, QFINDTESTDATA("testdata/Library")); - QCOMPARE(jar, {QFileInfo(QFINDTESTDATA("testdata/Library/codecwav-20101023.jar")).absoluteFilePath()}); + QCOMPARE(jar, { QFileInfo(QFINDTESTDATA("testdata/Library/codecwav-20101023.jar")).absoluteFilePath() }); QCOMPARE(native, {}); QCOMPARE(native32, {}); QCOMPARE(native64, {}); @@ -158,9 +154,9 @@ slots: { RuntimeContext r = dummyContext(); Library test("test.package:testname:testversion"); - test.m_nativeClassifiers["linux"]="linux-${arch}"; - test.m_nativeClassifiers["osx"]="osx-${arch}"; - test.m_nativeClassifiers["windows"]="windows-${arch}"; + test.m_nativeClassifiers["linux"] = "linux-${arch}"; + test.m_nativeClassifiers["osx"] = "osx-${arch}"; + test.m_nativeClassifiers["windows"] = "windows-${arch}"; QCOMPARE(test.isNative(), true); test.setRepositoryURL("file://foo/bar"); { @@ -212,7 +208,7 @@ slots: { RuntimeContext r = dummyContext(); Library test("test.package:testname:testversion"); - test.m_nativeClassifiers["linux"]="linux-${arch}"; + test.m_nativeClassifiers["linux"] = "linux-${arch}"; test.setHint("local"); QCOMPARE(test.isNative(), true); test.setRepositoryURL("file://foo/bar"); @@ -221,12 +217,13 @@ slots: test.getApplicableFiles(r, jar, native, native32, native64, QFINDTESTDATA("testdata/Library")); QCOMPARE(jar, {}); QCOMPARE(native, {}); - QCOMPARE(native32, {QFileInfo(QFINDTESTDATA("testdata/Library/testname-testversion-linux-32.jar")).absoluteFilePath()}); - QCOMPARE(native64, {QFileInfo(QFINDTESTDATA("testdata/Library") + "/testname-testversion-linux-64.jar").absoluteFilePath()}); + QCOMPARE(native32, { QFileInfo(QFINDTESTDATA("testdata/Library/testname-testversion-linux-32.jar")).absoluteFilePath() }); + QCOMPARE(native64, { QFileInfo(QFINDTESTDATA("testdata/Library") + "/testname-testversion-linux-64.jar").absoluteFilePath() }); QStringList failedFiles; auto dls = test.getDownloads(r, cache.get(), failedFiles, QFINDTESTDATA("testdata/Library")); QCOMPARE(dls.size(), 0); - QCOMPARE(failedFiles, {QFileInfo(QFINDTESTDATA("testdata/Library") + "/testname-testversion-linux-64.jar").absoluteFilePath()}); + QCOMPARE(failedFiles, + { QFileInfo(QFINDTESTDATA("testdata/Library") + "/testname-testversion-linux-64.jar").absoluteFilePath() }); } } void test_onenine() @@ -254,7 +251,7 @@ slots: { QStringList jar, native, native32, native64; test->getApplicableFiles(r, jar, native, native32, native64, QFINDTESTDATA("testdata/Library")); - QCOMPARE(jar, {QFileInfo(QFINDTESTDATA("testdata/Library/codecwav-20101023.jar")).absoluteFilePath()}); + QCOMPARE(jar, { QFileInfo(QFINDTESTDATA("testdata/Library/codecwav-20101023.jar")).absoluteFilePath() }); QCOMPARE(native, {}); QCOMPARE(native32, {}); QCOMPARE(native64, {}); @@ -275,7 +272,7 @@ slots: { QStringList jar, native, native32, native64; test->getApplicableFiles(r, jar, native, native32, native64, QFINDTESTDATA("testdata/Library")); - QCOMPARE(jar, {QFileInfo(QFINDTESTDATA("testdata/Library/codecwav-20101023.jar")).absoluteFilePath()}); + QCOMPARE(jar, { QFileInfo(QFINDTESTDATA("testdata/Library/codecwav-20101023.jar")).absoluteFilePath() }); QCOMPARE(native, {}); QCOMPARE(native32, {}); QCOMPARE(native64, {}); @@ -295,14 +292,16 @@ slots: QStringList jar, native, native32, native64; test->getApplicableFiles(r, jar, native, native32, native64, QString()); QCOMPARE(jar, QStringList()); - QCOMPARE(native, getStorage("org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar")); + QCOMPARE(native, + getStorage("org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar")); QCOMPARE(native32, {}); QCOMPARE(native64, {}); QStringList failedFiles; auto dls = test->getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(dls.size(), 1); QCOMPARE(failedFiles, {}); - QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar")); + QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/" + "lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar")); } void test_onenine_native_arch() { @@ -318,10 +317,13 @@ slots: auto dls = test->getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(dls.size(), 2); QCOMPARE(failedFiles, {}); - QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar")); - QCOMPARE(dls[1]->m_url, QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar")); + QCOMPARE(dls[0]->m_url, + QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar")); + QCOMPARE(dls[1]->m_url, + QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar")); } -private: + + private: std::unique_ptr cache; QString dataDir; }; diff --git a/tests/MojangVersionFormat_test.cpp b/tests/MojangVersionFormat_test.cpp index 219fbfa25..9ff5d78c0 100644 --- a/tests/MojangVersionFormat_test.cpp +++ b/tests/MojangVersionFormat_test.cpp @@ -1,10 +1,9 @@ -#include #include +#include #include -class MojangVersionFormatTest : public QObject -{ +class MojangVersionFormatTest : public QObject { Q_OBJECT static QJsonDocument readJson(const QString path) @@ -15,7 +14,7 @@ class MojangVersionFormatTest : public QObject jsonFile.close(); return QJsonDocument::fromJson(data); } - static void writeJson(const char *file, QJsonDocument doc) + static void writeJson(const char* file, QJsonDocument doc) { QFile jsonFile(file); jsonFile.open(QIODevice::WriteOnly | QIODevice::Text); @@ -25,8 +24,7 @@ class MojangVersionFormatTest : public QObject jsonFile.close(); } -private -slots: + private slots: void test_Through_Simple() { QJsonDocument doc = readJson(QFINDTESTDATA("testdata/MojangVersionFormat/1.9-simple.json")); @@ -50,4 +48,3 @@ slots: QTEST_GUILESS_MAIN(MojangVersionFormatTest) #include "MojangVersionFormat_test.moc" - diff --git a/tests/Packwiz_test.cpp b/tests/Packwiz_test.cpp index 292894699..d1b274d12 100644 --- a/tests/Packwiz_test.cpp +++ b/tests/Packwiz_test.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * @@ -26,9 +26,9 @@ class PackwizTest : public QObject { Q_OBJECT private slots: - // Files taken from https://github.com/packwiz/packwiz-example-pack - void loadFromFile_Modrinth() - { + // Files taken from https://github.com/packwiz/packwiz-example-pack + void loadFromFile_Modrinth() + { QString source = QFINDTESTDATA("testdata/Packwiz"); QDir index_dir(source); @@ -43,15 +43,17 @@ class PackwizTest : public QObject { QCOMPARE(metadata.name, "Borderless Mining"); QCOMPARE(metadata.filename, "borderless-mining-1.1.1+1.18.jar"); QCOMPARE(metadata.side, "client"); - + QCOMPARE(metadata.url, QUrl("https://cdn.modrinth.com/data/kYq5qkSL/versions/1.1.1+1.18/borderless-mining-1.1.1+1.18.jar")); QCOMPARE(metadata.hash_format, "sha512"); - QCOMPARE(metadata.hash, "c8fe6e15ddea32668822dddb26e1851e5f03834be4bcb2eff9c0da7fdc086a9b6cead78e31a44d3bc66335cba11144ee0337c6d5346f1ba63623064499b3188d"); + QCOMPARE(metadata.hash, + "c8fe6e15ddea32668822dddb26e1851e5f03834be4bcb2eff9c0da7fdc086a9b6cead78e31a44d3bc66335cba11144ee0337c6d5346f1ba6362306449" + "9b3188d"); QCOMPARE(metadata.provider, ModPlatform::ResourceProvider::MODRINTH); QCOMPARE(metadata.version(), "ug2qKTPR"); QCOMPARE(metadata.mod_id(), "kYq5qkSL"); - } + } void loadFromFile_Curseforge() { @@ -71,7 +73,7 @@ class PackwizTest : public QObject { QCOMPARE(metadata.name, "Screenshot to Clipboard (Fabric)"); QCOMPARE(metadata.filename, "screenshot-to-clipboard-1.0.7-fabric.jar"); QCOMPARE(metadata.side, "both"); - + QCOMPARE(metadata.url, QUrl("https://edge.forgecdn.net/files/3509/43/screenshot-to-clipboard-1.0.7-fabric.jar")); QCOMPARE(metadata.hash_format, "murmur2"); QCOMPARE(metadata.hash, "1781245820"); diff --git a/tests/ParseUtils_test.cpp b/tests/ParseUtils_test.cpp index 02208fdf2..0f64a6017 100644 --- a/tests/ParseUtils_test.cpp +++ b/tests/ParseUtils_test.cpp @@ -2,27 +2,16 @@ #include -class ParseUtilsTest : public QObject -{ +class ParseUtilsTest : public QObject { Q_OBJECT -private -slots: + private slots: void test_Through_data() { QTest::addColumn("timestamp"); - const char * timestamps[] = - { - "2016-02-29T13:49:54+01:00", - "2016-02-26T15:21:11+00:01", - "2016-02-24T15:52:36+01:13", - "2016-02-18T17:41:00+00:00", - "2016-02-17T15:23:19+00:00", - "2016-02-16T15:22:39+09:22", - "2016-02-10T15:06:41+00:00", - "2016-02-04T15:28:02-05:33" - }; - for(unsigned i = 0; i < (sizeof(timestamps) / sizeof(const char *)); i++) - { + const char* timestamps[] = { "2016-02-29T13:49:54+01:00", "2016-02-26T15:21:11+00:01", "2016-02-24T15:52:36+01:13", + "2016-02-18T17:41:00+00:00", "2016-02-17T15:23:19+00:00", "2016-02-16T15:22:39+09:22", + "2016-02-10T15:06:41+00:00", "2016-02-04T15:28:02-05:33" }; + for (unsigned i = 0; i < (sizeof(timestamps) / sizeof(const char*)); i++) { QTest::newRow(timestamps[i]) << QString(timestamps[i]); } } @@ -35,7 +24,6 @@ slots: QCOMPARE(time_serialized, timestamp); } - }; QTEST_GUILESS_MAIN(ParseUtilsTest) diff --git a/tests/ResourceFolderModel_test.cpp b/tests/ResourceFolderModel_test.cpp index 962d89f11..57c2cbdff 100644 --- a/tests/ResourceFolderModel_test.cpp +++ b/tests/ResourceFolderModel_test.cpp @@ -1,40 +1,40 @@ // SPDX-License-Identifier: GPL-3.0-only /* -* PolyMC - Minecraft Launcher -* Copyright (C) 2022 Sefa Eyeoglu -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, version 3. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* This file incorporates work covered by the following copyright and -* permission notice: -* -* Copyright 2013-2021 MultiMC Contributors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -#include #include +#include #include #include "BaseInstance.h" @@ -61,12 +61,10 @@ \ disconnect(&model, nullptr, &loop, nullptr); -class ResourceFolderModelTest : public QObject -{ +class ResourceFolderModelTest : public QObject { Q_OBJECT -private -slots: + private slots: // test for GH-1178 - install a folder with files to a mod list void test_1178() { @@ -76,8 +74,7 @@ slots: // sanity check QVERIFY(!source.endsWith('/')); - auto verify = [](QString path) - { + auto verify = [](QString path) { QDir target_dir(FS::PathCombine(path, "test_folder")); QVERIFY(target_dir.entryList().contains("pack.mcmeta")); QVERIFY(target_dir.entryList().contains("assets")); @@ -161,23 +158,17 @@ slots: QCOMPARE(model.size(), 0); - { - EXEC_UPDATE_TASK(model.installResource(file_mod), QVERIFY) - } + { EXEC_UPDATE_TASK(model.installResource(file_mod), QVERIFY) } QCOMPARE(model.size(), 1); qDebug() << "Added first mod."; - { - EXEC_UPDATE_TASK(model.startWatching(), ) - } + { EXEC_UPDATE_TASK(model.startWatching(), ) } QCOMPARE(model.size(), 1); qDebug() << "Started watching the temp folder."; - - { - EXEC_UPDATE_TASK(model.installResource(folder_resource), QVERIFY) - } + + { EXEC_UPDATE_TASK(model.installResource(folder_resource), QVERIFY) } QCOMPARE(model.size(), 2); qDebug() << "Added second mod."; @@ -189,7 +180,7 @@ slots: QCOMPARE(model.size(), 1); qDebug() << "Removed first mod."; - QString mod_file_name {model.at(0).fileinfo().fileName()}; + QString mod_file_name{ model.at(0).fileinfo().fileName() }; QVERIFY(!mod_file_name.isEmpty()); { @@ -212,10 +203,7 @@ slots: QCOMPARE(model.size(), 0); - { - EXEC_UPDATE_TASK(model.installResource(folder_resource), QVERIFY) - } - { + { EXEC_UPDATE_TASK(model.installResource(folder_resource), QVERIFY) } { EXEC_UPDATE_TASK(model.installResource(file_mod), QVERIFY) } @@ -228,8 +216,8 @@ slots: auto& res_2 = model.at(0).type() == ResourceType::FOLDER ? model.at(0) : model.at(1); auto id_1 = res_1.internal_id(); auto id_2 = res_2.internal_id(); - bool initial_enabled_res_2 = res_2.enabled(); - bool initial_enabled_res_1 = res_1.enabled(); + bool initial_enabled_res_2 = res_2.enabled(); + bool initial_enabled_res_1 = res_1.enabled(); QVERIFY(res_1.type() != ResourceType::FOLDER && res_1.type() != ResourceType::UNKNOWN); qDebug() << "res_1 is of the correct type."; diff --git a/tests/ResourceModel_test.cpp b/tests/ResourceModel_test.cpp index 30353d3f9..b589758aa 100644 --- a/tests/ResourceModel_test.cpp +++ b/tests/ResourceModel_test.cpp @@ -40,17 +40,17 @@ class DummyResourceModel : public ResourceModel { DummyResourceModel() : ResourceModel(new DummyResourceAPI) {} ~DummyResourceModel() {} - [[nodiscard]] auto metaEntryBase() const -> QString override { return ""; }; + [[nodiscard]] auto metaEntryBase() const -> QString override { return ""; } - ResourceAPI::SearchArgs createSearchArguments() override { return {}; }; - ResourceAPI::VersionSearchArgs createVersionsArguments(QModelIndex&) override { return {}; }; - ResourceAPI::ProjectInfoArgs createInfoArguments(QModelIndex&) override { return {}; }; + ResourceAPI::SearchArgs createSearchArguments() override { return {}; } + ResourceAPI::VersionSearchArgs createVersionsArguments(QModelIndex&) override { return {}; } + ResourceAPI::ProjectInfoArgs createInfoArguments(QModelIndex&) override { return {}; } QJsonArray documentToArray(QJsonDocument& doc) const override { return doc.object().value("hits").toArray(); } void loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) override { - pack.authors.append({ Json::requireString(obj, "author") }); + pack.authors.append({ Json::requireString(obj, "author"), "" }); pack.description = Json::requireString(obj, "description"); pack.addonId = Json::requireString(obj, "project_id"); } @@ -59,7 +59,8 @@ class DummyResourceModel : public ResourceModel { class ResourceModelTest : public QObject { Q_OBJECT private slots: - void test_abstract_item_model() { + void test_abstract_item_model() + { auto dummy = DummyResourceModel(); auto tester = QAbstractItemModelTester(&dummy); } diff --git a/tests/ResourcePackParse_test.cpp b/tests/ResourcePackParse_test.cpp index 7f2f86bf1..e1092167d 100644 --- a/tests/ResourcePackParse_test.cpp +++ b/tests/ResourcePackParse_test.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify @@ -27,18 +27,20 @@ class ResourcePackParseTest : public QObject { Q_OBJECT - private slots: + private slots: void test_parseZIP() { QString source = QFINDTESTDATA("testdata/ResourcePackParse"); QString zip_rp = FS::PathCombine(source, "test_resource_pack_idk.zip"); - ResourcePack pack { QFileInfo(zip_rp) }; + ResourcePack pack{ QFileInfo(zip_rp) }; bool valid = ResourcePackUtils::processZIP(pack, ResourcePackUtils::ProcessingLevel::BasicInfoOnly); QVERIFY(pack.packFormat() == 3); - QVERIFY(pack.description() == "um dois, feijão com arroz, três quatro, feijão no prato, cinco seis, café inglês, sete oito, comer biscoito, nove dez comer pastéis!!"); + QVERIFY(pack.description() == + "um dois, feijão com arroz, três quatro, feijão no prato, cinco seis, café inglês, sete oito, comer biscoito, nove dez " + "comer pastéis!!"); QVERIFY(valid == true); } @@ -47,7 +49,7 @@ class ResourcePackParseTest : public QObject { QString source = QFINDTESTDATA("testdata/ResourcePackParse"); QString folder_rp = FS::PathCombine(source, "test_folder"); - ResourcePack pack { QFileInfo(folder_rp) }; + ResourcePack pack{ QFileInfo(folder_rp) }; bool valid = ResourcePackUtils::processFolder(pack, ResourcePackUtils::ProcessingLevel::BasicInfoOnly); @@ -61,13 +63,13 @@ class ResourcePackParseTest : public QObject { QString source = QFINDTESTDATA("testdata/ResourcePackParse"); QString folder_rp = FS::PathCombine(source, "another_test_folder"); - ResourcePack pack { QFileInfo(folder_rp) }; + ResourcePack pack{ QFileInfo(folder_rp) }; bool valid = ResourcePackUtils::process(pack, ResourcePackUtils::ProcessingLevel::BasicInfoOnly); QVERIFY(pack.packFormat() == 6); QVERIFY(pack.description() == "o quartel pegou fogo, policia deu sinal, acode acode acode a bandeira nacional"); - QVERIFY(valid == false); // no assets dir + QVERIFY(valid == false); // no assets dir } }; diff --git a/tests/ShaderPackParse_test.cpp b/tests/ShaderPackParse_test.cpp index 7df105c61..898278103 100644 --- a/tests/ShaderPackParse_test.cpp +++ b/tests/ShaderPackParse_test.cpp @@ -31,13 +31,13 @@ class ShaderPackParseTest : public QObject { Q_OBJECT - private slots: + private slots: void test_parseZIP() { QString source = QFINDTESTDATA("testdata/ShaderPackParse"); QString zip_sp = FS::PathCombine(source, "shaderpack1.zip"); - ShaderPack pack { QFileInfo(zip_sp) }; + ShaderPack pack{ QFileInfo(zip_sp) }; bool valid = ShaderPackUtils::processZIP(pack); @@ -50,7 +50,7 @@ class ShaderPackParseTest : public QObject { QString source = QFINDTESTDATA("testdata/ShaderPackParse"); QString folder_sp = FS::PathCombine(source, "shaderpack2"); - ShaderPack pack { QFileInfo(folder_sp) }; + ShaderPack pack{ QFileInfo(folder_sp) }; bool valid = ShaderPackUtils::processFolder(pack); @@ -63,7 +63,7 @@ class ShaderPackParseTest : public QObject { QString source = QFINDTESTDATA("testdata/ShaderPackParse"); QString folder_sp = FS::PathCombine(source, "shaderpack3.zip"); - ShaderPack pack { QFileInfo(folder_sp) }; + ShaderPack pack{ QFileInfo(folder_sp) }; bool valid = ShaderPackUtils::process(pack); diff --git a/tests/Task_test.cpp b/tests/Task_test.cpp index c59d4bb73..abc9be905 100644 --- a/tests/Task_test.cpp +++ b/tests/Task_test.cpp @@ -19,7 +19,7 @@ class BasicTask : public Task { BasicTask(bool show_debug_log = true) : Task(nullptr, show_debug_log) {} private: - void executeTask() override { emitSucceeded(); }; + void executeTask() override { emitSucceeded(); } }; /* Does nothing. Only used for testing. */ @@ -31,7 +31,7 @@ class BasicTask_MultiStep : public Task { private: auto isMultiStep() const -> bool override { return true; } - void executeTask() override{}; + void executeTask() override {} }; class BigConcurrentTask : public ConcurrentTask { @@ -66,7 +66,10 @@ class BigConcurrentTaskThread : public QThread { } connect(&big_task, &Task::finished, this, &QThread::quit); - connect(&m_deadline, &QTimer::timeout, this, [&] { passed_the_deadline = true; quit(); }); + connect(&m_deadline, &QTimer::timeout, this, [&] { + passed_the_deadline = true; + quit(); + }); m_deadline.start(); big_task.run(); diff --git a/tests/TexturePackParse_test.cpp b/tests/TexturePackParse_test.cpp index 4ddc0a3a1..85c96b57c 100644 --- a/tests/TexturePackParse_test.cpp +++ b/tests/TexturePackParse_test.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * @@ -28,13 +28,13 @@ class TexturePackParseTest : public QObject { Q_OBJECT - private slots: + private slots: void test_parseZIP() { QString source = QFINDTESTDATA("testdata/TexturePackParse"); QString zip_rp = FS::PathCombine(source, "test_texture_pack_idk.zip"); - TexturePack pack { QFileInfo(zip_rp) }; + TexturePack pack{ QFileInfo(zip_rp) }; bool valid = TexturePackUtils::processZIP(pack); @@ -47,7 +47,7 @@ class TexturePackParseTest : public QObject { QString source = QFINDTESTDATA("testdata/TexturePackParse"); QString folder_rp = FS::PathCombine(source, "test_texturefolder"); - TexturePack pack { QFileInfo(folder_rp) }; + TexturePack pack{ QFileInfo(folder_rp) }; bool valid = TexturePackUtils::processFolder(pack, TexturePackUtils::ProcessingLevel::BasicInfoOnly); @@ -60,7 +60,7 @@ class TexturePackParseTest : public QObject { QString source = QFINDTESTDATA("testdata/TexturePackParse"); QString folder_rp = FS::PathCombine(source, "another_test_texturefolder"); - TexturePack pack { QFileInfo(folder_rp) }; + TexturePack pack{ QFileInfo(folder_rp) }; bool valid = TexturePackUtils::process(pack, TexturePackUtils::ProcessingLevel::BasicInfoOnly); diff --git a/tests/Version_test.cpp b/tests/Version_test.cpp index f5488cbce..d25bf7bb5 100644 --- a/tests/Version_test.cpp +++ b/tests/Version_test.cpp @@ -34,33 +34,48 @@ class VersionTest : public QObject { { addDataColumns(); - QTest::newRow("equal, explicit") << "1.2.0" << "1.2.0" << false << true; - QTest::newRow("equal, two-digit") << "1.42" << "1.42" << false << true; + QTest::newRow("equal, explicit") << "1.2.0" + << "1.2.0" << false << true; + QTest::newRow("equal, two-digit") << "1.42" + << "1.42" << false << true; - QTest::newRow("lessThan, explicit 1") << "1.2.0" << "1.2.1" << true << false; - QTest::newRow("lessThan, explicit 2") << "1.2.0" << "1.3.0" << true << false; - QTest::newRow("lessThan, explicit 3") << "1.2.0" << "2.2.0" << true << false; - QTest::newRow("lessThan, implicit 1") << "1.2" << "1.2.0" << true << false; - QTest::newRow("lessThan, implicit 2") << "1.2" << "1.2.1" << true << false; - QTest::newRow("lessThan, implicit 3") << "1.2" << "1.3.0" << true << false; - QTest::newRow("lessThan, implicit 4") << "1.2" << "2.2.0" << true << false; - QTest::newRow("lessThan, two-digit") << "1.41" << "1.42" << true << false; + QTest::newRow("lessThan, explicit 1") << "1.2.0" + << "1.2.1" << true << false; + QTest::newRow("lessThan, explicit 2") << "1.2.0" + << "1.3.0" << true << false; + QTest::newRow("lessThan, explicit 3") << "1.2.0" + << "2.2.0" << true << false; + QTest::newRow("lessThan, implicit 1") << "1.2" + << "1.2.0" << true << false; + QTest::newRow("lessThan, implicit 2") << "1.2" + << "1.2.1" << true << false; + QTest::newRow("lessThan, implicit 3") << "1.2" + << "1.3.0" << true << false; + QTest::newRow("lessThan, implicit 4") << "1.2" + << "2.2.0" << true << false; + QTest::newRow("lessThan, two-digit") << "1.41" + << "1.42" << true << false; - QTest::newRow("greaterThan, explicit 1") << "1.2.1" << "1.2.0" << false << false; - QTest::newRow("greaterThan, explicit 2") << "1.3.0" << "1.2.0" << false << false; - QTest::newRow("greaterThan, explicit 3") << "2.2.0" << "1.2.0" << false << false; - QTest::newRow("greaterThan, implicit 1") << "1.2.0" << "1.2" << false << false; - QTest::newRow("greaterThan, implicit 2") << "1.2.1" << "1.2" << false << false; - QTest::newRow("greaterThan, implicit 3") << "1.3.0" << "1.2" << false << false; - QTest::newRow("greaterThan, implicit 4") << "2.2.0" << "1.2" << false << false; - QTest::newRow("greaterThan, two-digit") << "1.42" << "1.41" << false << false; + QTest::newRow("greaterThan, explicit 1") << "1.2.1" + << "1.2.0" << false << false; + QTest::newRow("greaterThan, explicit 2") << "1.3.0" + << "1.2.0" << false << false; + QTest::newRow("greaterThan, explicit 3") << "2.2.0" + << "1.2.0" << false << false; + QTest::newRow("greaterThan, implicit 1") << "1.2.0" + << "1.2" << false << false; + QTest::newRow("greaterThan, implicit 2") << "1.2.1" + << "1.2" << false << false; + QTest::newRow("greaterThan, implicit 3") << "1.3.0" + << "1.2" << false << false; + QTest::newRow("greaterThan, implicit 4") << "2.2.0" + << "1.2" << false << false; + QTest::newRow("greaterThan, two-digit") << "1.42" + << "1.41" << false << false; } private slots: - void test_versionCompare_data() - { - setupVersions(); - } + void test_versionCompare_data() { setupVersions(); } void test_versionCompare() { @@ -85,12 +100,12 @@ class VersionTest : public QObject { QDir test_vector_dir(QFINDTESTDATA("testdata/Version")); - QFile vector_file{test_vector_dir.absoluteFilePath("test_vectors.txt")}; + QFile vector_file{ test_vector_dir.absoluteFilePath("test_vectors.txt") }; vector_file.open(QFile::OpenModeFlag::ReadOnly); int test_number = 0; - const QString test_name_template { "FlexVer test #%1 (%2)" }; + const QString test_name_template{ "FlexVer test #%1 (%2)" }; for (auto line = vector_file.readLine(); !vector_file.atEnd(); line = vector_file.readLine()) { line = line.simplified(); if (line.startsWith('#') || line.isEmpty()) @@ -100,8 +115,8 @@ class VersionTest : public QObject { auto split_line = line.split('<'); if (split_line.size() == 2) { - QString first{split_line.first().simplified()}; - QString second{split_line.last().simplified()}; + QString first{ split_line.first().simplified() }; + QString second{ split_line.last().simplified() }; auto new_test_name = test_name_template.arg(QString::number(test_number), "lessThan"); m_flex_test_names.append(new_test_name); @@ -112,8 +127,8 @@ class VersionTest : public QObject { split_line = line.split('='); if (split_line.size() == 2) { - QString first{split_line.first().simplified()}; - QString second{split_line.last().simplified()}; + QString first{ split_line.first().simplified() }; + QString second{ split_line.last().simplified() }; auto new_test_name = test_name_template.arg(QString::number(test_number), "equals"); m_flex_test_names.append(new_test_name); @@ -124,8 +139,8 @@ class VersionTest : public QObject { split_line = line.split('>'); if (split_line.size() == 2) { - QString first{split_line.first().simplified()}; - QString second{split_line.last().simplified()}; + QString first{ split_line.first().simplified() }; + QString second{ split_line.last().simplified() }; auto new_test_name = test_name_template.arg(QString::number(test_number), "greaterThan"); m_flex_test_names.append(new_test_name); diff --git a/tests/WorldSaveParse_test.cpp b/tests/WorldSaveParse_test.cpp index 4a8c3d29c..8dabaeb51 100644 --- a/tests/WorldSaveParse_test.cpp +++ b/tests/WorldSaveParse_test.cpp @@ -31,13 +31,13 @@ class WorldSaveParseTest : public QObject { Q_OBJECT - private slots: + private slots: void test_parseZIP() { QString source = QFINDTESTDATA("testdata/WorldSaveParse"); - QString zip_ws = FS::PathCombine(source, "minecraft_save_1.zip") ; - WorldSave save { QFileInfo(zip_ws) }; + QString zip_ws = FS::PathCombine(source, "minecraft_save_1.zip"); + WorldSave save{ QFileInfo(zip_ws) }; bool valid = WorldSaveUtils::processZIP(save); @@ -45,13 +45,13 @@ class WorldSaveParseTest : public QObject { QVERIFY(save.saveDirName() == "world_1"); QVERIFY(valid == true); } - + void test_parse_ZIP2() { QString source = QFINDTESTDATA("testdata/WorldSaveParse"); - QString zip_ws = FS::PathCombine(source, "minecraft_save_2.zip") ; - WorldSave save { QFileInfo(zip_ws) }; + QString zip_ws = FS::PathCombine(source, "minecraft_save_2.zip"); + WorldSave save{ QFileInfo(zip_ws) }; bool valid = WorldSaveUtils::processZIP(save); @@ -59,13 +59,13 @@ class WorldSaveParseTest : public QObject { QVERIFY(save.saveDirName() == "world_2"); QVERIFY(valid == true); } - + void test_parseFolder() { QString source = QFINDTESTDATA("testdata/WorldSaveParse"); QString folder_ws = FS::PathCombine(source, "minecraft_save_3"); - WorldSave save { QFileInfo(folder_ws) }; + WorldSave save{ QFileInfo(folder_ws) }; bool valid = WorldSaveUtils::processFolder(save); @@ -79,7 +79,7 @@ class WorldSaveParseTest : public QObject { QString source = QFINDTESTDATA("testdata/WorldSaveParse"); QString folder_ws = FS::PathCombine(source, "minecraft_save_4"); - WorldSave save { QFileInfo(folder_ws) }; + WorldSave save{ QFileInfo(folder_ws) }; bool valid = WorldSaveUtils::process(save); diff --git a/tests/testdata/MojangVersionFormat/1.9-simple.json b/tests/testdata/MojangVersionFormat/1.9-simple.json index 574c5b065..c71bc6553 100644 --- a/tests/testdata/MojangVersionFormat/1.9-simple.json +++ b/tests/testdata/MojangVersionFormat/1.9-simple.json @@ -190,7 +190,8 @@ } ], "mainClass": "net.minecraft.client.main.Main", - "minecraftArguments": "--username ${auth_player_name} --version ${version_name} --gameDir ${game_directory} --assetsDir ${assets_root} --assetIndex ${assets_index_name} --uuid ${auth_uuid} --accessToken ${auth_access_token} --userType ${user_type} --versionType ${version_type}", + "minecraftArguments": + "--username ${auth_player_name} --version ${version_name} --gameDir ${game_directory} --assetsDir ${assets_root} --assetIndex ${assets_index_name} --uuid ${auth_uuid} --accessToken ${auth_access_token} --userType ${user_type} --versionType ${version_type}", "minimumLauncherVersion": 18, "releaseTime": "2016-02-29T13:49:54+00:00", "time": "2016-03-01T13:14:53+00:00", diff --git a/tests/testdata/MojangVersionFormat/1.9.json b/tests/testdata/MojangVersionFormat/1.9.json index 697c60590..b298634ad 100644 --- a/tests/testdata/MojangVersionFormat/1.9.json +++ b/tests/testdata/MojangVersionFormat/1.9.json @@ -335,7 +335,8 @@ "path": "org/lwjgl/lwjgl/lwjgl_util/2.9.4-nightly-20150209/lwjgl_util-2.9.4-nightly-20150209.jar", "sha1": "d51a7c040a721d13efdfbd34f8b257b2df882ad0", "size": 173887, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl_util/2.9.4-nightly-20150209/lwjgl_util-2.9.4-nightly-20150209.jar" + "url": + "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl_util/2.9.4-nightly-20150209/lwjgl_util-2.9.4-nightly-20150209.jar" } }, "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209", @@ -357,26 +358,33 @@ "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar", "sha1": "b04f3ee8f5e43fa3b162981b50bb72fe1acabb33", "size": 22, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar" + "url": + "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar" }, "classifiers": { "natives-linux": { - "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar", + "path": + "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar", "sha1": "931074f46c795d2f7b30ed6395df5715cfd7675b", "size": 578680, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar" + "url": + "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar" }, "natives-osx": { - "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar", + "path": + "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar", "sha1": "bcab850f8f487c3f4c4dbabde778bb82bd1a40ed", "size": 426822, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar" + "url": + "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar" }, "natives-windows": { - "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar", + "path": + "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar", "sha1": "b84d5102b9dbfabfeb5e43c7e2828d98a7fc80e0", "size": 613748, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar" + "url": + "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar" } } }, @@ -428,7 +436,8 @@ "path": "org/lwjgl/lwjgl/lwjgl_util/2.9.2-nightly-20140822/lwjgl_util-2.9.2-nightly-20140822.jar", "sha1": "f0e612c840a7639c1f77f68d72a28dae2f0c8490", "size": 173887, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl_util/2.9.2-nightly-20140822/lwjgl_util-2.9.2-nightly-20140822.jar" + "url": + "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl_util/2.9.2-nightly-20140822/lwjgl_util-2.9.2-nightly-20140822.jar" } }, "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.2-nightly-20140822", @@ -445,22 +454,28 @@ "downloads": { "classifiers": { "natives-linux": { - "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-linux.jar", + "path": + "org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-linux.jar", "sha1": "d898a33b5d0a6ef3fed3a4ead506566dce6720a5", "size": 578539, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-linux.jar" + "url": + "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-linux.jar" }, "natives-osx": { - "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-osx.jar", + "path": + "org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-osx.jar", "sha1": "79f5ce2fea02e77fe47a3c745219167a542121d7", "size": 468116, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-osx.jar" + "url": + "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-osx.jar" }, "natives-windows": { - "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-windows.jar", + "path": + "org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-windows.jar", "sha1": "78b2a55ce4dc29c6b3ec4df8ca165eba05f9b341", "size": 613680, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-windows.jar" + "url": + "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-windows.jar" } } }, @@ -491,7 +506,8 @@ "path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar", "sha1": "7ff832a6eb9ab6a767f1ade2b548092d0fa64795", "size": 10362, - "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar" + "url": + "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar" }, "natives-osx": { "path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-osx.jar", @@ -503,7 +519,8 @@ "path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-windows.jar", "sha1": "385ee093e01f587f30ee1c8a2ee7d408fd732e16", "size": 155179, - "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-windows.jar" + "url": + "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-windows.jar" } } }, @@ -521,7 +538,8 @@ } ], "mainClass": "net.minecraft.client.main.Main", - "minecraftArguments": "--username ${auth_player_name} --version ${version_name} --gameDir ${game_directory} --assetsDir ${assets_root} --assetIndex ${assets_index_name} --uuid ${auth_uuid} --accessToken ${auth_access_token} --userType ${user_type} --versionType ${version_type}", + "minecraftArguments": + "--username ${auth_player_name} --version ${version_name} --gameDir ${game_directory} --assetsDir ${assets_root} --assetIndex ${assets_index_name} --uuid ${auth_uuid} --accessToken ${auth_access_token} --userType ${user_type} --versionType ${version_type}", "minimumLauncherVersion": 18, "releaseTime": "2016-02-29T13:49:54+00:00", "time": "2016-03-01T13:14:53+00:00", diff --git a/tests/testdata/MojangVersionFormat/lib-native.json b/tests/testdata/MojangVersionFormat/lib-native.json index 5b9f3b556..e7a6094f9 100644 --- a/tests/testdata/MojangVersionFormat/lib-native.json +++ b/tests/testdata/MojangVersionFormat/lib-native.json @@ -4,26 +4,30 @@ "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar", "sha1": "b04f3ee8f5e43fa3b162981b50bb72fe1acabb33", "size": 22, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar" + "url": + "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar" }, "classifiers": { "natives-linux": { "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar", "sha1": "931074f46c795d2f7b30ed6395df5715cfd7675b", "size": 578680, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar" + "url": + "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar" }, "natives-osx": { "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar", "sha1": "bcab850f8f487c3f4c4dbabde778bb82bd1a40ed", "size": 426822, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar" + "url": + "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar" }, "natives-windows": { "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar", "sha1": "b84d5102b9dbfabfeb5e43c7e2828d98a7fc80e0", "size": 613748, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar" + "url": + "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar" } } }, diff --git a/tests/testdata/PackageManifest/1.8.0_202-x64.json b/tests/testdata/PackageManifest/1.8.0_202-x64.json index 3d99d719b..eaf67a7ac 100644 --- a/tests/testdata/PackageManifest/1.8.0_202-x64.json +++ b/tests/testdata/PackageManifest/1.8.0_202-x64.json @@ -1 +1,4667 @@ -{"files": {"COPYRIGHT": {"downloads": {"lzma": {"sha1": "dd860e040807f7e53ae89da5f28dd73d57ac605d", "size": 1431, "url": "https://launcher.mojang.com/v1/objects/dd860e040807f7e53ae89da5f28dd73d57ac605d/COPYRIGHT"}, "raw": {"sha1": "c725183c757011e7ba96c83c1e86ee7e8b516a2b", "size": 3244, "url": "https://launcher.mojang.com/v1/objects/c725183c757011e7ba96c83c1e86ee7e8b516a2b/COPYRIGHT"}}, "executable": false, "type": "file"}, "LICENSE": {"downloads": {"raw": {"sha1": "3e86865deec0814c958bcf7fb87f790bccc0e8bd", "size": 40, "url": "https://launcher.mojang.com/v1/objects/3e86865deec0814c958bcf7fb87f790bccc0e8bd/LICENSE"}}, "executable": false, "type": "file"}, "README": {"downloads": {"raw": {"sha1": "f90331df1e5badeadc501d8dd70714c62a920204", "size": 46, "url": "https://launcher.mojang.com/v1/objects/f90331df1e5badeadc501d8dd70714c62a920204/README"}}, "executable": false, "type": "file"}, "THIRDPARTYLICENSEREADME-JAVAFX.txt": {"downloads": {"lzma": {"sha1": "4fee85109d7ff04b982d0576dabd15397f599125", "size": 15455, "url": "https://launcher.mojang.com/v1/objects/4fee85109d7ff04b982d0576dabd15397f599125/THIRDPARTYLICENSEREADME-JAVAFX.txt"}, "raw": {"sha1": "56ff42f87607b997b52ae0ef8bf315e36932e870", "size": 112724, "url": "https://launcher.mojang.com/v1/objects/56ff42f87607b997b52ae0ef8bf315e36932e870/THIRDPARTYLICENSEREADME-JAVAFX.txt"}}, "executable": false, "type": "file"}, "THIRDPARTYLICENSEREADME.txt": {"downloads": {"lzma": {"sha1": "419c1414ba46ae9dbfd38cf4e0601fff61644429", "size": 32266, "url": "https://launcher.mojang.com/v1/objects/419c1414ba46ae9dbfd38cf4e0601fff61644429/THIRDPARTYLICENSEREADME.txt"}, "raw": {"sha1": "b83c3f32261de3e48ccd20614a11e066b1ec9027", "size": 153824, "url": "https://launcher.mojang.com/v1/objects/b83c3f32261de3e48ccd20614a11e066b1ec9027/THIRDPARTYLICENSEREADME.txt"}}, "executable": false, "type": "file"}, "Welcome.html": {"downloads": {"lzma": {"sha1": "01c21a74b4aafb7cbe0388233c43cbdf77dcaaea", "size": 528, "url": "https://launcher.mojang.com/v1/objects/01c21a74b4aafb7cbe0388233c43cbdf77dcaaea/Welcome.html"}, "raw": {"sha1": "d98ae54f03dac87419abc19b97e315830c2da55f", "size": 955, "url": "https://launcher.mojang.com/v1/objects/d98ae54f03dac87419abc19b97e315830c2da55f/Welcome.html"}}, "executable": false, "type": "file"}, "bin": {"type": "directory"}, "bin/ControlPanel": {"target": "jcontrol", "type": "link"}, "bin/java": {"downloads": {"lzma": {"sha1": "3857eea1d59e1bc545c67a753ed2768254807b8a", "size": 2088, "url": "https://launcher.mojang.com/v1/objects/3857eea1d59e1bc545c67a753ed2768254807b8a/java"}, "raw": {"sha1": "3d20560fb5d1a49cb689c2226972e92e06d27ba6", "size": 8464, "url": "https://launcher.mojang.com/v1/objects/3d20560fb5d1a49cb689c2226972e92e06d27ba6/java"}}, "executable": true, "type": "file"}, "bin/javaws": {"downloads": {"lzma": {"sha1": "a6bec5c049e76c4488294a256a2084ea23ddb440", "size": 38173, "url": "https://launcher.mojang.com/v1/objects/a6bec5c049e76c4488294a256a2084ea23ddb440/javaws"}, "raw": {"sha1": "955c0f0066e2f893b0c2b3ccd83e223722e4ab74", "size": 140296, "url": "https://launcher.mojang.com/v1/objects/955c0f0066e2f893b0c2b3ccd83e223722e4ab74/javaws"}}, "executable": true, "type": "file"}, "bin/jcontrol": {"downloads": {"lzma": {"sha1": "40c5e33748f252e1d950b579a4185ab2c23fc908", "size": 2166, "url": "https://launcher.mojang.com/v1/objects/40c5e33748f252e1d950b579a4185ab2c23fc908/jcontrol"}, "raw": {"sha1": "ed541733c8b51e34349c1f8010b277e58ad73f1e", "size": 6264, "url": "https://launcher.mojang.com/v1/objects/ed541733c8b51e34349c1f8010b277e58ad73f1e/jcontrol"}}, "executable": true, "type": "file"}, "bin/jjs": {"downloads": {"lzma": {"sha1": "d44d1ac421979f7671921986214812095a5b0e3b", "size": 2168, "url": "https://launcher.mojang.com/v1/objects/d44d1ac421979f7671921986214812095a5b0e3b/jjs"}, "raw": {"sha1": "f00f944c3dbe556793b5dc686aaeee3e5722e99b", "size": 8584, "url": "https://launcher.mojang.com/v1/objects/f00f944c3dbe556793b5dc686aaeee3e5722e99b/jjs"}}, "executable": true, "type": "file"}, "bin/keytool": {"downloads": {"lzma": {"sha1": "93c607dce450976667c382f609a367167bdec05c", "size": 2175, "url": "https://launcher.mojang.com/v1/objects/93c607dce450976667c382f609a367167bdec05c/keytool"}, "raw": {"sha1": "7114b561546270e441e9ed1bcc24e5188c068a42", "size": 8584, "url": "https://launcher.mojang.com/v1/objects/7114b561546270e441e9ed1bcc24e5188c068a42/keytool"}}, "executable": true, "type": "file"}, "bin/orbd": {"downloads": {"lzma": {"sha1": "b27dfded5e2b2f6f02c555971c94e46ca14ac81b", "size": 2254, "url": "https://launcher.mojang.com/v1/objects/b27dfded5e2b2f6f02c555971c94e46ca14ac81b/orbd"}, "raw": {"sha1": "7f31217fecb3dbbd89f1dd3783fca58793a66fd2", "size": 8656, "url": "https://launcher.mojang.com/v1/objects/7f31217fecb3dbbd89f1dd3783fca58793a66fd2/orbd"}}, "executable": true, "type": "file"}, "bin/pack200": {"downloads": {"lzma": {"sha1": "b52da4497b49b1508b6225a5740857ddb8f52e97", "size": 2183, "url": "https://launcher.mojang.com/v1/objects/b52da4497b49b1508b6225a5740857ddb8f52e97/pack200"}, "raw": {"sha1": "16ef3e801efb57e50bc6477a27a9d95d02d0775b", "size": 8584, "url": "https://launcher.mojang.com/v1/objects/16ef3e801efb57e50bc6477a27a9d95d02d0775b/pack200"}}, "executable": true, "type": "file"}, "bin/policytool": {"downloads": {"lzma": {"sha1": "87da4c07da45f3d1a1a9d732af197cd39bf69d10", "size": 2182, "url": "https://launcher.mojang.com/v1/objects/87da4c07da45f3d1a1a9d732af197cd39bf69d10/policytool"}, "raw": {"sha1": "a52a29424470cb9b8db5c2fb1751d0b697a7ec8e", "size": 8592, "url": "https://launcher.mojang.com/v1/objects/a52a29424470cb9b8db5c2fb1751d0b697a7ec8e/policytool"}}, "executable": true, "type": "file"}, "bin/rmid": {"downloads": {"lzma": {"sha1": "1494c1174fde0c0a93ea117bc7edf7eb936c0512", "size": 2172, "url": "https://launcher.mojang.com/v1/objects/1494c1174fde0c0a93ea117bc7edf7eb936c0512/rmid"}, "raw": {"sha1": "5c8710e1ab924e5b09a07bcb4c6e106293bbd1a8", "size": 8584, "url": "https://launcher.mojang.com/v1/objects/5c8710e1ab924e5b09a07bcb4c6e106293bbd1a8/rmid"}}, "executable": true, "type": "file"}, "bin/rmiregistry": {"downloads": {"lzma": {"sha1": "7070cf2ec5a5e520a880bae699431edf02083e7e", "size": 2174, "url": "https://launcher.mojang.com/v1/objects/7070cf2ec5a5e520a880bae699431edf02083e7e/rmiregistry"}, "raw": {"sha1": "5f518daa7050028d5d9d849634c73136f2b23a54", "size": 8592, "url": "https://launcher.mojang.com/v1/objects/5f518daa7050028d5d9d849634c73136f2b23a54/rmiregistry"}}, "executable": true, "type": "file"}, "bin/servertool": {"downloads": {"lzma": {"sha1": "1db683a11cc9b7313426c84412f4d95be2fa7ccd", "size": 2185, "url": "https://launcher.mojang.com/v1/objects/1db683a11cc9b7313426c84412f4d95be2fa7ccd/servertool"}, "raw": {"sha1": "49d0ebfeb265ce5a8733e1014541ea2525674a60", "size": 8592, "url": "https://launcher.mojang.com/v1/objects/49d0ebfeb265ce5a8733e1014541ea2525674a60/servertool"}}, "executable": true, "type": "file"}, "bin/tnameserv": {"downloads": {"lzma": {"sha1": "36da9c9a2c5a8b662a3f8d52ca67339bce1c2714", "size": 2291, "url": "https://launcher.mojang.com/v1/objects/36da9c9a2c5a8b662a3f8d52ca67339bce1c2714/tnameserv"}, "raw": {"sha1": "09d998f8efcb6f55d0d87f59e08f8b89662796d9", "size": 8656, "url": "https://launcher.mojang.com/v1/objects/09d998f8efcb6f55d0d87f59e08f8b89662796d9/tnameserv"}}, "executable": true, "type": "file"}, "bin/unpack200": {"downloads": {"lzma": {"sha1": "344959e32fc7ee19eebe7b3cf5ab6d1a7d6641f2", "size": 79721, "url": "https://launcher.mojang.com/v1/objects/344959e32fc7ee19eebe7b3cf5ab6d1a7d6641f2/unpack200"}, "raw": {"sha1": "5dd933132f1b202e19e0c8e093f7113711cfdfc1", "size": 182616, "url": "https://launcher.mojang.com/v1/objects/5dd933132f1b202e19e0c8e093f7113711cfdfc1/unpack200"}}, "executable": true, "type": "file"}, "lib": {"type": "directory"}, "lib/amd64": {"type": "directory"}, "lib/amd64/jli": {"type": "directory"}, "lib/amd64/jli/libjli.so": {"downloads": {"lzma": {"sha1": "372331ee8e375888f798a2e88180a94493e141b0", "size": 48327, "url": "https://launcher.mojang.com/v1/objects/372331ee8e375888f798a2e88180a94493e141b0/libjli.so"}, "raw": {"sha1": "73b0cf8b7415686bc40c561ff77ff2740ccf7a44", "size": 108616, "url": "https://launcher.mojang.com/v1/objects/73b0cf8b7415686bc40c561ff77ff2740ccf7a44/libjli.so"}}, "executable": true, "type": "file"}, "lib/amd64/jvm.cfg": {"downloads": {"lzma": {"sha1": "86bcfebec37b38415525ffd77d3eaf70d0b1b4ca", "size": 435, "url": "https://launcher.mojang.com/v1/objects/86bcfebec37b38415525ffd77d3eaf70d0b1b4ca/jvm.cfg"}, "raw": {"sha1": "84b38bdc745de446ba0ca0232ea3aaf2efd721da", "size": 627, "url": "https://launcher.mojang.com/v1/objects/84b38bdc745de446ba0ca0232ea3aaf2efd721da/jvm.cfg"}}, "executable": false, "type": "file"}, "lib/amd64/libavplugin-53.so": {"downloads": {"lzma": {"sha1": "a332366762d9efc7b845a682b7edce62db44618c", "size": 14747, "url": "https://launcher.mojang.com/v1/objects/a332366762d9efc7b845a682b7edce62db44618c/libavplugin-53.so"}, "raw": {"sha1": "9bd1473dd8a0dc7950c7af1cc69a45548df26eb5", "size": 51720, "url": "https://launcher.mojang.com/v1/objects/9bd1473dd8a0dc7950c7af1cc69a45548df26eb5/libavplugin-53.so"}}, "executable": true, "type": "file"}, "lib/amd64/libavplugin-54.so": {"downloads": {"lzma": {"sha1": "2c615852a0720a275163e00597c1f711f11341da", "size": 15153, "url": "https://launcher.mojang.com/v1/objects/2c615852a0720a275163e00597c1f711f11341da/libavplugin-54.so"}, "raw": {"sha1": "8808050c5949c4800b42d1b19b1f8b0d120bcacb", "size": 51768, "url": "https://launcher.mojang.com/v1/objects/8808050c5949c4800b42d1b19b1f8b0d120bcacb/libavplugin-54.so"}}, "executable": true, "type": "file"}, "lib/amd64/libavplugin-55.so": {"downloads": {"lzma": {"sha1": "39ee8e7fe14f0010c78973962800f539c3e4c16b", "size": 15168, "url": "https://launcher.mojang.com/v1/objects/39ee8e7fe14f0010c78973962800f539c3e4c16b/libavplugin-55.so"}, "raw": {"sha1": "f10ea4ea3489e96d8d161a96790133c417ec44e1", "size": 51784, "url": "https://launcher.mojang.com/v1/objects/f10ea4ea3489e96d8d161a96790133c417ec44e1/libavplugin-55.so"}}, "executable": true, "type": "file"}, "lib/amd64/libavplugin-56.so": {"downloads": {"lzma": {"sha1": "abe7feced5a559f1bdc868526dc69484e0e591a0", "size": 15169, "url": "https://launcher.mojang.com/v1/objects/abe7feced5a559f1bdc868526dc69484e0e591a0/libavplugin-56.so"}, "raw": {"sha1": "e5bfcbff5a5a5a5993a3e689a05ef358c131a3ed", "size": 51784, "url": "https://launcher.mojang.com/v1/objects/e5bfcbff5a5a5a5993a3e689a05ef358c131a3ed/libavplugin-56.so"}}, "executable": true, "type": "file"}, "lib/amd64/libavplugin-57.so": {"downloads": {"lzma": {"sha1": "4dd26b4ef2294b6929dcb2c7546b47eac5cc78a9", "size": 15174, "url": "https://launcher.mojang.com/v1/objects/4dd26b4ef2294b6929dcb2c7546b47eac5cc78a9/libavplugin-57.so"}, "raw": {"sha1": "2949e7ff9b0ac90e8943c211cff141ab12eec3f8", "size": 51784, "url": "https://launcher.mojang.com/v1/objects/2949e7ff9b0ac90e8943c211cff141ab12eec3f8/libavplugin-57.so"}}, "executable": true, "type": "file"}, "lib/amd64/libavplugin-ffmpeg-56.so": {"downloads": {"lzma": {"sha1": "c688ba1cfa442bf18bee43b2fa870b4dc1ce3fb6", "size": 15231, "url": "https://launcher.mojang.com/v1/objects/c688ba1cfa442bf18bee43b2fa870b4dc1ce3fb6/libavplugin-ffmpeg-56.so"}, "raw": {"sha1": "0d36c971a9ad99fc2292092fdec3a4179b1021b9", "size": 51920, "url": "https://launcher.mojang.com/v1/objects/0d36c971a9ad99fc2292092fdec3a4179b1021b9/libavplugin-ffmpeg-56.so"}}, "executable": true, "type": "file"}, "lib/amd64/libavplugin-ffmpeg-57.so": {"downloads": {"lzma": {"sha1": "087426bdbffebcfa372a438e863785f4ffbe9a6b", "size": 15180, "url": "https://launcher.mojang.com/v1/objects/087426bdbffebcfa372a438e863785f4ffbe9a6b/libavplugin-ffmpeg-57.so"}, "raw": {"sha1": "5e9c4eb4b49eb8e57c01003ec73a1eb8d6d8c462", "size": 51784, "url": "https://launcher.mojang.com/v1/objects/5e9c4eb4b49eb8e57c01003ec73a1eb8d6d8c462/libavplugin-ffmpeg-57.so"}}, "executable": true, "type": "file"}, "lib/amd64/libawt.so": {"downloads": {"lzma": {"sha1": "018be58b205b73c842a55df811b70d0e8237216e", "size": 195720, "url": "https://launcher.mojang.com/v1/objects/018be58b205b73c842a55df811b70d0e8237216e/libawt.so"}, "raw": {"sha1": "02632cd326e3161c00a7e784599dd7b9ee053dce", "size": 759184, "url": "https://launcher.mojang.com/v1/objects/02632cd326e3161c00a7e784599dd7b9ee053dce/libawt.so"}}, "executable": true, "type": "file"}, "lib/amd64/libawt_headless.so": {"downloads": {"lzma": {"sha1": "7ac2517cff75d4bbb0a0412a9b5f18c74ea188fa", "size": 11211, "url": "https://launcher.mojang.com/v1/objects/7ac2517cff75d4bbb0a0412a9b5f18c74ea188fa/libawt_headless.so"}, "raw": {"sha1": "862157ec957008d0911c5daedc004b3a202623a4", "size": 39176, "url": "https://launcher.mojang.com/v1/objects/862157ec957008d0911c5daedc004b3a202623a4/libawt_headless.so"}}, "executable": true, "type": "file"}, "lib/amd64/libawt_xawt.so": {"downloads": {"lzma": {"sha1": "d536a96af27dfe35de6bb2c8759d51c488cdd8d4", "size": 149598, "url": "https://launcher.mojang.com/v1/objects/d536a96af27dfe35de6bb2c8759d51c488cdd8d4/libawt_xawt.so"}, "raw": {"sha1": "28232b3e01b6f11bfe098bfc6eafc3a513dcebf1", "size": 470232, "url": "https://launcher.mojang.com/v1/objects/28232b3e01b6f11bfe098bfc6eafc3a513dcebf1/libawt_xawt.so"}}, "executable": true, "type": "file"}, "lib/amd64/libbci.so": {"downloads": {"lzma": {"sha1": "c36fad091d11e64c815d5ca17c0ef7a55b0776b1", "size": 3509, "url": "https://launcher.mojang.com/v1/objects/c36fad091d11e64c815d5ca17c0ef7a55b0776b1/libbci.so"}, "raw": {"sha1": "33824051db1ccb6332e22c2b63231055240d0af0", "size": 12760, "url": "https://launcher.mojang.com/v1/objects/33824051db1ccb6332e22c2b63231055240d0af0/libbci.so"}}, "executable": true, "type": "file"}, "lib/amd64/libdcpr.so": {"downloads": {"lzma": {"sha1": "70c6b0933a37f2b1124d6e7c131039241fe796ee", "size": 75969, "url": "https://launcher.mojang.com/v1/objects/70c6b0933a37f2b1124d6e7c131039241fe796ee/libdcpr.so"}, "raw": {"sha1": "fa7001bc5d80579e2716590f3eee8027da0beae7", "size": 204456, "url": "https://launcher.mojang.com/v1/objects/fa7001bc5d80579e2716590f3eee8027da0beae7/libdcpr.so"}}, "executable": true, "type": "file"}, "lib/amd64/libdecora_sse.so": {"downloads": {"lzma": {"sha1": "514acc017dfb6cefaf8cc6d18006ce55781cc9bc", "size": 24397, "url": "https://launcher.mojang.com/v1/objects/514acc017dfb6cefaf8cc6d18006ce55781cc9bc/libdecora_sse.so"}, "raw": {"sha1": "d0c84233504c916e548e29f513e25f6a7479abfc", "size": 74912, "url": "https://launcher.mojang.com/v1/objects/d0c84233504c916e548e29f513e25f6a7479abfc/libdecora_sse.so"}}, "executable": true, "type": "file"}, "lib/amd64/libdeploy.so": {"downloads": {"lzma": {"sha1": "6cf31fd98301c749ac0d2c7825f6d925a4409760", "size": 168999, "url": "https://launcher.mojang.com/v1/objects/6cf31fd98301c749ac0d2c7825f6d925a4409760/libdeploy.so"}, "raw": {"sha1": "b3832e97ed8ca794884b56a591b83d02a2c0c06f", "size": 642368, "url": "https://launcher.mojang.com/v1/objects/b3832e97ed8ca794884b56a591b83d02a2c0c06f/libdeploy.so"}}, "executable": true, "type": "file"}, "lib/amd64/libdt_socket.so": {"downloads": {"lzma": {"sha1": "4cc5c880dbb6fa180436d12d60f0abec8ebb59dc", "size": 7784, "url": "https://launcher.mojang.com/v1/objects/4cc5c880dbb6fa180436d12d60f0abec8ebb59dc/libdt_socket.so"}, "raw": {"sha1": "91ce96f252b8139fc12f0f224ed5b1a041767ab7", "size": 24616, "url": "https://launcher.mojang.com/v1/objects/91ce96f252b8139fc12f0f224ed5b1a041767ab7/libdt_socket.so"}}, "executable": true, "type": "file"}, "lib/amd64/libfontmanager.so": {"downloads": {"lzma": {"sha1": "f94e5e94c71c603ff4d3cd1e7e3d9e181fcc145d", "size": 146951, "url": "https://launcher.mojang.com/v1/objects/f94e5e94c71c603ff4d3cd1e7e3d9e181fcc145d/libfontmanager.so"}, "raw": {"sha1": "2428e805f2c53d1283a033dfd11a86fbb7bd7159", "size": 490672, "url": "https://launcher.mojang.com/v1/objects/2428e805f2c53d1283a033dfd11a86fbb7bd7159/libfontmanager.so"}}, "executable": true, "type": "file"}, "lib/amd64/libfxplugins.so": {"downloads": {"lzma": {"sha1": "a640143365d382a5ad743a784bc2f3706d9d6d67", "size": 50048, "url": "https://launcher.mojang.com/v1/objects/a640143365d382a5ad743a784bc2f3706d9d6d67/libfxplugins.so"}, "raw": {"sha1": "0fd4ac04a84c131f1aaee9e6b0898ff9ea69e3ee", "size": 151448, "url": "https://launcher.mojang.com/v1/objects/0fd4ac04a84c131f1aaee9e6b0898ff9ea69e3ee/libfxplugins.so"}}, "executable": true, "type": "file"}, "lib/amd64/libglass.so": {"downloads": {"lzma": {"sha1": "f1ff517714fa5f2c861f33b32db823fe851541f1", "size": 2856, "url": "https://launcher.mojang.com/v1/objects/f1ff517714fa5f2c861f33b32db823fe851541f1/libglass.so"}, "raw": {"sha1": "e7f4fece30ac727be8148d33b8256abd3a41cef9", "size": 13072, "url": "https://launcher.mojang.com/v1/objects/e7f4fece30ac727be8148d33b8256abd3a41cef9/libglass.so"}}, "executable": true, "type": "file"}, "lib/amd64/libglassgtk2.so": {"downloads": {"lzma": {"sha1": "15b90f7a2baacd15e80aa9785d87cf1e4258376d", "size": 220476, "url": "https://launcher.mojang.com/v1/objects/15b90f7a2baacd15e80aa9785d87cf1e4258376d/libglassgtk2.so"}, "raw": {"sha1": "e30a634c2ff2143bdee512360553d6e0304f33b2", "size": 844984, "url": "https://launcher.mojang.com/v1/objects/e30a634c2ff2143bdee512360553d6e0304f33b2/libglassgtk2.so"}}, "executable": true, "type": "file"}, "lib/amd64/libglassgtk3.so": {"downloads": {"lzma": {"sha1": "868c231165f8c9043b7f0e7de208ec023f06a6e7", "size": 220560, "url": "https://launcher.mojang.com/v1/objects/868c231165f8c9043b7f0e7de208ec023f06a6e7/libglassgtk3.so"}, "raw": {"sha1": "762a11a2b376b7b5a2a7cad780715524fdd176d5", "size": 845304, "url": "https://launcher.mojang.com/v1/objects/762a11a2b376b7b5a2a7cad780715524fdd176d5/libglassgtk3.so"}}, "executable": true, "type": "file"}, "lib/amd64/libglib-lite.so": {"downloads": {"lzma": {"sha1": "61b8871242febe1be262de167dc20ae94bf964b4", "size": 457046, "url": "https://launcher.mojang.com/v1/objects/61b8871242febe1be262de167dc20ae94bf964b4/libglib-lite.so"}, "raw": {"sha1": "63afa060fc3f120af76128e51d32603fc4336fa8", "size": 1538352, "url": "https://launcher.mojang.com/v1/objects/63afa060fc3f120af76128e51d32603fc4336fa8/libglib-lite.so"}}, "executable": true, "type": "file"}, "lib/amd64/libgstreamer-lite.so": {"downloads": {"lzma": {"sha1": "2447dc368406ba1b989a29937d41924620e01988", "size": 673056, "url": "https://launcher.mojang.com/v1/objects/2447dc368406ba1b989a29937d41924620e01988/libgstreamer-lite.so"}, "raw": {"sha1": "5505e7ca592ac64371d3db8fe53bcb602e9723d3", "size": 2263872, "url": "https://launcher.mojang.com/v1/objects/5505e7ca592ac64371d3db8fe53bcb602e9723d3/libgstreamer-lite.so"}}, "executable": true, "type": "file"}, "lib/amd64/libhprof.so": {"downloads": {"lzma": {"sha1": "94a5589c818db1fb1cf1881e24e217c309fce2e4", "size": 64471, "url": "https://launcher.mojang.com/v1/objects/94a5589c818db1fb1cf1881e24e217c309fce2e4/libhprof.so"}, "raw": {"sha1": "4bb9bdeef6133b6dd558d52d691b077c03e9b0ee", "size": 175504, "url": "https://launcher.mojang.com/v1/objects/4bb9bdeef6133b6dd558d52d691b077c03e9b0ee/libhprof.so"}}, "executable": true, "type": "file"}, "lib/amd64/libinstrument.so": {"downloads": {"lzma": {"sha1": "84ffea356caf725b42c86a8ebc9587f477ddde29", "size": 18603, "url": "https://launcher.mojang.com/v1/objects/84ffea356caf725b42c86a8ebc9587f477ddde29/libinstrument.so"}, "raw": {"sha1": "cb8009769601e3fecd7ea2b36c344f737b1a9da7", "size": 51560, "url": "https://launcher.mojang.com/v1/objects/cb8009769601e3fecd7ea2b36c344f737b1a9da7/libinstrument.so"}}, "executable": true, "type": "file"}, "lib/amd64/libj2gss.so": {"downloads": {"lzma": {"sha1": "4b2aa699506b126098b585a9617ce1c05707fa29", "size": 14132, "url": "https://launcher.mojang.com/v1/objects/4b2aa699506b126098b585a9617ce1c05707fa29/libj2gss.so"}, "raw": {"sha1": "cbce4a302b255d4d1924ef7606f038af766c5e86", "size": 47688, "url": "https://launcher.mojang.com/v1/objects/cbce4a302b255d4d1924ef7606f038af766c5e86/libj2gss.so"}}, "executable": true, "type": "file"}, "lib/amd64/libj2pcsc.so": {"downloads": {"lzma": {"sha1": "2361d3b2e3da48593c391b29b0d2b5409e4c55e5", "size": 5074, "url": "https://launcher.mojang.com/v1/objects/2361d3b2e3da48593c391b29b0d2b5409e4c55e5/libj2pcsc.so"}, "raw": {"sha1": "1274178492e7a3e997e12f67794616f7c3d8d0b9", "size": 18296, "url": "https://launcher.mojang.com/v1/objects/1274178492e7a3e997e12f67794616f7c3d8d0b9/libj2pcsc.so"}}, "executable": true, "type": "file"}, "lib/amd64/libj2pkcs11.so": {"downloads": {"lzma": {"sha1": "ef927e2790ba05931d0f0bdd63da3d275a834946", "size": 21573, "url": "https://launcher.mojang.com/v1/objects/ef927e2790ba05931d0f0bdd63da3d275a834946/libj2pkcs11.so"}, "raw": {"sha1": "bd4f2af9bfdc6168633d1920c1a1415de06bb45a", "size": 79472, "url": "https://launcher.mojang.com/v1/objects/bd4f2af9bfdc6168633d1920c1a1415de06bb45a/libj2pkcs11.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjaas_unix.so": {"downloads": {"lzma": {"sha1": "7f7e843544ee1eb1454a5826bdd4218685b79430", "size": 2404, "url": "https://launcher.mojang.com/v1/objects/7f7e843544ee1eb1454a5826bdd4218685b79430/libjaas_unix.so"}, "raw": {"sha1": "4c517925c7d464a5b719898eb0bea1b04df31f1f", "size": 8192, "url": "https://launcher.mojang.com/v1/objects/4c517925c7d464a5b719898eb0bea1b04df31f1f/libjaas_unix.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjava.so": {"downloads": {"lzma": {"sha1": "5eee7a42600a44a8bb8d6d7f510fd96a29637ac0", "size": 63113, "url": "https://launcher.mojang.com/v1/objects/5eee7a42600a44a8bb8d6d7f510fd96a29637ac0/libjava.so"}, "raw": {"sha1": "e280aeddf3fc0ec664aef7efc0e0e197a54aaf02", "size": 227672, "url": "https://launcher.mojang.com/v1/objects/e280aeddf3fc0ec664aef7efc0e0e197a54aaf02/libjava.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjava_crw_demo.so": {"downloads": {"lzma": {"sha1": "b197cf23ae3556eb0b45c663f0a8cb62408b961e", "size": 10412, "url": "https://launcher.mojang.com/v1/objects/b197cf23ae3556eb0b45c663f0a8cb62408b961e/libjava_crw_demo.so"}, "raw": {"sha1": "18f20f906977c90d0090b41dbda8dd5cfead5a4c", "size": 26144, "url": "https://launcher.mojang.com/v1/objects/18f20f906977c90d0090b41dbda8dd5cfead5a4c/libjava_crw_demo.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjavafx_font.so": {"downloads": {"lzma": {"sha1": "ffbba0e5022f829412b86063d8a90f95f16709b1", "size": 5608, "url": "https://launcher.mojang.com/v1/objects/ffbba0e5022f829412b86063d8a90f95f16709b1/libjavafx_font.so"}, "raw": {"sha1": "8634a0aca612fc40420a4a7cc8af4cc46cfc6725", "size": 17104, "url": "https://launcher.mojang.com/v1/objects/8634a0aca612fc40420a4a7cc8af4cc46cfc6725/libjavafx_font.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjavafx_font_freetype.so": {"downloads": {"lzma": {"sha1": "310271eda8a2ac264ffc3640a9d847b49438d0bd", "size": 6942, "url": "https://launcher.mojang.com/v1/objects/310271eda8a2ac264ffc3640a9d847b49438d0bd/libjavafx_font_freetype.so"}, "raw": {"sha1": "3e7572d047c12ba2bc43acec7f98a67c20af8042", "size": 27616, "url": "https://launcher.mojang.com/v1/objects/3e7572d047c12ba2bc43acec7f98a67c20af8042/libjavafx_font_freetype.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjavafx_font_pango.so": {"downloads": {"lzma": {"sha1": "a7bcf0669e70b0f43099a99c81e6b6440cb40ac0", "size": 5820, "url": "https://launcher.mojang.com/v1/objects/a7bcf0669e70b0f43099a99c81e6b6440cb40ac0/libjavafx_font_pango.so"}, "raw": {"sha1": "f0b775cc9a514c7ee8b4d6fb300653ce548caf10", "size": 25560, "url": "https://launcher.mojang.com/v1/objects/f0b775cc9a514c7ee8b4d6fb300653ce548caf10/libjavafx_font_pango.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjavafx_font_t2k.so": {"downloads": {"lzma": {"sha1": "551c29dc7c7fc83223aa36a6187f7e0c5d650538", "size": 431450, "url": "https://launcher.mojang.com/v1/objects/551c29dc7c7fc83223aa36a6187f7e0c5d650538/libjavafx_font_t2k.so"}, "raw": {"sha1": "91e5813057c3b852d411540160f8ad05fb9f1ed3", "size": 1486128, "url": "https://launcher.mojang.com/v1/objects/91e5813057c3b852d411540160f8ad05fb9f1ed3/libjavafx_font_t2k.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjavafx_iio.so": {"downloads": {"lzma": {"sha1": "c832998fd5e06ed6dcd6428816194c350785420c", "size": 101479, "url": "https://launcher.mojang.com/v1/objects/c832998fd5e06ed6dcd6428816194c350785420c/libjavafx_iio.so"}, "raw": {"sha1": "dcdf68cb25677b76c1cf0bb94294e6e9880a6678", "size": 256336, "url": "https://launcher.mojang.com/v1/objects/dcdf68cb25677b76c1cf0bb94294e6e9880a6678/libjavafx_iio.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjawt.so": {"downloads": {"lzma": {"sha1": "c1ced6aad5c69ff444dc67d0fd7e333558953831", "size": 1872, "url": "https://launcher.mojang.com/v1/objects/c1ced6aad5c69ff444dc67d0fd7e333558953831/libjawt.so"}, "raw": {"sha1": "c5032f2c6fa40bea24e56605cf76b26a27e87b67", "size": 8048, "url": "https://launcher.mojang.com/v1/objects/c5032f2c6fa40bea24e56605cf76b26a27e87b67/libjawt.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjdwp.so": {"downloads": {"lzma": {"sha1": "c1aabbb3f5a624b9ad10ed871a1d83510a99b646", "size": 94884, "url": "https://launcher.mojang.com/v1/objects/c1aabbb3f5a624b9ad10ed871a1d83510a99b646/libjdwp.so"}, "raw": {"sha1": "a043e97be47937f6f552e94cf79c76c1c57f9594", "size": 272248, "url": "https://launcher.mojang.com/v1/objects/a043e97be47937f6f552e94cf79c76c1c57f9594/libjdwp.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjfr.so": {"downloads": {"lzma": {"sha1": "11b8e6bfffdccbacbf9dd29dea4b90b753f3c1b7", "size": 8780, "url": "https://launcher.mojang.com/v1/objects/11b8e6bfffdccbacbf9dd29dea4b90b753f3c1b7/libjfr.so"}, "raw": {"sha1": "312392dd186b11c418183e818f1928e8685a07e5", "size": 28384, "url": "https://launcher.mojang.com/v1/objects/312392dd186b11c418183e818f1928e8685a07e5/libjfr.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjfxmedia.so": {"downloads": {"lzma": {"sha1": "a4e7a126eb648ce6e5e6dc151831da37d8334139", "size": 391897, "url": "https://launcher.mojang.com/v1/objects/a4e7a126eb648ce6e5e6dc151831da37d8334139/libjfxmedia.so"}, "raw": {"sha1": "5fa54944327a6012c3d34cb5c1c4432762178dc8", "size": 1636376, "url": "https://launcher.mojang.com/v1/objects/5fa54944327a6012c3d34cb5c1c4432762178dc8/libjfxmedia.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjfxwebkit.so": {"downloads": {"lzma": {"sha1": "b274debd222cdcc2ee84160ebb95144b3880bc97", "size": 20492825, "url": "https://launcher.mojang.com/v1/objects/b274debd222cdcc2ee84160ebb95144b3880bc97/libjfxwebkit.so"}, "raw": {"sha1": "ecee564c3b2f645131b35bb3004abd4caeabd291", "size": 91014584, "url": "https://launcher.mojang.com/v1/objects/ecee564c3b2f645131b35bb3004abd4caeabd291/libjfxwebkit.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjpeg.so": {"downloads": {"lzma": {"sha1": "9ad55e370c5eaaa73c3158339db3c368b1aaf0cb", "size": 113072, "url": "https://launcher.mojang.com/v1/objects/9ad55e370c5eaaa73c3158339db3c368b1aaf0cb/libjpeg.so"}, "raw": {"sha1": "651e6d53ae67db1f0efbf7f104447a9b49b7e333", "size": 292520, "url": "https://launcher.mojang.com/v1/objects/651e6d53ae67db1f0efbf7f104447a9b49b7e333/libjpeg.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjsdt.so": {"downloads": {"lzma": {"sha1": "04b6d1361a34c496b5f652b2477784d69b8b6baf", "size": 3964, "url": "https://launcher.mojang.com/v1/objects/04b6d1361a34c496b5f652b2477784d69b8b6baf/libjsdt.so"}, "raw": {"sha1": "82b48a82bf6183d34cf00a0f81661b45c616f31b", "size": 12904, "url": "https://launcher.mojang.com/v1/objects/82b48a82bf6183d34cf00a0f81661b45c616f31b/libjsdt.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjsig.so": {"downloads": {"lzma": {"sha1": "37d3b89abde397216cc4ecb1339d8543d99b8428", "size": 3536, "url": "https://launcher.mojang.com/v1/objects/37d3b89abde397216cc4ecb1339d8543d99b8428/libjsig.so"}, "raw": {"sha1": "42e52ba1bcbe0362ab24bcf65c93797354db6fb9", "size": 13336, "url": "https://launcher.mojang.com/v1/objects/42e52ba1bcbe0362ab24bcf65c93797354db6fb9/libjsig.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjsound.so": {"downloads": {"lzma": {"sha1": "7e3c565d74d8ffae716f32b05544fa4a6f108adc", "size": 2002, "url": "https://launcher.mojang.com/v1/objects/7e3c565d74d8ffae716f32b05544fa4a6f108adc/libjsound.so"}, "raw": {"sha1": "0c0fc63b92d7b83c9960fa80d45c80553ea20254", "size": 8232, "url": "https://launcher.mojang.com/v1/objects/0c0fc63b92d7b83c9960fa80d45c80553ea20254/libjsound.so"}}, "executable": true, "type": "file"}, "lib/amd64/libjsoundalsa.so": {"downloads": {"lzma": {"sha1": "b06c51858a25ff776519495f1b9b3d9f604b089f", "size": 23097, "url": "https://launcher.mojang.com/v1/objects/b06c51858a25ff776519495f1b9b3d9f604b089f/libjsoundalsa.so"}, "raw": {"sha1": "281d37f0326d4a12dc7ea316ead09c198ff7bdf7", "size": 83256, "url": "https://launcher.mojang.com/v1/objects/281d37f0326d4a12dc7ea316ead09c198ff7bdf7/libjsoundalsa.so"}}, "executable": true, "type": "file"}, "lib/amd64/liblcms.so": {"downloads": {"lzma": {"sha1": "7a239baba2086cae49114b382b74b971da02f08e", "size": 176175, "url": "https://launcher.mojang.com/v1/objects/7a239baba2086cae49114b382b74b971da02f08e/liblcms.so"}, "raw": {"sha1": "c8895cc3c3d023d9e059225969ab67954772c0a1", "size": 526872, "url": "https://launcher.mojang.com/v1/objects/c8895cc3c3d023d9e059225969ab67954772c0a1/liblcms.so"}}, "executable": true, "type": "file"}, "lib/amd64/libmanagement.so": {"downloads": {"lzma": {"sha1": "aed3fdbcefd1716abfc6a306687c8b741cbb318e", "size": 12838, "url": "https://launcher.mojang.com/v1/objects/aed3fdbcefd1716abfc6a306687c8b741cbb318e/libmanagement.so"}, "raw": {"sha1": "eba35f61e0d50e30874b7c7b335edf2d52662423", "size": 51808, "url": "https://launcher.mojang.com/v1/objects/eba35f61e0d50e30874b7c7b335edf2d52662423/libmanagement.so"}}, "executable": true, "type": "file"}, "lib/amd64/libmlib_image.so": {"downloads": {"lzma": {"sha1": "1bb181f079492d55c7a458e96488cd17fe0a7b86", "size": 310272, "url": "https://launcher.mojang.com/v1/objects/1bb181f079492d55c7a458e96488cd17fe0a7b86/libmlib_image.so"}, "raw": {"sha1": "c973c450d33873675945d4694be484e3427f58f1", "size": 1048136, "url": "https://launcher.mojang.com/v1/objects/c973c450d33873675945d4694be484e3427f58f1/libmlib_image.so"}}, "executable": true, "type": "file"}, "lib/amd64/libnet.so": {"downloads": {"lzma": {"sha1": "9dd79703b6deb86e0321afe01c6ac508263c8312", "size": 38123, "url": "https://launcher.mojang.com/v1/objects/9dd79703b6deb86e0321afe01c6ac508263c8312/libnet.so"}, "raw": {"sha1": "b3a17b7d53fcdf1e689e1ec29ce851eee6022ead", "size": 116920, "url": "https://launcher.mojang.com/v1/objects/b3a17b7d53fcdf1e689e1ec29ce851eee6022ead/libnet.so"}}, "executable": true, "type": "file"}, "lib/amd64/libnio.so": {"downloads": {"lzma": {"sha1": "5697c89d5d5d9b74f2e1555fcbba79dd4049e287", "size": 24445, "url": "https://launcher.mojang.com/v1/objects/5697c89d5d5d9b74f2e1555fcbba79dd4049e287/libnio.so"}, "raw": {"sha1": "573bf8f64dbcc397f8abd3e1da28f90ab0679f5b", "size": 93872, "url": "https://launcher.mojang.com/v1/objects/573bf8f64dbcc397f8abd3e1da28f90ab0679f5b/libnio.so"}}, "executable": true, "type": "file"}, "lib/amd64/libnpjp2.so": {"downloads": {"lzma": {"sha1": "6fe53b5951ff740e7f2ef7ffe5975af26da06718", "size": 57892, "url": "https://launcher.mojang.com/v1/objects/6fe53b5951ff740e7f2ef7ffe5975af26da06718/libnpjp2.so"}, "raw": {"sha1": "2bb13c53a4280379253475e51216b97eed1d4ce3", "size": 216592, "url": "https://launcher.mojang.com/v1/objects/2bb13c53a4280379253475e51216b97eed1d4ce3/libnpjp2.so"}}, "executable": true, "type": "file"}, "lib/amd64/libnpt.so": {"downloads": {"lzma": {"sha1": "1b170b09a32b1b8b6624fa5d1f94ec60b2bf3876", "size": 5070, "url": "https://launcher.mojang.com/v1/objects/1b170b09a32b1b8b6624fa5d1f94ec60b2bf3876/libnpt.so"}, "raw": {"sha1": "6b1ff6b9b4624f3cc7801f221c82b8046fb76364", "size": 17504, "url": "https://launcher.mojang.com/v1/objects/6b1ff6b9b4624f3cc7801f221c82b8046fb76364/libnpt.so"}}, "executable": true, "type": "file"}, "lib/amd64/libprism_common.so": {"downloads": {"lzma": {"sha1": "f4aca04c90bc7505851c074a08b2c31cae1f94fa", "size": 23315, "url": "https://launcher.mojang.com/v1/objects/f4aca04c90bc7505851c074a08b2c31cae1f94fa/libprism_common.so"}, "raw": {"sha1": "b00866b6ed8646a29a334d46e297267552f27de8", "size": 59008, "url": "https://launcher.mojang.com/v1/objects/b00866b6ed8646a29a334d46e297267552f27de8/libprism_common.so"}}, "executable": true, "type": "file"}, "lib/amd64/libprism_es2.so": {"downloads": {"lzma": {"sha1": "7ff4173c338c7a6f370f231670055737e032da3e", "size": 18416, "url": "https://launcher.mojang.com/v1/objects/7ff4173c338c7a6f370f231670055737e032da3e/libprism_es2.so"}, "raw": {"sha1": "1390a1dc14345e5a948148e59195d62f3a83863f", "size": 63808, "url": "https://launcher.mojang.com/v1/objects/1390a1dc14345e5a948148e59195d62f3a83863f/libprism_es2.so"}}, "executable": true, "type": "file"}, "lib/amd64/libprism_sw.so": {"downloads": {"lzma": {"sha1": "6728e8bf7b214067d715be6fe0325910d63c2468", "size": 29457, "url": "https://launcher.mojang.com/v1/objects/6728e8bf7b214067d715be6fe0325910d63c2468/libprism_sw.so"}, "raw": {"sha1": "7a6c34cb2bbcde411778d1b3f8677c39e32c3ac4", "size": 71608, "url": "https://launcher.mojang.com/v1/objects/7a6c34cb2bbcde411778d1b3f8677c39e32c3ac4/libprism_sw.so"}}, "executable": true, "type": "file"}, "lib/amd64/libresource.so": {"downloads": {"lzma": {"sha1": "1e35e63f1e74915fba620f1febf420b919d49bc5", "size": 2633, "url": "https://launcher.mojang.com/v1/objects/1e35e63f1e74915fba620f1febf420b919d49bc5/libresource.so"}, "raw": {"sha1": "57490353ad0d83ab1930355213dea269795434fe", "size": 13456, "url": "https://launcher.mojang.com/v1/objects/57490353ad0d83ab1930355213dea269795434fe/libresource.so"}}, "executable": true, "type": "file"}, "lib/amd64/libsctp.so": {"downloads": {"lzma": {"sha1": "4340132ed250d7849a016e071be773eaedd33aa8", "size": 9332, "url": "https://launcher.mojang.com/v1/objects/4340132ed250d7849a016e071be773eaedd33aa8/libsctp.so"}, "raw": {"sha1": "4a80e743750f127c0d5a359f5cd60b97e7ee12ae", "size": 29552, "url": "https://launcher.mojang.com/v1/objects/4a80e743750f127c0d5a359f5cd60b97e7ee12ae/libsctp.so"}}, "executable": true, "type": "file"}, "lib/amd64/libsplashscreen.so": {"downloads": {"lzma": {"sha1": "b226c8dbd73a548fc2b042ee6db6cc80e727597c", "size": 190305, "url": "https://launcher.mojang.com/v1/objects/b226c8dbd73a548fc2b042ee6db6cc80e727597c/libsplashscreen.so"}, "raw": {"sha1": "87d6a491f5ba7e6c4d972264a0c9063afea567a2", "size": 441376, "url": "https://launcher.mojang.com/v1/objects/87d6a491f5ba7e6c4d972264a0c9063afea567a2/libsplashscreen.so"}}, "executable": true, "type": "file"}, "lib/amd64/libsunec.so": {"downloads": {"lzma": {"sha1": "6ebba98fab1e3d872d1363235b76497f6f9babdc", "size": 88829, "url": "https://launcher.mojang.com/v1/objects/6ebba98fab1e3d872d1363235b76497f6f9babdc/libsunec.so"}, "raw": {"sha1": "3b262a0a530f6e4e539aed2cd27b4de1d0ed8859", "size": 283368, "url": "https://launcher.mojang.com/v1/objects/3b262a0a530f6e4e539aed2cd27b4de1d0ed8859/libsunec.so"}}, "executable": true, "type": "file"}, "lib/amd64/libt2k.so": {"downloads": {"lzma": {"sha1": "602cb812ef0b350ccf56ce209a260ddbe3743d92", "size": 190720, "url": "https://launcher.mojang.com/v1/objects/602cb812ef0b350ccf56ce209a260ddbe3743d92/libt2k.so"}, "raw": {"sha1": "b072c56df997f61e15e6b5a43b8907b0d25c2043", "size": 504840, "url": "https://launcher.mojang.com/v1/objects/b072c56df997f61e15e6b5a43b8907b0d25c2043/libt2k.so"}}, "executable": true, "type": "file"}, "lib/amd64/libunpack.so": {"downloads": {"lzma": {"sha1": "7107b615e941074f0b14c31c88fb67200aacd37f", "size": 70308, "url": "https://launcher.mojang.com/v1/objects/7107b615e941074f0b14c31c88fb67200aacd37f/libunpack.so"}, "raw": {"sha1": "b05ff862ed87928ed91e80e5604673c5ea710a53", "size": 197712, "url": "https://launcher.mojang.com/v1/objects/b05ff862ed87928ed91e80e5604673c5ea710a53/libunpack.so"}}, "executable": true, "type": "file"}, "lib/amd64/libverify.so": {"downloads": {"lzma": {"sha1": "ecd98efb8c7da441a8c3580e8f5598f3cb4165b1", "size": 21885, "url": "https://launcher.mojang.com/v1/objects/ecd98efb8c7da441a8c3580e8f5598f3cb4165b1/libverify.so"}, "raw": {"sha1": "e2c8d92531c45ab9be69ffb72c87fa12e9e59827", "size": 66112, "url": "https://launcher.mojang.com/v1/objects/e2c8d92531c45ab9be69ffb72c87fa12e9e59827/libverify.so"}}, "executable": true, "type": "file"}, "lib/amd64/libzip.so": {"downloads": {"lzma": {"sha1": "7c562342e3f7b138dc978495447e3e6a96c2cf45", "size": 54876, "url": "https://launcher.mojang.com/v1/objects/7c562342e3f7b138dc978495447e3e6a96c2cf45/libzip.so"}, "raw": {"sha1": "5f4bf35a5c3e8f8c650e891d1031589b8ab6d77f", "size": 127016, "url": "https://launcher.mojang.com/v1/objects/5f4bf35a5c3e8f8c650e891d1031589b8ab6d77f/libzip.so"}}, "executable": true, "type": "file"}, "lib/amd64/server": {"type": "directory"}, "lib/amd64/server/Xusage.txt": {"downloads": {"lzma": {"sha1": "acb2da24a4c765887df83985e4c26d6be302a0a3", "size": 629, "url": "https://launcher.mojang.com/v1/objects/acb2da24a4c765887df83985e4c26d6be302a0a3/Xusage.txt"}, "raw": {"sha1": "6983727eafe140f9dd793c78aa6f3e007438243a", "size": 1423, "url": "https://launcher.mojang.com/v1/objects/6983727eafe140f9dd793c78aa6f3e007438243a/Xusage.txt"}}, "executable": false, "type": "file"}, "lib/amd64/server/libjsig.so": {"target": "../libjsig.so", "type": "link"}, "lib/amd64/server/libjvm.so": {"downloads": {"lzma": {"sha1": "d5c6f3338aaa6712f79d680ac8c3e31beebaa886", "size": 4154311, "url": "https://launcher.mojang.com/v1/objects/d5c6f3338aaa6712f79d680ac8c3e31beebaa886/libjvm.so"}, "raw": {"sha1": "23a98e1eb505cc3fb91bc0cb2adb71ab9270e9ca", "size": 17045016, "url": "https://launcher.mojang.com/v1/objects/23a98e1eb505cc3fb91bc0cb2adb71ab9270e9ca/libjvm.so"}}, "executable": true, "type": "file"}, "lib/applet": {"type": "directory"}, "lib/calendars.properties": {"downloads": {"lzma": {"sha1": "4a757c23f2942bd802a4f80235131146d9267750", "size": 558, "url": "https://launcher.mojang.com/v1/objects/4a757c23f2942bd802a4f80235131146d9267750/calendars.properties"}, "raw": {"sha1": "42ebb0988124433b8f2a6e5d9a74ed41240bcfc6", "size": 1378, "url": "https://launcher.mojang.com/v1/objects/42ebb0988124433b8f2a6e5d9a74ed41240bcfc6/calendars.properties"}}, "executable": false, "type": "file"}, "lib/charsets.jar": {"downloads": {"lzma": {"sha1": "2bf44143b2ad9d7d55045a4de4a562330c194dc0", "size": 412367, "url": "https://launcher.mojang.com/v1/objects/2bf44143b2ad9d7d55045a4de4a562330c194dc0/charsets.jar"}, "raw": {"sha1": "d73ab9f8de255a7e112ddd13622bf7f6b18c8447", "size": 3135615, "url": "https://launcher.mojang.com/v1/objects/d73ab9f8de255a7e112ddd13622bf7f6b18c8447/charsets.jar"}}, "executable": false, "type": "file"}, "lib/classlist": {"downloads": {"lzma": {"sha1": "14e7c73d21b8513b0aff8d86e5cb34c52021fbca", "size": 15024, "url": "https://launcher.mojang.com/v1/objects/14e7c73d21b8513b0aff8d86e5cb34c52021fbca/classlist"}, "raw": {"sha1": "9c0404b63c87e2fed35e3a6cd137d6cf876c42bd", "size": 84311, "url": "https://launcher.mojang.com/v1/objects/9c0404b63c87e2fed35e3a6cd137d6cf876c42bd/classlist"}}, "executable": false, "type": "file"}, "lib/cmm": {"type": "directory"}, "lib/cmm/CIEXYZ.pf": {"downloads": {"lzma": {"sha1": "fcc5ca2fd3f45cac3434b480fa3ce00007e96529", "size": 8964, "url": "https://launcher.mojang.com/v1/objects/fcc5ca2fd3f45cac3434b480fa3ce00007e96529/CIEXYZ.pf"}, "raw": {"sha1": "b7779924c70554647b87c2a86159ca7781e929f8", "size": 51236, "url": "https://launcher.mojang.com/v1/objects/b7779924c70554647b87c2a86159ca7781e929f8/CIEXYZ.pf"}}, "executable": false, "type": "file"}, "lib/cmm/GRAY.pf": {"downloads": {"lzma": {"sha1": "5388ccfe67d3131d6d02143d8e8895003ab14ff6", "size": 299, "url": "https://launcher.mojang.com/v1/objects/5388ccfe67d3131d6d02143d8e8895003ab14ff6/GRAY.pf"}, "raw": {"sha1": "27f93961d66b8230d0cdb8b166bc8b4153d5bc2d", "size": 632, "url": "https://launcher.mojang.com/v1/objects/27f93961d66b8230d0cdb8b166bc8b4153d5bc2d/GRAY.pf"}}, "executable": false, "type": "file"}, "lib/cmm/LINEAR_RGB.pf": {"downloads": {"lzma": {"sha1": "2bd90f09c8deb64b1729d6b8173c78f9e9cab27b", "size": 678, "url": "https://launcher.mojang.com/v1/objects/2bd90f09c8deb64b1729d6b8173c78f9e9cab27b/LINEAR_RGB.pf"}, "raw": {"sha1": "7913274c2f73bafcf888f09ff60990b100214ede", "size": 1044, "url": "https://launcher.mojang.com/v1/objects/7913274c2f73bafcf888f09ff60990b100214ede/LINEAR_RGB.pf"}}, "executable": false, "type": "file"}, "lib/cmm/PYCC.pf": {"downloads": {"lzma": {"sha1": "dbb2197ecff3fcdd142e9006490c8cb5c8d19af8", "size": 171521, "url": "https://launcher.mojang.com/v1/objects/dbb2197ecff3fcdd142e9006490c8cb5c8d19af8/PYCC.pf"}, "raw": {"sha1": "4f7eed05b8f0eea7bcdc8f8f7aaeb1925ce7b144", "size": 274474, "url": "https://launcher.mojang.com/v1/objects/4f7eed05b8f0eea7bcdc8f8f7aaeb1925ce7b144/PYCC.pf"}}, "executable": false, "type": "file"}, "lib/cmm/sRGB.pf": {"downloads": {"raw": {"sha1": "9eaea0911d89d63e39e95f2e2116eaec7e0bb91e", "size": 3144, "url": "https://launcher.mojang.com/v1/objects/9eaea0911d89d63e39e95f2e2116eaec7e0bb91e/sRGB.pf"}}, "executable": false, "type": "file"}, "lib/content-types.properties": {"downloads": {"lzma": {"sha1": "43a23d9a6c637c128b14cfa3feced93cbcf85b1a", "size": 1617, "url": "https://launcher.mojang.com/v1/objects/43a23d9a6c637c128b14cfa3feced93cbcf85b1a/content-types.properties"}, "raw": {"sha1": "b21698017c4a2866b5fabe59681b7592e72c83b1", "size": 5916, "url": "https://launcher.mojang.com/v1/objects/b21698017c4a2866b5fabe59681b7592e72c83b1/content-types.properties"}}, "executable": false, "type": "file"}, "lib/currency.data": {"downloads": {"lzma": {"sha1": "451b3f166ea34ef2aefbb01606ea5adcc0d65b42", "size": 1184, "url": "https://launcher.mojang.com/v1/objects/451b3f166ea34ef2aefbb01606ea5adcc0d65b42/currency.data"}, "raw": {"sha1": "bf524381a7a9b9d5bbab48069c583d2936e367a1", "size": 4134, "url": "https://launcher.mojang.com/v1/objects/bf524381a7a9b9d5bbab48069c583d2936e367a1/currency.data"}}, "executable": false, "type": "file"}, "lib/deploy": {"type": "directory"}, "lib/deploy.jar": {"downloads": {"raw": {"sha1": "fbe1de8fcd9a3d482c59414dce9311e4194766c9", "size": 2255881, "url": "https://launcher.mojang.com/v1/objects/fbe1de8fcd9a3d482c59414dce9311e4194766c9/deploy.jar"}}, "executable": false, "type": "file"}, "lib/deploy/MixedCodeMainDialog.ui": {"downloads": {"lzma": {"sha1": "7d812964343d1e978442f5c847c709784fc18fc0", "size": 683, "url": "https://launcher.mojang.com/v1/objects/7d812964343d1e978442f5c847c709784fc18fc0/MixedCodeMainDialog.ui"}, "raw": {"sha1": "c9b1af1c229e54b2d8a3d642d4f0bb31dc15be79", "size": 4507, "url": "https://launcher.mojang.com/v1/objects/c9b1af1c229e54b2d8a3d642d4f0bb31dc15be79/MixedCodeMainDialog.ui"}}, "executable": false, "type": "file"}, "lib/deploy/MixedCodeMainDialogJs.ui": {"downloads": {"lzma": {"sha1": "54fb58dbcc59e35e0ae896d0e266ae0c5bcf85c2", "size": 792, "url": "https://launcher.mojang.com/v1/objects/54fb58dbcc59e35e0ae896d0e266ae0c5bcf85c2/MixedCodeMainDialogJs.ui"}, "raw": {"sha1": "ad6337fb6d46750e14c12b439a5856f4b6864d0d", "size": 6110, "url": "https://launcher.mojang.com/v1/objects/ad6337fb6d46750e14c12b439a5856f4b6864d0d/MixedCodeMainDialogJs.ui"}}, "executable": false, "type": "file"}, "lib/deploy/cautionshield.icns": {"downloads": {"lzma": {"sha1": "7cea751dc168605054ec38ce8bfa71812be405c1", "size": 2333, "url": "https://launcher.mojang.com/v1/objects/7cea751dc168605054ec38ce8bfa71812be405c1/cautionshield.icns"}, "raw": {"sha1": "1de7ed5d5fc75aa1bcede088c655bee3bde64c38", "size": 3588, "url": "https://launcher.mojang.com/v1/objects/1de7ed5d5fc75aa1bcede088c655bee3bde64c38/cautionshield.icns"}}, "executable": false, "type": "file"}, "lib/deploy/ffjcext.zip": {"downloads": {"lzma": {"sha1": "80bcb9b3794f69d87dba93e90230f288e651e798", "size": 1809, "url": "https://launcher.mojang.com/v1/objects/80bcb9b3794f69d87dba93e90230f288e651e798/ffjcext.zip"}, "raw": {"sha1": "76d051ca7d3666ff25ea8eb9957a05574a45287f", "size": 13454, "url": "https://launcher.mojang.com/v1/objects/76d051ca7d3666ff25ea8eb9957a05574a45287f/ffjcext.zip"}}, "executable": false, "type": "file"}, "lib/deploy/java-icon.ico": {"downloads": {"lzma": {"sha1": "2a24f0207d7ab5976a8b0d92b4b381d49e895c9d", "size": 8468, "url": "https://launcher.mojang.com/v1/objects/2a24f0207d7ab5976a8b0d92b4b381d49e895c9d/java-icon.ico"}, "raw": {"sha1": "2997ceb26ff49a7d7c5e7a2405b5fb50b62c7914", "size": 29926, "url": "https://launcher.mojang.com/v1/objects/2997ceb26ff49a7d7c5e7a2405b5fb50b62c7914/java-icon.ico"}}, "executable": false, "type": "file"}, "lib/deploy/messages.properties": {"downloads": {"lzma": {"sha1": "c1e16f80dc0b1f1a591cecf3cbab4ba5e47492f4", "size": 1225, "url": "https://launcher.mojang.com/v1/objects/c1e16f80dc0b1f1a591cecf3cbab4ba5e47492f4/messages.properties"}, "raw": {"sha1": "dc52841c708e3c1eb2a044088a43396d1291bb5e", "size": 2860, "url": "https://launcher.mojang.com/v1/objects/dc52841c708e3c1eb2a044088a43396d1291bb5e/messages.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_de.properties": {"downloads": {"lzma": {"sha1": "42b42e6e1d2cb2d781f2226bde612ce519b29bc8", "size": 1394, "url": "https://launcher.mojang.com/v1/objects/42b42e6e1d2cb2d781f2226bde612ce519b29bc8/messages_de.properties"}, "raw": {"sha1": "d989fe1b8f7904888d5102294ebefd28d932ecdb", "size": 3306, "url": "https://launcher.mojang.com/v1/objects/d989fe1b8f7904888d5102294ebefd28d932ecdb/messages_de.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_es.properties": {"downloads": {"lzma": {"sha1": "c4a653e9802ca982e892b45d88c1e259c09e8c8e", "size": 1404, "url": "https://launcher.mojang.com/v1/objects/c4a653e9802ca982e892b45d88c1e259c09e8c8e/messages_es.properties"}, "raw": {"sha1": "1b0334b79db481c3a59be6915d5118d760c97baa", "size": 3600, "url": "https://launcher.mojang.com/v1/objects/1b0334b79db481c3a59be6915d5118d760c97baa/messages_es.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_fr.properties": {"downloads": {"lzma": {"sha1": "2d8dee07e3f5aab7318a22e169810b216ac44f97", "size": 1401, "url": "https://launcher.mojang.com/v1/objects/2d8dee07e3f5aab7318a22e169810b216ac44f97/messages_fr.properties"}, "raw": {"sha1": "69bd2d03c2064f8679de5b4e430ea61b567c69c5", "size": 3409, "url": "https://launcher.mojang.com/v1/objects/69bd2d03c2064f8679de5b4e430ea61b567c69c5/messages_fr.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_it.properties": {"downloads": {"lzma": {"sha1": "7c28cdd8d9e34355ba0fc03004c4f64749cae57e", "size": 1375, "url": "https://launcher.mojang.com/v1/objects/7c28cdd8d9e34355ba0fc03004c4f64749cae57e/messages_it.properties"}, "raw": {"sha1": "dbe49949308f28540a42ae6cd2ad58afbf615592", "size": 3223, "url": "https://launcher.mojang.com/v1/objects/dbe49949308f28540a42ae6cd2ad58afbf615592/messages_it.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_ja.properties": {"downloads": {"lzma": {"sha1": "9a6a4c086e48b9e615b72b6bbebb3c724d178ff4", "size": 1680, "url": "https://launcher.mojang.com/v1/objects/9a6a4c086e48b9e615b72b6bbebb3c724d178ff4/messages_ja.properties"}, "raw": {"sha1": "751170a7cdefcb1226604ac3f8196e06a04fd7ac", "size": 6349, "url": "https://launcher.mojang.com/v1/objects/751170a7cdefcb1226604ac3f8196e06a04fd7ac/messages_ja.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_ko.properties": {"downloads": {"lzma": {"sha1": "0c57c2ebfa0830f816657a384898487fc492efac", "size": 1645, "url": "https://launcher.mojang.com/v1/objects/0c57c2ebfa0830f816657a384898487fc492efac/messages_ko.properties"}, "raw": {"sha1": "bf9e055b5ab138ad6d49769e2b7630b7938848d6", "size": 5712, "url": "https://launcher.mojang.com/v1/objects/bf9e055b5ab138ad6d49769e2b7630b7938848d6/messages_ko.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_pt_BR.properties": {"downloads": {"lzma": {"sha1": "f8364dba0aa0a7e445a1a8d0e7ad66b996f70063", "size": 1388, "url": "https://launcher.mojang.com/v1/objects/f8364dba0aa0a7e445a1a8d0e7ad66b996f70063/messages_pt_BR.properties"}, "raw": {"sha1": "24e4951743521ab9a11381c77bd0cdb1ed30f5b5", "size": 3285, "url": "https://launcher.mojang.com/v1/objects/24e4951743521ab9a11381c77bd0cdb1ed30f5b5/messages_pt_BR.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_sv.properties": {"downloads": {"lzma": {"sha1": "65e5897d552258141aacf02f087c7c9c33ad0727", "size": 1355, "url": "https://launcher.mojang.com/v1/objects/65e5897d552258141aacf02f087c7c9c33ad0727/messages_sv.properties"}, "raw": {"sha1": "bb5a4aa0ba499f6b1916a83e3c7922a4583b4adb", "size": 3384, "url": "https://launcher.mojang.com/v1/objects/bb5a4aa0ba499f6b1916a83e3c7922a4583b4adb/messages_sv.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_zh_CN.properties": {"downloads": {"lzma": {"sha1": "de7d39a6e6748e9f47e842c9da90f515921c222c", "size": 1506, "url": "https://launcher.mojang.com/v1/objects/de7d39a6e6748e9f47e842c9da90f515921c222c/messages_zh_CN.properties"}, "raw": {"sha1": "1c2b96673dddd3596890ef4fc22017d484a1f652", "size": 4072, "url": "https://launcher.mojang.com/v1/objects/1c2b96673dddd3596890ef4fc22017d484a1f652/messages_zh_CN.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_zh_HK.properties": {"downloads": {"lzma": {"sha1": "e8d0e3a63caa2535a4f361033941f34dcc170a7e", "size": 1529, "url": "https://launcher.mojang.com/v1/objects/e8d0e3a63caa2535a4f361033941f34dcc170a7e/messages_zh_TW.properties"}, "raw": {"sha1": "37a57aad121c14c25e149206179728fa62203bf0", "size": 3752, "url": "https://launcher.mojang.com/v1/objects/37a57aad121c14c25e149206179728fa62203bf0/messages_zh_TW.properties"}}, "executable": false, "type": "file"}, "lib/deploy/messages_zh_TW.properties": {"downloads": {"lzma": {"sha1": "e8d0e3a63caa2535a4f361033941f34dcc170a7e", "size": 1529, "url": "https://launcher.mojang.com/v1/objects/e8d0e3a63caa2535a4f361033941f34dcc170a7e/messages_zh_TW.properties"}, "raw": {"sha1": "37a57aad121c14c25e149206179728fa62203bf0", "size": 3752, "url": "https://launcher.mojang.com/v1/objects/37a57aad121c14c25e149206179728fa62203bf0/messages_zh_TW.properties"}}, "executable": false, "type": "file"}, "lib/deploy/mixcode_s.png": {"downloads": {"raw": {"sha1": "4604e9f265eec97bccd0151c3a81afa9e69499e5", "size": 3115, "url": "https://launcher.mojang.com/v1/objects/4604e9f265eec97bccd0151c3a81afa9e69499e5/mixcode_s.png"}}, "executable": false, "type": "file"}, "lib/deploy/splash.gif": {"downloads": {"raw": {"sha1": "20e7aec75f6d036d504277542e507eb7dc24aae8", "size": 8590, "url": "https://launcher.mojang.com/v1/objects/20e7aec75f6d036d504277542e507eb7dc24aae8/splash.gif"}}, "executable": false, "type": "file"}, "lib/deploy/splash@2x.gif": {"downloads": {"raw": {"sha1": "0ae4a5bda2a6d628fac51462390b503c99509fdc", "size": 15276, "url": "https://launcher.mojang.com/v1/objects/0ae4a5bda2a6d628fac51462390b503c99509fdc/splash2x.gif"}}, "executable": false, "type": "file"}, "lib/deploy/splash_11-lic.gif": {"downloads": {"raw": {"sha1": "8def364e07f40142822df84b5bb4f50846cb5e4e", "size": 7805, "url": "https://launcher.mojang.com/v1/objects/8def364e07f40142822df84b5bb4f50846cb5e4e/splash_11-lic.gif"}}, "executable": false, "type": "file"}, "lib/deploy/splash_11@2x-lic.gif": {"downloads": {"raw": {"sha1": "d2bff9bbf7920ca743b81a0ee23b0719b4d057ca", "size": 12250, "url": "https://launcher.mojang.com/v1/objects/d2bff9bbf7920ca743b81a0ee23b0719b4d057ca/splash_11%402x-lic.gif"}}, "executable": false, "type": "file"}, "lib/desktop": {"type": "directory"}, "lib/desktop/applications": {"type": "directory"}, "lib/desktop/applications/sun-java.desktop": {"downloads": {"lzma": {"sha1": "109d1cdf165f38da92da70b403ca940192a7a9a8", "size": 536, "url": "https://launcher.mojang.com/v1/objects/109d1cdf165f38da92da70b403ca940192a7a9a8/sun-java.desktop"}, "raw": {"sha1": "d346dfe90505603ce5aff5a3c6c2e4a23d5bd990", "size": 777, "url": "https://launcher.mojang.com/v1/objects/d346dfe90505603ce5aff5a3c6c2e4a23d5bd990/sun-java.desktop"}}, "executable": false, "type": "file"}, "lib/desktop/applications/sun-javaws.desktop": {"downloads": {"lzma": {"sha1": "5e1815e7f83515881e6998584dc6bb02c5bef09a", "size": 451, "url": "https://launcher.mojang.com/v1/objects/5e1815e7f83515881e6998584dc6bb02c5bef09a/sun-javaws.desktop"}, "raw": {"sha1": "50ce8e519b836e0f53d58ce1a359d98b6cafdda6", "size": 619, "url": "https://launcher.mojang.com/v1/objects/50ce8e519b836e0f53d58ce1a359d98b6cafdda6/sun-javaws.desktop"}}, "executable": false, "type": "file"}, "lib/desktop/applications/sun_java.desktop": {"downloads": {"lzma": {"sha1": "49ab0ccb54c3be68281d05055bc56a88b1281d3c", "size": 447, "url": "https://launcher.mojang.com/v1/objects/49ab0ccb54c3be68281d05055bc56a88b1281d3c/sun_java.desktop"}, "raw": {"sha1": "79120ee8160ad6f3c9b90c2641fb7edf3af96b5d", "size": 624, "url": "https://launcher.mojang.com/v1/objects/79120ee8160ad6f3c9b90c2641fb7edf3af96b5d/sun_java.desktop"}}, "executable": false, "type": "file"}, "lib/desktop/icons": {"type": "directory"}, "lib/desktop/icons/HighContrast": {"type": "directory"}, "lib/desktop/icons/HighContrast/16x16": {"type": "directory"}, "lib/desktop/icons/HighContrast/16x16/apps": {"type": "directory"}, "lib/desktop/icons/HighContrast/16x16/apps/sun-java.png": {"downloads": {"raw": {"sha1": "366e7a48e9e4fb92eaeabbcaeb4626122a66cecb", "size": 417, "url": "https://launcher.mojang.com/v1/objects/366e7a48e9e4fb92eaeabbcaeb4626122a66cecb/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/16x16/apps/sun-javaws.png": {"downloads": {"raw": {"sha1": "366e7a48e9e4fb92eaeabbcaeb4626122a66cecb", "size": 417, "url": "https://launcher.mojang.com/v1/objects/366e7a48e9e4fb92eaeabbcaeb4626122a66cecb/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/16x16/apps/sun-jcontrol.png": {"downloads": {"raw": {"sha1": "366e7a48e9e4fb92eaeabbcaeb4626122a66cecb", "size": 417, "url": "https://launcher.mojang.com/v1/objects/366e7a48e9e4fb92eaeabbcaeb4626122a66cecb/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/16x16/mimetypes": {"type": "directory"}, "lib/desktop/icons/HighContrast/16x16/mimetypes/gnome-mime-application-x-java-archive.png": {"downloads": {"raw": {"sha1": "629c48907368ecf32d2395b6459c367f79d84689", "size": 464, "url": "https://launcher.mojang.com/v1/objects/629c48907368ecf32d2395b6459c367f79d84689/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/16x16/mimetypes/gnome-mime-application-x-java-jnlp-file.png": {"downloads": {"raw": {"sha1": "629c48907368ecf32d2395b6459c367f79d84689", "size": 464, "url": "https://launcher.mojang.com/v1/objects/629c48907368ecf32d2395b6459c367f79d84689/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/16x16/mimetypes/gnome-mime-text-x-java.png": {"downloads": {"raw": {"sha1": "629c48907368ecf32d2395b6459c367f79d84689", "size": 464, "url": "https://launcher.mojang.com/v1/objects/629c48907368ecf32d2395b6459c367f79d84689/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/48x48": {"type": "directory"}, "lib/desktop/icons/HighContrast/48x48/apps": {"type": "directory"}, "lib/desktop/icons/HighContrast/48x48/apps/sun-java.png": {"downloads": {"raw": {"sha1": "8373482d072684e09830dbdb97a76ea264c9f4e9", "size": 3451, "url": "https://launcher.mojang.com/v1/objects/8373482d072684e09830dbdb97a76ea264c9f4e9/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/48x48/apps/sun-javaws.png": {"downloads": {"raw": {"sha1": "8373482d072684e09830dbdb97a76ea264c9f4e9", "size": 3451, "url": "https://launcher.mojang.com/v1/objects/8373482d072684e09830dbdb97a76ea264c9f4e9/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/48x48/apps/sun-jcontrol.png": {"downloads": {"raw": {"sha1": "8373482d072684e09830dbdb97a76ea264c9f4e9", "size": 3451, "url": "https://launcher.mojang.com/v1/objects/8373482d072684e09830dbdb97a76ea264c9f4e9/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/48x48/mimetypes": {"type": "directory"}, "lib/desktop/icons/HighContrast/48x48/mimetypes/gnome-mime-application-x-java-archive.png": {"downloads": {"raw": {"sha1": "56a4996519f8f3541eba7b7a7a69bcdcd8ed0410", "size": 2088, "url": "https://launcher.mojang.com/v1/objects/56a4996519f8f3541eba7b7a7a69bcdcd8ed0410/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/48x48/mimetypes/gnome-mime-application-x-java-jnlp-file.png": {"downloads": {"raw": {"sha1": "56a4996519f8f3541eba7b7a7a69bcdcd8ed0410", "size": 2088, "url": "https://launcher.mojang.com/v1/objects/56a4996519f8f3541eba7b7a7a69bcdcd8ed0410/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrast/48x48/mimetypes/gnome-mime-text-x-java.png": {"downloads": {"raw": {"sha1": "56a4996519f8f3541eba7b7a7a69bcdcd8ed0410", "size": 2088, "url": "https://launcher.mojang.com/v1/objects/56a4996519f8f3541eba7b7a7a69bcdcd8ed0410/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse": {"type": "directory"}, "lib/desktop/icons/HighContrastInverse/16x16": {"type": "directory"}, "lib/desktop/icons/HighContrastInverse/16x16/apps": {"type": "directory"}, "lib/desktop/icons/HighContrastInverse/16x16/apps/sun-java.png": {"downloads": {"raw": {"sha1": "bf0995acb94aa794e73c5b971282ff13ffe42793", "size": 402, "url": "https://launcher.mojang.com/v1/objects/bf0995acb94aa794e73c5b971282ff13ffe42793/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/16x16/apps/sun-javaws.png": {"downloads": {"raw": {"sha1": "bf0995acb94aa794e73c5b971282ff13ffe42793", "size": 402, "url": "https://launcher.mojang.com/v1/objects/bf0995acb94aa794e73c5b971282ff13ffe42793/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/16x16/apps/sun-jcontrol.png": {"downloads": {"raw": {"sha1": "bf0995acb94aa794e73c5b971282ff13ffe42793", "size": 402, "url": "https://launcher.mojang.com/v1/objects/bf0995acb94aa794e73c5b971282ff13ffe42793/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/16x16/mimetypes": {"type": "directory"}, "lib/desktop/icons/HighContrastInverse/16x16/mimetypes/gnome-mime-application-x-java-archive.png": {"downloads": {"raw": {"sha1": "1477eceda25e162fcda2e69ee3906091973d8344", "size": 473, "url": "https://launcher.mojang.com/v1/objects/1477eceda25e162fcda2e69ee3906091973d8344/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/16x16/mimetypes/gnome-mime-application-x-java-jnlp-file.png": {"downloads": {"raw": {"sha1": "1477eceda25e162fcda2e69ee3906091973d8344", "size": 473, "url": "https://launcher.mojang.com/v1/objects/1477eceda25e162fcda2e69ee3906091973d8344/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/16x16/mimetypes/gnome-mime-text-x-java.png": {"downloads": {"raw": {"sha1": "1477eceda25e162fcda2e69ee3906091973d8344", "size": 473, "url": "https://launcher.mojang.com/v1/objects/1477eceda25e162fcda2e69ee3906091973d8344/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/48x48": {"type": "directory"}, "lib/desktop/icons/HighContrastInverse/48x48/apps": {"type": "directory"}, "lib/desktop/icons/HighContrastInverse/48x48/apps/sun-java.png": {"downloads": {"raw": {"sha1": "413da160dd9899b95f53d4cc11f5ee0550cc6585", "size": 3410, "url": "https://launcher.mojang.com/v1/objects/413da160dd9899b95f53d4cc11f5ee0550cc6585/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/48x48/apps/sun-javaws.png": {"downloads": {"raw": {"sha1": "413da160dd9899b95f53d4cc11f5ee0550cc6585", "size": 3410, "url": "https://launcher.mojang.com/v1/objects/413da160dd9899b95f53d4cc11f5ee0550cc6585/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/48x48/apps/sun-jcontrol.png": {"downloads": {"raw": {"sha1": "413da160dd9899b95f53d4cc11f5ee0550cc6585", "size": 3410, "url": "https://launcher.mojang.com/v1/objects/413da160dd9899b95f53d4cc11f5ee0550cc6585/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/48x48/mimetypes": {"type": "directory"}, "lib/desktop/icons/HighContrastInverse/48x48/mimetypes/gnome-mime-application-x-java-archive.png": {"downloads": {"raw": {"sha1": "d66e04dfa25c196bec2e201547325b79846ab674", "size": 2085, "url": "https://launcher.mojang.com/v1/objects/d66e04dfa25c196bec2e201547325b79846ab674/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/48x48/mimetypes/gnome-mime-application-x-java-jnlp-file.png": {"downloads": {"raw": {"sha1": "d66e04dfa25c196bec2e201547325b79846ab674", "size": 2085, "url": "https://launcher.mojang.com/v1/objects/d66e04dfa25c196bec2e201547325b79846ab674/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/HighContrastInverse/48x48/mimetypes/gnome-mime-text-x-java.png": {"downloads": {"raw": {"sha1": "d66e04dfa25c196bec2e201547325b79846ab674", "size": 2085, "url": "https://launcher.mojang.com/v1/objects/d66e04dfa25c196bec2e201547325b79846ab674/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast": {"type": "directory"}, "lib/desktop/icons/LowContrast/16x16": {"type": "directory"}, "lib/desktop/icons/LowContrast/16x16/apps": {"type": "directory"}, "lib/desktop/icons/LowContrast/16x16/apps/sun-java.png": {"downloads": {"raw": {"sha1": "f93b7cf0a6d27d664a7f09dab6933b2768536f52", "size": 519, "url": "https://launcher.mojang.com/v1/objects/f93b7cf0a6d27d664a7f09dab6933b2768536f52/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/16x16/apps/sun-javaws.png": {"downloads": {"raw": {"sha1": "f93b7cf0a6d27d664a7f09dab6933b2768536f52", "size": 519, "url": "https://launcher.mojang.com/v1/objects/f93b7cf0a6d27d664a7f09dab6933b2768536f52/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/16x16/apps/sun-jcontrol.png": {"downloads": {"raw": {"sha1": "f93b7cf0a6d27d664a7f09dab6933b2768536f52", "size": 519, "url": "https://launcher.mojang.com/v1/objects/f93b7cf0a6d27d664a7f09dab6933b2768536f52/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/16x16/mimetypes": {"type": "directory"}, "lib/desktop/icons/LowContrast/16x16/mimetypes/gnome-mime-application-x-java-archive.png": {"downloads": {"raw": {"sha1": "0aa1605877280b88de1f1cc3e7e4bdbeed968a73", "size": 525, "url": "https://launcher.mojang.com/v1/objects/0aa1605877280b88de1f1cc3e7e4bdbeed968a73/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/16x16/mimetypes/gnome-mime-application-x-java-jnlp-file.png": {"downloads": {"raw": {"sha1": "0aa1605877280b88de1f1cc3e7e4bdbeed968a73", "size": 525, "url": "https://launcher.mojang.com/v1/objects/0aa1605877280b88de1f1cc3e7e4bdbeed968a73/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/16x16/mimetypes/gnome-mime-text-x-java.png": {"downloads": {"raw": {"sha1": "0aa1605877280b88de1f1cc3e7e4bdbeed968a73", "size": 525, "url": "https://launcher.mojang.com/v1/objects/0aa1605877280b88de1f1cc3e7e4bdbeed968a73/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/48x48": {"type": "directory"}, "lib/desktop/icons/LowContrast/48x48/apps": {"type": "directory"}, "lib/desktop/icons/LowContrast/48x48/apps/sun-java.png": {"downloads": {"raw": {"sha1": "1fcf4fd6da61873b5f21b39412da26509734b7cc", "size": 1507, "url": "https://launcher.mojang.com/v1/objects/1fcf4fd6da61873b5f21b39412da26509734b7cc/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/48x48/apps/sun-javaws.png": {"downloads": {"raw": {"sha1": "1fcf4fd6da61873b5f21b39412da26509734b7cc", "size": 1507, "url": "https://launcher.mojang.com/v1/objects/1fcf4fd6da61873b5f21b39412da26509734b7cc/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/48x48/apps/sun-jcontrol.png": {"downloads": {"raw": {"sha1": "1fcf4fd6da61873b5f21b39412da26509734b7cc", "size": 1507, "url": "https://launcher.mojang.com/v1/objects/1fcf4fd6da61873b5f21b39412da26509734b7cc/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/48x48/mimetypes": {"type": "directory"}, "lib/desktop/icons/LowContrast/48x48/mimetypes/gnome-mime-application-x-java-archive.png": {"downloads": {"raw": {"sha1": "e36636b1c04dc283c18adf669b892d54b15d3ee6", "size": 1948, "url": "https://launcher.mojang.com/v1/objects/e36636b1c04dc283c18adf669b892d54b15d3ee6/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/48x48/mimetypes/gnome-mime-application-x-java-jnlp-file.png": {"downloads": {"raw": {"sha1": "e36636b1c04dc283c18adf669b892d54b15d3ee6", "size": 1948, "url": "https://launcher.mojang.com/v1/objects/e36636b1c04dc283c18adf669b892d54b15d3ee6/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/LowContrast/48x48/mimetypes/gnome-mime-text-x-java.png": {"downloads": {"raw": {"sha1": "e36636b1c04dc283c18adf669b892d54b15d3ee6", "size": 1948, "url": "https://launcher.mojang.com/v1/objects/e36636b1c04dc283c18adf669b892d54b15d3ee6/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor": {"type": "directory"}, "lib/desktop/icons/hicolor/16x16": {"type": "directory"}, "lib/desktop/icons/hicolor/16x16/apps": {"type": "directory"}, "lib/desktop/icons/hicolor/16x16/apps/sun-java.png": {"downloads": {"raw": {"sha1": "e91d05bfe9b889bf8a227908b597cab4630da8f2", "size": 383, "url": "https://launcher.mojang.com/v1/objects/e91d05bfe9b889bf8a227908b597cab4630da8f2/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/16x16/apps/sun-javaws.png": {"downloads": {"raw": {"sha1": "e91d05bfe9b889bf8a227908b597cab4630da8f2", "size": 383, "url": "https://launcher.mojang.com/v1/objects/e91d05bfe9b889bf8a227908b597cab4630da8f2/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/16x16/apps/sun-jcontrol.png": {"downloads": {"raw": {"sha1": "e91d05bfe9b889bf8a227908b597cab4630da8f2", "size": 383, "url": "https://launcher.mojang.com/v1/objects/e91d05bfe9b889bf8a227908b597cab4630da8f2/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/16x16/mimetypes": {"type": "directory"}, "lib/desktop/icons/hicolor/16x16/mimetypes/gnome-mime-application-x-java-archive.png": {"downloads": {"raw": {"sha1": "d2f6abe8e498aeecb334fb43f63001d34dbf6ea5", "size": 783, "url": "https://launcher.mojang.com/v1/objects/d2f6abe8e498aeecb334fb43f63001d34dbf6ea5/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/16x16/mimetypes/gnome-mime-application-x-java-jnlp-file.png": {"downloads": {"raw": {"sha1": "d2f6abe8e498aeecb334fb43f63001d34dbf6ea5", "size": 783, "url": "https://launcher.mojang.com/v1/objects/d2f6abe8e498aeecb334fb43f63001d34dbf6ea5/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/16x16/mimetypes/gnome-mime-text-x-java.png": {"downloads": {"raw": {"sha1": "d2f6abe8e498aeecb334fb43f63001d34dbf6ea5", "size": 783, "url": "https://launcher.mojang.com/v1/objects/d2f6abe8e498aeecb334fb43f63001d34dbf6ea5/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/48x48": {"type": "directory"}, "lib/desktop/icons/hicolor/48x48/apps": {"type": "directory"}, "lib/desktop/icons/hicolor/48x48/apps/sun-java.png": {"downloads": {"raw": {"sha1": "6c90a38eaada9c32a678a282be18ec5b43a84264", "size": 1439, "url": "https://launcher.mojang.com/v1/objects/6c90a38eaada9c32a678a282be18ec5b43a84264/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/48x48/apps/sun-javaws.png": {"downloads": {"raw": {"sha1": "6c90a38eaada9c32a678a282be18ec5b43a84264", "size": 1439, "url": "https://launcher.mojang.com/v1/objects/6c90a38eaada9c32a678a282be18ec5b43a84264/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/48x48/apps/sun-jcontrol.png": {"downloads": {"raw": {"sha1": "6c90a38eaada9c32a678a282be18ec5b43a84264", "size": 1439, "url": "https://launcher.mojang.com/v1/objects/6c90a38eaada9c32a678a282be18ec5b43a84264/sun-javaws.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/48x48/mimetypes": {"type": "directory"}, "lib/desktop/icons/hicolor/48x48/mimetypes/gnome-mime-application-x-java-archive.png": {"downloads": {"raw": {"sha1": "4d5e6e0c41d1076bc86f3ab157c88a41a5716997", "size": 3202, "url": "https://launcher.mojang.com/v1/objects/4d5e6e0c41d1076bc86f3ab157c88a41a5716997/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/48x48/mimetypes/gnome-mime-application-x-java-jnlp-file.png": {"downloads": {"raw": {"sha1": "4d5e6e0c41d1076bc86f3ab157c88a41a5716997", "size": 3202, "url": "https://launcher.mojang.com/v1/objects/4d5e6e0c41d1076bc86f3ab157c88a41a5716997/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/icons/hicolor/48x48/mimetypes/gnome-mime-text-x-java.png": {"downloads": {"raw": {"sha1": "4d5e6e0c41d1076bc86f3ab157c88a41a5716997", "size": 3202, "url": "https://launcher.mojang.com/v1/objects/4d5e6e0c41d1076bc86f3ab157c88a41a5716997/gnome-mime-application-x-java-archive.png"}}, "executable": false, "type": "file"}, "lib/desktop/mime": {"type": "directory"}, "lib/desktop/mime/packages": {"type": "directory"}, "lib/desktop/mime/packages/x-java-archive.xml": {"downloads": {"lzma": {"sha1": "841230729f0a59de2a1071d155d96358232b2ba1", "size": 591, "url": "https://launcher.mojang.com/v1/objects/841230729f0a59de2a1071d155d96358232b2ba1/x-java-archive.xml"}, "raw": {"sha1": "b6297fd36efa799312961f95ebf0c85c920d5037", "size": 1822, "url": "https://launcher.mojang.com/v1/objects/b6297fd36efa799312961f95ebf0c85c920d5037/x-java-archive.xml"}}, "executable": false, "type": "file"}, "lib/desktop/mime/packages/x-java-jnlp-file.xml": {"downloads": {"lzma": {"sha1": "abf9acbe7c18027c4f036c4e42bb2cf1115525fa", "size": 302, "url": "https://launcher.mojang.com/v1/objects/abf9acbe7c18027c4f036c4e42bb2cf1115525fa/x-java-jnlp-file.xml"}, "raw": {"sha1": "72f03da83ddb76c9105f619fcfa4dbdad70e6b30", "size": 412, "url": "https://launcher.mojang.com/v1/objects/72f03da83ddb76c9105f619fcfa4dbdad70e6b30/x-java-jnlp-file.xml"}}, "executable": false, "type": "file"}, "lib/ext": {"type": "directory"}, "lib/ext/cldrdata.jar": {"downloads": {"raw": {"sha1": "6cacc961942d3f02a88907aa8f2eaae8e20c95a0", "size": 3860502, "url": "https://launcher.mojang.com/v1/objects/6cacc961942d3f02a88907aa8f2eaae8e20c95a0/cldrdata.jar"}}, "executable": false, "type": "file"}, "lib/ext/dnsns.jar": {"downloads": {"raw": {"sha1": "93bebdd7514e53ae31d60c5daba673878c8822ec", "size": 8286, "url": "https://launcher.mojang.com/v1/objects/93bebdd7514e53ae31d60c5daba673878c8822ec/dnsns.jar"}}, "executable": false, "type": "file"}, "lib/ext/jaccess.jar": {"downloads": {"raw": {"sha1": "2f54879df7c29ec67c40d40cfc95c0d4a968bea1", "size": 44516, "url": "https://launcher.mojang.com/v1/objects/2f54879df7c29ec67c40d40cfc95c0d4a968bea1/jaccess.jar"}}, "executable": false, "type": "file"}, "lib/ext/jfxrt.jar": {"downloads": {"lzma": {"sha1": "a6c5b6a782ba360ada6651f5322dcab88c75711c", "size": 3374270, "url": "https://launcher.mojang.com/v1/objects/a6c5b6a782ba360ada6651f5322dcab88c75711c/jfxrt.jar"}, "raw": {"sha1": "1ad7a876f045399c23ee4ab7dee380a04ca2ac08", "size": 18508094, "url": "https://launcher.mojang.com/v1/objects/1ad7a876f045399c23ee4ab7dee380a04ca2ac08/jfxrt.jar"}}, "executable": false, "type": "file"}, "lib/ext/localedata.jar": {"downloads": {"raw": {"sha1": "0cc9f550d4e410b5aa29dbfd2c1b5c99391c7f70", "size": 1178926, "url": "https://launcher.mojang.com/v1/objects/0cc9f550d4e410b5aa29dbfd2c1b5c99391c7f70/localedata.jar"}}, "executable": false, "type": "file"}, "lib/ext/meta-index": {"downloads": {"lzma": {"sha1": "1359457529f42bacf495afcb68149ae036442dd9", "size": 594, "url": "https://launcher.mojang.com/v1/objects/1359457529f42bacf495afcb68149ae036442dd9/meta-index"}, "raw": {"sha1": "334649c6e2d5d7248211f30855e97cfcb4558851", "size": 1269, "url": "https://launcher.mojang.com/v1/objects/334649c6e2d5d7248211f30855e97cfcb4558851/meta-index"}}, "executable": false, "type": "file"}, "lib/ext/nashorn.jar": {"downloads": {"raw": {"sha1": "dec5dd17a0f52ae79dfbfb38840bffb8b7a679a5", "size": 2023869, "url": "https://launcher.mojang.com/v1/objects/dec5dd17a0f52ae79dfbfb38840bffb8b7a679a5/nashorn.jar"}}, "executable": false, "type": "file"}, "lib/ext/sunec.jar": {"downloads": {"raw": {"sha1": "bf1c817820341a246f7130fe046e8310b03d04f6", "size": 41672, "url": "https://launcher.mojang.com/v1/objects/bf1c817820341a246f7130fe046e8310b03d04f6/sunec.jar"}}, "executable": false, "type": "file"}, "lib/ext/sunjce_provider.jar": {"downloads": {"raw": {"sha1": "bb3494e4b5cb3c3e60da767207731f18b267cb34", "size": 279326, "url": "https://launcher.mojang.com/v1/objects/bb3494e4b5cb3c3e60da767207731f18b267cb34/sunjce_provider.jar"}}, "executable": false, "type": "file"}, "lib/ext/sunpkcs11.jar": {"downloads": {"raw": {"sha1": "5bb1dedc3344cd3bb86828d4aa8ca82f4a606ed4", "size": 250218, "url": "https://launcher.mojang.com/v1/objects/5bb1dedc3344cd3bb86828d4aa8ca82f4a606ed4/sunpkcs11.jar"}}, "executable": false, "type": "file"}, "lib/ext/zipfs.jar": {"downloads": {"raw": {"sha1": "37b338f0e8e60d6396f51275130e8110816d7b30", "size": 68964, "url": "https://launcher.mojang.com/v1/objects/37b338f0e8e60d6396f51275130e8110816d7b30/zipfs.jar"}}, "executable": false, "type": "file"}, "lib/flavormap.properties": {"downloads": {"lzma": {"sha1": "2d5ef19ee77ccfc95c9413eea155cde59a48fadd", "size": 1541, "url": "https://launcher.mojang.com/v1/objects/2d5ef19ee77ccfc95c9413eea155cde59a48fadd/flavormap.properties"}, "raw": {"sha1": "4e66e8fe12d7f8b3b0c4e1a1915f329bb1fbf6d2", "size": 3901, "url": "https://launcher.mojang.com/v1/objects/4e66e8fe12d7f8b3b0c4e1a1915f329bb1fbf6d2/flavormap.properties"}}, "executable": false, "type": "file"}, "lib/fontconfig.RedHat.5.bfc": {"downloads": {"lzma": {"sha1": "5197f6e387f16458b7408134e38b06f20f625e4c", "size": 795, "url": "https://launcher.mojang.com/v1/objects/5197f6e387f16458b7408134e38b06f20f625e4c/fontconfig.RedHat.5.bfc"}, "raw": {"sha1": "fb806ada6e68f16a9fe2b726a39d9ef5a835c0c2", "size": 4532, "url": "https://launcher.mojang.com/v1/objects/fb806ada6e68f16a9fe2b726a39d9ef5a835c0c2/fontconfig.RedHat.5.bfc"}}, "executable": false, "type": "file"}, "lib/fontconfig.RedHat.5.properties.src": {"downloads": {"lzma": {"sha1": "3897ae198e96e5a687c9c9b218ff5df60c868e0d", "size": 1089, "url": "https://launcher.mojang.com/v1/objects/3897ae198e96e5a687c9c9b218ff5df60c868e0d/fontconfig.RedHat.5.properties.src"}, "raw": {"sha1": "c67d1a06cb37b66e69560c9f5e4be7cf08af0493", "size": 8841, "url": "https://launcher.mojang.com/v1/objects/c67d1a06cb37b66e69560c9f5e4be7cf08af0493/fontconfig.RedHat.5.properties.src"}}, "executable": false, "type": "file"}, "lib/fontconfig.RedHat.6.bfc": {"downloads": {"lzma": {"sha1": "ef2f5d1f8d620be9927db45d3a28bd75777245cb", "size": 818, "url": "https://launcher.mojang.com/v1/objects/ef2f5d1f8d620be9927db45d3a28bd75777245cb/fontconfig.RedHat.6.bfc"}, "raw": {"sha1": "9ba3b3e2c621c31d0ef1b2053c80f77419a19965", "size": 4250, "url": "https://launcher.mojang.com/v1/objects/9ba3b3e2c621c31d0ef1b2053c80f77419a19965/fontconfig.RedHat.6.bfc"}}, "executable": false, "type": "file"}, "lib/fontconfig.RedHat.6.properties.src": {"downloads": {"lzma": {"sha1": "74f4148f9d7ec3d67bbd724834d478a72cfdb0db", "size": 1111, "url": "https://launcher.mojang.com/v1/objects/74f4148f9d7ec3d67bbd724834d478a72cfdb0db/fontconfig.RedHat.6.properties.src"}, "raw": {"sha1": "768e58d4d314621c38daf9fde6d67119f370acd9", "size": 8735, "url": "https://launcher.mojang.com/v1/objects/768e58d4d314621c38daf9fde6d67119f370acd9/fontconfig.RedHat.6.properties.src"}}, "executable": false, "type": "file"}, "lib/fontconfig.SuSE.10.bfc": {"downloads": {"lzma": {"sha1": "d8fe9b1d8d02368dcd452de93024c6f60670eb87", "size": 1083, "url": "https://launcher.mojang.com/v1/objects/d8fe9b1d8d02368dcd452de93024c6f60670eb87/fontconfig.SuSE.10.bfc"}, "raw": {"sha1": "ffd0dfbd1553e15b11649a73a0b3f48318138e0d", "size": 6702, "url": "https://launcher.mojang.com/v1/objects/ffd0dfbd1553e15b11649a73a0b3f48318138e0d/fontconfig.SuSE.10.bfc"}}, "executable": false, "type": "file"}, "lib/fontconfig.SuSE.10.properties.src": {"downloads": {"lzma": {"sha1": "2c382bd741a9e23114be3da82dee8290ebfca8a9", "size": 1555, "url": "https://launcher.mojang.com/v1/objects/2c382bd741a9e23114be3da82dee8290ebfca8a9/fontconfig.SuSE.10.properties.src"}, "raw": {"sha1": "a38dbdbbc514567b8281e1aea726865f37e97894", "size": 16772, "url": "https://launcher.mojang.com/v1/objects/a38dbdbbc514567b8281e1aea726865f37e97894/fontconfig.SuSE.10.properties.src"}}, "executable": false, "type": "file"}, "lib/fontconfig.SuSE.11.bfc": {"downloads": {"lzma": {"sha1": "2b78cbf11289c9858951fea7180696ba3b7176d6", "size": 1092, "url": "https://launcher.mojang.com/v1/objects/2b78cbf11289c9858951fea7180696ba3b7176d6/fontconfig.SuSE.11.bfc"}, "raw": {"sha1": "a4d8500fcb47f6327460a95851b1368660da8302", "size": 7032, "url": "https://launcher.mojang.com/v1/objects/a4d8500fcb47f6327460a95851b1368660da8302/fontconfig.SuSE.11.bfc"}}, "executable": false, "type": "file"}, "lib/fontconfig.SuSE.11.properties.src": {"downloads": {"lzma": {"sha1": "5c1635803906e2c59d36492dec724dd7ae49a5ab", "size": 1589, "url": "https://launcher.mojang.com/v1/objects/5c1635803906e2c59d36492dec724dd7ae49a5ab/fontconfig.SuSE.11.properties.src"}, "raw": {"sha1": "c4b69589e41a7279a71866a9134213be19cdf88d", "size": 16781, "url": "https://launcher.mojang.com/v1/objects/c4b69589e41a7279a71866a9134213be19cdf88d/fontconfig.SuSE.11.properties.src"}}, "executable": false, "type": "file"}, "lib/fontconfig.Turbo.bfc": {"downloads": {"lzma": {"sha1": "1c771325d9ee4af209a3db92294451d58962c7a4", "size": 822, "url": "https://launcher.mojang.com/v1/objects/1c771325d9ee4af209a3db92294451d58962c7a4/fontconfig.Turbo.bfc"}, "raw": {"sha1": "f24368deeb85cc9d0781083bc56e773518d72219", "size": 4668, "url": "https://launcher.mojang.com/v1/objects/f24368deeb85cc9d0781083bc56e773518d72219/fontconfig.Turbo.bfc"}}, "executable": false, "type": "file"}, "lib/fontconfig.Turbo.properties.src": {"downloads": {"lzma": {"sha1": "7748ffa17e2c8a34754138efa963ba39bd1cbbb3", "size": 1113, "url": "https://launcher.mojang.com/v1/objects/7748ffa17e2c8a34754138efa963ba39bd1cbbb3/fontconfig.Turbo.properties.src"}, "raw": {"sha1": "2bb7258bed7ccd4f117e4e5f892c9b13424b0c82", "size": 9192, "url": "https://launcher.mojang.com/v1/objects/2bb7258bed7ccd4f117e4e5f892c9b13424b0c82/fontconfig.Turbo.properties.src"}}, "executable": false, "type": "file"}, "lib/fontconfig.bfc": {"downloads": {"lzma": {"sha1": "be6d49ee8c64f458c4f0e64254963fec48d25150", "size": 286, "url": "https://launcher.mojang.com/v1/objects/be6d49ee8c64f458c4f0e64254963fec48d25150/fontconfig.bfc"}, "raw": {"sha1": "de39b0e19637f58d92a0188122514aa7247ebb5b", "size": 1678, "url": "https://launcher.mojang.com/v1/objects/de39b0e19637f58d92a0188122514aa7247ebb5b/fontconfig.bfc"}}, "executable": false, "type": "file"}, "lib/fontconfig.properties.src": {"downloads": {"lzma": {"sha1": "9498d5e00e5401200667687e826e28c60fa60ba4", "size": 417, "url": "https://launcher.mojang.com/v1/objects/9498d5e00e5401200667687e826e28c60fa60ba4/fontconfig.properties.src"}, "raw": {"sha1": "3617ff1424fd204415242565541facf862b16eb4", "size": 1938, "url": "https://launcher.mojang.com/v1/objects/3617ff1424fd204415242565541facf862b16eb4/fontconfig.properties.src"}}, "executable": false, "type": "file"}, "lib/fonts": {"type": "directory"}, "lib/fonts/LucidaBrightDemiBold.ttf": {"downloads": {"lzma": {"sha1": "4f748750831a7719440dff5457f4d207d0f24d21", "size": 42347, "url": "https://launcher.mojang.com/v1/objects/4f748750831a7719440dff5457f4d207d0f24d21/LucidaBrightDemiBold.ttf"}, "raw": {"sha1": "b5c97f985639e19a3b712193ee48b55dda581fd1", "size": 75144, "url": "https://launcher.mojang.com/v1/objects/b5c97f985639e19a3b712193ee48b55dda581fd1/LucidaBrightDemiBold.ttf"}}, "executable": false, "type": "file"}, "lib/fonts/LucidaBrightDemiItalic.ttf": {"downloads": {"lzma": {"sha1": "f82e9a688553c100ecb98412b985807ed56dff5d", "size": 43119, "url": "https://launcher.mojang.com/v1/objects/f82e9a688553c100ecb98412b985807ed56dff5d/LucidaBrightDemiItalic.ttf"}, "raw": {"sha1": "1fd1f757febf3e5f5fbb7fbf7a56587a40d57de7", "size": 75124, "url": "https://launcher.mojang.com/v1/objects/1fd1f757febf3e5f5fbb7fbf7a56587a40d57de7/LucidaBrightDemiItalic.ttf"}}, "executable": false, "type": "file"}, "lib/fonts/LucidaBrightItalic.ttf": {"downloads": {"lzma": {"sha1": "6d630df719271319c3d53f90a3d425118b908266", "size": 46206, "url": "https://launcher.mojang.com/v1/objects/6d630df719271319c3d53f90a3d425118b908266/LucidaBrightItalic.ttf"}, "raw": {"sha1": "aa5c037865c563726ecd63d61ca26443589be425", "size": 80856, "url": "https://launcher.mojang.com/v1/objects/aa5c037865c563726ecd63d61ca26443589be425/LucidaBrightItalic.ttf"}}, "executable": false, "type": "file"}, "lib/fonts/LucidaBrightRegular.ttf": {"downloads": {"lzma": {"sha1": "4b2e31aaec2238b6ecf9f845bad0a1c6d09fbbfe", "size": 181085, "url": "https://launcher.mojang.com/v1/objects/4b2e31aaec2238b6ecf9f845bad0a1c6d09fbbfe/LucidaBrightRegular.ttf"}, "raw": {"sha1": "5d7ed564791c900a8786936930ba99385653139c", "size": 344908, "url": "https://launcher.mojang.com/v1/objects/5d7ed564791c900a8786936930ba99385653139c/LucidaBrightRegular.ttf"}}, "executable": false, "type": "file"}, "lib/fonts/LucidaSansDemiBold.ttf": {"downloads": {"lzma": {"sha1": "079b16dc3c4918ab1f4f760b6dc5e6586c219042", "size": 173229, "url": "https://launcher.mojang.com/v1/objects/079b16dc3c4918ab1f4f760b6dc5e6586c219042/LucidaSansDemiBold.ttf"}, "raw": {"sha1": "92b79fefc35e96190250c602a8fed85276b32a95", "size": 317896, "url": "https://launcher.mojang.com/v1/objects/92b79fefc35e96190250c602a8fed85276b32a95/LucidaSansDemiBold.ttf"}}, "executable": false, "type": "file"}, "lib/fonts/LucidaSansRegular.ttf": {"downloads": {"lzma": {"sha1": "64a65d7b94d7153d20957ef6d06bebb4dd0f48e4", "size": 326062, "url": "https://launcher.mojang.com/v1/objects/64a65d7b94d7153d20957ef6d06bebb4dd0f48e4/LucidaSansRegular.ttf"}, "raw": {"sha1": "39cc8bcb8d4a71d4657fc92ef0b9f4e3e9e67add", "size": 698236, "url": "https://launcher.mojang.com/v1/objects/39cc8bcb8d4a71d4657fc92ef0b9f4e3e9e67add/LucidaSansRegular.ttf"}}, "executable": false, "type": "file"}, "lib/fonts/LucidaTypewriterBold.ttf": {"downloads": {"lzma": {"sha1": "cdb017f7c34bea0802bc5ea5583aef721ed99c49", "size": 130412, "url": "https://launcher.mojang.com/v1/objects/cdb017f7c34bea0802bc5ea5583aef721ed99c49/LucidaTypewriterBold.ttf"}, "raw": {"sha1": "a5da2eb49448f461470387c939f0e69119310e0b", "size": 234068, "url": "https://launcher.mojang.com/v1/objects/a5da2eb49448f461470387c939f0e69119310e0b/LucidaTypewriterBold.ttf"}}, "executable": false, "type": "file"}, "lib/fonts/LucidaTypewriterRegular.ttf": {"downloads": {"lzma": {"sha1": "aeda4a09a53783b0dc97de8e20071bea874cbfe5", "size": 135184, "url": "https://launcher.mojang.com/v1/objects/aeda4a09a53783b0dc97de8e20071bea874cbfe5/LucidaTypewriterRegular.ttf"}, "raw": {"sha1": "c144dcafe4faf2e79cfd74d8134a631f30234db1", "size": 242700, "url": "https://launcher.mojang.com/v1/objects/c144dcafe4faf2e79cfd74d8134a631f30234db1/LucidaTypewriterRegular.ttf"}}, "executable": false, "type": "file"}, "lib/fonts/fonts.dir": {"downloads": {"lzma": {"sha1": "68f2dd93b215ec8b8d9409d2b9c825632c6b907d", "size": 273, "url": "https://launcher.mojang.com/v1/objects/68f2dd93b215ec8b8d9409d2b9c825632c6b907d/fonts.dir"}, "raw": {"sha1": "97f40cca185c954adf5cc582345a7cb8e4c50578", "size": 4041, "url": "https://launcher.mojang.com/v1/objects/97f40cca185c954adf5cc582345a7cb8e4c50578/fonts.dir"}}, "executable": false, "type": "file"}, "lib/hijrah-config-umalqura.properties": {"downloads": {"lzma": {"sha1": "02e8d296e3b18a450f1ed1547cbf2b7275664c9a", "size": 1969, "url": "https://launcher.mojang.com/v1/objects/02e8d296e3b18a450f1ed1547cbf2b7275664c9a/hijrah-config-umalqura.properties"}, "raw": {"sha1": "84aa425100740722e91f4725caf849e7863d12ba", "size": 13962, "url": "https://launcher.mojang.com/v1/objects/84aa425100740722e91f4725caf849e7863d12ba/hijrah-config-umalqura.properties"}}, "executable": false, "type": "file"}, "lib/images": {"type": "directory"}, "lib/images/cursors": {"type": "directory"}, "lib/images/cursors/cursors.properties": {"downloads": {"lzma": {"sha1": "612bd0f610ee1023947c4a2a8d3fc7d6f97e7d8f", "size": 385, "url": "https://launcher.mojang.com/v1/objects/612bd0f610ee1023947c4a2a8d3fc7d6f97e7d8f/cursors.properties"}, "raw": {"sha1": "f2b9a22ddd0a77869497a64f28f07e89a7d41f48", "size": 1274, "url": "https://launcher.mojang.com/v1/objects/f2b9a22ddd0a77869497a64f28f07e89a7d41f48/cursors.properties"}}, "executable": false, "type": "file"}, "lib/images/cursors/invalid32x32.gif": {"downloads": {"raw": {"sha1": "259edc45b4569427e8319895a444f4295d54348f", "size": 153, "url": "https://launcher.mojang.com/v1/objects/259edc45b4569427e8319895a444f4295d54348f/invalid32x32.gif"}}, "executable": false, "type": "file"}, "lib/images/cursors/motif_CopyDrop32x32.gif": {"downloads": {"raw": {"sha1": "eb7620fae702172aa663a19d170a0b929d3b11d1", "size": 158, "url": "https://launcher.mojang.com/v1/objects/eb7620fae702172aa663a19d170a0b929d3b11d1/motif_CopyDrop32x32.gif"}}, "executable": false, "type": "file"}, "lib/images/cursors/motif_CopyNoDrop32x32.gif": {"downloads": {"raw": {"sha1": "259edc45b4569427e8319895a444f4295d54348f", "size": 153, "url": "https://launcher.mojang.com/v1/objects/259edc45b4569427e8319895a444f4295d54348f/invalid32x32.gif"}}, "executable": false, "type": "file"}, "lib/images/cursors/motif_LinkDrop32x32.gif": {"downloads": {"raw": {"sha1": "9699137f990c240e714481563181069c8f6c17bb", "size": 162, "url": "https://launcher.mojang.com/v1/objects/9699137f990c240e714481563181069c8f6c17bb/motif_LinkDrop32x32.gif"}}, "executable": false, "type": "file"}, "lib/images/cursors/motif_LinkNoDrop32x32.gif": {"downloads": {"raw": {"sha1": "259edc45b4569427e8319895a444f4295d54348f", "size": 153, "url": "https://launcher.mojang.com/v1/objects/259edc45b4569427e8319895a444f4295d54348f/invalid32x32.gif"}}, "executable": false, "type": "file"}, "lib/images/cursors/motif_MoveDrop32x32.gif": {"downloads": {"raw": {"sha1": "03c1617ce3c5ab8af03e46d30a8c8f31ab57fb1b", "size": 141, "url": "https://launcher.mojang.com/v1/objects/03c1617ce3c5ab8af03e46d30a8c8f31ab57fb1b/motif_MoveDrop32x32.gif"}}, "executable": false, "type": "file"}, "lib/images/cursors/motif_MoveNoDrop32x32.gif": {"downloads": {"raw": {"sha1": "259edc45b4569427e8319895a444f4295d54348f", "size": 153, "url": "https://launcher.mojang.com/v1/objects/259edc45b4569427e8319895a444f4295d54348f/invalid32x32.gif"}}, "executable": false, "type": "file"}, "lib/images/icons": {"type": "directory"}, "lib/images/icons/sun-java.png": {"downloads": {"raw": {"sha1": "d101b693aa054f51097eebdfeed8b8a6ca7b55b8", "size": 4707, "url": "https://launcher.mojang.com/v1/objects/d101b693aa054f51097eebdfeed8b8a6ca7b55b8/sun-java.png"}}, "executable": false, "type": "file"}, "lib/images/icons/sun-java_HighContrast.png": {"downloads": {"raw": {"sha1": "a6b1e418d6b5d03719b96f61f0c5236a60970151", "size": 3729, "url": "https://launcher.mojang.com/v1/objects/a6b1e418d6b5d03719b96f61f0c5236a60970151/sun-java_HighContrast.png"}}, "executable": false, "type": "file"}, "lib/images/icons/sun-java_HighContrastInverse.png": {"downloads": {"raw": {"sha1": "2dda28b9bddc9b5b018e3e8a8b062a99d9b2f887", "size": 3777, "url": "https://launcher.mojang.com/v1/objects/2dda28b9bddc9b5b018e3e8a8b062a99d9b2f887/sun-java_HighContrastInverse.png"}}, "executable": false, "type": "file"}, "lib/images/icons/sun-java_LowContrast.png": {"downloads": {"raw": {"sha1": "7714cc4e894c3626c8da6fe742ed22b2829122d9", "size": 4012, "url": "https://launcher.mojang.com/v1/objects/7714cc4e894c3626c8da6fe742ed22b2829122d9/sun-java_LowContrast.png"}}, "executable": false, "type": "file"}, "lib/javafx.properties": {"downloads": {"raw": {"sha1": "49e6b75d109e5fd3f6cbe7cc5fa9a7980796d14d", "size": 56, "url": "https://launcher.mojang.com/v1/objects/49e6b75d109e5fd3f6cbe7cc5fa9a7980796d14d/javafx.properties"}}, "executable": false, "type": "file"}, "lib/javaws.jar": {"downloads": {"raw": {"sha1": "04fa5ae04ead65b91be5dee575497e49ffd49fe9", "size": 488118, "url": "https://launcher.mojang.com/v1/objects/04fa5ae04ead65b91be5dee575497e49ffd49fe9/javaws.jar"}}, "executable": false, "type": "file"}, "lib/jce.jar": {"downloads": {"raw": {"sha1": "5460adee09cc5fc8829c0acfc46c34670a7d70a0", "size": 115646, "url": "https://launcher.mojang.com/v1/objects/5460adee09cc5fc8829c0acfc46c34670a7d70a0/jce.jar"}}, "executable": false, "type": "file"}, "lib/jexec": {"downloads": {"lzma": {"sha1": "2d4323d4e060f8126d026ca6c03b8972aedd2fab", "size": 3311, "url": "https://launcher.mojang.com/v1/objects/2d4323d4e060f8126d026ca6c03b8972aedd2fab/jexec"}, "raw": {"sha1": "6aa01f1d8d103974164bcfaea03c04eeeefd7d41", "size": 13376, "url": "https://launcher.mojang.com/v1/objects/6aa01f1d8d103974164bcfaea03c04eeeefd7d41/jexec"}}, "executable": true, "type": "file"}, "lib/jfr": {"type": "directory"}, "lib/jfr.jar": {"downloads": {"lzma": {"sha1": "5b9d615c91c72f4fe356d9b4105946679452d1e1", "size": 137982, "url": "https://launcher.mojang.com/v1/objects/5b9d615c91c72f4fe356d9b4105946679452d1e1/jfr.jar"}, "raw": {"sha1": "0f3fd66a336703d935bdc22ad8082bc51d34e534", "size": 560713, "url": "https://launcher.mojang.com/v1/objects/0f3fd66a336703d935bdc22ad8082bc51d34e534/jfr.jar"}}, "executable": false, "type": "file"}, "lib/jfr/default.jfc": {"downloads": {"lzma": {"sha1": "373ddd878146dd8ce8991c2c5115a05a82859bdb", "size": 2207, "url": "https://launcher.mojang.com/v1/objects/373ddd878146dd8ce8991c2c5115a05a82859bdb/default.jfc"}, "raw": {"sha1": "1a64b68d0e7d43f8149faba94440be54f4f24527", "size": 20109, "url": "https://launcher.mojang.com/v1/objects/1a64b68d0e7d43f8149faba94440be54f4f24527/default.jfc"}}, "executable": false, "type": "file"}, "lib/jfr/profile.jfc": {"downloads": {"lzma": {"sha1": "3dcdc5feee3ccfb66bc8726b666944cd4bdadae3", "size": 2199, "url": "https://launcher.mojang.com/v1/objects/3dcdc5feee3ccfb66bc8726b666944cd4bdadae3/profile.jfc"}, "raw": {"sha1": "5d7d08a595f76322c51ae43ea966fbba6b69eebe", "size": 20065, "url": "https://launcher.mojang.com/v1/objects/5d7d08a595f76322c51ae43ea966fbba6b69eebe/profile.jfc"}}, "executable": false, "type": "file"}, "lib/jfxswt.jar": {"downloads": {"raw": {"sha1": "99d9a264c898d84c01e1c42565e7fe1a89dcd72d", "size": 33932, "url": "https://launcher.mojang.com/v1/objects/99d9a264c898d84c01e1c42565e7fe1a89dcd72d/jfxswt.jar"}}, "executable": false, "type": "file"}, "lib/jsse.jar": {"downloads": {"lzma": {"sha1": "94a17dfbc2e76cd12c33970a15341424f875a9ce", "size": 187549, "url": "https://launcher.mojang.com/v1/objects/94a17dfbc2e76cd12c33970a15341424f875a9ce/jsse.jar"}, "raw": {"sha1": "92c5c626e8a2d16f41272c0e404d4f992dd8310a", "size": 675599, "url": "https://launcher.mojang.com/v1/objects/92c5c626e8a2d16f41272c0e404d4f992dd8310a/jsse.jar"}}, "executable": false, "type": "file"}, "lib/jvm.hprof.txt": {"downloads": {"lzma": {"sha1": "eccdb240a815b2a83a502749339b27bb8669965b", "size": 1863, "url": "https://launcher.mojang.com/v1/objects/eccdb240a815b2a83a502749339b27bb8669965b/jvm.hprof.txt"}, "raw": {"sha1": "fbd61d52534cdd0c15df332114d469c65d001e33", "size": 4226, "url": "https://launcher.mojang.com/v1/objects/fbd61d52534cdd0c15df332114d469c65d001e33/jvm.hprof.txt"}}, "executable": false, "type": "file"}, "lib/locale": {"type": "directory"}, "lib/locale/de": {"type": "directory"}, "lib/locale/de/LC_MESSAGES": {"type": "directory"}, "lib/locale/de/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "3061d922907cc557208109088fc6ab81d577ff6f", "size": 970, "url": "https://launcher.mojang.com/v1/objects/3061d922907cc557208109088fc6ab81d577ff6f/sunw_java_plugin.mo"}, "raw": {"sha1": "5b223a3d723ac1cfce63623fb109f2868d47d1b7", "size": 2483, "url": "https://launcher.mojang.com/v1/objects/5b223a3d723ac1cfce63623fb109f2868d47d1b7/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/es": {"type": "directory"}, "lib/locale/es/LC_MESSAGES": {"type": "directory"}, "lib/locale/es/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "24338049a89b323e17182b3a3006b50565d4fa0f", "size": 979, "url": "https://launcher.mojang.com/v1/objects/24338049a89b323e17182b3a3006b50565d4fa0f/sunw_java_plugin.mo"}, "raw": {"sha1": "6cc63dc97f2fdb2ed799e48b1dc98c4f37cdecc1", "size": 2477, "url": "https://launcher.mojang.com/v1/objects/6cc63dc97f2fdb2ed799e48b1dc98c4f37cdecc1/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/fr": {"type": "directory"}, "lib/locale/fr/LC_MESSAGES": {"type": "directory"}, "lib/locale/fr/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "22796a48ef39f57d2d6fa70f41308e493d7f05c1", "size": 1033, "url": "https://launcher.mojang.com/v1/objects/22796a48ef39f57d2d6fa70f41308e493d7f05c1/sunw_java_plugin.mo"}, "raw": {"sha1": "d9d5b458db6e83fdf85c3526aeee3f57c4929840", "size": 2746, "url": "https://launcher.mojang.com/v1/objects/d9d5b458db6e83fdf85c3526aeee3f57c4929840/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/it": {"type": "directory"}, "lib/locale/it/LC_MESSAGES": {"type": "directory"}, "lib/locale/it/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "59a4cae38bfb8927745674d0efc2f284bc277987", "size": 958, "url": "https://launcher.mojang.com/v1/objects/59a4cae38bfb8927745674d0efc2f284bc277987/sunw_java_plugin.mo"}, "raw": {"sha1": "f6e72e3b2141ccc3dffab10ae14a754e494577ba", "size": 2434, "url": "https://launcher.mojang.com/v1/objects/f6e72e3b2141ccc3dffab10ae14a754e494577ba/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/ja": {"type": "directory"}, "lib/locale/ja/LC_MESSAGES": {"type": "directory"}, "lib/locale/ja/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "7d6aeed563e1cefcf0224cf522048468088884a9", "size": 1036, "url": "https://launcher.mojang.com/v1/objects/7d6aeed563e1cefcf0224cf522048468088884a9/sunw_java_plugin.mo"}, "raw": {"sha1": "378881a8cb8dd2aebb43eacd0c68519be4f258b1", "size": 2415, "url": "https://launcher.mojang.com/v1/objects/378881a8cb8dd2aebb43eacd0c68519be4f258b1/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/ko": {"type": "directory"}, "lib/locale/ko.UTF-8": {"type": "directory"}, "lib/locale/ko.UTF-8/LC_MESSAGES": {"type": "directory"}, "lib/locale/ko.UTF-8/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "12ee3b21511e8497d95ea0ba9d6fe519227d0b16", "size": 1069, "url": "https://launcher.mojang.com/v1/objects/12ee3b21511e8497d95ea0ba9d6fe519227d0b16/sunw_java_plugin.mo"}, "raw": {"sha1": "cb19df01c59662dbe2f4050b1290d374b82fe1fa", "size": 2753, "url": "https://launcher.mojang.com/v1/objects/cb19df01c59662dbe2f4050b1290d374b82fe1fa/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/ko/LC_MESSAGES": {"type": "directory"}, "lib/locale/ko/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "6e2e47c64c360517fd436bc79c823b5679a1efe6", "size": 996, "url": "https://launcher.mojang.com/v1/objects/6e2e47c64c360517fd436bc79c823b5679a1efe6/sunw_java_plugin.mo"}, "raw": {"sha1": "12c8a118d150c78f719314df6dec49a967af71e9", "size": 2399, "url": "https://launcher.mojang.com/v1/objects/12c8a118d150c78f719314df6dec49a967af71e9/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/pt_BR": {"type": "directory"}, "lib/locale/pt_BR/LC_MESSAGES": {"type": "directory"}, "lib/locale/pt_BR/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "bcaa7e7916493f071f1bf64bf58c6b038e3569c9", "size": 940, "url": "https://launcher.mojang.com/v1/objects/bcaa7e7916493f071f1bf64bf58c6b038e3569c9/sunw_java_plugin.mo"}, "raw": {"sha1": "a3bc0c43994c53c59bba94982cf95f6d36283dd0", "size": 2420, "url": "https://launcher.mojang.com/v1/objects/a3bc0c43994c53c59bba94982cf95f6d36283dd0/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/sv": {"type": "directory"}, "lib/locale/sv/LC_MESSAGES": {"type": "directory"}, "lib/locale/sv/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "76017835d6261fe2eedbcbe5eb08a7484c3080c5", "size": 946, "url": "https://launcher.mojang.com/v1/objects/76017835d6261fe2eedbcbe5eb08a7484c3080c5/sunw_java_plugin.mo"}, "raw": {"sha1": "09a47686edec4bbb34e82fbd08559f8bb6266544", "size": 2359, "url": "https://launcher.mojang.com/v1/objects/09a47686edec4bbb34e82fbd08559f8bb6266544/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/zh": {"type": "directory"}, "lib/locale/zh.GBK": {"type": "directory"}, "lib/locale/zh.GBK/LC_MESSAGES": {"type": "directory"}, "lib/locale/zh.GBK/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "75fd04045bf5890b8bb822770bfdb90a2e9ea65b", "size": 902, "url": "https://launcher.mojang.com/v1/objects/75fd04045bf5890b8bb822770bfdb90a2e9ea65b/sunw_java_plugin.mo"}, "raw": {"sha1": "7006fe7767b8807441a1f359a90509b3e507b0d1", "size": 2002, "url": "https://launcher.mojang.com/v1/objects/7006fe7767b8807441a1f359a90509b3e507b0d1/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/zh/LC_MESSAGES": {"type": "directory"}, "lib/locale/zh/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "75fd04045bf5890b8bb822770bfdb90a2e9ea65b", "size": 902, "url": "https://launcher.mojang.com/v1/objects/75fd04045bf5890b8bb822770bfdb90a2e9ea65b/sunw_java_plugin.mo"}, "raw": {"sha1": "7006fe7767b8807441a1f359a90509b3e507b0d1", "size": 2002, "url": "https://launcher.mojang.com/v1/objects/7006fe7767b8807441a1f359a90509b3e507b0d1/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/zh_HK.BIG5HK": {"type": "directory"}, "lib/locale/zh_HK.BIG5HK/LC_MESSAGES": {"type": "directory"}, "lib/locale/zh_HK.BIG5HK/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "3a1397bb1b1741697be1479232b6d9599940c851", "size": 912, "url": "https://launcher.mojang.com/v1/objects/3a1397bb1b1741697be1479232b6d9599940c851/sunw_java_plugin.mo"}, "raw": {"sha1": "c6023544067278c78599921f1032de353ff7da42", "size": 2025, "url": "https://launcher.mojang.com/v1/objects/c6023544067278c78599921f1032de353ff7da42/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/zh_TW": {"type": "directory"}, "lib/locale/zh_TW.BIG5": {"type": "directory"}, "lib/locale/zh_TW.BIG5/LC_MESSAGES": {"type": "directory"}, "lib/locale/zh_TW.BIG5/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "3a1397bb1b1741697be1479232b6d9599940c851", "size": 912, "url": "https://launcher.mojang.com/v1/objects/3a1397bb1b1741697be1479232b6d9599940c851/sunw_java_plugin.mo"}, "raw": {"sha1": "c6023544067278c78599921f1032de353ff7da42", "size": 2025, "url": "https://launcher.mojang.com/v1/objects/c6023544067278c78599921f1032de353ff7da42/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/locale/zh_TW/LC_MESSAGES": {"type": "directory"}, "lib/locale/zh_TW/LC_MESSAGES/sunw_java_plugin.mo": {"downloads": {"lzma": {"sha1": "c05e610e75182f0c4e77f3e7a4d9670ed62bf63c", "size": 897, "url": "https://launcher.mojang.com/v1/objects/c05e610e75182f0c4e77f3e7a4d9670ed62bf63c/sunw_java_plugin.mo"}, "raw": {"sha1": "f9b972dd059eae3cd337dfcef6a178e8ed8a7db6", "size": 2025, "url": "https://launcher.mojang.com/v1/objects/f9b972dd059eae3cd337dfcef6a178e8ed8a7db6/sunw_java_plugin.mo"}}, "executable": false, "type": "file"}, "lib/logging.properties": {"downloads": {"lzma": {"sha1": "642202a58e5216d086ad37c0b5a633be802edc78", "size": 896, "url": "https://launcher.mojang.com/v1/objects/642202a58e5216d086ad37c0b5a633be802edc78/logging.properties"}, "raw": {"sha1": "89da8094484891f9ec1fa40c6c8b61f94c5869d0", "size": 2455, "url": "https://launcher.mojang.com/v1/objects/89da8094484891f9ec1fa40c6c8b61f94c5869d0/logging.properties"}}, "executable": false, "type": "file"}, "lib/management": {"type": "directory"}, "lib/management-agent.jar": {"downloads": {"lzma": {"sha1": "3ea0bf17e14b3428296a0f4011bf4025fcbfa4bd", "size": 243, "url": "https://launcher.mojang.com/v1/objects/3ea0bf17e14b3428296a0f4011bf4025fcbfa4bd/management-agent.jar"}, "raw": {"sha1": "9fbed36522aa3a80bac08a328942cbc5ef39ca8e", "size": 381, "url": "https://launcher.mojang.com/v1/objects/9fbed36522aa3a80bac08a328942cbc5ef39ca8e/management-agent.jar"}}, "executable": false, "type": "file"}, "lib/management/jmxremote.access": {"downloads": {"lzma": {"sha1": "69042ff1b14165db19c9d728614639dec16d6a31", "size": 1419, "url": "https://launcher.mojang.com/v1/objects/69042ff1b14165db19c9d728614639dec16d6a31/jmxremote.access"}, "raw": {"sha1": "21200eaad898ba4a2a8834a032efb6616fabb930", "size": 3998, "url": "https://launcher.mojang.com/v1/objects/21200eaad898ba4a2a8834a032efb6616fabb930/jmxremote.access"}}, "executable": false, "type": "file"}, "lib/management/jmxremote.password.template": {"downloads": {"lzma": {"sha1": "556c64b1e920766f8867be3964de6e49f5b81a60", "size": 1129, "url": "https://launcher.mojang.com/v1/objects/556c64b1e920766f8867be3964de6e49f5b81a60/jmxremote.password.template"}, "raw": {"sha1": "c1e0f01408bf20fbbb8b4810520c725f70050db5", "size": 2856, "url": "https://launcher.mojang.com/v1/objects/c1e0f01408bf20fbbb8b4810520c725f70050db5/jmxremote.password.template"}}, "executable": false, "type": "file"}, "lib/management/management.properties": {"downloads": {"lzma": {"sha1": "3e52f9baa6394ca6956845424c607e5cde5d3c67", "size": 3176, "url": "https://launcher.mojang.com/v1/objects/3e52f9baa6394ca6956845424c607e5cde5d3c67/management.properties"}, "raw": {"sha1": "e0451d8d7d9e84d7b1c39ec7d00993307a5cbbf1", "size": 14630, "url": "https://launcher.mojang.com/v1/objects/e0451d8d7d9e84d7b1c39ec7d00993307a5cbbf1/management.properties"}}, "executable": false, "type": "file"}, "lib/management/snmp.acl.template": {"downloads": {"lzma": {"sha1": "9a4aa6396c3b488b0663bed5e5ecb762985669c9", "size": 1121, "url": "https://launcher.mojang.com/v1/objects/9a4aa6396c3b488b0663bed5e5ecb762985669c9/snmp.acl.template"}, "raw": {"sha1": "2e9f9ac287274532eb1f0d1afcefd7f3e97cc794", "size": 3376, "url": "https://launcher.mojang.com/v1/objects/2e9f9ac287274532eb1f0d1afcefd7f3e97cc794/snmp.acl.template"}}, "executable": false, "type": "file"}, "lib/meta-index": {"downloads": {"lzma": {"sha1": "1ac60b31362fda4725c665b591c5fbe384cbc8c1", "size": 788, "url": "https://launcher.mojang.com/v1/objects/1ac60b31362fda4725c665b591c5fbe384cbc8c1/meta-index"}, "raw": {"sha1": "bf204f09242203e713c31785158a0792f9edb600", "size": 2034, "url": "https://launcher.mojang.com/v1/objects/bf204f09242203e713c31785158a0792f9edb600/meta-index"}}, "executable": false, "type": "file"}, "lib/net.properties": {"downloads": {"lzma": {"sha1": "e9ec3981a0797bf55bb87b24d9eb651ce7e6916b", "size": 1830, "url": "https://launcher.mojang.com/v1/objects/e9ec3981a0797bf55bb87b24d9eb651ce7e6916b/net.properties"}, "raw": {"sha1": "fd9471742eb759f4478bb1de9a0dc0527265b6ea", "size": 5352, "url": "https://launcher.mojang.com/v1/objects/fd9471742eb759f4478bb1de9a0dc0527265b6ea/net.properties"}}, "executable": false, "type": "file"}, "lib/oblique-fonts": {"type": "directory"}, "lib/oblique-fonts/LucidaSansDemiOblique.ttf": {"downloads": {"lzma": {"sha1": "49c8980c1b89bbdbab59d0f5bd5bebf0afcb93b2", "size": 38580, "url": "https://launcher.mojang.com/v1/objects/49c8980c1b89bbdbab59d0f5bd5bebf0afcb93b2/LucidaSansDemiOblique.ttf"}, "raw": {"sha1": "53e4e12a675ac222469341c3dbc102464a1be4c7", "size": 91352, "url": "https://launcher.mojang.com/v1/objects/53e4e12a675ac222469341c3dbc102464a1be4c7/LucidaSansDemiOblique.ttf"}}, "executable": false, "type": "file"}, "lib/oblique-fonts/LucidaSansOblique.ttf": {"downloads": {"lzma": {"sha1": "553123c0edcd08035dede4ffd92b5b81c9a7538a", "size": 116575, "url": "https://launcher.mojang.com/v1/objects/553123c0edcd08035dede4ffd92b5b81c9a7538a/LucidaSansOblique.ttf"}, "raw": {"sha1": "95a195ad4fc520b3e395c85b747fc3024d118dd9", "size": 253724, "url": "https://launcher.mojang.com/v1/objects/95a195ad4fc520b3e395c85b747fc3024d118dd9/LucidaSansOblique.ttf"}}, "executable": false, "type": "file"}, "lib/oblique-fonts/LucidaTypewriterBoldOblique.ttf": {"downloads": {"lzma": {"sha1": "2475b08151556ad4d89bb1d2b6494c6bee9abd82", "size": 29954, "url": "https://launcher.mojang.com/v1/objects/2475b08151556ad4d89bb1d2b6494c6bee9abd82/LucidaTypewriterBoldOblique.ttf"}, "raw": {"sha1": "f331fc8b0cc494702bc46b690f2b8eed36469a02", "size": 63168, "url": "https://launcher.mojang.com/v1/objects/f331fc8b0cc494702bc46b690f2b8eed36469a02/LucidaTypewriterBoldOblique.ttf"}}, "executable": false, "type": "file"}, "lib/oblique-fonts/LucidaTypewriterOblique.ttf": {"downloads": {"lzma": {"sha1": "5b970bc3b7abb21dce1aa28ff7f03459d351e552", "size": 60133, "url": "https://launcher.mojang.com/v1/objects/5b970bc3b7abb21dce1aa28ff7f03459d351e552/LucidaTypewriterOblique.ttf"}, "raw": {"sha1": "f8ea00db73f8a89a27674d050edc37c2280930e1", "size": 137484, "url": "https://launcher.mojang.com/v1/objects/f8ea00db73f8a89a27674d050edc37c2280930e1/LucidaTypewriterOblique.ttf"}}, "executable": false, "type": "file"}, "lib/oblique-fonts/fonts.dir": {"downloads": {"lzma": {"sha1": "067528c789bd713c7c3f34e779aa6e2e8253dcf6", "size": 188, "url": "https://launcher.mojang.com/v1/objects/067528c789bd713c7c3f34e779aa6e2e8253dcf6/fonts.dir"}, "raw": {"sha1": "5aee54ffba9e33de56fd84ef64fa496b898585bb", "size": 2115, "url": "https://launcher.mojang.com/v1/objects/5aee54ffba9e33de56fd84ef64fa496b898585bb/fonts.dir"}}, "executable": false, "type": "file"}, "lib/plugin.jar": {"downloads": {"raw": {"sha1": "3f250842c79112bae5369e372025b166990820e8", "size": 950772, "url": "https://launcher.mojang.com/v1/objects/3f250842c79112bae5369e372025b166990820e8/plugin.jar"}}, "executable": false, "type": "file"}, "lib/psfont.properties.ja": {"downloads": {"lzma": {"sha1": "7ca1cc244ed251cd1eb2347f1eea37d7d18c8ad4", "size": 701, "url": "https://launcher.mojang.com/v1/objects/7ca1cc244ed251cd1eb2347f1eea37d7d18c8ad4/psfont.properties.ja"}, "raw": {"sha1": "56ed1c661eeede17b4fae8c9de7b5edbad387abc", "size": 2796, "url": "https://launcher.mojang.com/v1/objects/56ed1c661eeede17b4fae8c9de7b5edbad387abc/psfont.properties.ja"}}, "executable": false, "type": "file"}, "lib/psfontj2d.properties": {"downloads": {"lzma": {"sha1": "4252fa01af8739a3545e2b705e3383892e22ab40", "size": 2278, "url": "https://launcher.mojang.com/v1/objects/4252fa01af8739a3545e2b705e3383892e22ab40/psfontj2d.properties"}, "raw": {"sha1": "aa327a22a49967f4d74afeee6726f505f209692f", "size": 10393, "url": "https://launcher.mojang.com/v1/objects/aa327a22a49967f4d74afeee6726f505f209692f/psfontj2d.properties"}}, "executable": false, "type": "file"}, "lib/resources.jar": {"downloads": {"lzma": {"sha1": "1b0e08441750dc17efe4b527aa146da6cc14e8a6", "size": 579294, "url": "https://launcher.mojang.com/v1/objects/1b0e08441750dc17efe4b527aa146da6cc14e8a6/resources.jar"}, "raw": {"sha1": "daa021906e4648d4c37e798c11733dc2047f2da1", "size": 3505206, "url": "https://launcher.mojang.com/v1/objects/daa021906e4648d4c37e798c11733dc2047f2da1/resources.jar"}}, "executable": false, "type": "file"}, "lib/rt.jar": {"downloads": {"lzma": {"sha1": "fc4a8681aeda29c2a2a3fd11bad7729543283f3d", "size": 14378994, "url": "https://launcher.mojang.com/v1/objects/fc4a8681aeda29c2a2a3fd11bad7729543283f3d/rt.jar"}, "raw": {"sha1": "5396b0954a20f3210f1f4f1886ead30880d6ebfe", "size": 66334986, "url": "https://launcher.mojang.com/v1/objects/5396b0954a20f3210f1f4f1886ead30880d6ebfe/rt.jar"}}, "executable": false, "type": "file"}, "lib/security": {"type": "directory"}, "lib/security/blacklist": {"downloads": {"lzma": {"sha1": "8206fce6c1d91a39fdf78e8e79e953913994a1cd", "size": 1969, "url": "https://launcher.mojang.com/v1/objects/8206fce6c1d91a39fdf78e8e79e953913994a1cd/blacklist"}, "raw": {"sha1": "d4ffb3857eab403955ce9d156e46d056061e6a5a", "size": 4054, "url": "https://launcher.mojang.com/v1/objects/d4ffb3857eab403955ce9d156e46d056061e6a5a/blacklist"}}, "executable": false, "type": "file"}, "lib/security/blacklisted.certs": {"downloads": {"lzma": {"sha1": "8311bead054caf6cfe678d4b7998de4caaabfa53", "size": 806, "url": "https://launcher.mojang.com/v1/objects/8311bead054caf6cfe678d4b7998de4caaabfa53/blacklisted.certs"}, "raw": {"sha1": "c5c005c29a80493f5c31cd7eb629ac1b9c752404", "size": 1273, "url": "https://launcher.mojang.com/v1/objects/c5c005c29a80493f5c31cd7eb629ac1b9c752404/blacklisted.certs"}}, "executable": false, "type": "file"}, "lib/security/cacerts": {"downloads": {"lzma": {"sha1": "654dd94809655d5b28385cbb5eba8d6ad9f2c1aa", "size": 67802, "url": "https://launcher.mojang.com/v1/objects/654dd94809655d5b28385cbb5eba8d6ad9f2c1aa/cacerts"}, "raw": {"sha1": "2917859c443c68e19f93abcd1315c3c2904cbef9", "size": 104430, "url": "https://launcher.mojang.com/v1/objects/2917859c443c68e19f93abcd1315c3c2904cbef9/cacerts"}}, "executable": false, "type": "file"}, "lib/security/java.policy": {"downloads": {"lzma": {"sha1": "b601c420d02ef3dbd8595453d08fdef91134e8b5", "size": 647, "url": "https://launcher.mojang.com/v1/objects/b601c420d02ef3dbd8595453d08fdef91134e8b5/java.policy"}, "raw": {"sha1": "c0112209a567b3b523cfed7041709f9440227968", "size": 2466, "url": "https://launcher.mojang.com/v1/objects/c0112209a567b3b523cfed7041709f9440227968/java.policy"}}, "executable": false, "type": "file"}, "lib/security/java.security": {"downloads": {"lzma": {"sha1": "531620e82ca0365ce8dc97096bb0ac5a7ace5952", "size": 10959, "url": "https://launcher.mojang.com/v1/objects/531620e82ca0365ce8dc97096bb0ac5a7ace5952/java.security"}, "raw": {"sha1": "5dcc17a168c53d0b366784e520bd4d55aa61ac18", "size": 41528, "url": "https://launcher.mojang.com/v1/objects/5dcc17a168c53d0b366784e520bd4d55aa61ac18/java.security"}}, "executable": false, "type": "file"}, "lib/security/javaws.policy": {"downloads": {"raw": {"sha1": "4384ca5e4d32f7dd86d8baddd1e690730d74e694", "size": 98, "url": "https://launcher.mojang.com/v1/objects/4384ca5e4d32f7dd86d8baddd1e690730d74e694/javaws.policy"}}, "executable": false, "type": "file"}, "lib/security/policy": {"type": "directory"}, "lib/security/policy/limited": {"type": "directory"}, "lib/security/policy/limited/US_export_policy.jar": {"downloads": {"raw": {"sha1": "7d69ea3b385bc067738520f1b5c549e1084be285", "size": 3026, "url": "https://launcher.mojang.com/v1/objects/7d69ea3b385bc067738520f1b5c549e1084be285/US_export_policy.jar"}}, "executable": false, "type": "file"}, "lib/security/policy/limited/local_policy.jar": {"downloads": {"raw": {"sha1": "238b8826e110f58acb2e1959773b0a577cd4d569", "size": 3527, "url": "https://launcher.mojang.com/v1/objects/238b8826e110f58acb2e1959773b0a577cd4d569/local_policy.jar"}}, "executable": false, "type": "file"}, "lib/security/policy/unlimited": {"type": "directory"}, "lib/security/policy/unlimited/US_export_policy.jar": {"downloads": {"raw": {"sha1": "f6fb2af1e87fc622cda194a7d6b5f5f069653ff1", "size": 3023, "url": "https://launcher.mojang.com/v1/objects/f6fb2af1e87fc622cda194a7d6b5f5f069653ff1/US_export_policy.jar"}}, "executable": false, "type": "file"}, "lib/security/policy/unlimited/local_policy.jar": {"downloads": {"raw": {"sha1": "517368ab2cbaf6b42ea0b963f98eeedd996e83e3", "size": 3035, "url": "https://launcher.mojang.com/v1/objects/517368ab2cbaf6b42ea0b963f98eeedd996e83e3/local_policy.jar"}}, "executable": false, "type": "file"}, "lib/security/trusted.libraries": {"downloads": {"raw": {"sha1": "da39a3ee5e6b4b0d3255bfef95601890afd80709", "size": 0, "url": "https://launcher.mojang.com/v1/objects/da39a3ee5e6b4b0d3255bfef95601890afd80709/trusted.libraries"}}, "executable": false, "type": "file"}, "lib/sound.properties": {"downloads": {"lzma": {"sha1": "3b5f7e4ec437d79048af35094290577f483b3fe1", "size": 473, "url": "https://launcher.mojang.com/v1/objects/3b5f7e4ec437d79048af35094290577f483b3fe1/sound.properties"}, "raw": {"sha1": "9afceb218059d981d0fa9f07aad3c5097cf41b0c", "size": 1210, "url": "https://launcher.mojang.com/v1/objects/9afceb218059d981d0fa9f07aad3c5097cf41b0c/sound.properties"}}, "executable": false, "type": "file"}, "lib/tzdb.dat": {"downloads": {"lzma": {"sha1": "39c69339965484afe89c14111baeeb862fdefd97", "size": 32547, "url": "https://launcher.mojang.com/v1/objects/39c69339965484afe89c14111baeeb862fdefd97/tzdb.dat"}, "raw": {"sha1": "b59c07e3619271a3b9861e999f4b138e971baf69", "size": 105734, "url": "https://launcher.mojang.com/v1/objects/b59c07e3619271a3b9861e999f4b138e971baf69/tzdb.dat"}}, "executable": false, "type": "file"}, "man": {"type": "directory"}, "man/ja": {"target": "ja_JP.UTF-8", "type": "link"}, "man/ja_JP.UTF-8": {"type": "directory"}, "man/ja_JP.UTF-8/man1": {"type": "directory"}, "man/ja_JP.UTF-8/man1/java.1": {"downloads": {"lzma": {"sha1": "f9da09710b6c6df23c256e324a0c4df00a0d6ded", "size": 25461, "url": "https://launcher.mojang.com/v1/objects/f9da09710b6c6df23c256e324a0c4df00a0d6ded/java.1"}, "raw": {"sha1": "b0b12a0bb66e6171771ca4b1dfca32fb759bcaec", "size": 148688, "url": "https://launcher.mojang.com/v1/objects/b0b12a0bb66e6171771ca4b1dfca32fb759bcaec/java.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/javaws.1": {"downloads": {"lzma": {"sha1": "6188fae453ca09ccb19be5c9f4d2059926b36267", "size": 2154, "url": "https://launcher.mojang.com/v1/objects/6188fae453ca09ccb19be5c9f4d2059926b36267/javaws.1"}, "raw": {"sha1": "8f39d928870268ace07bedfebd18db1e1d07fc37", "size": 6641, "url": "https://launcher.mojang.com/v1/objects/8f39d928870268ace07bedfebd18db1e1d07fc37/javaws.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/jjs.1": {"downloads": {"lzma": {"sha1": "6e42b989d28b185dc1aab50c0389834e649a37d4", "size": 3452, "url": "https://launcher.mojang.com/v1/objects/6e42b989d28b185dc1aab50c0389834e649a37d4/jjs.1"}, "raw": {"sha1": "e023322a2013912315a2bd1034e6f829a27c76e0", "size": 11365, "url": "https://launcher.mojang.com/v1/objects/e023322a2013912315a2bd1034e6f829a27c76e0/jjs.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/keytool.1": {"downloads": {"lzma": {"sha1": "a78134a4bddd53d684a70aa677e51a215db1c9cb", "size": 20698, "url": "https://launcher.mojang.com/v1/objects/a78134a4bddd53d684a70aa677e51a215db1c9cb/keytool.1"}, "raw": {"sha1": "148583c837eaaf6333ccfd8c9e8df08574e14b0c", "size": 111033, "url": "https://launcher.mojang.com/v1/objects/148583c837eaaf6333ccfd8c9e8df08574e14b0c/keytool.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/orbd.1": {"downloads": {"lzma": {"sha1": "326af0dcbff173ef8aee29163dbe146d7389cc3e", "size": 4225, "url": "https://launcher.mojang.com/v1/objects/326af0dcbff173ef8aee29163dbe146d7389cc3e/orbd.1"}, "raw": {"sha1": "95651622d33c08286858ec337edd3ea72acd93dc", "size": 16092, "url": "https://launcher.mojang.com/v1/objects/95651622d33c08286858ec337edd3ea72acd93dc/orbd.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/pack200.1": {"downloads": {"lzma": {"sha1": "e0eedafa748c61a44e5be4355fe9d44b05048e80", "size": 4293, "url": "https://launcher.mojang.com/v1/objects/e0eedafa748c61a44e5be4355fe9d44b05048e80/pack200.1"}, "raw": {"sha1": "aa21a0ab75707f7fc66e83c7a392e69b37ddf80e", "size": 14482, "url": "https://launcher.mojang.com/v1/objects/aa21a0ab75707f7fc66e83c7a392e69b37ddf80e/pack200.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/policytool.1": {"downloads": {"lzma": {"sha1": "3c766ed12dab58166169d35680c392a6be1814a1", "size": 1380, "url": "https://launcher.mojang.com/v1/objects/3c766ed12dab58166169d35680c392a6be1814a1/policytool.1"}, "raw": {"sha1": "80879c74e072a98fad6f32b3283331aaf9bd002f", "size": 4020, "url": "https://launcher.mojang.com/v1/objects/80879c74e072a98fad6f32b3283331aaf9bd002f/policytool.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/rmid.1": {"downloads": {"lzma": {"sha1": "1e20779d990beacc32a48237777d670fcc47ca14", "size": 4836, "url": "https://launcher.mojang.com/v1/objects/1e20779d990beacc32a48237777d670fcc47ca14/rmid.1"}, "raw": {"sha1": "7e40cb8003d098d6e36f45640b26f979ac94b5c5", "size": 19715, "url": "https://launcher.mojang.com/v1/objects/7e40cb8003d098d6e36f45640b26f979ac94b5c5/rmid.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/rmiregistry.1": {"downloads": {"lzma": {"sha1": "aaf4ffe07e954f8696eef1ecb7a5e244628d0ad9", "size": 1627, "url": "https://launcher.mojang.com/v1/objects/aaf4ffe07e954f8696eef1ecb7a5e244628d0ad9/rmiregistry.1"}, "raw": {"sha1": "c53c52f3ae7a011c135894c9fc51b741e729c33d", "size": 4557, "url": "https://launcher.mojang.com/v1/objects/c53c52f3ae7a011c135894c9fc51b741e729c33d/rmiregistry.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/servertool.1": {"downloads": {"lzma": {"sha1": "3b9e624e9d1cf2959b438a35061162e2100ddecd", "size": 2626, "url": "https://launcher.mojang.com/v1/objects/3b9e624e9d1cf2959b438a35061162e2100ddecd/servertool.1"}, "raw": {"sha1": "50ab8bcd9dd9d0b1a3d81348fbce1c8f82e7189e", "size": 9081, "url": "https://launcher.mojang.com/v1/objects/50ab8bcd9dd9d0b1a3d81348fbce1c8f82e7189e/servertool.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/tnameserv.1": {"downloads": {"lzma": {"sha1": "bb3106ff74c60a76de3d20659b9c2128c70f3bf2", "size": 4478, "url": "https://launcher.mojang.com/v1/objects/bb3106ff74c60a76de3d20659b9c2128c70f3bf2/tnameserv.1"}, "raw": {"sha1": "01e714671ecd1167edcb5310b16a9c59c33c3eaa", "size": 17722, "url": "https://launcher.mojang.com/v1/objects/01e714671ecd1167edcb5310b16a9c59c33c3eaa/tnameserv.1"}}, "executable": false, "type": "file"}, "man/ja_JP.UTF-8/man1/unpack200.1": {"downloads": {"lzma": {"sha1": "c115a881cf800b08df294df55d9f250ae944e33c", "size": 1973, "url": "https://launcher.mojang.com/v1/objects/c115a881cf800b08df294df55d9f250ae944e33c/unpack200.1"}, "raw": {"sha1": "7c882bba0067367a41ad84868d18793b8a7397a3", "size": 5382, "url": "https://launcher.mojang.com/v1/objects/7c882bba0067367a41ad84868d18793b8a7397a3/unpack200.1"}}, "executable": false, "type": "file"}, "man/man1": {"type": "directory"}, "man/man1/java.1": {"downloads": {"lzma": {"sha1": "06a6b0275c202bf698d73ca71f95618d56d81c15", "size": 25796, "url": "https://launcher.mojang.com/v1/objects/06a6b0275c202bf698d73ca71f95618d56d81c15/java.1"}, "raw": {"sha1": "69fec7a341aa91f18dbdcdb95952dede7e1b689a", "size": 124796, "url": "https://launcher.mojang.com/v1/objects/69fec7a341aa91f18dbdcdb95952dede7e1b689a/java.1"}}, "executable": false, "type": "file"}, "man/man1/javaws.1": {"downloads": {"lzma": {"sha1": "4bae251c6dfb5420f56928815cf80d0b6d517a1f", "size": 1759, "url": "https://launcher.mojang.com/v1/objects/4bae251c6dfb5420f56928815cf80d0b6d517a1f/javaws.1"}, "raw": {"sha1": "e61e44e101b1bc119c2d2d4b10320f38b36a8036", "size": 4897, "url": "https://launcher.mojang.com/v1/objects/e61e44e101b1bc119c2d2d4b10320f38b36a8036/javaws.1"}}, "executable": false, "type": "file"}, "man/man1/jjs.1": {"downloads": {"lzma": {"sha1": "29683cf2bd47015c9461b688749ddffd95f6671d", "size": 1881, "url": "https://launcher.mojang.com/v1/objects/29683cf2bd47015c9461b688749ddffd95f6671d/jjs.1"}, "raw": {"sha1": "78d419bd3a7f3e0802d5220e690429194b5d1beb", "size": 4932, "url": "https://launcher.mojang.com/v1/objects/78d419bd3a7f3e0802d5220e690429194b5d1beb/jjs.1"}}, "executable": false, "type": "file"}, "man/man1/keytool.1": {"downloads": {"lzma": {"sha1": "b67e5126d43713ee3675706724b34061578b42db", "size": 19690, "url": "https://launcher.mojang.com/v1/objects/b67e5126d43713ee3675706724b34061578b42db/keytool.1"}, "raw": {"sha1": "4c976f86057ab779763fcfb98f5702ebef47f629", "size": 86925, "url": "https://launcher.mojang.com/v1/objects/4c976f86057ab779763fcfb98f5702ebef47f629/keytool.1"}}, "executable": false, "type": "file"}, "man/man1/orbd.1": {"downloads": {"lzma": {"sha1": "147064d6f7e027002e296bb246ae572d0ce0495b", "size": 3708, "url": "https://launcher.mojang.com/v1/objects/147064d6f7e027002e296bb246ae572d0ce0495b/orbd.1"}, "raw": {"sha1": "64201e1846fcf1dcc45c786ffeab89426d1c7742", "size": 12180, "url": "https://launcher.mojang.com/v1/objects/64201e1846fcf1dcc45c786ffeab89426d1c7742/orbd.1"}}, "executable": false, "type": "file"}, "man/man1/pack200.1": {"downloads": {"lzma": {"sha1": "fe17486bbe9c58cf4182fa056b9cd124e8295607", "size": 3724, "url": "https://launcher.mojang.com/v1/objects/fe17486bbe9c58cf4182fa056b9cd124e8295607/pack200.1"}, "raw": {"sha1": "26826cf52b89924f2d2a60d6cda798891875eae6", "size": 11623, "url": "https://launcher.mojang.com/v1/objects/26826cf52b89924f2d2a60d6cda798891875eae6/pack200.1"}}, "executable": false, "type": "file"}, "man/man1/policytool.1": {"downloads": {"lzma": {"sha1": "bd154e7c39aca71d15b2098c588866f8d95bc743", "size": 1122, "url": "https://launcher.mojang.com/v1/objects/bd154e7c39aca71d15b2098c588866f8d95bc743/policytool.1"}, "raw": {"sha1": "ab296625155d9a2b25ecc2b4feff2f741b3ad136", "size": 3235, "url": "https://launcher.mojang.com/v1/objects/ab296625155d9a2b25ecc2b4feff2f741b3ad136/policytool.1"}}, "executable": false, "type": "file"}, "man/man1/rmid.1": {"downloads": {"lzma": {"sha1": "6a7da234e7f43ebca5c4ba8cd862fda3be62fbaa", "size": 4255, "url": "https://launcher.mojang.com/v1/objects/6a7da234e7f43ebca5c4ba8cd862fda3be62fbaa/rmid.1"}, "raw": {"sha1": "6f10e214d7950a6a8460524e41dc700f112f89e5", "size": 15979, "url": "https://launcher.mojang.com/v1/objects/6f10e214d7950a6a8460524e41dc700f112f89e5/rmid.1"}}, "executable": false, "type": "file"}, "man/man1/rmiregistry.1": {"downloads": {"lzma": {"sha1": "f40dd17e3a734600ad1828b0c42d3a1685c4c520", "size": 1301, "url": "https://launcher.mojang.com/v1/objects/f40dd17e3a734600ad1828b0c42d3a1685c4c520/rmiregistry.1"}, "raw": {"sha1": "d9a3d23fab689df5bb9a792b88f462f939b49f70", "size": 3449, "url": "https://launcher.mojang.com/v1/objects/d9a3d23fab689df5bb9a792b88f462f939b49f70/rmiregistry.1"}}, "executable": false, "type": "file"}, "man/man1/servertool.1": {"downloads": {"lzma": {"sha1": "74f1e10712202cd3ca0ff5833de05b7ee67092e1", "size": 2307, "url": "https://launcher.mojang.com/v1/objects/74f1e10712202cd3ca0ff5833de05b7ee67092e1/servertool.1"}, "raw": {"sha1": "e6c7b510740ac8681a9bfb5f4ee1f0306125b728", "size": 7237, "url": "https://launcher.mojang.com/v1/objects/e6c7b510740ac8681a9bfb5f4ee1f0306125b728/servertool.1"}}, "executable": false, "type": "file"}, "man/man1/tnameserv.1": {"downloads": {"lzma": {"sha1": "4bec7f4e070d023f124f9352a8971d7acd249a15", "size": 3955, "url": "https://launcher.mojang.com/v1/objects/4bec7f4e070d023f124f9352a8971d7acd249a15/tnameserv.1"}, "raw": {"sha1": "a31dbbe800d49cb371fab9a4b73d22c3bf8799ad", "size": 15747, "url": "https://launcher.mojang.com/v1/objects/a31dbbe800d49cb371fab9a4b73d22c3bf8799ad/tnameserv.1"}}, "executable": false, "type": "file"}, "man/man1/unpack200.1": {"downloads": {"lzma": {"sha1": "f8e73863187929debf2ea6dadefb2995ec7917e7", "size": 1672, "url": "https://launcher.mojang.com/v1/objects/f8e73863187929debf2ea6dadefb2995ec7917e7/unpack200.1"}, "raw": {"sha1": "437f7233d738cb9b822e99003127049005663e0f", "size": 4244, "url": "https://launcher.mojang.com/v1/objects/437f7233d738cb9b822e99003127049005663e0f/unpack200.1"}}, "executable": false, "type": "file"}, "plugin": {"type": "directory"}, "plugin/desktop": {"type": "directory"}, "plugin/desktop/sun_java.desktop": {"downloads": {"lzma": {"sha1": "49ab0ccb54c3be68281d05055bc56a88b1281d3c", "size": 447, "url": "https://launcher.mojang.com/v1/objects/49ab0ccb54c3be68281d05055bc56a88b1281d3c/sun_java.desktop"}, "raw": {"sha1": "79120ee8160ad6f3c9b90c2641fb7edf3af96b5d", "size": 624, "url": "https://launcher.mojang.com/v1/objects/79120ee8160ad6f3c9b90c2641fb7edf3af96b5d/sun_java.desktop"}}, "executable": false, "type": "file"}, "plugin/desktop/sun_java.png": {"downloads": {"raw": {"sha1": "699c41e97a35414e72a80327a54d6e14e874e951", "size": 4351, "url": "https://launcher.mojang.com/v1/objects/699c41e97a35414e72a80327a54d6e14e874e951/sun_java.png"}}, "executable": false, "type": "file"}, "release": {"downloads": {"raw": {"sha1": "cb462682644c0275d94a45b759108815f3112064", "size": 424, "url": "https://launcher.mojang.com/v1/objects/cb462682644c0275d94a45b759108815f3112064/release"}}, "executable": false, "type": "file"}}} \ No newline at end of file +{ + "files": { + "COPYRIGHT": { + "downloads": { + "lzma": { + "sha1": "dd860e040807f7e53ae89da5f28dd73d57ac605d", + "size": 1431, + "url": "https://launcher.mojang.com/v1/objects/dd860e040807f7e53ae89da5f28dd73d57ac605d/COPYRIGHT" + }, + "raw": { + "sha1": "c725183c757011e7ba96c83c1e86ee7e8b516a2b", + "size": 3244, + "url": "https://launcher.mojang.com/v1/objects/c725183c757011e7ba96c83c1e86ee7e8b516a2b/COPYRIGHT" + } + }, + "executable": false, + "type": "file" + }, + "LICENSE": { + "downloads": { + "raw": { + "sha1": "3e86865deec0814c958bcf7fb87f790bccc0e8bd", + "size": 40, + "url": "https://launcher.mojang.com/v1/objects/3e86865deec0814c958bcf7fb87f790bccc0e8bd/LICENSE" + } + }, + "executable": false, + "type": "file" + }, + "README": { + "downloads": { + "raw": { + "sha1": "f90331df1e5badeadc501d8dd70714c62a920204", + "size": 46, + "url": "https://launcher.mojang.com/v1/objects/f90331df1e5badeadc501d8dd70714c62a920204/README" + } + }, + "executable": false, + "type": "file" + }, + "THIRDPARTYLICENSEREADME-JAVAFX.txt": { + "downloads": { + "lzma": { + "sha1": "4fee85109d7ff04b982d0576dabd15397f599125", + "size": 15455, + "url": + "https://launcher.mojang.com/v1/objects/4fee85109d7ff04b982d0576dabd15397f599125/THIRDPARTYLICENSEREADME-JAVAFX.txt" + }, + "raw": { + "sha1": "56ff42f87607b997b52ae0ef8bf315e36932e870", + "size": 112724, + "url": + "https://launcher.mojang.com/v1/objects/56ff42f87607b997b52ae0ef8bf315e36932e870/THIRDPARTYLICENSEREADME-JAVAFX.txt" + } + }, + "executable": false, + "type": "file" + }, + "THIRDPARTYLICENSEREADME.txt": { + "downloads": { + "lzma": { + "sha1": "419c1414ba46ae9dbfd38cf4e0601fff61644429", + "size": 32266, + "url": "https://launcher.mojang.com/v1/objects/419c1414ba46ae9dbfd38cf4e0601fff61644429/THIRDPARTYLICENSEREADME.txt" + }, + "raw": { + "sha1": "b83c3f32261de3e48ccd20614a11e066b1ec9027", + "size": 153824, + "url": "https://launcher.mojang.com/v1/objects/b83c3f32261de3e48ccd20614a11e066b1ec9027/THIRDPARTYLICENSEREADME.txt" + } + }, + "executable": false, + "type": "file" + }, + "Welcome.html": { + "downloads": { + "lzma": { + "sha1": "01c21a74b4aafb7cbe0388233c43cbdf77dcaaea", + "size": 528, + "url": "https://launcher.mojang.com/v1/objects/01c21a74b4aafb7cbe0388233c43cbdf77dcaaea/Welcome.html" + }, + "raw": { + "sha1": "d98ae54f03dac87419abc19b97e315830c2da55f", + "size": 955, + "url": "https://launcher.mojang.com/v1/objects/d98ae54f03dac87419abc19b97e315830c2da55f/Welcome.html" + } + }, + "executable": false, + "type": "file" + }, + "bin": { + "type": "directory" + }, + "bin/ControlPanel": { + "target": "jcontrol", + "type": "link" + }, + "bin/java": { + "downloads": { + "lzma": { + "sha1": "3857eea1d59e1bc545c67a753ed2768254807b8a", + "size": 2088, + "url": "https://launcher.mojang.com/v1/objects/3857eea1d59e1bc545c67a753ed2768254807b8a/java" + }, + "raw": { + "sha1": "3d20560fb5d1a49cb689c2226972e92e06d27ba6", + "size": 8464, + "url": "https://launcher.mojang.com/v1/objects/3d20560fb5d1a49cb689c2226972e92e06d27ba6/java" + } + }, + "executable": true, + "type": "file" + }, + "bin/javaws": { + "downloads": { + "lzma": { + "sha1": "a6bec5c049e76c4488294a256a2084ea23ddb440", + "size": 38173, + "url": "https://launcher.mojang.com/v1/objects/a6bec5c049e76c4488294a256a2084ea23ddb440/javaws" + }, + "raw": { + "sha1": "955c0f0066e2f893b0c2b3ccd83e223722e4ab74", + "size": 140296, + "url": "https://launcher.mojang.com/v1/objects/955c0f0066e2f893b0c2b3ccd83e223722e4ab74/javaws" + } + }, + "executable": true, + "type": "file" + }, + "bin/jcontrol": { + "downloads": { + "lzma": { + "sha1": "40c5e33748f252e1d950b579a4185ab2c23fc908", + "size": 2166, + "url": "https://launcher.mojang.com/v1/objects/40c5e33748f252e1d950b579a4185ab2c23fc908/jcontrol" + }, + "raw": { + "sha1": "ed541733c8b51e34349c1f8010b277e58ad73f1e", + "size": 6264, + "url": "https://launcher.mojang.com/v1/objects/ed541733c8b51e34349c1f8010b277e58ad73f1e/jcontrol" + } + }, + "executable": true, + "type": "file" + }, + "bin/jjs": { + "downloads": { + "lzma": { + "sha1": "d44d1ac421979f7671921986214812095a5b0e3b", + "size": 2168, + "url": "https://launcher.mojang.com/v1/objects/d44d1ac421979f7671921986214812095a5b0e3b/jjs" + }, + "raw": { + "sha1": "f00f944c3dbe556793b5dc686aaeee3e5722e99b", + "size": 8584, + "url": "https://launcher.mojang.com/v1/objects/f00f944c3dbe556793b5dc686aaeee3e5722e99b/jjs" + } + }, + "executable": true, + "type": "file" + }, + "bin/keytool": { + "downloads": { + "lzma": { + "sha1": "93c607dce450976667c382f609a367167bdec05c", + "size": 2175, + "url": "https://launcher.mojang.com/v1/objects/93c607dce450976667c382f609a367167bdec05c/keytool" + }, + "raw": { + "sha1": "7114b561546270e441e9ed1bcc24e5188c068a42", + "size": 8584, + "url": "https://launcher.mojang.com/v1/objects/7114b561546270e441e9ed1bcc24e5188c068a42/keytool" + } + }, + "executable": true, + "type": "file" + }, + "bin/orbd": { + "downloads": { + "lzma": { + "sha1": "b27dfded5e2b2f6f02c555971c94e46ca14ac81b", + "size": 2254, + "url": "https://launcher.mojang.com/v1/objects/b27dfded5e2b2f6f02c555971c94e46ca14ac81b/orbd" + }, + "raw": { + "sha1": "7f31217fecb3dbbd89f1dd3783fca58793a66fd2", + "size": 8656, + "url": "https://launcher.mojang.com/v1/objects/7f31217fecb3dbbd89f1dd3783fca58793a66fd2/orbd" + } + }, + "executable": true, + "type": "file" + }, + "bin/pack200": { + "downloads": { + "lzma": { + "sha1": "b52da4497b49b1508b6225a5740857ddb8f52e97", + "size": 2183, + "url": "https://launcher.mojang.com/v1/objects/b52da4497b49b1508b6225a5740857ddb8f52e97/pack200" + }, + "raw": { + "sha1": "16ef3e801efb57e50bc6477a27a9d95d02d0775b", + "size": 8584, + "url": "https://launcher.mojang.com/v1/objects/16ef3e801efb57e50bc6477a27a9d95d02d0775b/pack200" + } + }, + "executable": true, + "type": "file" + }, + "bin/policytool": { + "downloads": { + "lzma": { + "sha1": "87da4c07da45f3d1a1a9d732af197cd39bf69d10", + "size": 2182, + "url": "https://launcher.mojang.com/v1/objects/87da4c07da45f3d1a1a9d732af197cd39bf69d10/policytool" + }, + "raw": { + "sha1": "a52a29424470cb9b8db5c2fb1751d0b697a7ec8e", + "size": 8592, + "url": "https://launcher.mojang.com/v1/objects/a52a29424470cb9b8db5c2fb1751d0b697a7ec8e/policytool" + } + }, + "executable": true, + "type": "file" + }, + "bin/rmid": { + "downloads": { + "lzma": { + "sha1": "1494c1174fde0c0a93ea117bc7edf7eb936c0512", + "size": 2172, + "url": "https://launcher.mojang.com/v1/objects/1494c1174fde0c0a93ea117bc7edf7eb936c0512/rmid" + }, + "raw": { + "sha1": "5c8710e1ab924e5b09a07bcb4c6e106293bbd1a8", + "size": 8584, + "url": "https://launcher.mojang.com/v1/objects/5c8710e1ab924e5b09a07bcb4c6e106293bbd1a8/rmid" + } + }, + "executable": true, + "type": "file" + }, + "bin/rmiregistry": { + "downloads": { + "lzma": { + "sha1": "7070cf2ec5a5e520a880bae699431edf02083e7e", + "size": 2174, + "url": "https://launcher.mojang.com/v1/objects/7070cf2ec5a5e520a880bae699431edf02083e7e/rmiregistry" + }, + "raw": { + "sha1": "5f518daa7050028d5d9d849634c73136f2b23a54", + "size": 8592, + "url": "https://launcher.mojang.com/v1/objects/5f518daa7050028d5d9d849634c73136f2b23a54/rmiregistry" + } + }, + "executable": true, + "type": "file" + }, + "bin/servertool": { + "downloads": { + "lzma": { + "sha1": "1db683a11cc9b7313426c84412f4d95be2fa7ccd", + "size": 2185, + "url": "https://launcher.mojang.com/v1/objects/1db683a11cc9b7313426c84412f4d95be2fa7ccd/servertool" + }, + "raw": { + "sha1": "49d0ebfeb265ce5a8733e1014541ea2525674a60", + "size": 8592, + "url": "https://launcher.mojang.com/v1/objects/49d0ebfeb265ce5a8733e1014541ea2525674a60/servertool" + } + }, + "executable": true, + "type": "file" + }, + "bin/tnameserv": { + "downloads": { + "lzma": { + "sha1": "36da9c9a2c5a8b662a3f8d52ca67339bce1c2714", + "size": 2291, + "url": "https://launcher.mojang.com/v1/objects/36da9c9a2c5a8b662a3f8d52ca67339bce1c2714/tnameserv" + }, + "raw": { + "sha1": "09d998f8efcb6f55d0d87f59e08f8b89662796d9", + "size": 8656, + "url": "https://launcher.mojang.com/v1/objects/09d998f8efcb6f55d0d87f59e08f8b89662796d9/tnameserv" + } + }, + "executable": true, + "type": "file" + }, + "bin/unpack200": { + "downloads": { + "lzma": { + "sha1": "344959e32fc7ee19eebe7b3cf5ab6d1a7d6641f2", + "size": 79721, + "url": "https://launcher.mojang.com/v1/objects/344959e32fc7ee19eebe7b3cf5ab6d1a7d6641f2/unpack200" + }, + "raw": { + "sha1": "5dd933132f1b202e19e0c8e093f7113711cfdfc1", + "size": 182616, + "url": "https://launcher.mojang.com/v1/objects/5dd933132f1b202e19e0c8e093f7113711cfdfc1/unpack200" + } + }, + "executable": true, + "type": "file" + }, + "lib": { + "type": "directory" + }, + "lib/amd64": { + "type": "directory" + }, + "lib/amd64/jli": { + "type": "directory" + }, + "lib/amd64/jli/libjli.so": { + "downloads": { + "lzma": { + "sha1": "372331ee8e375888f798a2e88180a94493e141b0", + "size": 48327, + "url": "https://launcher.mojang.com/v1/objects/372331ee8e375888f798a2e88180a94493e141b0/libjli.so" + }, + "raw": { + "sha1": "73b0cf8b7415686bc40c561ff77ff2740ccf7a44", + "size": 108616, + "url": "https://launcher.mojang.com/v1/objects/73b0cf8b7415686bc40c561ff77ff2740ccf7a44/libjli.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/jvm.cfg": { + "downloads": { + "lzma": { + "sha1": "86bcfebec37b38415525ffd77d3eaf70d0b1b4ca", + "size": 435, + "url": "https://launcher.mojang.com/v1/objects/86bcfebec37b38415525ffd77d3eaf70d0b1b4ca/jvm.cfg" + }, + "raw": { + "sha1": "84b38bdc745de446ba0ca0232ea3aaf2efd721da", + "size": 627, + "url": "https://launcher.mojang.com/v1/objects/84b38bdc745de446ba0ca0232ea3aaf2efd721da/jvm.cfg" + } + }, + "executable": false, + "type": "file" + }, + "lib/amd64/libavplugin-53.so": { + "downloads": { + "lzma": { + "sha1": "a332366762d9efc7b845a682b7edce62db44618c", + "size": 14747, + "url": "https://launcher.mojang.com/v1/objects/a332366762d9efc7b845a682b7edce62db44618c/libavplugin-53.so" + }, + "raw": { + "sha1": "9bd1473dd8a0dc7950c7af1cc69a45548df26eb5", + "size": 51720, + "url": "https://launcher.mojang.com/v1/objects/9bd1473dd8a0dc7950c7af1cc69a45548df26eb5/libavplugin-53.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libavplugin-54.so": { + "downloads": { + "lzma": { + "sha1": "2c615852a0720a275163e00597c1f711f11341da", + "size": 15153, + "url": "https://launcher.mojang.com/v1/objects/2c615852a0720a275163e00597c1f711f11341da/libavplugin-54.so" + }, + "raw": { + "sha1": "8808050c5949c4800b42d1b19b1f8b0d120bcacb", + "size": 51768, + "url": "https://launcher.mojang.com/v1/objects/8808050c5949c4800b42d1b19b1f8b0d120bcacb/libavplugin-54.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libavplugin-55.so": { + "downloads": { + "lzma": { + "sha1": "39ee8e7fe14f0010c78973962800f539c3e4c16b", + "size": 15168, + "url": "https://launcher.mojang.com/v1/objects/39ee8e7fe14f0010c78973962800f539c3e4c16b/libavplugin-55.so" + }, + "raw": { + "sha1": "f10ea4ea3489e96d8d161a96790133c417ec44e1", + "size": 51784, + "url": "https://launcher.mojang.com/v1/objects/f10ea4ea3489e96d8d161a96790133c417ec44e1/libavplugin-55.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libavplugin-56.so": { + "downloads": { + "lzma": { + "sha1": "abe7feced5a559f1bdc868526dc69484e0e591a0", + "size": 15169, + "url": "https://launcher.mojang.com/v1/objects/abe7feced5a559f1bdc868526dc69484e0e591a0/libavplugin-56.so" + }, + "raw": { + "sha1": "e5bfcbff5a5a5a5993a3e689a05ef358c131a3ed", + "size": 51784, + "url": "https://launcher.mojang.com/v1/objects/e5bfcbff5a5a5a5993a3e689a05ef358c131a3ed/libavplugin-56.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libavplugin-57.so": { + "downloads": { + "lzma": { + "sha1": "4dd26b4ef2294b6929dcb2c7546b47eac5cc78a9", + "size": 15174, + "url": "https://launcher.mojang.com/v1/objects/4dd26b4ef2294b6929dcb2c7546b47eac5cc78a9/libavplugin-57.so" + }, + "raw": { + "sha1": "2949e7ff9b0ac90e8943c211cff141ab12eec3f8", + "size": 51784, + "url": "https://launcher.mojang.com/v1/objects/2949e7ff9b0ac90e8943c211cff141ab12eec3f8/libavplugin-57.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libavplugin-ffmpeg-56.so": { + "downloads": { + "lzma": { + "sha1": "c688ba1cfa442bf18bee43b2fa870b4dc1ce3fb6", + "size": 15231, + "url": "https://launcher.mojang.com/v1/objects/c688ba1cfa442bf18bee43b2fa870b4dc1ce3fb6/libavplugin-ffmpeg-56.so" + }, + "raw": { + "sha1": "0d36c971a9ad99fc2292092fdec3a4179b1021b9", + "size": 51920, + "url": "https://launcher.mojang.com/v1/objects/0d36c971a9ad99fc2292092fdec3a4179b1021b9/libavplugin-ffmpeg-56.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libavplugin-ffmpeg-57.so": { + "downloads": { + "lzma": { + "sha1": "087426bdbffebcfa372a438e863785f4ffbe9a6b", + "size": 15180, + "url": "https://launcher.mojang.com/v1/objects/087426bdbffebcfa372a438e863785f4ffbe9a6b/libavplugin-ffmpeg-57.so" + }, + "raw": { + "sha1": "5e9c4eb4b49eb8e57c01003ec73a1eb8d6d8c462", + "size": 51784, + "url": "https://launcher.mojang.com/v1/objects/5e9c4eb4b49eb8e57c01003ec73a1eb8d6d8c462/libavplugin-ffmpeg-57.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libawt.so": { + "downloads": { + "lzma": { + "sha1": "018be58b205b73c842a55df811b70d0e8237216e", + "size": 195720, + "url": "https://launcher.mojang.com/v1/objects/018be58b205b73c842a55df811b70d0e8237216e/libawt.so" + }, + "raw": { + "sha1": "02632cd326e3161c00a7e784599dd7b9ee053dce", + "size": 759184, + "url": "https://launcher.mojang.com/v1/objects/02632cd326e3161c00a7e784599dd7b9ee053dce/libawt.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libawt_headless.so": { + "downloads": { + "lzma": { + "sha1": "7ac2517cff75d4bbb0a0412a9b5f18c74ea188fa", + "size": 11211, + "url": "https://launcher.mojang.com/v1/objects/7ac2517cff75d4bbb0a0412a9b5f18c74ea188fa/libawt_headless.so" + }, + "raw": { + "sha1": "862157ec957008d0911c5daedc004b3a202623a4", + "size": 39176, + "url": "https://launcher.mojang.com/v1/objects/862157ec957008d0911c5daedc004b3a202623a4/libawt_headless.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libawt_xawt.so": { + "downloads": { + "lzma": { + "sha1": "d536a96af27dfe35de6bb2c8759d51c488cdd8d4", + "size": 149598, + "url": "https://launcher.mojang.com/v1/objects/d536a96af27dfe35de6bb2c8759d51c488cdd8d4/libawt_xawt.so" + }, + "raw": { + "sha1": "28232b3e01b6f11bfe098bfc6eafc3a513dcebf1", + "size": 470232, + "url": "https://launcher.mojang.com/v1/objects/28232b3e01b6f11bfe098bfc6eafc3a513dcebf1/libawt_xawt.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libbci.so": { + "downloads": { + "lzma": { + "sha1": "c36fad091d11e64c815d5ca17c0ef7a55b0776b1", + "size": 3509, + "url": "https://launcher.mojang.com/v1/objects/c36fad091d11e64c815d5ca17c0ef7a55b0776b1/libbci.so" + }, + "raw": { + "sha1": "33824051db1ccb6332e22c2b63231055240d0af0", + "size": 12760, + "url": "https://launcher.mojang.com/v1/objects/33824051db1ccb6332e22c2b63231055240d0af0/libbci.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libdcpr.so": { + "downloads": { + "lzma": { + "sha1": "70c6b0933a37f2b1124d6e7c131039241fe796ee", + "size": 75969, + "url": "https://launcher.mojang.com/v1/objects/70c6b0933a37f2b1124d6e7c131039241fe796ee/libdcpr.so" + }, + "raw": { + "sha1": "fa7001bc5d80579e2716590f3eee8027da0beae7", + "size": 204456, + "url": "https://launcher.mojang.com/v1/objects/fa7001bc5d80579e2716590f3eee8027da0beae7/libdcpr.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libdecora_sse.so": { + "downloads": { + "lzma": { + "sha1": "514acc017dfb6cefaf8cc6d18006ce55781cc9bc", + "size": 24397, + "url": "https://launcher.mojang.com/v1/objects/514acc017dfb6cefaf8cc6d18006ce55781cc9bc/libdecora_sse.so" + }, + "raw": { + "sha1": "d0c84233504c916e548e29f513e25f6a7479abfc", + "size": 74912, + "url": "https://launcher.mojang.com/v1/objects/d0c84233504c916e548e29f513e25f6a7479abfc/libdecora_sse.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libdeploy.so": { + "downloads": { + "lzma": { + "sha1": "6cf31fd98301c749ac0d2c7825f6d925a4409760", + "size": 168999, + "url": "https://launcher.mojang.com/v1/objects/6cf31fd98301c749ac0d2c7825f6d925a4409760/libdeploy.so" + }, + "raw": { + "sha1": "b3832e97ed8ca794884b56a591b83d02a2c0c06f", + "size": 642368, + "url": "https://launcher.mojang.com/v1/objects/b3832e97ed8ca794884b56a591b83d02a2c0c06f/libdeploy.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libdt_socket.so": { + "downloads": { + "lzma": { + "sha1": "4cc5c880dbb6fa180436d12d60f0abec8ebb59dc", + "size": 7784, + "url": "https://launcher.mojang.com/v1/objects/4cc5c880dbb6fa180436d12d60f0abec8ebb59dc/libdt_socket.so" + }, + "raw": { + "sha1": "91ce96f252b8139fc12f0f224ed5b1a041767ab7", + "size": 24616, + "url": "https://launcher.mojang.com/v1/objects/91ce96f252b8139fc12f0f224ed5b1a041767ab7/libdt_socket.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libfontmanager.so": { + "downloads": { + "lzma": { + "sha1": "f94e5e94c71c603ff4d3cd1e7e3d9e181fcc145d", + "size": 146951, + "url": "https://launcher.mojang.com/v1/objects/f94e5e94c71c603ff4d3cd1e7e3d9e181fcc145d/libfontmanager.so" + }, + "raw": { + "sha1": "2428e805f2c53d1283a033dfd11a86fbb7bd7159", + "size": 490672, + "url": "https://launcher.mojang.com/v1/objects/2428e805f2c53d1283a033dfd11a86fbb7bd7159/libfontmanager.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libfxplugins.so": { + "downloads": { + "lzma": { + "sha1": "a640143365d382a5ad743a784bc2f3706d9d6d67", + "size": 50048, + "url": "https://launcher.mojang.com/v1/objects/a640143365d382a5ad743a784bc2f3706d9d6d67/libfxplugins.so" + }, + "raw": { + "sha1": "0fd4ac04a84c131f1aaee9e6b0898ff9ea69e3ee", + "size": 151448, + "url": "https://launcher.mojang.com/v1/objects/0fd4ac04a84c131f1aaee9e6b0898ff9ea69e3ee/libfxplugins.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libglass.so": { + "downloads": { + "lzma": { + "sha1": "f1ff517714fa5f2c861f33b32db823fe851541f1", + "size": 2856, + "url": "https://launcher.mojang.com/v1/objects/f1ff517714fa5f2c861f33b32db823fe851541f1/libglass.so" + }, + "raw": { + "sha1": "e7f4fece30ac727be8148d33b8256abd3a41cef9", + "size": 13072, + "url": "https://launcher.mojang.com/v1/objects/e7f4fece30ac727be8148d33b8256abd3a41cef9/libglass.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libglassgtk2.so": { + "downloads": { + "lzma": { + "sha1": "15b90f7a2baacd15e80aa9785d87cf1e4258376d", + "size": 220476, + "url": "https://launcher.mojang.com/v1/objects/15b90f7a2baacd15e80aa9785d87cf1e4258376d/libglassgtk2.so" + }, + "raw": { + "sha1": "e30a634c2ff2143bdee512360553d6e0304f33b2", + "size": 844984, + "url": "https://launcher.mojang.com/v1/objects/e30a634c2ff2143bdee512360553d6e0304f33b2/libglassgtk2.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libglassgtk3.so": { + "downloads": { + "lzma": { + "sha1": "868c231165f8c9043b7f0e7de208ec023f06a6e7", + "size": 220560, + "url": "https://launcher.mojang.com/v1/objects/868c231165f8c9043b7f0e7de208ec023f06a6e7/libglassgtk3.so" + }, + "raw": { + "sha1": "762a11a2b376b7b5a2a7cad780715524fdd176d5", + "size": 845304, + "url": "https://launcher.mojang.com/v1/objects/762a11a2b376b7b5a2a7cad780715524fdd176d5/libglassgtk3.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libglib-lite.so": { + "downloads": { + "lzma": { + "sha1": "61b8871242febe1be262de167dc20ae94bf964b4", + "size": 457046, + "url": "https://launcher.mojang.com/v1/objects/61b8871242febe1be262de167dc20ae94bf964b4/libglib-lite.so" + }, + "raw": { + "sha1": "63afa060fc3f120af76128e51d32603fc4336fa8", + "size": 1538352, + "url": "https://launcher.mojang.com/v1/objects/63afa060fc3f120af76128e51d32603fc4336fa8/libglib-lite.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libgstreamer-lite.so": { + "downloads": { + "lzma": { + "sha1": "2447dc368406ba1b989a29937d41924620e01988", + "size": 673056, + "url": "https://launcher.mojang.com/v1/objects/2447dc368406ba1b989a29937d41924620e01988/libgstreamer-lite.so" + }, + "raw": { + "sha1": "5505e7ca592ac64371d3db8fe53bcb602e9723d3", + "size": 2263872, + "url": "https://launcher.mojang.com/v1/objects/5505e7ca592ac64371d3db8fe53bcb602e9723d3/libgstreamer-lite.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libhprof.so": { + "downloads": { + "lzma": { + "sha1": "94a5589c818db1fb1cf1881e24e217c309fce2e4", + "size": 64471, + "url": "https://launcher.mojang.com/v1/objects/94a5589c818db1fb1cf1881e24e217c309fce2e4/libhprof.so" + }, + "raw": { + "sha1": "4bb9bdeef6133b6dd558d52d691b077c03e9b0ee", + "size": 175504, + "url": "https://launcher.mojang.com/v1/objects/4bb9bdeef6133b6dd558d52d691b077c03e9b0ee/libhprof.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libinstrument.so": { + "downloads": { + "lzma": { + "sha1": "84ffea356caf725b42c86a8ebc9587f477ddde29", + "size": 18603, + "url": "https://launcher.mojang.com/v1/objects/84ffea356caf725b42c86a8ebc9587f477ddde29/libinstrument.so" + }, + "raw": { + "sha1": "cb8009769601e3fecd7ea2b36c344f737b1a9da7", + "size": 51560, + "url": "https://launcher.mojang.com/v1/objects/cb8009769601e3fecd7ea2b36c344f737b1a9da7/libinstrument.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libj2gss.so": { + "downloads": { + "lzma": { + "sha1": "4b2aa699506b126098b585a9617ce1c05707fa29", + "size": 14132, + "url": "https://launcher.mojang.com/v1/objects/4b2aa699506b126098b585a9617ce1c05707fa29/libj2gss.so" + }, + "raw": { + "sha1": "cbce4a302b255d4d1924ef7606f038af766c5e86", + "size": 47688, + "url": "https://launcher.mojang.com/v1/objects/cbce4a302b255d4d1924ef7606f038af766c5e86/libj2gss.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libj2pcsc.so": { + "downloads": { + "lzma": { + "sha1": "2361d3b2e3da48593c391b29b0d2b5409e4c55e5", + "size": 5074, + "url": "https://launcher.mojang.com/v1/objects/2361d3b2e3da48593c391b29b0d2b5409e4c55e5/libj2pcsc.so" + }, + "raw": { + "sha1": "1274178492e7a3e997e12f67794616f7c3d8d0b9", + "size": 18296, + "url": "https://launcher.mojang.com/v1/objects/1274178492e7a3e997e12f67794616f7c3d8d0b9/libj2pcsc.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libj2pkcs11.so": { + "downloads": { + "lzma": { + "sha1": "ef927e2790ba05931d0f0bdd63da3d275a834946", + "size": 21573, + "url": "https://launcher.mojang.com/v1/objects/ef927e2790ba05931d0f0bdd63da3d275a834946/libj2pkcs11.so" + }, + "raw": { + "sha1": "bd4f2af9bfdc6168633d1920c1a1415de06bb45a", + "size": 79472, + "url": "https://launcher.mojang.com/v1/objects/bd4f2af9bfdc6168633d1920c1a1415de06bb45a/libj2pkcs11.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjaas_unix.so": { + "downloads": { + "lzma": { + "sha1": "7f7e843544ee1eb1454a5826bdd4218685b79430", + "size": 2404, + "url": "https://launcher.mojang.com/v1/objects/7f7e843544ee1eb1454a5826bdd4218685b79430/libjaas_unix.so" + }, + "raw": { + "sha1": "4c517925c7d464a5b719898eb0bea1b04df31f1f", + "size": 8192, + "url": "https://launcher.mojang.com/v1/objects/4c517925c7d464a5b719898eb0bea1b04df31f1f/libjaas_unix.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjava.so": { + "downloads": { + "lzma": { + "sha1": "5eee7a42600a44a8bb8d6d7f510fd96a29637ac0", + "size": 63113, + "url": "https://launcher.mojang.com/v1/objects/5eee7a42600a44a8bb8d6d7f510fd96a29637ac0/libjava.so" + }, + "raw": { + "sha1": "e280aeddf3fc0ec664aef7efc0e0e197a54aaf02", + "size": 227672, + "url": "https://launcher.mojang.com/v1/objects/e280aeddf3fc0ec664aef7efc0e0e197a54aaf02/libjava.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjava_crw_demo.so": { + "downloads": { + "lzma": { + "sha1": "b197cf23ae3556eb0b45c663f0a8cb62408b961e", + "size": 10412, + "url": "https://launcher.mojang.com/v1/objects/b197cf23ae3556eb0b45c663f0a8cb62408b961e/libjava_crw_demo.so" + }, + "raw": { + "sha1": "18f20f906977c90d0090b41dbda8dd5cfead5a4c", + "size": 26144, + "url": "https://launcher.mojang.com/v1/objects/18f20f906977c90d0090b41dbda8dd5cfead5a4c/libjava_crw_demo.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjavafx_font.so": { + "downloads": { + "lzma": { + "sha1": "ffbba0e5022f829412b86063d8a90f95f16709b1", + "size": 5608, + "url": "https://launcher.mojang.com/v1/objects/ffbba0e5022f829412b86063d8a90f95f16709b1/libjavafx_font.so" + }, + "raw": { + "sha1": "8634a0aca612fc40420a4a7cc8af4cc46cfc6725", + "size": 17104, + "url": "https://launcher.mojang.com/v1/objects/8634a0aca612fc40420a4a7cc8af4cc46cfc6725/libjavafx_font.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjavafx_font_freetype.so": { + "downloads": { + "lzma": { + "sha1": "310271eda8a2ac264ffc3640a9d847b49438d0bd", + "size": 6942, + "url": "https://launcher.mojang.com/v1/objects/310271eda8a2ac264ffc3640a9d847b49438d0bd/libjavafx_font_freetype.so" + }, + "raw": { + "sha1": "3e7572d047c12ba2bc43acec7f98a67c20af8042", + "size": 27616, + "url": "https://launcher.mojang.com/v1/objects/3e7572d047c12ba2bc43acec7f98a67c20af8042/libjavafx_font_freetype.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjavafx_font_pango.so": { + "downloads": { + "lzma": { + "sha1": "a7bcf0669e70b0f43099a99c81e6b6440cb40ac0", + "size": 5820, + "url": "https://launcher.mojang.com/v1/objects/a7bcf0669e70b0f43099a99c81e6b6440cb40ac0/libjavafx_font_pango.so" + }, + "raw": { + "sha1": "f0b775cc9a514c7ee8b4d6fb300653ce548caf10", + "size": 25560, + "url": "https://launcher.mojang.com/v1/objects/f0b775cc9a514c7ee8b4d6fb300653ce548caf10/libjavafx_font_pango.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjavafx_font_t2k.so": { + "downloads": { + "lzma": { + "sha1": "551c29dc7c7fc83223aa36a6187f7e0c5d650538", + "size": 431450, + "url": "https://launcher.mojang.com/v1/objects/551c29dc7c7fc83223aa36a6187f7e0c5d650538/libjavafx_font_t2k.so" + }, + "raw": { + "sha1": "91e5813057c3b852d411540160f8ad05fb9f1ed3", + "size": 1486128, + "url": "https://launcher.mojang.com/v1/objects/91e5813057c3b852d411540160f8ad05fb9f1ed3/libjavafx_font_t2k.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjavafx_iio.so": { + "downloads": { + "lzma": { + "sha1": "c832998fd5e06ed6dcd6428816194c350785420c", + "size": 101479, + "url": "https://launcher.mojang.com/v1/objects/c832998fd5e06ed6dcd6428816194c350785420c/libjavafx_iio.so" + }, + "raw": { + "sha1": "dcdf68cb25677b76c1cf0bb94294e6e9880a6678", + "size": 256336, + "url": "https://launcher.mojang.com/v1/objects/dcdf68cb25677b76c1cf0bb94294e6e9880a6678/libjavafx_iio.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjawt.so": { + "downloads": { + "lzma": { + "sha1": "c1ced6aad5c69ff444dc67d0fd7e333558953831", + "size": 1872, + "url": "https://launcher.mojang.com/v1/objects/c1ced6aad5c69ff444dc67d0fd7e333558953831/libjawt.so" + }, + "raw": { + "sha1": "c5032f2c6fa40bea24e56605cf76b26a27e87b67", + "size": 8048, + "url": "https://launcher.mojang.com/v1/objects/c5032f2c6fa40bea24e56605cf76b26a27e87b67/libjawt.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjdwp.so": { + "downloads": { + "lzma": { + "sha1": "c1aabbb3f5a624b9ad10ed871a1d83510a99b646", + "size": 94884, + "url": "https://launcher.mojang.com/v1/objects/c1aabbb3f5a624b9ad10ed871a1d83510a99b646/libjdwp.so" + }, + "raw": { + "sha1": "a043e97be47937f6f552e94cf79c76c1c57f9594", + "size": 272248, + "url": "https://launcher.mojang.com/v1/objects/a043e97be47937f6f552e94cf79c76c1c57f9594/libjdwp.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjfr.so": { + "downloads": { + "lzma": { + "sha1": "11b8e6bfffdccbacbf9dd29dea4b90b753f3c1b7", + "size": 8780, + "url": "https://launcher.mojang.com/v1/objects/11b8e6bfffdccbacbf9dd29dea4b90b753f3c1b7/libjfr.so" + }, + "raw": { + "sha1": "312392dd186b11c418183e818f1928e8685a07e5", + "size": 28384, + "url": "https://launcher.mojang.com/v1/objects/312392dd186b11c418183e818f1928e8685a07e5/libjfr.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjfxmedia.so": { + "downloads": { + "lzma": { + "sha1": "a4e7a126eb648ce6e5e6dc151831da37d8334139", + "size": 391897, + "url": "https://launcher.mojang.com/v1/objects/a4e7a126eb648ce6e5e6dc151831da37d8334139/libjfxmedia.so" + }, + "raw": { + "sha1": "5fa54944327a6012c3d34cb5c1c4432762178dc8", + "size": 1636376, + "url": "https://launcher.mojang.com/v1/objects/5fa54944327a6012c3d34cb5c1c4432762178dc8/libjfxmedia.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjfxwebkit.so": { + "downloads": { + "lzma": { + "sha1": "b274debd222cdcc2ee84160ebb95144b3880bc97", + "size": 20492825, + "url": "https://launcher.mojang.com/v1/objects/b274debd222cdcc2ee84160ebb95144b3880bc97/libjfxwebkit.so" + }, + "raw": { + "sha1": "ecee564c3b2f645131b35bb3004abd4caeabd291", + "size": 91014584, + "url": "https://launcher.mojang.com/v1/objects/ecee564c3b2f645131b35bb3004abd4caeabd291/libjfxwebkit.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjpeg.so": { + "downloads": { + "lzma": { + "sha1": "9ad55e370c5eaaa73c3158339db3c368b1aaf0cb", + "size": 113072, + "url": "https://launcher.mojang.com/v1/objects/9ad55e370c5eaaa73c3158339db3c368b1aaf0cb/libjpeg.so" + }, + "raw": { + "sha1": "651e6d53ae67db1f0efbf7f104447a9b49b7e333", + "size": 292520, + "url": "https://launcher.mojang.com/v1/objects/651e6d53ae67db1f0efbf7f104447a9b49b7e333/libjpeg.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjsdt.so": { + "downloads": { + "lzma": { + "sha1": "04b6d1361a34c496b5f652b2477784d69b8b6baf", + "size": 3964, + "url": "https://launcher.mojang.com/v1/objects/04b6d1361a34c496b5f652b2477784d69b8b6baf/libjsdt.so" + }, + "raw": { + "sha1": "82b48a82bf6183d34cf00a0f81661b45c616f31b", + "size": 12904, + "url": "https://launcher.mojang.com/v1/objects/82b48a82bf6183d34cf00a0f81661b45c616f31b/libjsdt.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjsig.so": { + "downloads": { + "lzma": { + "sha1": "37d3b89abde397216cc4ecb1339d8543d99b8428", + "size": 3536, + "url": "https://launcher.mojang.com/v1/objects/37d3b89abde397216cc4ecb1339d8543d99b8428/libjsig.so" + }, + "raw": { + "sha1": "42e52ba1bcbe0362ab24bcf65c93797354db6fb9", + "size": 13336, + "url": "https://launcher.mojang.com/v1/objects/42e52ba1bcbe0362ab24bcf65c93797354db6fb9/libjsig.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjsound.so": { + "downloads": { + "lzma": { + "sha1": "7e3c565d74d8ffae716f32b05544fa4a6f108adc", + "size": 2002, + "url": "https://launcher.mojang.com/v1/objects/7e3c565d74d8ffae716f32b05544fa4a6f108adc/libjsound.so" + }, + "raw": { + "sha1": "0c0fc63b92d7b83c9960fa80d45c80553ea20254", + "size": 8232, + "url": "https://launcher.mojang.com/v1/objects/0c0fc63b92d7b83c9960fa80d45c80553ea20254/libjsound.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libjsoundalsa.so": { + "downloads": { + "lzma": { + "sha1": "b06c51858a25ff776519495f1b9b3d9f604b089f", + "size": 23097, + "url": "https://launcher.mojang.com/v1/objects/b06c51858a25ff776519495f1b9b3d9f604b089f/libjsoundalsa.so" + }, + "raw": { + "sha1": "281d37f0326d4a12dc7ea316ead09c198ff7bdf7", + "size": 83256, + "url": "https://launcher.mojang.com/v1/objects/281d37f0326d4a12dc7ea316ead09c198ff7bdf7/libjsoundalsa.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/liblcms.so": { + "downloads": { + "lzma": { + "sha1": "7a239baba2086cae49114b382b74b971da02f08e", + "size": 176175, + "url": "https://launcher.mojang.com/v1/objects/7a239baba2086cae49114b382b74b971da02f08e/liblcms.so" + }, + "raw": { + "sha1": "c8895cc3c3d023d9e059225969ab67954772c0a1", + "size": 526872, + "url": "https://launcher.mojang.com/v1/objects/c8895cc3c3d023d9e059225969ab67954772c0a1/liblcms.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libmanagement.so": { + "downloads": { + "lzma": { + "sha1": "aed3fdbcefd1716abfc6a306687c8b741cbb318e", + "size": 12838, + "url": "https://launcher.mojang.com/v1/objects/aed3fdbcefd1716abfc6a306687c8b741cbb318e/libmanagement.so" + }, + "raw": { + "sha1": "eba35f61e0d50e30874b7c7b335edf2d52662423", + "size": 51808, + "url": "https://launcher.mojang.com/v1/objects/eba35f61e0d50e30874b7c7b335edf2d52662423/libmanagement.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libmlib_image.so": { + "downloads": { + "lzma": { + "sha1": "1bb181f079492d55c7a458e96488cd17fe0a7b86", + "size": 310272, + "url": "https://launcher.mojang.com/v1/objects/1bb181f079492d55c7a458e96488cd17fe0a7b86/libmlib_image.so" + }, + "raw": { + "sha1": "c973c450d33873675945d4694be484e3427f58f1", + "size": 1048136, + "url": "https://launcher.mojang.com/v1/objects/c973c450d33873675945d4694be484e3427f58f1/libmlib_image.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libnet.so": { + "downloads": { + "lzma": { + "sha1": "9dd79703b6deb86e0321afe01c6ac508263c8312", + "size": 38123, + "url": "https://launcher.mojang.com/v1/objects/9dd79703b6deb86e0321afe01c6ac508263c8312/libnet.so" + }, + "raw": { + "sha1": "b3a17b7d53fcdf1e689e1ec29ce851eee6022ead", + "size": 116920, + "url": "https://launcher.mojang.com/v1/objects/b3a17b7d53fcdf1e689e1ec29ce851eee6022ead/libnet.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libnio.so": { + "downloads": { + "lzma": { + "sha1": "5697c89d5d5d9b74f2e1555fcbba79dd4049e287", + "size": 24445, + "url": "https://launcher.mojang.com/v1/objects/5697c89d5d5d9b74f2e1555fcbba79dd4049e287/libnio.so" + }, + "raw": { + "sha1": "573bf8f64dbcc397f8abd3e1da28f90ab0679f5b", + "size": 93872, + "url": "https://launcher.mojang.com/v1/objects/573bf8f64dbcc397f8abd3e1da28f90ab0679f5b/libnio.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libnpjp2.so": { + "downloads": { + "lzma": { + "sha1": "6fe53b5951ff740e7f2ef7ffe5975af26da06718", + "size": 57892, + "url": "https://launcher.mojang.com/v1/objects/6fe53b5951ff740e7f2ef7ffe5975af26da06718/libnpjp2.so" + }, + "raw": { + "sha1": "2bb13c53a4280379253475e51216b97eed1d4ce3", + "size": 216592, + "url": "https://launcher.mojang.com/v1/objects/2bb13c53a4280379253475e51216b97eed1d4ce3/libnpjp2.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libnpt.so": { + "downloads": { + "lzma": { + "sha1": "1b170b09a32b1b8b6624fa5d1f94ec60b2bf3876", + "size": 5070, + "url": "https://launcher.mojang.com/v1/objects/1b170b09a32b1b8b6624fa5d1f94ec60b2bf3876/libnpt.so" + }, + "raw": { + "sha1": "6b1ff6b9b4624f3cc7801f221c82b8046fb76364", + "size": 17504, + "url": "https://launcher.mojang.com/v1/objects/6b1ff6b9b4624f3cc7801f221c82b8046fb76364/libnpt.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libprism_common.so": { + "downloads": { + "lzma": { + "sha1": "f4aca04c90bc7505851c074a08b2c31cae1f94fa", + "size": 23315, + "url": "https://launcher.mojang.com/v1/objects/f4aca04c90bc7505851c074a08b2c31cae1f94fa/libprism_common.so" + }, + "raw": { + "sha1": "b00866b6ed8646a29a334d46e297267552f27de8", + "size": 59008, + "url": "https://launcher.mojang.com/v1/objects/b00866b6ed8646a29a334d46e297267552f27de8/libprism_common.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libprism_es2.so": { + "downloads": { + "lzma": { + "sha1": "7ff4173c338c7a6f370f231670055737e032da3e", + "size": 18416, + "url": "https://launcher.mojang.com/v1/objects/7ff4173c338c7a6f370f231670055737e032da3e/libprism_es2.so" + }, + "raw": { + "sha1": "1390a1dc14345e5a948148e59195d62f3a83863f", + "size": 63808, + "url": "https://launcher.mojang.com/v1/objects/1390a1dc14345e5a948148e59195d62f3a83863f/libprism_es2.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libprism_sw.so": { + "downloads": { + "lzma": { + "sha1": "6728e8bf7b214067d715be6fe0325910d63c2468", + "size": 29457, + "url": "https://launcher.mojang.com/v1/objects/6728e8bf7b214067d715be6fe0325910d63c2468/libprism_sw.so" + }, + "raw": { + "sha1": "7a6c34cb2bbcde411778d1b3f8677c39e32c3ac4", + "size": 71608, + "url": "https://launcher.mojang.com/v1/objects/7a6c34cb2bbcde411778d1b3f8677c39e32c3ac4/libprism_sw.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libresource.so": { + "downloads": { + "lzma": { + "sha1": "1e35e63f1e74915fba620f1febf420b919d49bc5", + "size": 2633, + "url": "https://launcher.mojang.com/v1/objects/1e35e63f1e74915fba620f1febf420b919d49bc5/libresource.so" + }, + "raw": { + "sha1": "57490353ad0d83ab1930355213dea269795434fe", + "size": 13456, + "url": "https://launcher.mojang.com/v1/objects/57490353ad0d83ab1930355213dea269795434fe/libresource.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libsctp.so": { + "downloads": { + "lzma": { + "sha1": "4340132ed250d7849a016e071be773eaedd33aa8", + "size": 9332, + "url": "https://launcher.mojang.com/v1/objects/4340132ed250d7849a016e071be773eaedd33aa8/libsctp.so" + }, + "raw": { + "sha1": "4a80e743750f127c0d5a359f5cd60b97e7ee12ae", + "size": 29552, + "url": "https://launcher.mojang.com/v1/objects/4a80e743750f127c0d5a359f5cd60b97e7ee12ae/libsctp.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libsplashscreen.so": { + "downloads": { + "lzma": { + "sha1": "b226c8dbd73a548fc2b042ee6db6cc80e727597c", + "size": 190305, + "url": "https://launcher.mojang.com/v1/objects/b226c8dbd73a548fc2b042ee6db6cc80e727597c/libsplashscreen.so" + }, + "raw": { + "sha1": "87d6a491f5ba7e6c4d972264a0c9063afea567a2", + "size": 441376, + "url": "https://launcher.mojang.com/v1/objects/87d6a491f5ba7e6c4d972264a0c9063afea567a2/libsplashscreen.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libsunec.so": { + "downloads": { + "lzma": { + "sha1": "6ebba98fab1e3d872d1363235b76497f6f9babdc", + "size": 88829, + "url": "https://launcher.mojang.com/v1/objects/6ebba98fab1e3d872d1363235b76497f6f9babdc/libsunec.so" + }, + "raw": { + "sha1": "3b262a0a530f6e4e539aed2cd27b4de1d0ed8859", + "size": 283368, + "url": "https://launcher.mojang.com/v1/objects/3b262a0a530f6e4e539aed2cd27b4de1d0ed8859/libsunec.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libt2k.so": { + "downloads": { + "lzma": { + "sha1": "602cb812ef0b350ccf56ce209a260ddbe3743d92", + "size": 190720, + "url": "https://launcher.mojang.com/v1/objects/602cb812ef0b350ccf56ce209a260ddbe3743d92/libt2k.so" + }, + "raw": { + "sha1": "b072c56df997f61e15e6b5a43b8907b0d25c2043", + "size": 504840, + "url": "https://launcher.mojang.com/v1/objects/b072c56df997f61e15e6b5a43b8907b0d25c2043/libt2k.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libunpack.so": { + "downloads": { + "lzma": { + "sha1": "7107b615e941074f0b14c31c88fb67200aacd37f", + "size": 70308, + "url": "https://launcher.mojang.com/v1/objects/7107b615e941074f0b14c31c88fb67200aacd37f/libunpack.so" + }, + "raw": { + "sha1": "b05ff862ed87928ed91e80e5604673c5ea710a53", + "size": 197712, + "url": "https://launcher.mojang.com/v1/objects/b05ff862ed87928ed91e80e5604673c5ea710a53/libunpack.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libverify.so": { + "downloads": { + "lzma": { + "sha1": "ecd98efb8c7da441a8c3580e8f5598f3cb4165b1", + "size": 21885, + "url": "https://launcher.mojang.com/v1/objects/ecd98efb8c7da441a8c3580e8f5598f3cb4165b1/libverify.so" + }, + "raw": { + "sha1": "e2c8d92531c45ab9be69ffb72c87fa12e9e59827", + "size": 66112, + "url": "https://launcher.mojang.com/v1/objects/e2c8d92531c45ab9be69ffb72c87fa12e9e59827/libverify.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/libzip.so": { + "downloads": { + "lzma": { + "sha1": "7c562342e3f7b138dc978495447e3e6a96c2cf45", + "size": 54876, + "url": "https://launcher.mojang.com/v1/objects/7c562342e3f7b138dc978495447e3e6a96c2cf45/libzip.so" + }, + "raw": { + "sha1": "5f4bf35a5c3e8f8c650e891d1031589b8ab6d77f", + "size": 127016, + "url": "https://launcher.mojang.com/v1/objects/5f4bf35a5c3e8f8c650e891d1031589b8ab6d77f/libzip.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/amd64/server": { + "type": "directory" + }, + "lib/amd64/server/Xusage.txt": { + "downloads": { + "lzma": { + "sha1": "acb2da24a4c765887df83985e4c26d6be302a0a3", + "size": 629, + "url": "https://launcher.mojang.com/v1/objects/acb2da24a4c765887df83985e4c26d6be302a0a3/Xusage.txt" + }, + "raw": { + "sha1": "6983727eafe140f9dd793c78aa6f3e007438243a", + "size": 1423, + "url": "https://launcher.mojang.com/v1/objects/6983727eafe140f9dd793c78aa6f3e007438243a/Xusage.txt" + } + }, + "executable": false, + "type": "file" + }, + "lib/amd64/server/libjsig.so": { + "target": "../libjsig.so", + "type": "link" + }, + "lib/amd64/server/libjvm.so": { + "downloads": { + "lzma": { + "sha1": "d5c6f3338aaa6712f79d680ac8c3e31beebaa886", + "size": 4154311, + "url": "https://launcher.mojang.com/v1/objects/d5c6f3338aaa6712f79d680ac8c3e31beebaa886/libjvm.so" + }, + "raw": { + "sha1": "23a98e1eb505cc3fb91bc0cb2adb71ab9270e9ca", + "size": 17045016, + "url": "https://launcher.mojang.com/v1/objects/23a98e1eb505cc3fb91bc0cb2adb71ab9270e9ca/libjvm.so" + } + }, + "executable": true, + "type": "file" + }, + "lib/applet": { + "type": "directory" + }, + "lib/calendars.properties": { + "downloads": { + "lzma": { + "sha1": "4a757c23f2942bd802a4f80235131146d9267750", + "size": 558, + "url": "https://launcher.mojang.com/v1/objects/4a757c23f2942bd802a4f80235131146d9267750/calendars.properties" + }, + "raw": { + "sha1": "42ebb0988124433b8f2a6e5d9a74ed41240bcfc6", + "size": 1378, + "url": "https://launcher.mojang.com/v1/objects/42ebb0988124433b8f2a6e5d9a74ed41240bcfc6/calendars.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/charsets.jar": { + "downloads": { + "lzma": { + "sha1": "2bf44143b2ad9d7d55045a4de4a562330c194dc0", + "size": 412367, + "url": "https://launcher.mojang.com/v1/objects/2bf44143b2ad9d7d55045a4de4a562330c194dc0/charsets.jar" + }, + "raw": { + "sha1": "d73ab9f8de255a7e112ddd13622bf7f6b18c8447", + "size": 3135615, + "url": "https://launcher.mojang.com/v1/objects/d73ab9f8de255a7e112ddd13622bf7f6b18c8447/charsets.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/classlist": { + "downloads": { + "lzma": { + "sha1": "14e7c73d21b8513b0aff8d86e5cb34c52021fbca", + "size": 15024, + "url": "https://launcher.mojang.com/v1/objects/14e7c73d21b8513b0aff8d86e5cb34c52021fbca/classlist" + }, + "raw": { + "sha1": "9c0404b63c87e2fed35e3a6cd137d6cf876c42bd", + "size": 84311, + "url": "https://launcher.mojang.com/v1/objects/9c0404b63c87e2fed35e3a6cd137d6cf876c42bd/classlist" + } + }, + "executable": false, + "type": "file" + }, + "lib/cmm": { + "type": "directory" + }, + "lib/cmm/CIEXYZ.pf": { + "downloads": { + "lzma": { + "sha1": "fcc5ca2fd3f45cac3434b480fa3ce00007e96529", + "size": 8964, + "url": "https://launcher.mojang.com/v1/objects/fcc5ca2fd3f45cac3434b480fa3ce00007e96529/CIEXYZ.pf" + }, + "raw": { + "sha1": "b7779924c70554647b87c2a86159ca7781e929f8", + "size": 51236, + "url": "https://launcher.mojang.com/v1/objects/b7779924c70554647b87c2a86159ca7781e929f8/CIEXYZ.pf" + } + }, + "executable": false, + "type": "file" + }, + "lib/cmm/GRAY.pf": { + "downloads": { + "lzma": { + "sha1": "5388ccfe67d3131d6d02143d8e8895003ab14ff6", + "size": 299, + "url": "https://launcher.mojang.com/v1/objects/5388ccfe67d3131d6d02143d8e8895003ab14ff6/GRAY.pf" + }, + "raw": { + "sha1": "27f93961d66b8230d0cdb8b166bc8b4153d5bc2d", + "size": 632, + "url": "https://launcher.mojang.com/v1/objects/27f93961d66b8230d0cdb8b166bc8b4153d5bc2d/GRAY.pf" + } + }, + "executable": false, + "type": "file" + }, + "lib/cmm/LINEAR_RGB.pf": { + "downloads": { + "lzma": { + "sha1": "2bd90f09c8deb64b1729d6b8173c78f9e9cab27b", + "size": 678, + "url": "https://launcher.mojang.com/v1/objects/2bd90f09c8deb64b1729d6b8173c78f9e9cab27b/LINEAR_RGB.pf" + }, + "raw": { + "sha1": "7913274c2f73bafcf888f09ff60990b100214ede", + "size": 1044, + "url": "https://launcher.mojang.com/v1/objects/7913274c2f73bafcf888f09ff60990b100214ede/LINEAR_RGB.pf" + } + }, + "executable": false, + "type": "file" + }, + "lib/cmm/PYCC.pf": { + "downloads": { + "lzma": { + "sha1": "dbb2197ecff3fcdd142e9006490c8cb5c8d19af8", + "size": 171521, + "url": "https://launcher.mojang.com/v1/objects/dbb2197ecff3fcdd142e9006490c8cb5c8d19af8/PYCC.pf" + }, + "raw": { + "sha1": "4f7eed05b8f0eea7bcdc8f8f7aaeb1925ce7b144", + "size": 274474, + "url": "https://launcher.mojang.com/v1/objects/4f7eed05b8f0eea7bcdc8f8f7aaeb1925ce7b144/PYCC.pf" + } + }, + "executable": false, + "type": "file" + }, + "lib/cmm/sRGB.pf": { + "downloads": { + "raw": { + "sha1": "9eaea0911d89d63e39e95f2e2116eaec7e0bb91e", + "size": 3144, + "url": "https://launcher.mojang.com/v1/objects/9eaea0911d89d63e39e95f2e2116eaec7e0bb91e/sRGB.pf" + } + }, + "executable": false, + "type": "file" + }, + "lib/content-types.properties": { + "downloads": { + "lzma": { + "sha1": "43a23d9a6c637c128b14cfa3feced93cbcf85b1a", + "size": 1617, + "url": "https://launcher.mojang.com/v1/objects/43a23d9a6c637c128b14cfa3feced93cbcf85b1a/content-types.properties" + }, + "raw": { + "sha1": "b21698017c4a2866b5fabe59681b7592e72c83b1", + "size": 5916, + "url": "https://launcher.mojang.com/v1/objects/b21698017c4a2866b5fabe59681b7592e72c83b1/content-types.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/currency.data": { + "downloads": { + "lzma": { + "sha1": "451b3f166ea34ef2aefbb01606ea5adcc0d65b42", + "size": 1184, + "url": "https://launcher.mojang.com/v1/objects/451b3f166ea34ef2aefbb01606ea5adcc0d65b42/currency.data" + }, + "raw": { + "sha1": "bf524381a7a9b9d5bbab48069c583d2936e367a1", + "size": 4134, + "url": "https://launcher.mojang.com/v1/objects/bf524381a7a9b9d5bbab48069c583d2936e367a1/currency.data" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy": { + "type": "directory" + }, + "lib/deploy.jar": { + "downloads": { + "raw": { + "sha1": "fbe1de8fcd9a3d482c59414dce9311e4194766c9", + "size": 2255881, + "url": "https://launcher.mojang.com/v1/objects/fbe1de8fcd9a3d482c59414dce9311e4194766c9/deploy.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/MixedCodeMainDialog.ui": { + "downloads": { + "lzma": { + "sha1": "7d812964343d1e978442f5c847c709784fc18fc0", + "size": 683, + "url": "https://launcher.mojang.com/v1/objects/7d812964343d1e978442f5c847c709784fc18fc0/MixedCodeMainDialog.ui" + }, + "raw": { + "sha1": "c9b1af1c229e54b2d8a3d642d4f0bb31dc15be79", + "size": 4507, + "url": "https://launcher.mojang.com/v1/objects/c9b1af1c229e54b2d8a3d642d4f0bb31dc15be79/MixedCodeMainDialog.ui" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/MixedCodeMainDialogJs.ui": { + "downloads": { + "lzma": { + "sha1": "54fb58dbcc59e35e0ae896d0e266ae0c5bcf85c2", + "size": 792, + "url": "https://launcher.mojang.com/v1/objects/54fb58dbcc59e35e0ae896d0e266ae0c5bcf85c2/MixedCodeMainDialogJs.ui" + }, + "raw": { + "sha1": "ad6337fb6d46750e14c12b439a5856f4b6864d0d", + "size": 6110, + "url": "https://launcher.mojang.com/v1/objects/ad6337fb6d46750e14c12b439a5856f4b6864d0d/MixedCodeMainDialogJs.ui" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/cautionshield.icns": { + "downloads": { + "lzma": { + "sha1": "7cea751dc168605054ec38ce8bfa71812be405c1", + "size": 2333, + "url": "https://launcher.mojang.com/v1/objects/7cea751dc168605054ec38ce8bfa71812be405c1/cautionshield.icns" + }, + "raw": { + "sha1": "1de7ed5d5fc75aa1bcede088c655bee3bde64c38", + "size": 3588, + "url": "https://launcher.mojang.com/v1/objects/1de7ed5d5fc75aa1bcede088c655bee3bde64c38/cautionshield.icns" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/ffjcext.zip": { + "downloads": { + "lzma": { + "sha1": "80bcb9b3794f69d87dba93e90230f288e651e798", + "size": 1809, + "url": "https://launcher.mojang.com/v1/objects/80bcb9b3794f69d87dba93e90230f288e651e798/ffjcext.zip" + }, + "raw": { + "sha1": "76d051ca7d3666ff25ea8eb9957a05574a45287f", + "size": 13454, + "url": "https://launcher.mojang.com/v1/objects/76d051ca7d3666ff25ea8eb9957a05574a45287f/ffjcext.zip" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/java-icon.ico": { + "downloads": { + "lzma": { + "sha1": "2a24f0207d7ab5976a8b0d92b4b381d49e895c9d", + "size": 8468, + "url": "https://launcher.mojang.com/v1/objects/2a24f0207d7ab5976a8b0d92b4b381d49e895c9d/java-icon.ico" + }, + "raw": { + "sha1": "2997ceb26ff49a7d7c5e7a2405b5fb50b62c7914", + "size": 29926, + "url": "https://launcher.mojang.com/v1/objects/2997ceb26ff49a7d7c5e7a2405b5fb50b62c7914/java-icon.ico" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/messages.properties": { + "downloads": { + "lzma": { + "sha1": "c1e16f80dc0b1f1a591cecf3cbab4ba5e47492f4", + "size": 1225, + "url": "https://launcher.mojang.com/v1/objects/c1e16f80dc0b1f1a591cecf3cbab4ba5e47492f4/messages.properties" + }, + "raw": { + "sha1": "dc52841c708e3c1eb2a044088a43396d1291bb5e", + "size": 2860, + "url": "https://launcher.mojang.com/v1/objects/dc52841c708e3c1eb2a044088a43396d1291bb5e/messages.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/messages_de.properties": { + "downloads": { + "lzma": { + "sha1": "42b42e6e1d2cb2d781f2226bde612ce519b29bc8", + "size": 1394, + "url": "https://launcher.mojang.com/v1/objects/42b42e6e1d2cb2d781f2226bde612ce519b29bc8/messages_de.properties" + }, + "raw": { + "sha1": "d989fe1b8f7904888d5102294ebefd28d932ecdb", + "size": 3306, + "url": "https://launcher.mojang.com/v1/objects/d989fe1b8f7904888d5102294ebefd28d932ecdb/messages_de.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/messages_es.properties": { + "downloads": { + "lzma": { + "sha1": "c4a653e9802ca982e892b45d88c1e259c09e8c8e", + "size": 1404, + "url": "https://launcher.mojang.com/v1/objects/c4a653e9802ca982e892b45d88c1e259c09e8c8e/messages_es.properties" + }, + "raw": { + "sha1": "1b0334b79db481c3a59be6915d5118d760c97baa", + "size": 3600, + "url": "https://launcher.mojang.com/v1/objects/1b0334b79db481c3a59be6915d5118d760c97baa/messages_es.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/messages_fr.properties": { + "downloads": { + "lzma": { + "sha1": "2d8dee07e3f5aab7318a22e169810b216ac44f97", + "size": 1401, + "url": "https://launcher.mojang.com/v1/objects/2d8dee07e3f5aab7318a22e169810b216ac44f97/messages_fr.properties" + }, + "raw": { + "sha1": "69bd2d03c2064f8679de5b4e430ea61b567c69c5", + "size": 3409, + "url": "https://launcher.mojang.com/v1/objects/69bd2d03c2064f8679de5b4e430ea61b567c69c5/messages_fr.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/messages_it.properties": { + "downloads": { + "lzma": { + "sha1": "7c28cdd8d9e34355ba0fc03004c4f64749cae57e", + "size": 1375, + "url": "https://launcher.mojang.com/v1/objects/7c28cdd8d9e34355ba0fc03004c4f64749cae57e/messages_it.properties" + }, + "raw": { + "sha1": "dbe49949308f28540a42ae6cd2ad58afbf615592", + "size": 3223, + "url": "https://launcher.mojang.com/v1/objects/dbe49949308f28540a42ae6cd2ad58afbf615592/messages_it.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/messages_ja.properties": { + "downloads": { + "lzma": { + "sha1": "9a6a4c086e48b9e615b72b6bbebb3c724d178ff4", + "size": 1680, + "url": "https://launcher.mojang.com/v1/objects/9a6a4c086e48b9e615b72b6bbebb3c724d178ff4/messages_ja.properties" + }, + "raw": { + "sha1": "751170a7cdefcb1226604ac3f8196e06a04fd7ac", + "size": 6349, + "url": "https://launcher.mojang.com/v1/objects/751170a7cdefcb1226604ac3f8196e06a04fd7ac/messages_ja.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/messages_ko.properties": { + "downloads": { + "lzma": { + "sha1": "0c57c2ebfa0830f816657a384898487fc492efac", + "size": 1645, + "url": "https://launcher.mojang.com/v1/objects/0c57c2ebfa0830f816657a384898487fc492efac/messages_ko.properties" + }, + "raw": { + "sha1": "bf9e055b5ab138ad6d49769e2b7630b7938848d6", + "size": 5712, + "url": "https://launcher.mojang.com/v1/objects/bf9e055b5ab138ad6d49769e2b7630b7938848d6/messages_ko.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/messages_pt_BR.properties": { + "downloads": { + "lzma": { + "sha1": "f8364dba0aa0a7e445a1a8d0e7ad66b996f70063", + "size": 1388, + "url": "https://launcher.mojang.com/v1/objects/f8364dba0aa0a7e445a1a8d0e7ad66b996f70063/messages_pt_BR.properties" + }, + "raw": { + "sha1": "24e4951743521ab9a11381c77bd0cdb1ed30f5b5", + "size": 3285, + "url": "https://launcher.mojang.com/v1/objects/24e4951743521ab9a11381c77bd0cdb1ed30f5b5/messages_pt_BR.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/messages_sv.properties": { + "downloads": { + "lzma": { + "sha1": "65e5897d552258141aacf02f087c7c9c33ad0727", + "size": 1355, + "url": "https://launcher.mojang.com/v1/objects/65e5897d552258141aacf02f087c7c9c33ad0727/messages_sv.properties" + }, + "raw": { + "sha1": "bb5a4aa0ba499f6b1916a83e3c7922a4583b4adb", + "size": 3384, + "url": "https://launcher.mojang.com/v1/objects/bb5a4aa0ba499f6b1916a83e3c7922a4583b4adb/messages_sv.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/messages_zh_CN.properties": { + "downloads": { + "lzma": { + "sha1": "de7d39a6e6748e9f47e842c9da90f515921c222c", + "size": 1506, + "url": "https://launcher.mojang.com/v1/objects/de7d39a6e6748e9f47e842c9da90f515921c222c/messages_zh_CN.properties" + }, + "raw": { + "sha1": "1c2b96673dddd3596890ef4fc22017d484a1f652", + "size": 4072, + "url": "https://launcher.mojang.com/v1/objects/1c2b96673dddd3596890ef4fc22017d484a1f652/messages_zh_CN.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/messages_zh_HK.properties": { + "downloads": { + "lzma": { + "sha1": "e8d0e3a63caa2535a4f361033941f34dcc170a7e", + "size": 1529, + "url": "https://launcher.mojang.com/v1/objects/e8d0e3a63caa2535a4f361033941f34dcc170a7e/messages_zh_TW.properties" + }, + "raw": { + "sha1": "37a57aad121c14c25e149206179728fa62203bf0", + "size": 3752, + "url": "https://launcher.mojang.com/v1/objects/37a57aad121c14c25e149206179728fa62203bf0/messages_zh_TW.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/messages_zh_TW.properties": { + "downloads": { + "lzma": { + "sha1": "e8d0e3a63caa2535a4f361033941f34dcc170a7e", + "size": 1529, + "url": "https://launcher.mojang.com/v1/objects/e8d0e3a63caa2535a4f361033941f34dcc170a7e/messages_zh_TW.properties" + }, + "raw": { + "sha1": "37a57aad121c14c25e149206179728fa62203bf0", + "size": 3752, + "url": "https://launcher.mojang.com/v1/objects/37a57aad121c14c25e149206179728fa62203bf0/messages_zh_TW.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/mixcode_s.png": { + "downloads": { + "raw": { + "sha1": "4604e9f265eec97bccd0151c3a81afa9e69499e5", + "size": 3115, + "url": "https://launcher.mojang.com/v1/objects/4604e9f265eec97bccd0151c3a81afa9e69499e5/mixcode_s.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/splash.gif": { + "downloads": { + "raw": { + "sha1": "20e7aec75f6d036d504277542e507eb7dc24aae8", + "size": 8590, + "url": "https://launcher.mojang.com/v1/objects/20e7aec75f6d036d504277542e507eb7dc24aae8/splash.gif" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/splash@2x.gif": { + "downloads": { + "raw": { + "sha1": "0ae4a5bda2a6d628fac51462390b503c99509fdc", + "size": 15276, + "url": "https://launcher.mojang.com/v1/objects/0ae4a5bda2a6d628fac51462390b503c99509fdc/splash2x.gif" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/splash_11-lic.gif": { + "downloads": { + "raw": { + "sha1": "8def364e07f40142822df84b5bb4f50846cb5e4e", + "size": 7805, + "url": "https://launcher.mojang.com/v1/objects/8def364e07f40142822df84b5bb4f50846cb5e4e/splash_11-lic.gif" + } + }, + "executable": false, + "type": "file" + }, + "lib/deploy/splash_11@2x-lic.gif": { + "downloads": { + "raw": { + "sha1": "d2bff9bbf7920ca743b81a0ee23b0719b4d057ca", + "size": 12250, + "url": "https://launcher.mojang.com/v1/objects/d2bff9bbf7920ca743b81a0ee23b0719b4d057ca/splash_11%402x-lic.gif" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop": { + "type": "directory" + }, + "lib/desktop/applications": { + "type": "directory" + }, + "lib/desktop/applications/sun-java.desktop": { + "downloads": { + "lzma": { + "sha1": "109d1cdf165f38da92da70b403ca940192a7a9a8", + "size": 536, + "url": "https://launcher.mojang.com/v1/objects/109d1cdf165f38da92da70b403ca940192a7a9a8/sun-java.desktop" + }, + "raw": { + "sha1": "d346dfe90505603ce5aff5a3c6c2e4a23d5bd990", + "size": 777, + "url": "https://launcher.mojang.com/v1/objects/d346dfe90505603ce5aff5a3c6c2e4a23d5bd990/sun-java.desktop" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/applications/sun-javaws.desktop": { + "downloads": { + "lzma": { + "sha1": "5e1815e7f83515881e6998584dc6bb02c5bef09a", + "size": 451, + "url": "https://launcher.mojang.com/v1/objects/5e1815e7f83515881e6998584dc6bb02c5bef09a/sun-javaws.desktop" + }, + "raw": { + "sha1": "50ce8e519b836e0f53d58ce1a359d98b6cafdda6", + "size": 619, + "url": "https://launcher.mojang.com/v1/objects/50ce8e519b836e0f53d58ce1a359d98b6cafdda6/sun-javaws.desktop" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/applications/sun_java.desktop": { + "downloads": { + "lzma": { + "sha1": "49ab0ccb54c3be68281d05055bc56a88b1281d3c", + "size": 447, + "url": "https://launcher.mojang.com/v1/objects/49ab0ccb54c3be68281d05055bc56a88b1281d3c/sun_java.desktop" + }, + "raw": { + "sha1": "79120ee8160ad6f3c9b90c2641fb7edf3af96b5d", + "size": 624, + "url": "https://launcher.mojang.com/v1/objects/79120ee8160ad6f3c9b90c2641fb7edf3af96b5d/sun_java.desktop" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons": { + "type": "directory" + }, + "lib/desktop/icons/HighContrast": { + "type": "directory" + }, + "lib/desktop/icons/HighContrast/16x16": { + "type": "directory" + }, + "lib/desktop/icons/HighContrast/16x16/apps": { + "type": "directory" + }, + "lib/desktop/icons/HighContrast/16x16/apps/sun-java.png": { + "downloads": { + "raw": { + "sha1": "366e7a48e9e4fb92eaeabbcaeb4626122a66cecb", + "size": 417, + "url": "https://launcher.mojang.com/v1/objects/366e7a48e9e4fb92eaeabbcaeb4626122a66cecb/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrast/16x16/apps/sun-javaws.png": { + "downloads": { + "raw": { + "sha1": "366e7a48e9e4fb92eaeabbcaeb4626122a66cecb", + "size": 417, + "url": "https://launcher.mojang.com/v1/objects/366e7a48e9e4fb92eaeabbcaeb4626122a66cecb/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrast/16x16/apps/sun-jcontrol.png": { + "downloads": { + "raw": { + "sha1": "366e7a48e9e4fb92eaeabbcaeb4626122a66cecb", + "size": 417, + "url": "https://launcher.mojang.com/v1/objects/366e7a48e9e4fb92eaeabbcaeb4626122a66cecb/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrast/16x16/mimetypes": { + "type": "directory" + }, + "lib/desktop/icons/HighContrast/16x16/mimetypes/gnome-mime-application-x-java-archive.png": { + "downloads": { + "raw": { + "sha1": "629c48907368ecf32d2395b6459c367f79d84689", + "size": 464, + "url": + "https://launcher.mojang.com/v1/objects/629c48907368ecf32d2395b6459c367f79d84689/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrast/16x16/mimetypes/gnome-mime-application-x-java-jnlp-file.png": { + "downloads": { + "raw": { + "sha1": "629c48907368ecf32d2395b6459c367f79d84689", + "size": 464, + "url": + "https://launcher.mojang.com/v1/objects/629c48907368ecf32d2395b6459c367f79d84689/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrast/16x16/mimetypes/gnome-mime-text-x-java.png": { + "downloads": { + "raw": { + "sha1": "629c48907368ecf32d2395b6459c367f79d84689", + "size": 464, + "url": + "https://launcher.mojang.com/v1/objects/629c48907368ecf32d2395b6459c367f79d84689/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrast/48x48": { + "type": "directory" + }, + "lib/desktop/icons/HighContrast/48x48/apps": { + "type": "directory" + }, + "lib/desktop/icons/HighContrast/48x48/apps/sun-java.png": { + "downloads": { + "raw": { + "sha1": "8373482d072684e09830dbdb97a76ea264c9f4e9", + "size": 3451, + "url": "https://launcher.mojang.com/v1/objects/8373482d072684e09830dbdb97a76ea264c9f4e9/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrast/48x48/apps/sun-javaws.png": { + "downloads": { + "raw": { + "sha1": "8373482d072684e09830dbdb97a76ea264c9f4e9", + "size": 3451, + "url": "https://launcher.mojang.com/v1/objects/8373482d072684e09830dbdb97a76ea264c9f4e9/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrast/48x48/apps/sun-jcontrol.png": { + "downloads": { + "raw": { + "sha1": "8373482d072684e09830dbdb97a76ea264c9f4e9", + "size": 3451, + "url": "https://launcher.mojang.com/v1/objects/8373482d072684e09830dbdb97a76ea264c9f4e9/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrast/48x48/mimetypes": { + "type": "directory" + }, + "lib/desktop/icons/HighContrast/48x48/mimetypes/gnome-mime-application-x-java-archive.png": { + "downloads": { + "raw": { + "sha1": "56a4996519f8f3541eba7b7a7a69bcdcd8ed0410", + "size": 2088, + "url": + "https://launcher.mojang.com/v1/objects/56a4996519f8f3541eba7b7a7a69bcdcd8ed0410/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrast/48x48/mimetypes/gnome-mime-application-x-java-jnlp-file.png": { + "downloads": { + "raw": { + "sha1": "56a4996519f8f3541eba7b7a7a69bcdcd8ed0410", + "size": 2088, + "url": + "https://launcher.mojang.com/v1/objects/56a4996519f8f3541eba7b7a7a69bcdcd8ed0410/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrast/48x48/mimetypes/gnome-mime-text-x-java.png": { + "downloads": { + "raw": { + "sha1": "56a4996519f8f3541eba7b7a7a69bcdcd8ed0410", + "size": 2088, + "url": + "https://launcher.mojang.com/v1/objects/56a4996519f8f3541eba7b7a7a69bcdcd8ed0410/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrastInverse": { + "type": "directory" + }, + "lib/desktop/icons/HighContrastInverse/16x16": { + "type": "directory" + }, + "lib/desktop/icons/HighContrastInverse/16x16/apps": { + "type": "directory" + }, + "lib/desktop/icons/HighContrastInverse/16x16/apps/sun-java.png": { + "downloads": { + "raw": { + "sha1": "bf0995acb94aa794e73c5b971282ff13ffe42793", + "size": 402, + "url": "https://launcher.mojang.com/v1/objects/bf0995acb94aa794e73c5b971282ff13ffe42793/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrastInverse/16x16/apps/sun-javaws.png": { + "downloads": { + "raw": { + "sha1": "bf0995acb94aa794e73c5b971282ff13ffe42793", + "size": 402, + "url": "https://launcher.mojang.com/v1/objects/bf0995acb94aa794e73c5b971282ff13ffe42793/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrastInverse/16x16/apps/sun-jcontrol.png": { + "downloads": { + "raw": { + "sha1": "bf0995acb94aa794e73c5b971282ff13ffe42793", + "size": 402, + "url": "https://launcher.mojang.com/v1/objects/bf0995acb94aa794e73c5b971282ff13ffe42793/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrastInverse/16x16/mimetypes": { + "type": "directory" + }, + "lib/desktop/icons/HighContrastInverse/16x16/mimetypes/gnome-mime-application-x-java-archive.png": { + "downloads": { + "raw": { + "sha1": "1477eceda25e162fcda2e69ee3906091973d8344", + "size": 473, + "url": + "https://launcher.mojang.com/v1/objects/1477eceda25e162fcda2e69ee3906091973d8344/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrastInverse/16x16/mimetypes/gnome-mime-application-x-java-jnlp-file.png": { + "downloads": { + "raw": { + "sha1": "1477eceda25e162fcda2e69ee3906091973d8344", + "size": 473, + "url": + "https://launcher.mojang.com/v1/objects/1477eceda25e162fcda2e69ee3906091973d8344/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrastInverse/16x16/mimetypes/gnome-mime-text-x-java.png": { + "downloads": { + "raw": { + "sha1": "1477eceda25e162fcda2e69ee3906091973d8344", + "size": 473, + "url": + "https://launcher.mojang.com/v1/objects/1477eceda25e162fcda2e69ee3906091973d8344/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrastInverse/48x48": { + "type": "directory" + }, + "lib/desktop/icons/HighContrastInverse/48x48/apps": { + "type": "directory" + }, + "lib/desktop/icons/HighContrastInverse/48x48/apps/sun-java.png": { + "downloads": { + "raw": { + "sha1": "413da160dd9899b95f53d4cc11f5ee0550cc6585", + "size": 3410, + "url": "https://launcher.mojang.com/v1/objects/413da160dd9899b95f53d4cc11f5ee0550cc6585/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrastInverse/48x48/apps/sun-javaws.png": { + "downloads": { + "raw": { + "sha1": "413da160dd9899b95f53d4cc11f5ee0550cc6585", + "size": 3410, + "url": "https://launcher.mojang.com/v1/objects/413da160dd9899b95f53d4cc11f5ee0550cc6585/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrastInverse/48x48/apps/sun-jcontrol.png": { + "downloads": { + "raw": { + "sha1": "413da160dd9899b95f53d4cc11f5ee0550cc6585", + "size": 3410, + "url": "https://launcher.mojang.com/v1/objects/413da160dd9899b95f53d4cc11f5ee0550cc6585/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrastInverse/48x48/mimetypes": { + "type": "directory" + }, + "lib/desktop/icons/HighContrastInverse/48x48/mimetypes/gnome-mime-application-x-java-archive.png": { + "downloads": { + "raw": { + "sha1": "d66e04dfa25c196bec2e201547325b79846ab674", + "size": 2085, + "url": + "https://launcher.mojang.com/v1/objects/d66e04dfa25c196bec2e201547325b79846ab674/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrastInverse/48x48/mimetypes/gnome-mime-application-x-java-jnlp-file.png": { + "downloads": { + "raw": { + "sha1": "d66e04dfa25c196bec2e201547325b79846ab674", + "size": 2085, + "url": + "https://launcher.mojang.com/v1/objects/d66e04dfa25c196bec2e201547325b79846ab674/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/HighContrastInverse/48x48/mimetypes/gnome-mime-text-x-java.png": { + "downloads": { + "raw": { + "sha1": "d66e04dfa25c196bec2e201547325b79846ab674", + "size": 2085, + "url": + "https://launcher.mojang.com/v1/objects/d66e04dfa25c196bec2e201547325b79846ab674/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/LowContrast": { + "type": "directory" + }, + "lib/desktop/icons/LowContrast/16x16": { + "type": "directory" + }, + "lib/desktop/icons/LowContrast/16x16/apps": { + "type": "directory" + }, + "lib/desktop/icons/LowContrast/16x16/apps/sun-java.png": { + "downloads": { + "raw": { + "sha1": "f93b7cf0a6d27d664a7f09dab6933b2768536f52", + "size": 519, + "url": "https://launcher.mojang.com/v1/objects/f93b7cf0a6d27d664a7f09dab6933b2768536f52/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/LowContrast/16x16/apps/sun-javaws.png": { + "downloads": { + "raw": { + "sha1": "f93b7cf0a6d27d664a7f09dab6933b2768536f52", + "size": 519, + "url": "https://launcher.mojang.com/v1/objects/f93b7cf0a6d27d664a7f09dab6933b2768536f52/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/LowContrast/16x16/apps/sun-jcontrol.png": { + "downloads": { + "raw": { + "sha1": "f93b7cf0a6d27d664a7f09dab6933b2768536f52", + "size": 519, + "url": "https://launcher.mojang.com/v1/objects/f93b7cf0a6d27d664a7f09dab6933b2768536f52/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/LowContrast/16x16/mimetypes": { + "type": "directory" + }, + "lib/desktop/icons/LowContrast/16x16/mimetypes/gnome-mime-application-x-java-archive.png": { + "downloads": { + "raw": { + "sha1": "0aa1605877280b88de1f1cc3e7e4bdbeed968a73", + "size": 525, + "url": + "https://launcher.mojang.com/v1/objects/0aa1605877280b88de1f1cc3e7e4bdbeed968a73/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/LowContrast/16x16/mimetypes/gnome-mime-application-x-java-jnlp-file.png": { + "downloads": { + "raw": { + "sha1": "0aa1605877280b88de1f1cc3e7e4bdbeed968a73", + "size": 525, + "url": + "https://launcher.mojang.com/v1/objects/0aa1605877280b88de1f1cc3e7e4bdbeed968a73/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/LowContrast/16x16/mimetypes/gnome-mime-text-x-java.png": { + "downloads": { + "raw": { + "sha1": "0aa1605877280b88de1f1cc3e7e4bdbeed968a73", + "size": 525, + "url": + "https://launcher.mojang.com/v1/objects/0aa1605877280b88de1f1cc3e7e4bdbeed968a73/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/LowContrast/48x48": { + "type": "directory" + }, + "lib/desktop/icons/LowContrast/48x48/apps": { + "type": "directory" + }, + "lib/desktop/icons/LowContrast/48x48/apps/sun-java.png": { + "downloads": { + "raw": { + "sha1": "1fcf4fd6da61873b5f21b39412da26509734b7cc", + "size": 1507, + "url": "https://launcher.mojang.com/v1/objects/1fcf4fd6da61873b5f21b39412da26509734b7cc/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/LowContrast/48x48/apps/sun-javaws.png": { + "downloads": { + "raw": { + "sha1": "1fcf4fd6da61873b5f21b39412da26509734b7cc", + "size": 1507, + "url": "https://launcher.mojang.com/v1/objects/1fcf4fd6da61873b5f21b39412da26509734b7cc/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/LowContrast/48x48/apps/sun-jcontrol.png": { + "downloads": { + "raw": { + "sha1": "1fcf4fd6da61873b5f21b39412da26509734b7cc", + "size": 1507, + "url": "https://launcher.mojang.com/v1/objects/1fcf4fd6da61873b5f21b39412da26509734b7cc/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/LowContrast/48x48/mimetypes": { + "type": "directory" + }, + "lib/desktop/icons/LowContrast/48x48/mimetypes/gnome-mime-application-x-java-archive.png": { + "downloads": { + "raw": { + "sha1": "e36636b1c04dc283c18adf669b892d54b15d3ee6", + "size": 1948, + "url": + "https://launcher.mojang.com/v1/objects/e36636b1c04dc283c18adf669b892d54b15d3ee6/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/LowContrast/48x48/mimetypes/gnome-mime-application-x-java-jnlp-file.png": { + "downloads": { + "raw": { + "sha1": "e36636b1c04dc283c18adf669b892d54b15d3ee6", + "size": 1948, + "url": + "https://launcher.mojang.com/v1/objects/e36636b1c04dc283c18adf669b892d54b15d3ee6/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/LowContrast/48x48/mimetypes/gnome-mime-text-x-java.png": { + "downloads": { + "raw": { + "sha1": "e36636b1c04dc283c18adf669b892d54b15d3ee6", + "size": 1948, + "url": + "https://launcher.mojang.com/v1/objects/e36636b1c04dc283c18adf669b892d54b15d3ee6/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/hicolor": { + "type": "directory" + }, + "lib/desktop/icons/hicolor/16x16": { + "type": "directory" + }, + "lib/desktop/icons/hicolor/16x16/apps": { + "type": "directory" + }, + "lib/desktop/icons/hicolor/16x16/apps/sun-java.png": { + "downloads": { + "raw": { + "sha1": "e91d05bfe9b889bf8a227908b597cab4630da8f2", + "size": 383, + "url": "https://launcher.mojang.com/v1/objects/e91d05bfe9b889bf8a227908b597cab4630da8f2/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/hicolor/16x16/apps/sun-javaws.png": { + "downloads": { + "raw": { + "sha1": "e91d05bfe9b889bf8a227908b597cab4630da8f2", + "size": 383, + "url": "https://launcher.mojang.com/v1/objects/e91d05bfe9b889bf8a227908b597cab4630da8f2/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/hicolor/16x16/apps/sun-jcontrol.png": { + "downloads": { + "raw": { + "sha1": "e91d05bfe9b889bf8a227908b597cab4630da8f2", + "size": 383, + "url": "https://launcher.mojang.com/v1/objects/e91d05bfe9b889bf8a227908b597cab4630da8f2/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/hicolor/16x16/mimetypes": { + "type": "directory" + }, + "lib/desktop/icons/hicolor/16x16/mimetypes/gnome-mime-application-x-java-archive.png": { + "downloads": { + "raw": { + "sha1": "d2f6abe8e498aeecb334fb43f63001d34dbf6ea5", + "size": 783, + "url": + "https://launcher.mojang.com/v1/objects/d2f6abe8e498aeecb334fb43f63001d34dbf6ea5/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/hicolor/16x16/mimetypes/gnome-mime-application-x-java-jnlp-file.png": { + "downloads": { + "raw": { + "sha1": "d2f6abe8e498aeecb334fb43f63001d34dbf6ea5", + "size": 783, + "url": + "https://launcher.mojang.com/v1/objects/d2f6abe8e498aeecb334fb43f63001d34dbf6ea5/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/hicolor/16x16/mimetypes/gnome-mime-text-x-java.png": { + "downloads": { + "raw": { + "sha1": "d2f6abe8e498aeecb334fb43f63001d34dbf6ea5", + "size": 783, + "url": + "https://launcher.mojang.com/v1/objects/d2f6abe8e498aeecb334fb43f63001d34dbf6ea5/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/hicolor/48x48": { + "type": "directory" + }, + "lib/desktop/icons/hicolor/48x48/apps": { + "type": "directory" + }, + "lib/desktop/icons/hicolor/48x48/apps/sun-java.png": { + "downloads": { + "raw": { + "sha1": "6c90a38eaada9c32a678a282be18ec5b43a84264", + "size": 1439, + "url": "https://launcher.mojang.com/v1/objects/6c90a38eaada9c32a678a282be18ec5b43a84264/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/hicolor/48x48/apps/sun-javaws.png": { + "downloads": { + "raw": { + "sha1": "6c90a38eaada9c32a678a282be18ec5b43a84264", + "size": 1439, + "url": "https://launcher.mojang.com/v1/objects/6c90a38eaada9c32a678a282be18ec5b43a84264/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/hicolor/48x48/apps/sun-jcontrol.png": { + "downloads": { + "raw": { + "sha1": "6c90a38eaada9c32a678a282be18ec5b43a84264", + "size": 1439, + "url": "https://launcher.mojang.com/v1/objects/6c90a38eaada9c32a678a282be18ec5b43a84264/sun-javaws.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/hicolor/48x48/mimetypes": { + "type": "directory" + }, + "lib/desktop/icons/hicolor/48x48/mimetypes/gnome-mime-application-x-java-archive.png": { + "downloads": { + "raw": { + "sha1": "4d5e6e0c41d1076bc86f3ab157c88a41a5716997", + "size": 3202, + "url": + "https://launcher.mojang.com/v1/objects/4d5e6e0c41d1076bc86f3ab157c88a41a5716997/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/hicolor/48x48/mimetypes/gnome-mime-application-x-java-jnlp-file.png": { + "downloads": { + "raw": { + "sha1": "4d5e6e0c41d1076bc86f3ab157c88a41a5716997", + "size": 3202, + "url": + "https://launcher.mojang.com/v1/objects/4d5e6e0c41d1076bc86f3ab157c88a41a5716997/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/icons/hicolor/48x48/mimetypes/gnome-mime-text-x-java.png": { + "downloads": { + "raw": { + "sha1": "4d5e6e0c41d1076bc86f3ab157c88a41a5716997", + "size": 3202, + "url": + "https://launcher.mojang.com/v1/objects/4d5e6e0c41d1076bc86f3ab157c88a41a5716997/gnome-mime-application-x-java-archive.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/mime": { + "type": "directory" + }, + "lib/desktop/mime/packages": { + "type": "directory" + }, + "lib/desktop/mime/packages/x-java-archive.xml": { + "downloads": { + "lzma": { + "sha1": "841230729f0a59de2a1071d155d96358232b2ba1", + "size": 591, + "url": "https://launcher.mojang.com/v1/objects/841230729f0a59de2a1071d155d96358232b2ba1/x-java-archive.xml" + }, + "raw": { + "sha1": "b6297fd36efa799312961f95ebf0c85c920d5037", + "size": 1822, + "url": "https://launcher.mojang.com/v1/objects/b6297fd36efa799312961f95ebf0c85c920d5037/x-java-archive.xml" + } + }, + "executable": false, + "type": "file" + }, + "lib/desktop/mime/packages/x-java-jnlp-file.xml": { + "downloads": { + "lzma": { + "sha1": "abf9acbe7c18027c4f036c4e42bb2cf1115525fa", + "size": 302, + "url": "https://launcher.mojang.com/v1/objects/abf9acbe7c18027c4f036c4e42bb2cf1115525fa/x-java-jnlp-file.xml" + }, + "raw": { + "sha1": "72f03da83ddb76c9105f619fcfa4dbdad70e6b30", + "size": 412, + "url": "https://launcher.mojang.com/v1/objects/72f03da83ddb76c9105f619fcfa4dbdad70e6b30/x-java-jnlp-file.xml" + } + }, + "executable": false, + "type": "file" + }, + "lib/ext": { + "type": "directory" + }, + "lib/ext/cldrdata.jar": { + "downloads": { + "raw": { + "sha1": "6cacc961942d3f02a88907aa8f2eaae8e20c95a0", + "size": 3860502, + "url": "https://launcher.mojang.com/v1/objects/6cacc961942d3f02a88907aa8f2eaae8e20c95a0/cldrdata.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/ext/dnsns.jar": { + "downloads": { + "raw": { + "sha1": "93bebdd7514e53ae31d60c5daba673878c8822ec", + "size": 8286, + "url": "https://launcher.mojang.com/v1/objects/93bebdd7514e53ae31d60c5daba673878c8822ec/dnsns.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/ext/jaccess.jar": { + "downloads": { + "raw": { + "sha1": "2f54879df7c29ec67c40d40cfc95c0d4a968bea1", + "size": 44516, + "url": "https://launcher.mojang.com/v1/objects/2f54879df7c29ec67c40d40cfc95c0d4a968bea1/jaccess.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/ext/jfxrt.jar": { + "downloads": { + "lzma": { + "sha1": "a6c5b6a782ba360ada6651f5322dcab88c75711c", + "size": 3374270, + "url": "https://launcher.mojang.com/v1/objects/a6c5b6a782ba360ada6651f5322dcab88c75711c/jfxrt.jar" + }, + "raw": { + "sha1": "1ad7a876f045399c23ee4ab7dee380a04ca2ac08", + "size": 18508094, + "url": "https://launcher.mojang.com/v1/objects/1ad7a876f045399c23ee4ab7dee380a04ca2ac08/jfxrt.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/ext/localedata.jar": { + "downloads": { + "raw": { + "sha1": "0cc9f550d4e410b5aa29dbfd2c1b5c99391c7f70", + "size": 1178926, + "url": "https://launcher.mojang.com/v1/objects/0cc9f550d4e410b5aa29dbfd2c1b5c99391c7f70/localedata.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/ext/meta-index": { + "downloads": { + "lzma": { + "sha1": "1359457529f42bacf495afcb68149ae036442dd9", + "size": 594, + "url": "https://launcher.mojang.com/v1/objects/1359457529f42bacf495afcb68149ae036442dd9/meta-index" + }, + "raw": { + "sha1": "334649c6e2d5d7248211f30855e97cfcb4558851", + "size": 1269, + "url": "https://launcher.mojang.com/v1/objects/334649c6e2d5d7248211f30855e97cfcb4558851/meta-index" + } + }, + "executable": false, + "type": "file" + }, + "lib/ext/nashorn.jar": { + "downloads": { + "raw": { + "sha1": "dec5dd17a0f52ae79dfbfb38840bffb8b7a679a5", + "size": 2023869, + "url": "https://launcher.mojang.com/v1/objects/dec5dd17a0f52ae79dfbfb38840bffb8b7a679a5/nashorn.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/ext/sunec.jar": { + "downloads": { + "raw": { + "sha1": "bf1c817820341a246f7130fe046e8310b03d04f6", + "size": 41672, + "url": "https://launcher.mojang.com/v1/objects/bf1c817820341a246f7130fe046e8310b03d04f6/sunec.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/ext/sunjce_provider.jar": { + "downloads": { + "raw": { + "sha1": "bb3494e4b5cb3c3e60da767207731f18b267cb34", + "size": 279326, + "url": "https://launcher.mojang.com/v1/objects/bb3494e4b5cb3c3e60da767207731f18b267cb34/sunjce_provider.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/ext/sunpkcs11.jar": { + "downloads": { + "raw": { + "sha1": "5bb1dedc3344cd3bb86828d4aa8ca82f4a606ed4", + "size": 250218, + "url": "https://launcher.mojang.com/v1/objects/5bb1dedc3344cd3bb86828d4aa8ca82f4a606ed4/sunpkcs11.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/ext/zipfs.jar": { + "downloads": { + "raw": { + "sha1": "37b338f0e8e60d6396f51275130e8110816d7b30", + "size": 68964, + "url": "https://launcher.mojang.com/v1/objects/37b338f0e8e60d6396f51275130e8110816d7b30/zipfs.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/flavormap.properties": { + "downloads": { + "lzma": { + "sha1": "2d5ef19ee77ccfc95c9413eea155cde59a48fadd", + "size": 1541, + "url": "https://launcher.mojang.com/v1/objects/2d5ef19ee77ccfc95c9413eea155cde59a48fadd/flavormap.properties" + }, + "raw": { + "sha1": "4e66e8fe12d7f8b3b0c4e1a1915f329bb1fbf6d2", + "size": 3901, + "url": "https://launcher.mojang.com/v1/objects/4e66e8fe12d7f8b3b0c4e1a1915f329bb1fbf6d2/flavormap.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/fontconfig.RedHat.5.bfc": { + "downloads": { + "lzma": { + "sha1": "5197f6e387f16458b7408134e38b06f20f625e4c", + "size": 795, + "url": "https://launcher.mojang.com/v1/objects/5197f6e387f16458b7408134e38b06f20f625e4c/fontconfig.RedHat.5.bfc" + }, + "raw": { + "sha1": "fb806ada6e68f16a9fe2b726a39d9ef5a835c0c2", + "size": 4532, + "url": "https://launcher.mojang.com/v1/objects/fb806ada6e68f16a9fe2b726a39d9ef5a835c0c2/fontconfig.RedHat.5.bfc" + } + }, + "executable": false, + "type": "file" + }, + "lib/fontconfig.RedHat.5.properties.src": { + "downloads": { + "lzma": { + "sha1": "3897ae198e96e5a687c9c9b218ff5df60c868e0d", + "size": 1089, + "url": + "https://launcher.mojang.com/v1/objects/3897ae198e96e5a687c9c9b218ff5df60c868e0d/fontconfig.RedHat.5.properties.src" + }, + "raw": { + "sha1": "c67d1a06cb37b66e69560c9f5e4be7cf08af0493", + "size": 8841, + "url": + "https://launcher.mojang.com/v1/objects/c67d1a06cb37b66e69560c9f5e4be7cf08af0493/fontconfig.RedHat.5.properties.src" + } + }, + "executable": false, + "type": "file" + }, + "lib/fontconfig.RedHat.6.bfc": { + "downloads": { + "lzma": { + "sha1": "ef2f5d1f8d620be9927db45d3a28bd75777245cb", + "size": 818, + "url": "https://launcher.mojang.com/v1/objects/ef2f5d1f8d620be9927db45d3a28bd75777245cb/fontconfig.RedHat.6.bfc" + }, + "raw": { + "sha1": "9ba3b3e2c621c31d0ef1b2053c80f77419a19965", + "size": 4250, + "url": "https://launcher.mojang.com/v1/objects/9ba3b3e2c621c31d0ef1b2053c80f77419a19965/fontconfig.RedHat.6.bfc" + } + }, + "executable": false, + "type": "file" + }, + "lib/fontconfig.RedHat.6.properties.src": { + "downloads": { + "lzma": { + "sha1": "74f4148f9d7ec3d67bbd724834d478a72cfdb0db", + "size": 1111, + "url": + "https://launcher.mojang.com/v1/objects/74f4148f9d7ec3d67bbd724834d478a72cfdb0db/fontconfig.RedHat.6.properties.src" + }, + "raw": { + "sha1": "768e58d4d314621c38daf9fde6d67119f370acd9", + "size": 8735, + "url": + "https://launcher.mojang.com/v1/objects/768e58d4d314621c38daf9fde6d67119f370acd9/fontconfig.RedHat.6.properties.src" + } + }, + "executable": false, + "type": "file" + }, + "lib/fontconfig.SuSE.10.bfc": { + "downloads": { + "lzma": { + "sha1": "d8fe9b1d8d02368dcd452de93024c6f60670eb87", + "size": 1083, + "url": "https://launcher.mojang.com/v1/objects/d8fe9b1d8d02368dcd452de93024c6f60670eb87/fontconfig.SuSE.10.bfc" + }, + "raw": { + "sha1": "ffd0dfbd1553e15b11649a73a0b3f48318138e0d", + "size": 6702, + "url": "https://launcher.mojang.com/v1/objects/ffd0dfbd1553e15b11649a73a0b3f48318138e0d/fontconfig.SuSE.10.bfc" + } + }, + "executable": false, + "type": "file" + }, + "lib/fontconfig.SuSE.10.properties.src": { + "downloads": { + "lzma": { + "sha1": "2c382bd741a9e23114be3da82dee8290ebfca8a9", + "size": 1555, + "url": + "https://launcher.mojang.com/v1/objects/2c382bd741a9e23114be3da82dee8290ebfca8a9/fontconfig.SuSE.10.properties.src" + }, + "raw": { + "sha1": "a38dbdbbc514567b8281e1aea726865f37e97894", + "size": 16772, + "url": + "https://launcher.mojang.com/v1/objects/a38dbdbbc514567b8281e1aea726865f37e97894/fontconfig.SuSE.10.properties.src" + } + }, + "executable": false, + "type": "file" + }, + "lib/fontconfig.SuSE.11.bfc": { + "downloads": { + "lzma": { + "sha1": "2b78cbf11289c9858951fea7180696ba3b7176d6", + "size": 1092, + "url": "https://launcher.mojang.com/v1/objects/2b78cbf11289c9858951fea7180696ba3b7176d6/fontconfig.SuSE.11.bfc" + }, + "raw": { + "sha1": "a4d8500fcb47f6327460a95851b1368660da8302", + "size": 7032, + "url": "https://launcher.mojang.com/v1/objects/a4d8500fcb47f6327460a95851b1368660da8302/fontconfig.SuSE.11.bfc" + } + }, + "executable": false, + "type": "file" + }, + "lib/fontconfig.SuSE.11.properties.src": { + "downloads": { + "lzma": { + "sha1": "5c1635803906e2c59d36492dec724dd7ae49a5ab", + "size": 1589, + "url": + "https://launcher.mojang.com/v1/objects/5c1635803906e2c59d36492dec724dd7ae49a5ab/fontconfig.SuSE.11.properties.src" + }, + "raw": { + "sha1": "c4b69589e41a7279a71866a9134213be19cdf88d", + "size": 16781, + "url": + "https://launcher.mojang.com/v1/objects/c4b69589e41a7279a71866a9134213be19cdf88d/fontconfig.SuSE.11.properties.src" + } + }, + "executable": false, + "type": "file" + }, + "lib/fontconfig.Turbo.bfc": { + "downloads": { + "lzma": { + "sha1": "1c771325d9ee4af209a3db92294451d58962c7a4", + "size": 822, + "url": "https://launcher.mojang.com/v1/objects/1c771325d9ee4af209a3db92294451d58962c7a4/fontconfig.Turbo.bfc" + }, + "raw": { + "sha1": "f24368deeb85cc9d0781083bc56e773518d72219", + "size": 4668, + "url": "https://launcher.mojang.com/v1/objects/f24368deeb85cc9d0781083bc56e773518d72219/fontconfig.Turbo.bfc" + } + }, + "executable": false, + "type": "file" + }, + "lib/fontconfig.Turbo.properties.src": { + "downloads": { + "lzma": { + "sha1": "7748ffa17e2c8a34754138efa963ba39bd1cbbb3", + "size": 1113, + "url": "https://launcher.mojang.com/v1/objects/7748ffa17e2c8a34754138efa963ba39bd1cbbb3/fontconfig.Turbo.properties.src" + }, + "raw": { + "sha1": "2bb7258bed7ccd4f117e4e5f892c9b13424b0c82", + "size": 9192, + "url": "https://launcher.mojang.com/v1/objects/2bb7258bed7ccd4f117e4e5f892c9b13424b0c82/fontconfig.Turbo.properties.src" + } + }, + "executable": false, + "type": "file" + }, + "lib/fontconfig.bfc": { + "downloads": { + "lzma": { + "sha1": "be6d49ee8c64f458c4f0e64254963fec48d25150", + "size": 286, + "url": "https://launcher.mojang.com/v1/objects/be6d49ee8c64f458c4f0e64254963fec48d25150/fontconfig.bfc" + }, + "raw": { + "sha1": "de39b0e19637f58d92a0188122514aa7247ebb5b", + "size": 1678, + "url": "https://launcher.mojang.com/v1/objects/de39b0e19637f58d92a0188122514aa7247ebb5b/fontconfig.bfc" + } + }, + "executable": false, + "type": "file" + }, + "lib/fontconfig.properties.src": { + "downloads": { + "lzma": { + "sha1": "9498d5e00e5401200667687e826e28c60fa60ba4", + "size": 417, + "url": "https://launcher.mojang.com/v1/objects/9498d5e00e5401200667687e826e28c60fa60ba4/fontconfig.properties.src" + }, + "raw": { + "sha1": "3617ff1424fd204415242565541facf862b16eb4", + "size": 1938, + "url": "https://launcher.mojang.com/v1/objects/3617ff1424fd204415242565541facf862b16eb4/fontconfig.properties.src" + } + }, + "executable": false, + "type": "file" + }, + "lib/fonts": { + "type": "directory" + }, + "lib/fonts/LucidaBrightDemiBold.ttf": { + "downloads": { + "lzma": { + "sha1": "4f748750831a7719440dff5457f4d207d0f24d21", + "size": 42347, + "url": "https://launcher.mojang.com/v1/objects/4f748750831a7719440dff5457f4d207d0f24d21/LucidaBrightDemiBold.ttf" + }, + "raw": { + "sha1": "b5c97f985639e19a3b712193ee48b55dda581fd1", + "size": 75144, + "url": "https://launcher.mojang.com/v1/objects/b5c97f985639e19a3b712193ee48b55dda581fd1/LucidaBrightDemiBold.ttf" + } + }, + "executable": false, + "type": "file" + }, + "lib/fonts/LucidaBrightDemiItalic.ttf": { + "downloads": { + "lzma": { + "sha1": "f82e9a688553c100ecb98412b985807ed56dff5d", + "size": 43119, + "url": "https://launcher.mojang.com/v1/objects/f82e9a688553c100ecb98412b985807ed56dff5d/LucidaBrightDemiItalic.ttf" + }, + "raw": { + "sha1": "1fd1f757febf3e5f5fbb7fbf7a56587a40d57de7", + "size": 75124, + "url": "https://launcher.mojang.com/v1/objects/1fd1f757febf3e5f5fbb7fbf7a56587a40d57de7/LucidaBrightDemiItalic.ttf" + } + }, + "executable": false, + "type": "file" + }, + "lib/fonts/LucidaBrightItalic.ttf": { + "downloads": { + "lzma": { + "sha1": "6d630df719271319c3d53f90a3d425118b908266", + "size": 46206, + "url": "https://launcher.mojang.com/v1/objects/6d630df719271319c3d53f90a3d425118b908266/LucidaBrightItalic.ttf" + }, + "raw": { + "sha1": "aa5c037865c563726ecd63d61ca26443589be425", + "size": 80856, + "url": "https://launcher.mojang.com/v1/objects/aa5c037865c563726ecd63d61ca26443589be425/LucidaBrightItalic.ttf" + } + }, + "executable": false, + "type": "file" + }, + "lib/fonts/LucidaBrightRegular.ttf": { + "downloads": { + "lzma": { + "sha1": "4b2e31aaec2238b6ecf9f845bad0a1c6d09fbbfe", + "size": 181085, + "url": "https://launcher.mojang.com/v1/objects/4b2e31aaec2238b6ecf9f845bad0a1c6d09fbbfe/LucidaBrightRegular.ttf" + }, + "raw": { + "sha1": "5d7ed564791c900a8786936930ba99385653139c", + "size": 344908, + "url": "https://launcher.mojang.com/v1/objects/5d7ed564791c900a8786936930ba99385653139c/LucidaBrightRegular.ttf" + } + }, + "executable": false, + "type": "file" + }, + "lib/fonts/LucidaSansDemiBold.ttf": { + "downloads": { + "lzma": { + "sha1": "079b16dc3c4918ab1f4f760b6dc5e6586c219042", + "size": 173229, + "url": "https://launcher.mojang.com/v1/objects/079b16dc3c4918ab1f4f760b6dc5e6586c219042/LucidaSansDemiBold.ttf" + }, + "raw": { + "sha1": "92b79fefc35e96190250c602a8fed85276b32a95", + "size": 317896, + "url": "https://launcher.mojang.com/v1/objects/92b79fefc35e96190250c602a8fed85276b32a95/LucidaSansDemiBold.ttf" + } + }, + "executable": false, + "type": "file" + }, + "lib/fonts/LucidaSansRegular.ttf": { + "downloads": { + "lzma": { + "sha1": "64a65d7b94d7153d20957ef6d06bebb4dd0f48e4", + "size": 326062, + "url": "https://launcher.mojang.com/v1/objects/64a65d7b94d7153d20957ef6d06bebb4dd0f48e4/LucidaSansRegular.ttf" + }, + "raw": { + "sha1": "39cc8bcb8d4a71d4657fc92ef0b9f4e3e9e67add", + "size": 698236, + "url": "https://launcher.mojang.com/v1/objects/39cc8bcb8d4a71d4657fc92ef0b9f4e3e9e67add/LucidaSansRegular.ttf" + } + }, + "executable": false, + "type": "file" + }, + "lib/fonts/LucidaTypewriterBold.ttf": { + "downloads": { + "lzma": { + "sha1": "cdb017f7c34bea0802bc5ea5583aef721ed99c49", + "size": 130412, + "url": "https://launcher.mojang.com/v1/objects/cdb017f7c34bea0802bc5ea5583aef721ed99c49/LucidaTypewriterBold.ttf" + }, + "raw": { + "sha1": "a5da2eb49448f461470387c939f0e69119310e0b", + "size": 234068, + "url": "https://launcher.mojang.com/v1/objects/a5da2eb49448f461470387c939f0e69119310e0b/LucidaTypewriterBold.ttf" + } + }, + "executable": false, + "type": "file" + }, + "lib/fonts/LucidaTypewriterRegular.ttf": { + "downloads": { + "lzma": { + "sha1": "aeda4a09a53783b0dc97de8e20071bea874cbfe5", + "size": 135184, + "url": "https://launcher.mojang.com/v1/objects/aeda4a09a53783b0dc97de8e20071bea874cbfe5/LucidaTypewriterRegular.ttf" + }, + "raw": { + "sha1": "c144dcafe4faf2e79cfd74d8134a631f30234db1", + "size": 242700, + "url": "https://launcher.mojang.com/v1/objects/c144dcafe4faf2e79cfd74d8134a631f30234db1/LucidaTypewriterRegular.ttf" + } + }, + "executable": false, + "type": "file" + }, + "lib/fonts/fonts.dir": { + "downloads": { + "lzma": { + "sha1": "68f2dd93b215ec8b8d9409d2b9c825632c6b907d", + "size": 273, + "url": "https://launcher.mojang.com/v1/objects/68f2dd93b215ec8b8d9409d2b9c825632c6b907d/fonts.dir" + }, + "raw": { + "sha1": "97f40cca185c954adf5cc582345a7cb8e4c50578", + "size": 4041, + "url": "https://launcher.mojang.com/v1/objects/97f40cca185c954adf5cc582345a7cb8e4c50578/fonts.dir" + } + }, + "executable": false, + "type": "file" + }, + "lib/hijrah-config-umalqura.properties": { + "downloads": { + "lzma": { + "sha1": "02e8d296e3b18a450f1ed1547cbf2b7275664c9a", + "size": 1969, + "url": + "https://launcher.mojang.com/v1/objects/02e8d296e3b18a450f1ed1547cbf2b7275664c9a/hijrah-config-umalqura.properties" + }, + "raw": { + "sha1": "84aa425100740722e91f4725caf849e7863d12ba", + "size": 13962, + "url": + "https://launcher.mojang.com/v1/objects/84aa425100740722e91f4725caf849e7863d12ba/hijrah-config-umalqura.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/images": { + "type": "directory" + }, + "lib/images/cursors": { + "type": "directory" + }, + "lib/images/cursors/cursors.properties": { + "downloads": { + "lzma": { + "sha1": "612bd0f610ee1023947c4a2a8d3fc7d6f97e7d8f", + "size": 385, + "url": "https://launcher.mojang.com/v1/objects/612bd0f610ee1023947c4a2a8d3fc7d6f97e7d8f/cursors.properties" + }, + "raw": { + "sha1": "f2b9a22ddd0a77869497a64f28f07e89a7d41f48", + "size": 1274, + "url": "https://launcher.mojang.com/v1/objects/f2b9a22ddd0a77869497a64f28f07e89a7d41f48/cursors.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/images/cursors/invalid32x32.gif": { + "downloads": { + "raw": { + "sha1": "259edc45b4569427e8319895a444f4295d54348f", + "size": 153, + "url": "https://launcher.mojang.com/v1/objects/259edc45b4569427e8319895a444f4295d54348f/invalid32x32.gif" + } + }, + "executable": false, + "type": "file" + }, + "lib/images/cursors/motif_CopyDrop32x32.gif": { + "downloads": { + "raw": { + "sha1": "eb7620fae702172aa663a19d170a0b929d3b11d1", + "size": 158, + "url": "https://launcher.mojang.com/v1/objects/eb7620fae702172aa663a19d170a0b929d3b11d1/motif_CopyDrop32x32.gif" + } + }, + "executable": false, + "type": "file" + }, + "lib/images/cursors/motif_CopyNoDrop32x32.gif": { + "downloads": { + "raw": { + "sha1": "259edc45b4569427e8319895a444f4295d54348f", + "size": 153, + "url": "https://launcher.mojang.com/v1/objects/259edc45b4569427e8319895a444f4295d54348f/invalid32x32.gif" + } + }, + "executable": false, + "type": "file" + }, + "lib/images/cursors/motif_LinkDrop32x32.gif": { + "downloads": { + "raw": { + "sha1": "9699137f990c240e714481563181069c8f6c17bb", + "size": 162, + "url": "https://launcher.mojang.com/v1/objects/9699137f990c240e714481563181069c8f6c17bb/motif_LinkDrop32x32.gif" + } + }, + "executable": false, + "type": "file" + }, + "lib/images/cursors/motif_LinkNoDrop32x32.gif": { + "downloads": { + "raw": { + "sha1": "259edc45b4569427e8319895a444f4295d54348f", + "size": 153, + "url": "https://launcher.mojang.com/v1/objects/259edc45b4569427e8319895a444f4295d54348f/invalid32x32.gif" + } + }, + "executable": false, + "type": "file" + }, + "lib/images/cursors/motif_MoveDrop32x32.gif": { + "downloads": { + "raw": { + "sha1": "03c1617ce3c5ab8af03e46d30a8c8f31ab57fb1b", + "size": 141, + "url": "https://launcher.mojang.com/v1/objects/03c1617ce3c5ab8af03e46d30a8c8f31ab57fb1b/motif_MoveDrop32x32.gif" + } + }, + "executable": false, + "type": "file" + }, + "lib/images/cursors/motif_MoveNoDrop32x32.gif": { + "downloads": { + "raw": { + "sha1": "259edc45b4569427e8319895a444f4295d54348f", + "size": 153, + "url": "https://launcher.mojang.com/v1/objects/259edc45b4569427e8319895a444f4295d54348f/invalid32x32.gif" + } + }, + "executable": false, + "type": "file" + }, + "lib/images/icons": { + "type": "directory" + }, + "lib/images/icons/sun-java.png": { + "downloads": { + "raw": { + "sha1": "d101b693aa054f51097eebdfeed8b8a6ca7b55b8", + "size": 4707, + "url": "https://launcher.mojang.com/v1/objects/d101b693aa054f51097eebdfeed8b8a6ca7b55b8/sun-java.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/images/icons/sun-java_HighContrast.png": { + "downloads": { + "raw": { + "sha1": "a6b1e418d6b5d03719b96f61f0c5236a60970151", + "size": 3729, + "url": "https://launcher.mojang.com/v1/objects/a6b1e418d6b5d03719b96f61f0c5236a60970151/sun-java_HighContrast.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/images/icons/sun-java_HighContrastInverse.png": { + "downloads": { + "raw": { + "sha1": "2dda28b9bddc9b5b018e3e8a8b062a99d9b2f887", + "size": 3777, + "url": + "https://launcher.mojang.com/v1/objects/2dda28b9bddc9b5b018e3e8a8b062a99d9b2f887/sun-java_HighContrastInverse.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/images/icons/sun-java_LowContrast.png": { + "downloads": { + "raw": { + "sha1": "7714cc4e894c3626c8da6fe742ed22b2829122d9", + "size": 4012, + "url": "https://launcher.mojang.com/v1/objects/7714cc4e894c3626c8da6fe742ed22b2829122d9/sun-java_LowContrast.png" + } + }, + "executable": false, + "type": "file" + }, + "lib/javafx.properties": { + "downloads": { + "raw": { + "sha1": "49e6b75d109e5fd3f6cbe7cc5fa9a7980796d14d", + "size": 56, + "url": "https://launcher.mojang.com/v1/objects/49e6b75d109e5fd3f6cbe7cc5fa9a7980796d14d/javafx.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/javaws.jar": { + "downloads": { + "raw": { + "sha1": "04fa5ae04ead65b91be5dee575497e49ffd49fe9", + "size": 488118, + "url": "https://launcher.mojang.com/v1/objects/04fa5ae04ead65b91be5dee575497e49ffd49fe9/javaws.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/jce.jar": { + "downloads": { + "raw": { + "sha1": "5460adee09cc5fc8829c0acfc46c34670a7d70a0", + "size": 115646, + "url": "https://launcher.mojang.com/v1/objects/5460adee09cc5fc8829c0acfc46c34670a7d70a0/jce.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/jexec": { + "downloads": { + "lzma": { + "sha1": "2d4323d4e060f8126d026ca6c03b8972aedd2fab", + "size": 3311, + "url": "https://launcher.mojang.com/v1/objects/2d4323d4e060f8126d026ca6c03b8972aedd2fab/jexec" + }, + "raw": { + "sha1": "6aa01f1d8d103974164bcfaea03c04eeeefd7d41", + "size": 13376, + "url": "https://launcher.mojang.com/v1/objects/6aa01f1d8d103974164bcfaea03c04eeeefd7d41/jexec" + } + }, + "executable": true, + "type": "file" + }, + "lib/jfr": { + "type": "directory" + }, + "lib/jfr.jar": { + "downloads": { + "lzma": { + "sha1": "5b9d615c91c72f4fe356d9b4105946679452d1e1", + "size": 137982, + "url": "https://launcher.mojang.com/v1/objects/5b9d615c91c72f4fe356d9b4105946679452d1e1/jfr.jar" + }, + "raw": { + "sha1": "0f3fd66a336703d935bdc22ad8082bc51d34e534", + "size": 560713, + "url": "https://launcher.mojang.com/v1/objects/0f3fd66a336703d935bdc22ad8082bc51d34e534/jfr.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/jfr/default.jfc": { + "downloads": { + "lzma": { + "sha1": "373ddd878146dd8ce8991c2c5115a05a82859bdb", + "size": 2207, + "url": "https://launcher.mojang.com/v1/objects/373ddd878146dd8ce8991c2c5115a05a82859bdb/default.jfc" + }, + "raw": { + "sha1": "1a64b68d0e7d43f8149faba94440be54f4f24527", + "size": 20109, + "url": "https://launcher.mojang.com/v1/objects/1a64b68d0e7d43f8149faba94440be54f4f24527/default.jfc" + } + }, + "executable": false, + "type": "file" + }, + "lib/jfr/profile.jfc": { + "downloads": { + "lzma": { + "sha1": "3dcdc5feee3ccfb66bc8726b666944cd4bdadae3", + "size": 2199, + "url": "https://launcher.mojang.com/v1/objects/3dcdc5feee3ccfb66bc8726b666944cd4bdadae3/profile.jfc" + }, + "raw": { + "sha1": "5d7d08a595f76322c51ae43ea966fbba6b69eebe", + "size": 20065, + "url": "https://launcher.mojang.com/v1/objects/5d7d08a595f76322c51ae43ea966fbba6b69eebe/profile.jfc" + } + }, + "executable": false, + "type": "file" + }, + "lib/jfxswt.jar": { + "downloads": { + "raw": { + "sha1": "99d9a264c898d84c01e1c42565e7fe1a89dcd72d", + "size": 33932, + "url": "https://launcher.mojang.com/v1/objects/99d9a264c898d84c01e1c42565e7fe1a89dcd72d/jfxswt.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/jsse.jar": { + "downloads": { + "lzma": { + "sha1": "94a17dfbc2e76cd12c33970a15341424f875a9ce", + "size": 187549, + "url": "https://launcher.mojang.com/v1/objects/94a17dfbc2e76cd12c33970a15341424f875a9ce/jsse.jar" + }, + "raw": { + "sha1": "92c5c626e8a2d16f41272c0e404d4f992dd8310a", + "size": 675599, + "url": "https://launcher.mojang.com/v1/objects/92c5c626e8a2d16f41272c0e404d4f992dd8310a/jsse.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/jvm.hprof.txt": { + "downloads": { + "lzma": { + "sha1": "eccdb240a815b2a83a502749339b27bb8669965b", + "size": 1863, + "url": "https://launcher.mojang.com/v1/objects/eccdb240a815b2a83a502749339b27bb8669965b/jvm.hprof.txt" + }, + "raw": { + "sha1": "fbd61d52534cdd0c15df332114d469c65d001e33", + "size": 4226, + "url": "https://launcher.mojang.com/v1/objects/fbd61d52534cdd0c15df332114d469c65d001e33/jvm.hprof.txt" + } + }, + "executable": false, + "type": "file" + }, + "lib/locale": { + "type": "directory" + }, + "lib/locale/de": { + "type": "directory" + }, + "lib/locale/de/LC_MESSAGES": { + "type": "directory" + }, + "lib/locale/de/LC_MESSAGES/sunw_java_plugin.mo": { + "downloads": { + "lzma": { + "sha1": "3061d922907cc557208109088fc6ab81d577ff6f", + "size": 970, + "url": "https://launcher.mojang.com/v1/objects/3061d922907cc557208109088fc6ab81d577ff6f/sunw_java_plugin.mo" + }, + "raw": { + "sha1": "5b223a3d723ac1cfce63623fb109f2868d47d1b7", + "size": 2483, + "url": "https://launcher.mojang.com/v1/objects/5b223a3d723ac1cfce63623fb109f2868d47d1b7/sunw_java_plugin.mo" + } + }, + "executable": false, + "type": "file" + }, + "lib/locale/es": { + "type": "directory" + }, + "lib/locale/es/LC_MESSAGES": { + "type": "directory" + }, + "lib/locale/es/LC_MESSAGES/sunw_java_plugin.mo": { + "downloads": { + "lzma": { + "sha1": "24338049a89b323e17182b3a3006b50565d4fa0f", + "size": 979, + "url": "https://launcher.mojang.com/v1/objects/24338049a89b323e17182b3a3006b50565d4fa0f/sunw_java_plugin.mo" + }, + "raw": { + "sha1": "6cc63dc97f2fdb2ed799e48b1dc98c4f37cdecc1", + "size": 2477, + "url": "https://launcher.mojang.com/v1/objects/6cc63dc97f2fdb2ed799e48b1dc98c4f37cdecc1/sunw_java_plugin.mo" + } + }, + "executable": false, + "type": "file" + }, + "lib/locale/fr": { + "type": "directory" + }, + "lib/locale/fr/LC_MESSAGES": { + "type": "directory" + }, + "lib/locale/fr/LC_MESSAGES/sunw_java_plugin.mo": { + "downloads": { + "lzma": { + "sha1": "22796a48ef39f57d2d6fa70f41308e493d7f05c1", + "size": 1033, + "url": "https://launcher.mojang.com/v1/objects/22796a48ef39f57d2d6fa70f41308e493d7f05c1/sunw_java_plugin.mo" + }, + "raw": { + "sha1": "d9d5b458db6e83fdf85c3526aeee3f57c4929840", + "size": 2746, + "url": "https://launcher.mojang.com/v1/objects/d9d5b458db6e83fdf85c3526aeee3f57c4929840/sunw_java_plugin.mo" + } + }, + "executable": false, + "type": "file" + }, + "lib/locale/it": { + "type": "directory" + }, + "lib/locale/it/LC_MESSAGES": { + "type": "directory" + }, + "lib/locale/it/LC_MESSAGES/sunw_java_plugin.mo": { + "downloads": { + "lzma": { + "sha1": "59a4cae38bfb8927745674d0efc2f284bc277987", + "size": 958, + "url": "https://launcher.mojang.com/v1/objects/59a4cae38bfb8927745674d0efc2f284bc277987/sunw_java_plugin.mo" + }, + "raw": { + "sha1": "f6e72e3b2141ccc3dffab10ae14a754e494577ba", + "size": 2434, + "url": "https://launcher.mojang.com/v1/objects/f6e72e3b2141ccc3dffab10ae14a754e494577ba/sunw_java_plugin.mo" + } + }, + "executable": false, + "type": "file" + }, + "lib/locale/ja": { + "type": "directory" + }, + "lib/locale/ja/LC_MESSAGES": { + "type": "directory" + }, + "lib/locale/ja/LC_MESSAGES/sunw_java_plugin.mo": { + "downloads": { + "lzma": { + "sha1": "7d6aeed563e1cefcf0224cf522048468088884a9", + "size": 1036, + "url": "https://launcher.mojang.com/v1/objects/7d6aeed563e1cefcf0224cf522048468088884a9/sunw_java_plugin.mo" + }, + "raw": { + "sha1": "378881a8cb8dd2aebb43eacd0c68519be4f258b1", + "size": 2415, + "url": "https://launcher.mojang.com/v1/objects/378881a8cb8dd2aebb43eacd0c68519be4f258b1/sunw_java_plugin.mo" + } + }, + "executable": false, + "type": "file" + }, + "lib/locale/ko": { + "type": "directory" + }, + "lib/locale/ko.UTF-8": { + "type": "directory" + }, + "lib/locale/ko.UTF-8/LC_MESSAGES": { + "type": "directory" + }, + "lib/locale/ko.UTF-8/LC_MESSAGES/sunw_java_plugin.mo": { + "downloads": { + "lzma": { + "sha1": "12ee3b21511e8497d95ea0ba9d6fe519227d0b16", + "size": 1069, + "url": "https://launcher.mojang.com/v1/objects/12ee3b21511e8497d95ea0ba9d6fe519227d0b16/sunw_java_plugin.mo" + }, + "raw": { + "sha1": "cb19df01c59662dbe2f4050b1290d374b82fe1fa", + "size": 2753, + "url": "https://launcher.mojang.com/v1/objects/cb19df01c59662dbe2f4050b1290d374b82fe1fa/sunw_java_plugin.mo" + } + }, + "executable": false, + "type": "file" + }, + "lib/locale/ko/LC_MESSAGES": { + "type": "directory" + }, + "lib/locale/ko/LC_MESSAGES/sunw_java_plugin.mo": { + "downloads": { + "lzma": { + "sha1": "6e2e47c64c360517fd436bc79c823b5679a1efe6", + "size": 996, + "url": "https://launcher.mojang.com/v1/objects/6e2e47c64c360517fd436bc79c823b5679a1efe6/sunw_java_plugin.mo" + }, + "raw": { + "sha1": "12c8a118d150c78f719314df6dec49a967af71e9", + "size": 2399, + "url": "https://launcher.mojang.com/v1/objects/12c8a118d150c78f719314df6dec49a967af71e9/sunw_java_plugin.mo" + } + }, + "executable": false, + "type": "file" + }, + "lib/locale/pt_BR": { + "type": "directory" + }, + "lib/locale/pt_BR/LC_MESSAGES": { + "type": "directory" + }, + "lib/locale/pt_BR/LC_MESSAGES/sunw_java_plugin.mo": { + "downloads": { + "lzma": { + "sha1": "bcaa7e7916493f071f1bf64bf58c6b038e3569c9", + "size": 940, + "url": "https://launcher.mojang.com/v1/objects/bcaa7e7916493f071f1bf64bf58c6b038e3569c9/sunw_java_plugin.mo" + }, + "raw": { + "sha1": "a3bc0c43994c53c59bba94982cf95f6d36283dd0", + "size": 2420, + "url": "https://launcher.mojang.com/v1/objects/a3bc0c43994c53c59bba94982cf95f6d36283dd0/sunw_java_plugin.mo" + } + }, + "executable": false, + "type": "file" + }, + "lib/locale/sv": { + "type": "directory" + }, + "lib/locale/sv/LC_MESSAGES": { + "type": "directory" + }, + "lib/locale/sv/LC_MESSAGES/sunw_java_plugin.mo": { + "downloads": { + "lzma": { + "sha1": "76017835d6261fe2eedbcbe5eb08a7484c3080c5", + "size": 946, + "url": "https://launcher.mojang.com/v1/objects/76017835d6261fe2eedbcbe5eb08a7484c3080c5/sunw_java_plugin.mo" + }, + "raw": { + "sha1": "09a47686edec4bbb34e82fbd08559f8bb6266544", + "size": 2359, + "url": "https://launcher.mojang.com/v1/objects/09a47686edec4bbb34e82fbd08559f8bb6266544/sunw_java_plugin.mo" + } + }, + "executable": false, + "type": "file" + }, + "lib/locale/zh": { + "type": "directory" + }, + "lib/locale/zh.GBK": { + "type": "directory" + }, + "lib/locale/zh.GBK/LC_MESSAGES": { + "type": "directory" + }, + "lib/locale/zh.GBK/LC_MESSAGES/sunw_java_plugin.mo": { + "downloads": { + "lzma": { + "sha1": "75fd04045bf5890b8bb822770bfdb90a2e9ea65b", + "size": 902, + "url": "https://launcher.mojang.com/v1/objects/75fd04045bf5890b8bb822770bfdb90a2e9ea65b/sunw_java_plugin.mo" + }, + "raw": { + "sha1": "7006fe7767b8807441a1f359a90509b3e507b0d1", + "size": 2002, + "url": "https://launcher.mojang.com/v1/objects/7006fe7767b8807441a1f359a90509b3e507b0d1/sunw_java_plugin.mo" + } + }, + "executable": false, + "type": "file" + }, + "lib/locale/zh/LC_MESSAGES": { + "type": "directory" + }, + "lib/locale/zh/LC_MESSAGES/sunw_java_plugin.mo": { + "downloads": { + "lzma": { + "sha1": "75fd04045bf5890b8bb822770bfdb90a2e9ea65b", + "size": 902, + "url": "https://launcher.mojang.com/v1/objects/75fd04045bf5890b8bb822770bfdb90a2e9ea65b/sunw_java_plugin.mo" + }, + "raw": { + "sha1": "7006fe7767b8807441a1f359a90509b3e507b0d1", + "size": 2002, + "url": "https://launcher.mojang.com/v1/objects/7006fe7767b8807441a1f359a90509b3e507b0d1/sunw_java_plugin.mo" + } + }, + "executable": false, + "type": "file" + }, + "lib/locale/zh_HK.BIG5HK": { + "type": "directory" + }, + "lib/locale/zh_HK.BIG5HK/LC_MESSAGES": { + "type": "directory" + }, + "lib/locale/zh_HK.BIG5HK/LC_MESSAGES/sunw_java_plugin.mo": { + "downloads": { + "lzma": { + "sha1": "3a1397bb1b1741697be1479232b6d9599940c851", + "size": 912, + "url": "https://launcher.mojang.com/v1/objects/3a1397bb1b1741697be1479232b6d9599940c851/sunw_java_plugin.mo" + }, + "raw": { + "sha1": "c6023544067278c78599921f1032de353ff7da42", + "size": 2025, + "url": "https://launcher.mojang.com/v1/objects/c6023544067278c78599921f1032de353ff7da42/sunw_java_plugin.mo" + } + }, + "executable": false, + "type": "file" + }, + "lib/locale/zh_TW": { + "type": "directory" + }, + "lib/locale/zh_TW.BIG5": { + "type": "directory" + }, + "lib/locale/zh_TW.BIG5/LC_MESSAGES": { + "type": "directory" + }, + "lib/locale/zh_TW.BIG5/LC_MESSAGES/sunw_java_plugin.mo": { + "downloads": { + "lzma": { + "sha1": "3a1397bb1b1741697be1479232b6d9599940c851", + "size": 912, + "url": "https://launcher.mojang.com/v1/objects/3a1397bb1b1741697be1479232b6d9599940c851/sunw_java_plugin.mo" + }, + "raw": { + "sha1": "c6023544067278c78599921f1032de353ff7da42", + "size": 2025, + "url": "https://launcher.mojang.com/v1/objects/c6023544067278c78599921f1032de353ff7da42/sunw_java_plugin.mo" + } + }, + "executable": false, + "type": "file" + }, + "lib/locale/zh_TW/LC_MESSAGES": { + "type": "directory" + }, + "lib/locale/zh_TW/LC_MESSAGES/sunw_java_plugin.mo": { + "downloads": { + "lzma": { + "sha1": "c05e610e75182f0c4e77f3e7a4d9670ed62bf63c", + "size": 897, + "url": "https://launcher.mojang.com/v1/objects/c05e610e75182f0c4e77f3e7a4d9670ed62bf63c/sunw_java_plugin.mo" + }, + "raw": { + "sha1": "f9b972dd059eae3cd337dfcef6a178e8ed8a7db6", + "size": 2025, + "url": "https://launcher.mojang.com/v1/objects/f9b972dd059eae3cd337dfcef6a178e8ed8a7db6/sunw_java_plugin.mo" + } + }, + "executable": false, + "type": "file" + }, + "lib/logging.properties": { + "downloads": { + "lzma": { + "sha1": "642202a58e5216d086ad37c0b5a633be802edc78", + "size": 896, + "url": "https://launcher.mojang.com/v1/objects/642202a58e5216d086ad37c0b5a633be802edc78/logging.properties" + }, + "raw": { + "sha1": "89da8094484891f9ec1fa40c6c8b61f94c5869d0", + "size": 2455, + "url": "https://launcher.mojang.com/v1/objects/89da8094484891f9ec1fa40c6c8b61f94c5869d0/logging.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/management": { + "type": "directory" + }, + "lib/management-agent.jar": { + "downloads": { + "lzma": { + "sha1": "3ea0bf17e14b3428296a0f4011bf4025fcbfa4bd", + "size": 243, + "url": "https://launcher.mojang.com/v1/objects/3ea0bf17e14b3428296a0f4011bf4025fcbfa4bd/management-agent.jar" + }, + "raw": { + "sha1": "9fbed36522aa3a80bac08a328942cbc5ef39ca8e", + "size": 381, + "url": "https://launcher.mojang.com/v1/objects/9fbed36522aa3a80bac08a328942cbc5ef39ca8e/management-agent.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/management/jmxremote.access": { + "downloads": { + "lzma": { + "sha1": "69042ff1b14165db19c9d728614639dec16d6a31", + "size": 1419, + "url": "https://launcher.mojang.com/v1/objects/69042ff1b14165db19c9d728614639dec16d6a31/jmxremote.access" + }, + "raw": { + "sha1": "21200eaad898ba4a2a8834a032efb6616fabb930", + "size": 3998, + "url": "https://launcher.mojang.com/v1/objects/21200eaad898ba4a2a8834a032efb6616fabb930/jmxremote.access" + } + }, + "executable": false, + "type": "file" + }, + "lib/management/jmxremote.password.template": { + "downloads": { + "lzma": { + "sha1": "556c64b1e920766f8867be3964de6e49f5b81a60", + "size": 1129, + "url": "https://launcher.mojang.com/v1/objects/556c64b1e920766f8867be3964de6e49f5b81a60/jmxremote.password.template" + }, + "raw": { + "sha1": "c1e0f01408bf20fbbb8b4810520c725f70050db5", + "size": 2856, + "url": "https://launcher.mojang.com/v1/objects/c1e0f01408bf20fbbb8b4810520c725f70050db5/jmxremote.password.template" + } + }, + "executable": false, + "type": "file" + }, + "lib/management/management.properties": { + "downloads": { + "lzma": { + "sha1": "3e52f9baa6394ca6956845424c607e5cde5d3c67", + "size": 3176, + "url": "https://launcher.mojang.com/v1/objects/3e52f9baa6394ca6956845424c607e5cde5d3c67/management.properties" + }, + "raw": { + "sha1": "e0451d8d7d9e84d7b1c39ec7d00993307a5cbbf1", + "size": 14630, + "url": "https://launcher.mojang.com/v1/objects/e0451d8d7d9e84d7b1c39ec7d00993307a5cbbf1/management.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/management/snmp.acl.template": { + "downloads": { + "lzma": { + "sha1": "9a4aa6396c3b488b0663bed5e5ecb762985669c9", + "size": 1121, + "url": "https://launcher.mojang.com/v1/objects/9a4aa6396c3b488b0663bed5e5ecb762985669c9/snmp.acl.template" + }, + "raw": { + "sha1": "2e9f9ac287274532eb1f0d1afcefd7f3e97cc794", + "size": 3376, + "url": "https://launcher.mojang.com/v1/objects/2e9f9ac287274532eb1f0d1afcefd7f3e97cc794/snmp.acl.template" + } + }, + "executable": false, + "type": "file" + }, + "lib/meta-index": { + "downloads": { + "lzma": { + "sha1": "1ac60b31362fda4725c665b591c5fbe384cbc8c1", + "size": 788, + "url": "https://launcher.mojang.com/v1/objects/1ac60b31362fda4725c665b591c5fbe384cbc8c1/meta-index" + }, + "raw": { + "sha1": "bf204f09242203e713c31785158a0792f9edb600", + "size": 2034, + "url": "https://launcher.mojang.com/v1/objects/bf204f09242203e713c31785158a0792f9edb600/meta-index" + } + }, + "executable": false, + "type": "file" + }, + "lib/net.properties": { + "downloads": { + "lzma": { + "sha1": "e9ec3981a0797bf55bb87b24d9eb651ce7e6916b", + "size": 1830, + "url": "https://launcher.mojang.com/v1/objects/e9ec3981a0797bf55bb87b24d9eb651ce7e6916b/net.properties" + }, + "raw": { + "sha1": "fd9471742eb759f4478bb1de9a0dc0527265b6ea", + "size": 5352, + "url": "https://launcher.mojang.com/v1/objects/fd9471742eb759f4478bb1de9a0dc0527265b6ea/net.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/oblique-fonts": { + "type": "directory" + }, + "lib/oblique-fonts/LucidaSansDemiOblique.ttf": { + "downloads": { + "lzma": { + "sha1": "49c8980c1b89bbdbab59d0f5bd5bebf0afcb93b2", + "size": 38580, + "url": "https://launcher.mojang.com/v1/objects/49c8980c1b89bbdbab59d0f5bd5bebf0afcb93b2/LucidaSansDemiOblique.ttf" + }, + "raw": { + "sha1": "53e4e12a675ac222469341c3dbc102464a1be4c7", + "size": 91352, + "url": "https://launcher.mojang.com/v1/objects/53e4e12a675ac222469341c3dbc102464a1be4c7/LucidaSansDemiOblique.ttf" + } + }, + "executable": false, + "type": "file" + }, + "lib/oblique-fonts/LucidaSansOblique.ttf": { + "downloads": { + "lzma": { + "sha1": "553123c0edcd08035dede4ffd92b5b81c9a7538a", + "size": 116575, + "url": "https://launcher.mojang.com/v1/objects/553123c0edcd08035dede4ffd92b5b81c9a7538a/LucidaSansOblique.ttf" + }, + "raw": { + "sha1": "95a195ad4fc520b3e395c85b747fc3024d118dd9", + "size": 253724, + "url": "https://launcher.mojang.com/v1/objects/95a195ad4fc520b3e395c85b747fc3024d118dd9/LucidaSansOblique.ttf" + } + }, + "executable": false, + "type": "file" + }, + "lib/oblique-fonts/LucidaTypewriterBoldOblique.ttf": { + "downloads": { + "lzma": { + "sha1": "2475b08151556ad4d89bb1d2b6494c6bee9abd82", + "size": 29954, + "url": "https://launcher.mojang.com/v1/objects/2475b08151556ad4d89bb1d2b6494c6bee9abd82/LucidaTypewriterBoldOblique.ttf" + }, + "raw": { + "sha1": "f331fc8b0cc494702bc46b690f2b8eed36469a02", + "size": 63168, + "url": "https://launcher.mojang.com/v1/objects/f331fc8b0cc494702bc46b690f2b8eed36469a02/LucidaTypewriterBoldOblique.ttf" + } + }, + "executable": false, + "type": "file" + }, + "lib/oblique-fonts/LucidaTypewriterOblique.ttf": { + "downloads": { + "lzma": { + "sha1": "5b970bc3b7abb21dce1aa28ff7f03459d351e552", + "size": 60133, + "url": "https://launcher.mojang.com/v1/objects/5b970bc3b7abb21dce1aa28ff7f03459d351e552/LucidaTypewriterOblique.ttf" + }, + "raw": { + "sha1": "f8ea00db73f8a89a27674d050edc37c2280930e1", + "size": 137484, + "url": "https://launcher.mojang.com/v1/objects/f8ea00db73f8a89a27674d050edc37c2280930e1/LucidaTypewriterOblique.ttf" + } + }, + "executable": false, + "type": "file" + }, + "lib/oblique-fonts/fonts.dir": { + "downloads": { + "lzma": { + "sha1": "067528c789bd713c7c3f34e779aa6e2e8253dcf6", + "size": 188, + "url": "https://launcher.mojang.com/v1/objects/067528c789bd713c7c3f34e779aa6e2e8253dcf6/fonts.dir" + }, + "raw": { + "sha1": "5aee54ffba9e33de56fd84ef64fa496b898585bb", + "size": 2115, + "url": "https://launcher.mojang.com/v1/objects/5aee54ffba9e33de56fd84ef64fa496b898585bb/fonts.dir" + } + }, + "executable": false, + "type": "file" + }, + "lib/plugin.jar": { + "downloads": { + "raw": { + "sha1": "3f250842c79112bae5369e372025b166990820e8", + "size": 950772, + "url": "https://launcher.mojang.com/v1/objects/3f250842c79112bae5369e372025b166990820e8/plugin.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/psfont.properties.ja": { + "downloads": { + "lzma": { + "sha1": "7ca1cc244ed251cd1eb2347f1eea37d7d18c8ad4", + "size": 701, + "url": "https://launcher.mojang.com/v1/objects/7ca1cc244ed251cd1eb2347f1eea37d7d18c8ad4/psfont.properties.ja" + }, + "raw": { + "sha1": "56ed1c661eeede17b4fae8c9de7b5edbad387abc", + "size": 2796, + "url": "https://launcher.mojang.com/v1/objects/56ed1c661eeede17b4fae8c9de7b5edbad387abc/psfont.properties.ja" + } + }, + "executable": false, + "type": "file" + }, + "lib/psfontj2d.properties": { + "downloads": { + "lzma": { + "sha1": "4252fa01af8739a3545e2b705e3383892e22ab40", + "size": 2278, + "url": "https://launcher.mojang.com/v1/objects/4252fa01af8739a3545e2b705e3383892e22ab40/psfontj2d.properties" + }, + "raw": { + "sha1": "aa327a22a49967f4d74afeee6726f505f209692f", + "size": 10393, + "url": "https://launcher.mojang.com/v1/objects/aa327a22a49967f4d74afeee6726f505f209692f/psfontj2d.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/resources.jar": { + "downloads": { + "lzma": { + "sha1": "1b0e08441750dc17efe4b527aa146da6cc14e8a6", + "size": 579294, + "url": "https://launcher.mojang.com/v1/objects/1b0e08441750dc17efe4b527aa146da6cc14e8a6/resources.jar" + }, + "raw": { + "sha1": "daa021906e4648d4c37e798c11733dc2047f2da1", + "size": 3505206, + "url": "https://launcher.mojang.com/v1/objects/daa021906e4648d4c37e798c11733dc2047f2da1/resources.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/rt.jar": { + "downloads": { + "lzma": { + "sha1": "fc4a8681aeda29c2a2a3fd11bad7729543283f3d", + "size": 14378994, + "url": "https://launcher.mojang.com/v1/objects/fc4a8681aeda29c2a2a3fd11bad7729543283f3d/rt.jar" + }, + "raw": { + "sha1": "5396b0954a20f3210f1f4f1886ead30880d6ebfe", + "size": 66334986, + "url": "https://launcher.mojang.com/v1/objects/5396b0954a20f3210f1f4f1886ead30880d6ebfe/rt.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/security": { + "type": "directory" + }, + "lib/security/blacklist": { + "downloads": { + "lzma": { + "sha1": "8206fce6c1d91a39fdf78e8e79e953913994a1cd", + "size": 1969, + "url": "https://launcher.mojang.com/v1/objects/8206fce6c1d91a39fdf78e8e79e953913994a1cd/blacklist" + }, + "raw": { + "sha1": "d4ffb3857eab403955ce9d156e46d056061e6a5a", + "size": 4054, + "url": "https://launcher.mojang.com/v1/objects/d4ffb3857eab403955ce9d156e46d056061e6a5a/blacklist" + } + }, + "executable": false, + "type": "file" + }, + "lib/security/blacklisted.certs": { + "downloads": { + "lzma": { + "sha1": "8311bead054caf6cfe678d4b7998de4caaabfa53", + "size": 806, + "url": "https://launcher.mojang.com/v1/objects/8311bead054caf6cfe678d4b7998de4caaabfa53/blacklisted.certs" + }, + "raw": { + "sha1": "c5c005c29a80493f5c31cd7eb629ac1b9c752404", + "size": 1273, + "url": "https://launcher.mojang.com/v1/objects/c5c005c29a80493f5c31cd7eb629ac1b9c752404/blacklisted.certs" + } + }, + "executable": false, + "type": "file" + }, + "lib/security/cacerts": { + "downloads": { + "lzma": { + "sha1": "654dd94809655d5b28385cbb5eba8d6ad9f2c1aa", + "size": 67802, + "url": "https://launcher.mojang.com/v1/objects/654dd94809655d5b28385cbb5eba8d6ad9f2c1aa/cacerts" + }, + "raw": { + "sha1": "2917859c443c68e19f93abcd1315c3c2904cbef9", + "size": 104430, + "url": "https://launcher.mojang.com/v1/objects/2917859c443c68e19f93abcd1315c3c2904cbef9/cacerts" + } + }, + "executable": false, + "type": "file" + }, + "lib/security/java.policy": { + "downloads": { + "lzma": { + "sha1": "b601c420d02ef3dbd8595453d08fdef91134e8b5", + "size": 647, + "url": "https://launcher.mojang.com/v1/objects/b601c420d02ef3dbd8595453d08fdef91134e8b5/java.policy" + }, + "raw": { + "sha1": "c0112209a567b3b523cfed7041709f9440227968", + "size": 2466, + "url": "https://launcher.mojang.com/v1/objects/c0112209a567b3b523cfed7041709f9440227968/java.policy" + } + }, + "executable": false, + "type": "file" + }, + "lib/security/java.security": { + "downloads": { + "lzma": { + "sha1": "531620e82ca0365ce8dc97096bb0ac5a7ace5952", + "size": 10959, + "url": "https://launcher.mojang.com/v1/objects/531620e82ca0365ce8dc97096bb0ac5a7ace5952/java.security" + }, + "raw": { + "sha1": "5dcc17a168c53d0b366784e520bd4d55aa61ac18", + "size": 41528, + "url": "https://launcher.mojang.com/v1/objects/5dcc17a168c53d0b366784e520bd4d55aa61ac18/java.security" + } + }, + "executable": false, + "type": "file" + }, + "lib/security/javaws.policy": { + "downloads": { + "raw": { + "sha1": "4384ca5e4d32f7dd86d8baddd1e690730d74e694", + "size": 98, + "url": "https://launcher.mojang.com/v1/objects/4384ca5e4d32f7dd86d8baddd1e690730d74e694/javaws.policy" + } + }, + "executable": false, + "type": "file" + }, + "lib/security/policy": { + "type": "directory" + }, + "lib/security/policy/limited": { + "type": "directory" + }, + "lib/security/policy/limited/US_export_policy.jar": { + "downloads": { + "raw": { + "sha1": "7d69ea3b385bc067738520f1b5c549e1084be285", + "size": 3026, + "url": "https://launcher.mojang.com/v1/objects/7d69ea3b385bc067738520f1b5c549e1084be285/US_export_policy.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/security/policy/limited/local_policy.jar": { + "downloads": { + "raw": { + "sha1": "238b8826e110f58acb2e1959773b0a577cd4d569", + "size": 3527, + "url": "https://launcher.mojang.com/v1/objects/238b8826e110f58acb2e1959773b0a577cd4d569/local_policy.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/security/policy/unlimited": { + "type": "directory" + }, + "lib/security/policy/unlimited/US_export_policy.jar": { + "downloads": { + "raw": { + "sha1": "f6fb2af1e87fc622cda194a7d6b5f5f069653ff1", + "size": 3023, + "url": "https://launcher.mojang.com/v1/objects/f6fb2af1e87fc622cda194a7d6b5f5f069653ff1/US_export_policy.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/security/policy/unlimited/local_policy.jar": { + "downloads": { + "raw": { + "sha1": "517368ab2cbaf6b42ea0b963f98eeedd996e83e3", + "size": 3035, + "url": "https://launcher.mojang.com/v1/objects/517368ab2cbaf6b42ea0b963f98eeedd996e83e3/local_policy.jar" + } + }, + "executable": false, + "type": "file" + }, + "lib/security/trusted.libraries": { + "downloads": { + "raw": { + "sha1": "da39a3ee5e6b4b0d3255bfef95601890afd80709", + "size": 0, + "url": "https://launcher.mojang.com/v1/objects/da39a3ee5e6b4b0d3255bfef95601890afd80709/trusted.libraries" + } + }, + "executable": false, + "type": "file" + }, + "lib/sound.properties": { + "downloads": { + "lzma": { + "sha1": "3b5f7e4ec437d79048af35094290577f483b3fe1", + "size": 473, + "url": "https://launcher.mojang.com/v1/objects/3b5f7e4ec437d79048af35094290577f483b3fe1/sound.properties" + }, + "raw": { + "sha1": "9afceb218059d981d0fa9f07aad3c5097cf41b0c", + "size": 1210, + "url": "https://launcher.mojang.com/v1/objects/9afceb218059d981d0fa9f07aad3c5097cf41b0c/sound.properties" + } + }, + "executable": false, + "type": "file" + }, + "lib/tzdb.dat": { + "downloads": { + "lzma": { + "sha1": "39c69339965484afe89c14111baeeb862fdefd97", + "size": 32547, + "url": "https://launcher.mojang.com/v1/objects/39c69339965484afe89c14111baeeb862fdefd97/tzdb.dat" + }, + "raw": { + "sha1": "b59c07e3619271a3b9861e999f4b138e971baf69", + "size": 105734, + "url": "https://launcher.mojang.com/v1/objects/b59c07e3619271a3b9861e999f4b138e971baf69/tzdb.dat" + } + }, + "executable": false, + "type": "file" + }, + "man": { + "type": "directory" + }, + "man/ja": { + "target": "ja_JP.UTF-8", + "type": "link" + }, + "man/ja_JP.UTF-8": { + "type": "directory" + }, + "man/ja_JP.UTF-8/man1": { + "type": "directory" + }, + "man/ja_JP.UTF-8/man1/java.1": { + "downloads": { + "lzma": { + "sha1": "f9da09710b6c6df23c256e324a0c4df00a0d6ded", + "size": 25461, + "url": "https://launcher.mojang.com/v1/objects/f9da09710b6c6df23c256e324a0c4df00a0d6ded/java.1" + }, + "raw": { + "sha1": "b0b12a0bb66e6171771ca4b1dfca32fb759bcaec", + "size": 148688, + "url": "https://launcher.mojang.com/v1/objects/b0b12a0bb66e6171771ca4b1dfca32fb759bcaec/java.1" + } + }, + "executable": false, + "type": "file" + }, + "man/ja_JP.UTF-8/man1/javaws.1": { + "downloads": { + "lzma": { + "sha1": "6188fae453ca09ccb19be5c9f4d2059926b36267", + "size": 2154, + "url": "https://launcher.mojang.com/v1/objects/6188fae453ca09ccb19be5c9f4d2059926b36267/javaws.1" + }, + "raw": { + "sha1": "8f39d928870268ace07bedfebd18db1e1d07fc37", + "size": 6641, + "url": "https://launcher.mojang.com/v1/objects/8f39d928870268ace07bedfebd18db1e1d07fc37/javaws.1" + } + }, + "executable": false, + "type": "file" + }, + "man/ja_JP.UTF-8/man1/jjs.1": { + "downloads": { + "lzma": { + "sha1": "6e42b989d28b185dc1aab50c0389834e649a37d4", + "size": 3452, + "url": "https://launcher.mojang.com/v1/objects/6e42b989d28b185dc1aab50c0389834e649a37d4/jjs.1" + }, + "raw": { + "sha1": "e023322a2013912315a2bd1034e6f829a27c76e0", + "size": 11365, + "url": "https://launcher.mojang.com/v1/objects/e023322a2013912315a2bd1034e6f829a27c76e0/jjs.1" + } + }, + "executable": false, + "type": "file" + }, + "man/ja_JP.UTF-8/man1/keytool.1": { + "downloads": { + "lzma": { + "sha1": "a78134a4bddd53d684a70aa677e51a215db1c9cb", + "size": 20698, + "url": "https://launcher.mojang.com/v1/objects/a78134a4bddd53d684a70aa677e51a215db1c9cb/keytool.1" + }, + "raw": { + "sha1": "148583c837eaaf6333ccfd8c9e8df08574e14b0c", + "size": 111033, + "url": "https://launcher.mojang.com/v1/objects/148583c837eaaf6333ccfd8c9e8df08574e14b0c/keytool.1" + } + }, + "executable": false, + "type": "file" + }, + "man/ja_JP.UTF-8/man1/orbd.1": { + "downloads": { + "lzma": { + "sha1": "326af0dcbff173ef8aee29163dbe146d7389cc3e", + "size": 4225, + "url": "https://launcher.mojang.com/v1/objects/326af0dcbff173ef8aee29163dbe146d7389cc3e/orbd.1" + }, + "raw": { + "sha1": "95651622d33c08286858ec337edd3ea72acd93dc", + "size": 16092, + "url": "https://launcher.mojang.com/v1/objects/95651622d33c08286858ec337edd3ea72acd93dc/orbd.1" + } + }, + "executable": false, + "type": "file" + }, + "man/ja_JP.UTF-8/man1/pack200.1": { + "downloads": { + "lzma": { + "sha1": "e0eedafa748c61a44e5be4355fe9d44b05048e80", + "size": 4293, + "url": "https://launcher.mojang.com/v1/objects/e0eedafa748c61a44e5be4355fe9d44b05048e80/pack200.1" + }, + "raw": { + "sha1": "aa21a0ab75707f7fc66e83c7a392e69b37ddf80e", + "size": 14482, + "url": "https://launcher.mojang.com/v1/objects/aa21a0ab75707f7fc66e83c7a392e69b37ddf80e/pack200.1" + } + }, + "executable": false, + "type": "file" + }, + "man/ja_JP.UTF-8/man1/policytool.1": { + "downloads": { + "lzma": { + "sha1": "3c766ed12dab58166169d35680c392a6be1814a1", + "size": 1380, + "url": "https://launcher.mojang.com/v1/objects/3c766ed12dab58166169d35680c392a6be1814a1/policytool.1" + }, + "raw": { + "sha1": "80879c74e072a98fad6f32b3283331aaf9bd002f", + "size": 4020, + "url": "https://launcher.mojang.com/v1/objects/80879c74e072a98fad6f32b3283331aaf9bd002f/policytool.1" + } + }, + "executable": false, + "type": "file" + }, + "man/ja_JP.UTF-8/man1/rmid.1": { + "downloads": { + "lzma": { + "sha1": "1e20779d990beacc32a48237777d670fcc47ca14", + "size": 4836, + "url": "https://launcher.mojang.com/v1/objects/1e20779d990beacc32a48237777d670fcc47ca14/rmid.1" + }, + "raw": { + "sha1": "7e40cb8003d098d6e36f45640b26f979ac94b5c5", + "size": 19715, + "url": "https://launcher.mojang.com/v1/objects/7e40cb8003d098d6e36f45640b26f979ac94b5c5/rmid.1" + } + }, + "executable": false, + "type": "file" + }, + "man/ja_JP.UTF-8/man1/rmiregistry.1": { + "downloads": { + "lzma": { + "sha1": "aaf4ffe07e954f8696eef1ecb7a5e244628d0ad9", + "size": 1627, + "url": "https://launcher.mojang.com/v1/objects/aaf4ffe07e954f8696eef1ecb7a5e244628d0ad9/rmiregistry.1" + }, + "raw": { + "sha1": "c53c52f3ae7a011c135894c9fc51b741e729c33d", + "size": 4557, + "url": "https://launcher.mojang.com/v1/objects/c53c52f3ae7a011c135894c9fc51b741e729c33d/rmiregistry.1" + } + }, + "executable": false, + "type": "file" + }, + "man/ja_JP.UTF-8/man1/servertool.1": { + "downloads": { + "lzma": { + "sha1": "3b9e624e9d1cf2959b438a35061162e2100ddecd", + "size": 2626, + "url": "https://launcher.mojang.com/v1/objects/3b9e624e9d1cf2959b438a35061162e2100ddecd/servertool.1" + }, + "raw": { + "sha1": "50ab8bcd9dd9d0b1a3d81348fbce1c8f82e7189e", + "size": 9081, + "url": "https://launcher.mojang.com/v1/objects/50ab8bcd9dd9d0b1a3d81348fbce1c8f82e7189e/servertool.1" + } + }, + "executable": false, + "type": "file" + }, + "man/ja_JP.UTF-8/man1/tnameserv.1": { + "downloads": { + "lzma": { + "sha1": "bb3106ff74c60a76de3d20659b9c2128c70f3bf2", + "size": 4478, + "url": "https://launcher.mojang.com/v1/objects/bb3106ff74c60a76de3d20659b9c2128c70f3bf2/tnameserv.1" + }, + "raw": { + "sha1": "01e714671ecd1167edcb5310b16a9c59c33c3eaa", + "size": 17722, + "url": "https://launcher.mojang.com/v1/objects/01e714671ecd1167edcb5310b16a9c59c33c3eaa/tnameserv.1" + } + }, + "executable": false, + "type": "file" + }, + "man/ja_JP.UTF-8/man1/unpack200.1": { + "downloads": { + "lzma": { + "sha1": "c115a881cf800b08df294df55d9f250ae944e33c", + "size": 1973, + "url": "https://launcher.mojang.com/v1/objects/c115a881cf800b08df294df55d9f250ae944e33c/unpack200.1" + }, + "raw": { + "sha1": "7c882bba0067367a41ad84868d18793b8a7397a3", + "size": 5382, + "url": "https://launcher.mojang.com/v1/objects/7c882bba0067367a41ad84868d18793b8a7397a3/unpack200.1" + } + }, + "executable": false, + "type": "file" + }, + "man/man1": { + "type": "directory" + }, + "man/man1/java.1": { + "downloads": { + "lzma": { + "sha1": "06a6b0275c202bf698d73ca71f95618d56d81c15", + "size": 25796, + "url": "https://launcher.mojang.com/v1/objects/06a6b0275c202bf698d73ca71f95618d56d81c15/java.1" + }, + "raw": { + "sha1": "69fec7a341aa91f18dbdcdb95952dede7e1b689a", + "size": 124796, + "url": "https://launcher.mojang.com/v1/objects/69fec7a341aa91f18dbdcdb95952dede7e1b689a/java.1" + } + }, + "executable": false, + "type": "file" + }, + "man/man1/javaws.1": { + "downloads": { + "lzma": { + "sha1": "4bae251c6dfb5420f56928815cf80d0b6d517a1f", + "size": 1759, + "url": "https://launcher.mojang.com/v1/objects/4bae251c6dfb5420f56928815cf80d0b6d517a1f/javaws.1" + }, + "raw": { + "sha1": "e61e44e101b1bc119c2d2d4b10320f38b36a8036", + "size": 4897, + "url": "https://launcher.mojang.com/v1/objects/e61e44e101b1bc119c2d2d4b10320f38b36a8036/javaws.1" + } + }, + "executable": false, + "type": "file" + }, + "man/man1/jjs.1": { + "downloads": { + "lzma": { + "sha1": "29683cf2bd47015c9461b688749ddffd95f6671d", + "size": 1881, + "url": "https://launcher.mojang.com/v1/objects/29683cf2bd47015c9461b688749ddffd95f6671d/jjs.1" + }, + "raw": { + "sha1": "78d419bd3a7f3e0802d5220e690429194b5d1beb", + "size": 4932, + "url": "https://launcher.mojang.com/v1/objects/78d419bd3a7f3e0802d5220e690429194b5d1beb/jjs.1" + } + }, + "executable": false, + "type": "file" + }, + "man/man1/keytool.1": { + "downloads": { + "lzma": { + "sha1": "b67e5126d43713ee3675706724b34061578b42db", + "size": 19690, + "url": "https://launcher.mojang.com/v1/objects/b67e5126d43713ee3675706724b34061578b42db/keytool.1" + }, + "raw": { + "sha1": "4c976f86057ab779763fcfb98f5702ebef47f629", + "size": 86925, + "url": "https://launcher.mojang.com/v1/objects/4c976f86057ab779763fcfb98f5702ebef47f629/keytool.1" + } + }, + "executable": false, + "type": "file" + }, + "man/man1/orbd.1": { + "downloads": { + "lzma": { + "sha1": "147064d6f7e027002e296bb246ae572d0ce0495b", + "size": 3708, + "url": "https://launcher.mojang.com/v1/objects/147064d6f7e027002e296bb246ae572d0ce0495b/orbd.1" + }, + "raw": { + "sha1": "64201e1846fcf1dcc45c786ffeab89426d1c7742", + "size": 12180, + "url": "https://launcher.mojang.com/v1/objects/64201e1846fcf1dcc45c786ffeab89426d1c7742/orbd.1" + } + }, + "executable": false, + "type": "file" + }, + "man/man1/pack200.1": { + "downloads": { + "lzma": { + "sha1": "fe17486bbe9c58cf4182fa056b9cd124e8295607", + "size": 3724, + "url": "https://launcher.mojang.com/v1/objects/fe17486bbe9c58cf4182fa056b9cd124e8295607/pack200.1" + }, + "raw": { + "sha1": "26826cf52b89924f2d2a60d6cda798891875eae6", + "size": 11623, + "url": "https://launcher.mojang.com/v1/objects/26826cf52b89924f2d2a60d6cda798891875eae6/pack200.1" + } + }, + "executable": false, + "type": "file" + }, + "man/man1/policytool.1": { + "downloads": { + "lzma": { + "sha1": "bd154e7c39aca71d15b2098c588866f8d95bc743", + "size": 1122, + "url": "https://launcher.mojang.com/v1/objects/bd154e7c39aca71d15b2098c588866f8d95bc743/policytool.1" + }, + "raw": { + "sha1": "ab296625155d9a2b25ecc2b4feff2f741b3ad136", + "size": 3235, + "url": "https://launcher.mojang.com/v1/objects/ab296625155d9a2b25ecc2b4feff2f741b3ad136/policytool.1" + } + }, + "executable": false, + "type": "file" + }, + "man/man1/rmid.1": { + "downloads": { + "lzma": { + "sha1": "6a7da234e7f43ebca5c4ba8cd862fda3be62fbaa", + "size": 4255, + "url": "https://launcher.mojang.com/v1/objects/6a7da234e7f43ebca5c4ba8cd862fda3be62fbaa/rmid.1" + }, + "raw": { + "sha1": "6f10e214d7950a6a8460524e41dc700f112f89e5", + "size": 15979, + "url": "https://launcher.mojang.com/v1/objects/6f10e214d7950a6a8460524e41dc700f112f89e5/rmid.1" + } + }, + "executable": false, + "type": "file" + }, + "man/man1/rmiregistry.1": { + "downloads": { + "lzma": { + "sha1": "f40dd17e3a734600ad1828b0c42d3a1685c4c520", + "size": 1301, + "url": "https://launcher.mojang.com/v1/objects/f40dd17e3a734600ad1828b0c42d3a1685c4c520/rmiregistry.1" + }, + "raw": { + "sha1": "d9a3d23fab689df5bb9a792b88f462f939b49f70", + "size": 3449, + "url": "https://launcher.mojang.com/v1/objects/d9a3d23fab689df5bb9a792b88f462f939b49f70/rmiregistry.1" + } + }, + "executable": false, + "type": "file" + }, + "man/man1/servertool.1": { + "downloads": { + "lzma": { + "sha1": "74f1e10712202cd3ca0ff5833de05b7ee67092e1", + "size": 2307, + "url": "https://launcher.mojang.com/v1/objects/74f1e10712202cd3ca0ff5833de05b7ee67092e1/servertool.1" + }, + "raw": { + "sha1": "e6c7b510740ac8681a9bfb5f4ee1f0306125b728", + "size": 7237, + "url": "https://launcher.mojang.com/v1/objects/e6c7b510740ac8681a9bfb5f4ee1f0306125b728/servertool.1" + } + }, + "executable": false, + "type": "file" + }, + "man/man1/tnameserv.1": { + "downloads": { + "lzma": { + "sha1": "4bec7f4e070d023f124f9352a8971d7acd249a15", + "size": 3955, + "url": "https://launcher.mojang.com/v1/objects/4bec7f4e070d023f124f9352a8971d7acd249a15/tnameserv.1" + }, + "raw": { + "sha1": "a31dbbe800d49cb371fab9a4b73d22c3bf8799ad", + "size": 15747, + "url": "https://launcher.mojang.com/v1/objects/a31dbbe800d49cb371fab9a4b73d22c3bf8799ad/tnameserv.1" + } + }, + "executable": false, + "type": "file" + }, + "man/man1/unpack200.1": { + "downloads": { + "lzma": { + "sha1": "f8e73863187929debf2ea6dadefb2995ec7917e7", + "size": 1672, + "url": "https://launcher.mojang.com/v1/objects/f8e73863187929debf2ea6dadefb2995ec7917e7/unpack200.1" + }, + "raw": { + "sha1": "437f7233d738cb9b822e99003127049005663e0f", + "size": 4244, + "url": "https://launcher.mojang.com/v1/objects/437f7233d738cb9b822e99003127049005663e0f/unpack200.1" + } + }, + "executable": false, + "type": "file" + }, + "plugin": { + "type": "directory" + }, + "plugin/desktop": { + "type": "directory" + }, + "plugin/desktop/sun_java.desktop": { + "downloads": { + "lzma": { + "sha1": "49ab0ccb54c3be68281d05055bc56a88b1281d3c", + "size": 447, + "url": "https://launcher.mojang.com/v1/objects/49ab0ccb54c3be68281d05055bc56a88b1281d3c/sun_java.desktop" + }, + "raw": { + "sha1": "79120ee8160ad6f3c9b90c2641fb7edf3af96b5d", + "size": 624, + "url": "https://launcher.mojang.com/v1/objects/79120ee8160ad6f3c9b90c2641fb7edf3af96b5d/sun_java.desktop" + } + }, + "executable": false, + "type": "file" + }, + "plugin/desktop/sun_java.png": { + "downloads": { + "raw": { + "sha1": "699c41e97a35414e72a80327a54d6e14e874e951", + "size": 4351, + "url": "https://launcher.mojang.com/v1/objects/699c41e97a35414e72a80327a54d6e14e874e951/sun_java.png" + } + }, + "executable": false, + "type": "file" + }, + "release": { + "downloads": { + "raw": { + "sha1": "cb462682644c0275d94a45b759108815f3112064", + "size": 424, + "url": "https://launcher.mojang.com/v1/objects/cb462682644c0275d94a45b759108815f3112064/release" + } + }, + "executable": false, + "type": "file" + } + } +} \ No newline at end of file