Merge branch 'develop' into rename-groups

Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
This commit is contained in:
TheKodeToad 2023-08-15 11:03:19 +01:00 committed by GitHub
commit 8cff7c4de6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
750 changed files with 21315 additions and 19331 deletions

View File

@ -1,16 +1,17 @@
--- ---
Language: Cpp BasedOnStyle: Chromium
BasedOnStyle: Chromium
IndentWidth: 4 IndentWidth: 4
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AllowShortIfStatementsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false
ColumnLimit: 140
---
Language: Cpp
AlignConsecutiveMacros: None
AlignConsecutiveAssignments: None
BraceWrapping: BraceWrapping:
AfterFunction: true AfterFunction: true
SplitEmptyFunction: false SplitEmptyFunction: false
SplitEmptyRecord: false SplitEmptyRecord: false
SplitEmptyNamespace: false SplitEmptyNamespace: false
BreakBeforeBraces: Custom BreakBeforeBraces: Custom
BreakConstructorInitializers: BeforeComma BreakConstructorInitializers: BeforeComma
ColumnLimit: 140
Cpp11BracedListStyle: false Cpp11BracedListStyle: false

View File

@ -24,6 +24,12 @@ on:
CACHIX_AUTH_TOKEN: CACHIX_AUTH_TOKEN:
description: Private token for authenticating against Cachix cache description: Private token for authenticating against Cachix cache
required: false 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: jobs:
build: build:
@ -68,7 +74,7 @@ jobs:
qt_ver: 6 qt_ver: 6
qt_host: windows qt_host: windows
qt_arch: '' qt_arch: ''
qt_version: '6.5.1' qt_version: '6.5.2'
qt_modules: 'qt5compat qtimageformats' qt_modules: 'qt5compat qtimageformats'
qt_tools: '' qt_tools: ''
@ -80,7 +86,7 @@ jobs:
qt_ver: 6 qt_ver: 6
qt_host: windows qt_host: windows
qt_arch: 'win64_msvc2019_arm64' qt_arch: 'win64_msvc2019_arm64'
qt_version: '6.5.1' qt_version: '6.5.2'
qt_modules: 'qt5compat qtimageformats' qt_modules: 'qt5compat qtimageformats'
qt_tools: '' qt_tools: ''
@ -90,7 +96,7 @@ jobs:
qt_ver: 6 qt_ver: 6
qt_host: mac qt_host: mac
qt_arch: '' qt_arch: ''
qt_version: '6.5.0' qt_version: '6.5.2'
qt_modules: 'qt5compat qtimageformats' qt_modules: 'qt5compat qtimageformats'
qt_tools: '' qt_tools: ''
@ -152,7 +158,7 @@ jobs:
- name: Setup ccache - name: Setup ccache
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug' 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: with:
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }} 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-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/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 ${{ github.workspace }}/.github/scripts/prepare_JREs.sh
sudo apt install libopengl0 sudo apt install libopengl0
@ -387,8 +395,8 @@ jobs:
cd ${{ env.INSTALL_DIR }} cd ${{ env.INSTALL_DIR }}
if ("${{ matrix.qt_ver }}" -eq "5") 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 ${{ runner.workspace }}/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/libssl-1_1.dll -Destination libssl-1_1.dll
} }
cd ${{ github.workspace }} cd ${{ github.workspace }}
@ -425,7 +433,7 @@ jobs:
run: | run: |
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead 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 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 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) - name: Package (Windows, installer)
@ -466,11 +474,15 @@ jobs:
- name: Package AppImage (Linux) - name: Package AppImage (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 5 if: runner.os == 'Linux' && matrix.qt_ver != 5
shell: bash shell: bash
env:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
run: | run: |
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr 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 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 "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 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 ${{ 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/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/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/ 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" LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
export LD_LIBRARY_PATH 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 ./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 # UPLOAD BUILDS
## ##
@ -562,6 +599,13 @@ jobs:
with: with:
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
path: 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) - name: ccache stats (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != '' if: runner.os == 'Windows' && matrix.msystem != ''
@ -586,33 +630,3 @@ jobs:
with: with:
bundle: "Prism Launcher.flatpak" bundle: "Prism Launcher.flatpak"
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml 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

View File

@ -43,7 +43,8 @@ jobs:
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz 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-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-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-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
@ -78,9 +79,8 @@ jobs:
- name: Create release - name: Create release
id: create_release id: create_release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
token: ${{ secrets.GITHUB_TOKEN }}
tag_name: ${{ github.ref }} tag_name: ${{ github.ref }}
name: Prism Launcher ${{ env.VERSION }} name: Prism Launcher ${{ env.VERSION }}
draft: true draft: true
@ -88,7 +88,8 @@ jobs:
files: | files: |
PrismLauncher-Linux-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-Portable-${{ 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-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip

View File

@ -25,4 +25,6 @@ jobs:
pr-title: "chore(nix): update lockfile" pr-title: "chore(nix): update lockfile"
pr-labels: | pr-labels: |
Linux Linux
packaging
simple change simple change
changelog:omit

3
.gitmodules vendored
View File

@ -19,3 +19,6 @@
[submodule "libraries/cmark"] [submodule "libraries/cmark"]
path = libraries/cmark path = libraries/cmark
url = https://github.com/commonmark/cmark.git url = https://github.com/commonmark/cmark.git
[submodule "flatpak/shared-modules"]
path = flatpak/shared-modules
url = https://github.com/flathub/shared-modules.git

View File

@ -33,6 +33,13 @@ if(MSVC)
# Use /W4 as /Wall includes unnesserey warnings such as added padding to structs # Use /W4 as /Wall includes unnesserey warnings such as added padding to structs
set(CMAKE_CXX_FLAGS "/GS /permissive- /W4 ${CMAKE_CXX_FLAGS}") 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 # 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 # This implicitly selects an entrypoint specific to the subsystem selected
# qtmain/QtEntryPointLib provides the correct entrypoint (wWinMain) for gui programs # 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 CXXFLAGS for build targets
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}") 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 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") 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_ID}" MATCHES "Clang")
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
# using clang with clang-cl front end # using clang with clang-cl front end
message(STATUS "Address Sanitizer available on Clang MSVC frontend") message(STATUS "Address Sanitizer available on Clang MSVC frontend")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
else() else()
# AppleClang and Clang # AppleClang and Clang
message(STATUS "Address Sanitizer available on Clang") message(STATUS "Address Sanitizer available on Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_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 -O1 -fno-omit-frame-pointer") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
endif() endif()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# GCC # GCC
message(STATUS "Address Sanitizer available on GCC") message(STATUS "Address Sanitizer available on GCC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_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 -O1 -fno-omit-frame-pointer") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
link_libraries("asan") link_libraries("asan")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
message(STATUS "Address Sanitizer available on MSVC") message(STATUS "Address Sanitizer available on MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
else() else()
message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}") message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
endif() endif()
endif() endif()
option(ENABLE_LTO "Enable Link Time Optimization" off) option(ENABLE_LTO "Enable Link Time Optimization" off)
if(ENABLE_LTO) if(ENABLE_LTO)
@ -318,6 +326,8 @@ add_subdirectory(program_info)
####################################### Install layout ####################################### ####################################### Install layout #######################################
set(Launcher_ENABLE_UPDATER NO)
if(NOT (UNIX AND APPLE)) if(NOT (UNIX AND APPLE))
# Install "portable.txt" if selected component is "portable" # Install "portable.txt" if selected component is "portable"
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Portable_File}" DESTINATION "." COMPONENT portable EXCLUDE_FROM_ALL) 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_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}")
set(MACOSX_BUNDLE_LONG_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_ICON_FILE ${Launcher_Name}.icns)
set(MACOSX_BUNDLE_COPYRIGHT "© 2022 ${Launcher_Copyright_Mac}") set(MACOSX_BUNDLE_COPYRIGHT "© 2022-2023 ${Launcher_Copyright_Mac}")
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=") 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") 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_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") 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 # directories to look for dependencies
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${MACOSX_SPARKLE_DIR}) 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 # install as bundle
set(INSTALL_BUNDLE "full") set(INSTALL_BUNDLE "full" CACHE STRING "Use fixup_bundle to bundle dependencies")
# Add the icon # Add the icon
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns) 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}") set(JARS_DEST_DIR "share/${Launcher_Name}")
# install as bundle with no dependencies included # 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 RPATH
SET(Launcher_BINARY_RPATH "$ORIGIN/") 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}) set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
# install as bundle # install as bundle
set(INSTALL_BUNDLE "full") set(INSTALL_BUNDLE "full" CACHE STRING "Use fixup_bundle to bundle dependencies")
else() else()
message(FATAL_ERROR "Platform not supported") message(FATAL_ERROR "Platform not supported")
endif() endif()

View File

@ -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: This can be done by appending `-s` to your `git commit` call, or by manually appending the following text to your commit message:
``` ```text
<commit message> <commit message>
Signed-off-by: Author name <Author email> Signed-off-by: Author name <Author email>
@ -27,7 +27,7 @@ Signed-off-by: Author name <Author email>
By signing off your work, you agree to the terms below: By signing off your work, you agree to the terms below:
``` ```text
Developer's Certificate of Origin 1.1 Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that: By making a contribution to this project, I certify that:
@ -62,7 +62,6 @@ 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-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 [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 ## 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. We use [automated backports](https://github.com/PrismLauncher/PrismLauncher/blob/develop/.github/workflows/backport.yml) to merge specific contributions from develop into `release` branches.

View File

@ -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:** - **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:** - **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 ## 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 <https://github.com/PrismLauncher/Translations> 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 <https://github.com/PrismLauncher/Translations>
## Building ## Building
@ -82,14 +82,16 @@ Thanks to the awesome people over at [MacStadium](https://www.macstadium.com/),
## Forking/Redistributing/Custom builds policy ## 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 (<https://prismlauncher.org>). - Make it clear that your fork is not Prism Launcher and is not endorsed by or affiliated with the Prism Launcher project (<https://prismlauncher.org>).
- 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). - 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. 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) - [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) - [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions)

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify

View File

@ -36,8 +36,8 @@
*/ */
#pragma once #pragma once
#include <QString>
#include <QList> #include <QList>
#include <QString>
/** /**
* \brief The Config class holds all the build-time information passed from the build system. * \brief The Config class holds all the build-time information passed from the build system.
@ -145,7 +145,7 @@ class Config {
QString AUTH_BASE = "https://authserver.mojang.com/"; QString AUTH_BASE = "https://authserver.mojang.com/";
QString IMGUR_BASE_URL = "https://api.imgur.com/3/"; QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists 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/"; 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_STAGING_URL = "https://staging-api.modrinth.com/v2";
QString MODRINTH_PROD_URL = "https://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"; QString FLAME_BASE_URL = "https://api.curseforge.com/v1";

View File

@ -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
$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_WARNINGS_CXX}>
# C warnings
$<$<COMPILE_LANGUAGE:C>:${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 "^<CMAKE_CXX_COMPILER>")
_set_project_warnings_add_target_link_option(
${_project_name} "$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_WARNINGS_CXX}>"
)
endif()
if(CMAKE_C_LINK_EXECUTABLE MATCHES "^<CMAKE_C_COMPILER>")
_set_project_warnings_add_target_link_option(
${_project_name} "$<$<COMPILE_LANGUAGE:C>:${PROJECT_WARNINGS_C}>"
)
endif()
endfunction()

30
flake.lock generated
View File

@ -21,11 +21,11 @@
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1688254665, "lastModified": 1690933134,
"narHash": "sha256-8FHEgBrr7gYNiS/NzCxIO3m4hvtLRW9YY1nYo1ivm3o=", "narHash": "sha256-ab989mN63fQZBFrkk4Q8bYxQCktuHmBIBqUG1jl6/FQ=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "267149c58a14d15f7f81b4d737308421de9d7152", "rev": "59cf3f1447cfc75087e7273b04b31e689a8599fb",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -76,11 +76,11 @@
"libnbtplusplus": { "libnbtplusplus": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1650031308, "lastModified": 1690036783,
"narHash": "sha256-TvVOjkUobYJD9itQYueELJX3wmecvEdCbJ0FinW2mL4=", "narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=",
"owner": "PrismLauncher", "owner": "PrismLauncher",
"repo": "libnbtplusplus", "repo": "libnbtplusplus",
"rev": "2203af7eeb48c45398139b583615134efd8d407f", "rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -91,11 +91,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1688221086, "lastModified": 1691853136,
"narHash": "sha256-cdW6qUL71cNWhHCpMPOJjlw0wzSRP0pVlRn2vqX/VVg=", "narHash": "sha256-wTzDsRV4HN8A2Sl0SVQY0q8ILs90CD43Ha//7gNZE+E=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "cd99c2b3c9f160cd004318e0697f90bbd5960825", "rev": "f0451844bbdf545f696f029d1448de4906c7f753",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -108,11 +108,11 @@
"nixpkgs-lib": { "nixpkgs-lib": {
"locked": { "locked": {
"dir": "lib", "dir": "lib",
"lastModified": 1688049487, "lastModified": 1690881714,
"narHash": "sha256-100g4iaKC9MalDjUW9iN6Jl/OocTDtXdeAj7pEGIRh4=", "narHash": "sha256-h/nXluEqdiQHs1oSgkOOWF+j8gcJMWhwnZ9PFabN6q0=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "4bc72cae107788bf3f24f30db2e2f685c9298dc9", "rev": "9e1960bc196baf6881340d53dccb203a951745a2",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -138,11 +138,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1688386108, "lastModified": 1691747570,
"narHash": "sha256-Vffto9QaVonzYAcPlAzd0soqWYpPpKk60dfNLSIXcFA=", "narHash": "sha256-J3fnIwJtHVQ0tK2JMBv4oAmII+1mCdXdpeCxtIsrL2A=",
"owner": "cachix", "owner": "cachix",
"repo": "pre-commit-hooks.nix", "repo": "pre-commit-hooks.nix",
"rev": "42587d3414d1747999a5f71e92a83cf6547b62da", "rev": "c5ac3aa3324bd8aebe8622a3fc92eeb3975d317a",
"type": "github" "type": "github"
}, },
"original": { "original": {

22
flatpak/libdecor.json Normal file
View File

@ -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"
]
}

View File

@ -5,13 +5,6 @@ sdk: org.kde.Sdk
sdk-extensions: sdk-extensions:
- org.freedesktop.Sdk.Extension.openjdk17 - org.freedesktop.Sdk.Extension.openjdk17
- org.freedesktop.Sdk.Extension.openjdk8 - 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 command: prismlauncher
finish-args: finish-args:
@ -25,22 +18,34 @@ finish-args:
- --filesystem=xdg-run/app/com.discordapp.Discord:create - --filesystem=xdg-run/app/com.discordapp.Discord:create
# Mod drag&drop # Mod drag&drop
- --filesystem=xdg-download:ro - --filesystem=xdg-download:ro
# FTBApp import
- --filesystem=~/.ftba:ro
cleanup:
- /lib/libGLU*
modules: 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 - name: prismlauncher
buildsystem: cmake-ninja buildsystem: cmake-ninja
builddir: true
config-opts: config-opts:
- -DLauncher_BUILD_PLATFORM=flatpak - -DLauncher_BUILD_PLATFORM=flatpak
- -DCMAKE_BUILD_TYPE=Debug - -DCMAKE_BUILD_TYPE=RelWithDebInfo
- -DLauncher_QT_VERSION_MAJOR=5 - -DLauncher_QT_VERSION_MAJOR=5
build-options: build-options:
env: env:
JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17 JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17
JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac
sources: sources:
- type: dir - type: dir
path: ../ path: ../
builddir: true
- name: openjdk - name: openjdk
buildsystem: simple buildsystem: simple
build-commands: build-commands:
@ -49,14 +54,45 @@ modules:
- mv /app/jre /app/jdk/17 - mv /app/jre /app/jdk/17
- /usr/lib/sdk/openjdk8/install.sh - /usr/lib/sdk/openjdk8/install.sh
- mv /app/jre /app/jdk/8 - 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 - name: xrandr
buildsystem: autotools buildsystem: autotools
sources: sources:
- type: archive - type: archive
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.1.tar.xz url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.2.tar.xz
sha256: 7bc76daf9d72f8aff885efad04ce06b90488a1a169d118dea8a2b661832e8762 sha256: c8bee4790d9058bacc4b6246456c58021db58a87ddda1a9d0139bf5f18f1f240
cleanup: [/share/man, /bin/xkeystone] 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 - name: gamemode
buildsystem: meson buildsystem: meson
config-opts: config-opts:
@ -67,19 +103,56 @@ modules:
# post-install is running inside the build dir, we need it from the source though # post-install is running inside the build dir, we need it from the source though
- install -Dm755 ../data/gamemoderun -t /app/bin - install -Dm755 ../data/gamemoderun -t /app/bin
sources: sources:
- type: git - type: archive
url: https://github.com/FeralInteractive/gamemode archive-type: tar-gzip
tag: "1.7" url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.7
commit: 4dc99dff76218718763a6b07fc1900fa6d1dafd9 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 - name: enhance
buildsystem: simple buildsystem: simple
build-commands: build-commands:
- mkdir -p /app/utils/gamescope
- install -Dm755 prime-run /app/bin/prime-run - install -Dm755 prime-run /app/bin/prime-run
- mv /app/bin/prismlauncher /app/bin/prismrun - mv /app/bin/prismlauncher /app/bin/prismrun
- install -Dm755 prismlauncher /app/bin/prismlauncher - install -Dm755 prismlauncher /app/bin/prismlauncher
sources: sources:
- type: file - type: file
path: ../flatpak/prime-run path: prime-run
- type: file - type: file
path: ../flatpak/prismlauncher path: prismlauncher

View File

@ -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,

View File

@ -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;
}

View File

@ -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)

View File

@ -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 *

View File

@ -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"; test -S "$XDG_RUNTIME_DIR"/discord-ipc-"$i" || ln -sf {app/com.discordapp.Discord,"$XDG_RUNTIME_DIR"}/discord-ipc-"$i";
done done
export PATH="${PATH}${PATH:+:}/app/utils/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin" export PATH="${PATH}${PATH:+:}/usr/lib/extensions/vulkan/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 VK_LAYER_PATH="/usr/lib/extensions/vulkan/share/vulkan/implicit_layer.d/"
exec /app/bin/prismrun "$@" exec /app/bin/prismrun "$@"

@ -0,0 +1 @@
Subproject commit 45094ca570be383d06df729b6972830ec63bd3df

6
garnix.yaml Normal file
View File

@ -0,0 +1,6 @@
builds:
exclude: []
include:
- "checks.x86_64-linux.*"
- "devShells.*.*"
- "packages.*.*"

File diff suppressed because it is too large Load Diff

View File

@ -38,16 +38,17 @@
#pragma once #pragma once
#include <QApplication> #include <QApplication>
#include <memory> #include <QDateTime>
#include <QDebug> #include <QDebug>
#include <QFlag> #include <QFlag>
#include <QIcon> #include <QIcon>
#include <QDateTime>
#include <QUrl> #include <QUrl>
#include <memory>
#include <BaseInstance.h> #include <BaseInstance.h>
#include "minecraft/launch/MinecraftServerTarget.h" #include "minecraft/launch/MinecraftServerTarget.h"
#include "ui/themes/CatPack.h"
class LaunchController; class LaunchController;
class LocalPeer; class LocalPeer;
@ -70,27 +71,22 @@ class TranslationsModel;
class ITheme; class ITheme;
class MCEditTool; class MCEditTool;
class ThemeManager; class ThemeManager;
class IconTheme;
namespace Meta { namespace Meta {
class Index; class Index;
} }
#if defined(APPLICATION) #if defined(APPLICATION)
#undef APPLICATION #undef APPLICATION
#endif #endif
#define APPLICATION (static_cast<Application *>(QCoreApplication::instance())) #define APPLICATION (static_cast<Application*>(QCoreApplication::instance()))
class Application : public QApplication class Application : public QApplication {
{
// friends for the purpose of limiting access to deprecated stuff // friends for the purpose of limiting access to deprecated stuff
Q_OBJECT Q_OBJECT
public: public:
enum Status { enum Status { StartingUp, Failed, Succeeded, Initialized };
StartingUp,
Failed,
Succeeded,
Initialized
};
enum Capability { enum Capability {
None = 0, None = 0,
@ -102,33 +98,21 @@ public:
}; };
Q_DECLARE_FLAGS(Capabilities, Capability) Q_DECLARE_FLAGS(Capabilities, Capability)
public: public:
Application(int &argc, char **argv); Application(int& argc, char** argv);
virtual ~Application(); virtual ~Application();
bool event(QEvent* event) override; bool event(QEvent* event) override;
std::shared_ptr<SettingsObject> settings() const { std::shared_ptr<SettingsObject> settings() const { return m_settings; }
return m_settings;
}
qint64 timeSinceStart() const { qint64 timeSinceStart() const { return startTime.msecsTo(QDateTime::currentDateTime()); }
return startTime.msecsTo(QDateTime::currentDateTime());
}
QIcon getThemedIcon(const QString& name); QIcon getThemedIcon(const QString& name);
void setIconTheme(const QString& name); ThemeManager* themeManager() { return m_themeManager.get(); }
void applyCurrentlySelectedTheme(bool initial = false); shared_qobject_ptr<ExternalUpdater> updater() { return m_updater; }
QList<ITheme*> getValidApplicationThemes();
void setApplicationTheme(const QString& name);
shared_qobject_ptr<ExternalUpdater> updater() {
return m_updater;
}
void triggerUpdateCheck(); void triggerUpdateCheck();
@ -136,29 +120,17 @@ public:
std::shared_ptr<JavaInstallList> javalist(); std::shared_ptr<JavaInstallList> javalist();
std::shared_ptr<InstanceList> instances() const { std::shared_ptr<InstanceList> instances() const { return m_instances; }
return m_instances;
}
std::shared_ptr<IconList> icons() const { std::shared_ptr<IconList> icons() const { return m_icons; }
return m_icons;
}
MCEditTool *mcedit() const { MCEditTool* mcedit() const { return m_mcedit.get(); }
return m_mcedit.get();
}
shared_qobject_ptr<AccountList> accounts() const { shared_qobject_ptr<AccountList> accounts() const { return m_accounts; }
return m_accounts;
}
Status status() const { Status status() const { return m_status; }
return m_status;
}
const QMap<QString, std::shared_ptr<BaseProfilerFactory>> &profilers() const { const QMap<QString, std::shared_ptr<BaseProfilerFactory>>& profilers() const { return m_profilers; }
return m_profilers;
}
void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password); void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
@ -183,35 +155,29 @@ public:
QString getUserAgentUncached(); QString getUserAgentUncached();
/// this is the root of the 'installation'. Used for automatic updates /// this is the root of the 'installation'. Used for automatic updates
const QString &root() { const QString& root() { return m_rootPath; }
return m_rootPath;
}
bool isPortable() { bool isPortable() { return m_portable; }
return m_portable;
}
const Capabilities capabilities() { const Capabilities capabilities() { return m_capabilities; }
return m_capabilities;
}
/*! /*!
* Opens a json file using either a system default editor, or, if not empty, the editor * Opens a json file using either a system default editor, or, if not empty, the editor
* specified in the settings * specified in the settings
*/ */
bool openJsonEditor(const QString &filename); bool openJsonEditor(const QString& filename);
InstanceWindow *showInstanceWindow(InstancePtr instance, QString page = QString()); InstanceWindow* showInstanceWindow(InstancePtr instance, QString page = QString());
MainWindow *showMainWindow(bool minimized = false); MainWindow* showMainWindow(bool minimized = false);
void updateIsRunning(bool running); void updateIsRunning(bool running);
bool updatesAreAllowed(); bool updatesAreAllowed();
void ShowGlobalSettings(class QWidget * parent, QString open_page = QString()); void ShowGlobalSettings(class QWidget* parent, QString open_page = QString());
int suitableMaxMem(); int suitableMaxMem();
signals: signals:
void updateAllowedChanged(bool status); void updateAllowedChanged(bool status);
void globalSettingsAboutToOpen(); void globalSettingsAboutToOpen();
void globalSettingsClosed(); void globalSettingsClosed();
@ -221,39 +187,37 @@ signals:
void clickedOnDock(); void clickedOnDock();
#endif #endif
public slots: public slots:
bool launch( bool launch(InstancePtr instance,
InstancePtr instance, bool online = true,
bool online = true, bool demo = false,
bool demo = false, BaseProfilerFactory* profiler = nullptr,
BaseProfilerFactory *profiler = nullptr, MinecraftServerTargetPtr serverToJoin = nullptr,
MinecraftServerTargetPtr serverToJoin = nullptr, MinecraftAccountPtr accountToUse = nullptr);
MinecraftAccountPtr accountToUse = nullptr
);
bool kill(InstancePtr instance); bool kill(InstancePtr instance);
void closeCurrentWindow(); void closeCurrentWindow();
private slots: private slots:
void on_windowClose(); void on_windowClose();
void messageReceived(const QByteArray & message); void messageReceived(const QByteArray& message);
void controllerSucceeded(); void controllerSucceeded();
void controllerFailed(const QString & error); void controllerFailed(const QString& error);
void setupWizardFinished(int status); void setupWizardFinished(int status);
private: private:
bool handleDataMigration(const QString & currentData, const QString & oldData, const QString & name, const QString & configFile) const; bool handleDataMigration(const QString& currentData, const QString& oldData, const QString& name, const QString& configFile) const;
bool createSetupWizard(); bool createSetupWizard();
void performMainStartupAction(); void performMainStartupAction();
// sets the fatal error message and m_status to Failed. // 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 addRunningInstance();
void subRunningInstance(); void subRunningInstance();
bool shouldExitNow() const; bool shouldExitNow() const;
private: private:
QDateTime startTime; QDateTime startTime;
shared_qobject_ptr<QNetworkAccessManager> m_network; shared_qobject_ptr<QNetworkAccessManager> m_network;
@ -279,7 +243,7 @@ private:
QString m_rootPath; QString m_rootPath;
Status m_status = Application::StartingUp; Status m_status = Application::StartingUp;
Capabilities m_capabilities; Capabilities m_capabilities;
bool m_portable = false; bool m_portable = false;
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive; Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive;
@ -292,7 +256,7 @@ private:
// FIXME: attach to instances instead. // FIXME: attach to instances instead.
struct InstanceXtras { struct InstanceXtras {
InstanceWindow * window = nullptr; InstanceWindow* window = nullptr;
shared_qobject_ptr<LaunchController> controller; shared_qobject_ptr<LaunchController> controller;
}; };
std::map<QString, InstanceXtras> m_instanceExtras; std::map<QString, InstanceXtras> m_instanceExtras;
@ -303,13 +267,14 @@ private:
bool m_updateRunning = false; bool m_updateRunning = false;
// main window, if any // main window, if any
MainWindow * m_mainWindow = nullptr; MainWindow* m_mainWindow = nullptr;
// peer launcher instance connector - used to implement single instance launcher and signalling // peer launcher instance connector - used to implement single instance launcher and signalling
LocalPeer * m_peerInstance = nullptr; LocalPeer* m_peerInstance = nullptr;
SetupWizard * m_setupWizard = nullptr; SetupWizard* m_setupWizard = nullptr;
public:
public:
QString m_instanceIdToLaunch; QString m_instanceIdToLaunch;
QString m_serverToJoin; QString m_serverToJoin;
QString m_profileToUse; QString m_profileToUse;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -39,7 +39,8 @@
#include <QJsonObject> #include <QJsonObject>
#include "Json.h" #include "Json.h"
void ApplicationMessage::parse(const QByteArray & input) { void ApplicationMessage::parse(const QByteArray& input)
{
auto doc = Json::requireDocument(input, "ApplicationMessage"); auto doc = Json::requireDocument(input, "ApplicationMessage");
auto root = Json::requireObject(doc, "ApplicationMessage"); auto root = Json::requireObject(doc, "ApplicationMessage");
@ -47,12 +48,13 @@ void ApplicationMessage::parse(const QByteArray & input) {
args.clear(); args.clear();
auto parsedArgs = root.value("args").toObject(); 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()); args.insert(iter.key(), iter.value().toString());
} }
} }
QByteArray ApplicationMessage::serialize() { QByteArray ApplicationMessage::serialize()
{
QJsonObject root; QJsonObject root;
root.insert("command", command); root.insert("command", command);
QJsonObject outArgs; QJsonObject outArgs;

View File

@ -1,13 +1,13 @@
#pragma once #pragma once
#include <QString>
#include <QHash>
#include <QByteArray> #include <QByteArray>
#include <QHash>
#include <QString>
struct ApplicationMessage { struct ApplicationMessage {
QString command; QString command;
QHash<QString, QString> args; QHash<QString, QString> args;
QByteArray serialize(); QByteArray serialize();
void parse(const QByteArray & input); void parse(const QByteArray& input);
}; };

View File

@ -18,27 +18,21 @@
#include "BaseInstaller.h" #include "BaseInstaller.h"
#include "minecraft/MinecraftInstance.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())); 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"); QDir(to->instanceRoot()).mkdir("patches");
} }
if (isApplied(to)) if (isApplied(to)) {
{ if (!remove(to)) {
if (!remove(to))
{
return false; return false;
} }
} }
@ -46,16 +40,16 @@ bool BaseInstaller::add(MinecraftInstance *to)
return true; return true;
} }
bool BaseInstaller::remove(MinecraftInstance *from) bool BaseInstaller::remove(MinecraftInstance* from)
{ {
return QFile::remove(filename(from->instanceRoot())); 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"); return patchesDir(root).absoluteFilePath(id() + ".json");
} }
QDir BaseInstaller::patchesDir(const QString &root) const QDir BaseInstaller::patchesDir(const QString& root) const
{ {
return QDir(root + "/patches/"); return QDir(root + "/patches/");
} }

View File

