Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into update_file
This commit is contained in:
commit
044ce4df32
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
- name: Create backport PRs
|
- name: Create backport PRs
|
||||||
uses: korthout/backport-action@v1.3.1
|
uses: korthout/backport-action@v1.4.0
|
||||||
with:
|
with:
|
||||||
# Config README: https://github.com/korthout/backport-action#backport-action
|
# Config README: https://github.com/korthout/backport-action#backport-action
|
||||||
pull_description: |-
|
pull_description: |-
|
||||||
|
54
.github/workflows/build.yml
vendored
54
.github/workflows/build.yml
vendored
@ -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:
|
||||||
@ -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 }}
|
||||||
|
|
||||||
@ -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,7 +493,7 @@ 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/
|
||||||
@ -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
|
||||||
##
|
##
|
||||||
@ -563,6 +600,13 @@ jobs:
|
|||||||
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 != ''
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
|
9
.github/workflows/trigger_release.yml
vendored
9
.github/workflows/trigger_release.yml
vendored
@ -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
|
||||||
|
2
.github/workflows/update-flake.yml
vendored
2
.github/workflows/update-flake.yml
vendored
@ -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
|
||||||
|
@ -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)
|
||||||
|
152
cmake/CompilerWarnings.cmake
Normal file
152
cmake/CompilerWarnings.cmake
Normal 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()
|
@ -67,5 +67,16 @@
|
|||||||
<string>Alternate</string>
|
<string>Alternate</string>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>Curseforge</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>curseforge</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
24
flake.lock
generated
24
flake.lock
generated
@ -21,11 +21,11 @@
|
|||||||
"nixpkgs-lib": "nixpkgs-lib"
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1688466019,
|
"lastModified": 1690933134,
|
||||||
"narHash": "sha256-VeM2akYrBYMsb4W/MmBo1zmaMfgbL4cH3Pu8PGyIwJ0=",
|
"narHash": "sha256-ab989mN63fQZBFrkk4Q8bYxQCktuHmBIBqUG1jl6/FQ=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "8e8d955c22df93dbe24f19ea04f47a74adbdc5ec",
|
"rev": "59cf3f1447cfc75087e7273b04b31e689a8599fb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -91,11 +91,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1690630721,
|
"lastModified": 1691853136,
|
||||||
"narHash": "sha256-Y04onHyBQT4Erfr2fc82dbJTfXGYrf4V0ysLUYnPOP8=",
|
"narHash": "sha256-wTzDsRV4HN8A2Sl0SVQY0q8ILs90CD43Ha//7gNZE+E=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "d2b52322f35597c62abf56de91b0236746b2a03d",
|
"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": 1690628027,
|
"lastModified": 1691747570,
|
||||||
"narHash": "sha256-OTSbA2hM6VmxyZ/4siYPANffMBzIsKu04GLjXcv8ST0=",
|
"narHash": "sha256-J3fnIwJtHVQ0tK2JMBv4oAmII+1mCdXdpeCxtIsrL2A=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "pre-commit-hooks.nix",
|
"repo": "pre-commit-hooks.nix",
|
||||||
"rev": "1e2443dd3f669eb65433b2fc26a3065e05a7dc9c",
|
"rev": "c5ac3aa3324bd8aebe8622a3fc92eeb3975d317a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -136,11 +136,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#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
|
||||||
|
|
||||||
#define STRINGIFY(x) #x
|
#define STRINGIFY(x) #x
|
||||||
@ -172,22 +168,8 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt
|
|||||||
Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||||
{
|
{
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
// attach the parent console
|
// attach the parent console if stdout not already captured
|
||||||
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
|
||||||
@ -212,8 +194,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
|
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
|
||||||
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
|
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
|
||||||
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
|
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
|
||||||
{ { "I", "import" }, "Import instance from specified zip (local path or URL)", "file" },
|
{ { "I", "import" }, "Import instance or resource from specified local path or URL", "url" },
|
||||||
{ "show", "Opens the window for the specified instance (by instance ID)", "show" } });
|
{ "show", "Opens the window for the specified instance (by instance ID)", "show" } });
|
||||||
|
// Has to be positional for some OS to handle that properly
|
||||||
|
parser.addPositionalArgument("URL", "Import the resource(s) at the given URL(s) (same as -I / --import)", "[URL...]");
|
||||||
|
|
||||||
parser.addHelpOption();
|
parser.addHelpOption();
|
||||||
parser.addVersionOption();
|
parser.addVersionOption();
|
||||||
|
|
||||||
@ -226,13 +211,13 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
m_instanceIdToShowWindowOf = parser.value("show");
|
m_instanceIdToShowWindowOf = parser.value("show");
|
||||||
|
|
||||||
for (auto zip_path : parser.values("import")) {
|
for (auto url : parser.values("import")) {
|
||||||
m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath()));
|
m_urlsToImport.append(normalizeImportUrl(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// treat unspecified positional arguments as import urls
|
// treat unspecified positional arguments as import urls
|
||||||
for (auto zip_path : parser.positionalArguments()) {
|
for (auto url : parser.positionalArguments()) {
|
||||||
m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath()));
|
m_urlsToImport.append(normalizeImportUrl(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// error if --launch is missing with --server or --profile
|
// error if --launch is missing with --server or --profile
|
||||||
@ -331,11 +316,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
activate.command = "activate";
|
activate.command = "activate";
|
||||||
m_peerInstance->sendMessage(activate.serialize(), timeout);
|
m_peerInstance->sendMessage(activate.serialize(), timeout);
|
||||||
|
|
||||||
if (!m_zipsToImport.isEmpty()) {
|
if (!m_urlsToImport.isEmpty()) {
|
||||||
for (auto zip_url : m_zipsToImport) {
|
for (auto url : m_urlsToImport) {
|
||||||
ApplicationMessage import;
|
ApplicationMessage import;
|
||||||
import.command = "import";
|
import.command = "import";
|
||||||
import.args.insert("path", zip_url.toString());
|
import.args.insert("url", url.toString());
|
||||||
m_peerInstance->sendMessage(import.serialize(), timeout);
|
m_peerInstance->sendMessage(import.serialize(), timeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -510,7 +495,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
m_settings.reset(new INISettingsObject({ BuildConfig.LAUNCHER_CONFIGFILE, "polymc.cfg", "multimc.cfg" }, this));
|
m_settings.reset(new INISettingsObject({ BuildConfig.LAUNCHER_CONFIGFILE, "polymc.cfg", "multimc.cfg" }, this));
|
||||||
|
|
||||||
// Theming
|
// Theming
|
||||||
m_settings->registerSetting("IconTheme", QString("pe_colored"));
|
m_settings->registerSetting("IconTheme", QString());
|
||||||
m_settings->registerSetting("ApplicationTheme", QString());
|
m_settings->registerSetting("ApplicationTheme", QString());
|
||||||
m_settings->registerSetting("BackgroundCat", QString("kitteh"));
|
m_settings->registerSetting("BackgroundCat", QString("kitteh"));
|
||||||
|
|
||||||
@ -772,7 +757,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Themes
|
// Themes
|
||||||
m_themeManager = std::make_unique<ThemeManager>(m_mainWindow);
|
m_themeManager = std::make_unique<ThemeManager>();
|
||||||
|
|
||||||
// initialize and load all instances
|
// initialize and load all instances
|
||||||
{
|
{
|
||||||
@ -858,14 +843,13 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
applyCurrentlySelectedTheme(true);
|
|
||||||
|
|
||||||
updateCapabilities();
|
updateCapabilities();
|
||||||
|
|
||||||
if (createSetupWizard()) {
|
if (createSetupWizard()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_themeManager->applyCurrentlySelectedTheme(true);
|
||||||
performMainStartupAction();
|
performMainStartupAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -891,10 +875,20 @@ bool Application::createSetupWizard()
|
|||||||
}();
|
}();
|
||||||
bool languageRequired = settings()->get("Language").toString().isEmpty();
|
bool languageRequired = settings()->get("Language").toString().isEmpty();
|
||||||
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
||||||
bool themeInterventionRequired = settings()->get("ApplicationTheme") == "";
|
bool validWidgets = m_themeManager->isValidApplicationTheme(settings()->get("ApplicationTheme").toString());
|
||||||
|
bool validIcons = m_themeManager->isValidIconTheme(settings()->get("IconTheme").toString());
|
||||||
|
bool themeInterventionRequired = !validWidgets || !validIcons;
|
||||||
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
|
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
|
||||||
|
|
||||||
if (wizardRequired) {
|
if (wizardRequired) {
|
||||||
|
// set default theme after going into theme wizard
|
||||||
|
if (!validIcons)
|
||||||
|
settings()->set("IconTheme", QString("pe_colored"));
|
||||||
|
if (!validWidgets)
|
||||||
|
settings()->set("ApplicationTheme", QString("system"));
|
||||||
|
|
||||||
|
m_themeManager->applyCurrentlySelectedTheme(true);
|
||||||
|
|
||||||
m_setupWizard = new SetupWizard(nullptr);
|
m_setupWizard = new SetupWizard(nullptr);
|
||||||
if (languageRequired) {
|
if (languageRequired) {
|
||||||
m_setupWizard->addPage(new LanguageWizardPage(m_setupWizard));
|
m_setupWizard->addPage(new LanguageWizardPage(m_setupWizard));
|
||||||
@ -909,9 +903,9 @@ bool Application::createSetupWizard()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (themeInterventionRequired) {
|
if (themeInterventionRequired) {
|
||||||
settings()->set("ApplicationTheme", QString("system")); // set default theme after going into theme wizard
|
|
||||||
m_setupWizard->addPage(new ThemeWizardPage(m_setupWizard));
|
m_setupWizard->addPage(new ThemeWizardPage(m_setupWizard));
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished);
|
connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished);
|
||||||
m_setupWizard->show();
|
m_setupWizard->show();
|
||||||
return true;
|
return true;
|
||||||
@ -987,9 +981,9 @@ void Application::performMainStartupAction()
|
|||||||
showMainWindow(false);
|
showMainWindow(false);
|
||||||
qDebug() << "<> Main window shown.";
|
qDebug() << "<> Main window shown.";
|
||||||
}
|
}
|
||||||
if (!m_zipsToImport.isEmpty()) {
|
if (!m_urlsToImport.isEmpty()) {
|
||||||
qDebug() << "<> Importing from zip:" << m_zipsToImport;
|
qDebug() << "<> Importing from url:" << m_urlsToImport;
|
||||||
m_mainWindow->processURLs(m_zipsToImport);
|
m_mainWindow->processURLs(m_urlsToImport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1031,12 +1025,12 @@ void Application::messageReceived(const QByteArray& message)
|
|||||||
if (command == "activate") {
|
if (command == "activate") {
|
||||||
showMainWindow();
|
showMainWindow();
|
||||||
} else if (command == "import") {
|
} else if (command == "import") {
|
||||||
QString path = received.args["path"];
|
QString url = received.args["url"];
|
||||||
if (path.isEmpty()) {
|
if (url.isEmpty()) {
|
||||||
qWarning() << "Received" << command << "message without a zip path/URL.";
|
qWarning() << "Received" << command << "message without a zip path/URL.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_mainWindow->processURLs({ QUrl::fromLocalFile(QFileInfo(path).absoluteFilePath()) });
|
m_mainWindow->processURLs({ normalizeImportUrl(url) });
|
||||||
} else if (command == "launch") {
|
} else if (command == "launch") {
|
||||||
QString id = received.args["id"];
|
QString id = received.args["id"];
|
||||||
QString server = received.args["server"];
|
QString server = received.args["server"];
|
||||||
@ -1088,26 +1082,6 @@ std::shared_ptr<JavaInstallList> Application::javalist()
|
|||||||
return m_javalist;
|
return m_javalist;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ITheme*> Application::getValidApplicationThemes()
|
|
||||||
{
|
|
||||||
return m_themeManager->getValidApplicationThemes();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::applyCurrentlySelectedTheme(bool initial)
|
|
||||||
{
|
|
||||||
m_themeManager->applyCurrentlySelectedTheme(initial);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::setApplicationTheme(const QString& name)
|
|
||||||
{
|
|
||||||
m_themeManager->setApplicationTheme(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::setIconTheme(const QString& name)
|
|
||||||
{
|
|
||||||
m_themeManager->setIconTheme(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon Application::getThemedIcon(const QString& name)
|
QIcon Application::getThemedIcon(const QString& name)
|
||||||
{
|
{
|
||||||
if (name == "logo") {
|
if (name == "logo") {
|
||||||
@ -1116,16 +1090,6 @@ QIcon Application::getThemedIcon(const QString& name)
|
|||||||
return QIcon::fromTheme(name);
|
return QIcon::fromTheme(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<CatPack*> Application::getValidCatPacks()
|
|
||||||
{
|
|
||||||
return m_themeManager->getValidCatPacks();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Application::getCatPack(QString catName)
|
|
||||||
{
|
|
||||||
return m_themeManager->getCatPack(catName);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Application::openJsonEditor(const QString& filename)
|
bool Application::openJsonEditor(const QString& filename)
|
||||||
{
|
{
|
||||||
const QString file = QDir::current().absoluteFilePath(filename);
|
const QString file = QDir::current().absoluteFilePath(filename);
|
||||||
@ -1629,3 +1593,13 @@ void Application::triggerUpdateCheck()
|
|||||||
qDebug() << "Updater not available.";
|
qDebug() << "Updater not available.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QUrl Application::normalizeImportUrl(QString const& url)
|
||||||
|
{
|
||||||
|
auto local_file = QFileInfo(url);
|
||||||
|
if (local_file.exists()) {
|
||||||
|
return QUrl::fromLocalFile(local_file.absoluteFilePath());
|
||||||
|
} else {
|
||||||
|
return QUrl::fromUserInput(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -71,6 +71,7 @@ class TranslationsModel;
|
|||||||
class ITheme;
|
class ITheme;
|
||||||
class MCEditTool;
|
class MCEditTool;
|
||||||
class ThemeManager;
|
class ThemeManager;
|
||||||
|
class IconTheme;
|
||||||
|
|
||||||
namespace Meta {
|
namespace Meta {
|
||||||
class Index;
|
class Index;
|
||||||
@ -109,17 +110,7 @@ class Application : public QApplication {
|
|||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
QList<ITheme*> getValidApplicationThemes();
|
|
||||||
|
|
||||||
void setApplicationTheme(const QString& name);
|
|
||||||
|
|
||||||
QList<CatPack*> getValidCatPacks();
|
|
||||||
|
|
||||||
QString getCatPack(QString catName = "");
|
|
||||||
|
|
||||||
shared_qobject_ptr<ExternalUpdater> updater() { return m_updater; }
|
shared_qobject_ptr<ExternalUpdater> updater() { return m_updater; }
|
||||||
|
|
||||||
@ -186,6 +177,8 @@ class Application : public QApplication {
|
|||||||
|
|
||||||
int suitableMaxMem();
|
int suitableMaxMem();
|
||||||
|
|
||||||
|
QUrl normalizeImportUrl(QString const& url);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void updateAllowedChanged(bool status);
|
void updateAllowedChanged(bool status);
|
||||||
void globalSettingsAboutToOpen();
|
void globalSettingsAboutToOpen();
|
||||||
@ -288,7 +281,7 @@ class Application : public QApplication {
|
|||||||
QString m_serverToJoin;
|
QString m_serverToJoin;
|
||||||
QString m_profileToUse;
|
QString m_profileToUse;
|
||||||
bool m_liveCheck = false;
|
bool m_liveCheck = false;
|
||||||
QList<QUrl> m_zipsToImport;
|
QList<QUrl> m_urlsToImport;
|
||||||
QString m_instanceIdToShowWindowOf;
|
QString m_instanceIdToShowWindowOf;
|
||||||
std::unique_ptr<QFile> logFile;
|
std::unique_ptr<QFile> logFile;
|
||||||
};
|
};
|
||||||
|
@ -86,7 +86,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||||||
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) { return level; };
|
virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString& line, MessageLevel::Enum level) { return level; }
|
||||||
|
|
||||||
virtual QStringList extraArguments();
|
virtual QStringList extraArguments();
|
||||||
|
|
||||||
@ -267,7 +267,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||||||
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; }
|
||||||
|
@ -43,9 +43,8 @@ class BaseVersion {
|
|||||||
* 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) { return name() < a.name(); };
|
virtual bool operator>(BaseVersion& a) { return name() > a.name(); }
|
||||||
virtual bool operator>(BaseVersion& a) { return name() > a.name(); };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(BaseVersion::Ptr)
|
Q_DECLARE_METATYPE(BaseVersion::Ptr)
|
||||||
|
@ -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
|
||||||
@ -566,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
|
||||||
@ -762,6 +774,8 @@ 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.cpp
|
||||||
@ -962,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
|
||||||
@ -1026,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
|
||||||
@ -1109,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
|
||||||
@ -1195,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
|
||||||
|
@ -96,12 +96,12 @@ 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 = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); };
|
auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); };
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -474,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; }
|
||||||
|
|
||||||
@ -491,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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,6 +16,12 @@ bool ExactFilter::accepts(const QString& value)
|
|||||||
return value == pattern;
|
return value == pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExactIfPresentFilter::ExactIfPresentFilter(const QString& pattern) : pattern(pattern) {}
|
||||||
|
bool ExactIfPresentFilter::accepts(const QString& value)
|
||||||
|
{
|
||||||
|
return value.isEmpty() || value == pattern;
|
||||||
|
}
|
||||||
|
|
||||||
RegexpFilter::RegexpFilter(const QString& regexp, bool invert) : invert(invert)
|
RegexpFilter::RegexpFilter(const QString& regexp, bool invert) : invert(invert)
|
||||||
{
|
{
|
||||||
pattern.setPattern(regexp);
|
pattern.setPattern(regexp);
|
||||||
|
@ -29,6 +29,16 @@ class ExactFilter : public Filter {
|
|||||||
QString pattern;
|
QString pattern;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ExactIfPresentFilter : public Filter {
|
||||||
|
public:
|
||||||
|
ExactIfPresentFilter(const QString& pattern);
|
||||||
|
~ExactIfPresentFilter() override = default;
|
||||||
|
bool accepts(const QString& value) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString pattern;
|
||||||
|
};
|
||||||
|
|
||||||
class RegexpFilter : public Filter {
|
class RegexpFilter : public Filter {
|
||||||
public:
|
public:
|
||||||
RegexpFilter(const QString& regexp, bool invert);
|
RegexpFilter(const QString& regexp, bool invert);
|
||||||
|
@ -50,6 +50,9 @@
|
|||||||
#include "modplatform/technic/TechnicPackProcessor.h"
|
#include "modplatform/technic/TechnicPackProcessor.h"
|
||||||
|
|
||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
|
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -88,24 +91,26 @@ void InstanceImportTask::executeTask()
|
|||||||
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
||||||
m_downloadRequired = true;
|
m_downloadRequired = true;
|
||||||
|
|
||||||
const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path());
|
downloadFromUrl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceImportTask::downloadFromUrl()
|
||||||
|
{
|
||||||
|
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
|
||||||
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
||||||
entry->setStale(true);
|
entry->setStale(true);
|
||||||
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));
|
||||||
|
m_archivePath = entry->getFullPath();
|
||||||
|
|
||||||
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::propagateStepProgress);
|
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);
|
||||||
|
|
||||||
m_filesNetJob->start();
|
m_filesNetJob->start();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceImportTask::downloadSucceeded()
|
void InstanceImportTask::downloadSucceeded()
|
||||||
{
|
{
|
||||||
|
@ -101,4 +101,5 @@ class InstanceImportTask : public InstanceTask {
|
|||||||
|
|
||||||
// FIXME: nuke
|
// FIXME: nuke
|
||||||
QWidget* m_parent;
|
QWidget* m_parent;
|
||||||
|
void downloadFromUrl();
|
||||||
};
|
};
|
||||||
|
@ -96,7 +96,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;
|
||||||
@ -104,7 +108,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;
|
||||||
@ -751,7 +759,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) {
|
||||||
@ -789,7 +797,7 @@ class InstanceStaging : public Task {
|
|||||||
, m_groupName(std::move(groupName))
|
, 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);
|
||||||
@ -797,7 +805,7 @@ class InstanceStaging : public Task {
|
|||||||
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::propagateStepProgress);
|
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(){};
|
||||||
@ -819,7 +827,7 @@ class InstanceStaging : public Task {
|
|||||||
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())) {
|
||||||
|
@ -65,13 +65,8 @@ 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;
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ class NullInstance : public BaseInstance {
|
|||||||
QSet<QString> traits() const override { return {}; };
|
QSet<QString> traits() const override { return {}; };
|
||||||
QString instanceConfigFolder() const override { return instanceRoot(); };
|
QString instanceConfigFolder() const override { return instanceRoot(); };
|
||||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override { return nullptr; }
|
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override { return nullptr; }
|
||||||
shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override { return nullptr; }
|
shared_qobject_ptr<Task> createUpdateTask([[maybe_unused]] Net::Mode mode) override { return nullptr; }
|
||||||
QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); }
|
QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); }
|
||||||
QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); }
|
QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); }
|
||||||
QMap<QString, QString> getVariables() override { return QMap<QString, QString>(); }
|
QMap<QString, QString> getVariables() override { return QMap<QString, QString>(); }
|
||||||
|
@ -12,7 +12,7 @@ struct PatchProblem {
|
|||||||
|
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
@ -90,7 +90,7 @@ 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));
|
||||||
}
|
}
|
||||||
|
@ -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,7 +53,7 @@ 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::propagateStepProgress);
|
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propagateStepProgress);
|
||||||
|
@ -35,6 +35,11 @@ QPixmap getFaceFromCache(QString username, int height, int width)
|
|||||||
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));
|
||||||
|
@ -16,6 +16,8 @@ class Usable {
|
|||||||
friend class UseLock;
|
friend class UseLock;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
virtual ~Usable() {}
|
||||||
|
|
||||||
std::size_t useCount() const { return m_useCount; }
|
std::size_t useCount() const { return m_useCount; }
|
||||||
bool isInUse() const { return m_useCount > 0; }
|
bool isInUse() const { return m_useCount > 0; }
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -194,12 +194,12 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
|
|||||||
switch (column) {
|
switch (column) {
|
||||||
case Name: {
|
case Name: {
|
||||||
if (hasRecommended) {
|
if (hasRecommended) {
|
||||||
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
|
auto recommenced = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
|
||||||
if (value.toBool()) {
|
if (recommenced.toBool()) {
|
||||||
return APPLICATION->getThemedIcon("star");
|
return APPLICATION->getThemedIcon("star");
|
||||||
} else if (hasLatest) {
|
} else if (hasLatest) {
|
||||||
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
|
auto latest = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
|
||||||
if (value.toBool()) {
|
if (latest.toBool()) {
|
||||||
return APPLICATION->getThemedIcon("bug");
|
return APPLICATION->getThemedIcon("bug");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -228,7 +228,7 @@ 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();
|
||||||
}
|
}
|
||||||
@ -408,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();
|
||||||
}
|
}
|
||||||
@ -418,7 +420,7 @@ 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();
|
||||||
}
|
}
|
||||||
|
128
launcher/WindowsConsole.cpp
Normal file
128
launcher/WindowsConsole.cpp
Normal 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
25
launcher/WindowsConsole.h
Normal 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();
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - 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
|
||||||
@ -42,6 +43,7 @@
|
|||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include "icons/IconUtils.h"
|
||||||
|
|
||||||
#define MAX_SIZE 1024
|
#define MAX_SIZE 1024
|
||||||
|
|
||||||
@ -128,7 +130,7 @@ void IconList::directoryChanged(const QString& path)
|
|||||||
|
|
||||||
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);
|
||||||
@ -155,7 +157,7 @@ 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)) {
|
||||||
@ -255,9 +257,6 @@ bool IconList::dropMimeData(const QMimeData* data,
|
|||||||
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;
|
|
||||||
else
|
|
||||||
return Qt::ItemIsDropEnabled | defaultFlags;
|
return Qt::ItemIsDropEnabled | defaultFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,19 +289,8 @@ int IconList::rowCount(const QModelIndex& parent) const
|
|||||||
|
|
||||||
void IconList::installIcons(const QStringList& iconFiles)
|
void IconList::installIcons(const QStringList& iconFiles)
|
||||||
{
|
{
|
||||||
for (QString file : iconFiles) {
|
for (QString file : iconFiles)
|
||||||
QFileInfo fileinfo(file);
|
installIcon(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)
|
||||||
@ -311,18 +299,17 @@ void IconList::installIcon(const QString& file, const QString& name)
|
|||||||
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
|
||||||
@ -335,18 +322,12 @@ const MMCIcon* IconList::icon(const QString& key) const
|
|||||||
|
|
||||||
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)
|
||||||
@ -357,7 +338,7 @@ bool IconList::addThemeIcon(const QString& key)
|
|||||||
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
|
// add a new icon
|
||||||
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
||||||
{
|
{
|
||||||
@ -371,7 +352,6 @@ bool IconList::addThemeIcon(const QString& key)
|
|||||||
endInsertRows();
|
endInsertRows();
|
||||||
return true;
|
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)
|
||||||
{
|
{
|
||||||
@ -385,7 +365,7 @@ bool IconList::addIcon(const QString& key, const QString& name, const QString& p
|
|||||||
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
|
// add a new icon
|
||||||
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
||||||
{
|
{
|
||||||
@ -399,7 +379,6 @@ bool IconList::addIcon(const QString& key, const QString& name, const QString& p
|
|||||||
endInsertRows();
|
endInsertRows();
|
||||||
return true;
|
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
|
||||||
{
|
{
|
||||||
@ -446,5 +425,3 @@ QString IconList::getDirectory() const
|
|||||||
{
|
{
|
||||||
return m_dir.absolutePath();
|
return m_dir.absolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
//#include "IconList.moc"
|
|
||||||
|
@ -1,4 +1,24 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
// 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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -12,7 +32,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
|
@ -1,19 +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 <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::array<const char*, 6> validIconExtensions = { { "svg", "png", "ico", "gif", "jpg", "jpeg" } };
|
static const QStringList validIconExtensions = { { "svg", "png", "ico", "gif", "jpg", "jpeg" } };
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace IconUtils {
|
namespace IconUtils {
|
||||||
|
|
||||||
QString findBestIconIn(const QString& folder, const QString& iconKey)
|
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);
|
||||||
@ -21,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 {};
|
||||||
}
|
|
||||||
return FS::PathCombine(folder, best_filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString getIconFilter()
|
QString getIconFilter()
|
||||||
{
|
{
|
||||||
QString out;
|
return "(*." + validIconExtensions.join(" *.") + ")";
|
||||||
QTextStream stream(&out);
|
|
||||||
stream << '(';
|
|
||||||
for (size_t i = 0; i < validIconExtensions.size() - 1; i++) {
|
|
||||||
if (i > 0) {
|
|
||||||
stream << " ";
|
|
||||||
}
|
}
|
||||||
stream << "*." << validIconExtensions[i];
|
|
||||||
}
|
bool isIconSuffix(QString suffix)
|
||||||
stream << " *." << validIconExtensions[validIconExtensions.size() - 1];
|
{
|
||||||
stream << ')';
|
return validIconExtensions.contains(suffix);
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace IconUtils
|
} // namespace IconUtils
|
||||||
|
@ -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>
|
||||||
@ -10,4 +45,5 @@ 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
|
} // namespace IconUtils
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - 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
|
||||||
@ -50,8 +51,8 @@ IconType operator--(IconType& t, int)
|
|||||||
case IconType::FileBased:
|
case IconType::FileBased:
|
||||||
t = IconType::Transient;
|
t = IconType::Transient;
|
||||||
break;
|
break;
|
||||||
default: {
|
default:
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,24 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
// 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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -12,7 +32,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
@ -14,7 +14,7 @@ class JavaVersion {
|
|||||||
friend class JavaVersionTest;
|
friend class JavaVersionTest;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
JavaVersion(){};
|
JavaVersion() {}
|
||||||
JavaVersion(const QString& rhs);
|
JavaVersion(const QString& rhs);
|
||||||
|
|
||||||
JavaVersion& operator=(const QString& rhs);
|
JavaVersion& operator=(const QString& rhs);
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
#include "BaseEntity.h"
|
#include "BaseEntity.h"
|
||||||
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
#include "net/Download.h"
|
#include "net/ApiDownload.h"
|
||||||
#include "net/HttpMetaCache.h"
|
#include "net/HttpMetaCache.h"
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ class ParsingValidator : public Net::Validator {
|
|||||||
bool init(QNetworkRequest&) override { return true; }
|
bool init(QNetworkRequest&) override { return true; }
|
||||||
bool write(QByteArray& data) override
|
bool write(QByteArray& data) override
|
||||||
{
|
{
|
||||||
this->data.append(data);
|
this->m_data.append(data);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool abort() override { return true; }
|
bool abort() override { return true; }
|
||||||
@ -40,7 +40,7 @@ class ParsingValidator : public Net::Validator {
|
|||||||
{
|
{
|
||||||
auto fname = m_entity->localFilename();
|
auto fname = m_entity->localFilename();
|
||||||
try {
|
try {
|
||||||
auto doc = Json::requireDocument(data, fname);
|
auto doc = Json::requireDocument(m_data, fname);
|
||||||
auto obj = Json::requireObject(doc, fname);
|
auto obj = Json::requireObject(doc, fname);
|
||||||
m_entity->parse(obj);
|
m_entity->parse(obj);
|
||||||
return true;
|
return true;
|
||||||
@ -51,7 +51,7 @@ class ParsingValidator : public Net::Validator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
QByteArray data;
|
QByteArray m_data;
|
||||||
Meta::BaseEntity* m_entity;
|
Meta::BaseEntity* m_entity;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ void Meta::BaseEntity::load(Net::Mode loadType)
|
|||||||
auto url = this->url();
|
auto url = this->url();
|
||||||
auto entry = APPLICATION->metacache()->resolveEntry("meta", localFilename());
|
auto entry = APPLICATION->metacache()->resolveEntry("meta", localFilename());
|
||||||
entry->setStale(true);
|
entry->setStale(true);
|
||||||
auto dl = Net::Download::makeCached(url, entry);
|
auto dl = Net::ApiDownload::makeCached(url, entry);
|
||||||
/*
|
/*
|
||||||
* The validator parses the file and loads it into the object.
|
* The validator parses the file and loads it into the object.
|
||||||
* If that fails, the file is not written to storage.
|
* If that fails, the file is not written to storage.
|
||||||
|
@ -46,8 +46,8 @@
|
|||||||
#include "AssetsUtils.h"
|
#include "AssetsUtils.h"
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
#include "net/ChecksumValidator.h"
|
#include "net/ChecksumValidator.h"
|
||||||
#include "net/Download.h"
|
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
@ -279,7 +279,7 @@ NetAction::Ptr AssetObject::getDownloadAction()
|
|||||||
{
|
{
|
||||||
QFileInfo objectFile(getLocalPath());
|
QFileInfo objectFile(getLocalPath());
|
||||||
if ((!objectFile.isFile()) || (objectFile.size() != size)) {
|
if ((!objectFile.isFile()) || (objectFile.size() != size)) {
|
||||||
auto objectDL = Net::Download::makeFile(getUrl(), objectFile.filePath());
|
auto objectDL = Net::ApiDownload::makeFile(getUrl(), objectFile.filePath());
|
||||||
if (hash.size()) {
|
if (hash.size()) {
|
||||||
auto rawHash = QByteArray::fromHex(hash.toLatin1());
|
auto rawHash = QByteArray::fromHex(hash.toLatin1());
|
||||||
objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash));
|
objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash));
|
||||||
|
@ -25,7 +25,8 @@ class Component : public QObject, public ProblemProvider {
|
|||||||
Component(PackProfile* parent, std::shared_ptr<Meta::Version> version);
|
Component(PackProfile* parent, std::shared_ptr<Meta::Version> version);
|
||||||
Component(PackProfile* parent, const QString& uid, std::shared_ptr<VersionFile> file);
|
Component(PackProfile* parent, const QString& uid, std::shared_ptr<VersionFile> file);
|
||||||
|
|
||||||
virtual ~Component(){};
|
virtual ~Component() {}
|
||||||
|
|
||||||
void applyTo(LaunchProfile* profile);
|
void applyTo(LaunchProfile* profile);
|
||||||
|
|
||||||
bool isEnabled();
|
bool isEnabled();
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
|
|
||||||
class LaunchProfile : public ProblemProvider {
|
class LaunchProfile : public ProblemProvider {
|
||||||
public:
|
public:
|
||||||
virtual ~LaunchProfile(){};
|
virtual ~LaunchProfile() {}
|
||||||
|
|
||||||
public: /* application of profile variables from patches */
|
public: /* application of profile variables from patches */
|
||||||
void applyMinecraftVersion(const QString& id);
|
void applyMinecraftVersion(const QString& id);
|
||||||
|
@ -38,8 +38,8 @@
|
|||||||
|
|
||||||
#include <BuildConfig.h>
|
#include <BuildConfig.h>
|
||||||
#include <FileSystem.h>
|
#include <FileSystem.h>
|
||||||
|
#include <net/ApiDownload.h>
|
||||||
#include <net/ChecksumValidator.h>
|
#include <net/ChecksumValidator.h>
|
||||||
#include <net/Download.h>
|
|
||||||
|
|
||||||
void Library::getApplicableFiles(const RuntimeContext& runtimeContext,
|
void Library::getApplicableFiles(const RuntimeContext& runtimeContext,
|
||||||
QStringList& jar,
|
QStringList& jar,
|
||||||
@ -115,12 +115,12 @@ QList<NetAction::Ptr> Library::getDownloads(const RuntimeContext& runtimeContext
|
|||||||
|
|
||||||
if (sha1.size()) {
|
if (sha1.size()) {
|
||||||
auto rawSha1 = QByteArray::fromHex(sha1.toLatin1());
|
auto rawSha1 = QByteArray::fromHex(sha1.toLatin1());
|
||||||
auto dl = Net::Download::makeCached(url, entry, options);
|
auto dl = Net::ApiDownload::makeCached(url, entry, options);
|
||||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
|
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
|
||||||
qDebug() << "Checksummed Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url;
|
qDebug() << "Checksummed Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url;
|
||||||
out.append(dl);
|
out.append(dl);
|
||||||
} else {
|
} else {
|
||||||
out.append(Net::Download::makeCached(url, entry, options));
|
out.append(Net::ApiDownload::makeCached(url, entry, options));
|
||||||
qDebug() << "Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url;
|
qDebug() << "Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -19,8 +19,8 @@ struct MojangDownloadInfo {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct MojangLibraryDownloadInfo {
|
struct MojangLibraryDownloadInfo {
|
||||||
MojangLibraryDownloadInfo(MojangDownloadInfo::Ptr artifact) : artifact(artifact){};
|
MojangLibraryDownloadInfo(MojangDownloadInfo::Ptr artifact_) : artifact(artifact_) {}
|
||||||
MojangLibraryDownloadInfo(){};
|
MojangLibraryDownloadInfo() {}
|
||||||
|
|
||||||
// types
|
// types
|
||||||
typedef std::shared_ptr<MojangLibraryDownloadInfo> Ptr;
|
typedef std::shared_ptr<MojangLibraryDownloadInfo> Ptr;
|
||||||
@ -47,18 +47,18 @@ struct MojangAssetIndexInfo : public MojangDownloadInfo {
|
|||||||
// methods
|
// methods
|
||||||
MojangAssetIndexInfo() {}
|
MojangAssetIndexInfo() {}
|
||||||
|
|
||||||
MojangAssetIndexInfo(QString id)
|
MojangAssetIndexInfo(QString id_)
|
||||||
{
|
{
|
||||||
this->id = id;
|
this->id = id_;
|
||||||
// HACK: ignore assets from other version files than Minecraft
|
// HACK: ignore assets from other version files than Minecraft
|
||||||
// workaround for stupid assets issue caused by amazon:
|
// workaround for stupid assets issue caused by amazon:
|
||||||
// https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/
|
// https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/
|
||||||
if (id == "legacy") {
|
if (id_ == "legacy") {
|
||||||
url = "https://piston-meta.mojang.com/mc/assets/legacy/c0fd82e8ce9fbc93119e40d96d5a4e62cfa3f729/legacy.json";
|
url = "https://piston-meta.mojang.com/mc/assets/legacy/c0fd82e8ce9fbc93119e40d96d5a4e62cfa3f729/legacy.json";
|
||||||
}
|
}
|
||||||
// HACK
|
// HACK
|
||||||
else {
|
else {
|
||||||
url = "https://s3.amazonaws.com/Minecraft.Download/indexes/" + id + ".json";
|
url = "https://s3.amazonaws.com/Minecraft.Download/indexes/" + id_ + ".json";
|
||||||
}
|
}
|
||||||
known = false;
|
known = false;
|
||||||
}
|
}
|
||||||
|
@ -350,7 +350,7 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr& patch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LibraryPtr OneSixVersionFormat::plusJarModFromJson(ProblemContainer& problems,
|
LibraryPtr OneSixVersionFormat::plusJarModFromJson([[maybe_unused]] ProblemContainer& problems,
|
||||||
const QJsonObject& libObj,
|
const QJsonObject& libObj,
|
||||||
const QString& filename,
|
const QString& filename,
|
||||||
const QString& originalName)
|
const QString& originalName)
|
||||||
|
@ -204,10 +204,10 @@ static bool loadPackProfile(PackProfile* parent,
|
|||||||
}
|
}
|
||||||
auto orderArray = Json::requireArray(obj.value("components"));
|
auto orderArray = Json::requireArray(obj.value("components"));
|
||||||
for (auto item : orderArray) {
|
for (auto item : orderArray) {
|
||||||
auto obj = Json::requireObject(item, "Component must be an object.");
|
auto comp_obj = Json::requireObject(item, "Component must be an object.");
|
||||||
container.append(componentFromJsonV1(parent, componentJsonPattern, obj));
|
container.append(componentFromJsonV1(parent, componentJsonPattern, comp_obj));
|
||||||
}
|
}
|
||||||
} catch (const JSONValidationError& err) {
|
} catch ([[maybe_unused]] const JSONValidationError& err) {
|
||||||
qCritical() << "Couldn't parse" << componentsFile.fileName() << ": bad file format";
|
qCritical() << "Couldn't parse" << componentsFile.fileName() << ": bad file format";
|
||||||
container.clear();
|
container.clear();
|
||||||
return false;
|
return false;
|
||||||
@ -377,7 +377,7 @@ void PackProfile::insertComponent(size_t index, ComponentPtr component)
|
|||||||
qWarning() << "Attempt to add a component that is already present!";
|
qWarning() << "Attempt to add a component that is already present!";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
beginInsertRows(QModelIndex(), index, index);
|
beginInsertRows(QModelIndex(), static_cast<int>(index), static_cast<int>(index));
|
||||||
d->components.insert(index, component);
|
d->components.insert(index, component);
|
||||||
d->componentIndex[id] = component;
|
d->componentIndex[id] = component;
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
@ -389,7 +389,7 @@ void PackProfile::componentDataChanged()
|
|||||||
{
|
{
|
||||||
auto objPtr = qobject_cast<Component*>(sender());
|
auto objPtr = qobject_cast<Component*>(sender());
|
||||||
if (!objPtr) {
|
if (!objPtr) {
|
||||||
qWarning() << "PackProfile got dataChenged signal from a non-Component!";
|
qWarning() << "PackProfile got dataChanged signal from a non-Component!";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (objPtr->getID() == "net.minecraft") {
|
if (objPtr->getID() == "net.minecraft") {
|
||||||
@ -405,7 +405,7 @@ void PackProfile::componentDataChanged()
|
|||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
qWarning() << "PackProfile got dataChenged signal from a Component which does not belong to it!";
|
qWarning() << "PackProfile got dataChanged signal from a Component which does not belong to it!";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PackProfile::remove(const int index)
|
bool PackProfile::remove(const int index)
|
||||||
@ -483,9 +483,9 @@ ComponentPtr PackProfile::getComponent(const QString& id)
|
|||||||
return (*iter);
|
return (*iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
ComponentPtr PackProfile::getComponent(int index)
|
ComponentPtr PackProfile::getComponent(size_t index)
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= d->components.size()) {
|
if (index >= static_cast<size_t>(d->components.size())) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return d->components[index];
|
return d->components[index];
|
||||||
@ -547,7 +547,7 @@ QVariant PackProfile::data(const QModelIndex& index, int role) const
|
|||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PackProfile::setData(const QModelIndex& index, const QVariant& value, int role)
|
bool PackProfile::setData(const QModelIndex& index, [[maybe_unused]] const QVariant& value, int role)
|
||||||
{
|
{
|
||||||
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index.parent())) {
|
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index.parent())) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -140,7 +140,7 @@ class PackProfile : public QAbstractListModel {
|
|||||||
ComponentPtr getComponent(const QString& id);
|
ComponentPtr getComponent(const QString& id);
|
||||||
|
|
||||||
/// get the profile component by index
|
/// get the profile component by index
|
||||||
ComponentPtr getComponent(int index);
|
ComponentPtr getComponent(size_t index);
|
||||||
|
|
||||||
/// Add the component to the internal list of patches
|
/// Add the component to the internal list of patches
|
||||||
// todo(merged): is this the best approach
|
// todo(merged): is this the best approach
|
||||||
|
@ -82,7 +82,7 @@ bool readOverrideOrders(QString path, PatchOrder& order)
|
|||||||
for (auto item : orderArray) {
|
for (auto item : orderArray) {
|
||||||
order.append(Json::requireString(item));
|
order.append(Json::requireString(item));
|
||||||
}
|
}
|
||||||
} catch (const JSONValidationError& err) {
|
} catch ([[maybe_unused]] const JSONValidationError& err) {
|
||||||
qCritical() << "Couldn't parse" << orderFile.fileName() << ": bad file format";
|
qCritical() << "Couldn't parse" << orderFile.fileName() << ": bad file format";
|
||||||
qWarning() << "Ignoring overriden order";
|
qWarning() << "Ignoring overriden order";
|
||||||
order.clear();
|
order.clear();
|
||||||
|
@ -55,7 +55,7 @@ class Rule {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
Rule(RuleAction result) : m_result(result) {}
|
Rule(RuleAction result) : m_result(result) {}
|
||||||
virtual ~Rule(){};
|
virtual ~Rule() {}
|
||||||
virtual QJsonObject toJson() = 0;
|
virtual QJsonObject toJson() = 0;
|
||||||
RuleAction apply(const Library* parent, const RuntimeContext& runtimeContext)
|
RuleAction apply(const Library* parent, const RuntimeContext& runtimeContext)
|
||||||
{
|
{
|
||||||
|
@ -368,11 +368,11 @@ optional<QString> read_string(nbt::value& parent, const char* name)
|
|||||||
}
|
}
|
||||||
auto& tag_str = namedValue.as<nbt::tag_string>();
|
auto& tag_str = namedValue.as<nbt::tag_string>();
|
||||||
return QString::fromStdString(tag_str.get());
|
return QString::fromStdString(tag_str.get());
|
||||||
} catch (const std::out_of_range& e) {
|
} catch ([[maybe_unused]] const std::out_of_range& e) {
|
||||||
// fallback for old world formats
|
// fallback for old world formats
|
||||||
qWarning() << "String NBT tag" << name << "could not be found.";
|
qWarning() << "String NBT tag" << name << "could not be found.";
|
||||||
return nullopt;
|
return nullopt;
|
||||||
} catch (const std::bad_cast& e) {
|
} catch ([[maybe_unused]] const std::bad_cast& e) {
|
||||||
// type mismatch
|
// type mismatch
|
||||||
qWarning() << "NBT tag" << name << "could not be converted to string.";
|
qWarning() << "NBT tag" << name << "could not be converted to string.";
|
||||||
return nullopt;
|
return nullopt;
|
||||||
@ -388,11 +388,11 @@ optional<int64_t> read_long(nbt::value& parent, const char* name)
|
|||||||
}
|
}
|
||||||
auto& tag_str = namedValue.as<nbt::tag_long>();
|
auto& tag_str = namedValue.as<nbt::tag_long>();
|
||||||
return tag_str.get();
|
return tag_str.get();
|
||||||
} catch (const std::out_of_range& e) {
|
} catch ([[maybe_unused]] const std::out_of_range& e) {
|
||||||
// fallback for old world formats
|
// fallback for old world formats
|
||||||
qWarning() << "Long NBT tag" << name << "could not be found.";
|
qWarning() << "Long NBT tag" << name << "could not be found.";
|
||||||
return nullopt;
|
return nullopt;
|
||||||
} catch (const std::bad_cast& e) {
|
} catch ([[maybe_unused]] const std::bad_cast& e) {
|
||||||
// type mismatch
|
// type mismatch
|
||||||
qWarning() << "NBT tag" << name << "could not be converted to long.";
|
qWarning() << "NBT tag" << name << "could not be converted to long.";
|
||||||
return nullopt;
|
return nullopt;
|
||||||
@ -408,11 +408,11 @@ optional<int> read_int(nbt::value& parent, const char* name)
|
|||||||
}
|
}
|
||||||
auto& tag_str = namedValue.as<nbt::tag_int>();
|
auto& tag_str = namedValue.as<nbt::tag_int>();
|
||||||
return tag_str.get();
|
return tag_str.get();
|
||||||
} catch (const std::out_of_range& e) {
|
} catch ([[maybe_unused]] const std::out_of_range& e) {
|
||||||
// fallback for old world formats
|
// fallback for old world formats
|
||||||
qWarning() << "Int NBT tag" << name << "could not be found.";
|
qWarning() << "Int NBT tag" << name << "could not be found.";
|
||||||
return nullopt;
|
return nullopt;
|
||||||
} catch (const std::bad_cast& e) {
|
} catch ([[maybe_unused]] const std::bad_cast& e) {
|
||||||
// type mismatch
|
// type mismatch
|
||||||
qWarning() << "NBT tag" << name << "could not be converted to int.";
|
qWarning() << "NBT tag" << name << "could not be converted to int.";
|
||||||
return nullopt;
|
return nullopt;
|
||||||
|
@ -255,7 +255,7 @@ QVariant WorldList::data(const QModelIndex& index, int role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant WorldList::headerData(int section, Qt::Orientation orientation, int role) const
|
QVariant WorldList::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const
|
||||||
{
|
{
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
@ -294,7 +294,6 @@ QVariant WorldList::headerData(int section, Qt::Orientation orientation, int rol
|
|||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
return QVariant();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList WorldList::mimeTypes() const
|
QStringList WorldList::mimeTypes() const
|
||||||
@ -339,19 +338,19 @@ QMimeData* WorldList::mimeData(const QModelIndexList& indexes) const
|
|||||||
if (indexes.size() == 0)
|
if (indexes.size() == 0)
|
||||||
return new QMimeData();
|
return new QMimeData();
|
||||||
|
|
||||||
QList<World> worlds;
|
QList<World> worlds_;
|
||||||
for (auto idx : indexes) {
|
for (auto idx : indexes) {
|
||||||
if (idx.column() != 0)
|
if (idx.column() != 0)
|
||||||
continue;
|
continue;
|
||||||
int row = idx.row();
|
int row = idx.row();
|
||||||
if (row < 0 || row >= this->worlds.size())
|
if (row < 0 || row >= this->worlds.size())
|
||||||
continue;
|
continue;
|
||||||
worlds.append(this->worlds[row]);
|
worlds_.append(this->worlds[row]);
|
||||||
}
|
}
|
||||||
if (!worlds.size()) {
|
if (!worlds_.size()) {
|
||||||
return new QMimeData();
|
return new QMimeData();
|
||||||
}
|
}
|
||||||
return new WorldMimeData(worlds);
|
return new WorldMimeData(worlds_);
|
||||||
}
|
}
|
||||||
|
|
||||||
Qt::ItemFlags WorldList::flags(const QModelIndex& index) const
|
Qt::ItemFlags WorldList::flags(const QModelIndex& index) const
|
||||||
|
@ -311,7 +311,7 @@ bool AccountData::resumeStateFromV2(QJsonObject data)
|
|||||||
QJsonObject profileObject = profileVal.toObject();
|
QJsonObject profileObject = profileVal.toObject();
|
||||||
QString id = profileObject.value("id").toString("");
|
QString id = profileObject.value("id").toString("");
|
||||||
QString name = profileObject.value("name").toString("");
|
QString name = profileObject.value("name").toString("");
|
||||||
bool legacy = profileObject.value("legacy").toBool(false);
|
bool legacy_ = profileObject.value("legacy").toBool(false);
|
||||||
if (id.isEmpty() || name.isEmpty()) {
|
if (id.isEmpty() || name.isEmpty()) {
|
||||||
qWarning() << "Unable to load a profile" << name << "because it was missing an ID or a name.";
|
qWarning() << "Unable to load a profile" << name << "because it was missing an ID or a name.";
|
||||||
continue;
|
continue;
|
||||||
@ -319,7 +319,7 @@ bool AccountData::resumeStateFromV2(QJsonObject data)
|
|||||||
if (id == currentProfile) {
|
if (id == currentProfile) {
|
||||||
currentProfileIndex = index;
|
currentProfileIndex = index;
|
||||||
}
|
}
|
||||||
profiles.append({ id, name, legacy });
|
profiles.append({ id, name, legacy_ });
|
||||||
}
|
}
|
||||||
auto& profile = profiles[currentProfileIndex];
|
auto& profile = profiles[currentProfileIndex];
|
||||||
|
|
||||||
|
@ -353,7 +353,7 @@ QVariant AccountList::data(const QModelIndex& index, int role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant AccountList::headerData(int section, Qt::Orientation orientation, int role) const
|
QVariant AccountList::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const
|
||||||
{
|
{
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
#include "MinecraftAccount.h"
|
#include "MinecraftAccount.h"
|
||||||
|
|
||||||
|
#include <QColor>
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
@ -126,6 +127,11 @@ QPixmap MinecraftAccount::getFace() const
|
|||||||
return QPixmap();
|
return QPixmap();
|
||||||
}
|
}
|
||||||
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));
|
||||||
|
@ -113,16 +113,16 @@ bool parseXTokenResponse(QByteArray& data, Katabasis::Token& output, QString nam
|
|||||||
if (!item.isObject()) {
|
if (!item.isObject()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto obj = item.toObject();
|
auto obj_ = item.toObject();
|
||||||
if (obj.contains("uhs")) {
|
if (obj_.contains("uhs")) {
|
||||||
foundUHS = true;
|
foundUHS = true;
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// consume all 'display claims' ... whatever that means
|
// consume all 'display claims' ... whatever that means
|
||||||
for (auto iter = obj.begin(); iter != obj.end(); iter++) {
|
for (auto iter = obj_.begin(); iter != obj_.end(); iter++) {
|
||||||
QString claim;
|
QString claim;
|
||||||
if (!getString(obj.value(iter.key()), claim)) {
|
if (!getString(obj_.value(iter.key()), claim)) {
|
||||||
qWarning() << "display claim " << iter.key() << " is not a string...";
|
qWarning() << "display claim " << iter.key() << " is not a string...";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,9 @@ void EntitlementsStep::rehydrate()
|
|||||||
// NOOP, for now. We only save bools and there's nothing to check.
|
// NOOP, for now. We only save bools and there's nothing to check.
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntitlementsStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair> headers)
|
void EntitlementsStep::onRequestDone([[maybe_unused]] QNetworkReply::NetworkError error,
|
||||||
|
QByteArray data,
|
||||||
|
[[maybe_unused]] QList<QNetworkReply::RawHeaderPair> headers)
|
||||||
{
|
{
|
||||||
auto requestor = qobject_cast<AuthRequest*>(QObject::sender());
|
auto requestor = qobject_cast<AuthRequest*>(QObject::sender());
|
||||||
requestor->deleteLater();
|
requestor->deleteLater();
|
||||||
|
@ -97,12 +97,11 @@ QVariant GameOptions::data(const QModelIndex& index, int role) const
|
|||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
return QVariant();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int GameOptions::rowCount(const QModelIndex&) const
|
int GameOptions::rowCount(const QModelIndex&) const
|
||||||
{
|
{
|
||||||
return contents.size();
|
return static_cast<int>(contents.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
int GameOptions::columnCount(const QModelIndex&) const
|
int GameOptions::columnCount(const QModelIndex&) const
|
||||||
|
@ -53,7 +53,7 @@ LauncherPartLaunch::LauncherPartLaunch(LaunchTask* parent) : LaunchStep(parent)
|
|||||||
auto instance = parent->instance();
|
auto instance = parent->instance();
|
||||||
if (instance->settings()->get("CloseAfterLaunch").toBool()) {
|
if (instance->settings()->get("CloseAfterLaunch").toBool()) {
|
||||||
std::shared_ptr<QMetaObject::Connection> connection{ new QMetaObject::Connection };
|
std::shared_ptr<QMetaObject::Connection> connection{ new QMetaObject::Connection };
|
||||||
*connection = connect(&m_process, &LoggedProcess::log, this, [=](QStringList lines, MessageLevel::Enum level) {
|
*connection = connect(&m_process, &LoggedProcess::log, this, [=](QStringList lines, [[maybe_unused]] MessageLevel::Enum level) {
|
||||||
qDebug() << lines;
|
qDebug() << lines;
|
||||||
if (lines.filter(QRegularExpression(".*Setting user.+", QRegularExpression::CaseInsensitiveOption)).length() != 0) {
|
if (lines.filter(QRegularExpression(".*Setting user.+", QRegularExpression::CaseInsensitiveOption)).length() != 0) {
|
||||||
APPLICATION->closeAllWindows();
|
APPLICATION->closeAllWindows();
|
||||||
|
@ -72,7 +72,7 @@ class Mod : public Resource {
|
|||||||
auto metaurl() const -> QString;
|
auto metaurl() const -> QString;
|
||||||
|
|
||||||
/** Get the intneral path to the mod's icon file*/
|
/** Get the intneral path to the mod's icon file*/
|
||||||
QString iconPath() const { return m_local_details.icon_file; };
|
QString iconPath() const { return m_local_details.icon_file; }
|
||||||
/** Gets the icon of the mod, converted to a QPixmap for drawing, and scaled to size. */
|
/** Gets the icon of the mod, converted to a QPixmap for drawing, and scaled to size. */
|
||||||
[[nodiscard]] QPixmap icon(QSize size, Qt::AspectRatioMode mode = Qt::AspectRatioMode::IgnoreAspectRatio) const;
|
[[nodiscard]] QPixmap icon(QSize size, Qt::AspectRatioMode mode = Qt::AspectRatioMode::IgnoreAspectRatio) const;
|
||||||
/** Thread-safe. */
|
/** Thread-safe. */
|
||||||
|
@ -60,17 +60,17 @@ struct ModLicense {
|
|||||||
|
|
||||||
ModLicense(const QString license)
|
ModLicense(const QString license)
|
||||||
{
|
{
|
||||||
// FIXME: come up with a better license parseing.
|
// FIXME: come up with a better license parsing.
|
||||||
// handle SPDX identifiers? https://spdx.org/licenses/
|
// handle SPDX identifiers? https://spdx.org/licenses/
|
||||||
auto parts = license.split(' ');
|
auto parts = license.split(' ');
|
||||||
QStringList notNameParts = {};
|
QStringList notNameParts = {};
|
||||||
for (auto part : parts) {
|
for (auto part : parts) {
|
||||||
auto url = QUrl(part);
|
auto _url = QUrl(part);
|
||||||
if (part.startsWith("(") && part.endsWith(")"))
|
if (part.startsWith("(") && part.endsWith(")"))
|
||||||
url = QUrl(part.mid(1, part.size() - 2));
|
_url = QUrl(part.mid(1, part.size() - 2));
|
||||||
|
|
||||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
if (_url.isValid() && !_url.scheme().isEmpty() && !_url.host().isEmpty()) {
|
||||||
this->url = url.toString();
|
this->url = _url.toString();
|
||||||
notNameParts.append(part);
|
notNameParts.append(part);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -89,13 +89,9 @@ struct ModLicense {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ModLicense(const QString name, const QString id, const QString url, const QString description)
|
ModLicense(const QString& name_, const QString& id_, const QString& url_, const QString& description_)
|
||||||
{
|
: name(name_), id(id_), url(url_), description(description_)
|
||||||
this->name = name;
|
{}
|
||||||
this->id = id;
|
|
||||||
this->url = url;
|
|
||||||
this->description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
ModLicense(const ModLicense& other) : name(other.name), id(other.id), url(other.url), description(other.description) {}
|
ModLicense(const ModLicense& other) : name(other.name), id(other.id), url(other.url), description(other.description) {}
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool
|
|||||||
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER };
|
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER };
|
||||||
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch,
|
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch,
|
||||||
QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents };
|
QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents };
|
||||||
|
m_columnsHideable = { false, true, false, true, true, true };
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant ModFolderModel::data(const QModelIndex& index, int role) const
|
QVariant ModFolderModel::data(const QModelIndex& index, int role) const
|
||||||
@ -137,7 +138,7 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant ModFolderModel::headerData(int section, Qt::Orientation orientation, int role) const
|
QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const
|
||||||
{
|
{
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
|
@ -447,7 +447,7 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ResourceFolderModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
bool ResourceFolderModel::setData(const QModelIndex& index, [[maybe_unused]] const QVariant& value, int role)
|
||||||
{
|
{
|
||||||
int row = index.row();
|
int row = index.row();
|
||||||
if (row < 0 || row >= rowCount(index.parent()) || !index.isValid())
|
if (row < 0 || row >= rowCount(index.parent()) || !index.isValid())
|
||||||
@ -471,7 +471,7 @@ bool ResourceFolderModel::setData(const QModelIndex& index, const QVariant& valu
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant ResourceFolderModel::headerData(int section, Qt::Orientation orientation, int role) const
|
QVariant ResourceFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const
|
||||||
{
|
{
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
@ -551,6 +551,9 @@ QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree)
|
|||||||
menu->addSeparator()->setText(tr("Show / Hide Columns"));
|
menu->addSeparator()->setText(tr("Show / Hide Columns"));
|
||||||
|
|
||||||
for (int col = 0; col < columnCount(); ++col) {
|
for (int col = 0; col < columnCount(); ++col) {
|
||||||
|
// Skip creating actions for columns that should not be hidden
|
||||||
|
if (!m_columnsHideable.at(col))
|
||||||
|
continue;
|
||||||
auto act = new QAction(menu);
|
auto act = new QAction(menu);
|
||||||
setupHeaderAction(act, col);
|
setupHeaderAction(act, col);
|
||||||
|
|
||||||
@ -584,7 +587,8 @@ SortType ResourceFolderModel::columnToSortKey(size_t column) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Standard Proxy Model for createFilterProxyModel */
|
/* Standard Proxy Model for createFilterProxyModel */
|
||||||
[[nodiscard]] bool ResourceFolderModel::ProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
|
[[nodiscard]] bool ResourceFolderModel::ProxyModel::filterAcceptsRow(int source_row,
|
||||||
|
[[maybe_unused]] const QModelIndex& source_parent) const
|
||||||
{
|
{
|
||||||
auto* model = qobject_cast<ResourceFolderModel*>(sourceModel());
|
auto* model = qobject_cast<ResourceFolderModel*>(sourceModel());
|
||||||
if (!model)
|
if (!model)
|
||||||
|
@ -49,8 +49,8 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||||||
bool stopWatching(const QStringList paths);
|
bool stopWatching(const QStringList paths);
|
||||||
|
|
||||||
/* Helper methods for subclasses, using a predetermined list of paths. */
|
/* Helper methods for subclasses, using a predetermined list of paths. */
|
||||||
virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); };
|
virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); }
|
||||||
virtual bool stopWatching() { return stopWatching({ m_dir.absolutePath() }); };
|
virtual bool stopWatching() { return stopWatching({ m_dir.absolutePath() }); }
|
||||||
|
|
||||||
/** Given a path in the system, install that resource, moving it to its place in the
|
/** Given a path in the system, install that resource, moving it to its place in the
|
||||||
* instance file hierarchy.
|
* instance file hierarchy.
|
||||||
@ -78,7 +78,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||||||
/** Creates a new parse task, if needed, for 'res' and start it.*/
|
/** Creates a new parse task, if needed, for 'res' and start it.*/
|
||||||
virtual void resolveResource(Resource* res);
|
virtual void resolveResource(Resource* res);
|
||||||
|
|
||||||
[[nodiscard]] size_t size() const { return m_resources.size(); };
|
[[nodiscard]] qsizetype size() const { return m_resources.size(); }
|
||||||
[[nodiscard]] bool empty() const { return size() == 0; }
|
[[nodiscard]] bool empty() const { return size() == 0; }
|
||||||
[[nodiscard]] Resource& at(int index) { return *m_resources.at(index); }
|
[[nodiscard]] Resource& at(int index) { return *m_resources.at(index); }
|
||||||
[[nodiscard]] Resource const& at(int index) const { return *m_resources.at(index); }
|
[[nodiscard]] Resource const& at(int index) const { return *m_resources.at(index); }
|
||||||
@ -97,10 +97,10 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||||||
|
|
||||||
/* Basic columns */
|
/* Basic columns */
|
||||||
enum Columns { ACTIVE_COLUMN = 0, NAME_COLUMN, DATE_COLUMN, NUM_COLUMNS };
|
enum Columns { ACTIVE_COLUMN = 0, NAME_COLUMN, DATE_COLUMN, NUM_COLUMNS };
|
||||||
QStringList columnNames(bool translated = true) const { return translated ? m_column_names_translated : m_column_names; };
|
QStringList columnNames(bool translated = true) const { return translated ? m_column_names_translated : m_column_names; }
|
||||||
|
|
||||||
[[nodiscard]] int rowCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : static_cast<int>(size()); }
|
[[nodiscard]] int rowCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : static_cast<int>(size()); }
|
||||||
[[nodiscard]] int columnCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : NUM_COLUMNS; };
|
[[nodiscard]] int columnCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : NUM_COLUMNS; }
|
||||||
|
|
||||||
[[nodiscard]] Qt::DropActions supportedDropActions() const override;
|
[[nodiscard]] Qt::DropActions supportedDropActions() const override;
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||||||
* This task should load and parse all heavy info needed by a resource, such as parsing a manifest. It gets executed
|
* This task should load and parse all heavy info needed by a resource, such as parsing a manifest. It gets executed
|
||||||
* in the background, so it slowly updates the UI as tasks get done.
|
* in the background, so it slowly updates the UI as tasks get done.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] virtual Task* createParseTask(Resource&) { return nullptr; };
|
[[nodiscard]] virtual Task* createParseTask(Resource&) { return nullptr; }
|
||||||
|
|
||||||
/** Standard implementation of the model update logic.
|
/** Standard implementation of the model update logic.
|
||||||
*
|
*
|
||||||
@ -203,6 +203,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||||||
QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified") };
|
QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified") };
|
||||||
QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch,
|
QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch,
|
||||||
QHeaderView::ResizeToContents };
|
QHeaderView::ResizeToContents };
|
||||||
|
QList<bool> m_columnsHideable = { false, false, true };
|
||||||
|
|
||||||
QDir m_dir;
|
QDir m_dir;
|
||||||
BaseInstance* m_instance;
|
BaseInstance* m_instance;
|
||||||
@ -224,15 +225,15 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||||||
|
|
||||||
/* A macro to define useful functions to handle Resource* -> T* more easily on derived classes */
|
/* A macro to define useful functions to handle Resource* -> T* more easily on derived classes */
|
||||||
#define RESOURCE_HELPERS(T) \
|
#define RESOURCE_HELPERS(T) \
|
||||||
[[nodiscard]] T* operator[](size_t index) \
|
[[nodiscard]] T* operator[](int index) \
|
||||||
{ \
|
{ \
|
||||||
return static_cast<T*>(m_resources[index].get()); \
|
return static_cast<T*>(m_resources[index].get()); \
|
||||||
} \
|
} \
|
||||||
[[nodiscard]] T* at(size_t index) \
|
[[nodiscard]] T* at(int index) \
|
||||||
{ \
|
{ \
|
||||||
return static_cast<T*>(m_resources[index].get()); \
|
return static_cast<T*>(m_resources[index].get()); \
|
||||||
} \
|
} \
|
||||||
[[nodiscard]] const T* at(size_t index) const \
|
[[nodiscard]] const T* at(int index) const \
|
||||||
{ \
|
{ \
|
||||||
return static_cast<const T*>(m_resources.at(index).get()); \
|
return static_cast<const T*>(m_resources.at(index).get()); \
|
||||||
} \
|
} \
|
||||||
|
@ -54,6 +54,7 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstanc
|
|||||||
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE };
|
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE };
|
||||||
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents,
|
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents,
|
||||||
QHeaderView::ResizeToContents };
|
QHeaderView::ResizeToContents };
|
||||||
|
m_columnsHideable = { false, true, false, true, true };
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
||||||
@ -128,7 +129,7 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orientation, int role) const
|
QVariant ResourcePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const
|
||||||
{
|
{
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
@ -165,7 +166,6 @@ QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orient
|
|||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ResourcePackFolderModel::columnCount(const QModelIndex& parent) const
|
int ResourcePackFolderModel::columnCount(const QModelIndex& parent) const
|
||||||
|
@ -49,6 +49,7 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance*
|
|||||||
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE };
|
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE };
|
||||||
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch,
|
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch,
|
||||||
QHeaderView::ResizeToContents };
|
QHeaderView::ResizeToContents };
|
||||||
|
m_columnsHideable = { false, true, false, true };
|
||||||
}
|
}
|
||||||
|
|
||||||
Task* TexturePackFolderModel::createUpdateTask()
|
Task* TexturePackFolderModel::createUpdateTask()
|
||||||
@ -113,7 +114,7 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant TexturePackFolderModel::headerData(int section, Qt::Orientation orientation, int role) const
|
QVariant TexturePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const
|
||||||
{
|
{
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
|
@ -61,7 +61,7 @@ GetModDependenciesTask::GetModDependenciesTask(QObject* parent,
|
|||||||
if (auto meta = mod->metadata(); meta)
|
if (auto meta = mod->metadata(); meta)
|
||||||
m_mods.append(meta);
|
m_mods.append(meta);
|
||||||
prepare();
|
prepare();
|
||||||
};
|
}
|
||||||
|
|
||||||
void GetModDependenciesTask::prepare()
|
void GetModDependenciesTask::prepare()
|
||||||
{
|
{
|
||||||
@ -130,7 +130,7 @@ QList<ModPlatform::Dependency> GetModDependenciesTask::getDependenciesForVersion
|
|||||||
c_dependencies.append(getOverride(ver_dep, providerName));
|
c_dependencies.append(getOverride(ver_dep, providerName));
|
||||||
}
|
}
|
||||||
return c_dependencies;
|
return c_dependencies;
|
||||||
};
|
}
|
||||||
|
|
||||||
Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr<PackDependency> pDep)
|
Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr<PackDependency> pDep)
|
||||||
{
|
{
|
||||||
@ -181,7 +181,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
|
|||||||
ResourceAPI::DependencySearchArgs args = { dep, m_version, m_loaderType };
|
ResourceAPI::DependencySearchArgs args = { dep, m_version, m_loaderType };
|
||||||
ResourceAPI::DependencySearchCallbacks callbacks;
|
ResourceAPI::DependencySearchCallbacks callbacks;
|
||||||
|
|
||||||
callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, auto& pack) {
|
callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, [[maybe_unused]] auto& pack) {
|
||||||
try {
|
try {
|
||||||
QJsonArray arr;
|
QJsonArray arr;
|
||||||
if (dep.version.length() != 0 && doc.isObject()) {
|
if (dep.version.length() != 0 && doc.isObject()) {
|
||||||
@ -215,27 +215,27 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (level == 0) {
|
if (level == 0) {
|
||||||
qWarning() << "Dependency cycle exeeded";
|
qWarning() << "Dependency cycle exceeded";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (dep.addonId.toString().isEmpty() && !pDep->version.addonId.toString().isEmpty()) {
|
if (dep.addonId.toString().isEmpty() && !pDep->version.addonId.toString().isEmpty()) {
|
||||||
pDep->pack->addonId = pDep->version.addonId;
|
pDep->pack->addonId = pDep->version.addonId;
|
||||||
auto dep = getOverride({ pDep->version.addonId, pDep->dependency.type }, provider.name);
|
auto dep_ = getOverride({ pDep->version.addonId, pDep->dependency.type }, provider.name);
|
||||||
if (dep.addonId != pDep->version.addonId) {
|
if (dep_.addonId != pDep->version.addonId) {
|
||||||
removePack(pDep->version.addonId);
|
removePack(pDep->version.addonId);
|
||||||
addTask(prepareDependencyTask(dep, provider.name, level));
|
addTask(prepareDependencyTask(dep_, provider.name, level));
|
||||||
} else
|
} else
|
||||||
addTask(getProjectInfoTask(pDep));
|
addTask(getProjectInfoTask(pDep));
|
||||||
}
|
}
|
||||||
for (auto dep : getDependenciesForVersion(pDep->version, provider.name)) {
|
for (auto dep_ : getDependenciesForVersion(pDep->version, provider.name)) {
|
||||||
addTask(prepareDependencyTask(dep, provider.name, level - 1));
|
addTask(prepareDependencyTask(dep_, provider.name, level - 1));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto version = provider.api->getDependencyVersion(std::move(args), std::move(callbacks));
|
auto version = provider.api->getDependencyVersion(std::move(args), std::move(callbacks));
|
||||||
tasks->addTask(version);
|
tasks->addTask(version);
|
||||||
return tasks;
|
return tasks;
|
||||||
};
|
}
|
||||||
|
|
||||||
void GetModDependenciesTask::removePack(const QVariant addonId)
|
void GetModDependenciesTask::removePack(const QVariant addonId)
|
||||||
{
|
{
|
||||||
|
@ -104,14 +104,15 @@ ModDetails ReadMCModTOML(QByteArray contents)
|
|||||||
#if TOML_EXCEPTIONS
|
#if TOML_EXCEPTIONS
|
||||||
try {
|
try {
|
||||||
tomlData = toml::parse(contents.toStdString());
|
tomlData = toml::parse(contents.toStdString());
|
||||||
} catch (const toml::parse_error& err) {
|
} catch ([[maybe_unused]] const toml::parse_error& err) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
tomlData = toml::parse(contents.toStdString());
|
toml::parse_result result = toml::parse(contents.toStdString());
|
||||||
if (!tomlData) {
|
if (!result) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
tomlData = result.table();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// array defined by [[mods]]
|
// array defined by [[mods]]
|
||||||
@ -151,8 +152,8 @@ ModDetails ReadMCModTOML(QByteArray contents)
|
|||||||
QString authors = "";
|
QString authors = "";
|
||||||
if (auto authorsDatum = tomlData["authors"].as_string()) {
|
if (auto authorsDatum = tomlData["authors"].as_string()) {
|
||||||
authors = QString::fromStdString(authorsDatum->get());
|
authors = QString::fromStdString(authorsDatum->get());
|
||||||
} else if (auto authorsDatum = (*modsTable)["authors"].as_string()) {
|
} else if (auto authorsDatumMods = (*modsTable)["authors"].as_string()) {
|
||||||
authors = QString::fromStdString(authorsDatum->get());
|
authors = QString::fromStdString(authorsDatumMods->get());
|
||||||
}
|
}
|
||||||
if (!authors.isEmpty()) {
|
if (!authors.isEmpty()) {
|
||||||
details.authors.append(authors);
|
details.authors.append(authors);
|
||||||
@ -161,8 +162,8 @@ ModDetails ReadMCModTOML(QByteArray contents)
|
|||||||
QString homeurl = "";
|
QString homeurl = "";
|
||||||
if (auto homeurlDatum = tomlData["displayURL"].as_string()) {
|
if (auto homeurlDatum = tomlData["displayURL"].as_string()) {
|
||||||
homeurl = QString::fromStdString(homeurlDatum->get());
|
homeurl = QString::fromStdString(homeurlDatum->get());
|
||||||
} else if (auto homeurlDatum = (*modsTable)["displayURL"].as_string()) {
|
} else if (auto homeurlDatumMods = (*modsTable)["displayURL"].as_string()) {
|
||||||
homeurl = QString::fromStdString(homeurlDatum->get());
|
homeurl = QString::fromStdString(homeurlDatumMods->get());
|
||||||
}
|
}
|
||||||
// fix up url.
|
// fix up url.
|
||||||
if (!homeurl.isEmpty() && !homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://")) {
|
if (!homeurl.isEmpty() && !homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://")) {
|
||||||
@ -173,16 +174,16 @@ ModDetails ReadMCModTOML(QByteArray contents)
|
|||||||
QString issueTrackerURL = "";
|
QString issueTrackerURL = "";
|
||||||
if (auto issueTrackerURLDatum = tomlData["issueTrackerURL"].as_string()) {
|
if (auto issueTrackerURLDatum = tomlData["issueTrackerURL"].as_string()) {
|
||||||
issueTrackerURL = QString::fromStdString(issueTrackerURLDatum->get());
|
issueTrackerURL = QString::fromStdString(issueTrackerURLDatum->get());
|
||||||
} else if (auto issueTrackerURLDatum = (*modsTable)["issueTrackerURL"].as_string()) {
|
} else if (auto issueTrackerURLDatumMods = (*modsTable)["issueTrackerURL"].as_string()) {
|
||||||
issueTrackerURL = QString::fromStdString(issueTrackerURLDatum->get());
|
issueTrackerURL = QString::fromStdString(issueTrackerURLDatumMods->get());
|
||||||
}
|
}
|
||||||
details.issue_tracker = issueTrackerURL;
|
details.issue_tracker = issueTrackerURL;
|
||||||
|
|
||||||
QString license = "";
|
QString license = "";
|
||||||
if (auto licenseDatum = tomlData["license"].as_string()) {
|
if (auto licenseDatum = tomlData["license"].as_string()) {
|
||||||
license = QString::fromStdString(licenseDatum->get());
|
license = QString::fromStdString(licenseDatum->get());
|
||||||
} else if (auto licenseDatum = (*modsTable)["license"].as_string()) {
|
} else if (auto licenseDatumMods = (*modsTable)["license"].as_string()) {
|
||||||
license = QString::fromStdString(licenseDatum->get());
|
license = QString::fromStdString(licenseDatumMods->get());
|
||||||
}
|
}
|
||||||
if (!license.isEmpty())
|
if (!license.isEmpty())
|
||||||
details.licenses.append(ModLicense(license));
|
details.licenses.append(ModLicense(license));
|
||||||
@ -190,8 +191,8 @@ ModDetails ReadMCModTOML(QByteArray contents)
|
|||||||
QString logoFile = "";
|
QString logoFile = "";
|
||||||
if (auto logoFileDatum = tomlData["logoFile"].as_string()) {
|
if (auto logoFileDatum = tomlData["logoFile"].as_string()) {
|
||||||
logoFile = QString::fromStdString(logoFileDatum->get());
|
logoFile = QString::fromStdString(logoFileDatum->get());
|
||||||
} else if (auto logoFileDatum = (*modsTable)["logoFile"].as_string()) {
|
} else if (auto logoFileDatumMods = (*modsTable)["logoFile"].as_string()) {
|
||||||
logoFile = QString::fromStdString(logoFileDatum->get());
|
logoFile = QString::fromStdString(logoFileDatumMods->get());
|
||||||
}
|
}
|
||||||
details.icon_file = logoFile;
|
details.icon_file = logoFile;
|
||||||
|
|
||||||
@ -458,7 +459,7 @@ bool process(Mod& mod, ProcessingLevel level)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool processZIP(Mod& mod, ProcessingLevel level)
|
bool processZIP(Mod& mod, [[maybe_unused]] ProcessingLevel level)
|
||||||
{
|
{
|
||||||
ModDetails details;
|
ModDetails details;
|
||||||
|
|
||||||
@ -591,7 +592,7 @@ bool processZIP(Mod& mod, ProcessingLevel level)
|
|||||||
return false; // no valid mod found in archive
|
return false; // no valid mod found in archive
|
||||||
}
|
}
|
||||||
|
|
||||||
bool processFolder(Mod& mod, ProcessingLevel level)
|
bool processFolder(Mod& mod, [[maybe_unused]] ProcessingLevel level)
|
||||||
{
|
{
|
||||||
ModDetails details;
|
ModDetails details;
|
||||||
|
|
||||||
@ -612,7 +613,7 @@ bool processFolder(Mod& mod, ProcessingLevel level)
|
|||||||
return false; // no valid mcmod.info file found
|
return false; // no valid mcmod.info file found
|
||||||
}
|
}
|
||||||
|
|
||||||
bool processLitemod(Mod& mod, ProcessingLevel level)
|
bool processLitemod(Mod& mod, [[maybe_unused]] ProcessingLevel level)
|
||||||
{
|
{
|
||||||
ModDetails details;
|
ModDetails details;
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
|
|
||||||
CapeChange::CapeChange(QObject* parent, QString token, QString cape) : Task(parent), m_capeId(cape), m_token(token) {}
|
CapeChange::CapeChange(QObject* parent, QString token, QString cape) : Task(parent), m_capeId(cape), m_token(token) {}
|
||||||
|
|
||||||
void CapeChange::setCape(QString& cape)
|
void CapeChange::setCape([[maybe_unused]] QString& cape)
|
||||||
{
|
{
|
||||||
QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active"));
|
QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active"));
|
||||||
auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId);
|
auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId);
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
|
|
||||||
AssetUpdateTask::AssetUpdateTask(MinecraftInstance* inst)
|
AssetUpdateTask::AssetUpdateTask(MinecraftInstance* inst)
|
||||||
{
|
{
|
||||||
m_inst = inst;
|
m_inst = inst;
|
||||||
@ -29,7 +31,7 @@ void AssetUpdateTask::executeTask()
|
|||||||
entry->setStale(true);
|
entry->setStale(true);
|
||||||
auto hexSha1 = assets->sha1.toLatin1();
|
auto hexSha1 = assets->sha1.toLatin1();
|
||||||
qDebug() << "Asset index SHA1:" << hexSha1;
|
qDebug() << "Asset index SHA1:" << hexSha1;
|
||||||
auto dl = Net::Download::makeCached(indexUrl, entry);
|
auto dl = Net::ApiDownload::makeCached(indexUrl, entry);
|
||||||
auto rawSha1 = QByteArray::fromHex(assets->sha1.toLatin1());
|
auto rawSha1 = QByteArray::fromHex(assets->sha1.toLatin1());
|
||||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
|
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
|
||||||
job->addNetAction(dl);
|
job->addNetAction(dl);
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
|
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
|
|
||||||
FMLLibrariesTask::FMLLibrariesTask(MinecraftInstance* inst)
|
FMLLibrariesTask::FMLLibrariesTask(MinecraftInstance* inst)
|
||||||
{
|
{
|
||||||
m_inst = inst;
|
m_inst = inst;
|
||||||
@ -62,7 +64,7 @@ void FMLLibrariesTask::executeTask()
|
|||||||
for (auto& lib : fmlLibsToProcess) {
|
for (auto& lib : fmlLibsToProcess) {
|
||||||
auto entry = metacache->resolveEntry("fmllibs", lib.filename);
|
auto entry = metacache->resolveEntry("fmllibs", lib.filename);
|
||||||
QString urlString = BuildConfig.FMLLIBS_BASE_URL + lib.filename;
|
QString urlString = BuildConfig.FMLLIBS_BASE_URL + lib.filename;
|
||||||
dljob->addNetAction(Net::Download::makeCached(QUrl(urlString), entry, options));
|
dljob->addNetAction(Net::ApiDownload::makeCached(QUrl(urlString), entry, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(dljob.get(), &NetJob::succeeded, this, &FMLLibrariesTask::fmllibsFinished);
|
connect(dljob.get(), &NetJob::succeeded, this, &FMLLibrariesTask::fmllibsFinished);
|
||||||
|
@ -113,7 +113,7 @@ struct IndexedPack {
|
|||||||
ExtraPackData extraData;
|
ExtraPackData extraData;
|
||||||
|
|
||||||
// For internal use, not provided by APIs
|
// For internal use, not provided by APIs
|
||||||
[[nodiscard]] bool isVersionSelected(size_t index) const
|
[[nodiscard]] bool isVersionSelected(int index) const
|
||||||
{
|
{
|
||||||
if (!versionsLoaded)
|
if (!versionsLoaded)
|
||||||
return false;
|
return false;
|
||||||
@ -144,7 +144,8 @@ inline auto getOverrideDeps() -> QList<OverrideDep>
|
|||||||
|
|
||||||
{ "qvIfYCYJ", "P7dR8mSH", "API", ModPlatform::ResourceProvider::MODRINTH },
|
{ "qvIfYCYJ", "P7dR8mSH", "API", ModPlatform::ResourceProvider::MODRINTH },
|
||||||
{ "lwVhp9o5", "Ha28R6CL", "KotlinLibraries", ModPlatform::ResourceProvider::MODRINTH } };
|
{ "lwVhp9o5", "Ha28R6CL", "KotlinLibraries", ModPlatform::ResourceProvider::MODRINTH } };
|
||||||
};
|
}
|
||||||
|
|
||||||
QString getMetaURL(ResourceProvider provider, QVariant projectID);
|
QString getMetaURL(ResourceProvider provider, QVariant projectID);
|
||||||
|
|
||||||
} // namespace ModPlatform
|
} // namespace ModPlatform
|
||||||
|
@ -128,28 +128,30 @@ class ResourceAPI {
|
|||||||
public slots:
|
public slots:
|
||||||
[[nodiscard]] virtual Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const
|
[[nodiscard]] virtual Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const
|
||||||
{
|
{
|
||||||
qWarning() << "TODO";
|
qWarning() << "TODO: ResourceAPI::searchProjects";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
[[nodiscard]] virtual Task::Ptr getProject(QString addonId, std::shared_ptr<QByteArray> response) const
|
[[nodiscard]] virtual Task::Ptr getProject([[maybe_unused]] QString addonId,
|
||||||
|
[[maybe_unused]] std::shared_ptr<QByteArray> response) const
|
||||||
{
|
{
|
||||||
qWarning() << "TODO";
|
qWarning() << "TODO: ResourceAPI::getProject";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
[[nodiscard]] virtual Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const
|
[[nodiscard]] virtual Task::Ptr getProjects([[maybe_unused]] QStringList addonIds,
|
||||||
|
[[maybe_unused]] std::shared_ptr<QByteArray> response) const
|
||||||
{
|
{
|
||||||
qWarning() << "TODO";
|
qWarning() << "TODO: ResourceAPI::getProjects";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const
|
[[nodiscard]] virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const
|
||||||
{
|
{
|
||||||
qWarning() << "TODO";
|
qWarning() << "TODO: ResourceAPI::getProjectInfo";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
[[nodiscard]] virtual Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const
|
[[nodiscard]] virtual Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const
|
||||||
{
|
{
|
||||||
qWarning() << "TODO";
|
qWarning() << "TODO: ResourceAPI::getProjectVersions";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +53,8 @@
|
|||||||
#include "net/ChecksumValidator.h"
|
#include "net/ChecksumValidator.h"
|
||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
|
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
|
|
||||||
@ -83,7 +85,7 @@ void PackInstallTask::executeTask()
|
|||||||
NetJob::Ptr netJob{ new NetJob("ATLauncher::VersionFetch", APPLICATION->network()) };
|
NetJob::Ptr netJob{ new NetJob("ATLauncher::VersionFetch", APPLICATION->network()) };
|
||||||
auto searchUrl =
|
auto searchUrl =
|
||||||
QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.json").arg(m_pack_safe_name).arg(m_version_name);
|
QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.json").arg(m_pack_safe_name).arg(m_version_name);
|
||||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response));
|
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded);
|
QObject::connect(netJob.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded);
|
||||||
QObject::connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed);
|
QObject::connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed);
|
||||||
@ -631,7 +633,7 @@ void PackInstallTask::installConfigs()
|
|||||||
auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", path);
|
auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", path);
|
||||||
entry->setStale(true);
|
entry->setStale(true);
|
||||||
|
|
||||||
auto dl = Net::Download::makeCached(url, entry);
|
auto dl = Net::ApiDownload::makeCached(url, entry);
|
||||||
if (!m_version.configs.sha1.isEmpty()) {
|
if (!m_version.configs.sha1.isEmpty()) {
|
||||||
auto rawSha1 = QByteArray::fromHex(m_version.configs.sha1.toLatin1());
|
auto rawSha1 = QByteArray::fromHex(m_version.configs.sha1.toLatin1());
|
||||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
|
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
|
||||||
@ -748,7 +750,7 @@ void PackInstallTask::downloadMods()
|
|||||||
entry->setStale(true);
|
entry->setStale(true);
|
||||||
modsToExtract.insert(entry->getFullPath(), mod);
|
modsToExtract.insert(entry->getFullPath(), mod);
|
||||||
|
|
||||||
auto dl = Net::Download::makeCached(url, entry);
|
auto dl = Net::ApiDownload::makeCached(url, entry);
|
||||||
if (!mod.md5.isEmpty()) {
|
if (!mod.md5.isEmpty()) {
|
||||||
auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
|
auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
|
||||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
|
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
|
||||||
@ -759,7 +761,7 @@ void PackInstallTask::downloadMods()
|
|||||||
entry->setStale(true);
|
entry->setStale(true);
|
||||||
modsToDecomp.insert(entry->getFullPath(), mod);
|
modsToDecomp.insert(entry->getFullPath(), mod);
|
||||||
|
|
||||||
auto dl = Net::Download::makeCached(url, entry);
|
auto dl = Net::ApiDownload::makeCached(url, entry);
|
||||||
if (!mod.md5.isEmpty()) {
|
if (!mod.md5.isEmpty()) {
|
||||||
auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
|
auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
|
||||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
|
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
|
||||||
@ -773,7 +775,7 @@ void PackInstallTask::downloadMods()
|
|||||||
auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", cacheName);
|
auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", cacheName);
|
||||||
entry->setStale(true);
|
entry->setStale(true);
|
||||||
|
|
||||||
auto dl = Net::Download::makeCached(url, entry);
|
auto dl = Net::ApiDownload::makeCached(url, entry);
|
||||||
if (!mod.md5.isEmpty()) {
|
if (!mod.md5.isEmpty()) {
|
||||||
auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
|
auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
|
||||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
|
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
|
||||||
@ -1003,15 +1005,30 @@ static Meta::Version::Ptr getComponentVersion(const QString& uid, const QString&
|
|||||||
if (!vlist)
|
if (!vlist)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (!vlist->isLoaded())
|
if (!vlist->isLoaded()) {
|
||||||
vlist->load(Net::Mode::Online);
|
QEventLoop loadVersionLoop;
|
||||||
|
auto task = vlist->getLoadTask();
|
||||||
|
QObject::connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit);
|
||||||
|
if (!task->isRunning())
|
||||||
|
task->start();
|
||||||
|
|
||||||
|
loadVersionLoop.exec();
|
||||||
|
}
|
||||||
|
|
||||||
auto ver = vlist->getVersion(version);
|
auto ver = vlist->getVersion(version);
|
||||||
if (!ver)
|
if (!ver)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (!ver->isLoaded())
|
if (!ver->isLoaded()) {
|
||||||
|
QEventLoop loadVersionLoop;
|
||||||
ver->load(Net::Mode::Online);
|
ver->load(Net::Mode::Online);
|
||||||
|
auto task = ver->getCurrentTask();
|
||||||
|
QObject::connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit);
|
||||||
|
if (!task->isRunning())
|
||||||
|
task->start();
|
||||||
|
|
||||||
|
loadVersionLoop.exec();
|
||||||
|
}
|
||||||
|
|
||||||
return ver;
|
return ver;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "FileResolvingTask.h"
|
#include "FileResolvingTask.h"
|
||||||
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
|
#include "net/ApiUpload.h"
|
||||||
#include "net/Upload.h"
|
#include "net/Upload.h"
|
||||||
|
|
||||||
#include "modplatform/modrinth/ModrinthPackIndex.h"
|
#include "modplatform/modrinth/ModrinthPackIndex.h"
|
||||||
@ -38,7 +40,7 @@ void Flame::FileResolvingTask::executeTask()
|
|||||||
return l;
|
return l;
|
||||||
}));
|
}));
|
||||||
QByteArray data = Json::toText(object);
|
QByteArray data = Json::toText(object);
|
||||||
auto dl = Net::Upload::makeByteArray(QUrl("https://api.curseforge.com/v1/mods/files"), result, data);
|
auto dl = Net::ApiUpload::makeByteArray(QUrl("https://api.curseforge.com/v1/mods/files"), result, data);
|
||||||
m_dljob->addNetAction(dl);
|
m_dljob->addNetAction(dl);
|
||||||
|
|
||||||
auto step_progress = std::make_shared<TaskStepProgress>();
|
auto step_progress = std::make_shared<TaskStepProgress>();
|
||||||
@ -93,13 +95,13 @@ void Flame::FileResolvingTask::netJobFinished()
|
|||||||
auto& out = m_toProcess.files[fileid];
|
auto& out = m_toProcess.files[fileid];
|
||||||
try {
|
try {
|
||||||
out.parseFromObject(Json::requireObject(file));
|
out.parseFromObject(Json::requireObject(file));
|
||||||
} catch (const JSONValidationError& e) {
|
} catch ([[maybe_unused]] const JSONValidationError& e) {
|
||||||
qDebug() << "Blocked mod on curseforge" << out.fileName;
|
qDebug() << "Blocked mod on curseforge" << out.fileName;
|
||||||
auto hash = out.hash;
|
auto hash = out.hash;
|
||||||
if (!hash.isEmpty()) {
|
if (!hash.isEmpty()) {
|
||||||
auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash);
|
auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash);
|
||||||
auto output = std::make_shared<QByteArray>();
|
auto output = std::make_shared<QByteArray>();
|
||||||
auto dl = Net::Download::makeByteArray(QUrl(url), output);
|
auto dl = Net::ApiDownload::makeByteArray(QUrl(url), output);
|
||||||
QObject::connect(dl.get(), &Net::Download::succeeded, [&out]() { out.resolved = true; });
|
QObject::connect(dl.get(), &Net::Download::succeeded, [&out]() { out.resolved = true; });
|
||||||
|
|
||||||
m_checkJob->addNetAction(dl);
|
m_checkJob->addNetAction(dl);
|
||||||
@ -171,7 +173,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished()
|
|||||||
auto projectId = mod->projectId;
|
auto projectId = mod->projectId;
|
||||||
auto output = std::make_shared<QByteArray>();
|
auto output = std::make_shared<QByteArray>();
|
||||||
auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId);
|
auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId);
|
||||||
auto dl = Net::Download::makeByteArray(url, output);
|
auto dl = Net::ApiDownload::makeByteArray(url, output);
|
||||||
qDebug() << "Fetching url slug for file:" << mod->fileName;
|
qDebug() << "Fetching url slug for file:" << mod->fileName;
|
||||||
QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() {
|
QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() {
|
||||||
auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done
|
auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
|
#include "net/ApiUpload.h"
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
#include "net/Upload.h"
|
#include "net/Upload.h"
|
||||||
|
|
||||||
@ -26,7 +28,7 @@ Task::Ptr FlameAPI::matchFingerprints(const QList<uint>& fingerprints, std::shar
|
|||||||
QJsonDocument body(body_obj);
|
QJsonDocument body(body_obj);
|
||||||
auto body_raw = body.toJson();
|
auto body_raw = body.toJson();
|
||||||
|
|
||||||
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/fingerprints"), response, body_raw));
|
netJob->addNetAction(Net::ApiUpload::makeByteArray(QString("https://api.curseforge.com/v1/fingerprints"), response, body_raw));
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
@ -38,7 +40,7 @@ auto FlameAPI::getModFileChangelog(int modId, int fileId) -> QString
|
|||||||
|
|
||||||
auto netJob = makeShared<NetJob>(QString("Flame::FileChangelog"), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("Flame::FileChangelog"), APPLICATION->network());
|
||||||
auto response = std::make_shared<QByteArray>();
|
auto response = std::make_shared<QByteArray>();
|
||||||
netJob->addNetAction(Net::Download::makeByteArray(
|
netJob->addNetAction(Net::ApiDownload::makeByteArray(
|
||||||
QString("https://api.curseforge.com/v1/mods/%1/files/%2/changelog")
|
QString("https://api.curseforge.com/v1/mods/%1/files/%2/changelog")
|
||||||
.arg(QString::fromStdString(std::to_string(modId)), QString::fromStdString(std::to_string(fileId))),
|
.arg(QString::fromStdString(std::to_string(modId)), QString::fromStdString(std::to_string(fileId))),
|
||||||
response));
|
response));
|
||||||
@ -73,8 +75,8 @@ auto FlameAPI::getModDescription(int modId) -> QString
|
|||||||
|
|
||||||
auto netJob = makeShared<NetJob>(QString("Flame::ModDescription"), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("Flame::ModDescription"), APPLICATION->network());
|
||||||
auto response = std::make_shared<QByteArray>();
|
auto response = std::make_shared<QByteArray>();
|
||||||
netJob->addNetAction(
|
netJob->addNetAction(Net::ApiDownload::makeByteArray(
|
||||||
Net::Download::makeByteArray(QString("https://api.curseforge.com/v1/mods/%1/description").arg(QString::number(modId)), response));
|
QString("https://api.curseforge.com/v1/mods/%1/description").arg(QString::number(modId)), response));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &description] {
|
QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &description] {
|
||||||
QJsonParseError parse_error{};
|
QJsonParseError parse_error{};
|
||||||
@ -113,7 +115,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe
|
|||||||
auto response = std::make_shared<QByteArray>();
|
auto response = std::make_shared<QByteArray>();
|
||||||
ModPlatform::IndexedVersion ver;
|
ModPlatform::IndexedVersion ver;
|
||||||
|
|
||||||
netJob->addNetAction(Net::Download::makeByteArray(versions_url, response));
|
netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::succeeded, [response, args, &ver] {
|
QObject::connect(netJob.get(), &NetJob::succeeded, [response, args, &ver] {
|
||||||
QJsonParseError parse_error{};
|
QJsonParseError parse_error{};
|
||||||
@ -173,7 +175,7 @@ Task::Ptr FlameAPI::getProjects(QStringList addonIds, std::shared_ptr<QByteArray
|
|||||||
QJsonDocument body(body_obj);
|
QJsonDocument body(body_obj);
|
||||||
auto body_raw = body.toJson();
|
auto body_raw = body.toJson();
|
||||||
|
|
||||||
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods"), response, body_raw));
|
netJob->addNetAction(Net::ApiUpload::makeByteArray(QString("https://api.curseforge.com/v1/mods"), response, body_raw));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; });
|
QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; });
|
||||||
|
|
||||||
@ -195,13 +197,24 @@ Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, std::shared_ptr<QByteAr
|
|||||||
QJsonDocument body(body_obj);
|
QJsonDocument body(body_obj);
|
||||||
auto body_raw = body.toJson();
|
auto body_raw = body.toJson();
|
||||||
|
|
||||||
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods/files"), response, body_raw));
|
netJob->addNetAction(Net::ApiUpload::makeByteArray(QString("https://api.curseforge.com/v1/mods/files"), response, body_raw));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; });
|
QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; });
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Task::Ptr FlameAPI::getFile(const QString& addonId, const QString& fileId, std::shared_ptr<QByteArray> response) const
|
||||||
|
{
|
||||||
|
auto netJob = makeShared<NetJob>(QString("Flame::GetFile"), APPLICATION->network());
|
||||||
|
netJob->addNetAction(
|
||||||
|
Net::ApiDownload::makeByteArray(QUrl(QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(addonId, fileId)), response));
|
||||||
|
|
||||||
|
QObject::connect(netJob.get(), &NetJob::failed, [addonId, fileId] { qDebug() << "Flame API file failure" << addonId << fileId; });
|
||||||
|
|
||||||
|
return netJob;
|
||||||
|
}
|
||||||
|
|
||||||
// https://docs.curseforge.com/?python#tocS_ModsSearchSortField
|
// https://docs.curseforge.com/?python#tocS_ModsSearchSortField
|
||||||
static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") },
|
static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") },
|
||||||
{ 2, "Popularity", QObject::tr("Sort by Popularity") },
|
{ 2, "Popularity", QObject::tr("Sort by Popularity") },
|
||||||
|
@ -20,6 +20,7 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
|
Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
|
||||||
Task::Ptr matchFingerprints(const QList<uint>& fingerprints, std::shared_ptr<QByteArray> response);
|
Task::Ptr matchFingerprints(const QList<uint>& fingerprints, std::shared_ptr<QByteArray> response);
|
||||||
Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const;
|
Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const;
|
||||||
|
Task::Ptr getFile(const QString& addonId, const QString& fileId, std::shared_ptr<QByteArray> response) const;
|
||||||
|
|
||||||
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
||||||
|
|
||||||
|
@ -13,6 +13,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"
|
||||||
|
|
||||||
static FlameAPI api;
|
static FlameAPI api;
|
||||||
|
|
||||||
bool FlameCheckUpdate::abort()
|
bool FlameCheckUpdate::abort()
|
||||||
@ -33,7 +35,7 @@ ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info)
|
|||||||
|
|
||||||
auto response = std::make_shared<QByteArray>();
|
auto response = std::make_shared<QByteArray>();
|
||||||
auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(ver_info.addonId.toString());
|
auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(ver_info.addonId.toString());
|
||||||
auto dl = Net::Download::makeByteArray(url, response);
|
auto dl = Net::ApiDownload::makeByteArray(url, response);
|
||||||
get_project_job->addNetAction(dl);
|
get_project_job->addNetAction(dl);
|
||||||
|
|
||||||
QObject::connect(get_project_job, &NetJob::succeeded, [response, &pack]() {
|
QObject::connect(get_project_job, &NetJob::succeeded, [response, &pack]() {
|
||||||
@ -77,7 +79,7 @@ ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId)
|
|||||||
|
|
||||||
auto response = std::make_shared<QByteArray>();
|
auto response = std::make_shared<QByteArray>();
|
||||||
auto url = QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(QString::number(addonId), QString::number(fileId));
|
auto url = QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(QString::number(addonId), QString::number(fileId));
|
||||||
auto dl = Net::Download::makeByteArray(url, response);
|
auto dl = Net::ApiDownload::makeByteArray(url, response);
|
||||||
get_file_info_job->addNetAction(dl);
|
get_file_info_job->addNetAction(dl);
|
||||||
|
|
||||||
QObject::connect(get_file_info_job, &NetJob::succeeded, [response, &ver]() {
|
QObject::connect(get_file_info_job, &NetJob::succeeded, [response, &ver]() {
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
#include "meta/VersionList.h"
|
#include "meta/VersionList.h"
|
||||||
#include "minecraft/World.h"
|
#include "minecraft/World.h"
|
||||||
#include "minecraft/mod/tasks/LocalResourceParse.h"
|
#include "minecraft/mod/tasks/LocalResourceParse.h"
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
|
|
||||||
static const FlameAPI api;
|
static const FlameAPI api;
|
||||||
|
|
||||||
@ -353,11 +354,11 @@ bool FlameCreationTask::createInstance()
|
|||||||
id.remove("forge-");
|
id.remove("forge-");
|
||||||
loaderType = "forge";
|
loaderType = "forge";
|
||||||
loaderUid = "net.minecraftforge";
|
loaderUid = "net.minecraftforge";
|
||||||
} else if (loaderType == "fabric") {
|
} else if (id.startsWith("fabric-")) {
|
||||||
id.remove("fabric-");
|
id.remove("fabric-");
|
||||||
loaderType = "fabric";
|
loaderType = "fabric";
|
||||||
loaderUid = "net.fabricmc.fabric-loader";
|
loaderUid = "net.fabricmc.fabric-loader";
|
||||||
} else if (loaderType == "quilt") {
|
} else if (id.startsWith("quilt-")) {
|
||||||
id.remove("quilt-");
|
id.remove("quilt-");
|
||||||
loaderType = "quilt";
|
loaderType = "quilt";
|
||||||
loaderUid = "org.quiltmc.quilt-loader";
|
loaderUid = "org.quiltmc.quilt-loader";
|
||||||
@ -523,7 +524,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
|||||||
case Flame::File::Type::Mod: {
|
case Flame::File::Type::Mod: {
|
||||||
if (!result.url.isEmpty()) {
|
if (!result.url.isEmpty()) {
|
||||||
qDebug() << "Will download" << result.url << "to" << path;
|
qDebug() << "Will download" << result.url << "to" << path;
|
||||||
auto dl = Net::Download::makeFile(result.url, path);
|
auto dl = Net::ApiDownload::makeFile(result.url, path);
|
||||||
m_files_job->addNetAction(dl);
|
m_files_job->addNetAction(dl);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -54,7 +54,7 @@ void FlameMod::loadURLs(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
|||||||
pack.extraDataLoaded = true;
|
pack.extraDataLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlameMod::loadBody(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
void FlameMod::loadBody(ModPlatform::IndexedPack& pack, [[maybe_unused]] QJsonObject& obj)
|
||||||
{
|
{
|
||||||
pack.extraData.body = api.getModDescription(pack.addonId.toInt());
|
pack.extraData.body = api.getModDescription(pack.addonId.toInt());
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ static QString enumToString(int hash_algorithm)
|
|||||||
|
|
||||||
void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||||
QJsonArray& arr,
|
QJsonArray& arr,
|
||||||
const shared_qobject_ptr<QNetworkAccessManager>& network,
|
[[maybe_unused]] const shared_qobject_ptr<QNetworkAccessManager>& network,
|
||||||
const BaseInstance* inst)
|
const BaseInstance* inst)
|
||||||
{
|
{
|
||||||
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
||||||
@ -193,4 +193,4 @@ ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::
|
|||||||
};
|
};
|
||||||
std::sort(versions.begin(), versions.end(), orderSortPredicate);
|
std::sort(versions.begin(), versions.end(), orderSortPredicate);
|
||||||
return versions.front();
|
return versions.front();
|
||||||
};
|
}
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#include "modplatform/ModIndex.h"
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
|
|
||||||
Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&& callbacks) const
|
Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&& callbacks) const
|
||||||
{
|
{
|
||||||
auto search_url_optional = getSearchURL(args);
|
auto search_url_optional = getSearchURL(args);
|
||||||
@ -23,7 +25,7 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&
|
|||||||
auto response = std::make_shared<QByteArray>();
|
auto response = std::make_shared<QByteArray>();
|
||||||
auto netJob = makeShared<NetJob>(QString("%1::Search").arg(debugName()), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("%1::Search").arg(debugName()), APPLICATION->network());
|
||||||
|
|
||||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(search_url), response));
|
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(search_url), response));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::succeeded, [this, response, callbacks] {
|
QObject::connect(netJob.get(), &NetJob::succeeded, [this, response, callbacks] {
|
||||||
QJsonParseError parse_error{};
|
QJsonParseError parse_error{};
|
||||||
@ -85,7 +87,7 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi
|
|||||||
auto netJob = makeShared<NetJob>(QString("%1::Versions").arg(args.pack.name), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("%1::Versions").arg(args.pack.name), APPLICATION->network());
|
||||||
auto response = std::make_shared<QByteArray>();
|
auto response = std::make_shared<QByteArray>();
|
||||||
|
|
||||||
netJob->addNetAction(Net::Download::makeByteArray(versions_url, response));
|
netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::succeeded, [response, callbacks, args] {
|
QObject::connect(netJob.get(), &NetJob::succeeded, [response, callbacks, args] {
|
||||||
QJsonParseError parse_error{};
|
QJsonParseError parse_error{};
|
||||||
@ -113,7 +115,7 @@ Task::Ptr NetworkResourceAPI::getProject(QString addonId, std::shared_ptr<QByteA
|
|||||||
|
|
||||||
auto netJob = makeShared<NetJob>(QString("%1::GetProject").arg(addonId), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("%1::GetProject").arg(addonId), APPLICATION->network());
|
||||||
|
|
||||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(project_url), response));
|
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(project_url), response));
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
@ -145,4 +147,4 @@ Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args,
|
|||||||
});
|
});
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
};
|
}
|
||||||
|
@ -40,6 +40,8 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
|
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
|
|
||||||
namespace LegacyFTB {
|
namespace LegacyFTB {
|
||||||
|
|
||||||
void PackFetchTask::fetch()
|
void PackFetchTask::fetch()
|
||||||
@ -51,7 +53,7 @@ void PackFetchTask::fetch()
|
|||||||
|
|
||||||
QUrl publicPacksUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/modpacks.xml");
|
QUrl publicPacksUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/modpacks.xml");
|
||||||
qDebug() << "Downloading public version info from" << publicPacksUrl.toString();
|
qDebug() << "Downloading public version info from" << publicPacksUrl.toString();
|
||||||
jobPtr->addNetAction(Net::Download::makeByteArray(publicPacksUrl, publicModpacksXmlFileData));
|
jobPtr->addNetAction(Net::ApiDownload::makeByteArray(publicPacksUrl, publicModpacksXmlFileData));
|
||||||
|
|
||||||
QUrl thirdPartyUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/thirdparty.xml");
|
QUrl thirdPartyUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/thirdparty.xml");
|
||||||
qDebug() << "Downloading thirdparty version info from" << thirdPartyUrl.toString();
|
qDebug() << "Downloading thirdparty version info from" << thirdPartyUrl.toString();
|
||||||
@ -71,7 +73,7 @@ void PackFetchTask::fetchPrivate(const QStringList& toFetch)
|
|||||||
for (auto& packCode : toFetch) {
|
for (auto& packCode : toFetch) {
|
||||||
auto data = std::make_shared<QByteArray>();
|
auto data = std::make_shared<QByteArray>();
|
||||||
NetJob* job = new NetJob("Fetching private pack", m_network);
|
NetJob* job = new NetJob("Fetching private pack", m_network);
|
||||||
job->addNetAction(Net::Download::makeByteArray(privatePackBaseUrl.arg(packCode), data));
|
job->addNetAction(Net::ApiDownload::makeByteArray(privatePackBaseUrl.arg(packCode), data));
|
||||||
|
|
||||||
QObject::connect(job, &NetJob::succeeded, this, [this, job, data, packCode] {
|
QObject::connect(job, &NetJob::succeeded, this, [this, job, data, packCode] {
|
||||||
ModpackList packs;
|
ModpackList packs;
|
||||||
|
@ -48,6 +48,8 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
|
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
|
|
||||||
namespace LegacyFTB {
|
namespace LegacyFTB {
|
||||||
|
|
||||||
PackInstallTask::PackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, Modpack pack, QString version)
|
PackInstallTask::PackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, Modpack pack, QString version)
|
||||||
@ -77,7 +79,7 @@ void PackInstallTask::downloadPack()
|
|||||||
} else {
|
} else {
|
||||||
url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "modpacks/%1").arg(archivePath);
|
url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "modpacks/%1").arg(archivePath);
|
||||||
}
|
}
|
||||||
netJobContainer->addNetAction(Net::Download::makeFile(url, archivePath));
|
netJobContainer->addNetAction(Net::ApiDownload::makeFile(url, archivePath));
|
||||||
|
|
||||||
connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip);
|
connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip);
|
||||||
connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed);
|
connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed);
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
|
#include "net/ApiUpload.h"
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
#include "net/Upload.h"
|
#include "net/Upload.h"
|
||||||
|
|
||||||
@ -13,7 +15,7 @@ Task::Ptr ModrinthAPI::currentVersion(QString hash, QString hash_format, std::sh
|
|||||||
{
|
{
|
||||||
auto netJob = makeShared<NetJob>(QString("Modrinth::GetCurrentVersion"), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("Modrinth::GetCurrentVersion"), APPLICATION->network());
|
||||||
|
|
||||||
netJob->addNetAction(Net::Download::makeByteArray(
|
netJob->addNetAction(Net::ApiDownload::makeByteArray(
|
||||||
QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1?algorithm=%2").arg(hash, hash_format), response));
|
QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1?algorithm=%2").arg(hash, hash_format), response));
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
@ -31,7 +33,7 @@ Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_f
|
|||||||
QJsonDocument body(body_obj);
|
QJsonDocument body(body_obj);
|
||||||
auto body_raw = body.toJson();
|
auto body_raw = body.toJson();
|
||||||
|
|
||||||
netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), response, body_raw));
|
netJob->addNetAction(Net::ApiUpload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), response, body_raw));
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
@ -60,7 +62,7 @@ Task::Ptr ModrinthAPI::latestVersion(QString hash,
|
|||||||
QJsonDocument body(body_obj);
|
QJsonDocument body(body_obj);
|
||||||
auto body_raw = body.toJson();
|
auto body_raw = body.toJson();
|
||||||
|
|
||||||
netJob->addNetAction(Net::Upload::makeByteArray(
|
netJob->addNetAction(Net::ApiUpload::makeByteArray(
|
||||||
QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1/update?algorithm=%2").arg(hash, hash_format), response, body_raw));
|
QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1/update?algorithm=%2").arg(hash, hash_format), response, body_raw));
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
@ -93,7 +95,8 @@ Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes,
|
|||||||
QJsonDocument body(body_obj);
|
QJsonDocument body(body_obj);
|
||||||
auto body_raw = body.toJson();
|
auto body_raw = body.toJson();
|
||||||
|
|
||||||
netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), response, body_raw));
|
netJob->addNetAction(
|
||||||
|
Net::ApiUpload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), response, body_raw));
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
@ -103,7 +106,7 @@ Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, std::shared_ptr<QByteAr
|
|||||||
auto netJob = makeShared<NetJob>(QString("Modrinth::GetProjects"), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("Modrinth::GetProjects"), APPLICATION->network());
|
||||||
auto searchUrl = getMultipleModInfoURL(addonIds);
|
auto searchUrl = getMultipleModInfoURL(addonIds);
|
||||||
|
|
||||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response));
|
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response));
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "net/ChecksumValidator.h"
|
#include "net/ChecksumValidator.h"
|
||||||
|
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
|
|
||||||
@ -238,7 +239,7 @@ bool ModrinthCreationTask::createInstance()
|
|||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Will try to download" << file.downloads.front() << "to" << file_path;
|
qDebug() << "Will try to download" << file.downloads.front() << "to" << file_path;
|
||||||
auto dl = Net::Download::makeFile(file.downloads.dequeue(), file_path);
|
auto dl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path);
|
||||||
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
||||||
m_files_job->addNetAction(dl);
|
m_files_job->addNetAction(dl);
|
||||||
|
|
||||||
@ -247,7 +248,7 @@ bool ModrinthCreationTask::createInstance()
|
|||||||
// MultipleOptionsTask's , once those exist :)
|
// MultipleOptionsTask's , once those exist :)
|
||||||
auto param = dl.toWeakRef();
|
auto param = dl.toWeakRef();
|
||||||
connect(dl.get(), &NetAction::failed, [this, &file, file_path, param] {
|
connect(dl.get(), &NetAction::failed, [this, &file, file_path, param] {
|
||||||
auto ndl = Net::Download::makeFile(file.downloads.dequeue(), file_path);
|
auto ndl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path);
|
||||||
ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
||||||
m_files_job->addNetAction(ndl);
|
m_files_job->addNetAction(ndl);
|
||||||
if (auto shared = param.lock())
|
if (auto shared = param.lock())
|
||||||
|
@ -174,10 +174,10 @@ void ModrinthPackExportTask::parseApiResponse(const std::shared_ptr<QByteArray>
|
|||||||
if (obj.isEmpty())
|
if (obj.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const QJsonArray files = obj["files"].toArray();
|
const QJsonArray files_array = obj["files"].toArray();
|
||||||
if (auto fileIter = std::find_if(files.begin(), files.end(),
|
if (auto fileIter = std::find_if(files_array.begin(), files_array.end(),
|
||||||
[&iterator](const QJsonValue& file) { return file["hashes"]["sha512"] == iterator.value(); });
|
[&iterator](const QJsonValue& file) { return file["hashes"]["sha512"] == iterator.value(); });
|
||||||
fileIter != files.end()) {
|
fileIter != files_array.end()) {
|
||||||
// map the file to the url
|
// map the file to the url
|
||||||
resolvedFiles[iterator.key()] =
|
resolvedFiles[iterator.key()] =
|
||||||
ResolvedFile{ fileIter->toObject()["hashes"].toObject()["sha1"].toString(), iterator.value(),
|
ResolvedFile{ fileIter->toObject()["hashes"].toObject()["sha1"].toString(), iterator.value(),
|
||||||
|
@ -95,7 +95,7 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob
|
|||||||
|
|
||||||
void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||||
QJsonArray& arr,
|
QJsonArray& arr,
|
||||||
const shared_qobject_ptr<QNetworkAccessManager>& network,
|
[[maybe_unused]] const shared_qobject_ptr<QNetworkAccessManager>& network,
|
||||||
const BaseInstance* inst)
|
const BaseInstance* inst)
|
||||||
{
|
{
|
||||||
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
||||||
@ -218,7 +218,7 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Modrinth::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion
|
auto Modrinth::loadDependencyVersions([[maybe_unused]] const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion
|
||||||
{
|
{
|
||||||
QVector<ModPlatform::IndexedVersion> versions;
|
QVector<ModPlatform::IndexedVersion> versions;
|
||||||
|
|
||||||
|
@ -89,7 +89,8 @@ auto intEntry(toml::table table, QString entry_name) -> int
|
|||||||
return node.value_or(0);
|
return node.value_or(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod
|
auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version)
|
||||||
|
-> Mod
|
||||||
{
|
{
|
||||||
Mod mod;
|
Mod mod;
|
||||||
|
|
||||||
@ -114,7 +115,7 @@ auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, Mo
|
|||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto V1::createModFormat(QDir& index_dir, ::Mod& internal_mod, QString slug) -> Mod
|
auto V1::createModFormat(QDir& index_dir, [[maybe_unused]] ::Mod& internal_mod, QString slug) -> Mod
|
||||||
{
|
{
|
||||||
// Try getting metadata if it exists
|
// Try getting metadata if it exists
|
||||||
Mod mod{ getIndexForMod(index_dir, slug) };
|
Mod mod{ getIndexForMod(index_dir, slug) };
|
||||||
@ -241,12 +242,13 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
table = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname)));
|
toml::parse_result result = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname)));
|
||||||
if (!table) {
|
if (!result) {
|
||||||
qWarning() << QString("Could not open file %1!").arg(normalized_fname);
|
qWarning() << QString("Could not open file %1!").arg(normalized_fname);
|
||||||
qWarning() << "Reason: " << QString(table.error().what());
|
qWarning() << "Reason: " << result.error().description();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
table = result.table();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// index_file.close();
|
// index_file.close();
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
|
|
||||||
Technic::SingleZipPackInstallTask::SingleZipPackInstallTask(const QUrl& sourceUrl, const QString& minecraftVersion)
|
Technic::SingleZipPackInstallTask::SingleZipPackInstallTask(const QUrl& sourceUrl, const QString& minecraftVersion)
|
||||||
{
|
{
|
||||||
m_sourceUrl = sourceUrl;
|
m_sourceUrl = sourceUrl;
|
||||||
@ -45,7 +47,7 @@ void Technic::SingleZipPackInstallTask::executeTask()
|
|||||||
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
||||||
entry->setStale(true);
|
entry->setStale(true);
|
||||||
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));
|
||||||
m_archivePath = entry->getFullPath();
|
m_archivePath = entry->getFullPath();
|
||||||
auto job = m_filesNetJob.get();
|
auto job = m_filesNetJob.get();
|
||||||
connect(job, &NetJob::succeeded, this, &Technic::SingleZipPackInstallTask::downloadSucceeded);
|
connect(job, &NetJob::succeeded, this, &Technic::SingleZipPackInstallTask::downloadSucceeded);
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
#include "SolderPackManifest.h"
|
#include "SolderPackManifest.h"
|
||||||
#include "TechnicPackProcessor.h"
|
#include "TechnicPackProcessor.h"
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
#include "net/ChecksumValidator.h"
|
#include "net/ChecksumValidator.h"
|
||||||
|
|
||||||
Technic::SolderPackInstallTask::SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network,
|
Technic::SolderPackInstallTask::SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network,
|
||||||
@ -71,7 +72,7 @@ void Technic::SolderPackInstallTask::executeTask()
|
|||||||
|
|
||||||
m_filesNetJob.reset(new NetJob(tr("Resolving modpack files"), m_network));
|
m_filesNetJob.reset(new NetJob(tr("Resolving modpack files"), m_network));
|
||||||
auto sourceUrl = QString("%1/modpack/%2/%3").arg(m_solderUrl.toString(), m_pack, m_version);
|
auto sourceUrl = QString("%1/modpack/%2/%3").arg(m_solderUrl.toString(), m_pack, m_version);
|
||||||
m_filesNetJob->addNetAction(Net::Download::makeByteArray(sourceUrl, m_response));
|
m_filesNetJob->addNetAction(Net::ApiDownload::makeByteArray(sourceUrl, m_response));
|
||||||
|
|
||||||
auto job = m_filesNetJob.get();
|
auto job = m_filesNetJob.get();
|
||||||
connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::fileListSucceeded);
|
connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::fileListSucceeded);
|
||||||
@ -111,7 +112,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded()
|
|||||||
for (const auto& mod : build.mods) {
|
for (const auto& mod : build.mods) {
|
||||||
auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i));
|
auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i));
|
||||||
|
|
||||||
auto dl = Net::Download::makeFile(mod.url, path);
|
auto dl = Net::ApiDownload::makeFile(mod.url, path);
|
||||||
if (!mod.md5.isEmpty()) {
|
if (!mod.md5.isEmpty()) {
|
||||||
auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
|
auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
|
||||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
|
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
|
||||||
|
@ -31,7 +31,7 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings,
|
|||||||
const QString& instIcon,
|
const QString& instIcon,
|
||||||
const QString& stagingPath,
|
const QString& stagingPath,
|
||||||
const QString& minecraftVersion,
|
const QString& minecraftVersion,
|
||||||
const bool isSolder)
|
[[maybe_unused]] const bool isSolder)
|
||||||
{
|
{
|
||||||
QString minecraftPath = FS::PathCombine(stagingPath, ".minecraft");
|
QString minecraftPath = FS::PathCombine(stagingPath, ".minecraft");
|
||||||
QString configPath = FS::PathCombine(stagingPath, "instance.cfg");
|
QString configPath = FS::PathCombine(stagingPath, "instance.cfg");
|
||||||
@ -138,15 +138,15 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings,
|
|||||||
try {
|
try {
|
||||||
QJsonDocument doc = Json::requireDocument(data);
|
QJsonDocument doc = Json::requireDocument(data);
|
||||||
QJsonObject root = Json::requireObject(doc, "version.json");
|
QJsonObject root = Json::requireObject(doc, "version.json");
|
||||||
QString minecraftVersion = Json::ensureString(root, "inheritsFrom", QString(), "");
|
QString packMinecraftVersion = Json::ensureString(root, "inheritsFrom", QString(), "");
|
||||||
if (minecraftVersion.isEmpty()) {
|
if (packMinecraftVersion.isEmpty()) {
|
||||||
if (fmlMinecraftVersion.isEmpty()) {
|
if (fmlMinecraftVersion.isEmpty()) {
|
||||||
emit failed(tr("Could not understand \"version.json\":\ninheritsFrom is missing"));
|
emit failed(tr("Could not understand \"version.json\":\ninheritsFrom is missing"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
minecraftVersion = fmlMinecraftVersion;
|
packMinecraftVersion = fmlMinecraftVersion;
|
||||||
}
|
}
|
||||||
components->setComponentVersion("net.minecraft", minecraftVersion, true);
|
components->setComponentVersion("net.minecraft", packMinecraftVersion, true);
|
||||||
for (auto library : Json::ensureArray(root, "libraries", {})) {
|
for (auto library : Json::ensureArray(root, "libraries", {})) {
|
||||||
if (!library.isObject()) {
|
if (!library.isObject()) {
|
||||||
continue;
|
continue;
|
||||||
|
66
launcher/net/ApiDownload.cpp
Normal file
66
launcher/net/ApiDownload.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "net/ApiDownload.h"
|
||||||
|
#include "ByteArraySink.h"
|
||||||
|
#include "ChecksumValidator.h"
|
||||||
|
#include "MetaCacheSink.h"
|
||||||
|
#include "net/NetAction.h"
|
||||||
|
|
||||||
|
namespace Net {
|
||||||
|
|
||||||
|
auto ApiDownload::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr
|
||||||
|
{
|
||||||
|
auto dl = makeShared<ApiDownload>();
|
||||||
|
dl->m_url = url;
|
||||||
|
dl->setObjectName(QString("CACHE:") + url.toString());
|
||||||
|
dl->m_options = options;
|
||||||
|
auto md5Node = new ChecksumValidator(QCryptographicHash::Md5);
|
||||||
|
auto cachedNode = new MetaCacheSink(entry, md5Node, options.testFlag(Option::MakeEternal));
|
||||||
|
dl->m_sink.reset(cachedNode);
|
||||||
|
return dl;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ApiDownload::makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Options options) -> Download::Ptr
|
||||||
|
{
|
||||||
|
auto dl = makeShared<ApiDownload>();
|
||||||
|
dl->m_url = url;
|
||||||
|
dl->setObjectName(QString("BYTES:") + url.toString());
|
||||||
|
dl->m_options = options;
|
||||||
|
dl->m_sink.reset(new ByteArraySink(output));
|
||||||
|
return dl;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ApiDownload::makeFile(QUrl url, QString path, Options options) -> Download::Ptr
|
||||||
|
{
|
||||||
|
auto dl = makeShared<ApiDownload>();
|
||||||
|
dl->m_url = url;
|
||||||
|
dl->setObjectName(QString("FILE:") + url.toString());
|
||||||
|
dl->m_options = options;
|
||||||
|
dl->m_sink.reset(new FileSink(path));
|
||||||
|
return dl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiDownload::init()
|
||||||
|
{
|
||||||
|
qDebug() << "Setting up api download";
|
||||||
|
auto api_headers = new ApiHeaderProxy();
|
||||||
|
addHeaderProxy(api_headers);
|
||||||
|
}
|
||||||
|
} // namespace Net
|
38
launcher/net/ApiDownload.h
Normal file
38
launcher/net/ApiDownload.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ApiHeaderProxy.h"
|
||||||
|
#include "Download.h"
|
||||||
|
|
||||||
|
namespace Net {
|
||||||
|
|
||||||
|
class ApiDownload : public Download {
|
||||||
|
public:
|
||||||
|
virtual ~ApiDownload() = default;
|
||||||
|
|
||||||
|
static auto makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions) -> Download::Ptr;
|
||||||
|
static auto makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Options options = Option::NoOptions) -> Download::Ptr;
|
||||||
|
static auto makeFile(QUrl url, QString path, Options options = Option::NoOptions) -> Download::Ptr;
|
||||||
|
|
||||||
|
void init() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Net
|
49
launcher/net/ApiHeaderProxy.h
Normal file
49
launcher/net/ApiHeaderProxy.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
|
#include "BuildConfig.h"
|
||||||
|
#include "net/HeaderProxy.h"
|
||||||
|
|
||||||
|
namespace Net {
|
||||||
|
|
||||||
|
class ApiHeaderProxy : public HeaderProxy {
|
||||||
|
public:
|
||||||
|
ApiHeaderProxy() : HeaderProxy() {}
|
||||||
|
virtual ~ApiHeaderProxy() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual QList<HeaderPair> headers(const QNetworkRequest& request) const override
|
||||||
|
{
|
||||||
|
QList<HeaderPair> hdrs;
|
||||||
|
if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) {
|
||||||
|
hdrs.append({ "x-api-key", APPLICATION->getFlameAPIKey().toUtf8() });
|
||||||
|
} else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() ||
|
||||||
|
request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) {
|
||||||
|
QString token = APPLICATION->getModrinthAPIToken();
|
||||||
|
if (!token.isNull())
|
||||||
|
hdrs.append({ "Authorization", token.toUtf8() });
|
||||||
|
}
|
||||||
|
return hdrs;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Net
|
43
launcher/net/ApiUpload.cpp
Normal file
43
launcher/net/ApiUpload.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "net/ApiUpload.h"
|
||||||
|
#include "ByteArraySink.h"
|
||||||
|
#include "ChecksumValidator.h"
|
||||||
|
#include "MetaCacheSink.h"
|
||||||
|
#include "net/NetAction.h"
|
||||||
|
|
||||||
|
namespace Net {
|
||||||
|
|
||||||
|
Upload::Ptr ApiUpload::makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, QByteArray m_post_data)
|
||||||
|
{
|
||||||
|
auto up = makeShared<ApiUpload>();
|
||||||
|
up->m_url = std::move(url);
|
||||||
|
up->m_sink.reset(new ByteArraySink(output));
|
||||||
|
up->m_post_data = std::move(m_post_data);
|
||||||
|
return up;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiUpload::init()
|
||||||
|
{
|
||||||
|
qDebug() << "Setting up api upload";
|
||||||
|
auto api_headers = new ApiHeaderProxy();
|
||||||
|
addHeaderProxy(api_headers);
|
||||||
|
}
|
||||||
|
} // namespace Net
|
36
launcher/net/ApiUpload.h
Normal file
36
launcher/net/ApiUpload.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ApiHeaderProxy.h"
|
||||||
|
#include "Upload.h"
|
||||||
|
|
||||||
|
namespace Net {
|
||||||
|
|
||||||
|
class ApiUpload : public Upload {
|
||||||
|
public:
|
||||||
|
virtual ~ApiUpload() = default;
|
||||||
|
|
||||||
|
static Upload::Ptr makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, QByteArray m_post_data);
|
||||||
|
|
||||||
|
void init() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Net
|
@ -42,8 +42,6 @@ namespace Net {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Sink object for downloads that uses an external QByteArray it doesn't own as a target.
|
* Sink object for downloads that uses an external QByteArray it doesn't own as a target.
|
||||||
* FIXME: It is possible that the QByteArray is freed while we're doing some operation on it,
|
|
||||||
* causing a segmentation fault.
|
|
||||||
*/
|
*/
|
||||||
class ByteArraySink : public Sink {
|
class ByteArraySink : public Sink {
|
||||||
public:
|
public:
|
||||||
|
@ -47,17 +47,11 @@
|
|||||||
#include "ChecksumValidator.h"
|
#include "ChecksumValidator.h"
|
||||||
#include "MetaCacheSink.h"
|
#include "MetaCacheSink.h"
|
||||||
|
|
||||||
#include "Application.h"
|
|
||||||
#include "BuildConfig.h"
|
|
||||||
|
|
||||||
#include "net/Logging.h"
|
|
||||||
#include "net/NetAction.h"
|
#include "net/NetAction.h"
|
||||||
|
|
||||||
#include "MMCTime.h"
|
|
||||||
#include "StringUtils.h"
|
|
||||||
|
|
||||||
namespace Net {
|
namespace Net {
|
||||||
|
|
||||||
|
#if defined(LAUNCHER_APPLICATION)
|
||||||
auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr
|
auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr
|
||||||
{
|
{
|
||||||
auto dl = makeShared<Download>();
|
auto dl = makeShared<Download>();
|
||||||
@ -69,6 +63,7 @@ auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Down
|
|||||||
dl->m_sink.reset(cachedNode);
|
dl->m_sink.reset(cachedNode);
|
||||||
return dl;
|
return dl;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
auto Download::makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Options options) -> Download::Ptr
|
auto Download::makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Options options) -> Download::Ptr
|
||||||
{
|
{
|
||||||
@ -90,259 +85,8 @@ auto Download::makeFile(QUrl url, QString path, Options options) -> Download::Pt
|
|||||||
return dl;
|
return dl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Download::addValidator(Validator* v)
|
QNetworkReply* Download::getReply(QNetworkRequest& request)
|
||||||
{
|
{
|
||||||
m_sink->addValidator(v);
|
return m_network->get(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Download::executeTask()
|
|
||||||
{
|
|
||||||
setStatus(tr("Downloading %1").arg(StringUtils::truncateUrlHumanFriendly(m_url, 80)));
|
|
||||||
|
|
||||||
if (getState() == Task::State::AbortedByUser) {
|
|
||||||
qCWarning(taskDownloadLogC) << getUid().toString() << "Attempt to start an aborted Download:" << m_url.toString();
|
|
||||||
emitAborted();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QNetworkRequest request(m_url);
|
|
||||||
m_state = m_sink->init(request);
|
|
||||||
switch (m_state) {
|
|
||||||
case State::Succeeded:
|
|
||||||
emit succeeded();
|
|
||||||
qCDebug(taskDownloadLogC) << getUid().toString() << "Download cache hit " << m_url.toString();
|
|
||||||
return;
|
|
||||||
case State::Running:
|
|
||||||
qCDebug(taskDownloadLogC) << getUid().toString() << "Downloading " << m_url.toString();
|
|
||||||
break;
|
|
||||||
case State::Inactive:
|
|
||||||
case State::Failed:
|
|
||||||
emitFailed();
|
|
||||||
return;
|
|
||||||
case State::AbortedByUser:
|
|
||||||
emitAborted();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8());
|
|
||||||
// TODO remove duplication
|
|
||||||
if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) {
|
|
||||||
request.setRawHeader("x-api-key", APPLICATION->getFlameAPIKey().toUtf8());
|
|
||||||
} else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() ||
|
|
||||||
request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) {
|
|
||||||
QString token = APPLICATION->getModrinthAPIToken();
|
|
||||||
if (!token.isNull())
|
|
||||||
request.setRawHeader("Authorization", token.toUtf8());
|
|
||||||
}
|
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
|
||||||
request.setTransferTimeout();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_last_progress_time = m_clock.now();
|
|
||||||
m_last_progress_bytes = 0;
|
|
||||||
|
|
||||||
QNetworkReply* rep = m_network->get(request);
|
|
||||||
m_reply.reset(rep);
|
|
||||||
connect(rep, &QNetworkReply::downloadProgress, this, &Download::downloadProgress);
|
|
||||||
connect(rep, &QNetworkReply::finished, this, &Download::downloadFinished);
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15
|
|
||||||
connect(rep, &QNetworkReply::errorOccurred, this, &Download::downloadError);
|
|
||||||
#else
|
|
||||||
connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &Download::downloadError);
|
|
||||||
#endif
|
|
||||||
connect(rep, &QNetworkReply::sslErrors, this, &Download::sslErrors);
|
|
||||||
connect(rep, &QNetworkReply::readyRead, this, &Download::downloadReadyRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
|
||||||
{
|
|
||||||
auto now = m_clock.now();
|
|
||||||
auto elapsed = now - m_last_progress_time;
|
|
||||||
|
|
||||||
// use milliseconds for speed precision
|
|
||||||
auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed);
|
|
||||||
auto bytes_received_since = bytesReceived - m_last_progress_bytes;
|
|
||||||
auto dl_speed_bps = (double)bytes_received_since / elapsed_ms.count() * 1000;
|
|
||||||
auto remaing_time_s = (bytesTotal - bytesReceived) / dl_speed_bps;
|
|
||||||
|
|
||||||
//: Current amount of bytes downloaded, out of the total amount of bytes in the download
|
|
||||||
QString dl_progress =
|
|
||||||
tr("%1 / %2").arg(StringUtils::humanReadableFileSize(bytesReceived)).arg(StringUtils::humanReadableFileSize(bytesTotal));
|
|
||||||
|
|
||||||
QString dl_speed_str;
|
|
||||||
if (elapsed_ms.count() > 0) {
|
|
||||||
auto str_eta = bytesTotal > 0 ? Time::humanReadableDuration(remaing_time_s) : tr("unknown");
|
|
||||||
//: Download speed, in bytes per second (remaining download time in parenthesis)
|
|
||||||
dl_speed_str = tr("%1 /s (%2)").arg(StringUtils::humanReadableFileSize(dl_speed_bps)).arg(str_eta);
|
|
||||||
} else {
|
|
||||||
//: Download speed at 0 bytes per second
|
|
||||||
dl_speed_str = tr("0 B/s");
|
|
||||||
}
|
|
||||||
|
|
||||||
setDetails(dl_progress + "\n" + dl_speed_str);
|
|
||||||
|
|
||||||
setProgress(bytesReceived, bytesTotal);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Download::downloadError(QNetworkReply::NetworkError error)
|
|
||||||
{
|
|
||||||
if (error == QNetworkReply::OperationCanceledError) {
|
|
||||||
qCCritical(taskDownloadLogC) << getUid().toString() << "Aborted " << m_url.toString();
|
|
||||||
m_state = State::AbortedByUser;
|
|
||||||
} else {
|
|
||||||
if (m_options & Option::AcceptLocalFiles) {
|
|
||||||
if (m_sink->hasLocalData()) {
|
|
||||||
m_state = State::Succeeded;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// error happened during download.
|
|
||||||
qCCritical(taskDownloadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error;
|
|
||||||
m_state = State::Failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Download::sslErrors(const QList<QSslError>& errors)
|
|
||||||
{
|
|
||||||
int i = 1;
|
|
||||||
for (auto error : errors) {
|
|
||||||
qCCritical(taskDownloadLogC) << getUid().toString() << "Download" << m_url.toString() << "SSL Error #" << i << " : "
|
|
||||||
<< error.errorString();
|
|
||||||
auto cert = error.certificate();
|
|
||||||
qCCritical(taskDownloadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText();
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Download::handleRedirect() -> bool
|
|
||||||
{
|
|
||||||
QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl();
|
|
||||||
if (!redirect.isValid()) {
|
|
||||||
if (!m_reply->hasRawHeader("Location")) {
|
|
||||||
// no redirect -> it's fine to continue
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// there is a Location header, but it's not correct. we need to apply some workarounds...
|
|
||||||
QByteArray redirectBA = m_reply->rawHeader("Location");
|
|
||||||
if (redirectBA.size() == 0) {
|
|
||||||
// empty, yet present redirect header? WTF?
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
QString redirectStr = QString::fromUtf8(redirectBA);
|
|
||||||
|
|
||||||
if (redirectStr.startsWith("//")) {
|
|
||||||
/*
|
|
||||||
* IF the URL begins with //, we need to insert the URL scheme.
|
|
||||||
* See: https://bugreports.qt.io/browse/QTBUG-41061
|
|
||||||
* See: http://tools.ietf.org/html/rfc3986#section-4.2
|
|
||||||
*/
|
|
||||||
redirectStr = m_reply->url().scheme() + ":" + redirectStr;
|
|
||||||
} else if (redirectStr.startsWith("/")) {
|
|
||||||
/*
|
|
||||||
* IF the URL begins with /, we need to process it as a relative URL
|
|
||||||
*/
|
|
||||||
auto url = m_reply->url();
|
|
||||||
url.setPath(redirectStr, QUrl::TolerantMode);
|
|
||||||
redirectStr = url.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues.
|
|
||||||
* FIXME: report Qt bug for this
|
|
||||||
*/
|
|
||||||
redirect = QUrl(redirectStr, QUrl::TolerantMode);
|
|
||||||
if (!redirect.isValid()) {
|
|
||||||
qCWarning(taskDownloadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr;
|
|
||||||
downloadError(QNetworkReply::ProtocolFailure);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
qCDebug(taskDownloadLogC) << getUid().toString() << "Fixed location header:" << redirect;
|
|
||||||
} else {
|
|
||||||
qCDebug(taskDownloadLogC) << getUid().toString() << "Location header:" << redirect;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_url = QUrl(redirect.toString());
|
|
||||||
qCDebug(taskDownloadLogC) << getUid().toString() << "Following redirect to " << m_url.toString();
|
|
||||||
startAction(m_network);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Download::downloadFinished()
|
|
||||||
{
|
|
||||||
// handle HTTP redirection first
|
|
||||||
if (handleRedirect()) {
|
|
||||||
qCDebug(taskDownloadLogC) << getUid().toString() << "Download redirected:" << m_url.toString();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the download failed before this point ...
|
|
||||||
if (m_state == State::Succeeded) // pretend to succeed so we continue processing :)
|
|
||||||
{
|
|
||||||
qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed but we are allowed to proceed:" << m_url.toString();
|
|
||||||
m_sink->abort();
|
|
||||||
m_reply.reset();
|
|
||||||
emit succeeded();
|
|
||||||
return;
|
|
||||||
} else if (m_state == State::Failed) {
|
|
||||||
qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed in previous step:" << m_url.toString();
|
|
||||||
m_sink->abort();
|
|
||||||
m_reply.reset();
|
|
||||||
emit failed("");
|
|
||||||
return;
|
|
||||||
} else if (m_state == State::AbortedByUser) {
|
|
||||||
qCDebug(taskDownloadLogC) << getUid().toString() << "Download aborted in previous step:" << m_url.toString();
|
|
||||||
m_sink->abort();
|
|
||||||
m_reply.reset();
|
|
||||||
emit aborted();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure we got all the remaining data, if any
|
|
||||||
auto data = m_reply->readAll();
|
|
||||||
if (data.size()) {
|
|
||||||
qCDebug(taskDownloadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes";
|
|
||||||
m_state = m_sink->write(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, finalize the whole graph
|
|
||||||
m_state = m_sink->finalize(*m_reply.get());
|
|
||||||
if (m_state != State::Succeeded) {
|
|
||||||
qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed to finalize:" << m_url.toString();
|
|
||||||
m_sink->abort();
|
|
||||||
m_reply.reset();
|
|
||||||
emit failed("");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_reply.reset();
|
|
||||||
qCDebug(taskDownloadLogC) << getUid().toString() << "Download succeeded:" << m_url.toString();
|
|
||||||
emit succeeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Download::downloadReadyRead()
|
|
||||||
{
|
|
||||||
if (m_state == State::Running) {
|
|
||||||
auto data = m_reply->readAll();
|
|
||||||
m_state = m_sink->write(data);
|
|
||||||
if (m_state == State::Failed) {
|
|
||||||
qCCritical(taskDownloadLogC) << getUid().toString() << "Failed to process response chunk";
|
|
||||||
}
|
|
||||||
// qDebug() << "Download" << m_url.toString() << "gained" << data.size() << "bytes";
|
|
||||||
} else {
|
|
||||||
qCCritical(taskDownloadLogC) << getUid().toString() << "Cannot write download data! illegal status " << m_status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Net
|
} // namespace Net
|
||||||
|
|
||||||
auto Net::Download::abort() -> bool
|
|
||||||
{
|
|
||||||
if (m_reply) {
|
|
||||||
m_reply->abort();
|
|
||||||
} else {
|
|
||||||
m_state = State::AbortedByUser;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
@ -38,57 +38,26 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
#include "HttpMetaCache.h"
|
#include "HttpMetaCache.h"
|
||||||
#include "NetAction.h"
|
|
||||||
#include "Sink.h"
|
|
||||||
#include "Validator.h"
|
|
||||||
|
|
||||||
#include "QObjectPtr.h"
|
#include "QObjectPtr.h"
|
||||||
|
#include "net/NetRequest.h"
|
||||||
|
|
||||||
namespace Net {
|
namespace Net {
|
||||||
class Download : public NetAction {
|
class Download : public NetRequest {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using Ptr = shared_qobject_ptr<class Download>;
|
using Ptr = shared_qobject_ptr<class Download>;
|
||||||
enum class Option { NoOptions = 0, AcceptLocalFiles = 1, MakeEternal = 2 };
|
explicit Download() : NetRequest() { logCat = taskDownloadLogC; }
|
||||||
Q_DECLARE_FLAGS(Options, Option)
|
|
||||||
|
|
||||||
public:
|
|
||||||
~Download() override = default;
|
|
||||||
|
|
||||||
|
#if defined(LAUNCHER_APPLICATION)
|
||||||
static auto makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions) -> Download::Ptr;
|
static auto makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions) -> Download::Ptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
static auto makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Options options = Option::NoOptions) -> Download::Ptr;
|
static auto makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Options options = Option::NoOptions) -> Download::Ptr;
|
||||||
static auto makeFile(QUrl url, QString path, Options options = Option::NoOptions) -> Download::Ptr;
|
static auto makeFile(QUrl url, QString path, Options options = Option::NoOptions) -> Download::Ptr;
|
||||||
|
|
||||||
public:
|
protected:
|
||||||
void addValidator(Validator* v);
|
virtual QNetworkReply* getReply(QNetworkRequest&) override;
|
||||||
auto abort() -> bool override;
|
|
||||||
auto canAbort() const -> bool override { return true; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
auto handleRedirect() -> bool;
|
|
||||||
|
|
||||||
protected slots:
|
|
||||||
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
|
|
||||||
void downloadError(QNetworkReply::NetworkError error) override;
|
|
||||||
void sslErrors(const QList<QSslError>& errors) override;
|
|
||||||
void downloadFinished() override;
|
|
||||||
void downloadReadyRead() override;
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void executeTask() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<Sink> m_sink;
|
|
||||||
Options m_options;
|
|
||||||
|
|
||||||
std::chrono::steady_clock m_clock;
|
|
||||||
std::chrono::time_point<std::chrono::steady_clock> m_last_progress_time;
|
|
||||||
qint64 m_last_progress_bytes;
|
|
||||||
};
|
};
|
||||||
} // namespace Net
|
} // namespace Net
|
||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Net::Download::Options)
|
|
||||||
|
49
launcher/net/HeaderProxy.h
Normal file
49
launcher/net/HeaderProxy.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
|
||||||
|
namespace Net {
|
||||||
|
|
||||||
|
struct HeaderPair {
|
||||||
|
QByteArray headerName;
|
||||||
|
QByteArray headerValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HeaderProxy {
|
||||||
|
public:
|
||||||
|
HeaderProxy() {}
|
||||||
|
virtual ~HeaderProxy() {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual QList<HeaderPair> headers(const QNetworkRequest& request) const = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void writeHeaders(QNetworkRequest& request)
|
||||||
|
{
|
||||||
|
for (auto header : headers(request)) {
|
||||||
|
request.setRawHeader(header.headerName, header.headerValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Net
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user