@ -26,20 +26,19 @@ class QObject;
class Task; class Task;
class BaseVersion; class BaseVersion;
class BaseInstaller class BaseInstaller {
{ public:
public:
BaseInstaller(); BaseInstaller();
virtual ~BaseInstaller(){}; virtual ~BaseInstaller(){};
bool isApplied(MinecraftInstance *on); bool isApplied(MinecraftInstance* on);
virtual bool add(MinecraftInstance *to); virtual bool add(MinecraftInstance* to);
virtual bool remove(MinecraftInstance *from); 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; virtual QString id() const = 0;
QString filename(const QString &root) const; QString filename(const QString& root) const;
QDir patchesDir(const QString &root) const; QDir patchesDir(const QString& root) const;
}; };

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* *
@ -36,23 +36,22 @@
#include "BaseInstance.h" #include "BaseInstance.h"
#include <QFileInfo>
#include <QDir>
#include <QDebug> #include <QDebug>
#include <QRegularExpression> #include <QDir>
#include <QFileInfo>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QRegularExpression>
#include "settings/INISettingsObject.h" #include "settings/INISettingsObject.h"
#include "settings/Setting.h"
#include "settings/OverrideSetting.h" #include "settings/OverrideSetting.h"
#include "settings/Setting.h"
#include "FileSystem.h"
#include "Commandline.h"
#include "BuildConfig.h" #include "BuildConfig.h"
#include "Commandline.h"
#include "FileSystem.h"
BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir) : QObject()
: QObject()
{ {
m_settings = settings; m_settings = settings;
m_global_settings = globalSettings; m_global_settings = globalSettings;
@ -79,7 +78,7 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
m_settings->registerSetting("InstanceType", ""); m_settings->registerSetting("InstanceType", "");
// Custom Commands // 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("PreLaunchCommand"), commandSetting);
m_settings->registerOverride(globalSettings->getSetting("WrapperCommand"), commandSetting); m_settings->registerOverride(globalSettings->getSetting("WrapperCommand"), commandSetting);
m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"), commandSetting); m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"), commandSetting);
@ -148,7 +147,11 @@ QString BaseInstance::getManagedPackVersionName() const
return m_settings->get("ManagedPackVersionName").toString(); 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("ManagedPack", true);
m_settings->set("ManagedPackType", type); m_settings->set("ManagedPackType", type);
@ -173,8 +176,7 @@ int BaseInstance::getConsoleMaxLines() const
auto lineSetting = m_settings->getSetting("ConsoleMaxLines"); auto lineSetting = m_settings->getSetting("ConsoleMaxLines");
bool conversionOk = false; bool conversionOk = false;
int maxLines = lineSetting->get().toInt(&conversionOk); int maxLines = lineSetting->get().toInt(&conversionOk);
if(!conversionOk) if (!conversionOk) {
{
maxLines = lineSetting->defValue().toInt(); maxLines = lineSetting->defValue().toInt();
qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines; qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines;
} }
@ -220,8 +222,7 @@ bool BaseInstance::isLinkedToInstanceId(const QString& id) const
void BaseInstance::iconUpdated(QString key) void BaseInstance::iconUpdated(QString key)
{ {
if(iconKey() == key) if (iconKey() == key) {
{
emit propertiesChanged(this); emit propertiesChanged(this);
} }
} }
@ -235,8 +236,7 @@ void BaseInstance::invalidate()
void BaseInstance::changeStatus(BaseInstance::Status newStatus) void BaseInstance::changeStatus(BaseInstance::Status newStatus)
{ {
Status status = currentStatus(); Status status = currentStatus();
if(status != newStatus) if (status != newStatus) {
{
m_status = newStatus; m_status = newStatus;
emit statusChanged(status, newStatus); emit statusChanged(status, newStatus);
} }
@ -259,23 +259,19 @@ bool BaseInstance::isRunning() const
void BaseInstance::setRunning(bool running) void BaseInstance::setRunning(bool running)
{ {
if(running == m_isRunning) if (running == m_isRunning)
return; return;
m_isRunning = running; m_isRunning = running;
if(!m_settings->get("RecordGameTime").toBool()) if (!m_settings->get("RecordGameTime").toBool()) {
{
emit runningStatusChanged(running); emit runningStatusChanged(running);
return; return;
} }
if(running) if (running) {
{
m_timeStarted = QDateTime::currentDateTime(); m_timeStarted = QDateTime::currentDateTime();
} } else {
else
{
QDateTime timeEnded = QDateTime::currentDateTime(); QDateTime timeEnded = QDateTime::currentDateTime();
qint64 current = settings()->get("totalTimePlayed").toLongLong(); qint64 current = settings()->get("totalTimePlayed").toLongLong();
@ -291,8 +287,7 @@ void BaseInstance::setRunning(bool running)
int64_t BaseInstance::totalTimePlayed() const int64_t BaseInstance::totalTimePlayed() const
{ {
qint64 current = m_settings->get("totalTimePlayed").toLongLong(); qint64 current = m_settings->get("totalTimePlayed").toLongLong();
if(m_isRunning) if (m_isRunning) {
{
QDateTime timeNow = QDateTime::currentDateTime(); QDateTime timeNow = QDateTime::currentDateTime();
return current + m_timeStarted.secsTo(timeNow); return current + m_timeStarted.secsTo(timeNow);
} }
@ -301,8 +296,7 @@ int64_t BaseInstance::totalTimePlayed() const
int64_t BaseInstance::lastTimePlayed() const int64_t BaseInstance::lastTimePlayed() const
{ {
if(m_isRunning) if (m_isRunning) {
{
QDateTime timeNow = QDateTime::currentDateTime(); QDateTime timeNow = QDateTime::currentDateTime();
return m_timeStarted.secsTo(timeNow); return m_timeStarted.secsTo(timeNow);
} }
@ -349,14 +343,14 @@ qint64 BaseInstance::lastLaunch() const
void BaseInstance::setLastLaunch(qint64 val) 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); m_settings->set("lastLaunchTime", val);
emit propertiesChanged(this); emit propertiesChanged(this);
} }
void BaseInstance::setNotes(QString val) 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); m_settings->set("notes", val);
} }
@ -367,7 +361,7 @@ QString BaseInstance::notes() const
void BaseInstance::setIconKey(QString val) 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); m_settings->set("iconKey", val);
emit propertiesChanged(this); emit propertiesChanged(this);
} }
@ -379,7 +373,7 @@ QString BaseInstance::iconKey() const
void BaseInstance::setName(QString val) 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); m_settings->set("name", val);
emit propertiesChanged(this); emit propertiesChanged(this);
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* *
@ -37,24 +37,24 @@
#pragma once #pragma once
#include <cassert> #include <cassert>
#include <QObject>
#include "QObjectPtr.h"
#include <QDateTime> #include <QDateTime>
#include <QSet> #include <QObject>
#include <QProcess> #include <QProcess>
#include <QSet>
#include "QObjectPtr.h"
#include "settings/SettingsObject.h" #include "settings/SettingsObject.h"
#include "settings/INIFile.h"
#include "BaseVersionList.h" #include "BaseVersionList.h"
#include "minecraft/auth/MinecraftAccount.h"
#include "MessageLevel.h" #include "MessageLevel.h"
#include "minecraft/auth/MinecraftAccount.h"
#include "pathmatcher/IPathMatcher.h" #include "pathmatcher/IPathMatcher.h"
#include "settings/INIFile.h"
#include "net/Mode.h" #include "net/Mode.h"
#include "minecraft/launch/MinecraftServerTarget.h"
#include "RuntimeContext.h" #include "RuntimeContext.h"
#include "minecraft/launch/MinecraftServerTarget.h"
class QDir; class QDir;
class Task; class Task;
@ -72,23 +72,21 @@ typedef std::shared_ptr<BaseInstance> InstancePtr;
* To create a new instance type, create a new class inheriting from this class * To create a new instance type, create a new class inheriting from this class
* and implement the pure virtual functions. * and implement the pure virtual functions.
*/ */
class BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance> class BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance> {
{
Q_OBJECT Q_OBJECT
protected: protected:
/// no-touchy! /// no-touchy!
BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir); BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir);
public: /* types */ public: /* types */
enum class Status enum class Status {
{
Present, Present,
Gone // either nuked or invalidated Gone // either nuked or invalidated
}; };
public: public:
/// virtual destructor to make sure the destruction is COMPLETE /// virtual destructor to make sure the destruction is COMPLETE
virtual ~BaseInstance() {}; virtual ~BaseInstance() {}
virtual void saveNow() = 0; virtual void saveNow() = 0;
@ -117,10 +115,7 @@ public:
QString instanceRoot() const; QString instanceRoot() const;
/// Path to the instance's game root directory. /// Path to the instance's game root directory.
virtual QString gameRoot() const virtual QString gameRoot() const { return instanceRoot(); }
{
return instanceRoot();
}
/// Path to the instance's mods directory. /// Path to the instance's mods directory.
virtual QString modsRoot() const = 0; virtual QString modsRoot() const = 0;
@ -151,15 +146,12 @@ public:
void copyManagedPack(BaseInstance& other); void copyManagedPack(BaseInstance& other);
/// guess log level from a line of game log /// guess log level from a line of game log
virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString &line, MessageLevel::Enum level) virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString& line, MessageLevel::Enum level) { return level; }
{
return level;
};
virtual QStringList extraArguments(); virtual QStringList extraArguments();
/// Traits. Normally inside the version, depends on instance implementation. /// Traits. Normally inside the version, depends on instance implementation.
virtual QSet <QString> traits() const = 0; virtual QSet<QString> traits() const = 0;
/** /**
* Gets the time that the instance was last launched. * Gets the time that the instance was last launched.
@ -189,8 +181,7 @@ public:
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0; virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
/// returns a valid launcher (task container) /// returns a valid launcher (task container)
virtual shared_qobject_ptr<LaunchTask> createLaunchTask( virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0;
AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0;
/// returns the current launch task (if any) /// returns the current launch task (if any)
shared_qobject_ptr<LaunchTask> getLaunchTask(); shared_qobject_ptr<LaunchTask> getLaunchTask();
@ -222,45 +213,30 @@ public:
virtual QString typeName() const = 0; virtual QString typeName() const = 0;
void updateRuntimeContext(); void updateRuntimeContext();
RuntimeContext runtimeContext() const RuntimeContext runtimeContext() const { return m_runtimeContext; }
{
return m_runtimeContext;
}
bool hasVersionBroken() const bool hasVersionBroken() const { return m_hasBrokenVersion; }
{
return m_hasBrokenVersion;
}
void setVersionBroken(bool value) void setVersionBroken(bool value)
{ {
if(m_hasBrokenVersion != value) if (m_hasBrokenVersion != value) {
{
m_hasBrokenVersion = value; m_hasBrokenVersion = value;
emit propertiesChanged(this); emit propertiesChanged(this);
} }
} }
bool hasUpdateAvailable() const bool hasUpdateAvailable() const { return m_hasUpdate; }
{
return m_hasUpdate;
}
void setUpdateAvailable(bool value) void setUpdateAvailable(bool value)
{ {
if(m_hasUpdate != value) if (m_hasUpdate != value) {
{
m_hasUpdate = value; m_hasUpdate = value;
emit propertiesChanged(this); emit propertiesChanged(this);
} }
} }
bool hasCrashed() const bool hasCrashed() const { return m_crashed; }
{
return m_crashed;
}
void setCrashed(bool value) void setCrashed(bool value)
{ {
if(m_crashed != value) if (m_crashed != value) {
{
m_crashed = value; m_crashed = value;
emit propertiesChanged(this); emit propertiesChanged(this);
} }
@ -288,19 +264,19 @@ public:
bool removeLinkedInstanceId(const QString& id); bool removeLinkedInstanceId(const QString& id);
bool isLinkedToInstanceId(const QString& id) const; bool isLinkedToInstanceId(const QString& id) const;
protected: protected:
void changeStatus(Status newStatus); 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; } bool isSpecificSettingsLoaded() const { return m_specific_settings_loaded; }
void setSpecificSettingsLoaded(bool loaded) { m_specific_settings_loaded = loaded; } void setSpecificSettingsLoaded(bool loaded) { m_specific_settings_loaded = loaded; }
signals: signals:
/*! /*!
* \brief Signal emitted when properties relevant to the instance view change * \brief Signal emitted when properties relevant to the instance view change
*/ */
void propertiesChanged(BaseInstance *inst); void propertiesChanged(BaseInstance* inst);
void launchTaskChanged(shared_qobject_ptr<LaunchTask>); void launchTaskChanged(shared_qobject_ptr<LaunchTask>);
@ -308,10 +284,10 @@ signals:
void statusChanged(Status from, Status to); void statusChanged(Status from, Status to);
protected slots: protected slots:
void iconUpdated(QString key); void iconUpdated(QString key);
protected: /* data */ protected: /* data */
QString m_rootDir; QString m_rootDir;
SettingsObjectPtr m_settings; SettingsObjectPtr m_settings;
// InstanceFlags m_flags; // InstanceFlags m_flags;
@ -320,7 +296,7 @@ protected: /* data */
QDateTime m_timeStarted; QDateTime m_timeStarted;
RuntimeContext m_runtimeContext; RuntimeContext m_runtimeContext;
private: /* data */ private: /* data */
Status m_status = Status::Present; Status m_status = Status::Present;
bool m_crashed = false; bool m_crashed = false;
bool m_hasUpdate = false; bool m_hasUpdate = false;
@ -328,9 +304,8 @@ private: /* data */
SettingsObjectWeakPtr m_global_settings; SettingsObjectWeakPtr m_global_settings;
bool m_specific_settings_loaded = false; bool m_specific_settings_loaded = false;
}; };
Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>) Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>)
//Q_DECLARE_METATYPE(BaseInstance::InstanceFlag) // Q_DECLARE_METATYPE(BaseInstance::InstanceFlag)
//Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags) // Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags)

View File

@ -15,16 +15,15 @@
#pragma once #pragma once
#include <memory>
#include <QString>
#include <QMetaType> #include <QMetaType>
#include <QString>
#include <memory>
/*! /*!
* An abstract base class for versions. * An abstract base class for versions.
*/ */
class BaseVersion class BaseVersion {
{ public:
public:
using Ptr = std::shared_ptr<BaseVersion>; using Ptr = std::shared_ptr<BaseVersion>;
virtual ~BaseVersion() {} virtual ~BaseVersion() {}
/*! /*!
@ -44,15 +43,8 @@ public:
* the kind of version this is (Stable, Beta, Snapshot, whatever) * the kind of version this is (Stable, Beta, Snapshot, whatever)
*/ */
virtual QString typeString() const = 0; virtual QString typeString() const = 0;
virtual bool operator<(BaseVersion& a) { return name() < a.name(); }
virtual bool operator<(BaseVersion &a) virtual bool operator>(BaseVersion& a) { return name() > a.name(); }
{
return name() < a.name();
};
virtual bool operator>(BaseVersion &a)
{
return name() > a.name();
};
}; };
Q_DECLARE_METATYPE(BaseVersion::Ptr) Q_DECLARE_METATYPE(BaseVersion::Ptr)

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -36,14 +36,11 @@
#include "BaseVersionList.h" #include "BaseVersionList.h"
#include "BaseVersion.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) if (at(i)->descriptor() == descriptor)
return at(i); return at(i);
} }
@ -58,7 +55,7 @@ BaseVersion::Ptr BaseVersionList::getRecommended() const
return at(0); return at(0);
} }
QVariant BaseVersionList::data(const QModelIndex &index, int role) const QVariant BaseVersionList::data(const QModelIndex& index, int role) const
{ {
if (!index.isValid()) if (!index.isValid())
return QVariant(); return QVariant();
@ -68,37 +65,36 @@ QVariant BaseVersionList::data(const QModelIndex &index, int role) const
BaseVersion::Ptr version = at(index.row()); BaseVersion::Ptr version = at(index.row());
switch (role) switch (role) {
{ case VersionPointerRole:
case VersionPointerRole: return QVariant::fromValue(version);
return QVariant::fromValue(version);
case VersionRole: case VersionRole:
return version->name(); return version->name();
case VersionIdRole: case VersionIdRole:
return version->descriptor(); return version->descriptor();
case TypeRole: case TypeRole:
return version->typeString(); return version->typeString();
default: default:
return QVariant(); return QVariant();
} }
} }
BaseVersionList::RoleList BaseVersionList::providesRoles() const 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 count
return parent.isValid() ? 0 : 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; return parent.isValid() ? 0 : 1;
} }

View File

@ -15,13 +15,13 @@
#pragma once #pragma once
#include <QAbstractListModel>
#include <QObject> #include <QObject>
#include <QVariant> #include <QVariant>
#include <QAbstractListModel>
#include "BaseVersion.h" #include "BaseVersion.h"
#include "tasks/Task.h"
#include "QObjectPtr.h" #include "QObjectPtr.h"
#include "tasks/Task.h"
/*! /*!
* \brief Class that each instance type's version list derives from. * \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 * all have a default implementation, but they can be overridden by plugins to
* change the behavior of the list. * change the behavior of the list.
*/ */
class BaseVersionList : public QAbstractListModel class BaseVersionList : public QAbstractListModel {
{
Q_OBJECT Q_OBJECT
public: public:
enum ModelRoles enum ModelRoles {
{
VersionPointerRole = Qt::UserRole, VersionPointerRole = Qt::UserRole,
VersionRole, VersionRole,
VersionIdRole, VersionIdRole,
@ -55,7 +53,7 @@ public:
}; };
typedef QList<int> RoleList; typedef QList<int> RoleList;
explicit BaseVersionList(QObject *parent = 0); explicit BaseVersionList(QObject* parent = 0);
/*! /*!
* \brief Gets a task that will reload the version list. * \brief Gets a task that will reload the version list.
@ -66,7 +64,7 @@ public:
virtual Task::Ptr getLoadTask() = 0; virtual Task::Ptr getLoadTask() = 0;
//! Checks whether or not the list is loaded. If this returns false, the list should be //! Checks whether or not the list is loaded. If this returns false, the list should be
//loaded. // loaded.
virtual bool isLoaded() = 0; virtual bool isLoaded() = 0;
//! Gets the version at the given index. //! Gets the version at the given index.
@ -76,9 +74,9 @@ public:
virtual int count() const = 0; virtual int count() const = 0;
//////// List Model Functions //////// //////// List Model Functions ////////
QVariant data(const QModelIndex &index, int role) const override; QVariant data(const QModelIndex& index, int role) const override;
int rowCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex& parent) const override;
int columnCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex& parent) const override;
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
//! which roles are provided by this version list? //! 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 * \return A const pointer to the version with the given descriptor. NULL if
* one doesn't exist. * 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 * \brief Gets the recommended version from this list
@ -103,8 +101,7 @@ public:
*/ */
virtual void sortVersions() = 0; virtual void sortVersions() = 0;
protected protected slots:
slots:
/*! /*!
* Updates this list with the given list of versions. * Updates this list with the given list of versions.
* This is done by copying each version in the given list and inserting it * This is done by copying each version in the given list and inserting it

View File

@ -136,6 +136,15 @@ set(NET_SOURCES
net/Validator.h net/Validator.h
net/Upload.cpp net/Upload.cpp
net/Upload.h 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 # Game launch logic
@ -262,8 +271,6 @@ set(MINECRAFT_SOURCES
minecraft/launch/CreateGameFolders.h minecraft/launch/CreateGameFolders.h
minecraft/launch/ModMinecraftJar.cpp minecraft/launch/ModMinecraftJar.cpp
minecraft/launch/ModMinecraftJar.h minecraft/launch/ModMinecraftJar.h
minecraft/launch/DirectJavaLaunch.cpp
minecraft/launch/DirectJavaLaunch.h
minecraft/launch/ExtractNatives.cpp minecraft/launch/ExtractNatives.cpp
minecraft/launch/ExtractNatives.h minecraft/launch/ExtractNatives.h
minecraft/launch/LauncherPartLaunch.cpp minecraft/launch/LauncherPartLaunch.cpp
@ -501,6 +508,11 @@ set(FTB_SOURCES
modplatform/legacy_ftb/PrivatePackManager.cpp modplatform/legacy_ftb/PrivatePackManager.cpp
modplatform/legacy_ftb/PackHelpers.h 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 set(FLAME_SOURCES
@ -563,6 +575,9 @@ set(ATLAUNCHER_SOURCES
) )
set(LINKEXE_SOURCES set(LINKEXE_SOURCES
WindowsConsole.cpp
WindowsConsole.h
filelink/FileLink.h filelink/FileLink.h
filelink/FileLink.cpp filelink/FileLink.cpp
FileSystem.h FileSystem.h
@ -668,7 +683,7 @@ set(LOGIC_SOURCES
${ATLAUNCHER_SOURCES} ${ATLAUNCHER_SOURCES}
) )
if(APPLE) if(APPLE AND Launcher_ENABLE_UPDATER)
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES}) set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES})
endif() endif()
@ -759,8 +774,12 @@ SET(LAUNCHER_SOURCES
ui/themes/ITheme.h ui/themes/ITheme.h
ui/themes/SystemTheme.cpp ui/themes/SystemTheme.cpp
ui/themes/SystemTheme.h ui/themes/SystemTheme.h
ui/themes/IconTheme.cpp
ui/themes/IconTheme.h
ui/themes/ThemeManager.cpp ui/themes/ThemeManager.cpp
ui/themes/ThemeManager.h ui/themes/ThemeManager.h
ui/themes/CatPack.cpp
ui/themes/CatPack.h
# Processes # Processes
LaunchController.h LaunchController.h
@ -870,6 +889,11 @@ SET(LAUNCHER_SOURCES
ui/pages/modplatform/legacy_ftb/ListModel.h ui/pages/modplatform/legacy_ftb/ListModel.h
ui/pages/modplatform/legacy_ftb/ListModel.cpp 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.cpp
ui/pages/modplatform/flame/FlameModel.h ui/pages/modplatform/flame/FlameModel.h
ui/pages/modplatform/flame/FlamePage.cpp ui/pages/modplatform/flame/FlamePage.cpp
@ -952,6 +976,8 @@ SET(LAUNCHER_SOURCES
ui/dialogs/ChooseProviderDialog.cpp ui/dialogs/ChooseProviderDialog.cpp
ui/dialogs/ModUpdateDialog.cpp ui/dialogs/ModUpdateDialog.cpp
ui/dialogs/ModUpdateDialog.h ui/dialogs/ModUpdateDialog.h
ui/dialogs/InstallLoaderDialog.cpp
ui/dialogs/InstallLoaderDialog.h
# GUI - widgets # GUI - widgets
ui/widgets/Common.cpp ui/widgets/Common.cpp
@ -1016,6 +1042,14 @@ SET(LAUNCHER_SOURCES
ui/instanceview/VisualGroup.h ui/instanceview/VisualGroup.h
) )
if(WIN32)
set(LAUNCHER_SOURCES
WindowsConsole.cpp
WindowsConsole.h
${LAUNCHER_SOURCES}
)
endif()
qt_wrap_ui(LAUNCHER_UI qt_wrap_ui(LAUNCHER_UI
ui/MainWindow.ui ui/MainWindow.ui
ui/setupwizard/PasteWizardPage.ui ui/setupwizard/PasteWizardPage.ui
@ -1044,6 +1078,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/modplatform/ResourcePage.ui ui/pages/modplatform/ResourcePage.ui
ui/pages/modplatform/flame/FlamePage.ui ui/pages/modplatform/flame/FlamePage.ui
ui/pages/modplatform/legacy_ftb/Page.ui ui/pages/modplatform/legacy_ftb/Page.ui
ui/pages/modplatform/import_ftb/ImportFTBPage.ui
ui/pages/modplatform/ImportPage.ui ui/pages/modplatform/ImportPage.ui
ui/pages/modplatform/modrinth/ModrinthPage.ui ui/pages/modplatform/modrinth/ModrinthPage.ui
ui/pages/modplatform/technic/TechnicPage.ui ui/pages/modplatform/technic/TechnicPage.ui
@ -1098,8 +1133,15 @@ if(WIN32)
set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC}) set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC})
endif() endif()
include(CompilerWarnings)
# Add executable # Add executable
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES}) 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_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(Launcher_logic target_link_libraries(Launcher_logic
systeminfo systeminfo
@ -1139,17 +1181,23 @@ if(APPLE)
set(CMAKE_MACOSX_RPATH 1) set(CMAKE_MACOSX_RPATH 1)
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/") 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}) if(Launcher_ENABLE_UPDATER)
file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle) 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 target_link_libraries(Launcher_logic
"-framework AppKit" "-framework AppKit"
"-framework Carbon" "-framework Carbon"
"-framework Foundation" "-framework Foundation"
"-framework ApplicationServices" "-framework ApplicationServices"
) )
target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK}) if(Launcher_ENABLE_UPDATER)
target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK})
endif()
endif() endif()
target_link_libraries(Launcher_logic) target_link_libraries(Launcher_logic)
@ -1178,6 +1226,11 @@ install(TARGETS ${Launcher_Name}
if(WIN32) if(WIN32)
add_library(filelink_logic STATIC ${LINKEXE_SOURCES}) 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_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(filelink_logic target_link_libraries(filelink_logic
systeminfo systeminfo
@ -1211,7 +1264,7 @@ if(WIN32)
) )
endif() endif()
if (UNIX AND APPLE) if (UNIX AND APPLE AND Launcher_ENABLE_UPDATER)
# Add Sparkle updater # Add Sparkle updater
# It has to be copied here instead of just allowing fixup_bundle to install it, otherwise essential parts of # It has to be copied here instead of just allowing fixup_bundle to install it, otherwise essential parts of
# the framework aren't installed # the framework aren't installed

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -41,8 +41,7 @@
* @file libutil/src/cmdutils.cpp * @file libutil/src/cmdutils.cpp
*/ */
namespace Commandline namespace Commandline {
{
// commandline splitter // commandline splitter
QStringList splitArgs(QString args) QStringList splitArgs(QString args)
@ -51,19 +50,15 @@ QStringList splitArgs(QString args)
QString current; QString current;
bool escape = false; bool escape = false;
QChar inquotes; QChar inquotes;
for (int i = 0; i < args.length(); i++) for (int i = 0; i < args.length(); i++) {
{
QChar cchar = args.at(i); QChar cchar = args.at(i);
// \ escaped // \ escaped
if (escape) if (escape) {
{
current += cchar; current += cchar;
escape = false; escape = false;
// in "quotes" // in "quotes"
} } else if (!inquotes.isNull()) {
else if (!inquotes.isNull())
{
if (cchar == '\\') if (cchar == '\\')
escape = true; escape = true;
else if (cchar == inquotes) else if (cchar == inquotes)
@ -71,18 +66,13 @@ QStringList splitArgs(QString args)
else else
current += cchar; current += cchar;
// otherwise // otherwise
} } else {
else if (cchar == ' ') {
{ if (!current.isEmpty()) {
if (cchar == ' ')
{
if (!current.isEmpty())
{
argv << current; argv << current;
current.clear(); current.clear();
} }
} } else if (cchar == '"' || cchar == '\'')
else if (cchar == '"' || cchar == '\'')
inquotes = cchar; inquotes = cchar;
else else
current += cchar; current += cchar;
@ -92,4 +82,4 @@ QStringList splitArgs(QString args)
argv << current; argv << current;
return argv; return argv;
} }
} } // namespace Commandline

View File

@ -25,8 +25,7 @@
* @brief commandline parsing and processing utilities * @brief commandline parsing and processing utilities
*/ */
namespace Commandline namespace Commandline {
{
/** /**
* @brief split a string into argv items like a shell would do * @brief split a string into argv items like a shell would do
@ -34,4 +33,4 @@ namespace Commandline
* @return a QStringList containing all arguments * @return a QStringList containing all arguments
*/ */
QStringList splitArgs(QString args); QStringList splitArgs(QString args);
} } // namespace Commandline

View File

@ -1,33 +1,21 @@
#pragma once #pragma once
template <typename T> template <typename T>
class DefaultVariable class DefaultVariable {
{ public:
public: DefaultVariable(const T& value) { defaultValue = value; }
DefaultVariable(const T & value) DefaultVariable<T>& operator=(const T& value)
{
defaultValue = value;
}
DefaultVariable<T> & operator =(const T & value)
{ {
currentValue = value; currentValue = value;
is_default = currentValue == defaultValue; is_default = currentValue == defaultValue;
is_explicit = true; is_explicit = true;
return *this; return *this;
} }
operator const T &() const operator const T&() const { return is_default ? defaultValue : currentValue; }
{ bool isDefault() const { return is_default; }
return is_default ? defaultValue : currentValue; bool isExplicit() const { return is_explicit; }
}
bool isDefault() const private:
{
return is_default;
}
bool isExplicit() const
{
return is_explicit;
}
private:
T currentValue; T currentValue;
T defaultValue; T defaultValue;
bool is_default = true; bool is_default = true;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 dada513 <dada513@protonmail.com> * Copyright (C) 2022 dada513 <dada513@protonmail.com>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -33,40 +33,37 @@
* limitations under the License. * limitations under the License.
*/ */
#include "DesktopServices.h" #include "DesktopServices.h"
#include <QDir>
#include <QDesktopServices>
#include <QProcess>
#include <QDebug> #include <QDebug>
#include <QDesktopServices>
#include <QDir>
#include <QProcess>
/** /**
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing. * 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) #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
#include <unistd.h>
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h>
template <typename T> template <typename T>
bool IndirectOpen(T callable, qint64 *pid_forked = nullptr) bool IndirectOpen(T callable, qint64* pid_forked = nullptr)
{ {
auto pid = fork(); auto pid = fork();
if(pid_forked) if (pid_forked) {
{ if (pid > 0)
if(pid > 0)
*pid_forked = pid; *pid_forked = pid;
else else
*pid_forked = 0; *pid_forked = 0;
} }
if(pid == -1) if (pid == -1) {
{
qWarning() << "IndirectOpen failed to fork: " << errno; qWarning() << "IndirectOpen failed to fork: " << errno;
return false; return false;
} }
// child - do the stuff // child - do the stuff
if(pid == 0) if (pid == 0) {
{
// unset all this garbage so it doesn't get passed to the child process // unset all this garbage so it doesn't get passed to the child process
qunsetenv("LD_PRELOAD"); qunsetenv("LD_PRELOAD");
qunsetenv("LD_LIBRARY_PATH"); 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. // die. now. do not clean up anything, it would just hang forever.
_exit(status ? 0 : 1); _exit(status ? 0 : 1);
} } else {
else // parent - assume it worked.
{
//parent - assume it worked.
int status; int status;
while (waitpid(pid, &status, 0)) while (waitpid(pid, &status, 0)) {
{ if (WIFEXITED(status)) {
if(WIFEXITED(status))
{
return WEXITSTATUS(status) == 0; return WEXITSTATUS(status) == 0;
} }
if(WIFSIGNALED(status)) if (WIFSIGNALED(status)) {
{
return false; return false;
} }
} }
@ -104,26 +96,19 @@ bool IndirectOpen(T callable, qint64 *pid_forked = nullptr)
#endif #endif
namespace DesktopServices { namespace DesktopServices {
bool openDirectory(const QString &path, bool ensureExists) bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists)
{ {
qDebug() << "Opening directory" << path; qDebug() << "Opening directory" << path;
QDir parentPath; QDir parentPath;
QDir dir(path); QDir dir(path);
if (!dir.exists()) if (ensureExists && !dir.exists()) {
{
parentPath.mkpath(dir.absolutePath()); parentPath.mkpath(dir.absolutePath());
} }
auto f = [&]() auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); };
{
return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
};
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
if(!isFlatpak()) if (!isSandbox()) {
{
return IndirectOpen(f); return IndirectOpen(f);
} } else {
else
{
return f(); return f();
} }
#else #else
@ -131,20 +116,14 @@ bool openDirectory(const QString &path, bool ensureExists)
#endif #endif
} }
bool openFile(const QString &path) bool openFile(const QString& path)
{ {
qDebug() << "Opening file" << path; qDebug() << "Opening file" << path;
auto f = [&]() auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); };
{
return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
};
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
if(!isFlatpak()) if (!isSandbox()) {
{
return IndirectOpen(f); return IndirectOpen(f);
} } else {
else
{
return f(); return f();
} }
#else #else
@ -152,41 +131,29 @@ bool openFile(const QString &path)
#endif #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; qDebug() << "Opening file" << path << "using" << application;
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) #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 // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
if(!isFlatpak()) if (!isSandbox()) {
{ return IndirectOpen([&]() { return QProcess::startDetached(application, QStringList() << path, workingDirectory); }, pid);
return IndirectOpen([&]() } else {
{ return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
return QProcess::startDetached(application, QStringList() << path, workingDirectory);
}, pid);
}
else
{
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
} }
#else #else
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid); return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
#endif #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(' '); qDebug() << "Running" << application << "with args" << args.join(' ');
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
if(!isFlatpak()) if (!isSandbox()) {
{ // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
// 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);
return IndirectOpen([&]() } else {
{
return QProcess::startDetached(application, args, workingDirectory);
}, pid);
}
else
{
return QProcess::startDetached(application, args, workingDirectory, pid); return QProcess::startDetached(application, args, workingDirectory, pid);
} }
#else #else
@ -194,20 +161,14 @@ bool run(const QString &application, const QStringList &args, const QString &wor
#endif #endif
} }
bool openUrl(const QUrl &url) bool openUrl(const QUrl& url)
{ {
qDebug() << "Opening URL" << url.toString(); qDebug() << "Opening URL" << url.toString();
auto f = [&]() auto f = [&]() { return QDesktopServices::openUrl(url); };
{
return QDesktopServices::openUrl(url);
};
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
if(!isFlatpak()) if (!isSandbox()) {
{
return IndirectOpen(f); return IndirectOpen(f);
} } else {
else
{
return f(); return f();
} }
#else #else
@ -224,4 +185,18 @@ bool isFlatpak()
#endif #endif
} }
bool isSnap()
{
#ifdef Q_OS_LINUX
return getenv("SNAP");
#else
return false;
#endif
} }
bool isSandbox()
{
return isSnap() || isFlatpak();
}
} // namespace DesktopServices

View File

@ -1,38 +1,50 @@
#pragma once #pragma once
#include <QUrl>
#include <QString> #include <QString>
#include <QUrl>
/** /**
* This wraps around QDesktopServices and adds workarounds where needed * This wraps around QDesktopServices and adds workarounds where needed
* Use this instead of QDesktopServices! * Use this instead of QDesktopServices!
*/ */
namespace DesktopServices namespace DesktopServices {
{ /**
/** * Open a file in whatever application is applicable
* Open a file in whatever application is applicable */
*/ bool openFile(const QString& path);
bool openFile(const QString &path);
/** /**
* Open a file in the specified application * Open a file in the specified application
*/ */
bool openFile(const QString &application, const QString &path, const QString & workingDirectory = QString(), qint64 *pid = 0); bool openFile(const QString& application, const QString& path, const QString& workingDirectory = QString(), qint64* pid = 0);
/** /**
* Run an application * Run an application
*/ */
bool run(const QString &application,const QStringList &args, const QString & workingDirectory = QString(), qint64 *pid = 0); bool run(const QString& application, const QStringList& args, const QString& workingDirectory = QString(), qint64* pid = 0);
/** /**
* Open a directory * Open a directory
*/ */
bool openDirectory(const QString &path, bool ensureExists = false); bool openDirectory(const QString& path, bool ensureExists = false);
/** /**
* Open the URL, most likely in a browser. Maybe. * Open the URL, most likely in a browser. Maybe.
*/ */
bool openUrl(const QUrl &url); 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

View File

@ -2,31 +2,18 @@
#pragma once #pragma once
#include <QString>
#include <QDebug> #include <QDebug>
#include <QString>
#include <exception> #include <exception>
class Exception : public std::exception class Exception : public std::exception {
{ public:
public: Exception(const QString& message) : std::exception(), m_message(message) { qCritical() << "Exception:" << message; }
Exception(const QString &message) : std::exception(), m_message(message) Exception(const Exception& other) : std::exception(), m_message(other.cause()) {}
{
qCritical() << "Exception:" << message;
}
Exception(const Exception &other)
: std::exception(), m_message(other.cause())
{
}
virtual ~Exception() noexcept {} virtual ~Exception() noexcept {}
const char *what() const noexcept const char* what() const noexcept { return m_message.toLatin1().constData(); }
{ QString cause() const { return m_message; }
return m_message.toLatin1().constData();
}
QString cause() const
{
return m_message;
}
private: private:
QString m_message; QString m_message;
}; };

View File

@ -4,31 +4,24 @@
template <typename T> template <typename T>
inline void clamp(T& current, T min, T max) inline void clamp(T& current, T min, T max)
{ {
if (current < min) if (current < min) {
{
current = min; current = min;
} } else if (current > max) {
else if(current > max)
{
current = max; current = max;
} }
} }
// List of numbers from min to max. Next is exponent times bigger than previous. // List of numbers from min to max. Next is exponent times bigger than previous.
class ExponentialSeries class ExponentialSeries {
{ public:
public:
ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2) ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2)
{ {
m_current = m_min = min; m_current = m_min = min;
m_max = max; m_max = max;
m_exponent = exponent; m_exponent = exponent;
} }
void reset() void reset() { m_current = m_min; }
{
m_current = m_min;
}
unsigned operator()() unsigned operator()()
{ {
unsigned retval = m_current; unsigned retval = m_current;

View File

@ -779,7 +779,8 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
} }
#if defined(Q_OS_MACOS) #if defined(Q_OS_MACOS)
// Create the Application // 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(".")) { if (!applicationDirectory.mkpath(".")) {
qWarning() << "Couldn't create application directory"; qWarning() << "Couldn't create application directory";
@ -843,7 +844,9 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
" <key>CFBundleIconFile</key>\n" " <key>CFBundleIconFile</key>\n"
" <string>Icon.icns</string>\n" " <string>Icon.icns</string>\n"
" <key>CFBundleName</key>\n" " <key>CFBundleName</key>\n"
" <string>" << name << "</string>\n" // Name of the application " <string>"
<< name
<< "</string>\n" // Name of the application
" <key>CFBundlePackageType</key>\n" " <key>CFBundlePackageType</key>\n"
" <string>APPL</string>\n" " <string>APPL</string>\n"
" <key>CFBundleShortVersionString</key>\n" " <key>CFBundleShortVersionString</key>\n"

View File

@ -43,10 +43,10 @@
#include <system_error> #include <system_error>
#include <QDir> #include <QDir>
#include <QPair>
#include <QFlags> #include <QFlags>
#include <QLocalServer> #include <QLocalServer>
#include <QObject> #include <QObject>
#include <QPair>
#include <QThread> #include <QThread>
namespace FS { namespace FS {
@ -112,8 +112,8 @@ class copy : public QObject {
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
int totalCopied() { return m_copied; } qsizetype totalCopied() { return m_copied; }
int totalFailed() { return m_failedPaths.length(); } qsizetype totalFailed() { return m_failedPaths.length(); }
QStringList failed() { return m_failedPaths; } QStringList failed() { return m_failedPaths; }
signals: signals:
@ -130,7 +130,7 @@ class copy : public QObject {
bool m_whitelist = false; bool m_whitelist = false;
QDir m_src; QDir m_src;
QDir m_dst; QDir m_dst;
int m_copied; qsizetype m_copied;
QStringList m_failedPaths; QStringList m_failedPaths;
}; };
@ -365,25 +365,24 @@ enum class FilesystemType {
* QMap is ordered * QMap is ordered
* *
*/ */
static const QMap<FilesystemType, QStringList> s_filesystem_type_names = { static const QMap<FilesystemType, QStringList> s_filesystem_type_names = { { FilesystemType::FAT, { "FAT" } },
{FilesystemType::FAT, { "FAT" }}, { FilesystemType::NTFS, { "NTFS" } },
{FilesystemType::NTFS, { "NTFS" }}, { FilesystemType::REFS, { "REFS" } },
{FilesystemType::REFS, { "REFS" }}, { FilesystemType::EXT_2_OLD, { "EXT_2_OLD", "EXT2_OLD" } },
{FilesystemType::EXT_2_OLD, { "EXT_2_OLD", "EXT2_OLD" }}, { FilesystemType::EXT_2_3_4,
{FilesystemType::EXT_2_3_4, { "EXT2/3/4", "EXT_2_3_4", "EXT2", "EXT3", "EXT4" }}, { "EXT2/3/4", "EXT_2_3_4", "EXT2", "EXT3", "EXT4" } },
{FilesystemType::EXT, { "EXT" }}, { FilesystemType::EXT, { "EXT" } },
{FilesystemType::XFS, { "XFS" }}, { FilesystemType::XFS, { "XFS" } },
{FilesystemType::BTRFS, { "BTRFS" }}, { FilesystemType::BTRFS, { "BTRFS" } },
{FilesystemType::NFS, { "NFS" }}, { FilesystemType::NFS, { "NFS" } },
{FilesystemType::ZFS, { "ZFS" }}, { FilesystemType::ZFS, { "ZFS" } },
{FilesystemType::APFS, { "APFS" }}, { FilesystemType::APFS, { "APFS" } },
{FilesystemType::HFS, { "HFS" }}, { FilesystemType::HFS, { "HFS" } },
{FilesystemType::HFSPLUS, { "HFSPLUS" }}, { FilesystemType::HFSPLUS, { "HFSPLUS" } },
{FilesystemType::HFSX, { "HFSX" }}, { FilesystemType::HFSX, { "HFSX" } },
{FilesystemType::FUSEBLK, { "FUSEBLK" }}, { FilesystemType::FUSEBLK, { "FUSEBLK" } },
{FilesystemType::F2FS, { "F2FS" }}, { FilesystemType::F2FS, { "F2FS" } },
{FilesystemType::UNKNOWN, { "UNKNOWN" }} { FilesystemType::UNKNOWN, { "UNKNOWN" } } };
};
/** /**
* @brief Get the string name of Filesystem enum object * @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); } bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
int totalCloned() { return m_cloned; } qsizetype totalCloned() { return m_cloned; }
int totalFailed() { return m_failedClones.length(); } qsizetype totalFailed() { return m_failedClones.length(); }
QList<QPair<QString, QString>> failed() { return m_failedClones; } QList<QPair<QString, QString>> failed() { return m_failedClones; }
@ -492,7 +491,7 @@ class clone : public QObject {
bool m_whitelist = false; bool m_whitelist = false;
QDir m_src; QDir m_src;
QDir m_dst; QDir m_dst;
int m_cloned; qsizetype m_cloned;
QList<QPair<QString, QString>> m_failedClones; QList<QPair<QString, QString>> m_failedClones;
}; };

View File

@ -1,28 +1,33 @@
#include "Filter.h" #include "Filter.h"
Filter::~Filter(){} Filter::~Filter() {}
ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern){} ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern) {}
ContainsFilter::~ContainsFilter(){} ContainsFilter::~ContainsFilter() {}
bool ContainsFilter::accepts(const QString& value) bool ContainsFilter::accepts(const QString& value)
{ {
return value.contains(pattern); return value.contains(pattern);
} }
ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern){} ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern) {}
ExactFilter::~ExactFilter(){} ExactFilter::~ExactFilter() {}
bool ExactFilter::accepts(const QString& value) bool ExactFilter::accepts(const QString& value)
{ {
return value == pattern; return value == pattern;
} }
RegexpFilter::RegexpFilter(const QString& regexp, bool invert) ExactIfPresentFilter::ExactIfPresentFilter(const QString& pattern) : pattern(pattern) {}
:invert(invert) bool ExactIfPresentFilter::accepts(const QString& value)
{
return value.isEmpty() || value == pattern;
}
RegexpFilter::RegexpFilter(const QString& regexp, bool invert) : invert(invert)
{ {
pattern.setPattern(regexp); pattern.setPattern(regexp);
pattern.optimize(); pattern.optimize();
} }
RegexpFilter::~RegexpFilter(){} RegexpFilter::~RegexpFilter() {}
bool RegexpFilter::accepts(const QString& value) bool RegexpFilter::accepts(const QString& value)
{ {
auto match = pattern.match(value); auto match = pattern.match(value);

View File

@ -1,42 +1,51 @@
#pragma once #pragma once
#include <QString>
#include <QRegularExpression> #include <QRegularExpression>
#include <QString>
class Filter class Filter {
{ public:
public:
virtual ~Filter(); virtual ~Filter();
virtual bool accepts(const QString & value) = 0; virtual bool accepts(const QString& value) = 0;
}; };
class ContainsFilter: public Filter class ContainsFilter : public Filter {
{ public:
public: ContainsFilter(const QString& pattern);
ContainsFilter(const QString &pattern);
virtual ~ContainsFilter(); virtual ~ContainsFilter();
bool accepts(const QString & value) override; bool accepts(const QString& value) override;
private:
private:
QString pattern; QString pattern;
}; };
class ExactFilter: public Filter class ExactFilter : public Filter {
{ public:
public: ExactFilter(const QString& pattern);
ExactFilter(const QString &pattern);
virtual ~ExactFilter(); virtual ~ExactFilter();
bool accepts(const QString & value) override; bool accepts(const QString& value) override;
private:
private:
QString pattern; QString pattern;
}; };
class RegexpFilter: public Filter class ExactIfPresentFilter : public Filter {
{ public:
public: ExactIfPresentFilter(const QString& pattern);
RegexpFilter(const QString &regexp, bool invert); ~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(); virtual ~RegexpFilter();
bool accepts(const QString & value) override; bool accepts(const QString& value) override;
private:
private:
QRegularExpression pattern; QRegularExpression pattern;
bool invert = false; bool invert = false;
}; };

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -37,10 +37,9 @@
#include <zlib.h> #include <zlib.h>
#include <QByteArray> #include <QByteArray>
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; uncompressedBytes = compressedBytes;
return true; return true;
} }
@ -51,42 +50,37 @@ bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedByte
z_stream strm; z_stream strm;
memset(&strm, 0, sizeof(strm)); memset(&strm, 0, sizeof(strm));
strm.next_in = (Bytef *)compressedBytes.data(); strm.next_in = (Bytef*)compressedBytes.data();
strm.avail_in = compressedBytes.size(); strm.avail_in = compressedBytes.size();
bool done = false; bool done = false;
if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) {
{
return false; return false;
} }
int err = Z_OK; int err = Z_OK;
while (!done) while (!done) {
{
// If our output buffer is too small // If our output buffer is too small
if (strm.total_out >= uncompLength) if (strm.total_out >= uncompLength) {
{
uncompressedBytes.resize(uncompLength * 2); uncompressedBytes.resize(uncompLength * 2);
uncompLength *= 2; uncompLength *= 2;
} }
strm.next_out = reinterpret_cast<Bytef *>((uncompressedBytes.data() + strm.total_out)); strm.next_out = reinterpret_cast<Bytef*>((uncompressedBytes.data() + strm.total_out));
strm.avail_out = uncompLength - strm.total_out; strm.avail_out = uncompLength - strm.total_out;
// Inflate another chunk. // Inflate another chunk.
err = inflate(&strm, Z_SYNC_FLUSH); err = inflate(&strm, Z_SYNC_FLUSH);
if (err == Z_STREAM_END) if (err == Z_STREAM_END)
done = true; done = true;
else if (err != Z_OK) else if (err != Z_OK) {
{
break; break;
} }
} }
if (inflateEnd(&strm) != Z_OK || !done) if (inflateEnd(&strm) != Z_OK || !done) {
{
return false; return false;
} }
@ -94,10 +88,9 @@ bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedByte
return true; 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; compressedBytes = uncompressedBytes;
return true; return true;
} }
@ -109,8 +102,7 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
z_stream zs; z_stream zs;
memset(&zs, 0, sizeof(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; return false;
} }
@ -122,11 +114,9 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
unsigned offset = 0; unsigned offset = 0;
unsigned temp = 0; unsigned temp = 0;
do do {
{
auto remaining = compressedBytes.size() - offset; auto remaining = compressedBytes.size() - offset;
if(remaining < 1) if (remaining < 1) {
{
compressedBytes.resize(compressedBytes.size() * 2); compressedBytes.resize(compressedBytes.size() * 2);
} }
zs.next_out = reinterpret_cast<Bytef*>((compressedBytes.data() + offset)); zs.next_out = reinterpret_cast<Bytef*>((compressedBytes.data() + offset));
@ -137,13 +127,11 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
compressedBytes.resize(offset); compressedBytes.resize(offset);
if (deflateEnd(&zs) != Z_OK) if (deflateEnd(&zs) != Z_OK) {
{
return false; return false;
} }
if (ret != Z_STREAM_END) if (ret != Z_STREAM_END) {
{
return false; return false;
} }
return true; return true;

View File

@ -1,10 +1,8 @@
#pragma once #pragma once
#include <QByteArray> #include <QByteArray>
class GZip class GZip {
{ public:
public: static bool unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes);
static bool unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes); static bool zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes);
static bool zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes);
}; };

View File

@ -6,17 +6,10 @@
bool InstanceCopyPrefs::allTrue() const bool InstanceCopyPrefs::allTrue() const
{ {
return copySaves && return copySaves && keepPlaytime && copyGameOptions && copyResourcePacks && copyShaderPacks && copyServers && copyMods &&
keepPlaytime && copyScreenshots;
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") // Returns a single RegEx string of the selected folders/files to filter out (ex: ".minecraft/saves|.minecraft/server.dat")
QString InstanceCopyPrefs::getSelectedFiltersAsRegex() const QString InstanceCopyPrefs::getSelectedFiltersAsRegex() const
{ {
@ -26,25 +19,30 @@ QString InstanceCopyPrefs::getSelectedFiltersAsRegex(const QStringList& addition
{ {
QStringList filters; QStringList filters;
if(!copySaves) if (!copySaves)
filters << "saves"; filters << "saves";
if(!copyGameOptions) if (!copyGameOptions)
filters << "options.txt"; filters << "options.txt";
if(!copyResourcePacks) if (!copyResourcePacks)
filters << "resourcepacks" << "texturepacks"; filters << "resourcepacks"
<< "texturepacks";
if(!copyShaderPacks) if (!copyShaderPacks)
filters << "shaderpacks"; filters << "shaderpacks";
if(!copyServers) if (!copyServers)
filters << "servers.dat" << "servers.dat_old" << "server-resource-packs"; filters << "servers.dat"
<< "servers.dat_old"
<< "server-resource-packs";
if(!copyMods) if (!copyMods)
filters << "coremods" << "mods" << "config"; filters << "coremods"
<< "mods"
<< "config";
if(!copyScreenshots) if (!copyScreenshots)
filters << "screenshots"; filters << "screenshots";
for (auto filter : additionalFilters) { for (auto filter : additionalFilters) {

View File

@ -40,7 +40,7 @@ struct InstanceCopyPrefs {
void enableDontLinkSaves(bool b); void enableDontLinkSaves(bool b);
void enableUseClone(bool b); void enableUseClone(bool b);
protected: // data protected: // data
bool copySaves = true; bool copySaves = true;
bool keepPlaytime = true; bool keepPlaytime = true;
bool copyGameOptions = true; bool copyGameOptions = true;

View File

@ -156,8 +156,9 @@ void InstanceCopyTask::copyFinished()
allowed_symlinks.append(m_origInstance->gameRoot().toUtf8()); allowed_symlinks.append(m_origInstance->gameRoot().toUtf8());
allowed_symlinks.append("\n"); allowed_symlinks.append("\n");
if (allowed_symlinks_file.isSymLink()) if (allowed_symlinks_file.isSymLink())
FS::deletePath(allowed_symlinks_file FS::deletePath(
.filePath()); // we dont want to modify the original. also make sure the resulting file is not itself a link. 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); FS::write(allowed_symlinks_file.filePath(), allowed_symlinks);
} }

View File

@ -11,19 +11,18 @@
#include "settings/SettingsObject.h" #include "settings/SettingsObject.h"
#include "tasks/Task.h" #include "tasks/Task.h"
class InstanceCopyTask : public InstanceTask class InstanceCopyTask : public InstanceTask {
{
Q_OBJECT Q_OBJECT
public: public:
explicit InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs); explicit InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs);
protected: protected:
//! Entry point for tasks. //! Entry point for tasks.
virtual void executeTask() override; virtual void executeTask() override;
void copyFinished(); void copyFinished();
void copyAborted(); void copyAborted();
private: private:
/* data */ /* data */
InstancePtr m_origInstance; InstancePtr m_origInstance;
QFuture<bool> m_copyFuture; QFuture<bool> m_copyFuture;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com> * Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
* *
@ -45,12 +45,14 @@
#include "icons/IconList.h" #include "icons/IconList.h"
#include "icons/IconUtils.h" #include "icons/IconUtils.h"
#include "modplatform/technic/TechnicPackProcessor.h"
#include "modplatform/modrinth/ModrinthInstanceCreationTask.h"
#include "modplatform/flame/FlameInstanceCreationTask.h" #include "modplatform/flame/FlameInstanceCreationTask.h"
#include "modplatform/modrinth/ModrinthInstanceCreationTask.h"
#include "modplatform/technic/TechnicPackProcessor.h"
#include "settings/INISettingsObject.h" #include "settings/INISettingsObject.h"
#include "net/ApiDownload.h"
#include <QtConcurrentRun> #include <QtConcurrentRun>
#include <algorithm> #include <algorithm>
@ -95,11 +97,11 @@ void InstanceImportTask::executeTask()
m_archivePath = entry->getFullPath(); m_archivePath = entry->getFullPath();
m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); 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));
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged); connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propogateStepProgress); connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed); connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted); connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
@ -138,8 +140,7 @@ void InstanceImportTask::processZipPack()
// open the zip and find relevant files in it // open the zip and find relevant files in it
m_packZip.reset(new QuaZip(m_archivePath)); 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.")); emitFailed(tr("Unable to open supplied modpack zip file."));
return; return;
} }
@ -153,44 +154,40 @@ void InstanceImportTask::processZipPack()
// NOTE: Prioritize modpack platforms that aren't searched for recursively. // 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 // 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 // process as Modrinth pack
qDebug() << "Modrinth:" << modrinthFound; qDebug() << "Modrinth:" << modrinthFound;
m_modpackType = ModpackType::Modrinth; m_modpackType = ModpackType::Modrinth;
} } else if (technicFound) {
else if (technicFound)
{
// process as Technic pack // process as Technic pack
qDebug() << "Technic:" << technicFound; qDebug() << "Technic:" << technicFound;
extractDir.mkpath(".minecraft"); extractDir.mkpath(".minecraft");
extractDir.cd(".minecraft"); extractDir.cd(".minecraft");
m_modpackType = ModpackType::Technic; m_modpackType = ModpackType::Technic;
} } else {
else QStringList paths_to_ignore{ "overrides/" };
{
QStringList paths_to_ignore { "overrides/" };
if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) { if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) {
// process as MultiMC instance/pack // process as MultiMC instance/pack
qDebug() << "MultiMC:" << mmcRoot; qDebug() << "MultiMC:" << mmcRoot;
root = mmcRoot; root = mmcRoot;
m_modpackType = ModpackType::MultiMC; 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 // process as Flame pack
qDebug() << "Flame:" << flameRoot; qDebug() << "Flame:" << flameRoot;
root = flameRoot; root = flameRoot;
m_modpackType = ModpackType::Flame; m_modpackType = ModpackType::Flame;
} }
} }
if(m_modpackType == ModpackType::Unknown) if (m_modpackType == ModpackType::Unknown) {
{
emitFailed(tr("Archive does not contain a recognized modpack type.")); emitFailed(tr("Archive does not contain a recognized modpack type."));
return; return;
} }
// make sure we extract just the pack // 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<QStringList>::finished, this, &InstanceImportTask::extractFinished); connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
m_extractFutureWatcher.setFuture(m_extractFuture); m_extractFutureWatcher.setFuture(m_extractFuture);
} }
@ -210,37 +207,28 @@ void InstanceImportTask::extractFinished()
qDebug() << "Fixing permissions for extracted pack files..."; qDebug() << "Fixing permissions for extracted pack files...";
QDirIterator it(extractDir, QDirIterator::Subdirectories); QDirIterator it(extractDir, QDirIterator::Subdirectories);
while (it.hasNext()) while (it.hasNext()) {
{
auto filepath = it.next(); auto filepath = it.next();
QFileInfo file(filepath); QFileInfo file(filepath);
auto permissions = QFile::permissions(filepath); auto permissions = QFile::permissions(filepath);
auto origPermissions = permissions; auto origPermissions = permissions;
if(file.isDir()) if (file.isDir()) {
{
// Folder +rwx for current user // Folder +rwx for current user
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser; permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
} } else {
else
{
// File +rw for current user // File +rw for current user
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser; permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
} }
if(origPermissions != permissions) if (origPermissions != permissions) {
{ if (!QFile::setPermissions(filepath, permissions)) {
if(!QFile::setPermissions(filepath, permissions))
{
logWarning(tr("Could not fix permissions for %1").arg(filepath)); logWarning(tr("Could not fix permissions for %1").arg(filepath));
} } else {
else
{
qDebug() << "Fixed" << filepath; qDebug() << "Fixed" << filepath;
} }
} }
} }
switch(m_modpackType) switch (m_modpackType) {
{
case ModpackType::MultiMC: case ModpackType::MultiMC:
processMultiMC(); processMultiMC();
return; return;
@ -276,7 +264,8 @@ void InstanceImportTask::processFlame()
if (original_instance_id_it != m_extra_info.constEnd()) if (original_instance_id_it != m_extra_info.constEnd())
original_instance_id = original_instance_id_it.value(); original_instance_id = original_instance_id_it.value();
inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); inst_creation_task =
makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
} else { } else {
// FIXME: Find a way to get IDs in directly imported ZIPs // FIXME: Find a way to get IDs in directly imported ZIPs
inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, QString(), QString()); inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, QString(), QString());
@ -286,14 +275,14 @@ void InstanceImportTask::processFlame()
inst_creation_task->setIcon(m_instIcon); inst_creation_task->setIcon(m_instIcon);
inst_creation_task->setGroup(m_instGroup); inst_creation_task->setGroup(m_instGroup);
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate()); inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
connect(inst_creation_task.get(), &Task::succeeded, this, [this, inst_creation_task] { connect(inst_creation_task.get(), &Task::succeeded, this, [this, inst_creation_task] {
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID()); setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
emitSucceeded(); emitSucceeded();
}); });
connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed); 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::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::status, this, &InstanceImportTask::setStatus);
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails); connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
@ -362,7 +351,8 @@ void InstanceImportTask::processModrinth()
if (original_instance_id_it != m_extra_info.constEnd()) if (original_instance_id_it != m_extra_info.constEnd())
original_instance_id = original_instance_id_it.value(); 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 { } else {
QString pack_id; QString pack_id;
if (!m_sourceUrl.isEmpty()) { if (!m_sourceUrl.isEmpty()) {
@ -378,14 +368,14 @@ void InstanceImportTask::processModrinth()
inst_creation_task->setIcon(m_instIcon); inst_creation_task->setIcon(m_instIcon);
inst_creation_task->setGroup(m_instGroup); inst_creation_task->setGroup(m_instGroup);
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate()); inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] { connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID()); setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
emitSucceeded(); emitSucceeded();
}); });
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed); connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress); 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::status, this, &InstanceImportTask::setStatus);
connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails); connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails);
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater); connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -35,54 +35,49 @@
#pragma once #pragma once
#include "InstanceTask.h"
#include "net/NetJob.h"
#include <QUrl>
#include <QFuture> #include <QFuture>
#include <QFutureWatcher> #include <QFutureWatcher>
#include "settings/SettingsObject.h" #include <QUrl>
#include "InstanceTask.h"
#include "QObjectPtr.h" #include "QObjectPtr.h"
#include "modplatform/flame/PackManifest.h" #include "modplatform/flame/PackManifest.h"
#include "net/NetJob.h"
#include "settings/SettingsObject.h"
#include <optional> #include <optional>
class QuaZip; class QuaZip;
namespace Flame namespace Flame {
{ class FileResolvingTask;
class FileResolvingTask;
} }
class InstanceImportTask : public InstanceTask class InstanceImportTask : public InstanceTask {
{
Q_OBJECT Q_OBJECT
public: public:
explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {}); explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
bool abort() override; bool abort() override;
const QVector<Flame::File> &getBlockedFiles() const const QVector<Flame::File>& getBlockedFiles() const { return m_blockedMods; }
{
return m_blockedMods;
}
protected: protected:
//! Entry point for tasks. //! Entry point for tasks.
virtual void executeTask() override; virtual void executeTask() override;
private: private:
void processZipPack(); void processZipPack();
void processMultiMC(); void processMultiMC();
void processTechnic(); void processTechnic();
void processFlame(); void processFlame();
void processModrinth(); void processModrinth();
private slots: private slots:
void downloadSucceeded(); void downloadSucceeded();
void downloadFailed(QString reason); void downloadFailed(QString reason);
void downloadProgressChanged(qint64 current, qint64 total); void downloadProgressChanged(qint64 current, qint64 total);
void downloadAborted(); void downloadAborted();
void extractFinished(); void extractFinished();
private: /* data */ private: /* data */
NetJob::Ptr m_filesNetJob; NetJob::Ptr m_filesNetJob;
shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver; shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver;
QUrl m_sourceUrl; QUrl m_sourceUrl;
@ -92,7 +87,7 @@ private: /* data */
QFuture<std::optional<QStringList>> m_extractFuture; QFuture<std::optional<QStringList>> m_extractFuture;
QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher; QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher;
QVector<Flame::File> m_blockedMods; QVector<Flame::File> m_blockedMods;
enum class ModpackType{ enum class ModpackType {
Unknown, Unknown,
MultiMC, MultiMC,
Technic, Technic,
@ -104,6 +99,6 @@ private: /* data */
// the source URL / the resource it points to alone. // the source URL / the resource it points to alone.
QMap<QString, QString> m_extra_info; QMap<QString, QString> m_extra_info;
//FIXME: nuke // FIXME: nuke
QWidget* m_parent; QWidget* m_parent;
}; };

View File

@ -42,9 +42,9 @@
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QMimeData> #include <QMimeData>
#include <QPair>
#include <QSet> #include <QSet>
#include <QStack> #include <QStack>
#include <QPair>
#include <QTextStream> #include <QTextStream>
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
@ -97,7 +97,11 @@ Qt::DropActions InstanceList::supportedDropActions() const
return Qt::MoveAction; 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")) { if (data && data->hasFormat("application/x-instanceid")) {
return true; return true;
@ -105,7 +109,11 @@ bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action,
return false; 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")) { if (data && data->hasFormat("application/x-instanceid")) {
return true; return true;
@ -130,7 +138,7 @@ QMimeData* InstanceList::mimeData(const QModelIndexList& indexes) const
return mimeData; return mimeData;
} }
QStringList InstanceList::getLinkedInstancesById(const QString &id) const QStringList InstanceList::getLinkedInstancesById(const QString& id) const
{ {
QStringList linkedInstances; QStringList linkedInstances;
for (auto inst : m_instances) { for (auto inst : m_instances) {
@ -159,42 +167,34 @@ QVariant InstanceList::data(const QModelIndex& index, int role) const
if (!index.isValid()) { if (!index.isValid()) {
return QVariant(); return QVariant();
} }
BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer()); BaseInstance* pdata = static_cast<BaseInstance*>(index.internalPointer());
switch (role) switch (role) {
{ case InstancePointerRole: {
case InstancePointerRole: QVariant v = QVariant::fromValue((void*)pdata);
{ return v;
QVariant v = QVariant::fromValue((void *)pdata); }
return v; case InstanceIDRole: {
} return pdata->id();
case InstanceIDRole: }
{ case Qt::EditRole:
return pdata->id(); case Qt::DisplayRole: {
} return pdata->name();
case Qt::EditRole: }
case Qt::DisplayRole: case Qt::AccessibleTextRole: {
{ return tr("%1 Instance").arg(pdata->name());
return pdata->name(); }
} case Qt::ToolTipRole: {
case Qt::AccessibleTextRole: return pdata->instanceRoot();
{ }
return tr("%1 Instance").arg(pdata->name()); case Qt::DecorationRole: {
} return pdata->iconKey();
case Qt::ToolTipRole: }
{ // HACK: see InstanceView.h in gui!
return pdata->instanceRoot(); case GroupRole: {
} return getInstanceGroup(pdata->id());
case Qt::DecorationRole: }
{ default:
return pdata->iconKey(); break;
}
// HACK: see InstanceView.h in gui!
case GroupRole:
{
return getInstanceGroup(pdata->id());
}
default:
break;
} }
return QVariant(); return QVariant();
} }
@ -352,16 +352,18 @@ bool InstanceList::trashInstance(const InstanceId& id)
} }
qDebug() << "Instance" << id << "has been trashed by the launcher."; 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; return true;
} }
bool InstanceList::trashedSomething() { bool InstanceList::trashedSomething()
{
return !m_trashHistory.empty(); return !m_trashHistory.empty();
} }
void InstanceList::undoTrashInstance() { void InstanceList::undoTrashInstance()
{
if (m_trashHistory.empty()) { if (m_trashHistory.empty()) {
qWarning() << "Nothing to recover from trash."; qWarning() << "Nothing to recover from trash.";
return; return;
@ -593,7 +595,7 @@ InstancePtr InstanceList::getInstanceByManagedName(const QString& managed_name)
return {}; return {};
} }
QModelIndex InstanceList::getInstanceIndexById(const QString &id) const QModelIndex InstanceList::getInstanceIndexById(const QString& id) const
{ {
return index(getInstIndex(getInstanceById(id).get())); return index(getInstIndex(getInstanceById(id).get()));
} }
@ -632,13 +634,11 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id)
QString inst_type = instanceSettings->get("InstanceType").toString(); 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 // NOTE: Some PolyMC versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a OneSix
if (inst_type == "OneSix" || inst_type.isEmpty()) // instance
{ if (inst_type == "OneSix" || inst_type.isEmpty()) {
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot)); inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
} } else {
else
{
inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot)); inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot));
} }
qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot(); qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot();
@ -815,7 +815,7 @@ void InstanceList::instanceDirContentsChanged(const QString& path)
emit instancesChanged(); 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(); QString newInstDir = QDir(value.toString()).canonicalPath();
if (newInstDir != m_instDir) { if (newInstDir != m_instDir) {
@ -843,20 +843,25 @@ class InstanceStaging : public Task {
Q_OBJECT Q_OBJECT
const unsigned minBackoff = 1; const unsigned minBackoff = 1;
const unsigned maxBackoff = 16; const unsigned maxBackoff = 16;
public: public:
InstanceStaging(InstanceList* parent, InstanceTask* child, QString stagingPath, InstanceName const& instanceName, QString groupName) 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); 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::failed, this, &InstanceStaging::childFailed);
connect(child, &Task::aborted, this, &InstanceStaging::childAborted); connect(child, &Task::aborted, this, &InstanceStaging::childAborted);
connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable); connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable);
connect(child, &Task::status, this, &InstanceStaging::setStatus); connect(child, &Task::status, this, &InstanceStaging::setStatus);
connect(child, &Task::details, this, &InstanceStaging::setDetails); connect(child, &Task::details, this, &InstanceStaging::setDetails);
connect(child, &Task::progress, this, &InstanceStaging::setProgress); connect(child, &Task::progress, this, &InstanceStaging::setProgress);
connect(child, &Task::stepProgress, this, &InstanceStaging::propogateStepProgress); connect(child, &Task::stepProgress, this, &InstanceStaging::propagateStepProgress);
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded); connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceeded);
} }
virtual ~InstanceStaging(){}; virtual ~InstanceStaging(){};
@ -871,21 +876,17 @@ class InstanceStaging : public Task {
return Task::abort(); return Task::abort();
} }
bool canAbort() const override bool canAbort() const override { return (m_child && m_child->canAbort()); }
{
return (m_child && m_child->canAbort());
}
protected: protected:
virtual void executeTask() override { m_child->start(); } virtual void executeTask() override { m_child->start(); }
QStringList warnings() const override { return m_child->warnings(); } QStringList warnings() const override { return m_child->warnings(); }
private slots: private slots:
void childSucceded() void childSucceeded()
{ {
unsigned sleepTime = backoff(); 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(); emitSucceeded();
return; return;
} }
@ -903,13 +904,10 @@ class InstanceStaging : public Task {
emitFailed(reason); emitFailed(reason);
} }
void childAborted() void childAborted() { emitAborted(); }
{
emitAborted();
}
private: private:
InstanceList * m_parent; InstanceList* m_parent;
/* /*
* WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows. * 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 * Basically, it starts messing things up while the launcher is extracting/creating instances
@ -948,7 +946,10 @@ QString InstanceList::getStagedInstancePath()
return path; 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; QDir dir;
QString instID; QString instID;

View File

@ -35,12 +35,12 @@
#pragma once #pragma once
#include <QObject>
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QSet>
#include <QList> #include <QList>
#include <QStack> #include <QObject>
#include <QPair> #include <QPair>
#include <QSet>
#include <QStack>
#include "BaseInstance.h" #include "BaseInstance.h"
@ -52,21 +52,9 @@ using InstanceId = QString;
using GroupId = QString; using GroupId = QString;
using InstanceLocator = std::pair<InstancePtr, int>; using InstanceLocator = std::pair<InstancePtr, int>;
enum class InstCreateError enum class InstCreateError { NoCreateError = 0, NoSuchVersion, UnknownCreateError, InstExists, CantCreateDir };
{
NoCreateError = 0,
NoSuchVersion,
UnknownCreateError,
InstExists,
CantCreateDir
};
enum class GroupsState enum class GroupsState { NotLoaded, Steady, Dirty };
{
NotLoaded,
Steady,
Dirty
};
struct TrashHistoryItem { struct TrashHistoryItem {
QString id; QString id;
@ -75,48 +63,36 @@ struct TrashHistoryItem {
QString groupName; QString groupName;
}; };
class InstanceList : public QAbstractListModel class InstanceList : public QAbstractListModel {
{
Q_OBJECT Q_OBJECT
public: public:
explicit InstanceList(SettingsObjectPtr settings, const QString & instDir, QObject *parent = 0); explicit InstanceList(SettingsObjectPtr settings, const QString& instDir, QObject* parent = 0);
virtual ~InstanceList(); virtual ~InstanceList();
public: public:
QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override; QModelIndex index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override; QVariant data(const QModelIndex& index, int role) const override;
Qt::ItemFlags flags(const QModelIndex &index) 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, GroupRole = Qt::UserRole,
InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance
InstanceIDRole = 0x34B1CB49 ///< Return id if the instance InstanceIDRole = 0x34B1CB49 ///< Return id if the instance
}; };
/*! /*!
* \brief Error codes returned by functions in the InstanceList class. * \brief Error codes returned by functions in the InstanceList class.
* NoError Indicates that no error occurred. * NoError Indicates that no error occurred.
* UnknownError indicates that an unspecified error occurred. * UnknownError indicates that an unspecified error occurred.
*/ */
enum InstListError enum InstListError { NoError = 0, UnknownError };
{
NoError = 0,
UnknownError
};
InstancePtr at(int i) const InstancePtr at(int i) const { return m_instances.at(i); }
{
return m_instances.at(i);
}
int count() const int count() const { return m_instances.count(); }
{
return m_instances.count();
}
InstListError loadList(); InstListError loadList();
void saveNow(); void saveNow();
@ -125,22 +101,22 @@ public:
InstancePtr getInstanceById(QString id) const; InstancePtr getInstanceById(QString id) const;
/* O(n) */ /* O(n) */
InstancePtr getInstanceByManagedName(const QString& managed_name) const; InstancePtr getInstanceByManagedName(const QString& managed_name) const;
QModelIndex getInstanceIndexById(const QString &id) const; QModelIndex getInstanceIndexById(const QString& id) const;
QStringList getGroups(); QStringList getGroups();
bool isGroupCollapsed(const QString &groupName); bool isGroupCollapsed(const QString& groupName);
GroupId getInstanceGroup(const InstanceId & id) const; GroupId getInstanceGroup(const InstanceId& id) const;
void setInstanceGroup(const InstanceId & id, GroupId name); void setInstanceGroup(const InstanceId& id, const GroupId& name);
void deleteGroup(const GroupId & name); void deleteGroup(const GroupId& name);
void renameGroup(const GroupId& src, const GroupId& dst); void renameGroup(const GroupId& src, const GroupId& dst);
bool trashInstance(const InstanceId &id); bool trashInstance(const InstanceId& id);
bool trashedSomething(); bool trashedSomething();
void undoTrashInstance(); 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 // 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. * Create a new empty staging area for instance creation and @return a path/key top commit it later.
@ -160,7 +136,7 @@ public:
* Destroy a previously created staging area given by @keyPath - used when creation fails. * Destroy a previously created staging area given by @keyPath - used when creation fails.
* Used by instance manipulation tasks. * Used by instance manipulation tasks.
*/ */
bool destroyStagingPath(const QString & keyPath); bool destroyStagingPath(const QString& keyPath);
int getTotalPlayTime(); int getTotalPlayTime();
@ -168,36 +144,36 @@ public:
Qt::DropActions supportedDropActions() const override; 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; 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 dataIsInvalid();
void instancesChanged(); void instancesChanged();
void instanceSelectRequest(QString instanceId); void instanceSelectRequest(QString instanceId);
void groupsChanged(QSet<QString> groups); void groupsChanged(QSet<QString> groups);
public slots: public slots:
void on_InstFolderChanged(const Setting &setting, QVariant value); void on_InstFolderChanged(const Setting& setting, QVariant value);
void on_GroupStateChanged(const QString &group, bool collapsed); void on_GroupStateChanged(const QString& group, bool collapsed);
private slots: private slots:
void propertiesChanged(BaseInstance *inst); void propertiesChanged(BaseInstance* inst);
void providerUpdated(); void providerUpdated();
void instanceDirContentsChanged(const QString &path); void instanceDirContentsChanged(const QString& path);
private: private:
int getInstIndex(BaseInstance *inst) const; int getInstIndex(BaseInstance* inst) const;
void updateTotalPlayTime(); void updateTotalPlayTime();
void suspendWatch(); void suspendWatch();
void resumeWatch(); void resumeWatch();
void add(const QList<InstancePtr> &list); void add(const QList<InstancePtr>& list);
void loadGroupList(); void loadGroupList();
void saveGroupList(); void saveGroupList();
QList<InstanceId> discoverInstances(); QList<InstanceId> discoverInstances();
@ -206,7 +182,7 @@ private:
void increaseGroupCount(const QString& group); void increaseGroupCount(const QString& group);
void decreaseGroupCount(const QString& group); void decreaseGroupCount(const QString& group);
private: private:
int m_watchLevel = 0; int m_watchLevel = 0;
int totalPlayTime = 0; int totalPlayTime = 0;
bool m_dirty = false; bool m_dirty = false;
@ -216,7 +192,7 @@ private:
SettingsObjectPtr m_globalSettings; SettingsObjectPtr m_globalSettings;
QString m_instDir; QString m_instDir;
QFileSystemWatcher * m_watcher; QFileSystemWatcher* m_watcher;
// FIXME: this is so inefficient that looking at it is almost painful. // FIXME: this is so inefficient that looking at it is almost painful.
QSet<QString> m_collapsedGroups; QSet<QString> m_collapsedGroups;
QMap<InstanceId, GroupId> m_instanceGroupIndex; QMap<InstanceId, GroupId> m_instanceGroupIndex;

View File

@ -1,36 +1,31 @@
#pragma once #pragma once
#include "minecraft/MinecraftInstance.h"
#include <FileSystem.h> #include <FileSystem.h>
#include "minecraft/MinecraftInstance.h"
#include "ui/pages/BasePage.h" #include "ui/pages/BasePage.h"
#include "ui/pages/BasePageProvider.h" #include "ui/pages/BasePageProvider.h"
#include "ui/pages/instance/InstanceSettingsPage.h"
#include "ui/pages/instance/LogPage.h" #include "ui/pages/instance/LogPage.h"
#include "ui/pages/instance/VersionPage.h"
#include "ui/pages/instance/ManagedPackPage.h" #include "ui/pages/instance/ManagedPackPage.h"
#include "ui/pages/instance/ModFolderPage.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/NotesPage.h"
#include "ui/pages/instance/ScreenshotsPage.h"
#include "ui/pages/instance/InstanceSettingsPage.h"
#include "ui/pages/instance/OtherLogsPage.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/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 Q_OBJECT
public: public:
explicit InstancePageProvider(InstancePtr parent) explicit InstancePageProvider(InstancePtr parent) { inst = parent; }
{
inst = parent;
}
virtual ~InstancePageProvider() {}; virtual ~InstancePageProvider(){};
virtual QList<BasePage *> getPages() override virtual QList<BasePage*> getPages() override
{ {
QList<BasePage *> values; QList<BasePage*> values;
values.append(new LogPage(inst)); values.append(new LogPage(inst));
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst); std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
values.append(new VersionPage(onesix.get())); values.append(new VersionPage(onesix.get()));
@ -50,18 +45,14 @@ public:
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots"))); values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
values.append(new InstanceSettingsPage(onesix.get())); values.append(new InstanceSettingsPage(onesix.get()));
auto logMatcher = inst->getLogFileMatcher(); auto logMatcher = inst->getLogFileMatcher();
if(logMatcher) if (logMatcher) {
{
values.append(new OtherLogsPage(inst->getLogFileRoot(), logMatcher)); values.append(new OtherLogsPage(inst->getLogFileRoot(), logMatcher));
} }
return values; return values;
} }
virtual QString dialogTitle() override virtual QString dialogTitle() override { return tr("Edit Instance (%1)").arg(inst->name()); }
{
return tr("Edit Instance (%1)").arg(inst->name()); protected:
}
protected:
InstancePtr inst; InstancePtr inst;
}; };

View File

@ -18,13 +18,14 @@ InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& ol
return InstanceNameChange::ShouldKeep; return InstanceNameChange::ShouldKeep;
} }
ShouldUpdate askIfShouldUpdate(QWidget *parent, QString original_version_name) ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name)
{ {
auto info = CustomMessageBox::selectable( auto info = CustomMessageBox::selectable(
parent, QObject::tr("Similar modpack was found!"), 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 " QObject::tr(
"separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before " "One or more of your instances are from this same modpack%1. Do you want to create a "
"updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).") "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), .arg(original_version_name),
QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort); QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort);
info->setButtonText(QMessageBox::Ok, QObject::tr("Update existing instance")); 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)) if (info->clickedButton() == info->button(QMessageBox::Abort))
return ShouldUpdate::SkipUpdating; return ShouldUpdate::SkipUpdating;
return ShouldUpdate::Cancel; return ShouldUpdate::Cancel;
} }
QString InstanceName::name() const QString InstanceName::name() const

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -39,43 +39,39 @@
#include <QRegularExpression> #include <QRegularExpression>
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent) bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget* parent)
{ {
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegularExpression("-Xm[sx]")) if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegularExpression("-Xm[sx]")) || jvmargs.contains("-XX-MaxHeapSize") ||
|| jvmargs.contains("-XX-MaxHeapSize") || jvmargs.contains("-XX:InitialHeapSize")) jvmargs.contains("-XX:InitialHeapSize")) {
{
auto warnStr = QObject::tr( 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" "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."); "This message will be displayed until you remove them from the JVM arguments.");
CustomMessageBox::selectable( CustomMessageBox::selectable(parent, QObject::tr("JVM arguments warning"), warnStr, QMessageBox::Warning)->exec();
parent, QObject::tr("JVM arguments warning"),
warnStr,
QMessageBox::Warning)->exec();
return false; return false;
} }
// block lunacy with passing required version to the JVM // block lunacy with passing required version to the JVM
if (jvmargs.contains(QRegularExpression("-version:.*"))) { if (jvmargs.contains(QRegularExpression("-version:.*"))) {
auto warnStr = QObject::tr( 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."); "This message will be displayed until you remove this from the JVM arguments.");
CustomMessageBox::selectable( CustomMessageBox::selectable(parent, QObject::tr("JVM arguments warning"), warnStr, QMessageBox::Warning)->exec();
parent, QObject::tr("JVM arguments warning"),
warnStr,
QMessageBox::Warning)->exec();
return false; return false;
} }
return true; return true;
} }
void JavaCommon::javaWasOk(QWidget *parent, JavaCheckResult result) void JavaCommon::javaWasOk(QWidget* parent, const JavaCheckResult& result)
{ {
QString text; QString text;
text += QObject::tr("Java test succeeded!<br />Platform reported: %1<br />Java version " text += QObject::tr(
"reported: %2<br />Java vendor " "Java test succeeded!<br />Platform reported: %1<br />Java version "
"reported: %3<br />").arg(result.realPlatform, result.javaVersion.toString(), result.javaVendor); "reported: %2<br />Java vendor "
if (result.errorLog.size()) "reported: %3<br />")
{ .arg(result.realPlatform, result.javaVersion.toString(), result.javaVendor);
if (result.errorLog.size()) {
auto htmlError = result.errorLog; auto htmlError = result.errorLog;
htmlError.replace('\n', "<br />"); htmlError.replace('\n', "<br />");
text += QObject::tr("<br />Warnings:<br /><font color=\"orange\">%1</font>").arg(htmlError); text += QObject::tr("<br />Warnings:<br /><font color=\"orange\">%1</font>").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(); 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; auto htmlError = result.errorLog;
QString text; 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(); 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; QString text;
text += QObject::tr( 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(); CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
} }
void JavaCommon::javaCheckNotFound(QWidget *parent) void JavaCommon::javaCheckNotFound(QWidget* parent)
{ {
QString text; QString text;
text += QObject::tr("Java checker library could not be found. Please check your installation."); 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() void JavaCommon::TestCheck::run()
{ {
if (!JavaCommon::checkJVMArgs(m_args, m_parent)) if (!JavaCommon::checkJVMArgs(m_args, m_parent)) {
{
emit finished(); emit finished();
return; return;
} }
@ -129,8 +124,7 @@ void JavaCommon::TestCheck::run()
void JavaCommon::TestCheck::checkFinished(JavaCheckResult result) void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
{ {
if (result.validity != JavaCheckResult::Validity::Valid) if (result.validity != JavaCheckResult::Validity::Valid) {
{
javaBinaryWasBad(m_parent, result); javaBinaryWasBad(m_parent, result);
emit finished(); emit finished();
return; return;
@ -141,8 +135,7 @@ void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
checker->m_args = m_args; checker->m_args = m_args;
checker->m_minMem = m_minMem; checker->m_minMem = m_minMem;
checker->m_maxMem = m_maxMem; checker->m_maxMem = m_maxMem;
if (result.javaVersion.requiresPermGen()) if (result.javaVersion.requiresPermGen()) {
{
checker->m_permGen = m_permGen; checker->m_permGen = m_permGen;
} }
checker->performCheck(); checker->performCheck();
@ -150,8 +143,7 @@ void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result) void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result)
{ {
if (result.validity == JavaCheckResult::Validity::Valid) if (result.validity == JavaCheckResult::Validity::Valid) {
{
javaWasOk(m_parent, result); javaWasOk(m_parent, result);
emit finished(); emit finished();
return; return;
@ -159,4 +151,3 @@ void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result)
javaArgsWereBad(m_parent, result); javaArgsWereBad(m_parent, result);
emit finished(); emit finished();
} }

View File

@ -6,45 +6,42 @@ class QWidget;
/** /**
* Common UI bits for the java pages to use. * Common UI bits for the java pages to use.
*/ */
namespace JavaCommon namespace JavaCommon {
{ bool checkJVMArgs(QString args, QWidget* parent);
bool checkJVMArgs(QString args, QWidget *parent);
// Show a dialog saying that the Java binary was usable // Show a dialog saying that the Java binary was usable
void javaWasOk(QWidget *parent, JavaCheckResult result); void javaWasOk(QWidget* parent, const JavaCheckResult& result);
// Show a dialog saying that the Java binary was not usable because of bad options // Show a dialog saying that the Java binary was not usable because of bad options
void javaArgsWereBad(QWidget *parent, JavaCheckResult result); void javaArgsWereBad(QWidget* parent, const JavaCheckResult& result);
// Show a dialog saying that the Java binary was not usable // Show a dialog saying that the Java binary was not usable
void javaBinaryWasBad(QWidget *parent, JavaCheckResult result); void javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result);
// Show a dialog if we couldn't find Java Checker // Show a dialog if we couldn't find Java Checker
void javaCheckNotFound(QWidget *parent); void javaCheckNotFound(QWidget* parent);
class TestCheck : public QObject class TestCheck : public QObject {
{ Q_OBJECT
Q_OBJECT public:
public: TestCheck(QWidget* parent, QString path, QString args, int minMem, int maxMem, int permGen)
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)
:m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen) {}
{ virtual ~TestCheck(){};
}
virtual ~TestCheck() {};
void run(); void run();
signals: signals:
void finished(); void finished();
private slots: private slots:
void checkFinished(JavaCheckResult result); void checkFinished(JavaCheckResult result);
void checkFinishedWithArgs(JavaCheckResult result); void checkFinishedWithArgs(JavaCheckResult result);
private: private:
std::shared_ptr<JavaChecker> checker; std::shared_ptr<JavaChecker> checker;
QWidget *m_parent = nullptr; QWidget* m_parent = nullptr;
QString m_path; QString m_path;
QString m_args; QString m_args;
int m_minMem = 0; int m_minMem = 0;
int m_maxMem = 0; int m_maxMem = 0;
int m_permGen = 64; int m_permGen = 64;
}; };
} } // namespace JavaCommon

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -37,257 +37,246 @@
#include <QFile> #include <QFile>
#include "FileSystem.h"
#include <math.h> #include <math.h>
#include "FileSystem.h"
namespace Json namespace Json {
{ void write(const QJsonDocument& doc, const QString& filename)
void write(const QJsonDocument &doc, const QString &filename)
{ {
FS::write(filename, doc.toJson()); 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); write(QJsonDocument(object), filename);
} }
void write(const QJsonArray &array, const QString &filename) void write(const QJsonArray& array, const QString& filename)
{ {
write(QJsonDocument(array), filename); write(QJsonDocument(array), filename);
} }
QByteArray toText(const QJsonObject &obj) QByteArray toText(const QJsonObject& obj)
{ {
return QJsonDocument(obj).toJson(QJsonDocument::Compact); return QJsonDocument(obj).toJson(QJsonDocument::Compact);
} }
QByteArray toText(const QJsonArray &array) QByteArray toText(const QJsonArray& array)
{ {
return QJsonDocument(array).toJson(QJsonDocument::Compact); return QJsonDocument(array).toJson(QJsonDocument::Compact);
} }
static bool isBinaryJson(const QByteArray &data) static bool isBinaryJson(const QByteArray& data)
{ {
decltype(QJsonDocument::BinaryFormatTag) tag = QJsonDocument::BinaryFormatTag; decltype(QJsonDocument::BinaryFormatTag) tag = QJsonDocument::BinaryFormatTag;
return memcmp(data.constData(), &tag, sizeof(QJsonDocument::BinaryFormatTag)) == 0; 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? // FIXME: Is this needed?
throw JsonException(what + ": Invalid JSON. Binary JSON unsupported"); throw JsonException(what + ": Invalid JSON. Binary JSON unsupported");
} } else {
else
{
QJsonParseError error; QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(data, &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()); throw JsonException(what + ": Error parsing JSON: " + error.errorString());
} }
return doc; return doc;
} }
} }
QJsonDocument requireDocument(const QString &filename, const QString &what) QJsonDocument requireDocument(const QString& filename, const QString& what)
{ {
return requireDocument(FS::read(filename), 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"); throw JsonException(what + " is not an object");
} }
return doc.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"); throw JsonException(what + " is not an array");
} }
return doc.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); 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; QJsonArray array;
for(auto value: values) for (auto value : values) {
{
array.append(value); array.append(value);
} }
to.insert(key, array); to.insert(key, array);
} }
} }
template<> template <>
QJsonValue toJson<QUrl>(const QUrl &url) QJsonValue toJson<QUrl>(const QUrl& url)
{ {
return QJsonValue(url.toString(QUrl::FullyEncoded)); return QJsonValue(url.toString(QUrl::FullyEncoded));
} }
template<> template <>
QJsonValue toJson<QByteArray>(const QByteArray &data) QJsonValue toJson<QByteArray>(const QByteArray& data)
{ {
return QJsonValue(QString::fromLatin1(data.toHex())); return QJsonValue(QString::fromLatin1(data.toHex()));
} }
template<> template <>
QJsonValue toJson<QDateTime>(const QDateTime &datetime) QJsonValue toJson<QDateTime>(const QDateTime& datetime)
{ {
return QJsonValue(datetime.toString(Qt::ISODate)); return QJsonValue(datetime.toString(Qt::ISODate));
} }
template<> template <>
QJsonValue toJson<QDir>(const QDir &dir) QJsonValue toJson<QDir>(const QDir& dir)
{ {
return QDir::current().relativeFilePath(dir.absolutePath()); return QDir::current().relativeFilePath(dir.absolutePath());
} }
template<> template <>
QJsonValue toJson<QUuid>(const QUuid &uuid) QJsonValue toJson<QUuid>(const QUuid& uuid)
{ {
return uuid.toString(); return uuid.toString();
} }
template<> template <>
QJsonValue toJson<QVariant>(const QVariant &variant) QJsonValue toJson<QVariant>(const QVariant& variant)
{ {
return QJsonValue::fromVariant(variant); return QJsonValue::fromVariant(variant);
} }
template <>
template<> QByteArray requireIsType<QByteArray>(const QJsonValue &value, const QString &what) QByteArray requireIsType<QByteArray>(const QJsonValue& value, const QString& what)
{ {
const QString string = ensureIsType<QString>(value, what); const QString string = ensureIsType<QString>(value, what);
// ensure that the string can be safely cast to Latin1 // 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"); throw JsonException(what + " is not encodable as Latin1");
} }
return QByteArray::fromHex(string.toLatin1()); return QByteArray::fromHex(string.toLatin1());
} }
template<> QJsonArray requireIsType<QJsonArray>(const QJsonValue &value, const QString &what) template <>
QJsonArray requireIsType<QJsonArray>(const QJsonValue& value, const QString& what)
{ {
if (!value.isArray()) if (!value.isArray()) {
{
throw JsonException(what + " is not an array"); throw JsonException(what + " is not an array");
} }
return value.toArray(); return value.toArray();
} }
template <>
template<> QString requireIsType<QString>(const QJsonValue &value, const QString &what) QString requireIsType<QString>(const QJsonValue& value, const QString& what)
{ {
if (!value.isString()) if (!value.isString()) {
{
throw JsonException(what + " is not a string"); throw JsonException(what + " is not a string");
} }
return value.toString(); return value.toString();
} }
template<> bool requireIsType<bool>(const QJsonValue &value, const QString &what) template <>
bool requireIsType<bool>(const QJsonValue& value, const QString& what)
{ {
if (!value.isBool()) if (!value.isBool()) {
{
throw JsonException(what + " is not a bool"); throw JsonException(what + " is not a bool");
} }
return value.toBool(); return value.toBool();
} }
template<> double requireIsType<double>(const QJsonValue &value, const QString &what) template <>
double requireIsType<double>(const QJsonValue& value, const QString& what)
{ {
if (!value.isDouble()) if (!value.isDouble()) {
{
throw JsonException(what + " is not a double"); throw JsonException(what + " is not a double");
} }
return value.toDouble(); return value.toDouble();
} }
template<> int requireIsType<int>(const QJsonValue &value, const QString &what) template <>
int requireIsType<int>(const QJsonValue& value, const QString& what)
{ {
const double doubl = requireIsType<double>(value, what); const double doubl = requireIsType<double>(value, what);
if (fmod(doubl, 1) != 0) if (fmod(doubl, 1) != 0) {
{
throw JsonException(what + " is not an integer"); throw JsonException(what + " is not an integer");
} }
return int(doubl); return int(doubl);
} }
template<> QDateTime requireIsType<QDateTime>(const QJsonValue &value, const QString &what) template <>
QDateTime requireIsType<QDateTime>(const QJsonValue& value, const QString& what)
{ {
const QString string = requireIsType<QString>(value, what); const QString string = requireIsType<QString>(value, what);
const QDateTime datetime = QDateTime::fromString(string, Qt::ISODate); 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"); throw JsonException(what + " is not a ISO formatted date/time value");
} }
return datetime; return datetime;
} }
template<> QUrl requireIsType<QUrl>(const QJsonValue &value, const QString &what) template <>
QUrl requireIsType<QUrl>(const QJsonValue& value, const QString& what)
{ {
const QString string = ensureIsType<QString>(value, what); const QString string = ensureIsType<QString>(value, what);
if (string.isEmpty()) if (string.isEmpty()) {
{
return QUrl(); return QUrl();
} }
const QUrl url = QUrl(string, QUrl::StrictMode); const QUrl url = QUrl(string, QUrl::StrictMode);
if (!url.isValid()) if (!url.isValid()) {
{
throw JsonException(what + " is not a correctly formatted URL"); throw JsonException(what + " is not a correctly formatted URL");
} }
return url; return url;
} }
template<> QDir requireIsType<QDir>(const QJsonValue &value, const QString &what) template <>
QDir requireIsType<QDir>(const QJsonValue& value, const QString& what)
{ {
const QString string = requireIsType<QString>(value, what); const QString string = requireIsType<QString>(value, what);
// FIXME: does not handle invalid characters! // FIXME: does not handle invalid characters!
return QDir::current().absoluteFilePath(string); return QDir::current().absoluteFilePath(string);
} }
template<> QUuid requireIsType<QUuid>(const QJsonValue &value, const QString &what) template <>
QUuid requireIsType<QUuid>(const QJsonValue& value, const QString& what)
{ {
const QString string = requireIsType<QString>(value, what); const QString string = requireIsType<QString>(value, what);
const QUuid uuid = QUuid(string); 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"); throw JsonException(what + " is not a valid UUID");
} }
return uuid; return uuid;
} }
template<> QJsonObject requireIsType<QJsonObject>(const QJsonValue &value, const QString &what) template <>
QJsonObject requireIsType<QJsonObject>(const QJsonValue& value, const QString& what)
{ {
if (!value.isObject()) if (!value.isObject()) {
{
throw JsonException(what + " is not an object"); throw JsonException(what + " is not an object");
} }
return value.toObject(); return value.toObject();
} }
template<> QVariant requireIsType<QVariant>(const QJsonValue &value, const QString &what) template <>
QVariant requireIsType<QVariant>(const QJsonValue& value, const QString& what)
{ {
if (value.isNull() || value.isUndefined()) if (value.isNull() || value.isUndefined()) {
{
throw JsonException(what + " is null or undefined"); throw JsonException(what + " is null or undefined");
} }
return value.toVariant(); return value.toVariant();
} }
template<> QJsonValue requireIsType<QJsonValue>(const QJsonValue &value, const QString &what) template <>
QJsonValue requireIsType<QJsonValue>(const QJsonValue& value, const QString& what)
{ {
if (value.isNull() || value.isUndefined()) if (value.isNull() || value.isUndefined()) {
{
throw JsonException(what + " is null or undefined"); throw JsonException(what + " is null or undefined");
} }
return value; return value;
} }
} } // namespace Json

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -35,74 +35,71 @@
#pragma once #pragma once
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QDateTime> #include <QDateTime>
#include <QUrl>
#include <QDir> #include <QDir>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QUrl>
#include <QUuid> #include <QUuid>
#include <QVariant> #include <QVariant>
#include <memory> #include <memory>
#include "Exception.h" #include "Exception.h"
namespace Json namespace Json {
{ class JsonException : public ::Exception {
class JsonException : public ::Exception public:
{ JsonException(const QString& message) : Exception(message) {}
public:
JsonException(const QString &message) : Exception(message) {}
}; };
/// @throw FileSystemException /// @throw FileSystemException
void write(const QJsonDocument &doc, const QString &filename); void write(const QJsonDocument& doc, const QString& filename);
/// @throw FileSystemException /// @throw FileSystemException
void write(const QJsonObject &object, const QString &filename); void write(const QJsonObject& object, const QString& filename);
/// @throw FileSystemException /// @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 QJsonObject& obj);
QByteArray toText(const QJsonArray &array); QByteArray toText(const QJsonArray& array);
/// @throw JsonException /// @throw JsonException
QJsonDocument requireDocument(const QByteArray &data, const QString &what = "Document"); QJsonDocument requireDocument(const QByteArray& data, const QString& what = "Document");
/// @throw JsonException /// @throw JsonException
QJsonDocument requireDocument(const QString &filename, const QString &what = "Document"); QJsonDocument requireDocument(const QString& filename, const QString& what = "Document");
/// @throw JsonException /// @throw JsonException
QJsonObject requireObject(const QJsonDocument &doc, const QString &what = "Document"); QJsonObject requireObject(const QJsonDocument& doc, const QString& what = "Document");
/// @throw JsonException /// @throw JsonException
QJsonArray requireArray(const QJsonDocument &doc, const QString &what = "Document"); QJsonArray requireArray(const QJsonDocument& doc, const QString& what = "Document");
/////////////////// WRITING //////////////////// /////////////////// WRITING ////////////////////
void writeString(QJsonObject & to, const QString &key, const QString &value); void writeString(QJsonObject& to, const QString& key, const QString& value);
void writeStringList(QJsonObject & to, const QString &key, const QStringList &values); void writeStringList(QJsonObject& to, const QString& key, const QStringList& values);
template<typename T> template <typename T>
QJsonValue toJson(const T &t) QJsonValue toJson(const T& t)
{ {
return QJsonValue(t); return QJsonValue(t);
} }
template<> template <>
QJsonValue toJson<QUrl>(const QUrl &url); QJsonValue toJson<QUrl>(const QUrl& url);
template<> template <>
QJsonValue toJson<QByteArray>(const QByteArray &data); QJsonValue toJson<QByteArray>(const QByteArray& data);
template<> template <>
QJsonValue toJson<QDateTime>(const QDateTime &datetime); QJsonValue toJson<QDateTime>(const QDateTime& datetime);
template<> template <>
QJsonValue toJson<QDir>(const QDir &dir); QJsonValue toJson<QDir>(const QDir& dir);
template<> template <>
QJsonValue toJson<QUuid>(const QUuid &uuid); QJsonValue toJson<QUuid>(const QUuid& uuid);
template<> template <>
QJsonValue toJson<QVariant>(const QVariant &variant); QJsonValue toJson<QVariant>(const QVariant& variant);
template<typename T> template <typename T>
QJsonArray toJsonArray(const QList<T> &container) QJsonArray toJsonArray(const QList<T>& container)
{ {
QJsonArray array; QJsonArray array;
for (const T item : container) for (const T item : container) {
{
array.append(toJson<T>(item)); array.append(toJson<T>(item));
} }
return array; return array;
@ -112,106 +109,110 @@ QJsonArray toJsonArray(const QList<T> &container)
/// @throw JsonException /// @throw JsonException
template <typename T> template <typename T>
T requireIsType(const QJsonValue &value, const QString &what = "Value"); T requireIsType(const QJsonValue& value, const QString& what = "Value");
/// @throw JsonException /// @throw JsonException
template<> double requireIsType<double>(const QJsonValue &value, const QString &what); template <>
double requireIsType<double>(const QJsonValue& value, const QString& what);
/// @throw JsonException /// @throw JsonException
template<> bool requireIsType<bool>(const QJsonValue &value, const QString &what); template <>
bool requireIsType<bool>(const QJsonValue& value, const QString& what);
/// @throw JsonException /// @throw JsonException
template<> int requireIsType<int>(const QJsonValue &value, const QString &what); template <>
int requireIsType<int>(const QJsonValue& value, const QString& what);
/// @throw JsonException /// @throw JsonException
template<> QJsonObject requireIsType<QJsonObject>(const QJsonValue &value, const QString &what); template <>
QJsonObject requireIsType<QJsonObject>(const QJsonValue& value, const QString& what);
/// @throw JsonException /// @throw JsonException
template<> QJsonArray requireIsType<QJsonArray>(const QJsonValue &value, const QString &what); template <>
QJsonArray requireIsType<QJsonArray>(const QJsonValue& value, const QString& what);
/// @throw JsonException /// @throw JsonException
template<> QJsonValue requireIsType<QJsonValue>(const QJsonValue &value, const QString &what); template <>
QJsonValue requireIsType<QJsonValue>(const QJsonValue& value, const QString& what);
/// @throw JsonException /// @throw JsonException
template<> QByteArray requireIsType<QByteArray>(const QJsonValue &value, const QString &what); template <>
QByteArray requireIsType<QByteArray>(const QJsonValue& value, const QString& what);
/// @throw JsonException /// @throw JsonException
template<> QDateTime requireIsType<QDateTime>(const QJsonValue &value, const QString &what); template <>
QDateTime requireIsType<QDateTime>(const QJsonValue& value, const QString& what);
/// @throw JsonException /// @throw JsonException
template<> QVariant requireIsType<QVariant>(const QJsonValue &value, const QString &what); template <>
QVariant requireIsType<QVariant>(const QJsonValue& value, const QString& what);
/// @throw JsonException /// @throw JsonException
template<> QString requireIsType<QString>(const QJsonValue &value, const QString &what); template <>
QString requireIsType<QString>(const QJsonValue& value, const QString& what);
/// @throw JsonException /// @throw JsonException
template<> QUuid requireIsType<QUuid>(const QJsonValue &value, const QString &what); template <>
QUuid requireIsType<QUuid>(const QJsonValue& value, const QString& what);
/// @throw JsonException /// @throw JsonException
template<> QDir requireIsType<QDir>(const QJsonValue &value, const QString &what); template <>
QDir requireIsType<QDir>(const QJsonValue& value, const QString& what);
/// @throw JsonException /// @throw JsonException
template<> QUrl requireIsType<QUrl>(const QJsonValue &value, const QString &what); template <>
QUrl requireIsType<QUrl>(const QJsonValue& value, const QString& what);
// the following functions are higher level functions, that make use of the above functions for // the following functions are higher level functions, that make use of the above functions for
// type conversion // type conversion
template <typename T> template <typename T>
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_; return default_;
} }
try try {
{
return requireIsType<T>(value, what); return requireIsType<T>(value, what);
} } catch (const JsonException&) {
catch (const JsonException &)
{
return default_; return default_;
} }
} }
/// @throw JsonException /// @throw JsonException
template <typename T> template <typename T>
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 + '\''); 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); throw JsonException(localWhat + "s parent does not contain " + localWhat);
} }
return requireIsType<T>(parent.value(key), localWhat); return requireIsType<T>(parent.value(key), localWhat);
} }
template <typename T> template <typename T>
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 + '\''); const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
if (!parent.contains(key)) if (!parent.contains(key)) {
{
return default_; return default_;
} }
return ensureIsType<T>(parent.value(key), default_, localWhat); return ensureIsType<T>(parent.value(key), default_, localWhat);
} }
template <typename T> template <typename T>
QVector<T> requireIsArrayOf(const QJsonDocument &doc) QVector<T> requireIsArrayOf(const QJsonDocument& doc)
{ {
const QJsonArray array = requireArray(doc); const QJsonArray array = requireArray(doc);
QVector<T> out; QVector<T> out;
for (const QJsonValue val : array) for (const QJsonValue val : array) {
{
out.append(requireIsType<T>(val, "Document")); out.append(requireIsType<T>(val, "Document"));
} }
return out; return out;
} }
template <typename T> template <typename T>
QVector<T> ensureIsArrayOf(const QJsonValue &value, const QString &what = "Value") QVector<T> ensureIsArrayOf(const QJsonValue& value, const QString& what = "Value")
{ {
const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what); const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what);
QVector<T> out; QVector<T> out;
for (const QJsonValue val : array) for (const QJsonValue val : array) {
{
out.append(requireIsType<T>(val, what)); out.append(requireIsType<T>(val, what));
} }
return out; return out;
} }
template <typename T> template <typename T>
QVector<T> ensureIsArrayOf(const QJsonValue &value, const QVector<T> default_, const QString &what = "Value") QVector<T> ensureIsArrayOf(const QJsonValue& value, const QVector<T> default_, const QString& what = "Value")
{ {
if (value.isUndefined()) if (value.isUndefined()) {
{
return default_; return default_;
} }
return ensureIsArrayOf<T>(value, what); return ensureIsArrayOf<T>(value, what);
@ -219,45 +220,46 @@ QVector<T> ensureIsArrayOf(const QJsonValue &value, const QVector<T> default_, c
/// @throw JsonException /// @throw JsonException
template <typename T> template <typename T>
QVector<T> requireIsArrayOf(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") QVector<T> requireIsArrayOf(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__")
{ {
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); 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); throw JsonException(localWhat + "s parent does not contain " + localWhat);
} }
return ensureIsArrayOf<T>(parent.value(key), localWhat); return ensureIsArrayOf<T>(parent.value(key), localWhat);
} }
template <typename T> template <typename T>
QVector<T> ensureIsArrayOf(const QJsonObject &parent, const QString &key, QVector<T> ensureIsArrayOf(const QJsonObject& parent,
const QVector<T> &default_ = QVector<T>(), const QString &what = "__placeholder__") const QString& key,
const QVector<T>& default_ = QVector<T>(),
const QString& what = "__placeholder__")
{ {
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
if (!parent.contains(key)) if (!parent.contains(key)) {
{
return default_; return default_;
} }
return ensureIsArrayOf<T>(parent.value(key), default_, localWhat); return ensureIsArrayOf<T>(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 // 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) \ #define JSON_HELPERFUNCTIONS(NAME, TYPE) \
inline TYPE require##NAME(const QJsonValue &value, const QString &what = "Value") \ inline TYPE require##NAME(const QJsonValue& value, const QString& what = "Value") \
{ \ { \
return requireIsType<TYPE>(value, what); \ return requireIsType<TYPE>(value, what); \
} \ } \
inline TYPE ensure##NAME(const QJsonValue &value, const TYPE default_ = TYPE(), const QString &what = "Value") \ inline TYPE ensure##NAME(const QJsonValue& value, const TYPE default_ = TYPE(), const QString& what = "Value") \
{ \ { \
return ensureIsType<TYPE>(value, default_, what); \ return ensureIsType<TYPE>(value, default_, what); \
} \ } \
inline TYPE require##NAME(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") \ inline TYPE require##NAME(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__") \
{ \ { \
return requireIsType<TYPE>(parent, key, what); \ return requireIsType<TYPE>(parent, key, what); \
} \ } \
inline TYPE ensure##NAME(const QJsonObject &parent, const QString &key, const TYPE default_ = TYPE(), const QString &what = "__placeholder") \ inline TYPE ensure##NAME(const QJsonObject& parent, const QString& key, const TYPE default_ = TYPE(), \
{ \ const QString& what = "__placeholder") \
return ensureIsType<TYPE>(parent, key, default_, what); \ { \
return ensureIsType<TYPE>(parent, key, default_, what); \
} }
JSON_HELPERFUNCTIONS(Array, QJsonArray) JSON_HELPERFUNCTIONS(Array, QJsonArray)
@ -276,5 +278,5 @@ JSON_HELPERFUNCTIONS(Variant, QVariant)
#undef JSON_HELPERFUNCTIONS #undef JSON_HELPERFUNCTIONS
} } // namespace Json
using JSONValidationError = Json::JsonException; using JSONValidationError = Json::JsonException;

View File

@ -1,42 +1,26 @@
#include "KonamiCode.h" #include "KonamiCode.h"
#include <array>
#include <QDebug> #include <QDebug>
#include <array>
namespace { namespace {
const std::array<Qt::Key, 10> konamiCode = const std::array<Qt::Key, 10> 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 } };
{
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)
{
} }
KonamiCode::KonamiCode(QObject* parent) : QObject(parent) {}
void KonamiCode::input(QEvent* event) void KonamiCode::input(QEvent* event)
{ {
if( event->type() == QEvent::KeyPress ) if (event->type() == QEvent::KeyPress) {
{ QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
QKeyEvent *keyEvent = static_cast<QKeyEvent*>( event );
auto key = Qt::Key(keyEvent->key()); auto key = Qt::Key(keyEvent->key());
if(key == konamiCode[m_progress]) if (key == konamiCode[m_progress]) {
{ m_progress++;
m_progress ++; } else {
}
else
{
m_progress = 0; m_progress = 0;
} }
if(m_progress == static_cast<int>(konamiCode.size())) if (m_progress == static_cast<int>(konamiCode.size())) {
{
m_progress = 0; m_progress = 0;
emit triggered(); emit triggered();
} }

View File

@ -2,16 +2,15 @@
#include <QKeyEvent> #include <QKeyEvent>
class KonamiCode : public QObject class KonamiCode : public QObject {
{
Q_OBJECT Q_OBJECT
public: public:
KonamiCode(QObject *parent = 0); KonamiCode(QObject* parent = 0);
void input(QEvent *event); void input(QEvent* event);
signals: signals:
void triggered(); void triggered();
private: private:
int m_progress = 0; int m_progress = 0;
}; };

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -34,44 +34,41 @@
*/ */
#include "LaunchController.h" #include "LaunchController.h"
#include "minecraft/auth/AccountList.h"
#include "Application.h" #include "Application.h"
#include "minecraft/auth/AccountList.h"
#include "ui/MainWindow.h"
#include "ui/InstanceWindow.h" #include "ui/InstanceWindow.h"
#include "ui/MainWindow.h"
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/ProfileSelectDialog.h"
#include "ui/dialogs/ProgressDialog.h"
#include "ui/dialogs/EditAccountDialog.h" #include "ui/dialogs/EditAccountDialog.h"
#include "ui/dialogs/ProfileSelectDialog.h"
#include "ui/dialogs/ProfileSetupDialog.h" #include "ui/dialogs/ProfileSetupDialog.h"
#include "ui/dialogs/ProgressDialog.h"
#include <QLineEdit>
#include <QInputDialog>
#include <QStringList>
#include <QHostInfo>
#include <QList>
#include <QHostAddress> #include <QHostAddress>
#include <QHostInfo>
#include <QInputDialog>
#include <QLineEdit>
#include <QList>
#include <QPushButton> #include <QPushButton>
#include <QStringList>
#include "BuildConfig.h" #include "BuildConfig.h"
#include "JavaCommon.h" #include "JavaCommon.h"
#include "tasks/Task.h"
#include "minecraft/auth/AccountTask.h"
#include "launch/steps/TextPrint.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() void LaunchController::executeTask()
{ {
if (!m_instance) if (!m_instance) {
{
emitFailed(tr("No instance specified!")); emitFailed(tr("No instance specified!"));
return; 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.")); emitFailed(tr("Invalid Java arguments specified. Please fix this first."));
return; return;
} }
@ -81,32 +78,25 @@ void LaunchController::executeTask()
void LaunchController::decideAccount() void LaunchController::decideAccount()
{ {
if(m_accountToUse) { if (m_accountToUse) {
return; return;
} }
// Find an account to use. // Find an account to use.
auto accounts = APPLICATION->accounts(); 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. // Tell the user they need to log in at least one account in order to play.
auto reply = CustomMessageBox::selectable( auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"),
m_parentWidget, tr("In order to play Minecraft, you must have at least one Microsoft or Mojang "
tr("No Accounts"), "account logged in. Mojang accounts can only be used offline. "
tr("In order to play Minecraft, you must have at least one Microsoft or Mojang " "Would you like to open the account manager to add an account now?"),
"account logged in. Mojang accounts can only be used offline. " QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)
"Would you like to open the account manager to add an account now?"), ->exec();
QMessageBox::Information,
QMessageBox::Yes | QMessageBox::No
)->exec();
if (reply == QMessageBox::Yes) if (reply == QMessageBox::Yes) {
{
// Open the account manager. // Open the account manager.
APPLICATION->ShowGlobalSettings(m_parentWidget, "accounts"); APPLICATION->ShowGlobalSettings(m_parentWidget, "accounts");
} } else if (reply == QMessageBox::No) {
else if (reply == QMessageBox::No)
{
// Do not open "profile select" dialog. // Do not open "profile select" dialog.
return; return;
} }
@ -121,14 +111,10 @@ void LaunchController::decideAccount()
m_accountToUse = accounts->at(instanceAccountIndex); m_accountToUse = accounts->at(instanceAccountIndex);
} }
if (!m_accountToUse) if (!m_accountToUse) {
{
// If no default account is set, ask the user which one to use. // If no default account is set, ask the user which one to use.
ProfileSelectDialog selectDialog( ProfileSelectDialog selectDialog(tr("Which account would you like to use?"), ProfileSelectDialog::GlobalDefaultCheckbox,
tr("Which account would you like to use?"), m_parentWidget);
ProfileSelectDialog::GlobalDefaultCheckbox,
m_parentWidget
);
selectDialog.exec(); selectDialog.exec();
@ -142,13 +128,12 @@ void LaunchController::decideAccount()
} }
} }
void LaunchController::login()
void LaunchController::login() { {
decideAccount(); decideAccount();
// if no account is selected, we bail // if no account is selected, we bail
if (!m_accountToUse) if (!m_accountToUse) {
{
emitFailed(tr("No account selected for launch.")); emitFailed(tr("No account selected for launch."));
return; return;
} }
@ -157,15 +142,11 @@ void LaunchController::login() {
bool tryagain = true; bool tryagain = true;
unsigned int tries = 0; unsigned int tries = 0;
while (tryagain) while (tryagain) {
{
if (tries > 0 && tries % 3 == 0) { if (tries > 0 && tries % 3 == 0) {
auto result = QMessageBox::question( auto result =
m_parentWidget, QMessageBox::question(m_parentWidget, tr("Continue launch?"),
tr("Continue launch?"), tr("It looks like we couldn't launch after %1 tries. Do you want to continue trying?").arg(tries));
tr("It looks like we couldn't launch after %1 tries. Do you want to continue trying?")
.arg(tries)
);
if (result == QMessageBox::No) { if (result == QMessageBox::No) {
emitAborted(); emitAborted();
@ -179,60 +160,48 @@ void LaunchController::login() {
m_accountToUse->fillSession(m_session); m_accountToUse->fillSession(m_session);
// Launch immediately in true offline mode // Launch immediately in true offline mode
if(m_accountToUse->isOffline()) { if (m_accountToUse->isOffline()) {
launchInstance(); launchInstance();
return; return;
} }
switch(m_accountToUse->accountState()) { switch (m_accountToUse->accountState()) {
case AccountState::Offline: { case AccountState::Offline: {
m_session->wants_online = false; m_session->wants_online = false;
} }
/* fallthrough */ /* fallthrough */
case AccountState::Online: { case AccountState::Online: {
if(!m_session->wants_online) { if (!m_session->wants_online) {
// we ask the user for a player name // we ask the user for a player name
bool ok = false; bool ok = false;
QString message = tr("Choose your offline mode player name."); 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."); message = tr("Choose your demo mode player name.");
} }
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString(); QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName; QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName;
QString name = QInputDialog::getText( QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok);
m_parentWidget, if (!ok) {
tr("Player name"),
message,
QLineEdit::Normal,
usedname,
&ok
);
if (!ok)
{
tryagain = false; tryagain = false;
break; break;
} }
if (name.length()) if (name.length()) {
{
usedname = name; usedname = name;
APPLICATION->settings()->set("LastOfflinePlayerName", usedname); APPLICATION->settings()->set("LastOfflinePlayerName", usedname);
} }
m_session->MakeOffline(usedname); m_session->MakeOffline(usedname);
// offline flavored game from here :3 // offline flavored game from here :3
} }
if(m_accountToUse->ownsMinecraft()) { if (m_accountToUse->ownsMinecraft()) {
if(!m_accountToUse->hasProfile()) { if (!m_accountToUse->hasProfile()) {
// Now handle setting up a profile name here... // Now handle setting up a profile name here...
ProfileSetupDialog dialog(m_accountToUse, m_parentWidget); ProfileSetupDialog dialog(m_accountToUse, m_parentWidget);
if (dialog.exec() == QDialog::Accepted) if (dialog.exec() == QDialog::Accepted) {
{
tryagain = true; tryagain = true;
continue; continue;
} } else {
else
{
emitFailed(tr("Received undetermined session status during login.")); emitFailed(tr("Received undetermined session status during login."));
return; return;
} }
@ -240,24 +209,24 @@ void LaunchController::login() {
// we own Minecraft, there is a profile, it's all ready to go! // we own Minecraft, there is a profile, it's all ready to go!
launchInstance(); launchInstance();
return; return;
} } else {
else {
// play demo ? // play demo ?
QMessageBox box(m_parentWidget); QMessageBox box(m_parentWidget);
box.setWindowTitle(tr("Play demo?")); 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); box.setIcon(QMessageBox::Warning);
auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole); auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole);
auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole); auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole);
box.setDefaultButton(cancelButton); box.setDefaultButton(cancelButton);
box.exec(); box.exec();
if(box.clickedButton() == demoButton) { if (box.clickedButton() == demoButton) {
// play demo here // play demo here
m_session->MakeDemo(); m_session->MakeDemo();
launchInstance(); launchInstance();
} } else {
else {
emitFailed(tr("Launch cancelled - account does not own Minecraft.")); emitFailed(tr("Launch cancelled - account does not own Minecraft."));
} }
} }
@ -272,8 +241,7 @@ void LaunchController::login() {
case AccountState::Working: { case AccountState::Working: {
// refresh is in progress, we need to wait for it to finish to proceed. // refresh is in progress, we need to wait for it to finish to proceed.
ProgressDialog progDialog(m_parentWidget); ProgressDialog progDialog(m_parentWidget);
if (m_online) if (m_online) {
{
progDialog.setSkipButton(true, tr("Play Offline")); progDialog.setSkipButton(true, tr("Play Offline"));
} }
auto task = m_accountToUse->currentTask(); auto task = m_accountToUse->currentTask();
@ -288,37 +256,24 @@ void LaunchController::login() {
*/ */
case AccountState::Expired: { case AccountState::Expired: {
auto errorString = tr("The account has expired and needs to be logged into manually again."); auto errorString = tr("The account has expired and needs to be logged into manually again.");
QMessageBox::warning( QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), errorString, QMessageBox::StandardButton::Ok,
m_parentWidget, QMessageBox::StandardButton::Ok);
tr("Account refresh failed"),
errorString,
QMessageBox::StandardButton::Ok,
QMessageBox::StandardButton::Ok
);
emitFailed(errorString); emitFailed(errorString);
return; return;
} }
case AccountState::Disabled: { case AccountState::Disabled: {
auto errorString = tr("The launcher's client identification has changed. Please remove this account and add it again."); auto errorString = tr("The launcher's client identification has changed. Please remove this account and add it again.");
QMessageBox::warning( QMessageBox::warning(m_parentWidget, tr("Client identification changed"), errorString, QMessageBox::StandardButton::Ok,
m_parentWidget, QMessageBox::StandardButton::Ok);
tr("Client identification changed"),
errorString,
QMessageBox::StandardButton::Ok,
QMessageBox::StandardButton::Ok
);
emitFailed(errorString); emitFailed(errorString);
return; return;
} }
case AccountState::Gone: { 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."); auto errorString =
QMessageBox::warning( tr("The account no longer exists on the servers. It may have been migrated, in which case please add the new account "
m_parentWidget, "you migrated this one to.");
tr("Account gone"), QMessageBox::warning(m_parentWidget, tr("Account gone"), errorString, QMessageBox::StandardButton::Ok,
errorString, QMessageBox::StandardButton::Ok);
QMessageBox::StandardButton::Ok,
QMessageBox::StandardButton::Ok
);
emitFailed(errorString); emitFailed(errorString);
return; return;
} }
@ -332,48 +287,45 @@ void LaunchController::launchInstance()
Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL"); Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL");
Q_ASSERT_X(m_session.get() != nullptr, "launchInstance", "session 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.")); QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't load the instance profile."));
emitFailed(tr("Couldn't load the instance profile.")); emitFailed(tr("Couldn't load the instance profile."));
return; return;
} }
m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin); m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin);
if (!m_launcher) if (!m_launcher) {
{
emitFailed(tr("Couldn't instantiate a launcher.")); emitFailed(tr("Couldn't instantiate a launcher."));
return; return;
} }
auto console = qobject_cast<InstanceWindow *>(m_parentWidget); auto console = qobject_cast<InstanceWindow*>(m_parentWidget);
auto showConsole = m_instance->settings()->get("ShowConsole").toBool(); auto showConsole = m_instance->settings()->get("ShowConsole").toBool();
if(!console && showConsole) if (!console && showConsole) {
{
APPLICATION->showInstanceWindow(m_instance); APPLICATION->showInstanceWindow(m_instance);
} }
connect(m_launcher.get(), &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch); connect(m_launcher.get(), &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch);
connect(m_launcher.get(), &LaunchTask::succeeded, this, &LaunchController::onSucceeded); 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); connect(m_launcher.get(), &LaunchTask::requestProgress, this, &LaunchController::onProgressRequested);
// Prepend Online and Auth Status // Prepend Online and Auth Status
QString online_mode; QString online_mode;
if(m_session->wants_online) { if (m_session->wants_online) {
online_mode = "online"; online_mode = "online";
// Prepend Server Status // 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 = ""; QString resolved_servers = "";
QHostInfo host_info; QHostInfo host_info;
for(QString server : servers) { for (QString server : servers) {
host_info = QHostInfo::fromName(server); host_info = QHostInfo::fromName(server);
resolved_servers = resolved_servers + server + " resolves to:\n ["; resolved_servers = resolved_servers + server + " resolves to:\n [";
if(!host_info.addresses().isEmpty()) { if (!host_info.addresses().isEmpty()) {
for(QHostAddress address : host_info.addresses()) { for (QHostAddress address : host_info.addresses()) {
resolved_servers = resolved_servers + address.toString(); resolved_servers = resolved_servers + address.toString();
if(!host_info.addresses().endsWith(address)) { if (!host_info.addresses().endsWith(address)) {
resolved_servers = resolved_servers + ", "; resolved_servers = resolved_servers + ", ";
} }
} }
@ -387,11 +339,13 @@ void LaunchController::launchInstance()
online_mode = m_demo ? "demo" : "offline"; online_mode = m_demo ? "demo" : "offline";
} }
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher)); m_launcher->prependStep(
makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
// Prepend Version // Prepend Version
{ {
auto versionString = QString("%1 version: %2 (%3)").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString(), BuildConfig.BUILD_PLATFORM); auto versionString = QString("%1 version: %2 (%3)")
.arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString(), BuildConfig.BUILD_PLATFORM);
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), versionString + "\n\n", MessageLevel::Launcher)); m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), versionString + "\n\n", MessageLevel::Launcher));
} }
m_launcher->start(); m_launcher->start();
@ -399,28 +353,26 @@ void LaunchController::launchInstance()
void LaunchController::readyForLaunch() void LaunchController::readyForLaunch()
{ {
if (!m_profiler) if (!m_profiler) {
{
m_launcher->proceed(); m_launcher->proceed();
return; return;
} }
QString error; QString error;
if (!m_profiler->check(&error)) if (!m_profiler->check(&error)) {
{
m_launcher->abort(); m_launcher->abort();
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't start profiler: %1").arg(error)); QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't start profiler: %1").arg(error));
emitFailed("Profiler startup failed!"); emitFailed("Profiler startup failed!");
return; 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; QMessageBox msg;
msg.setText(tr("The game launch is delayed until you press the " msg.setText(tr("The game launch is delayed until you press the "
"button. This is the right time to setup the profiler, as the " "button. This is the right time to setup the profiler, as the "
"profiler server is running now.\n\n%1").arg(message)); "profiler server is running now.\n\n%1")
.arg(message));
msg.setWindowTitle(tr("Waiting.")); msg.setWindowTitle(tr("Waiting."));
msg.setIcon(QMessageBox::Information); msg.setIcon(QMessageBox::Information);
msg.addButton(tr("Launch"), QMessageBox::AcceptRole); msg.addButton(tr("Launch"), QMessageBox::AcceptRole);
@ -428,8 +380,7 @@ void LaunchController::readyForLaunch()
msg.exec(); msg.exec();
m_launcher->proceed(); m_launcher->proceed();
}); });
connect(profilerInstance, &BaseProfiler::abortLaunch, [this](const QString & message) connect(profilerInstance, &BaseProfiler::abortLaunch, [this](const QString& message) {
{
QMessageBox msg; QMessageBox msg;
msg.setText(tr("Couldn't start the profiler: %1").arg(message)); msg.setText(tr("Couldn't start the profiler: %1").arg(message));
msg.setWindowTitle(tr("Error")); msg.setWindowTitle(tr("Error"));
@ -450,8 +401,7 @@ void LaunchController::onSucceeded()
void LaunchController::onFailed(QString reason) void LaunchController::onFailed(QString reason)
{ {
if(m_instance->settings()->get("ShowConsoleOnError").toBool()) if (m_instance->settings()->get("ShowConsoleOnError").toBool()) {
{
APPLICATION->showInstanceWindow(m_instance, "console"); APPLICATION->showInstanceWindow(m_instance, "console");
} }
emitFailed(reason); emitFailed(reason);
@ -467,21 +417,18 @@ void LaunchController::onProgressRequested(Task* task)
bool LaunchController::abort() bool LaunchController::abort()
{ {
if(!m_launcher) if (!m_launcher) {
{
return true; return true;
} }
if(!m_launcher->canAbort()) if (!m_launcher->canAbort()) {
{
return false; return false;
} }
auto response = CustomMessageBox::selectable( auto response = CustomMessageBox::selectable(m_parentWidget, tr("Kill Minecraft?"),
m_parentWidget, tr("Kill Minecraft?"), tr("This can cause the instance to get corrupted and should only be used if Minecraft "
tr("This can cause the instance to get corrupted and should only be used if Minecraft " "is frozen for some reason"),
"is frozen for some reason"), QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)
QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec(); ->exec();
if (response == QMessageBox::Yes) if (response == QMessageBox::Yes) {
{
return m_launcher->abort(); return m_launcher->abort();
} }
return false; return false;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -34,81 +34,61 @@
*/ */
#pragma once #pragma once
#include <QObject>
#include <BaseInstance.h> #include <BaseInstance.h>
#include <tools/BaseProfiler.h> #include <tools/BaseProfiler.h>
#include <QObject>
#include "minecraft/launch/MinecraftServerTarget.h"
#include "minecraft/auth/MinecraftAccount.h" #include "minecraft/auth/MinecraftAccount.h"
#include "minecraft/launch/MinecraftServerTarget.h"
class InstanceWindow; class InstanceWindow;
class LaunchController: public Task class LaunchController : public Task {
{
Q_OBJECT Q_OBJECT
public: public:
void executeTask() override; void executeTask() override;
LaunchController(QObject * parent = nullptr); LaunchController(QObject* parent = nullptr);
virtual ~LaunchController(){}; virtual ~LaunchController(){};
void setInstance(InstancePtr instance) { void setInstance(InstancePtr instance) { m_instance = instance; }
m_instance = instance;
}
InstancePtr instance() { InstancePtr instance() { return m_instance; }
return m_instance;
}
void setOnline(bool online) { void setOnline(bool online) { m_online = online; }
m_online = online;
}
void setDemo(bool demo) { void setDemo(bool demo) { m_demo = demo; }
m_demo = demo;
}
void setProfiler(BaseProfilerFactory *profiler) { void setProfiler(BaseProfilerFactory* profiler) { m_profiler = profiler; }
m_profiler = profiler;
}
void setParentWidget(QWidget * widget) { void setParentWidget(QWidget* widget) { m_parentWidget = widget; }
m_parentWidget = widget;
}
void setServerToJoin(MinecraftServerTargetPtr serverToJoin) { void setServerToJoin(MinecraftServerTargetPtr serverToJoin) { m_serverToJoin = std::move(serverToJoin); }
m_serverToJoin = std::move(serverToJoin);
}
void setAccountToUse(MinecraftAccountPtr accountToUse) { void setAccountToUse(MinecraftAccountPtr accountToUse) { m_accountToUse = std::move(accountToUse); }
m_accountToUse = std::move(accountToUse);
}
QString id() QString id() { return m_instance->id(); }
{
return m_instance->id();
}
bool abort() override; bool abort() override;
private: private:
void login(); void login();
void launchInstance(); void launchInstance();
void decideAccount(); void decideAccount();
private slots: private slots:
void readyForLaunch(); void readyForLaunch();
void onSucceeded(); void onSucceeded();
void onFailed(QString reason); void onFailed(QString reason);
void onProgressRequested(Task *task); void onProgressRequested(Task* task);
private: private:
BaseProfilerFactory *m_profiler = nullptr; BaseProfilerFactory* m_profiler = nullptr;
bool m_online = true; bool m_online = true;
bool m_demo = false; bool m_demo = false;
InstancePtr m_instance; InstancePtr m_instance;
QWidget * m_parentWidget = nullptr; QWidget* m_parentWidget = nullptr;
InstanceWindow *m_console = nullptr; InstanceWindow* m_console = nullptr;
MinecraftAccountPtr m_accountToUse = nullptr; MinecraftAccountPtr m_accountToUse = nullptr;
AuthSessionPtr m_session; AuthSessionPtr m_session;
shared_qobject_ptr<LaunchTask> m_launcher; shared_qobject_ptr<LaunchTask> m_launcher;

View File

@ -2,7 +2,7 @@
/* /*
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022,2023 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022,2023 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2023 flowln <flowlnlnln@gmail.com> * Copyright (c) 2023 flowln <flowlnlnln@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -39,7 +39,7 @@
#include <QTextDecoder> #include <QTextDecoder>
#include "MessageLevel.h" #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. // QProcess has a strange interface... let's map a lot of those into a few.
connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut); connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut);
@ -51,8 +51,7 @@ LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent)
LoggedProcess::~LoggedProcess() LoggedProcess::~LoggedProcess()
{ {
if(m_is_detachable) if (m_is_detachable) {
{
setProcessState(QProcess::NotRunning); setProcessState(QProcess::NotRunning);
} }
} }
@ -66,14 +65,9 @@ QStringList LoggedProcess::reprocess(const QByteArray& data, QTextDecoder& decod
m_leftover_line = ""; m_leftover_line = "";
} }
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed);
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, QString::SkipEmptyParts);
#else
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, Qt::SkipEmptyParts);
#endif
if (!str.endsWith(QChar::LineFeed)) m_leftover_line = lines.takeLast();
m_leftover_line = lines.takeLast();
return lines; return lines;
} }
@ -95,39 +89,31 @@ void LoggedProcess::on_exit(int exit_code, QProcess::ExitStatus status)
m_exit_code = exit_code; m_exit_code = exit_code;
// based on state, send signals // based on state, send signals
if (!m_is_aborting) if (!m_is_aborting) {
{ if (status == QProcess::NormalExit) {
if (status == QProcess::NormalExit)
{
//: Message displayed on instance exit //: 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); changeState(LoggedProcess::Finished);
} } else {
else
{
//: Message displayed on instance crashed //: Message displayed on instance crashed
if(exit_code == -1) if (exit_code == -1)
emit log({tr("Process crashed.")}, MessageLevel::Launcher); emit log({ tr("Process crashed.") }, MessageLevel::Launcher);
else 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); changeState(LoggedProcess::Crashed);
} }
} } else {
else
{
//: Message displayed after the instance exits due to kill request //: 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); changeState(LoggedProcess::Aborted);
} }
} }
void LoggedProcess::on_error(QProcess::ProcessError error) void LoggedProcess::on_error(QProcess::ProcessError error)
{ {
switch(error) switch (error) {
{ case QProcess::FailedToStart: {
case QProcess::FailedToStart: emit log({ tr("The process failed to start.") }, MessageLevel::Fatal);
{
emit log({tr("The process failed to start.")}, MessageLevel::Fatal);
changeState(LoggedProcess::FailedToStart); changeState(LoggedProcess::FailedToStart);
break; break;
} }
@ -154,7 +140,7 @@ int LoggedProcess::exitCode() const
void LoggedProcess::changeState(LoggedProcess::State state) void LoggedProcess::changeState(LoggedProcess::State state)
{ {
if(state == m_state) if (state == m_state)
return; return;
m_state = state; m_state = state;
emit stateChanged(m_state); emit stateChanged(m_state);
@ -167,24 +153,19 @@ LoggedProcess::State LoggedProcess::state() const
void LoggedProcess::on_stateChange(QProcess::ProcessState state) void LoggedProcess::on_stateChange(QProcess::ProcessState state)
{ {
switch(state) switch (state) {
{
case QProcess::NotRunning: case QProcess::NotRunning:
break; // let's not - there are too many that handle this already. break; // let's not - there are too many that handle this already.
case QProcess::Starting: case QProcess::Starting: {
{ if (m_state != LoggedProcess::NotRunning) {
if(m_state != LoggedProcess::NotRunning) qWarning() << "Wrong state change for process from state" << m_state << "to" << (int)LoggedProcess::Starting;
{
qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Starting;
} }
changeState(LoggedProcess::Starting); changeState(LoggedProcess::Starting);
return; return;
} }
case QProcess::Running: case QProcess::Running: {
{ if (m_state != LoggedProcess::Starting) {
if(m_state != LoggedProcess::Starting) qWarning() << "Wrong state change for process from state" << m_state << "to" << (int)LoggedProcess::Running;
{
qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Running;
} }
changeState(LoggedProcess::Running); changeState(LoggedProcess::Running);
return; return;

View File

@ -43,22 +43,12 @@
* This is a basic process. * This is a basic process.
* It has line-based logging support and hides some of the nasty bits. * It has line-based logging support and hides some of the nasty bits.
*/ */
class LoggedProcess : public QProcess class LoggedProcess : public QProcess {
{ Q_OBJECT
Q_OBJECT public:
public: enum State { NotRunning, Starting, FailedToStart, Running, Finished, Crashed, Aborted };
enum State
{
NotRunning,
Starting,
FailedToStart,
Running,
Finished,
Crashed,
Aborted
};
public: public:
explicit LoggedProcess(QObject* parent = 0); explicit LoggedProcess(QObject* parent = 0);
virtual ~LoggedProcess(); virtual ~LoggedProcess();
@ -67,30 +57,29 @@ public:
void setDetachable(bool detachable); void setDetachable(bool detachable);
signals: signals:
void log(QStringList lines, MessageLevel::Enum level); void log(QStringList lines, MessageLevel::Enum level);
void stateChanged(LoggedProcess::State state); void stateChanged(LoggedProcess::State state);
public slots: public slots:
/** /**
* @brief kill the process - equivalent to kill -9 * @brief kill the process - equivalent to kill -9
*/ */
void kill(); void kill();
private slots:
private slots:
void on_stdErr(); void on_stdErr();
void on_stdOut(); void on_stdOut();
void on_exit(int exit_code, QProcess::ExitStatus status); void on_exit(int exit_code, QProcess::ExitStatus status);
void on_error(QProcess::ProcessError error); void on_error(QProcess::ProcessError error);
void on_stateChange(QProcess::ProcessState); void on_stateChange(QProcess::ProcessState);
private: private:
void changeState(LoggedProcess::State state); void changeState(LoggedProcess::State state);
QStringList reprocess(const QByteArray& data, QTextDecoder& decoder); QStringList reprocess(const QByteArray& data, QTextDecoder& decoder);
private: private:
QTextDecoder m_err_decoder = QTextDecoder(QTextCodec::codecForLocale()); QTextDecoder m_err_decoder = QTextDecoder(QTextCodec::codecForLocale());
QTextDecoder m_out_decoder = QTextDecoder(QTextCodec::codecForLocale()); QTextDecoder m_out_decoder = QTextDecoder(QTextCodec::codecForLocale());
QString m_leftover_line; QString m_leftover_line;

View File

@ -17,30 +17,29 @@
#include <MMCTime.h> #include <MMCTime.h>
#include <QObject>
#include <QDateTime> #include <QDateTime>
#include <QObject>
#include <QTextStream> #include <QTextStream>
QString Time::prettifyDuration(int64_t duration) { QString Time::prettifyDuration(int64_t duration)
int seconds = (int) (duration % 60); {
int seconds = (int)(duration % 60);
duration /= 60; duration /= 60;
int minutes = (int) (duration % 60); int minutes = (int)(duration % 60);
duration /= 60; duration /= 60;
int hours = (int) (duration % 24); int hours = (int)(duration % 24);
int days = (int) (duration / 24); int days = (int)(duration / 24);
if((hours == 0)&&(days == 0)) if ((hours == 0) && (days == 0)) {
{
return QObject::tr("%1min %2s").arg(minutes).arg(seconds); 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("%1h %2min").arg(hours).arg(minutes);
} }
return QObject::tr("%1d %2h %3min").arg(days).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<int, std::ratio<86400>>; using days = std::chrono::duration<int, std::ratio<86400>>;
QString outStr; QString outStr;
@ -48,10 +47,10 @@ QString Time::humanReadableDuration(double duration, int precision) {
bool neg = false; bool neg = false;
if (duration < 0) { if (duration < 0) {
neg = true; // flag neg = true; // flag
duration *= -1; // invert duration *= -1; // invert
} }
auto std_duration = std::chrono::duration<double>(duration); auto std_duration = std::chrono::duration<double>(duration);
auto d = std::chrono::duration_cast<days>(std_duration); auto d = std::chrono::duration_cast<days>(std_duration);
std_duration -= d; std_duration -= d;
@ -78,22 +77,22 @@ QString Time::humanReadableDuration(double duration, int precision) {
if (hc) { if (hc) {
if (dc) if (dc)
os << " "; os << " ";
os << qSetFieldWidth(2) << hc << QObject::tr("h"); // hours os << qSetFieldWidth(2) << hc << QObject::tr("h"); // hours
} }
if (mc) { if (mc) {
if (dc || hc) if (dc || hc)
os << " "; 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 || sc) {
if (dc || hc || mc) if (dc || hc || mc)
os << " "; 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 ((msc && (precision > 0)) || !(dc || hc || mc || sc)) {
if (dc || hc || mc || sc) if (dc || hc || mc || sc)
os << " "; os << " ";
os << qSetFieldWidth(0) << qSetRealNumberPrecision(precision) << msc << QObject::tr("ms"); // miliseconds os << qSetFieldWidth(0) << qSetRealNumberPrecision(precision) << msc << QObject::tr("ms"); // miliseconds
} }
os.flush(); os.flush();

View File

@ -25,10 +25,10 @@ QString prettifyDuration(int64_t duration);
/** /**
* @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms`. * @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms`.
* miliseconds are only included if `precision` is greater than 0. * miliseconds are only included if `precision` is greater than 0.
* *
* @param duration a number of seconds as floating point * @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. * @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); QString humanReadableDuration(double duration, int precision = 0);
} } // namespace Time

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -33,56 +34,50 @@
* limitations under the License. * limitations under the License.
*/ */
#include "MMCZip.h"
#include <quazip/quazip.h> #include <quazip/quazip.h>
#include <quazip/quazipdir.h> #include <quazip/quazipdir.h>
#include <quazip/quazipfile.h> #include <quazip/quazipfile.h>
#include "MMCZip.h"
#include "FileSystem.h" #include "FileSystem.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
#include <QtConcurrentRun>
namespace MMCZip {
// ours // ours
bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, const FilterFunction filter) bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet<QString>& contained, const FilterFunction filter)
{ {
QuaZip modZip(from.filePath()); QuaZip modZip(from.filePath());
modZip.open(QuaZip::mdUnzip); modZip.open(QuaZip::mdUnzip);
QuaZipFile fileInsideMod(&modZip); QuaZipFile fileInsideMod(&modZip);
QuaZipFile zipOutFile(into); 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(); QString filename = modZip.getCurrentFileName();
if (filter && !filter(filename)) if (filter && !filter(filename)) {
{ qDebug() << "Skipping file " << filename << " from " << from.fileName() << " - filtered";
qDebug() << "Skipping file " << filename << " from "
<< from.fileName() << " - filtered";
continue; continue;
} }
if (contained.contains(filename)) if (contained.contains(filename)) {
{ qDebug() << "Skipping already contained file " << filename << " from " << from.fileName();
qDebug() << "Skipping already contained file " << filename << " from "
<< from.fileName();
continue; continue;
} }
contained.insert(filename); contained.insert(filename);
if (!fileInsideMod.open(QIODevice::ReadOnly)) if (!fileInsideMod.open(QIODevice::ReadOnly)) {
{
qCritical() << "Failed to open " << filename << " from " << from.fileName(); qCritical() << "Failed to open " << filename << " from " << from.fileName();
return false; return false;
} }
QuaZipNewInfo info_out(fileInsideMod.getActualFileName()); 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"; qCritical() << "Failed to open " << filename << " in the jar";
fileInsideMod.close(); fileInsideMod.close();
return false; return false;
} }
if (!JlCompress::copyData(fileInsideMod, zipOutFile)) if (!JlCompress::copyData(fileInsideMod, zipOutFile)) {
{
zipOutFile.close(); zipOutFile.close();
fileInsideMod.close(); fileInsideMod.close();
qCritical() << "Failed to copy data of " << filename << " into the jar"; qCritical() << "Failed to copy data of " << filename << " into the jar";
@ -94,10 +89,11 @@ bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &containe
return true; 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); QDir directory(dir);
if (!directory.exists()) return false; if (!directory.exists())
return false;
for (auto e : files) { for (auto e : files) {
auto filePath = directory.relativeFilePath(e.absoluteFilePath()); auto filePath = directory.relativeFilePath(e.absoluteFilePath());
@ -109,17 +105,18 @@ bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, boo
srcPath = e.canonicalFilePath(); srcPath = e.canonicalFilePath();
} }
} }
if( !JlCompress::compressFile(zip, srcPath, filePath)) return false; if (!JlCompress::compressFile(zip, srcPath, filePath))
return false;
} }
return true; 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); QuaZip zip(fileCompressed);
QDir().mkpath(QFileInfo(fileCompressed).absolutePath()); QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
if(!zip.open(QuaZip::mdCreate)) { if (!zip.open(QuaZip::mdCreate)) {
QFile::remove(fileCompressed); QFile::remove(fileCompressed);
return false; return false;
} }
@ -127,7 +124,7 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList
auto result = compressDirFiles(&zip, dir, files, followSymlinks); auto result = compressDirFiles(&zip, dir, files, followSymlinks);
zip.close(); zip.close();
if(zip.getZipError()!=0) { if (zip.getZipError() != 0) {
QFile::remove(fileCompressed); QFile::remove(fileCompressed);
return false; return false;
} }
@ -136,11 +133,10 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList
} }
// ours // ours
bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods) bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods)
{ {
QuaZip zipOut(targetJarPath); QuaZip zipOut(targetJarPath);
if (!zipOut.open(QuaZip::mdCreate)) if (!zipOut.open(QuaZip::mdCreate)) {
{
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to open the minecraft.jar for modding"; qCritical() << "Failed to open the minecraft.jar for modding";
return false; return false;
@ -151,37 +147,29 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
// Modify the jar // Modify the jar
// This needs to be done in reverse-order to ensure we respect the loading order of components // 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; const auto* mod = *i;
// do not merge disabled mods. // do not merge disabled mods.
if (!mod->enabled()) if (!mod->enabled())
continue; continue;
if (mod->type() == ResourceType::ZIPFILE) if (mod->type() == ResourceType::ZIPFILE) {
{ if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) {
if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles))
{
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
return false; return false;
} }
} } else if (mod->type() == ResourceType::SINGLEFILE) {
else if (mod->type() == ResourceType::SINGLEFILE)
{
// FIXME: buggy - does not work with addedFiles // FIXME: buggy - does not work with addedFiles
auto filename = mod->fileinfo(); auto filename = mod->fileinfo();
if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) {
{
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
return false; return false;
} }
addedFiles.insert(filename.fileName()); 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 // untested, but seems to be unused / not possible to reach
// FIXME: buggy - does not work with addedFiles // FIXME: buggy - does not work with addedFiles
auto filename = mod->fileinfo(); auto filename = mod->fileinfo();
@ -190,25 +178,21 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
dir.cdUp(); dir.cdUp();
QString parent_dir = dir.absolutePath(); QString parent_dir = dir.absolutePath();
auto files = QFileInfoList(); auto files = QFileInfoList();
MMCZip::collectFileListRecursively(what_to_zip, nullptr, &files, nullptr); collectFileListRecursively(what_to_zip, nullptr, &files, nullptr);
for (auto e : files) { for (auto e : files) {
if (addedFiles.contains(e.filePath())) if (addedFiles.contains(e.filePath()))
files.removeAll(e); files.removeAll(e);
} }
if (!MMCZip::compressDirFiles(&zipOut, parent_dir, files)) if (!compressDirFiles(&zipOut, parent_dir, files)) {
{
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
return false; return false;
} }
qDebug() << "Adding folder " << filename.fileName() << " from " qDebug() << "Adding folder " << filename.fileName() << " from " << filename.absoluteFilePath();
<< filename.absoluteFilePath(); } else {
}
else
{
// Make sure we do not continue launching when something is missing or undefined... // Make sure we do not continue launching when something is missing or undefined...
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); 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(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to insert minecraft.jar contents."; qCritical() << "Failed to insert minecraft.jar contents.";
@ -227,8 +210,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
// Recompress the jar // Recompress the jar
zipOut.close(); zipOut.close();
if (zipOut.getZipError() != 0) if (zipOut.getZipError() != 0) {
{
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to finalize minecraft.jar!"; qCritical() << "Failed to finalize minecraft.jar!";
return false; return false;
@ -237,7 +219,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
} }
// ours // 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); QuaZipDir rootDir(zip, root);
for (auto&& fileName : rootDir.entryList(QDir::Files)) { for (auto&& fileName : rootDir.entryList(QDir::Files)) {
@ -261,27 +243,23 @@ QString MMCZip::findFolderOfFileInZip(QuaZip* zip, const QString& what, const QS
} }
// ours // 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); QuaZipDir rootDir(zip, root);
for(auto fileName: rootDir.entryList(QDir::Files)) for (auto fileName : rootDir.entryList(QDir::Files)) {
{ if (fileName == what) {
if(fileName == what)
{
result.append(root); result.append(root);
return true; return true;
} }
} }
for(auto fileName: rootDir.entryList(QDir::Dirs)) for (auto fileName : rootDir.entryList(QDir::Dirs)) {
{
findFilesInZip(zip, what, result, root + fileName); findFilesInZip(zip, what, result, root + fileName);
} }
return !result.isEmpty(); return !result.isEmpty();
} }
// ours // ours
std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target) std::optional<QStringList> extractSubDir(QuaZip* zip, const QString& subdir, const QString& target)
{ {
auto target_top_dir = QUrl::fromLocalFile(target); auto target_top_dir = QUrl::fromLocalFile(target);
@ -289,16 +267,13 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su
qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target; qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target;
auto numEntries = zip->getEntriesCount(); auto numEntries = zip->getEntriesCount();
if(numEntries < 0) { if (numEntries < 0) {
qWarning() << "Failed to enumerate files in archive"; qWarning() << "Failed to enumerate files in archive";
return std::nullopt; return std::nullopt;
} } else if (numEntries == 0) {
else if(numEntries == 0) {
qDebug() << "Extracting empty archives seems odd..."; qDebug() << "Extracting empty archives seems odd...";
return extracted; return extracted;
} } else if (!zip->goToFirstFile()) {
else if (!zip->goToFirstFile())
{
qWarning() << "Failed to seek to first file in zip"; qWarning() << "Failed to seek to first file in zip";
return std::nullopt; return std::nullopt;
} }
@ -334,7 +309,8 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su
} }
if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) { 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; return std::nullopt;
} }
@ -345,7 +321,8 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su
} }
extracted.append(target_file_path); 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; qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path;
} while (zip->goToNextFile()); } while (zip->goToNextFile());
@ -354,66 +331,66 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su
} }
// ours // 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); return JlCompress::extractFile(zip, file, target);
} }
// ours // ours
std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString dir) std::optional<QStringList> extractDir(QString fileCompressed, QString dir)
{ {
QuaZip zip(fileCompressed); QuaZip zip(fileCompressed);
if (!zip.open(QuaZip::mdUnzip)) if (!zip.open(QuaZip::mdUnzip)) {
{
// check if this is a minimum size empty zip file... // check if this is a minimum size empty zip file...
QFileInfo fileInfo(fileCompressed); QFileInfo fileInfo(fileCompressed);
if(fileInfo.size() == 22) { if (fileInfo.size() == 22) {
return QStringList(); 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 std::nullopt;
} }
return MMCZip::extractSubDir(&zip, "", dir); return extractSubDir(&zip, "", dir);
} }
// ours // ours
std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString subdir, QString dir) std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir)
{ {
QuaZip zip(fileCompressed); QuaZip zip(fileCompressed);
if (!zip.open(QuaZip::mdUnzip)) if (!zip.open(QuaZip::mdUnzip)) {
{
// check if this is a minimum size empty zip file... // check if this is a minimum size empty zip file...
QFileInfo fileInfo(fileCompressed); QFileInfo fileInfo(fileCompressed);
if(fileInfo.size() == 22) { if (fileInfo.size() == 22) {
return QStringList(); 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 std::nullopt;
} }
return MMCZip::extractSubDir(&zip, subdir, dir); return extractSubDir(&zip, subdir, dir);
} }
// ours // ours
bool MMCZip::extractFile(QString fileCompressed, QString file, QString target) bool extractFile(QString fileCompressed, QString file, QString target)
{ {
QuaZip zip(fileCompressed); QuaZip zip(fileCompressed);
if (!zip.open(QuaZip::mdUnzip)) if (!zip.open(QuaZip::mdUnzip)) {
{
// check if this is a minimum size empty zip file... // check if this is a minimum size empty zip file...
QFileInfo fileInfo(fileCompressed); QFileInfo fileInfo(fileCompressed);
if(fileInfo.size() == 22) { if (fileInfo.size() == 22) {
return true; return true;
} }
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError(); qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();
return false; return false;
} }
return MMCZip::extractRelFile(&zip, file, target); return extractRelFile(&zip, file, target);
} }
bool MMCZip::collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList *files, bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter)
MMCZip::FilterFunction excludeFilter) { {
QDir rootDirectory(rootDir); QDir rootDirectory(rootDir);
if (!rootDirectory.exists()) return false; if (!rootDirectory.exists())
return false;
QDir directory; QDir directory;
if (subDir == nullptr) if (subDir == nullptr)
@ -421,25 +398,107 @@ bool MMCZip::collectFileListRecursively(const QString& rootDir, const QString& s
else else
directory = QDir(subDir); directory = QDir(subDir);
if (!directory.exists()) return false; // shouldn't ever happen if (!directory.exists())
return false; // shouldn't ever happen
// recurse directories // recurse directories
QFileInfoList entries = directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden); 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)) if (!collectFileListRecursively(rootDir, e.filePath(), files, excludeFilter))
return false; return false;
} }
// collect files // collect files
entries = directory.entryInfoList(QDir::Files); entries = directory.entryInfoList(QDir::Files);
for (const auto& e: entries) { for (const auto& e : entries) {
QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath()); QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath());
if (excludeFilter && excludeFilter(relativeFilePath)) { if (excludeFilter && excludeFilter(relativeFilePath)) {
qDebug() << "Skipping file " << relativeFilePath; qDebug() << "Skipping file " << relativeFilePath;
continue; continue;
} }
files->append(e); // we want the original paths for MMCZip::compressDirFiles files->append(e); // we want the original paths for compressDirFiles
} }
return true; 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<ZipResult>::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

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -35,110 +36,157 @@
#pragma once #pragma once
#include <QString> #include <quazip.h>
#include <QFileInfo>
#include <QSet>
#include "minecraft/mod/Mod.h"
#include <functional>
#include <quazip/JlCompress.h> #include <quazip/JlCompress.h>
#include <QDir>
#include <QFileInfo>
#include <QFuture>
#include <QFutureWatcher>
#include <QHash>
#include <QSet>
#include <QString>
#include <functional>
#include <memory>
#include <optional> #include <optional>
#include "minecraft/mod/Mod.h"
#include "tasks/Task.h"
namespace MMCZip namespace MMCZip {
{ using FilterFunction = std::function<bool(const QString&)>;
using FilterFunction = std::function<bool(const QString &)>;
/** /**
* Merge two zip files, using a filter function * Merge two zip files, using a filter function
*/ */
bool mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet<QString>& contained, const FilterFunction filter = nullptr);
const FilterFunction filter = nullptr);
/** /**
* Compress directory, by providing a list of files to compress * Compress directory, by providing a list of files to compress
* \param zip target archive * \param zip target archive
* \param dir directory that will be compressed (to compress with relative paths) * \param dir directory that will be compressed (to compress with relative paths)
* \param files list of files to compress * \param files list of files to compress
* \param followSymlinks should follow symlinks when compressing file data * \param followSymlinks should follow symlinks when compressing file data
* \return true for success or false for failure * \return true for success or false for failure
*/ */
bool compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, bool followSymlinks = false); bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool followSymlinks = false);
/** /**
* Compress directory, by providing a list of files to compress * Compress directory, by providing a list of files to compress
* \param fileCompressed target archive file * \param fileCompressed target archive file
* \param dir directory that will be compressed (to compress with relative paths) * \param dir directory that will be compressed (to compress with relative paths)
* \param files list of files to compress * \param files list of files to compress
* \param followSymlinks should follow symlinks when compressing file data * \param followSymlinks should follow symlinks when compressing file data
* \return true for success or false for failure * \return true for success or false for failure
*/ */
bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false); bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false);
/** /**
* take a source jar, add mods to it, resulting in target jar * take a source jar, add mods to it, resulting in target jar
*/ */
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods); bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods);
/** /**
* Find a single file in archive by file name (not path) * Find a single file in archive by file name (not path)
* *
* \param ignore_paths paths to skip when recursing the search * \param ignore_paths paths to skip when recursing the search
* *
* \return the path prefix where the file is * \return the path prefix where the file is
*/ */
QString findFolderOfFileInZip(QuaZip * zip, const QString & what, const QStringList& ignore_paths = {}, const QString &root = QString("")); 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 * 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 * If a file is found in a path, no deeper paths are searched
* *
* \return true if anything was found * \return true if anything was found
*/ */
bool findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString()); bool findFilesInZip(QuaZip* zip, const QString& what, QStringList& result, const QString& root = QString());
/** /**
* Extract a subdirectory from an archive * Extract a subdirectory from an archive
*/ */
std::optional<QStringList> extractSubDir(QuaZip *zip, const QString & subdir, const QString &target); std::optional<QStringList> 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. * Extract a whole archive.
* *
* \param fileCompressed The name of the archive. * \param fileCompressed The name of the archive.
* \param dir The directory to extract to, the current directory if left empty. * \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. * \return The list of the full paths of the files extracted, empty on failure.
*/ */
std::optional<QStringList> extractDir(QString fileCompressed, QString dir); std::optional<QStringList> extractDir(QString fileCompressed, QString dir);
/** /**
* Extract a subdirectory from an archive * Extract a subdirectory from an archive
* *
* \param fileCompressed The name of the archive. * \param fileCompressed The name of the archive.
* \param subdir The directory within the archive to extract * \param subdir The directory within the archive to extract
* \param dir The directory to extract to, the current directory if left empty. * \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. * \return The list of the full paths of the files extracted, empty on failure.
*/ */
std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir); std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir);
/** /**
* Extract a single file from an archive into a directory * Extract a single file from an archive into a directory
* *
* \param fileCompressed The name of the archive. * \param fileCompressed The name of the archive.
* \param file The file within the archive to extract * \param file The file within the archive to extract
* \param dir The directory to extract to, the current directory if left empty. * \param dir The directory to extract to, the current directory if left empty.
* \return true for success or false for failure * \return true for success or false for failure
*/ */
bool extractFile(QString fileCompressed, QString file, QString dir); 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. * Populate a QFileInfoList with a directory tree recursively, while allowing to excludeFilter what shouldn't be included.
* \param rootDir directory to start off * \param rootDir directory to start off
* \param subDir subdirectory, should be nullptr for first invocation * \param subDir subdirectory, should be nullptr for first invocation
* \param files resulting list of QFileInfo * \param files resulting list of QFileInfo
* \param excludeFilter function to excludeFilter which files shouldn't be included (returning true means to excude) * \param excludeFilter function to excludeFilter which files shouldn't be included (returning true means to excude)
* \return true for success or false for failure * \return true for success or false for failure
*/ */
bool collectFileListRecursively(const QString &rootDir, const QString &subDir, QFileInfoList *files, FilterFunction excludeFilter); 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<QString> 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<QString, QByteArray> m_extra_files;
QFuture<ZipResult> m_build_zip_future;
QFutureWatcher<ZipResult> m_build_zip_watcher;
};
} // namespace MMCZip

View File

@ -1,10 +1,10 @@
#pragma once #pragma once
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug>
#include <QPixmapCache> #include <QPixmapCache>
#include <QThread> #include <QThread>
#include <QTime> #include <QTime>
#include <QDebug>
#define GET_TYPE() \ #define GET_TYPE() \
Qt::ConnectionType type; \ Qt::ConnectionType type; \
@ -94,8 +94,8 @@ class PixmapCache final : public QObject {
return true; 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 * @return if the cache size was increased
*/ */
bool _markCacheMissByEviciton() bool _markCacheMissByEviciton()

View File

@ -16,15 +16,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <QStringList>
#include <QDir> #include <QDir>
#include <QString> #include <QString>
#include <QStringList>
#include <QSysInfo> #include <QSysInfo>
#include <QtGlobal> #include <QtGlobal>
#include "MangoHud.h"
#include "FileSystem.h" #include "FileSystem.h"
#include "Json.h" #include "Json.h"
#include "MangoHud.h"
namespace MangoHud { namespace MangoHud {

View File

@ -18,7 +18,7 @@
#pragma once #pragma once
#include <QString>
#include <cmark.h> #include <cmark.h>
#include <QString>
QString markdownToHTML(const QString& markdown); QString markdownToHTML(const QString& markdown);

View File

@ -22,12 +22,11 @@ MessageLevel::Enum MessageLevel::getLevel(const QString& levelName)
return MessageLevel::Unknown; return MessageLevel::Unknown;
} }
MessageLevel::Enum MessageLevel::fromLine(QString &line) MessageLevel::Enum MessageLevel::fromLine(QString& line)
{ {
// Level prefix // Level prefix
int endmark = line.indexOf("]!"); int endmark = line.indexOf("]!");
if (line.startsWith("!![") && endmark != -1) if (line.startsWith("!![") && endmark != -1) {
{
auto level = MessageLevel::getLevel(line.left(endmark).mid(3)); auto level = MessageLevel::getLevel(line.left(endmark).mid(3));
line = line.mid(endmark + 2); line = line.mid(endmark + 2);
return level; return level;

View File

@ -6,23 +6,21 @@
* @brief the MessageLevel Enum * @brief the MessageLevel Enum
* defines what level a log message is * defines what level a log message is
*/ */
namespace MessageLevel namespace MessageLevel {
{ enum Enum {
enum Enum Unknown, /**< No idea what this is or where it came from */
{ StdOut, /**< Undetermined stderr messages */
Unknown, /**< No idea what this is or where it came from */ StdErr, /**< Undetermined stdout messages */
StdOut, /**< Undetermined stderr messages */
StdErr, /**< Undetermined stdout messages */
Launcher, /**< Launcher Messages */ Launcher, /**< Launcher Messages */
Debug, /**< Debug Messages */ Debug, /**< Debug Messages */
Info, /**< Info Messages */ Info, /**< Info Messages */
Message, /**< Standard Messages */ Message, /**< Standard Messages */
Warning, /**< Warnings */ Warning, /**< Warnings */
Error, /**< Errors */ Error, /**< Errors */
Fatal, /**< Fatal 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. */ /* 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

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -37,88 +37,38 @@
#include "BaseInstance.h" #include "BaseInstance.h"
#include "launch/LaunchTask.h" #include "launch/LaunchTask.h"
class NullInstance: public BaseInstance class NullInstance : public BaseInstance {
{
Q_OBJECT Q_OBJECT
public: public:
NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir) NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir)
:BaseInstance(globalSettings, settings, rootDir) : BaseInstance(globalSettings, settings, rootDir)
{ {
setVersionBroken(true); setVersionBroken(true);
} }
virtual ~NullInstance() {}; virtual ~NullInstance(){};
void saveNow() override void saveNow() override {}
{ void loadSpecificSettings() override { setSpecificSettingsLoaded(true); }
} QString getStatusbarDescription() override { return tr("Unknown instance type"); };
void loadSpecificSettings() override QSet<QString> traits() const override { return {}; };
{ QString instanceConfigFolder() const override { return instanceRoot(); };
setSpecificSettingsLoaded(true); shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override { return nullptr; }
} shared_qobject_ptr<Task> createUpdateTask([[maybe_unused]] Net::Mode mode) override { return nullptr; }
QString getStatusbarDescription() override QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); }
{ QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); }
return tr("Unknown instance type"); QMap<QString, QString> getVariables() override { return QMap<QString, QString>(); }
}; IPathMatcher::Ptr getLogFileMatcher() override { return nullptr; }
QSet< QString > traits() const override QString getLogFileRoot() override { return instanceRoot(); }
{ QString typeName() const override { return "Null"; }
return {}; bool canExport() const override { return false; }
}; bool canEdit() const override { return false; }
QString instanceConfigFolder() const override bool canLaunch() const override { return false; }
{
return instanceRoot();
};
shared_qobject_ptr<LaunchTask> 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<QString, QString> getVariables() override
{
return QMap<QString, QString>();
}
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 verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override
{ {
QStringList out; QStringList out;
out << "Null instance - placeholder."; out << "Null instance - placeholder.";
return out; return out;
} }
QString modsRoot() const override { QString modsRoot() const override { return QString(); }
return QString();
}
void updateRuntimeContext() void updateRuntimeContext()
{ {
// NOOP // NOOP

View File

@ -1,47 +1,35 @@
#pragma once #pragma once
enum class ProblemSeverity #include <QList>
{ #include <QString>
None,
Warning,
Error
};
struct PatchProblem enum class ProblemSeverity { None, Warning, Error };
{
struct PatchProblem {
ProblemSeverity m_severity; ProblemSeverity m_severity;
QString m_description; QString m_description;
}; };
class ProblemProvider class ProblemProvider {
{ public:
public: virtual ~ProblemProvider() {}
virtual ~ProblemProvider() {};
virtual const QList<PatchProblem> getProblems() const = 0; virtual const QList<PatchProblem> getProblems() const = 0;
virtual ProblemSeverity getProblemSeverity() const = 0; virtual ProblemSeverity getProblemSeverity() const = 0;
}; };
class ProblemContainer : public ProblemProvider class ProblemContainer : public ProblemProvider {
{ public:
public: const QList<PatchProblem> getProblems() const override { return m_problems; }
const QList<PatchProblem> getProblems() const override ProblemSeverity getProblemSeverity() const override { return m_problemSeverity; }
virtual void addProblem(ProblemSeverity severity, const QString& description)
{ {
return m_problems; if (severity > m_problemSeverity) {
}
ProblemSeverity getProblemSeverity() const override
{
return m_problemSeverity;
}
virtual void addProblem(ProblemSeverity severity, const QString &description)
{
if(severity > m_problemSeverity)
{
m_problemSeverity = severity; m_problemSeverity = severity;
} }
m_problems.append({severity, description}); m_problems.append({ severity, description });
} }
private: private:
QList<PatchProblem> m_problems; QList<PatchProblem> m_problems;
ProblemSeverity m_problemSeverity = ProblemSeverity::None; ProblemSeverity m_problemSeverity = ProblemSeverity::None;
}; };

View File

@ -36,35 +36,34 @@
#pragma once #pragma once
#include <QVariant>
#include <QList> #include <QList>
#include <QVariant>
namespace QVariantUtils { namespace QVariantUtils {
template <typename T> template <typename T>
inline QList<T> toList(QVariant src) { inline QList<T> toList(QVariant src)
{
QVariantList variantList = src.toList(); QVariantList variantList = src.toList();
QList<T> list_t; QList<T> list_t;
list_t.reserve(variantList.size()); list_t.reserve(variantList.size());
for (const QVariant& v : variantList) for (const QVariant& v : variantList) {
{
list_t.append(v.value<T>()); list_t.append(v.value<T>());
} }
return list_t; return list_t;
} }
template <typename T> template <typename T>
inline QVariant fromList(QList<T> val) { inline QVariant fromList(QList<T> val)
{
QVariantList variantList; QVariantList variantList;
variantList.reserve(val.size()); variantList.reserve(val.size());
for (const T& v : val) for (const T& v : val) {
{
variantList.append(v); variantList.append(v);
} }
return variantList; return variantList;
} }
} } // namespace QVariantUtils

View File

@ -1,13 +1,12 @@
#pragma once #pragma once
#include <QWriteLocker>
#include <QReadLocker>
#include <QMap> #include <QMap>
#include <QReadLocker>
#include <QSet> #include <QSet>
#include <QWriteLocker>
template <typename K, typename V> template <typename K, typename V>
class RWStorage class RWStorage {
{ public:
public:
void add(K key, V value) void add(K key, V value)
{ {
QWriteLocker l(&lock); QWriteLocker l(&lock);
@ -17,21 +16,19 @@ public:
V get(K key) V get(K key)
{ {
QReadLocker l(&lock); QReadLocker l(&lock);
if(cache.contains(key)) if (cache.contains(key)) {
{
return cache[key]; return cache[key];
} } else
else return V(); return V();
} }
bool get(K key, V& value) bool get(K key, V& value)
{ {
QReadLocker l(&lock); QReadLocker l(&lock);
if(cache.contains(key)) if (cache.contains(key)) {
{
value = cache[key]; value = cache[key];
return true; return true;
} } else
else return false; return false;
} }
bool has(K key) bool has(K key)
{ {
@ -41,15 +38,14 @@ public:
bool stale(K key) bool stale(K key)
{ {
QReadLocker l(&lock); QReadLocker l(&lock);
if(!cache.contains(key)) if (!cache.contains(key))
return true; return true;
return stale_entries.contains(key); return stale_entries.contains(key);
} }
void setStale(K key) void setStale(K key)
{ {
QWriteLocker l(&lock); QWriteLocker l(&lock);
if(cache.contains(key)) if (cache.contains(key)) {
{
stale_entries.insert(key); stale_entries.insert(key);
} }
} }
@ -59,7 +55,8 @@ public:
cache.clear(); cache.clear();
stale_entries.clear(); stale_entries.clear();
} }
private:
private:
QReadWriteLock lock; QReadWriteLock lock;
QMap<K, V> cache; QMap<K, V> cache;
QSet<K> stale_entries; QSet<K> stale_entries;

View File

@ -1,25 +1,21 @@
#include "RecursiveFileSystemWatcher.h" #include "RecursiveFileSystemWatcher.h"
#include <QRegularExpression>
#include <QDebug> #include <QDebug>
#include <QRegularExpression>
RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject *parent) RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject* parent) : QObject(parent), m_watcher(new QFileSystemWatcher(this))
: QObject(parent), m_watcher(new QFileSystemWatcher(this))
{ {
connect(m_watcher, &QFileSystemWatcher::fileChanged, this, connect(m_watcher, &QFileSystemWatcher::fileChanged, this, &RecursiveFileSystemWatcher::fileChange);
&RecursiveFileSystemWatcher::fileChange); connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &RecursiveFileSystemWatcher::directoryChange);
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this,
&RecursiveFileSystemWatcher::directoryChange);
} }
void RecursiveFileSystemWatcher::setRootDir(const QDir &root) void RecursiveFileSystemWatcher::setRootDir(const QDir& root)
{ {
bool wasEnabled = m_isEnabled; bool wasEnabled = m_isEnabled;
disable(); disable();
m_root = root; m_root = root;
setFiles(scanRecursive(m_root)); setFiles(scanRecursive(m_root));
if (wasEnabled) if (wasEnabled) {
{
enable(); enable();
} }
} }
@ -28,16 +24,14 @@ void RecursiveFileSystemWatcher::setWatchFiles(const bool watchFiles)
bool wasEnabled = m_isEnabled; bool wasEnabled = m_isEnabled;
disable(); disable();
m_watchFiles = watchFiles; m_watchFiles = watchFiles;
if (wasEnabled) if (wasEnabled) {
{
enable(); enable();
} }
} }
void RecursiveFileSystemWatcher::enable() void RecursiveFileSystemWatcher::enable()
{ {
if (m_isEnabled) if (m_isEnabled) {
{
return; return;
} }
Q_ASSERT(m_root != QDir::root()); Q_ASSERT(m_root != QDir::root());
@ -46,8 +40,7 @@ void RecursiveFileSystemWatcher::enable()
} }
void RecursiveFileSystemWatcher::disable() void RecursiveFileSystemWatcher::disable()
{ {
if (!m_isEnabled) if (!m_isEnabled) {
{
return; return;
} }
m_isEnabled = false; m_isEnabled = false;
@ -55,57 +48,49 @@ void RecursiveFileSystemWatcher::disable()
m_watcher->removePaths(m_watcher->directories()); 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; m_files = files;
emit filesChanged(); emit filesChanged();
} }
} }
void RecursiveFileSystemWatcher::addFilesToWatcherRecursive(const QDir &dir) void RecursiveFileSystemWatcher::addFilesToWatcherRecursive(const QDir& dir)
{ {
m_watcher->addPath(dir.absolutePath()); 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)); addFilesToWatcherRecursive(dir.absoluteFilePath(directory));
} }
if (m_watchFiles) if (m_watchFiles) {
{ for (const QFileInfo& info : dir.entryInfoList(QDir::Files)) {
for (const QFileInfo &info : dir.entryInfoList(QDir::Files))
{
m_watcher->addPath(info.absoluteFilePath()); m_watcher->addPath(info.absoluteFilePath());
} }
} }
} }
QStringList RecursiveFileSystemWatcher::scanRecursive(const QDir &directory) QStringList RecursiveFileSystemWatcher::scanRecursive(const QDir& directory)
{ {
QStringList ret; QStringList ret;
if(!m_matcher) if (!m_matcher) {
{
return {}; 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))); 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)); auto relPath = m_root.relativeFilePath(directory.absoluteFilePath(file));
if (m_matcher->matches(relPath)) if (m_matcher->matches(relPath)) {
{
ret.append(relPath); ret.append(relPath);
} }
} }
return ret; return ret;
} }
void RecursiveFileSystemWatcher::fileChange(const QString &path) void RecursiveFileSystemWatcher::fileChange(const QString& path)
{ {
emit fileChanged(path); emit fileChanged(path);
} }
void RecursiveFileSystemWatcher::directoryChange(const QString &path) void RecursiveFileSystemWatcher::directoryChange([[maybe_unused]] const QString& path)
{ {
setFiles(scanRecursive(m_root)); setFiles(scanRecursive(m_root));
} }

View File

@ -1,61 +1,48 @@
#pragma once #pragma once
#include <QFileSystemWatcher>
#include <QDir> #include <QDir>
#include <QFileSystemWatcher>
#include "pathmatcher/IPathMatcher.h" #include "pathmatcher/IPathMatcher.h"
class RecursiveFileSystemWatcher : public QObject class RecursiveFileSystemWatcher : public QObject {
{
Q_OBJECT Q_OBJECT
public: public:
RecursiveFileSystemWatcher(QObject *parent); RecursiveFileSystemWatcher(QObject* parent);
void setRootDir(const QDir &root); void setRootDir(const QDir& root);
QDir rootDir() const QDir rootDir() const { return m_root; }
{
return m_root;
}
// WARNING: setting this to true may be bad for performance // WARNING: setting this to true may be bad for performance
void setWatchFiles(const bool watchFiles); void setWatchFiles(const bool watchFiles);
bool watchFiles() const bool watchFiles() const { return m_watchFiles; }
{
return m_watchFiles;
}
void setMatcher(IPathMatcher::Ptr matcher) void setMatcher(IPathMatcher::Ptr matcher) { m_matcher = matcher; }
{
m_matcher = matcher;
}
QStringList files() const QStringList files() const { return m_files; }
{
return m_files;
}
signals: signals:
void filesChanged(); void filesChanged();
void fileChanged(const QString &path); void fileChanged(const QString& path);
public slots: public slots:
void enable(); void enable();
void disable(); void disable();
private: private:
QDir m_root; QDir m_root;
bool m_watchFiles = false; bool m_watchFiles = false;
bool m_isEnabled = false; bool m_isEnabled = false;
IPathMatcher::Ptr m_matcher; IPathMatcher::Ptr m_matcher;
QFileSystemWatcher *m_watcher; QFileSystemWatcher* m_watcher;
QStringList m_files; QStringList m_files;
void setFiles(const QStringList &files); void setFiles(const QStringList& files);
void addFilesToWatcherRecursive(const QDir &dir); void addFilesToWatcherRecursive(const QDir& dir);
QStringList scanRecursive(const QDir &dir); QStringList scanRecursive(const QDir& dir);
private slots: private slots:
void fileChange(const QString &path); void fileChange(const QString& path);
void directoryChange(const QString &path); void directoryChange(const QString& path);
}; };

View File

@ -24,6 +24,8 @@
#include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ModFolderModel.h"
#include "minecraft/mod/ResourceFolderModel.h" #include "minecraft/mod/ResourceFolderModel.h"
#include "net/ApiDownload.h"
ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion version, ModPlatform::IndexedVersion version,
const std::shared_ptr<ResourceFolderModel> packs, const std::shared_ptr<ResourceFolderModel> 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::succeeded, this, &ResourceDownloadTask::downloadSucceeded);
connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged); 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); connect(m_filesNetJob.get(), &NetJob::failed, this, &ResourceDownloadTask::downloadFailed);
addTask(m_filesNetJob); addTask(m_filesNetJob);

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify

View File

@ -1,44 +1,32 @@
#pragma once #pragma once
#include <QString>
#include <QMap> #include <QMap>
#include <QString>
#include <QStringList> #include <QStringList>
template <char Tseparator> template <char Tseparator>
class SeparatorPrefixTree class SeparatorPrefixTree {
{ public:
public: SeparatorPrefixTree(QStringList paths) { insert(paths); }
SeparatorPrefixTree(QStringList paths)
{
insert(paths);
}
SeparatorPrefixTree(bool contained = false) SeparatorPrefixTree(bool contained = false) { m_contained = contained; }
{
m_contained = contained;
}
void insert(QStringList paths) void insert(QStringList paths)
{ {
for(auto &path: paths) for (auto& path : paths) {
{
insert(path); insert(path);
} }
} }
/// insert an exact path into the tree /// insert an exact path into the tree
SeparatorPrefixTree & insert(QString path) SeparatorPrefixTree& insert(QString path)
{ {
auto sepIndex = path.indexOf(Tseparator); auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1) if (sepIndex == -1) {
{
children[path] = SeparatorPrefixTree(true); children[path] = SeparatorPrefixTree(true);
return children[path]; return children[path];
} } else {
else
{
auto prefix = path.left(sepIndex); auto prefix = path.left(sepIndex);
if(!children.contains(prefix)) if (!children.contains(prefix)) {
{
children[prefix] = SeparatorPrefixTree(false); children[prefix] = SeparatorPrefixTree(false);
} }
return children[prefix].insert(path.mid(sepIndex + 1)); return children[prefix].insert(path.mid(sepIndex + 1));
@ -56,26 +44,20 @@ public:
bool covers(QString path) const bool covers(QString path) const
{ {
// if we found some valid node, it's good enough. the tree covers the path // if we found some valid node, it's good enough. the tree covers the path
if(m_contained) if (m_contained) {
{
return true; return true;
} }
auto sepIndex = path.indexOf(Tseparator); auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1) if (sepIndex == -1) {
{
auto found = children.find(path); auto found = children.find(path);
if(found == children.end()) if (found == children.end()) {
{
return false; return false;
} }
return (*found).covers(QString()); return (*found).covers(QString());
} } else {
else
{
auto prefix = path.left(sepIndex); auto prefix = path.left(sepIndex);
auto found = children.find(prefix); auto found = children.find(prefix);
if(found == children.end()) if (found == children.end()) {
{
return false; return false;
} }
return (*found).covers(path.mid(sepIndex + 1)); return (*found).covers(path.mid(sepIndex + 1));
@ -86,41 +68,33 @@ public:
QString cover(QString path) const QString cover(QString path) const
{ {
// if we found some valid node, it's good enough. the tree covers the path // if we found some valid node, it's good enough. the tree covers the path
if(m_contained) if (m_contained) {
{
return QString(""); return QString("");
} }
auto sepIndex = path.indexOf(Tseparator); auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1) if (sepIndex == -1) {
{
auto found = children.find(path); auto found = children.find(path);
if(found == children.end()) if (found == children.end()) {
{
return QString(); return QString();
} }
auto nested = (*found).cover(QString()); auto nested = (*found).cover(QString());
if(nested.isNull()) if (nested.isNull()) {
{
return nested; return nested;
} }
if(nested.isEmpty()) if (nested.isEmpty())
return path; return path;
return path + Tseparator + nested; return path + Tseparator + nested;
} } else {
else
{
auto prefix = path.left(sepIndex); auto prefix = path.left(sepIndex);
auto found = children.find(prefix); auto found = children.find(prefix);
if(found == children.end()) if (found == children.end()) {
{
return QString(); return QString();
} }
auto nested = (*found).cover(path.mid(sepIndex + 1)); auto nested = (*found).cover(path.mid(sepIndex + 1));
if(nested.isNull()) if (nested.isNull()) {
{
return nested; return nested;
} }
if(nested.isEmpty()) if (nested.isEmpty())
return prefix; return prefix;
return prefix + Tseparator + nested; return prefix + Tseparator + nested;
} }
@ -130,21 +104,16 @@ public:
bool exists(QString path) const bool exists(QString path) const
{ {
auto sepIndex = path.indexOf(Tseparator); auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1) if (sepIndex == -1) {
{
auto found = children.find(path); auto found = children.find(path);
if(found == children.end()) if (found == children.end()) {
{
return false; return false;
} }
return true; return true;
} } else {
else
{
auto prefix = path.left(sepIndex); auto prefix = path.left(sepIndex);
auto found = children.find(prefix); auto found = children.find(prefix);
if(found == children.end()) if (found == children.end()) {
{
return false; return false;
} }
return (*found).exists(path.mid(sepIndex + 1)); return (*found).exists(path.mid(sepIndex + 1));
@ -152,24 +121,19 @@ public:
} }
/// find a node in the tree by name /// 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); auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1) if (sepIndex == -1) {
{
auto found = children.find(path); auto found = children.find(path);
if(found == children.end()) if (found == children.end()) {
{
return nullptr; return nullptr;
} }
return &(*found); return &(*found);
} } else {
else
{
auto prefix = path.left(sepIndex); auto prefix = path.left(sepIndex);
auto found = children.find(prefix); auto found = children.find(prefix);
if(found == children.end()) if (found == children.end()) {
{
return nullptr; return nullptr;
} }
return (*found).find(path.mid(sepIndex + 1)); return (*found).find(path.mid(sepIndex + 1));
@ -177,70 +141,48 @@ public:
} }
/// is this a leaf node? /// is this a leaf node?
bool leaf() const bool leaf() const { return children.isEmpty(); }
{
return children.isEmpty();
}
/// is this node actually contained in the tree, or is it purely structural? /// is this node actually contained in the tree, or is it purely structural?
bool contained() const bool contained() const { return m_contained; }
{
return m_contained;
}
/// Remove a path from the tree /// Remove a path from the tree
bool remove(QString path) bool remove(QString path) { return removeInternal(path) != Failed; }
{
return removeInternal(path) != Failed;
}
/// Clear all children of this node tree node /// Clear all children of this node tree node
void clear() void clear() { children.clear(); }
{
children.clear();
}
QStringList toStringList() const QStringList toStringList() const
{ {
QStringList collected; QStringList collected;
// collecting these is more expensive. // collecting these is more expensive.
auto iter = children.begin(); auto iter = children.begin();
while(iter != children.end()) while (iter != children.end()) {
{
QStringList list = iter.value().toStringList(); 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]; list[i] = iter.key() + Tseparator + list[i];
} }
collected.append(list); collected.append(list);
if((*iter).m_contained) if ((*iter).m_contained) {
{
collected.append(iter.key()); collected.append(iter.key());
} }
iter++; iter++;
} }
return collected; return collected;
} }
private:
enum Removal private:
{ enum Removal { Failed, Succeeded, HasChildren };
Failed,
Succeeded,
HasChildren
};
Removal removeInternal(QString path = QString()) Removal removeInternal(QString path = QString())
{ {
if(path.isEmpty()) if (path.isEmpty()) {
{ if (!m_contained) {
if(!m_contained)
{
// remove all children - we are removing a prefix // remove all children - we are removing a prefix
clear(); clear();
return Succeeded; return Succeeded;
} }
m_contained = false; m_contained = false;
if(children.size()) if (children.size()) {
{
return HasChildren; return HasChildren;
} }
return Succeeded; return Succeeded;
@ -248,42 +190,32 @@ private:
Removal remStatus = Failed; Removal remStatus = Failed;
QString childToRemove; QString childToRemove;
auto sepIndex = path.indexOf(Tseparator); auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1) if (sepIndex == -1) {
{
childToRemove = path; childToRemove = path;
auto found = children.find(childToRemove); auto found = children.find(childToRemove);
if(found == children.end()) if (found == children.end()) {
{
return Failed; return Failed;
} }
remStatus = (*found).removeInternal(); remStatus = (*found).removeInternal();
} } else {
else
{
childToRemove = path.left(sepIndex); childToRemove = path.left(sepIndex);
auto found = children.find(childToRemove); auto found = children.find(childToRemove);
if(found == children.end()) if (found == children.end()) {
{
return Failed; return Failed;
} }
remStatus = (*found).removeInternal(path.mid(sepIndex + 1)); remStatus = (*found).removeInternal(path.mid(sepIndex + 1));
} }
switch (remStatus) switch (remStatus) {
{
case Failed: case Failed:
case HasChildren: case HasChildren: {
{
return remStatus; return remStatus;
} }
case Succeeded: case Succeeded: {
{
children.remove(childToRemove); children.remove(childToRemove);
if(m_contained) if (m_contained) {
{
return HasChildren; return HasChildren;
} }
if(children.size()) if (children.size()) {
{
return HasChildren; return HasChildren;
} }
return Succeeded; return Succeeded;
@ -292,7 +224,7 @@ private:
return Failed; return Failed;
} }
private: private:
QMap<QString,SeparatorPrefixTree<Tseparator>> children; QMap<QString, SeparatorPrefixTree<Tseparator>> children;
bool m_contained = false; bool m_contained = false;
}; };

View File

@ -14,17 +14,16 @@
*/ */
#include "SkinUtils.h" #include "SkinUtils.h"
#include "net/HttpMetaCache.h"
#include "Application.h" #include "Application.h"
#include "net/HttpMetaCache.h"
#include <QFile> #include <QFile>
#include <QPainter> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QJsonArray> #include <QPainter>
namespace SkinUtils namespace SkinUtils {
{
/* /*
* Given a username, return a pixmap of the cached skin (if it exists), QPixmap() otherwise * 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()); QFile fskin(APPLICATION->metacache()->resolveEntry("skins", username + ".png")->getFullPath());
if (fskin.exists()) if (fskin.exists()) {
{
QPixmap skinTexture(fskin.fileName()); QPixmap skinTexture(fskin.fileName());
if(!skinTexture.isNull()) if (!skinTexture.isNull()) {
{
QPixmap skin = QPixmap(8, 8); 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); QPainter painter(&skin);
painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8)); painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8));
painter.drawPixmap(0, 0, skinTexture.copy(40, 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(); return QPixmap();
} }
} } // namespace SkinUtils

View File

@ -17,7 +17,6 @@
#include <QPixmap> #include <QPixmap>
namespace SkinUtils namespace SkinUtils {
{
QPixmap getFaceFromCache(QString id, int height = 64, int width = 64); QPixmap getFaceFromCache(QString id, int height = 64, int width = 64);
} }

View File

@ -68,15 +68,14 @@ inline QString fromStdString(string s)
int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs); 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 url Url to truncate
* @param max_len max lenght of url in charaters * @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. * @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 humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1);
QString getRandomAlphaNumeric(); QString getRandomAlphaNumeric();
} // namespace StringUtils } // namespace StringUtils

View File

@ -12,28 +12,20 @@ class Usable;
* *
* @see UseLock * @see UseLock
*/ */
class Usable class Usable {
{
friend class UseLock; friend class UseLock;
public:
std::size_t useCount() const public:
{ virtual ~Usable() {}
return m_useCount;
} std::size_t useCount() const { return m_useCount; }
bool isInUse() const bool isInUse() const { return m_useCount > 0; }
{
return m_useCount > 0; protected:
} virtual void decrementUses() { m_useCount--; }
protected: virtual void incrementUses() { m_useCount++; }
virtual void decrementUses()
{ private:
m_useCount--;
}
virtual void incrementUses()
{
m_useCount++;
}
private:
std::size_t m_useCount = 0; std::size_t m_useCount = 0;
}; };
@ -42,19 +34,15 @@ private:
* *
* @see Usable * @see Usable
*/ */
class UseLock class UseLock {
{ public:
public: UseLock(shared_qobject_ptr<Usable> usable) : m_usable(usable)
UseLock(shared_qobject_ptr<Usable> usable)
: m_usable(usable)
{ {
// this doesn't use shared pointer use count, because that wouldn't be correct. this count is separate. // this doesn't use shared pointer use count, because that wouldn't be correct. this count is separate.
m_usable->incrementUses(); m_usable->incrementUses();
} }
~UseLock() ~UseLock() { m_usable->decrementUses(); }
{
m_usable->decrementUses(); private:
}
private:
shared_qobject_ptr<Usable> m_usable; shared_qobject_ptr<Usable> m_usable;
}; };

View File

@ -117,12 +117,14 @@ QDebug operator<<(QDebug debug, const Version& v)
bool first = true; bool first = true;
for (auto s : v.m_sections) { for (auto s : v.m_sections) {
if (!first) debug.nospace() << ", "; if (!first)
debug.nospace() << ", ";
debug.nospace() << s.m_fullString; debug.nospace() << s.m_fullString;
first = false; first = false;
} }
debug.nospace() << " ]" << " }"; debug.nospace() << " ]"
<< " }";
return debug; return debug;
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2023 flowln <flowlnlnln@gmail.com> * Copyright (C) 2023 flowln <flowlnlnln@gmail.com>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* *
@ -48,12 +48,12 @@ class Version {
Version(QString str); Version(QString str);
Version() = default; 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; } QString toString() const { return m_string; }
@ -63,7 +63,7 @@ class Version {
struct Section { struct Section {
explicit Section(QString fullString) : m_fullString(std::move(fullString)) 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++) { for (int i = 0; i < m_fullString.size(); i++) {
if (!m_fullString[i].isDigit()) { if (!m_fullString[i].isDigit()) {
cutoff = i; cutoff = i;
@ -72,7 +72,7 @@ class Version {
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
auto numPart = QStringView{m_fullString}.left(cutoff); auto numPart = QStringView{ m_fullString }.left(cutoff);
#else #else
auto numPart = m_fullString.leftRef(cutoff); auto numPart = m_fullString.leftRef(cutoff);
#endif #endif
@ -83,7 +83,7 @@ class Version {
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
auto stringPart = QStringView{m_fullString}.mid(cutoff); auto stringPart = QStringView{ m_fullString }.mid(cutoff);
#else #else
auto stringPart = m_fullString.midRef(cutoff); auto stringPart = m_fullString.midRef(cutoff);
#endif #endif
@ -103,8 +103,14 @@ class Version {
QString m_fullString; QString m_fullString;
[[nodiscard]] inline bool isAppendix() const { return m_stringPart.startsWith('+'); } [[nodiscard]] inline bool isAppendix() const
[[nodiscard]] inline bool isPreRelease() const { return m_stringPart.startsWith('-') && m_stringPart.length() > 1; } {
return m_stringPart.startsWith('+');
}
[[nodiscard]] inline bool isPreRelease() const
{
return m_stringPart.startsWith('-') && m_stringPart.length() > 1;
}
inline bool operator==(const Section& other) const inline bool operator==(const Section& other) const
{ {
@ -121,7 +127,7 @@ class Version {
} }
inline bool operator<(const Section& other) const inline bool operator<(const Section& other) const
{ {
static auto unequal_is_less = [](Section const& non_null) -> bool { static auto unequal_is_less = [](Section const& non_null) -> bool {
if (non_null.m_stringPart.isEmpty()) if (non_null.m_stringPart.isEmpty())
return non_null.m_numPart == 0; return non_null.m_numPart == 0;
@ -154,7 +160,7 @@ class Version {
{ {
return !(*this == other); return !(*this == other);
} }
inline bool operator>(const Section &other) const inline bool operator>(const Section& other) const
{ {
return !(*this < other || *this == other); return !(*this < other || *this == other);
} }
@ -166,5 +172,3 @@ class Version {
void parse(); void parse();
}; };

View File

@ -35,53 +35,48 @@
*/ */
#include "VersionProxyModel.h" #include "VersionProxyModel.h"
#include "Application.h"
#include <QSortFilterProxyModel>
#include <QPixmapCache>
#include <Version.h> #include <Version.h>
#include <meta/VersionList.h> #include <meta/VersionList.h>
#include <QPixmapCache>
#include <QSortFilterProxyModel>
#include "Application.h"
class VersionFilterModel : public QSortFilterProxyModel class VersionFilterModel : public QSortFilterProxyModel {
{
Q_OBJECT Q_OBJECT
public: public:
VersionFilterModel(VersionProxyModel *parent) : QSortFilterProxyModel(parent) VersionFilterModel(VersionProxyModel* parent) : QSortFilterProxyModel(parent)
{ {
m_parent = parent; m_parent = parent;
setSortRole(BaseVersionList::SortRole); setSortRole(BaseVersionList::SortRole);
sort(0, Qt::DescendingOrder); 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 auto& filters = m_parent->filters();
const QString &search = m_parent->search(); const QString& search = m_parent->search();
const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
if (!search.isEmpty() && !sourceModel()->data(idx, BaseVersionList::VersionRole).toString().contains(search, Qt::CaseInsensitive)) if (!search.isEmpty() && !sourceModel()->data(idx, BaseVersionList::VersionRole).toString().contains(search, Qt::CaseInsensitive))
return false; 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 data = sourceModel()->data(idx, it.key());
auto match = data.toString(); auto match = data.toString();
if(!it.value()->accepts(match)) if (!it.value()->accepts(match)) {
{
return false; return false;
} }
} }
return true; return true;
} }
void filterChanged() void filterChanged() { invalidateFilter(); }
{
invalidateFilter(); private:
} VersionProxyModel* m_parent;
private:
VersionProxyModel *m_parent;
}; };
VersionProxyModel::VersionProxyModel(QObject *parent) : QAbstractProxyModel(parent) VersionProxyModel::VersionProxyModel(QObject* parent) : QAbstractProxyModel(parent)
{ {
filterModel = new VersionFilterModel(this); filterModel = new VersionFilterModel(this);
connect(filterModel, &QAbstractItemModel::dataChanged, this, &VersionProxyModel::sourceDataChanged); 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 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(); return QVariant();
if(orientation != Qt::Horizontal) if (orientation != Qt::Horizontal)
return QVariant(); return QVariant();
auto column = m_columns[section]; auto column = m_columns[section];
if(role == Qt::DisplayRole) if (role == Qt::DisplayRole) {
{ switch (column) {
switch(column)
{
case Name: case Name:
return tr("Version"); return tr("Version");
case ParentVersion: case ParentVersion:
return tr("Minecraft"); //FIXME: this should come from metadata return tr("Minecraft"); // FIXME: this should come from metadata
case Branch: case Branch:
return tr("Branch"); return tr("Branch");
case Type: case Type:
@ -128,15 +121,12 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation,
case Time: case Time:
return tr("Released"); return tr("Released");
} }
} } else if (role == Qt::ToolTipRole) {
else if(role == Qt::ToolTipRole) switch (column) {
{
switch(column)
{
case Name: case Name:
return tr("The name of the version."); return tr("The name of the version.");
case ParentVersion: case ParentVersion:
return tr("Minecraft version"); //FIXME: this should come from metadata return tr("Minecraft version"); // FIXME: this should come from metadata
case Branch: case Branch:
return tr("The version's branch"); return tr("The version's branch");
case Type: case Type:
@ -152,25 +142,19 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation,
return QVariant(); 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(); return QVariant();
} }
auto column = m_columns[index.column()]; auto column = m_columns[index.column()];
auto parentIndex = mapToSource(index); auto parentIndex = mapToSource(index);
switch(role) switch (role) {
{ case Qt::DisplayRole: {
case Qt::DisplayRole: switch (column) {
{ case Name: {
switch(column)
{
case Name:
{
QString version = sourceModel()->data(parentIndex, BaseVersionList::VersionRole).toString(); QString version = sourceModel()->data(parentIndex, BaseVersionList::VersionRole).toString();
if(version == m_currentVersion) if (version == m_currentVersion) {
{
return tr("%1 (installed)").arg(version); return tr("%1 (installed)").arg(version);
} }
return version; return version;
@ -191,18 +175,14 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
} }
} }
case Qt::ToolTipRole: case Qt::ToolTipRole: {
{ if (column == Name && hasRecommended) {
if(column == Name && hasRecommended)
{
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole); auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
if(value.toBool()) if (value.toBool()) {
{
return tr("Recommended"); return tr("Recommended");
} else if(hasLatest) { } else if (hasLatest) {
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole); auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
if(value.toBool()) if (value.toBool()) {
{
return tr("Latest"); return tr("Latest");
} }
} }
@ -210,32 +190,23 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole); return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole);
} }
} }
case Qt::DecorationRole: case Qt::DecorationRole: {
{ switch (column) {
switch(column) case Name: {
{ if (hasRecommended) {
case Name: auto recommenced = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
{ if (recommenced.toBool()) {
if(hasRecommended)
{
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
if(value.toBool())
{
return APPLICATION->getThemedIcon("star"); return APPLICATION->getThemedIcon("star");
} } else if (hasLatest) {
else if(hasLatest) auto latest = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
{ if (latest.toBool()) {
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
if(value.toBool())
{
return APPLICATION->getThemedIcon("bug"); return APPLICATION->getThemedIcon("bug");
} }
} }
QPixmap pixmap; QPixmap pixmap;
QPixmapCache::find("placeholder", &pixmap); QPixmapCache::find("placeholder", &pixmap);
if(!pixmap) if (!pixmap) {
{ QPixmap px(16, 16);
QPixmap px(16,16);
px.fill(Qt::transparent); px.fill(Qt::transparent);
QPixmapCache::insert("placeholder", px); QPixmapCache::insert("placeholder", px);
return px; return px;
@ -243,16 +214,13 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
return pixmap; return pixmap;
} }
} }
default: default: {
{
return QVariant(); return QVariant();
} }
} }
} }
default: default: {
{ if (roles.contains((BaseVersionList::ModelRoles)role)) {
if(roles.contains((BaseVersionList::ModelRoles)role))
{
return sourceModel()->data(parentIndex, role); return sourceModel()->data(parentIndex, role);
} }
return QVariant(); 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(); 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 index(sourceIndex.row(), 0);
} }
return QModelIndex(); 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 sourceModel()->index(proxyIndex.row(), 0);
} }
return QModelIndex(); 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 // no trees here... shoo
if(parent.isValid()) if (parent.isValid()) {
{
return QModelIndex(); return QModelIndex();
} }
if(row < 0 || row >= sourceModel()->rowCount()) if (row < 0 || row >= sourceModel()->rowCount())
return QModelIndex(); return QModelIndex();
if(column < 0 || column >= columnCount()) if (column < 0 || column >= columnCount())
return QModelIndex(); return QModelIndex();
return QAbstractItemModel::createIndex(row, column); 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(); 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 sourceModel()->rowCount(parent);
} }
return 0; return 0;
} }
void VersionProxyModel::sourceDataChanged(const QModelIndex &source_top_left, void VersionProxyModel::sourceDataChanged(const QModelIndex& source_top_left, const QModelIndex& source_bottom_right)
const QModelIndex &source_bottom_right)
{ {
if(source_top_left.parent() != source_bottom_right.parent()) if (source_top_left.parent() != source_bottom_right.parent())
return; return;
// whole row is getting changed // whole row is getting changed
@ -323,22 +286,20 @@ void VersionProxyModel::sourceDataChanged(const QModelIndex &source_top_left,
emit dataChanged(topLeft, bottomRight); emit dataChanged(topLeft, bottomRight);
} }
void VersionProxyModel::setSourceModel(QAbstractItemModel *replacingRaw) void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw)
{ {
auto replacing = dynamic_cast<BaseVersionList *>(replacingRaw); auto replacing = dynamic_cast<BaseVersionList*>(replacingRaw);
beginResetModel(); beginResetModel();
m_columns.clear(); m_columns.clear();
if(!replacing) if (!replacing) {
{
roles.clear(); roles.clear();
filterModel->setSourceModel(replacing); filterModel->setSourceModel(replacing);
return; return;
} }
roles = replacing->providesRoles(); roles = replacing->providesRoles();
if(roles.contains(BaseVersionList::VersionRole)) if (roles.contains(BaseVersionList::VersionRole)) {
{
m_columns.push_back(Name); m_columns.push_back(Name);
} }
/* /*
@ -347,32 +308,25 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel *replacingRaw)
m_columns.push_back(ParentVersion); m_columns.push_back(ParentVersion);
} }
*/ */
if(roles.contains(BaseVersionList::ArchitectureRole)) if (roles.contains(BaseVersionList::ArchitectureRole)) {
{
m_columns.push_back(Architecture); m_columns.push_back(Architecture);
} }
if(roles.contains(BaseVersionList::PathRole)) if (roles.contains(BaseVersionList::PathRole)) {
{
m_columns.push_back(Path); m_columns.push_back(Path);
} }
if(roles.contains(Meta::VersionList::TimeRole)) if (roles.contains(Meta::VersionList::TimeRole)) {
{
m_columns.push_back(Time); m_columns.push_back(Time);
} }
if(roles.contains(BaseVersionList::BranchRole)) if (roles.contains(BaseVersionList::BranchRole)) {
{
m_columns.push_back(Branch); m_columns.push_back(Branch);
} }
if(roles.contains(BaseVersionList::TypeRole)) if (roles.contains(BaseVersionList::TypeRole)) {
{
m_columns.push_back(Type); m_columns.push_back(Type);
} }
if(roles.contains(BaseVersionList::RecommendedRole)) if (roles.contains(BaseVersionList::RecommendedRole)) {
{
hasRecommended = true; hasRecommended = true;
} }
if(roles.contains(BaseVersionList::LatestRole)) if (roles.contains(BaseVersionList::LatestRole)) {
{
hasLatest = true; hasLatest = true;
} }
filterModel->setSourceModel(replacing); filterModel->setSourceModel(replacing);
@ -382,16 +336,13 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel *replacingRaw)
QModelIndex VersionProxyModel::getRecommended() const QModelIndex VersionProxyModel::getRecommended() const
{ {
if(!roles.contains(BaseVersionList::RecommendedRole)) if (!roles.contains(BaseVersionList::RecommendedRole)) {
{
return index(0, 0); return index(0, 0);
} }
int recommended = 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); auto value = sourceModel()->data(mapToSource(index(i, 0)), BaseVersionList::RecommendedRole);
if (value.toBool()) if (value.toBool()) {
{
recommended = i; recommended = i;
} }
} }
@ -401,16 +352,13 @@ QModelIndex VersionProxyModel::getRecommended() const
QModelIndex VersionProxyModel::getVersion(const QString& version) const QModelIndex VersionProxyModel::getVersion(const QString& version) const
{ {
int found = -1; 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); auto value = sourceModel()->data(mapToSource(index(i, 0)), BaseVersionList::VersionRole);
if (value.toString() == version) if (value.toString() == version) {
{
found = i; found = i;
} }
} }
if(found == -1) if (found == -1) {
{
return QModelIndex(); return QModelIndex();
} }
return index(found, 0); return index(found, 0);
@ -423,23 +371,24 @@ void VersionProxyModel::clearFilters()
filterModel->filterChanged(); 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); m_filters[column].reset(f);
filterModel->filterChanged(); filterModel->filterChanged();
} }
void VersionProxyModel::setSearch(const QString &search) { void VersionProxyModel::setSearch(const QString& search)
{
m_search = search; m_search = search;
filterModel->filterChanged(); filterModel->filterChanged();
} }
const VersionProxyModel::FilterMap &VersionProxyModel::filters() const const VersionProxyModel::FilterMap& VersionProxyModel::filters() const
{ {
return m_filters; return m_filters;
} }
const QString &VersionProxyModel::search() const const QString& VersionProxyModel::search() const
{ {
return m_search; return m_search;
} }
@ -459,7 +408,9 @@ void VersionProxyModel::sourceRowsAboutToBeInserted(const QModelIndex& parent, i
beginInsertRows(parent, first, last); 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(); endInsertRows();
} }
@ -469,12 +420,12 @@ void VersionProxyModel::sourceRowsAboutToBeRemoved(const QModelIndex& parent, in
beginRemoveRows(parent, first, last); 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(); endRemoveRows();
} }
void VersionProxyModel::setCurrentVersion(const QString &version) void VersionProxyModel::setCurrentVersion(const QString& version)
{ {
m_currentVersion = version; m_currentVersion = version;
} }

View File

@ -6,64 +6,53 @@
class VersionFilterModel; class VersionFilterModel;
class VersionProxyModel: public QAbstractProxyModel class VersionProxyModel : public QAbstractProxyModel {
{
Q_OBJECT Q_OBJECT
public: public:
enum Column { Name, ParentVersion, Branch, Type, Architecture, Path, Time };
enum Column
{
Name,
ParentVersion,
Branch,
Type,
Architecture,
Path,
Time
};
typedef QHash<BaseVersionList::ModelRoles, std::shared_ptr<Filter>> FilterMap; typedef QHash<BaseVersionList::ModelRoles, std::shared_ptr<Filter>> FilterMap;
public: public:
VersionProxyModel ( QObject* parent = 0 ); VersionProxyModel(QObject* parent = 0);
virtual ~VersionProxyModel() {}; virtual ~VersionProxyModel(){};
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override; virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
virtual int rowCount(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 mapFromSource(const QModelIndex& sourceIndex) const override;
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; virtual QModelIndex mapToSource(const QModelIndex& proxyIndex) const override;
virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) 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 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 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
virtual QModelIndex parent(const QModelIndex &child) const override; virtual QModelIndex parent(const QModelIndex& child) const override;
virtual void setSourceModel(QAbstractItemModel *sourceModel) override; virtual void setSourceModel(QAbstractItemModel* sourceModel) override;
const FilterMap &filters() const; const FilterMap& filters() const;
const QString &search() const; const QString& search() const;
void setFilter(const BaseVersionList::ModelRoles column, Filter * filter); void setFilter(const BaseVersionList::ModelRoles column, Filter* filter);
void setSearch(const QString &search); void setSearch(const QString& search);
void clearFilters(); void clearFilters();
QModelIndex getRecommended() const; QModelIndex getRecommended() const;
QModelIndex getVersion(const QString & version) const; QModelIndex getVersion(const QString& version) const;
void setCurrentVersion(const QString &version); void setCurrentVersion(const QString& version);
private slots: 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 sourceAboutToBeReset();
void sourceReset(); void sourceReset();
void sourceRowsAboutToBeInserted(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 sourceRowsInserted(const QModelIndex& parent, int first, int last);
void sourceRowsAboutToBeRemoved(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 sourceRowsRemoved(const QModelIndex& parent, int first, int last);
private: private:
QList<Column> m_columns; QList<Column> m_columns;
FilterMap m_filters; FilterMap m_filters;
QString m_search; QString m_search;
BaseVersionList::RoleList roles; BaseVersionList::RoleList roles;
VersionFilterModel * filterModel; VersionFilterModel* filterModel;
bool hasRecommended = false; bool hasRecommended = false;
bool hasLatest = false; bool hasLatest = false;
QString m_currentVersion; QString m_currentVersion;

View File

@ -1,20 +1,15 @@
#pragma once #pragma once
#include <QString>
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
#include <QString>
struct WatchLock struct WatchLock {
{ WatchLock(QFileSystemWatcher* watcher, const QString& directory) : m_watcher(watcher), m_directory(directory)
WatchLock(QFileSystemWatcher * watcher, const QString& directory)
: m_watcher(watcher), m_directory(directory)
{ {
m_watcher->removePath(m_directory); m_watcher->removePath(m_directory);
} }
~WatchLock() ~WatchLock() { m_watcher->addPath(m_directory); }
{ QFileSystemWatcher* m_watcher;
m_watcher->addPath(m_directory);
}
QFileSystemWatcher * m_watcher;
QString m_directory; QString m_directory;
}; };

128
launcher/WindowsConsole.cpp Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
*
*/
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <fcntl.h>
#include <io.h>
#include <stdio.h>
#include <windows.h>
#include <iostream>
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;
}

25
launcher/WindowsConsole.h Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
*
*/
#pragma once
void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr);
bool AttachWindowsConsole();

View File

@ -37,11 +37,7 @@
#include <sys.h> #include <sys.h>
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
#ifndef WIN32_LEAN_AND_MEAN #include "WindowsConsole.h"
#define WIN32_LEAN_AND_MEAN
#endif
#include <stdio.h>
#include <windows.h>
#endif #endif
// Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header // 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 #if defined Q_OS_WIN32
// attach the parent console // attach the parent console
if (AttachConsole(ATTACH_PARENT_PROCESS)) { if (AttachWindowsConsole()) {
// 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);
consoleAttached = true; consoleAttached = true;
} }
#endif #endif
@ -188,7 +170,7 @@ void FileLinkApp::runLink()
FS::LinkResult result = { src_path, dst_path, QString::fromStdString(os_err.message()), os_err.value() }; FS::LinkResult result = { src_path, dst_path, QString::fromStdString(os_err.message()), os_err.value() };
m_path_results.append(result); m_path_results.append(result);
} else { } else {
FS::LinkResult result = { src_path, dst_path }; FS::LinkResult result = { src_path, dst_path, "", 0 };
m_path_results.append(result); m_path_results.append(result);
} }
} }
@ -248,7 +230,7 @@ void FileLinkApp::readPathPairs()
in >> numLinks; in >> numLinks;
qDebug() << "numLinks" << numLinks; qDebug() << "numLinks" << numLinks;
for (int i = 0; i < numLinks; i++) { for (quint32 i = 0; i < numLinks; i++) {
FS::LinkPair pair; FS::LinkPair pair;
in >> pair.src; in >> pair.src;
in >> pair.dst; in >> pair.dst;
@ -271,7 +253,6 @@ FileLinkApp::~FileLinkApp()
fclose(stdout); fclose(stdout);
fclose(stdin); fclose(stdin);
fclose(stderr); fclose(stderr);
FreeConsole();
} }
#endif #endif
} }

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -35,32 +36,30 @@
#include "IconList.h" #include "IconList.h"
#include <FileSystem.h> #include <FileSystem.h>
#include <QMap>
#include <QEventLoop>
#include <QMimeData>
#include <QUrl>
#include <QFileSystemWatcher>
#include <QSet>
#include <QDebug> #include <QDebug>
#include <QEventLoop>
#include <QFileSystemWatcher>
#include <QMap>
#include <QMimeData>
#include <QSet>
#include <QUrl>
#include "icons/IconUtils.h"
#define MAX_SIZE 1024 #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<QString> builtinNames; QSet<QString> builtinNames;
// add builtin icons // add builtin icons
for(auto & builtinPath: builtinPaths) for (auto& builtinPath : builtinPaths) {
{
QDir instance_icons(builtinPath); QDir instance_icons(builtinPath);
auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name); 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()); builtinNames.insert(file_info.completeBaseName());
} }
} }
for(auto & builtinName : builtinNames) for (auto& builtinName : builtinNames) {
{
addThemeIcon(builtinName); addThemeIcon(builtinName);
} }
@ -78,31 +77,27 @@ IconList::IconList(const QStringList &builtinPaths, QString path, QObject *paren
void IconList::sortIconList() void IconList::sortIconList()
{ {
qDebug() << "Sorting icon list..."; qDebug() << "Sorting icon list...";
std::sort(icons.begin(), icons.end(), [](const MMCIcon& a, const MMCIcon& b) { std::sort(icons.begin(), icons.end(), [](const MMCIcon& a, const MMCIcon& b) { return a.m_key.localeAwareCompare(b.m_key) < 0; });
return a.m_key.localeAwareCompare(b.m_key) < 0;
});
reindex(); reindex();
} }
void IconList::directoryChanged(const QString &path) void IconList::directoryChanged(const QString& path)
{ {
QDir new_dir (path); QDir new_dir(path);
if(m_dir.absolutePath() != new_dir.absolutePath()) if (m_dir.absolutePath() != new_dir.absolutePath()) {
{
m_dir.setPath(path); m_dir.setPath(path);
m_dir.refresh(); m_dir.refresh();
if(is_watching) if (is_watching)
stopWatching(); stopWatching();
startWatching(); startWatching();
} }
if(!m_dir.exists()) if (!m_dir.exists())
if(!FS::ensureFolderPathExists(m_dir.absolutePath())) if (!FS::ensureFolderPathExists(m_dir.absolutePath()))
return; return;
m_dir.refresh(); m_dir.refresh();
auto new_list = m_dir.entryList(QDir::Files, QDir::Name); auto new_list = m_dir.entryList(QDir::Files, QDir::Name);
for (auto it = new_list.begin(); it != new_list.end(); it++) for (auto it = new_list.begin(); it != new_list.end(); it++) {
{ QString& foo = (*it);
QString &foo = (*it);
foo = m_dir.filePath(foo); foo = m_dir.filePath(foo);
} }
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) #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(); auto new_set = new_list.toSet();
#endif #endif
QList<QString> current_list; QList<QString> current_list;
for (auto &it : icons) for (auto& it : icons) {
{
if (!it.has(IconType::FileBased)) if (!it.has(IconType::FileBased))
continue; continue;
current_list.push_back(it.m_images[IconType::FileBased].filename); current_list.push_back(it.m_images[IconType::FileBased].filename);
@ -129,38 +123,33 @@ void IconList::directoryChanged(const QString &path)
QSet<QString> to_add = new_set; QSet<QString> to_add = new_set;
to_add -= current_set; to_add -= current_set;
for (auto remove : to_remove) for (auto remove : to_remove) {
{
qDebug() << "Removing " << remove; qDebug() << "Removing " << remove;
QFileInfo rmfile(remove); QFileInfo rmfile(remove);
QString key = rmfile.completeBaseName(); QString key = rmfile.completeBaseName();
QString suffix = rmfile.suffix(); 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 // 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(); key = rmfile.fileName();
int idx = getIconIndex(key); int idx = getIconIndex(key);
if (idx == -1) if (idx == -1)
continue; continue;
icons[idx].remove(IconType::FileBased); icons[idx].remove(IconType::FileBased);
if (icons[idx].type() == IconType::ToBeDeleted) if (icons[idx].type() == IconType::ToBeDeleted) {
{
beginRemoveRows(QModelIndex(), idx, idx); beginRemoveRows(QModelIndex(), idx, idx);
icons.remove(idx); icons.remove(idx);
reindex(); reindex();
endRemoveRows(); endRemoveRows();
} } else {
else
{
dataChanged(index(idx), index(idx)); dataChanged(index(idx), index(idx));
} }
m_watcher->removePath(remove); m_watcher->removePath(remove);
emit iconUpdated(key); emit iconUpdated(key);
} }
for (auto add : to_add) for (auto add : to_add) {
{
qDebug() << "Adding " << add; qDebug() << "Adding " << add;
QFileInfo addfile(add); QFileInfo addfile(add);
@ -168,11 +157,10 @@ void IconList::directoryChanged(const QString &path)
QString suffix = addfile.suffix(); 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 // 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(); key = addfile.fileName();
if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased)) if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased)) {
{
m_watcher->addPath(add); m_watcher->addPath(add);
emit iconUpdated(key); emit iconUpdated(key);
} }
@ -181,7 +169,7 @@ void IconList::directoryChanged(const QString &path)
sortIconList(); sortIconList();
} }
void IconList::fileChanged(const QString &path) void IconList::fileChanged(const QString& path)
{ {
qDebug() << "Checking " << path; qDebug() << "Checking " << path;
QFileInfo checkfile(path); QFileInfo checkfile(path);
@ -200,9 +188,9 @@ void IconList::fileChanged(const QString &path)
emit iconUpdated(key); 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; return;
directoryChanged(value.toString()); directoryChanged(value.toString());
@ -213,12 +201,9 @@ void IconList::startWatching()
auto abs_path = m_dir.absolutePath(); auto abs_path = m_dir.absolutePath();
FS::ensureFolderPathExists(abs_path); FS::ensureFolderPathExists(abs_path);
is_watching = m_watcher->addPath(abs_path); is_watching = m_watcher->addPath(abs_path);
if (is_watching) if (is_watching) {
{
qDebug() << "Started watching " << abs_path; qDebug() << "Started watching " << abs_path;
} } else {
else
{
qDebug() << "Failed to start watching " << abs_path; qDebug() << "Failed to start watching " << abs_path;
} }
} }
@ -241,7 +226,11 @@ Qt::DropActions IconList::supportedDropActions() const
return Qt::CopyAction; 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) if (action == Qt::IgnoreAction)
return true; return true;
@ -250,12 +239,10 @@ bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[mayb
return false; return false;
// files dropped from outside? // files dropped from outside?
if (data->hasUrls()) if (data->hasUrls()) {
{
auto urls = data->urls(); auto urls = data->urls();
QStringList iconFiles; QStringList iconFiles;
for (auto url : urls) for (auto url : urls) {
{
// only local files may be dropped... // only local files may be dropped...
if (!url.isLocalFile()) if (!url.isLocalFile())
continue; continue;
@ -267,16 +254,13 @@ bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[mayb
return false; return false;
} }
Qt::ItemFlags IconList::flags(const QModelIndex &index) const Qt::ItemFlags IconList::flags(const QModelIndex& index) const
{ {
Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
if (index.isValid()) return Qt::ItemIsDropEnabled | defaultFlags;
return Qt::ItemIsDropEnabled | defaultFlags;
else
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()) if (!index.isValid())
return QVariant(); return QVariant();
@ -286,64 +270,49 @@ QVariant IconList::data(const QModelIndex &index, int role) const
if (row < 0 || row >= icons.size()) if (row < 0 || row >= icons.size())
return QVariant(); return QVariant();
switch (role) switch (role) {
{ case Qt::DecorationRole:
case Qt::DecorationRole: return icons[row].icon();
return icons[row].icon(); case Qt::DisplayRole:
case Qt::DisplayRole: return icons[row].name();
return icons[row].name(); case Qt::UserRole:
case Qt::UserRole: return icons[row].m_key;
return icons[row].m_key; default:
default: return QVariant();
return QVariant();
} }
} }
int IconList::rowCount(const QModelIndex &parent) const int IconList::rowCount(const QModelIndex& parent) const
{ {
return parent.isValid() ? 0 : icons.size(); return parent.isValid() ? 0 : icons.size();
} }
void IconList::installIcons(const QStringList &iconFiles) void IconList::installIcons(const QStringList& iconFiles)
{ {
for (QString file : iconFiles) for (QString file : iconFiles)
{ installIcon(file, {});
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;
}
} }
void IconList::installIcon(const QString &file, const QString &name) void IconList::installIcon(const QString& file, const QString& name)
{ {
QFileInfo fileinfo(file); QFileInfo fileinfo(file);
if(!fileinfo.isReadable() || !fileinfo.isFile()) if (!fileinfo.isReadable() || !fileinfo.isFile())
return; 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); QFile::copy(file, target);
} }
bool IconList::iconFileExists(const QString &key) const bool IconList::iconFileExists(const QString& key) const
{ {
auto iconEntry = icon(key); auto iconEntry = icon(key);
if(!iconEntry) return iconEntry && iconEntry->has(IconType::FileBased);
{
return false;
}
return iconEntry->has(IconType::FileBased);
} }
const MMCIcon *IconList::icon(const QString &key) const const MMCIcon* IconList::icon(const QString& key) const
{ {
int iconIdx = getIconIndex(key); int iconIdx = getIconIndex(key);
if (iconIdx == -1) if (iconIdx == -1)
@ -351,100 +320,84 @@ const MMCIcon *IconList::icon(const QString &key) const
return &icons[iconIdx]; return &icons[iconIdx];
} }
bool IconList::deleteIcon(const QString &key) bool IconList::deleteIcon(const QString& key)
{ {
if (!iconFileExists(key)) return iconFileExists(key) && QFile::remove(icon(key)->getFilePath());
return false;
return QFile::remove(icon(key)->getFilePath());
} }
bool IconList::trashIcon(const QString &key) bool IconList::trashIcon(const QString& key)
{ {
if (!iconFileExists(key)) return iconFileExists(key) && FS::trash(icon(key)->getFilePath(), nullptr);
return false;
return FS::trash(icon(key)->getFilePath(), nullptr);
} }
bool IconList::addThemeIcon(const QString& key) bool IconList::addThemeIcon(const QString& key)
{ {
auto iter = name_index.find(key); auto iter = name_index.find(key);
if (iter != name_index.end()) if (iter != name_index.end()) {
{ auto& oldOne = icons[*iter];
auto &oldOne = icons[*iter];
oldOne.replace(Builtin, key); oldOne.replace(Builtin, key);
dataChanged(index(*iter), index(*iter)); dataChanged(index(*iter), index(*iter));
return true; return true;
} }
else // add a new icon
beginInsertRows(QModelIndex(), icons.size(), icons.size());
{ {
// add a new icon MMCIcon mmc_icon;
beginInsertRows(QModelIndex(), icons.size(), icons.size()); mmc_icon.m_name = key;
{ mmc_icon.m_key = key;
MMCIcon mmc_icon; mmc_icon.replace(Builtin, key);
mmc_icon.m_name = key; icons.push_back(mmc_icon);
mmc_icon.m_key = key; name_index[key] = icons.size() - 1;
mmc_icon.replace(Builtin, key);
icons.push_back(mmc_icon);
name_index[key] = icons.size() - 1;
}
endInsertRows();
return true;
} }
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? // replace the icon even? is the input valid?
QIcon icon(path); QIcon icon(path);
if (icon.isNull()) if (icon.isNull())
return false; return false;
auto iter = name_index.find(key); auto iter = name_index.find(key);
if (iter != name_index.end()) if (iter != name_index.end()) {
{ auto& oldOne = icons[*iter];
auto &oldOne = icons[*iter];
oldOne.replace(type, icon, path); oldOne.replace(type, icon, path);
dataChanged(index(*iter), index(*iter)); dataChanged(index(*iter), index(*iter));
return true; return true;
} }
else // add a new icon
beginInsertRows(QModelIndex(), icons.size(), icons.size());
{ {
// add a new icon MMCIcon mmc_icon;
beginInsertRows(QModelIndex(), icons.size(), icons.size()); mmc_icon.m_name = name;
{ mmc_icon.m_key = key;
MMCIcon mmc_icon; mmc_icon.replace(type, icon, path);
mmc_icon.m_name = name; icons.push_back(mmc_icon);
mmc_icon.m_key = key; name_index[key] = icons.size() - 1;
mmc_icon.replace(type, icon, path);
icons.push_back(mmc_icon);
name_index[key] = icons.size() - 1;
}
endInsertRows();
return true;
} }
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 icon = getIcon(key);
auto pixmap = icon.pixmap(128, 128); auto pixmap = icon.pixmap(128, 128);
pixmap.save(path, format); pixmap.save(path, format);
} }
void IconList::reindex() void IconList::reindex()
{ {
name_index.clear(); name_index.clear();
int i = 0; int i = 0;
for (auto &iter : icons) for (auto& iter : icons) {
{
name_index[iter.m_key] = i; name_index[iter.m_key] = i;
i++; i++;
} }
} }
QIcon IconList::getIcon(const QString &key) const QIcon IconList::getIcon(const QString& key) const
{ {
int icon_index = getIconIndex(key); int icon_index = getIconIndex(key);
@ -459,7 +412,7 @@ QIcon IconList::getIcon(const QString &key) const
return QIcon(); 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); auto iter = name_index.find(key == "default" ? "grass" : key);
if (iter != name_index.end()) if (iter != name_index.end())
@ -472,5 +425,3 @@ QString IconList::getDirectory() const
{ {
return m_dir.absolutePath(); return m_dir.absolutePath();
} }
//#include "IconList.moc"

View File

@ -1,24 +1,43 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * This program is free software: you can redistribute it and/or modify
* you may not use this file except in compliance with the License. * it under the terms of the GNU General Public License as published by
* You may obtain a copy of the License at * 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 * You should have received a copy of the GNU General Public License
* distributed under the License is distributed on an "AS IS" BASIS, * along with this program. If not, see <https://www.gnu.org/licenses/>.
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and * This file incorporates work covered by the following copyright and
* limitations under the License. * 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 #pragma once
#include <QMutex>
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QFile>
#include <QDir> #include <QDir>
#include <QFile>
#include <QMutex>
#include <QtGui/QIcon> #include <QtGui/QIcon>
#include <memory> #include <memory>
@ -29,58 +48,58 @@
class QFileSystemWatcher; class QFileSystemWatcher;
class IconList : public QAbstractListModel class IconList : public QAbstractListModel {
{
Q_OBJECT Q_OBJECT
public: public:
explicit IconList(const QStringList &builtinPaths, QString path, QObject *parent = 0); explicit IconList(const QStringList& builtinPaths, QString path, QObject* parent = 0);
virtual ~IconList() {}; virtual ~IconList(){};
QIcon getIcon(const QString &key) const; QIcon getIcon(const QString& key) const;
int getIconIndex(const QString &key) const; int getIconIndex(const QString& key) const;
QString getDirectory() const; QString getDirectory() const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
virtual QStringList mimeTypes() const override; virtual QStringList mimeTypes() const override;
virtual Qt::DropActions supportedDropActions() 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 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 Qt::ItemFlags flags(const QModelIndex& index) const override;
bool addThemeIcon(const QString &key); bool addThemeIcon(const QString& key);
bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type); 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; void saveIcon(const QString& key, const QString& path, const char* format) const;
bool deleteIcon(const QString &key); bool deleteIcon(const QString& key);
bool trashIcon(const QString &key); bool trashIcon(const QString& key);
bool iconFileExists(const QString &key) const; bool iconFileExists(const QString& key) const;
void installIcons(const QStringList &iconFiles); void installIcons(const QStringList& iconFiles);
void installIcon(const QString &file, const QString &name); 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 startWatching();
void stopWatching(); void stopWatching();
signals: signals:
void iconUpdated(QString key); void iconUpdated(QString key);
private: private:
// hide copy constructor // hide copy constructor
IconList(const IconList &) = delete; IconList(const IconList&) = delete;
// hide assign op // hide assign op
IconList &operator=(const IconList &) = delete; IconList& operator=(const IconList&) = delete;
void reindex(); void reindex();
void sortIconList(); void sortIconList();
public slots: public slots:
void directoryChanged(const QString &path); void directoryChanged(const QString& path);
protected slots: protected slots:
void fileChanged(const QString &path); void fileChanged(const QString& path);
void SettingChanged(const Setting & setting, QVariant value); void SettingChanged(const Setting& setting, QVariant value);
private:
private:
shared_qobject_ptr<QFileSystemWatcher> m_watcher; shared_qobject_ptr<QFileSystemWatcher> m_watcher;
bool is_watching; bool is_watching;
QMap<QString, int> name_index; QMap<QString, int> name_index;

View File

@ -1,25 +1,51 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "IconUtils.h" #include "IconUtils.h"
#include "FileSystem.h"
#include <QDirIterator> #include <QDirIterator>
#include "FileSystem.h"
#include <array>
namespace { namespace {
std::array<const char *, 6> validIconExtensions = {{ static const QStringList validIconExtensions = { { "svg", "png", "ico", "gif", "jpg", "jpeg" } };
"svg",
"png",
"ico",
"gif",
"jpg",
"jpeg"
}};
} }
namespace IconUtils{ namespace IconUtils {
QString findBestIconIn(const QString &folder, const QString & iconKey) { QString findBestIconIn(const QString& folder, const QString& iconKey)
int best_found = validIconExtensions.size(); {
QString best_filename; QString best_filename;
QDirIterator it(folder, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::NoIteratorFlags); QDirIterator it(folder, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::NoIteratorFlags);
@ -27,36 +53,20 @@ QString findBestIconIn(const QString &folder, const QString & iconKey) {
it.next(); it.next();
auto fileInfo = it.fileInfo(); auto fileInfo = it.fileInfo();
if(fileInfo.completeBaseName() != iconKey) if (fileInfo.completeBaseName() == iconKey && isIconSuffix(fileInfo.suffix()))
continue; return fileInfo.absoluteFilePath();
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();
}
}
} }
return FS::PathCombine(folder, best_filename); return {};
} }
QString getIconFilter() { QString getIconFilter()
QString out; {
QTextStream stream(&out); return "(*." + validIconExtensions.join(" *.") + ")";
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;
} }
bool isIconSuffix(QString suffix)
{
return validIconExtensions.contains(suffix);
} }
} // namespace IconUtils

View File

@ -1,3 +1,38 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once #pragma once
#include <QString> #include <QString>
@ -5,9 +40,10 @@
namespace IconUtils { namespace IconUtils {
// Given a folder and an icon key, find 'best' of the icons with the given key in there and return its path // 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 // Get icon file type filter for file browser dialogs
QString getIconFilter(); QString getIconFilter();
} bool isIconSuffix(QString suffix);
} // namespace IconUtils

Some files were not shown because too many files have changed in this diff Show More