Compare commits
103 Commits
develop
...
release-7.
Author | SHA1 | Date | |
---|---|---|---|
|
770d5c92bc | ||
|
f326db11f1 | ||
|
f8c6a33134 | ||
|
64228bdddf | ||
|
88be40c4bf | ||
|
d6095358ad | ||
|
f7f2049223 | ||
|
cc43485650 | ||
|
b92d617df0 | ||
|
7b971a08a8 | ||
|
7cc3d34498 | ||
|
d9df60368c | ||
|
ba49afcc6a | ||
|
685badb8cf | ||
|
29fde6a322 | ||
|
e65d48bf36 | ||
|
38144b3661 | ||
|
3a0e30684e | ||
|
0fe184251d | ||
|
f51a66dad5 | ||
|
071e86fe68 | ||
|
d7a02ee456 | ||
|
648a69594f | ||
|
70fdfd1526 | ||
|
1e324e3e2f | ||
|
8bbe307a31 | ||
|
c946b2c4fb | ||
|
2dcfab0a19 | ||
|
b0b9b89bce | ||
|
862f4fb061 | ||
|
7c5d07e74d | ||
|
fe1ea7240e | ||
|
6f1d594f1c | ||
|
7baaf83f1d | ||
|
bab7105820 | ||
|
1389b74a8a | ||
|
5aa3aabdf9 | ||
|
42d2c7446a | ||
|
71a7358287 | ||
|
eaf125c31a | ||
|
f7e018d41a | ||
|
7d6e07ea71 | ||
|
cd011a097b | ||
|
055bcc2721 | ||
|
4bc4b29d5b | ||
|
82a9e7d372 | ||
|
cb81cadee3 | ||
|
0d8283df97 | ||
|
d4014534eb | ||
|
3c5ec5d967 | ||
|
e3625cad91 | ||
|
bb945c5165 | ||
|
daa5fcce67 | ||
|
2bcebe2989 | ||
|
27f6debdaf | ||
|
7926170073 | ||
|
79537f2948 | ||
|
2b3021b7c2 | ||
|
b11b86e026 | ||
|
a0ddd85b32 | ||
|
6f86e8b66e | ||
|
6cd259becd | ||
|
d9de326f22 | ||
|
1d4cf0fd03 | ||
|
a65e4af365 | ||
|
721ac015f3 | ||
|
c5572a5e0b | ||
|
81757717f7 | ||
|
1ab35357e9 | ||
|
7025f75903 | ||
|
3cc68fcea4 | ||
|
34be098f12 | ||
|
fb5655085f | ||
|
316ef9b725 | ||
|
5fdbc9d75e | ||
|
6464127d05 | ||
|
c349eff522 | ||
|
5fdf73a2ff | ||
|
6963bfc6c9 | ||
|
a25a5a7c9f | ||
|
20e9bf0e11 | ||
|
21ccf47ea7 | ||
|
6856c2f922 | ||
|
e52fd9d4fe | ||
|
05056e1abf | ||
|
9df3c5d3c0 | ||
|
1a4ea3b1cd | ||
|
0a7a7d9bfd | ||
|
8b017f9a5f | ||
|
b1fe4d1d93 | ||
|
80463f9761 | ||
|
567af5b22d | ||
|
7ed15b2687 | ||
|
3d502b12a9 | ||
|
f0d1df9139 | ||
|
0fcaa336dc | ||
|
949da6b50e | ||
|
fd0ba70080 | ||
|
89dd981dd7 | ||
|
30fda57c64 | ||
|
1d75c58d6c | ||
|
34d80a8c75 | ||
|
05360c1103 |
4
.git-blame-ignore-revs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# .git-blame-ignore-revs
|
||||||
|
|
||||||
|
# tabs -> spaces
|
||||||
|
bbb3b3e6f6e3c0f95873f22e6d0a4aaf350f49d9
|
49
.github/workflows/build.yml
vendored
@ -191,7 +191,7 @@ jobs:
|
|||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get -y update
|
sudo apt-get -y update
|
||||||
sudo apt-get -y install ninja-build extra-cmake-modules scdoc
|
sudo apt-get -y install ninja-build extra-cmake-modules scdoc appstream
|
||||||
|
|
||||||
- name: Install Dependencies (macOS)
|
- name: Install Dependencies (macOS)
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
@ -250,6 +250,7 @@ jobs:
|
|||||||
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"
|
||||||
|
|
||||||
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
|
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
|
||||||
|
sudo apt install libopengl0
|
||||||
|
|
||||||
- name: Add QT_HOST_PATH var (Windows MSVC arm64)
|
- name: Add QT_HOST_PATH var (Windows MSVC arm64)
|
||||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||||
@ -263,23 +264,23 @@ jobs:
|
|||||||
- name: Configure CMake (macOS)
|
- name: Configure CMake (macOS)
|
||||||
if: runner.os == 'macOS' && matrix.qt_ver == 6
|
if: runner.os == 'macOS' && matrix.qt_ver == 6
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
|
||||||
|
|
||||||
- name: Configure CMake (macOS-Legacy)
|
- name: Configure CMake (macOS-Legacy)
|
||||||
if: runner.os == 'macOS' && matrix.qt_ver == 5
|
if: runner.os == 'macOS' && matrix.qt_ver == 5
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja
|
||||||
|
|
||||||
- name: Configure CMake (Windows MinGW-w64)
|
- name: Configure CMake (Windows MinGW-w64)
|
||||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja
|
||||||
|
|
||||||
- name: Configure CMake (Windows MSVC)
|
- name: Configure CMake (Windows MSVC)
|
||||||
if: runner.os == 'Windows' && matrix.msystem == ''
|
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON
|
||||||
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
|
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
|
||||||
if ("${{ env.CCACHE_VAR }}")
|
if ("${{ env.CCACHE_VAR }}")
|
||||||
{
|
{
|
||||||
@ -294,7 +295,7 @@ jobs:
|
|||||||
- name: Configure CMake (Linux)
|
- name: Configure CMake (Linux)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
|
||||||
|
|
||||||
##
|
##
|
||||||
# BUILD
|
# BUILD
|
||||||
@ -467,7 +468,8 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
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
|
||||||
|
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-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
||||||
|
|
||||||
chmod +x linuxdeploy-*.AppImage
|
chmod +x linuxdeploy-*.AppImage
|
||||||
@ -482,7 +484,8 @@ jobs:
|
|||||||
cp -r /home/runner/work/PrismLauncher/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
cp -r /home/runner/work/PrismLauncher/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
||||||
|
|
||||||
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||||
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}//usr/lib/
|
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||||
|
cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||||
|
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
|
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
|
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
|
||||||
@ -583,33 +586,3 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
bundle: "Prism Launcher.flatpak"
|
bundle: "Prism Launcher.flatpak"
|
||||||
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
|
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
|
||||||
|
|
||||||
nix:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
package:
|
|
||||||
- prismlauncher
|
|
||||||
- prismlauncher-qt5
|
|
||||||
steps:
|
|
||||||
- name: Clone repository
|
|
||||||
if: inputs.build_type == 'Debug'
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
submodules: 'true'
|
|
||||||
- name: Install nix
|
|
||||||
if: inputs.build_type == 'Debug'
|
|
||||||
uses: cachix/install-nix-action@v21
|
|
||||||
with:
|
|
||||||
install_url: https://nixos.org/nix/install
|
|
||||||
extra_nix_config: |
|
|
||||||
auto-optimise-store = true
|
|
||||||
experimental-features = nix-command flakes
|
|
||||||
- uses: cachix/cachix-action@v12
|
|
||||||
if: inputs.build_type == 'Debug'
|
|
||||||
with:
|
|
||||||
name: prismlauncher
|
|
||||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
|
||||||
- name: Build
|
|
||||||
if: inputs.build_type == 'Debug'
|
|
||||||
run: nix build .#${{ matrix.package }} --print-build-logs
|
|
||||||
|
28
.github/workflows/update-flake.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
name: Update Flake Lockfile
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# run weekly on sunday
|
||||||
|
- cron: "0 0 * * 0"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-flake:
|
||||||
|
if: github.repository == 'PrismLauncher/PrismLauncher'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: cachix/install-nix-action@v22
|
||||||
|
|
||||||
|
- uses: DeterminateSystems/update-flake-lock@v19
|
||||||
|
with:
|
||||||
|
commit-msg: "chore(nix): update lockfile"
|
||||||
|
pr-title: "chore(nix): update lockfile"
|
||||||
|
pr-labels: |
|
||||||
|
Linux
|
||||||
|
simple change
|
3
.gitmodules
vendored
@ -19,3 +19,6 @@
|
|||||||
[submodule "libraries/cmark"]
|
[submodule "libraries/cmark"]
|
||||||
path = libraries/cmark
|
path = libraries/cmark
|
||||||
url = https://github.com/commonmark/cmark.git
|
url = https://github.com/commonmark/cmark.git
|
||||||
|
[submodule "flatpak/shared-modules"]
|
||||||
|
path = flatpak/shared-modules
|
||||||
|
url = https://github.com/flathub/shared-modules.git
|
||||||
|
@ -85,6 +85,38 @@ 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)
|
||||||
|
|
||||||
|
# If this is a Debug build turn on address sanitiser
|
||||||
|
if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND DEBUG_ADDRESS_SANITIZER)
|
||||||
|
message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
|
||||||
|
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||||
|
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||||
|
# using clang with clang-cl front end
|
||||||
|
message(STATUS "Address Sanitizer available on Clang MSVC frontend")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-")
|
||||||
|
else()
|
||||||
|
# AppleClang and Clang
|
||||||
|
message(STATUS "Address Sanitizer available on Clang")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
|
||||||
|
endif()
|
||||||
|
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||||
|
# GCC
|
||||||
|
message(STATUS "Address Sanitizer available on GCC")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
|
||||||
|
link_libraries("asan")
|
||||||
|
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||||
|
message(STATUS "Address Sanitizer available on MSVC")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-")
|
||||||
|
else()
|
||||||
|
message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
option(ENABLE_LTO "Enable Link Time Optimization" off)
|
option(ENABLE_LTO "Enable Link Time Optimization" off)
|
||||||
|
|
||||||
if(ENABLE_LTO)
|
if(ENABLE_LTO)
|
||||||
@ -139,14 +171,14 @@ set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRIN
|
|||||||
|
|
||||||
######## Set version numbers ########
|
######## Set version numbers ########
|
||||||
set(Launcher_VERSION_MAJOR 7)
|
set(Launcher_VERSION_MAJOR 7)
|
||||||
set(Launcher_VERSION_MINOR 0)
|
set(Launcher_VERSION_MINOR 2)
|
||||||
|
|
||||||
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
||||||
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
|
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
|
||||||
set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0")
|
set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0")
|
||||||
|
|
||||||
# Build platform.
|
# Build platform.
|
||||||
set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
|
set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
|
||||||
|
|
||||||
# Channel list URL
|
# Channel list URL
|
||||||
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
||||||
@ -332,7 +364,7 @@ elseif(UNIX)
|
|||||||
|
|
||||||
set(BINARY_DEST_DIR "bin")
|
set(BINARY_DEST_DIR "bin")
|
||||||
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
|
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
|
||||||
set(JARS_DEST_DIR "share/${Launcher_APP_BINARY_NAME}")
|
set(JARS_DEST_DIR "share/${Launcher_Name}")
|
||||||
|
|
||||||
# install as bundle with no dependencies included
|
# install as bundle with no dependencies included
|
||||||
set(INSTALL_BUNDLE "nodeps")
|
set(INSTALL_BUNDLE "nodeps")
|
||||||
@ -345,7 +377,7 @@ elseif(UNIX)
|
|||||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps")
|
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps")
|
||||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
|
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
|
||||||
|
|
||||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "${KDE_INSTALL_DATADIR}/${Launcher_Name}")
|
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}")
|
||||||
|
|
||||||
if(Launcher_ManPage)
|
if(Launcher_ManPage)
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
|
||||||
|
14
README.md
@ -42,7 +42,7 @@ Feel free to create a GitHub issue if you find a bug or want to suggest a new fe
|
|||||||
|
|
||||||
- **Our Matrix space:**
|
- **Our Matrix space:**
|
||||||
|
|
||||||
[![PrismLauncher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&label=Matrix%20Space&logo=matrix&color=purple)](https://prismlauncher.org/matrix)
|
[![Prism Launcher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&label=Matrix%20Space&logo=matrix&color=purple)](https://prismlauncher.org/matrix)
|
||||||
|
|
||||||
- **Our Subreddit:**
|
- **Our Subreddit:**
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ Feel free to create a GitHub issue if you find a bug or want to suggest a new fe
|
|||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
The translation effort for PrismLauncher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at <https://github.com/PrismLauncher/Translations>
|
The translation effort for Prism Launcher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at <https://github.com/PrismLauncher/Translations>
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
@ -82,14 +82,16 @@ Thanks to the awesome people over at [MacStadium](https://www.macstadium.com/),
|
|||||||
|
|
||||||
## Forking/Redistributing/Custom builds policy
|
## Forking/Redistributing/Custom builds policy
|
||||||
|
|
||||||
We don't care what you do with your fork/custom build as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy:
|
You are free to fork, redistribute and provide custom builds as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy:
|
||||||
|
|
||||||
- Make it clear that your fork is not PrismLauncher and is not endorsed by or affiliated with the PrismLauncher project (<https://prismlauncher.org>).
|
- Make it clear that your fork is not Prism Launcher and is not endorsed by or affiliated with the Prism Launcher project (<https://prismlauncher.org>).
|
||||||
- Go through [CMakeLists.txt](CMakeLists.txt) and change PrismLauncher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled).
|
- Go through [CMakeLists.txt](CMakeLists.txt) and change Prism Launcher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled).
|
||||||
|
|
||||||
If you have any questions or want any clarification on the above conditions please make an issue and ask us.
|
If you have any questions or want any clarification on the above conditions please make an issue and ask us.
|
||||||
|
|
||||||
Be aware that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions:
|
If you are just building Prism Launcher for your distribution, please make sure to set the `Launcher_BUILD_PLATFORM` to a slug representing your distribution. Examples are `archlinux`, `fedora` and `nixpkgs`.
|
||||||
|
|
||||||
|
Note that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions:
|
||||||
|
|
||||||
- [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
|
- [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
|
||||||
- [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions)
|
- [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions)
|
||||||
|
@ -65,7 +65,7 @@ Config::Config()
|
|||||||
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
|
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
|
||||||
MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
|
MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
|
||||||
|
|
||||||
if (BUILD_PLATFORM == "macOS" && !MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
|
if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
|
||||||
{
|
{
|
||||||
UPDATER_ENABLED = true;
|
UPDATER_ENABLED = true;
|
||||||
}
|
}
|
||||||
@ -82,6 +82,7 @@ Config::Config()
|
|||||||
{
|
{
|
||||||
GIT_REFSPEC = "refs/heads/stable";
|
GIT_REFSPEC = "refs/heads/stable";
|
||||||
GIT_TAG = versionString();
|
GIT_TAG = versionString();
|
||||||
|
GIT_COMMIT = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GIT_REFSPEC.startsWith("refs/heads/"))
|
if (GIT_REFSPEC.startsWith("refs/heads/"))
|
||||||
|
@ -68,7 +68,7 @@ class Config {
|
|||||||
|
|
||||||
bool UPDATER_ENABLED = false;
|
bool UPDATER_ENABLED = false;
|
||||||
|
|
||||||
/// A short string identifying this build's platform. For example, "lin64" or "win32".
|
/// A short string identifying this build's platform or distribution.
|
||||||
QString BUILD_PLATFORM;
|
QString BUILD_PLATFORM;
|
||||||
|
|
||||||
/// A string containing the build timestamp
|
/// A string containing the build timestamp
|
||||||
|
54
flake.lock
@ -21,11 +21,11 @@
|
|||||||
"nixpkgs-lib": "nixpkgs-lib"
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1683560683,
|
"lastModified": 1688466019,
|
||||||
"narHash": "sha256-XAygPMN5Xnk/W2c1aW0jyEa6lfMDZWlQgiNtmHXytPc=",
|
"narHash": "sha256-VeM2akYrBYMsb4W/MmBo1zmaMfgbL4cH3Pu8PGyIwJ0=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "006c75898cf814ef9497252b022e91c946ba8e17",
|
"rev": "8e8d955c22df93dbe24f19ea04f47a74adbdc5ec",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -35,12 +35,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-utils": {
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1667395993,
|
"lastModified": 1685518550,
|
||||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -73,11 +76,11 @@
|
|||||||
"libnbtplusplus": {
|
"libnbtplusplus": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1650031308,
|
"lastModified": 1690036783,
|
||||||
"narHash": "sha256-TvVOjkUobYJD9itQYueELJX3wmecvEdCbJ0FinW2mL4=",
|
"narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=",
|
||||||
"owner": "PrismLauncher",
|
"owner": "PrismLauncher",
|
||||||
"repo": "libnbtplusplus",
|
"repo": "libnbtplusplus",
|
||||||
"rev": "2203af7eeb48c45398139b583615134efd8d407f",
|
"rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -88,11 +91,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1685012353,
|
"lastModified": 1690026219,
|
||||||
"narHash": "sha256-U3oOge4cHnav8OLGdRVhL45xoRj4Ppd+It6nPC9nNIU=",
|
"narHash": "sha256-oOduRk/kzQxOBknZXTLSEYd7tk+GoKvr8wV6Ab+t4AU=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "aeb75dba965e790de427b73315d5addf91a54955",
|
"rev": "f465da166263bc0d4b39dfd4ca28b777c92d4b73",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -105,11 +108,11 @@
|
|||||||
"nixpkgs-lib": {
|
"nixpkgs-lib": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "lib",
|
"dir": "lib",
|
||||||
"lastModified": 1682879489,
|
"lastModified": 1688049487,
|
||||||
"narHash": "sha256-sASwo8gBt7JDnOOstnps90K1wxmVfyhsTPPNTGBPjjg=",
|
"narHash": "sha256-100g4iaKC9MalDjUW9iN6Jl/OocTDtXdeAj7pEGIRh4=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "da45bf6ec7bbcc5d1e14d3795c025199f28e0de0",
|
"rev": "4bc72cae107788bf3f24f30db2e2f685c9298dc9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -135,11 +138,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1684842236,
|
"lastModified": 1689668210,
|
||||||
"narHash": "sha256-rYWsIXHvNhVQ15RQlBUv67W3YnM+Pd+DuXGMvCBq2IE=",
|
"narHash": "sha256-XAATwDkaUxH958yXLs1lcEOmU6pSEIkatY3qjqk8X0E=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "pre-commit-hooks.nix",
|
"repo": "pre-commit-hooks.nix",
|
||||||
"rev": "61e567d6497bc9556f391faebe5e410e6623217f",
|
"rev": "eb433bff05b285258be76513add6f6c57b441775",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -156,6 +159,21 @@
|
|||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"pre-commit-hooks": "pre-commit-hooks"
|
"pre-commit-hooks": "pre-commit-hooks"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
22
flatpak/libdecor.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "libdecor",
|
||||||
|
"buildsystem": "meson",
|
||||||
|
"config-opts": [
|
||||||
|
"-Ddemo=false"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://gitlab.freedesktop.org/libdecor/libdecor.git",
|
||||||
|
"commit": "73260393a97291c887e1074ab7f318e031be0ac6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patch",
|
||||||
|
"path": "patches/weird_libdecor.patch"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cleanup": [
|
||||||
|
"/include",
|
||||||
|
"/lib/pkgconfig"
|
||||||
|
]
|
||||||
|
}
|
@ -5,13 +5,6 @@ sdk: org.kde.Sdk
|
|||||||
sdk-extensions:
|
sdk-extensions:
|
||||||
- org.freedesktop.Sdk.Extension.openjdk17
|
- org.freedesktop.Sdk.Extension.openjdk17
|
||||||
- org.freedesktop.Sdk.Extension.openjdk8
|
- org.freedesktop.Sdk.Extension.openjdk8
|
||||||
add-extensions:
|
|
||||||
com.valvesoftware.Steam.Utility.gamescope:
|
|
||||||
version: stable
|
|
||||||
add-ld-path: lib
|
|
||||||
no-autodownload: true
|
|
||||||
autodelete: false
|
|
||||||
directory: utils/gamescope
|
|
||||||
|
|
||||||
command: prismlauncher
|
command: prismlauncher
|
||||||
finish-args:
|
finish-args:
|
||||||
@ -26,21 +19,31 @@ finish-args:
|
|||||||
# Mod drag&drop
|
# Mod drag&drop
|
||||||
- --filesystem=xdg-download:ro
|
- --filesystem=xdg-download:ro
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
- /lib/libGLU*
|
||||||
|
|
||||||
modules:
|
modules:
|
||||||
|
# Might be needed by some Controller mods (see https://github.com/isXander/Controlify/issues/31)
|
||||||
|
- shared-modules/libusb/libusb.json
|
||||||
|
|
||||||
|
# Needed for proper Wayland support
|
||||||
|
- libdecor.json
|
||||||
|
|
||||||
- name: prismlauncher
|
- name: prismlauncher
|
||||||
buildsystem: cmake-ninja
|
buildsystem: cmake-ninja
|
||||||
|
builddir: true
|
||||||
config-opts:
|
config-opts:
|
||||||
- -DLauncher_BUILD_PLATFORM=flatpak
|
- -DLauncher_BUILD_PLATFORM=flatpak
|
||||||
- -DCMAKE_BUILD_TYPE=Debug
|
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||||
- -DLauncher_QT_VERSION_MAJOR=5
|
- -DLauncher_QT_VERSION_MAJOR=5
|
||||||
build-options:
|
build-options:
|
||||||
env:
|
env:
|
||||||
JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17
|
JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17
|
||||||
JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac
|
JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac
|
||||||
sources:
|
sources:
|
||||||
- type: dir
|
- type: dir
|
||||||
path: ../
|
path: ../
|
||||||
builddir: true
|
|
||||||
- name: openjdk
|
- name: openjdk
|
||||||
buildsystem: simple
|
buildsystem: simple
|
||||||
build-commands:
|
build-commands:
|
||||||
@ -49,14 +52,45 @@ modules:
|
|||||||
- mv /app/jre /app/jdk/17
|
- mv /app/jre /app/jdk/17
|
||||||
- /usr/lib/sdk/openjdk8/install.sh
|
- /usr/lib/sdk/openjdk8/install.sh
|
||||||
- mv /app/jre /app/jdk/8
|
- mv /app/jre /app/jdk/8
|
||||||
cleanup: [/jre]
|
cleanup:
|
||||||
|
- /jre
|
||||||
|
|
||||||
|
- name: glfw
|
||||||
|
buildsystem: cmake-ninja
|
||||||
|
config-opts:
|
||||||
|
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||||
|
- -DBUILD_SHARED_LIBS:BOOL=ON
|
||||||
|
- -DGLFW_USE_WAYLAND=ON
|
||||||
|
sources:
|
||||||
|
- type: git
|
||||||
|
url: https://github.com/glfw/glfw.git
|
||||||
|
commit: 3fa2360720eeba1964df3c0ecf4b5df8648a8e52
|
||||||
|
- type: patch
|
||||||
|
path: patches/0003-Don-t-crash-on-calls-to-focus-or-icon.patch
|
||||||
|
- type: patch
|
||||||
|
path: patches/0005-Add-warning-about-being-an-unofficial-patch.patch
|
||||||
|
- type: patch
|
||||||
|
path: patches/0007-Platform-Prefer-Wayland-over-X11.patch
|
||||||
|
cleanup:
|
||||||
|
- /include
|
||||||
|
- /lib/cmake
|
||||||
|
- /lib/pkgconfig
|
||||||
|
|
||||||
- name: xrandr
|
- name: xrandr
|
||||||
buildsystem: autotools
|
buildsystem: autotools
|
||||||
sources:
|
sources:
|
||||||
- type: archive
|
- type: archive
|
||||||
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.1.tar.xz
|
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.2.tar.xz
|
||||||
sha256: 7bc76daf9d72f8aff885efad04ce06b90488a1a169d118dea8a2b661832e8762
|
sha256: c8bee4790d9058bacc4b6246456c58021db58a87ddda1a9d0139bf5f18f1f240
|
||||||
cleanup: [/share/man, /bin/xkeystone]
|
x-checker-data:
|
||||||
|
type: anitya
|
||||||
|
project-id: 14957
|
||||||
|
stable-only: true
|
||||||
|
url-template: https://xorg.freedesktop.org/archive/individual/app/xrandr-$version.tar.xz
|
||||||
|
cleanup:
|
||||||
|
- /share/man
|
||||||
|
- /bin/xkeystone
|
||||||
|
|
||||||
- name: gamemode
|
- name: gamemode
|
||||||
buildsystem: meson
|
buildsystem: meson
|
||||||
config-opts:
|
config-opts:
|
||||||
@ -67,19 +101,56 @@ modules:
|
|||||||
# post-install is running inside the build dir, we need it from the source though
|
# post-install is running inside the build dir, we need it from the source though
|
||||||
- install -Dm755 ../data/gamemoderun -t /app/bin
|
- install -Dm755 ../data/gamemoderun -t /app/bin
|
||||||
sources:
|
sources:
|
||||||
- type: git
|
- type: archive
|
||||||
url: https://github.com/FeralInteractive/gamemode
|
archive-type: tar-gzip
|
||||||
tag: "1.7"
|
url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.7
|
||||||
commit: 4dc99dff76218718763a6b07fc1900fa6d1dafd9
|
sha256: 57ce73ba605d1cf12f8d13725006a895182308d93eba0f69f285648449641803
|
||||||
|
x-checker-data:
|
||||||
|
type: json
|
||||||
|
url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest
|
||||||
|
version-query: .tag_name
|
||||||
|
url-query: .tarball_url
|
||||||
|
timestamp-query: .published_at
|
||||||
|
cleanup:
|
||||||
|
- /include
|
||||||
|
- /lib/pkgconfig
|
||||||
|
- /lib/libgamemodeauto.a
|
||||||
|
|
||||||
|
- name: glxinfo
|
||||||
|
buildsystem: meson
|
||||||
|
config-opts:
|
||||||
|
- --bindir=/app/mesa-demos
|
||||||
|
- -Degl=disabled
|
||||||
|
- -Dglut=disabled
|
||||||
|
- -Dosmesa=disabled
|
||||||
|
- -Dvulkan=disabled
|
||||||
|
- -Dwayland=disabled
|
||||||
|
post-install:
|
||||||
|
- mv -v /app/mesa-demos/glxinfo /app/bin
|
||||||
|
sources:
|
||||||
|
- type: archive
|
||||||
|
url: https://archive.mesa3d.org/demos/mesa-demos-9.0.0.tar.xz
|
||||||
|
sha256: 3046a3d26a7b051af7ebdd257a5f23bfeb160cad6ed952329cdff1e9f1ed496b
|
||||||
|
x-checker-data:
|
||||||
|
type: anitya
|
||||||
|
project-id: 16781
|
||||||
|
stable-only: true
|
||||||
|
url-template: https://archive.mesa3d.org/demos/mesa-demos-$version.tar.xz
|
||||||
|
cleanup:
|
||||||
|
- /include
|
||||||
|
- /mesa-demos
|
||||||
|
- /share
|
||||||
|
modules:
|
||||||
|
- shared-modules/glu/glu-9.json
|
||||||
|
|
||||||
- name: enhance
|
- name: enhance
|
||||||
buildsystem: simple
|
buildsystem: simple
|
||||||
build-commands:
|
build-commands:
|
||||||
- mkdir -p /app/utils/gamescope
|
|
||||||
- install -Dm755 prime-run /app/bin/prime-run
|
- install -Dm755 prime-run /app/bin/prime-run
|
||||||
- mv /app/bin/prismlauncher /app/bin/prismrun
|
- mv /app/bin/prismlauncher /app/bin/prismrun
|
||||||
- install -Dm755 prismlauncher /app/bin/prismlauncher
|
- install -Dm755 prismlauncher /app/bin/prismlauncher
|
||||||
sources:
|
sources:
|
||||||
- type: file
|
- type: file
|
||||||
path: ../flatpak/prime-run
|
path: prime-run
|
||||||
- type: file
|
- type: file
|
||||||
path: ../flatpak/prismlauncher
|
path: prismlauncher
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
diff --git a/src/wl_window.c b/src/wl_window.c
|
||||||
|
index 52d3b9eb..4ac4eb5d 100644
|
||||||
|
--- a/src/wl_window.c
|
||||||
|
+++ b/src/wl_window.c
|
||||||
|
@@ -2117,8 +2117,7 @@ void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title)
|
||||||
|
void _glfwSetWindowIconWayland(_GLFWwindow* window,
|
||||||
|
int count, const GLFWimage* images)
|
||||||
|
{
|
||||||
|
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||||
|
- "Wayland: The platform does not support setting the window icon");
|
||||||
|
+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the window icon\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos)
|
||||||
|
@@ -2361,8 +2360,7 @@ void _glfwRequestWindowAttentionWayland(_GLFWwindow* window)
|
||||||
|
|
||||||
|
void _glfwFocusWindowWayland(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||||
|
- "Wayland: The platform does not support setting the input focus");
|
||||||
|
+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the input focus\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void _glfwSetWindowMonitorWayland(_GLFWwindow* window,
|
@ -0,0 +1,17 @@
|
|||||||
|
diff --git a/src/init.c b/src/init.c
|
||||||
|
index 06dbb3f2..a7c6da86 100644
|
||||||
|
--- a/src/init.c
|
||||||
|
+++ b/src/init.c
|
||||||
|
@@ -449,6 +449,12 @@ GLFWAPI int glfwInit(void)
|
||||||
|
_glfw.initialized = GLFW_TRUE;
|
||||||
|
|
||||||
|
glfwDefaultWindowHints();
|
||||||
|
+
|
||||||
|
+ fprintf(stderr, "!!! Patched GLFW from https://github.com/Admicos/minecraft-wayland\n"
|
||||||
|
+ "!!! If any issues with the window, or some issues with rendering, occur, "
|
||||||
|
+ "first try with the built-in GLFW, and if that solves the issue, report there first.\n"
|
||||||
|
+ "!!! Use outside Minecraft is untested, and things might break.\n");
|
||||||
|
+
|
||||||
|
return GLFW_TRUE;
|
||||||
|
}
|
||||||
|
|
20
flatpak/patches/0007-Platform-Prefer-Wayland-over-X11.patch
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
diff --git a/src/platform.c b/src/platform.c
|
||||||
|
index c5966ae7..3e7442f9 100644
|
||||||
|
--- a/src/platform.c
|
||||||
|
+++ b/src/platform.c
|
||||||
|
@@ -49,12 +49,12 @@ static const struct
|
||||||
|
#if defined(_GLFW_COCOA)
|
||||||
|
{ GLFW_PLATFORM_COCOA, _glfwConnectCocoa },
|
||||||
|
#endif
|
||||||
|
-#if defined(_GLFW_X11)
|
||||||
|
- { GLFW_PLATFORM_X11, _glfwConnectX11 },
|
||||||
|
-#endif
|
||||||
|
#if defined(_GLFW_WAYLAND)
|
||||||
|
{ GLFW_PLATFORM_WAYLAND, _glfwConnectWayland },
|
||||||
|
#endif
|
||||||
|
+#if defined(_GLFW_X11)
|
||||||
|
+ { GLFW_PLATFORM_X11, _glfwConnectX11 },
|
||||||
|
+#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
GLFWbool _glfwSelectPlatform(int desiredID, _GLFWplatform* platform)
|
40
flatpak/patches/weird_libdecor.patch
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
diff --git a/src/libdecor.c b/src/libdecor.c
|
||||||
|
index a9c1106..1aa38b3 100644
|
||||||
|
--- a/src/libdecor.c
|
||||||
|
+++ b/src/libdecor.c
|
||||||
|
@@ -1391,22 +1391,32 @@ calculate_priority(const struct libdecor_plugin_description *plugin_description)
|
||||||
|
static bool
|
||||||
|
check_symbol_conflicts(const struct libdecor_plugin_description *plugin_description)
|
||||||
|
{
|
||||||
|
+ bool ret = true;
|
||||||
|
char * const *symbol;
|
||||||
|
+ void* main_prog = dlopen(NULL, RTLD_LAZY);
|
||||||
|
+ if (!main_prog) {
|
||||||
|
+ fprintf(stderr, "Plugin \"%s\" couldn't check conflicting symbols: \"%s\".\n",
|
||||||
|
+ plugin_description->description, dlerror());
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
|
||||||
|
symbol = plugin_description->conflicting_symbols;
|
||||||
|
while (*symbol) {
|
||||||
|
dlerror();
|
||||||
|
- dlsym (RTLD_DEFAULT, *symbol);
|
||||||
|
+ dlsym (main_prog, *symbol);
|
||||||
|
if (!dlerror()) {
|
||||||
|
fprintf(stderr, "Plugin \"%s\" uses conflicting symbol \"%s\".\n",
|
||||||
|
plugin_description->description, *symbol);
|
||||||
|
- return false;
|
||||||
|
+ ret = false;
|
||||||
|
+ break;
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol++;
|
||||||
|
}
|
||||||
|
|
||||||
|
- return true;
|
||||||
|
+ dlclose(main_prog);
|
||||||
|
+ return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct plugin_loader *
|
@ -5,7 +5,7 @@ for i in {0..9}; do
|
|||||||
test -S "$XDG_RUNTIME_DIR"/discord-ipc-"$i" || ln -sf {app/com.discordapp.Discord,"$XDG_RUNTIME_DIR"}/discord-ipc-"$i";
|
test -S "$XDG_RUNTIME_DIR"/discord-ipc-"$i" || ln -sf {app/com.discordapp.Discord,"$XDG_RUNTIME_DIR"}/discord-ipc-"$i";
|
||||||
done
|
done
|
||||||
|
|
||||||
export PATH="${PATH}${PATH:+:}/app/utils/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin"
|
export PATH="${PATH}${PATH:+:}/usr/lib/extensions/vulkan/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin"
|
||||||
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}${LD_LIBRARY_PATH:+:}/usr/lib/extensions/vulkan/MangoHud/\$LIB/"
|
export VK_LAYER_PATH="/usr/lib/extensions/vulkan/share/vulkan/implicit_layer.d/"
|
||||||
|
|
||||||
exec /app/bin/prismrun "$@"
|
exec /app/bin/prismrun "$@"
|
||||||
|
1
flatpak/shared-modules
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 45094ca570be383d06df729b6972830ec63bd3df
|
5
garnix.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
builds:
|
||||||
|
exclude: []
|
||||||
|
include:
|
||||||
|
- "devShells.*-linux.*"
|
||||||
|
- "packages.*-linux.*"
|
@ -9,6 +9,7 @@
|
|||||||
* Copyright (C) 2022 Tayou <tayou@gmx.net>
|
* Copyright (C) 2022 Tayou <tayou@gmx.net>
|
||||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||||
|
* Copyright (C) 2023 seth <getchoo at tuta dot io>
|
||||||
*
|
*
|
||||||
* 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
|
||||||
@ -433,7 +434,11 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
}
|
}
|
||||||
// seach root path
|
// seach root path
|
||||||
if(!foundLoggingRules) {
|
if(!foundLoggingRules) {
|
||||||
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||||
|
logRulesPath = FS::PathCombine(m_rootPath, "share", BuildConfig.LAUNCHER_NAME, logRulesFile);
|
||||||
|
#else
|
||||||
logRulesPath = FS::PathCombine(m_rootPath, logRulesFile);
|
logRulesPath = FS::PathCombine(m_rootPath, logRulesFile);
|
||||||
|
#endif
|
||||||
qDebug() << "Testing" << logRulesPath << "...";
|
qDebug() << "Testing" << logRulesPath << "...";
|
||||||
foundLoggingRules = QFile::exists(logRulesPath);
|
foundLoggingRules = QFile::exists(logRulesPath);
|
||||||
}
|
}
|
||||||
@ -471,6 +476,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT;
|
qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT;
|
||||||
qDebug() << "Version : " << BuildConfig.printableVersionString();
|
qDebug() << "Version : " << BuildConfig.printableVersionString();
|
||||||
|
qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM;
|
||||||
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
|
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
|
||||||
qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC;
|
qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC;
|
||||||
if (adjustedBy.size())
|
if (adjustedBy.size())
|
||||||
@ -568,6 +574,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
// Language
|
// Language
|
||||||
m_settings->registerSetting("Language", QString());
|
m_settings->registerSetting("Language", QString());
|
||||||
|
m_settings->registerSetting("UseSystemLocale", false);
|
||||||
|
|
||||||
// Console
|
// Console
|
||||||
m_settings->registerSetting("ShowConsole", false);
|
m_settings->registerSetting("ShowConsole", false);
|
||||||
@ -594,7 +601,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
// Java Settings
|
// Java Settings
|
||||||
m_settings->registerSetting("JavaPath", "");
|
m_settings->registerSetting("JavaPath", "");
|
||||||
m_settings->registerSetting("JavaTimestamp", 0);
|
m_settings->registerSetting("JavaSignature", "");
|
||||||
m_settings->registerSetting("JavaArchitecture", "");
|
m_settings->registerSetting("JavaArchitecture", "");
|
||||||
m_settings->registerSetting("JavaRealArchitecture", "");
|
m_settings->registerSetting("JavaRealArchitecture", "");
|
||||||
m_settings->registerSetting("JavaVersion", "");
|
m_settings->registerSetting("JavaVersion", "");
|
||||||
@ -604,6 +611,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
m_settings->registerSetting("IgnoreJavaCompatibility", false);
|
m_settings->registerSetting("IgnoreJavaCompatibility", false);
|
||||||
m_settings->registerSetting("IgnoreJavaWizard", false);
|
m_settings->registerSetting("IgnoreJavaWizard", false);
|
||||||
|
|
||||||
|
// Mod loader settings
|
||||||
|
m_settings->registerSetting("DisableQuiltBeacon", false);
|
||||||
|
|
||||||
// Native library workarounds
|
// Native library workarounds
|
||||||
m_settings->registerSetting("UseNativeOpenAL", false);
|
m_settings->registerSetting("UseNativeOpenAL", false);
|
||||||
m_settings->registerSetting("UseNativeGLFW", false);
|
m_settings->registerSetting("UseNativeGLFW", false);
|
||||||
@ -687,8 +697,16 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
m_settings->reset("PastebinCustomAPIBase");
|
m_settings->reset("PastebinCustomAPIBase");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// meta URL
|
{
|
||||||
m_settings->registerSetting("MetaURLOverride", "");
|
// Meta URL
|
||||||
|
m_settings->registerSetting("MetaURLOverride", "");
|
||||||
|
|
||||||
|
QUrl metaUrl(m_settings->get("MetaURLOverride").toString());
|
||||||
|
|
||||||
|
// get rid of invalid meta urls
|
||||||
|
if (!metaUrl.isValid() || metaUrl.scheme() != "http" || metaUrl.scheme() != "https")
|
||||||
|
m_settings->reset("MetaURLOverride");
|
||||||
|
}
|
||||||
|
|
||||||
m_settings->registerSetting("CloseAfterLaunch", false);
|
m_settings->registerSetting("CloseAfterLaunch", false);
|
||||||
m_settings->registerSetting("QuitAfterGameStop", false);
|
m_settings->registerSetting("QuitAfterGameStop", false);
|
||||||
@ -910,12 +928,7 @@ bool Application::createSetupWizard()
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}();
|
}();
|
||||||
bool languageRequired = [&]()
|
bool languageRequired = settings()->get("Language").toString().isEmpty();
|
||||||
{
|
|
||||||
if (settings()->get("Language").toString().isEmpty())
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}();
|
|
||||||
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
||||||
bool themeInterventionRequired = settings()->get("ApplicationTheme") == "";
|
bool themeInterventionRequired = settings()->get("ApplicationTheme") == "";
|
||||||
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
|
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
|
||||||
@ -1559,7 +1572,7 @@ QString Application::getJarPath(QString jarFile)
|
|||||||
{
|
{
|
||||||
QStringList potentialPaths = {
|
QStringList potentialPaths = {
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||||
FS::PathCombine(m_rootPath, "share/" + BuildConfig.LAUNCHER_APP_BINARY_NAME),
|
FS::PathCombine(m_rootPath, "share", BuildConfig.LAUNCHER_NAME),
|
||||||
#endif
|
#endif
|
||||||
FS::PathCombine(m_rootPath, "jars"),
|
FS::PathCombine(m_rootPath, "jars"),
|
||||||
FS::PathCombine(applicationDirPath(), "jars"),
|
FS::PathCombine(applicationDirPath(), "jars"),
|
||||||
|
@ -15,16 +15,15 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <QString>
|
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
|
#include <QString>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* An abstract base class for versions.
|
* An abstract base class for versions.
|
||||||
*/
|
*/
|
||||||
class BaseVersion
|
class BaseVersion {
|
||||||
{
|
public:
|
||||||
public:
|
|
||||||
using Ptr = std::shared_ptr<BaseVersion>;
|
using Ptr = std::shared_ptr<BaseVersion>;
|
||||||
virtual ~BaseVersion() {}
|
virtual ~BaseVersion() {}
|
||||||
/*!
|
/*!
|
||||||
@ -45,14 +44,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual QString typeString() const = 0;
|
virtual QString typeString() const = 0;
|
||||||
|
|
||||||
virtual bool operator<(BaseVersion &a)
|
virtual bool operator<(BaseVersion& a) { return name() < a.name(); };
|
||||||
{
|
virtual bool operator>(BaseVersion& a) { return name() > a.name(); };
|
||||||
return name() < a.name();
|
|
||||||
};
|
|
||||||
virtual bool operator>(BaseVersion &a)
|
|
||||||
{
|
|
||||||
return name() > a.name();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(BaseVersion::Ptr)
|
Q_DECLARE_METATYPE(BaseVersion::Ptr)
|
||||||
|
@ -375,8 +375,6 @@ set(MINECRAFT_SOURCES
|
|||||||
minecraft/services/SkinDelete.cpp
|
minecraft/services/SkinDelete.cpp
|
||||||
minecraft/services/SkinDelete.h
|
minecraft/services/SkinDelete.h
|
||||||
|
|
||||||
mojang/PackageManifest.h
|
|
||||||
mojang/PackageManifest.cpp
|
|
||||||
minecraft/Agent.h)
|
minecraft/Agent.h)
|
||||||
|
|
||||||
# the screenshots feature
|
# the screenshots feature
|
||||||
@ -682,6 +680,7 @@ SET(LAUNCHER_SOURCES
|
|||||||
VersionProxyModel.h
|
VersionProxyModel.h
|
||||||
VersionProxyModel.cpp
|
VersionProxyModel.cpp
|
||||||
Markdown.h
|
Markdown.h
|
||||||
|
Markdown.cpp
|
||||||
|
|
||||||
# Super secret!
|
# Super secret!
|
||||||
KonamiCode.h
|
KonamiCode.h
|
||||||
@ -825,8 +824,8 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/pages/global/APIPage.h
|
ui/pages/global/APIPage.h
|
||||||
|
|
||||||
# GUI - platform pages
|
# GUI - platform pages
|
||||||
ui/pages/modplatform/VanillaPage.cpp
|
ui/pages/modplatform/CustomPage.cpp
|
||||||
ui/pages/modplatform/VanillaPage.h
|
ui/pages/modplatform/CustomPage.h
|
||||||
|
|
||||||
ui/pages/modplatform/ResourcePage.cpp
|
ui/pages/modplatform/ResourcePage.cpp
|
||||||
ui/pages/modplatform/ResourcePage.h
|
ui/pages/modplatform/ResourcePage.h
|
||||||
@ -1032,7 +1031,7 @@ qt_wrap_ui(LAUNCHER_UI
|
|||||||
ui/pages/instance/ScreenshotsPage.ui
|
ui/pages/instance/ScreenshotsPage.ui
|
||||||
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui
|
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui
|
||||||
ui/pages/modplatform/atlauncher/AtlPage.ui
|
ui/pages/modplatform/atlauncher/AtlPage.ui
|
||||||
ui/pages/modplatform/VanillaPage.ui
|
ui/pages/modplatform/CustomPage.ui
|
||||||
ui/pages/modplatform/ResourcePage.ui
|
ui/pages/modplatform/ResourcePage.ui
|
||||||
ui/pages/modplatform/flame/FlamePage.ui
|
ui/pages/modplatform/flame/FlamePage.ui
|
||||||
ui/pages/modplatform/legacy_ftb/Page.ui
|
ui/pages/modplatform/legacy_ftb/Page.ui
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include <QFileSystemModel>
|
#include <QFileSystemModel>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
#include <QStack>
|
#include <QStack>
|
||||||
|
#include <algorithm>
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "SeparatorPrefixTree.h"
|
#include "SeparatorPrefixTree.h"
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
@ -254,3 +255,25 @@ bool FileIgnoreProxy::filterAcceptsColumn(int source_column, const QModelIndex&
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FileIgnoreProxy::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
|
||||||
|
{
|
||||||
|
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||||
|
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
|
||||||
|
|
||||||
|
auto fileInfo = fsm->fileInfo(index);
|
||||||
|
return !ignoreFile(fileInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const
|
||||||
|
{
|
||||||
|
auto fileName = fileInfo.fileName();
|
||||||
|
auto path = relPath(fileInfo.absoluteFilePath());
|
||||||
|
return std::any_of(m_ignoreFiles.cbegin(), m_ignoreFiles.cend(), [fileName](auto iFileName) { return fileName == iFileName; }) ||
|
||||||
|
m_ignoreFilePaths.covers(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileIgnoreProxy::filterFile(const QString& fileName) const
|
||||||
|
{
|
||||||
|
return blocked.covers(fileName) || ignoreFile(QFileInfo(QDir(root), fileName));
|
||||||
|
}
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
#include "SeparatorPrefixTree.h"
|
#include "SeparatorPrefixTree.h"
|
||||||
|
|
||||||
@ -63,10 +64,22 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
|
|||||||
inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return blocked; }
|
inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return blocked; }
|
||||||
inline SeparatorPrefixTree<'/'>& blockedPaths() { return blocked; }
|
inline SeparatorPrefixTree<'/'>& blockedPaths() { return blocked; }
|
||||||
|
|
||||||
|
// list of file names that need to be removed completely from model
|
||||||
|
inline QStringList& ignoreFilesWithName() { return m_ignoreFiles; }
|
||||||
|
// list of relative paths that need to be removed completely from model
|
||||||
|
inline SeparatorPrefixTree<'/'>& ignoreFilesWithPath() { return m_ignoreFilePaths; }
|
||||||
|
|
||||||
|
bool filterFile(const QString& fileName) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const;
|
bool filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const;
|
||||||
|
bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
|
||||||
|
|
||||||
|
bool ignoreFile(QFileInfo file) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QString root;
|
const QString root;
|
||||||
SeparatorPrefixTree<'/'> blocked;
|
SeparatorPrefixTree<'/'> blocked;
|
||||||
|
QStringList m_ignoreFiles;
|
||||||
|
SeparatorPrefixTree<'/'> m_ignoreFilePaths;
|
||||||
};
|
};
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
#include <QPair>
|
||||||
|
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
|
|
||||||
@ -102,7 +103,7 @@ namespace fs = ghc::filesystem;
|
|||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
#elif defined(Q_OS_MACOS)
|
||||||
#include <sys/attr.h>
|
#include <sys/attr.h>
|
||||||
#include <sys/clonefile.h>
|
#include <sys/clonefile.h>
|
||||||
#elif defined(Q_OS_WIN)
|
#elif defined(Q_OS_WIN)
|
||||||
@ -246,6 +247,7 @@ bool copy::operator()(const QString& offset, bool dryRun)
|
|||||||
{
|
{
|
||||||
using copy_opts = fs::copy_options;
|
using copy_opts = fs::copy_options;
|
||||||
m_copied = 0; // reset counter
|
m_copied = 0; // reset counter
|
||||||
|
m_failedPaths.clear();
|
||||||
|
|
||||||
// NOTE always deep copy on windows. the alternatives are too messy.
|
// NOTE always deep copy on windows. the alternatives are too messy.
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
@ -277,6 +279,9 @@ bool copy::operator()(const QString& offset, bool dryRun)
|
|||||||
qWarning() << "Failed to copy files:" << QString::fromStdString(err.message());
|
qWarning() << "Failed to copy files:" << QString::fromStdString(err.message());
|
||||||
qDebug() << "Source file:" << src_path;
|
qDebug() << "Source file:" << src_path;
|
||||||
qDebug() << "Destination file:" << dst_path;
|
qDebug() << "Destination file:" << dst_path;
|
||||||
|
m_failedPaths.append(dst_path);
|
||||||
|
emit copyFailed(relative_dst_path);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
m_copied++;
|
m_copied++;
|
||||||
emit fileCopied(relative_dst_path);
|
emit fileCopied(relative_dst_path);
|
||||||
@ -372,7 +377,7 @@ void create_link::make_link_list(const QString& offset)
|
|||||||
auto src_path = source_it.next();
|
auto src_path = source_it.next();
|
||||||
auto relative_path = src_dir.relativeFilePath(src_path);
|
auto relative_path = src_dir.relativeFilePath(src_path);
|
||||||
|
|
||||||
if (m_max_depth >= 0 && pathDepth(relative_path) > m_max_depth){
|
if (m_max_depth >= 0 && pathDepth(relative_path) > m_max_depth) {
|
||||||
relative_path = pathTruncate(relative_path, m_max_depth);
|
relative_path = pathTruncate(relative_path, m_max_depth);
|
||||||
src_path = src_dir.filePath(relative_path);
|
src_path = src_dir.filePath(relative_path);
|
||||||
if (linkedPaths.contains(src_path)) {
|
if (linkedPaths.contains(src_path)) {
|
||||||
@ -663,7 +668,7 @@ QString pathTruncate(const QString& path, int depth)
|
|||||||
|
|
||||||
QString trunc = QFileInfo(path).path();
|
QString trunc = QFileInfo(path).path();
|
||||||
|
|
||||||
if (pathDepth(trunc) > depth ) {
|
if (pathDepth(trunc) > depth) {
|
||||||
return pathTruncate(trunc, depth);
|
return pathTruncate(trunc, depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -769,6 +774,9 @@ QString getDesktopDir()
|
|||||||
// Cross-platform Shortcut creation
|
// Cross-platform Shortcut creation
|
||||||
bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon)
|
bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon)
|
||||||
{
|
{
|
||||||
|
if (destination.isEmpty()) {
|
||||||
|
destination = PathCombine(getDesktopDir(), RemoveInvalidFilenameChars(name));
|
||||||
|
}
|
||||||
#if defined(Q_OS_MACOS)
|
#if defined(Q_OS_MACOS)
|
||||||
destination += ".command";
|
destination += ".command";
|
||||||
|
|
||||||
@ -791,6 +799,8 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||||
|
if (!destination.endsWith(".desktop")) // in case of isFlatpak destination is already populated
|
||||||
|
destination += ".desktop";
|
||||||
QFile f(destination);
|
QFile f(destination);
|
||||||
f.open(QIODevice::WriteOnly | QIODevice::Text);
|
f.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||||
QTextStream stream(&f);
|
QTextStream stream(&f);
|
||||||
@ -974,7 +984,7 @@ FilesystemType getFilesystemType(const QString& name)
|
|||||||
{
|
{
|
||||||
for (auto iter = s_filesystem_type_names.constBegin(); iter != s_filesystem_type_names.constEnd(); ++iter) {
|
for (auto iter = s_filesystem_type_names.constBegin(); iter != s_filesystem_type_names.constEnd(); ++iter) {
|
||||||
auto fs_names = iter.value();
|
auto fs_names = iter.value();
|
||||||
if(fs_names.contains(name.toUpper()))
|
if (fs_names.contains(name.toUpper()))
|
||||||
return iter.key();
|
return iter.key();
|
||||||
}
|
}
|
||||||
return FilesystemType::UNKNOWN;
|
return FilesystemType::UNKNOWN;
|
||||||
@ -1072,6 +1082,7 @@ bool clone::operator()(const QString& offset, bool dryRun)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_cloned = 0; // reset counter
|
m_cloned = 0; // reset counter
|
||||||
|
m_failedClones.clear();
|
||||||
|
|
||||||
auto src = PathCombine(m_src.absolutePath(), offset);
|
auto src = PathCombine(m_src.absolutePath(), offset);
|
||||||
auto dst = PathCombine(m_dst.absolutePath(), offset);
|
auto dst = PathCombine(m_dst.absolutePath(), offset);
|
||||||
@ -1092,6 +1103,9 @@ bool clone::operator()(const QString& offset, bool dryRun)
|
|||||||
qDebug() << "Failed to clone files: error" << err.value() << "message" << QString::fromStdString(err.message());
|
qDebug() << "Failed to clone files: error" << err.value() << "message" << QString::fromStdString(err.message());
|
||||||
qDebug() << "Source file:" << src_path;
|
qDebug() << "Source file:" << src_path;
|
||||||
qDebug() << "Destination file:" << dst_path;
|
qDebug() << "Destination file:" << dst_path;
|
||||||
|
m_failedClones.append(qMakePair(src_path, dst_path));
|
||||||
|
emit cloneFailed(src_path, dst_path);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
m_cloned++;
|
m_cloned++;
|
||||||
emit fileCloned(src_path, dst_path);
|
emit fileCloned(src_path, dst_path);
|
||||||
@ -1151,7 +1165,7 @@ bool clone_file(const QString& src, const QString& dst, std::error_code& ec)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
#elif defined(Q_OS_MACOS)
|
||||||
|
|
||||||
if (!macos_bsd_clonefile(src_path, dst_path, ec)) {
|
if (!macos_bsd_clonefile(src_path, dst_path, ec)) {
|
||||||
qDebug() << "failed macos_bsd_clonefile:";
|
qDebug() << "failed macos_bsd_clonefile:";
|
||||||
@ -1380,7 +1394,7 @@ bool linux_ficlone(const std::string& src_path, const std::string& dst_path, std
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
#elif defined(Q_OS_MACOS)
|
||||||
|
|
||||||
bool macos_bsd_clonefile(const std::string& src_path, const std::string& dst_path, std::error_code& ec)
|
bool macos_bsd_clonefile(const std::string& src_path, const std::string& dst_path, std::error_code& ec)
|
||||||
{
|
{
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QPair>
|
||||||
#include <QFlags>
|
#include <QFlags>
|
||||||
#include <QLocalServer>
|
#include <QLocalServer>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
@ -112,9 +113,12 @@ 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; }
|
int totalCopied() { return m_copied; }
|
||||||
|
int totalFailed() { return m_failedPaths.length(); }
|
||||||
|
QStringList failed() { return m_failedPaths; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void fileCopied(const QString& relativeName);
|
void fileCopied(const QString& relativeName);
|
||||||
|
void copyFailed(const QString& relativeName);
|
||||||
// TODO: maybe add a "shouldCopy" signal in the future?
|
// TODO: maybe add a "shouldCopy" signal in the future?
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -127,6 +131,7 @@ class copy : public QObject {
|
|||||||
QDir m_src;
|
QDir m_src;
|
||||||
QDir m_dst;
|
QDir m_dst;
|
||||||
int m_copied;
|
int m_copied;
|
||||||
|
QStringList m_failedPaths;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LinkPair {
|
struct LinkPair {
|
||||||
@ -471,6 +476,9 @@ 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; }
|
int totalCloned() { return m_cloned; }
|
||||||
|
int totalFailed() { return m_failedClones.length(); }
|
||||||
|
|
||||||
|
QList<QPair<QString, QString>> failed() { return m_failedClones; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void fileCloned(const QString& src, const QString& dst);
|
void fileCloned(const QString& src, const QString& dst);
|
||||||
@ -485,6 +493,7 @@ class clone : public QObject {
|
|||||||
QDir m_src;
|
QDir m_src;
|
||||||
QDir m_dst;
|
QDir m_dst;
|
||||||
int m_cloned;
|
int m_cloned;
|
||||||
|
QList<QPair<QString, QString>> m_failedClones;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -187,8 +187,8 @@ void LaunchController::login() {
|
|||||||
switch(m_accountToUse->accountState()) {
|
switch(m_accountToUse->accountState()) {
|
||||||
case AccountState::Offline: {
|
case AccountState::Offline: {
|
||||||
m_session->wants_online = false;
|
m_session->wants_online = false;
|
||||||
// NOTE: fallthrough is intentional
|
|
||||||
}
|
}
|
||||||
|
/* fallthrough */
|
||||||
case AccountState::Online: {
|
case AccountState::Online: {
|
||||||
if(!m_session->wants_online) {
|
if(!m_session->wants_online) {
|
||||||
// we ask the user for a player name
|
// we ask the user for a player name
|
||||||
@ -267,8 +267,8 @@ void LaunchController::login() {
|
|||||||
// This means some sort of soft error that we can fix with a refresh ... so let's refresh.
|
// This means some sort of soft error that we can fix with a refresh ... so let's refresh.
|
||||||
case AccountState::Unchecked: {
|
case AccountState::Unchecked: {
|
||||||
m_accountToUse->refresh();
|
m_accountToUse->refresh();
|
||||||
// NOTE: fallthrough intentional
|
|
||||||
}
|
}
|
||||||
|
/* fallthrough */
|
||||||
case AccountState::Working: {
|
case AccountState::Working: {
|
||||||
// refresh is in progress, we need to wait for it to finish to proceed.
|
// refresh is in progress, we need to wait for it to finish to proceed.
|
||||||
ProgressDialog progDialog(m_parentWidget);
|
ProgressDialog progDialog(m_parentWidget);
|
||||||
@ -390,7 +390,10 @@ void LaunchController::launchInstance()
|
|||||||
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
|
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
|
||||||
|
|
||||||
// Prepend Version
|
// Prepend Version
|
||||||
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), BuildConfig.LAUNCHER_DISPLAYNAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher));
|
{
|
||||||
|
auto versionString = QString("%1 version: %2 (%3)").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString(), BuildConfig.BUILD_PLATFORM);
|
||||||
|
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), versionString + "\n\n", MessageLevel::Launcher));
|
||||||
|
}
|
||||||
m_launcher->start();
|
m_launcher->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
31
launcher/Markdown.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 Joshua Goins <josh@redstrate.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 "Markdown.h"
|
||||||
|
|
||||||
|
QString markdownToHTML(const QString& markdown)
|
||||||
|
{
|
||||||
|
const QByteArray markdownData = markdown.toUtf8();
|
||||||
|
char* buffer = cmark_markdown_to_html(markdownData.constData(), markdownData.length(), CMARK_OPT_NOBREAKS | CMARK_OPT_UNSAFE);
|
||||||
|
|
||||||
|
QString htmlStr(buffer);
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
return htmlStr;
|
||||||
|
}
|
@ -21,14 +21,4 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <cmark.h>
|
#include <cmark.h>
|
||||||
|
|
||||||
static QString markdownToHTML(const QString& markdown)
|
QString markdownToHTML(const QString& markdown);
|
||||||
{
|
|
||||||
const QByteArray markdownData = markdown.toUtf8();
|
|
||||||
char* buffer = cmark_markdown_to_html(markdownData.constData(), markdownData.length(), CMARK_OPT_NOBREAKS | CMARK_OPT_UNSAFE);
|
|
||||||
|
|
||||||
QString htmlStr(buffer);
|
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
|
|
||||||
return htmlStr;
|
|
||||||
}
|
|
@ -187,35 +187,24 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
|
|||||||
}
|
}
|
||||||
case Qt::ToolTipRole:
|
case Qt::ToolTipRole:
|
||||||
{
|
{
|
||||||
switch(column)
|
if(column == Name && hasRecommended)
|
||||||
{
|
{
|
||||||
case Name:
|
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
|
||||||
|
if(value.toBool())
|
||||||
{
|
{
|
||||||
if(hasRecommended)
|
return tr("Recommended");
|
||||||
|
} else if(hasLatest) {
|
||||||
|
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
|
||||||
|
if(value.toBool())
|
||||||
{
|
{
|
||||||
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
|
return tr("Latest");
|
||||||
if(value.toBool())
|
|
||||||
{
|
|
||||||
return tr("Recommended");
|
|
||||||
}
|
|
||||||
else if(hasLatest)
|
|
||||||
{
|
|
||||||
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
|
|
||||||
if(value.toBool())
|
|
||||||
{
|
|
||||||
return tr("Latest");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(index.row() == 0)
|
|
||||||
{
|
|
||||||
return tr("Latest");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
} else if(index.row() == 0)
|
||||||
default:
|
|
||||||
{
|
{
|
||||||
return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole);
|
return tr("Latest");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case Qt::DecorationRole:
|
case Qt::DecorationRole:
|
||||||
|
@ -85,7 +85,7 @@ void JavaChecker::performCheck()
|
|||||||
process->setProgram(m_path);
|
process->setProgram(m_path);
|
||||||
process->setProcessChannelMode(QProcess::SeparateChannels);
|
process->setProcessChannelMode(QProcess::SeparateChannels);
|
||||||
process->setProcessEnvironment(CleanEnviroment());
|
process->setProcessEnvironment(CleanEnviroment());
|
||||||
qDebug() << "Running java checker: " + m_path + args.join(" ");;
|
qDebug() << "Running java checker:" << m_path << args.join(" ");
|
||||||
|
|
||||||
connect(process.get(), QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &JavaChecker::finished);
|
connect(process.get(), QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &JavaChecker::finished);
|
||||||
connect(process.get(), &QProcess::errorOccurred, this, &JavaChecker::error);
|
connect(process.get(), &QProcess::errorOccurred, this, &JavaChecker::error);
|
||||||
@ -128,7 +128,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
|||||||
result.outLog = m_stdout;
|
result.outLog = m_stdout;
|
||||||
qDebug() << "STDOUT" << m_stdout;
|
qDebug() << "STDOUT" << m_stdout;
|
||||||
qWarning() << "STDERR" << m_stderr;
|
qWarning() << "STDERR" << m_stderr;
|
||||||
qDebug() << "Java checker finished with status " << status << " exit code " << exitcode;
|
qDebug() << "Java checker finished with status" << status << "exit code" << exitcode;
|
||||||
|
|
||||||
if (status == QProcess::CrashExit || exitcode == 1)
|
if (status == QProcess::CrashExit || exitcode == 1)
|
||||||
{
|
{
|
||||||
|
@ -1,29 +1,64 @@
|
|||||||
|
// 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "JavaInstall.h"
|
#include "JavaInstall.h"
|
||||||
|
|
||||||
|
#include "BaseVersion.h"
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
|
|
||||||
bool JavaInstall::operator<(const JavaInstall &rhs)
|
bool JavaInstall::operator<(const JavaInstall& rhs)
|
||||||
{
|
{
|
||||||
auto archCompare = StringUtils::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive);
|
auto archCompare = StringUtils::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive);
|
||||||
if(archCompare != 0)
|
if (archCompare != 0)
|
||||||
return archCompare < 0;
|
return archCompare < 0;
|
||||||
if(id < rhs.id)
|
if (id < rhs.id) {
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(id > rhs.id)
|
if (id > rhs.id) {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return StringUtils::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0;
|
return StringUtils::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JavaInstall::operator==(const JavaInstall &rhs)
|
bool JavaInstall::operator==(const JavaInstall& rhs)
|
||||||
{
|
{
|
||||||
return arch == rhs.arch && id == rhs.id && path == rhs.path;
|
return arch == rhs.arch && id == rhs.id && path == rhs.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JavaInstall::operator>(const JavaInstall &rhs)
|
bool JavaInstall::operator>(const JavaInstall& rhs)
|
||||||
{
|
{
|
||||||
return (!operator<(rhs)) && (!operator==(rhs));
|
return (!operator<(rhs)) && (!operator==(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool JavaInstall::operator<(BaseVersion& a)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return operator<(dynamic_cast<JavaInstall&>(a));
|
||||||
|
} catch (const std::bad_cast& e) {
|
||||||
|
return BaseVersion::operator<(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JavaInstall::operator>(BaseVersion& a)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return operator>(dynamic_cast<JavaInstall&>(a));
|
||||||
|
} catch (const std::bad_cast& e) {
|
||||||
|
return BaseVersion::operator>(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,33 +1,40 @@
|
|||||||
|
// 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "BaseVersion.h"
|
#include "BaseVersion.h"
|
||||||
#include "JavaVersion.h"
|
#include "JavaVersion.h"
|
||||||
|
|
||||||
struct JavaInstall : public BaseVersion
|
struct JavaInstall : public BaseVersion {
|
||||||
{
|
JavaInstall() {}
|
||||||
JavaInstall(){}
|
JavaInstall(QString id, QString arch, QString path) : id(id), arch(arch), path(path) {}
|
||||||
JavaInstall(QString id, QString arch, QString path)
|
virtual QString descriptor() { return id.toString(); }
|
||||||
: id(id), arch(arch), path(path)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
virtual QString descriptor()
|
|
||||||
{
|
|
||||||
return id.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual QString name()
|
virtual QString name() { return id.toString(); }
|
||||||
{
|
|
||||||
return id.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual QString typeString() const
|
virtual QString typeString() const { return arch; }
|
||||||
{
|
|
||||||
return arch;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator<(const JavaInstall & rhs);
|
virtual bool operator<(BaseVersion& a) override;
|
||||||
bool operator==(const JavaInstall & rhs);
|
virtual bool operator>(BaseVersion& a) override;
|
||||||
bool operator>(const JavaInstall & rhs);
|
bool operator<(const JavaInstall& rhs);
|
||||||
|
bool operator==(const JavaInstall& rhs);
|
||||||
|
bool operator>(const JavaInstall& rhs);
|
||||||
|
|
||||||
JavaVersion id;
|
JavaVersion id;
|
||||||
QString arch;
|
QString arch;
|
||||||
|
@ -81,15 +81,20 @@ void CheckJava::executeTask()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QFileInfo javaInfo(realJavaPath);
|
QFileInfo javaInfo(realJavaPath);
|
||||||
qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch();
|
qint64 javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch();
|
||||||
auto storedUnixTime = settings->get("JavaTimestamp").toLongLong();
|
auto storedSignature = settings->get("JavaSignature").toString();
|
||||||
auto storedArchitecture = settings->get("JavaArchitecture").toString();
|
auto storedArchitecture = settings->get("JavaArchitecture").toString();
|
||||||
auto storedRealArchitecture = settings->get("JavaRealArchitecture").toString();
|
auto storedRealArchitecture = settings->get("JavaRealArchitecture").toString();
|
||||||
auto storedVersion = settings->get("JavaVersion").toString();
|
auto storedVersion = settings->get("JavaVersion").toString();
|
||||||
auto storedVendor = settings->get("JavaVendor").toString();
|
auto storedVendor = settings->get("JavaVendor").toString();
|
||||||
m_javaUnixTime = javaUnixTime;
|
|
||||||
|
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||||
|
hash.addData(QByteArray::number(javaUnixTime));
|
||||||
|
hash.addData(m_javaPath.toUtf8());
|
||||||
|
m_javaSignature = hash.result().toHex();
|
||||||
|
|
||||||
// if timestamps are not the same, or something is missing, check!
|
// if timestamps are not the same, or something is missing, check!
|
||||||
if (javaUnixTime != storedUnixTime || storedVersion.size() == 0
|
if (m_javaSignature != storedSignature || storedVersion.size() == 0
|
||||||
|| storedArchitecture.size() == 0 || storedRealArchitecture.size() == 0
|
|| storedArchitecture.size() == 0 || storedRealArchitecture.size() == 0
|
||||||
|| storedVendor.size() == 0)
|
|| storedVendor.size() == 0)
|
||||||
{
|
{
|
||||||
@ -140,7 +145,7 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
|
|||||||
instance->settings()->set("JavaArchitecture", result.mojangPlatform);
|
instance->settings()->set("JavaArchitecture", result.mojangPlatform);
|
||||||
instance->settings()->set("JavaRealArchitecture", result.realPlatform);
|
instance->settings()->set("JavaRealArchitecture", result.realPlatform);
|
||||||
instance->settings()->set("JavaVendor", result.javaVendor);
|
instance->settings()->set("JavaVendor", result.javaVendor);
|
||||||
instance->settings()->set("JavaTimestamp", m_javaUnixTime);
|
instance->settings()->set("JavaSignature", m_javaSignature);
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,6 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_javaPath;
|
QString m_javaPath;
|
||||||
qlonglong m_javaUnixTime;
|
QString m_javaSignature;
|
||||||
JavaCheckerPtr m_JavaChecker;
|
JavaCheckerPtr m_JavaChecker;
|
||||||
};
|
};
|
||||||
|
@ -45,10 +45,10 @@ QVariant Index::data(const QModelIndex &index, int role) const
|
|||||||
switch (role)
|
switch (role)
|
||||||
{
|
{
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
switch (index.column())
|
if (index.column() == 0) {
|
||||||
{
|
return list->humanReadable();
|
||||||
case 0: return list->humanReadable();
|
} else {
|
||||||
default: break;
|
break;
|
||||||
}
|
}
|
||||||
case UidRole: return list->uid();
|
case UidRole: return list->uid();
|
||||||
case NameRole: return list->name();
|
case NameRole: return list->name();
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
* Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
* Copyright (c) 2023 seth <getchoo at tuta dot io>
|
||||||
*
|
*
|
||||||
* 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
|
||||||
@ -148,7 +149,7 @@ void MinecraftInstance::loadSpecificSettings()
|
|||||||
m_settings->registerOverride(global_settings->getSetting("IgnoreJavaCompatibility"), javaOrLocation);
|
m_settings->registerOverride(global_settings->getSetting("IgnoreJavaCompatibility"), javaOrLocation);
|
||||||
|
|
||||||
// special!
|
// special!
|
||||||
m_settings->registerPassthrough(global_settings->getSetting("JavaTimestamp"), javaOrLocation);
|
m_settings->registerPassthrough(global_settings->getSetting("JavaSignature"), javaOrLocation);
|
||||||
m_settings->registerPassthrough(global_settings->getSetting("JavaArchitecture"), javaOrLocation);
|
m_settings->registerPassthrough(global_settings->getSetting("JavaArchitecture"), javaOrLocation);
|
||||||
m_settings->registerPassthrough(global_settings->getSetting("JavaRealArchitecture"), javaOrLocation);
|
m_settings->registerPassthrough(global_settings->getSetting("JavaRealArchitecture"), javaOrLocation);
|
||||||
m_settings->registerPassthrough(global_settings->getSetting("JavaVersion"), javaOrLocation);
|
m_settings->registerPassthrough(global_settings->getSetting("JavaVersion"), javaOrLocation);
|
||||||
@ -186,6 +187,10 @@ void MinecraftInstance::loadSpecificSettings()
|
|||||||
m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride);
|
m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride);
|
||||||
m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride);
|
m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride);
|
||||||
|
|
||||||
|
// Mod loader specific options
|
||||||
|
auto modLoaderSettings = m_settings->registerSetting("OverrideModLoaderSettings", false);
|
||||||
|
m_settings->registerOverride(global_settings->getSetting("DisableQuiltBeacon"), modLoaderSettings);
|
||||||
|
|
||||||
m_settings->set("InstanceType", "OneSix");
|
m_settings->set("InstanceType", "OneSix");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,6 +396,12 @@ QStringList MinecraftInstance::extraArguments()
|
|||||||
agent->library()->getApplicableFiles(runtimeContext(), jar, temp1, temp2, temp3, getLocalLibraryPath());
|
agent->library()->getApplicableFiles(runtimeContext(), jar, temp1, temp2, temp3, getLocalLibraryPath());
|
||||||
list.append("-javaagent:"+jar[0]+(agent->argument().isEmpty() ? "" : "="+agent->argument()));
|
list.append("-javaagent:"+jar[0]+(agent->argument().isEmpty() ? "" : "="+agent->argument()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto loaders = version->getModLoaders();
|
||||||
|
if (loaders.has_value() && loaders.value() & ResourceAPI::Quilt && settings()->get("DisableQuiltBeacon").toBool())
|
||||||
|
list.append("-Dloader.disable_beacon=true");
|
||||||
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -832,7 +843,7 @@ QMap<QString, QString> MinecraftInstance::createCensorFilterFromSession(AuthSess
|
|||||||
{
|
{
|
||||||
addToFilter(sessionRef.session, tr("<SESSION ID>"));
|
addToFilter(sessionRef.session, tr("<SESSION ID>"));
|
||||||
}
|
}
|
||||||
if (sessionRef.access_token != "offline") {
|
if (sessionRef.access_token != "0") {
|
||||||
addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>"));
|
addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>"));
|
||||||
}
|
}
|
||||||
if(sessionRef.client_token.size()) {
|
if(sessionRef.client_token.size()) {
|
||||||
|
@ -65,7 +65,8 @@
|
|||||||
static const QMap<QString, ResourceAPI::ModLoaderType> modloaderMapping{
|
static const QMap<QString, ResourceAPI::ModLoaderType> modloaderMapping{
|
||||||
{"net.minecraftforge", ResourceAPI::Forge},
|
{"net.minecraftforge", ResourceAPI::Forge},
|
||||||
{"net.fabricmc.fabric-loader", ResourceAPI::Fabric},
|
{"net.fabricmc.fabric-loader", ResourceAPI::Fabric},
|
||||||
{"org.quiltmc.quilt-loader", ResourceAPI::Quilt}
|
{"org.quiltmc.quilt-loader", ResourceAPI::Quilt},
|
||||||
|
{"com.mumfrey.liteloader", ResourceAPI::LiteLoader}
|
||||||
};
|
};
|
||||||
|
|
||||||
PackProfile::PackProfile(MinecraftInstance * instance)
|
PackProfile::PackProfile(MinecraftInstance * instance)
|
||||||
|
@ -374,6 +374,10 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
yggdrasilToken = tokenFromJSONV3(data, "ygg");
|
yggdrasilToken = tokenFromJSONV3(data, "ygg");
|
||||||
|
// versions before 7.2 used "offline" as the offline token
|
||||||
|
if (yggdrasilToken.token == "offline")
|
||||||
|
yggdrasilToken.token = "0";
|
||||||
|
|
||||||
minecraftProfile = profileFromJSONV3(data, "profile");
|
minecraftProfile = profileFromJSONV3(data, "profile");
|
||||||
if(!entitlementFromJSONV3(data, minecraftEntitlement)) {
|
if(!entitlementFromJSONV3(data, minecraftEntitlement)) {
|
||||||
if(minecraftProfile.validity != Katabasis::Validity::None) {
|
if(minecraftProfile.validity != Katabasis::Validity::None) {
|
||||||
|
@ -328,18 +328,21 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
|
|||||||
case AccountState::Gone: {
|
case AccountState::Gone: {
|
||||||
return tr("Gone", "Account status");
|
return tr("Gone", "Account status");
|
||||||
}
|
}
|
||||||
|
default: {
|
||||||
|
return tr("Unknown", "Account status");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case MigrationColumn: {
|
case MigrationColumn: {
|
||||||
if(account->isMSA() || account->isOffline()) {
|
if(account->isMSA() || account->isOffline()) {
|
||||||
return tr("N/A", "Can Migrate?");
|
return tr("N/A", "Can Migrate");
|
||||||
}
|
}
|
||||||
if (account->canMigrate()) {
|
if (account->canMigrate()) {
|
||||||
return tr("Yes", "Can Migrate?");
|
return tr("Yes", "Can Migrate");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return tr("No", "Can Migrate?");
|
return tr("No", "Can Migrate");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,12 +357,13 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
|
|||||||
return QVariant::fromValue(account);
|
return QVariant::fromValue(account);
|
||||||
|
|
||||||
case Qt::CheckStateRole:
|
case Qt::CheckStateRole:
|
||||||
switch (index.column())
|
if (index.column() == ProfileNameColumn) {
|
||||||
{
|
return account == m_defaultAccount ? Qt::Checked : Qt::Unchecked;
|
||||||
case ProfileNameColumn:
|
} else {
|
||||||
return account == m_defaultAccount ? Qt::Checked : Qt::Unchecked;
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ bool AuthSession::MakeOffline(QString offline_playername)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
session = "-";
|
session = "-";
|
||||||
|
access_token = "0";
|
||||||
player_name = offline_playername;
|
player_name = offline_playername;
|
||||||
status = PlayableOffline;
|
status = PlayableOffline;
|
||||||
return true;
|
return true;
|
||||||
|
@ -93,7 +93,7 @@ MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username)
|
|||||||
{
|
{
|
||||||
auto account = makeShared<MinecraftAccount>();
|
auto account = makeShared<MinecraftAccount>();
|
||||||
account->data.type = AccountType::Offline;
|
account->data.type = AccountType::Offline;
|
||||||
account->data.yggdrasilToken.token = "offline";
|
account->data.yggdrasilToken.token = "0";
|
||||||
account->data.yggdrasilToken.validity = Katabasis::Validity::Certain;
|
account->data.yggdrasilToken.validity = Katabasis::Validity::Certain;
|
||||||
account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc();
|
account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc();
|
||||||
account->data.yggdrasilToken.extra["userName"] = username;
|
account->data.yggdrasilToken.extra["userName"] = username;
|
||||||
|
@ -273,6 +273,7 @@ void Yggdrasil::processReply() {
|
|||||||
AccountTaskState::STATE_FAILED_GONE,
|
AccountTaskState::STATE_FAILED_GONE,
|
||||||
tr("The Mojang account no longer exists. It may have been migrated to a Microsoft account.")
|
tr("The Mojang account no longer exists. It may have been migrated to a Microsoft account.")
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
changeState(
|
changeState(
|
||||||
|
@ -74,6 +74,7 @@ std::pair<int, bool> DataPack::compare(const Resource& other, SortType type) con
|
|||||||
auto res = Resource::compare(other, type);
|
auto res = Resource::compare(other, type);
|
||||||
if (res.first != 0)
|
if (res.first != 0)
|
||||||
return res;
|
return res;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case SortType::PACK_FORMAT: {
|
case SortType::PACK_FORMAT: {
|
||||||
auto this_ver = packFormat();
|
auto this_ver = packFormat();
|
||||||
@ -83,6 +84,7 @@ std::pair<int, bool> DataPack::compare(const Resource& other, SortType type) con
|
|||||||
return { 1, type == SortType::PACK_FORMAT };
|
return { 1, type == SortType::PACK_FORMAT };
|
||||||
if (this_ver < other_ver)
|
if (this_ver < other_ver)
|
||||||
return { -1, type == SortType::PACK_FORMAT };
|
return { -1, type == SortType::PACK_FORMAT };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { 0, false };
|
return { 0, false };
|
||||||
|
@ -89,6 +89,7 @@ std::pair<int, bool> Mod::compare(const Resource& other, SortType type) const
|
|||||||
auto res = Resource::compare(other, type);
|
auto res = Resource::compare(other, type);
|
||||||
if (res.first != 0)
|
if (res.first != 0)
|
||||||
return res;
|
return res;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case SortType::VERSION: {
|
case SortType::VERSION: {
|
||||||
auto this_ver = Version(version());
|
auto this_ver = Version(version());
|
||||||
@ -97,11 +98,13 @@ std::pair<int, bool> Mod::compare(const Resource& other, SortType type) const
|
|||||||
return { 1, type == SortType::VERSION };
|
return { 1, type == SortType::VERSION };
|
||||||
if (this_ver < other_ver)
|
if (this_ver < other_ver)
|
||||||
return { -1, type == SortType::VERSION };
|
return { -1, type == SortType::VERSION };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case SortType::PROVIDER: {
|
case SortType::PROVIDER: {
|
||||||
auto compare_result = QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive);
|
auto compare_result = QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive);
|
||||||
if (compare_result != 0)
|
if (compare_result != 0)
|
||||||
return { compare_result, type == SortType::PROVIDER };
|
return { compare_result, type == SortType::PROVIDER };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { 0, false };
|
return { 0, false };
|
||||||
@ -121,7 +124,7 @@ bool Mod::applyFilter(QRegularExpression filter) const
|
|||||||
return Resource::applyFilter(filter);
|
return Resource::applyFilter(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Mod::destroy(QDir& index_dir, bool preserve_metadata) -> bool
|
auto Mod::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool
|
||||||
{
|
{
|
||||||
if (!preserve_metadata) {
|
if (!preserve_metadata) {
|
||||||
qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name());
|
qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name());
|
||||||
@ -134,7 +137,7 @@ auto Mod::destroy(QDir& index_dir, bool preserve_metadata) -> bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Resource::destroy();
|
return Resource::destroy(attempt_trash);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Mod::details() const -> const ModDetails&
|
auto Mod::details() const -> const ModDetails&
|
||||||
|
@ -79,7 +79,7 @@ public:
|
|||||||
[[nodiscard]] bool applyFilter(QRegularExpression filter) const override;
|
[[nodiscard]] bool applyFilter(QRegularExpression filter) const override;
|
||||||
|
|
||||||
// Delete all the files of this mod
|
// Delete all the files of this mod
|
||||||
auto destroy(QDir& index_dir, bool preserve_metadata = false) -> bool;
|
auto destroy(QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool;
|
||||||
|
|
||||||
void finishResolvingWithDetails(ModDetails&& details);
|
void finishResolvingWithDetails(ModDetails&& details);
|
||||||
|
|
||||||
|
@ -197,10 +197,10 @@ Task* ModFolderModel::createParseTask(Resource& resource)
|
|||||||
|
|
||||||
bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata)
|
bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata)
|
||||||
{
|
{
|
||||||
for(auto mod : allMods()){
|
for(auto mod : allMods()) {
|
||||||
if(mod->fileinfo().fileName() == filename){
|
if(mod->fileinfo().fileName() == filename) {
|
||||||
auto index_dir = indexDir();
|
auto index_dir = indexDir();
|
||||||
mod->destroy(index_dir, preserve_metadata);
|
mod->destroy(index_dir, preserve_metadata, false);
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
|
||||||
|
@ -71,6 +71,7 @@ std::pair<int, bool> Resource::compare(const Resource& other, SortType type) con
|
|||||||
return { 1, type == SortType::ENABLED };
|
return { 1, type == SortType::ENABLED };
|
||||||
if (!enabled() && other.enabled())
|
if (!enabled() && other.enabled())
|
||||||
return { -1, type == SortType::ENABLED };
|
return { -1, type == SortType::ENABLED };
|
||||||
|
break;
|
||||||
case SortType::NAME: {
|
case SortType::NAME: {
|
||||||
QString this_name{ name() };
|
QString this_name{ name() };
|
||||||
QString other_name{ other.name() };
|
QString other_name{ other.name() };
|
||||||
@ -81,12 +82,14 @@ std::pair<int, bool> Resource::compare(const Resource& other, SortType type) con
|
|||||||
auto compare_result = QString::compare(this_name, other_name, Qt::CaseInsensitive);
|
auto compare_result = QString::compare(this_name, other_name, Qt::CaseInsensitive);
|
||||||
if (compare_result != 0)
|
if (compare_result != 0)
|
||||||
return { compare_result, type == SortType::NAME };
|
return { compare_result, type == SortType::NAME };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case SortType::DATE:
|
case SortType::DATE:
|
||||||
if (dateTimeChanged() > other.dateTimeChanged())
|
if (dateTimeChanged() > other.dateTimeChanged())
|
||||||
return { 1, type == SortType::DATE };
|
return { 1, type == SortType::DATE };
|
||||||
if (dateTimeChanged() < other.dateTimeChanged())
|
if (dateTimeChanged() < other.dateTimeChanged())
|
||||||
return { -1, type == SortType::DATE };
|
return { -1, type == SortType::DATE };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { 0, false };
|
return { 0, false };
|
||||||
@ -145,14 +148,10 @@ bool Resource::enable(EnableAction action)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resource::destroy()
|
bool Resource::destroy(bool attemptTrash)
|
||||||
{
|
{
|
||||||
m_type = ResourceType::UNKNOWN;
|
m_type = ResourceType::UNKNOWN;
|
||||||
|
return (attemptTrash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath());
|
||||||
if (FS::trash(m_file_info.filePath()))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return FS::deletePath(m_file_info.filePath());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resource::isSymLinkUnder(const QString& instPath) const
|
bool Resource::isSymLinkUnder(const QString& instPath) const
|
||||||
|
@ -92,7 +92,7 @@ class Resource : public QObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete all files of this resource.
|
// Delete all files of this resource.
|
||||||
bool destroy();
|
bool destroy(bool attemptTrash = true);
|
||||||
|
|
||||||
[[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); }
|
[[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); }
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ bool ResourceFolderModel::uninstallResource(QString file_name)
|
|||||||
{
|
{
|
||||||
for (auto& resource : m_resources) {
|
for (auto& resource : m_resources) {
|
||||||
if (resource->fileinfo().fileName() == file_name) {
|
if (resource->fileinfo().fileName() == file_name) {
|
||||||
auto res = resource->destroy();
|
auto res = resource->destroy(false);
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
|
||||||
|
@ -95,6 +95,7 @@ std::pair<int, bool> ResourcePack::compare(const Resource& other, SortType type)
|
|||||||
auto res = Resource::compare(other, type);
|
auto res = Resource::compare(other, type);
|
||||||
if (res.first != 0)
|
if (res.first != 0)
|
||||||
return res;
|
return res;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case SortType::PACK_FORMAT: {
|
case SortType::PACK_FORMAT: {
|
||||||
auto this_ver = packFormat();
|
auto this_ver = packFormat();
|
||||||
@ -104,6 +105,7 @@ std::pair<int, bool> ResourcePack::compare(const Resource& other, SortType type)
|
|||||||
return { 1, type == SortType::PACK_FORMAT };
|
return { 1, type == SortType::PACK_FORMAT };
|
||||||
if (this_ver < other_ver)
|
if (this_ver < other_ver)
|
||||||
return { -1, type == SortType::PACK_FORMAT };
|
return { -1, type == SortType::PACK_FORMAT };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { 0, false };
|
return { 0, false };
|
||||||
|
@ -44,7 +44,11 @@ static const QMap<PackedResourceType, QString> s_packed_type_names = {
|
|||||||
namespace ResourceUtils {
|
namespace ResourceUtils {
|
||||||
PackedResourceType identify(QFileInfo file){
|
PackedResourceType identify(QFileInfo file){
|
||||||
if (file.exists() && file.isFile()) {
|
if (file.exists() && file.isFile()) {
|
||||||
if (ResourcePackUtils::validate(file)) {
|
if (ModUtils::validate(file)) {
|
||||||
|
// mods can contain resource and data packs so they must be tested first
|
||||||
|
qDebug() << file.fileName() << "is a mod";
|
||||||
|
return PackedResourceType::Mod;
|
||||||
|
} else if (ResourcePackUtils::validate(file)) {
|
||||||
qDebug() << file.fileName() << "is a resource pack";
|
qDebug() << file.fileName() << "is a resource pack";
|
||||||
return PackedResourceType::ResourcePack;
|
return PackedResourceType::ResourcePack;
|
||||||
} else if (TexturePackUtils::validate(file)) {
|
} else if (TexturePackUtils::validate(file)) {
|
||||||
@ -53,9 +57,6 @@ PackedResourceType identify(QFileInfo file){
|
|||||||
} else if (DataPackUtils::validate(file)) {
|
} else if (DataPackUtils::validate(file)) {
|
||||||
qDebug() << file.fileName() << "is a data pack";
|
qDebug() << file.fileName() << "is a data pack";
|
||||||
return PackedResourceType::DataPack;
|
return PackedResourceType::DataPack;
|
||||||
} else if (ModUtils::validate(file)) {
|
|
||||||
qDebug() << file.fileName() << "is a mod";
|
|
||||||
return PackedResourceType::Mod;
|
|
||||||
} else if (WorldSaveUtils::validate(file)) {
|
} else if (WorldSaveUtils::validate(file)) {
|
||||||
qDebug() << file.fileName() << "is a world save";
|
qDebug() << file.fileName() << "is a world save";
|
||||||
return PackedResourceType::WorldSave;
|
return PackedResourceType::WorldSave;
|
||||||
|
@ -103,7 +103,7 @@ void ModFolderLoadTask::executeTask()
|
|||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
auto mod = iter.next().value();
|
auto mod = iter.next().value();
|
||||||
if (mod->status() == ModStatus::NotInstalled) {
|
if (mod->status() == ModStatus::NotInstalled) {
|
||||||
mod->destroy(m_index_dir, false);
|
mod->destroy(m_index_dir, false, false);
|
||||||
iter.remove();
|
iter.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "modplatform/flame/FlameAPI.h"
|
#include "modplatform/flame/FlameAPI.h"
|
||||||
#include "modplatform/flame/FlameModIndex.h"
|
#include "modplatform/flame/FlameModIndex.h"
|
||||||
|
#include "modplatform/helpers/HashUtils.h"
|
||||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||||
#include "modplatform/modrinth/ModrinthPackIndex.h"
|
#include "modplatform/modrinth/ModrinthPackIndex.h"
|
||||||
|
|
||||||
@ -24,8 +25,8 @@ EnsureMetadataTask::EnsureMetadataTask(Mod* mod, QDir dir, ModPlatform::Resource
|
|||||||
auto hash_task = createNewHash(mod);
|
auto hash_task = createNewHash(mod);
|
||||||
if (!hash_task)
|
if (!hash_task)
|
||||||
return;
|
return;
|
||||||
connect(hash_task.get(), &Task::succeeded, [this, hash_task, mod] { m_mods.insert(hash_task->getResult(), mod); });
|
connect(hash_task.get(), &Hashing::Hasher::resultsReady, [this, mod](QString hash) { m_mods.insert(hash, mod); });
|
||||||
connect(hash_task.get(), &Task::failed, [this, hash_task, mod] { emitFail(mod, "", RemoveFromList::No); });
|
connect(hash_task.get(), &Task::failed, [this, mod] { emitFail(mod, "", RemoveFromList::No); });
|
||||||
hash_task->start();
|
hash_task->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,8 +38,8 @@ EnsureMetadataTask::EnsureMetadataTask(QList<Mod*>& mods, QDir dir, ModPlatform:
|
|||||||
auto hash_task = createNewHash(mod);
|
auto hash_task = createNewHash(mod);
|
||||||
if (!hash_task)
|
if (!hash_task)
|
||||||
continue;
|
continue;
|
||||||
connect(hash_task.get(), &Task::succeeded, [this, hash_task, mod] { m_mods.insert(hash_task->getResult(), mod); });
|
connect(hash_task.get(), &Hashing::Hasher::resultsReady, [this, mod](QString hash) { m_mods.insert(hash, mod); });
|
||||||
connect(hash_task.get(), &Task::failed, [this, hash_task, mod] { emitFail(mod, "", RemoveFromList::No); });
|
connect(hash_task.get(), &Task::failed, [this, mod] { emitFail(mod, "", RemoveFromList::No); });
|
||||||
m_hashing_task->addTask(hash_task);
|
m_hashing_task->addTask(hash_task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,7 +145,8 @@ void EnsureMetadataTask::executeTask()
|
|||||||
connect(project_task.get(), &Task::finished, this, [=] {
|
connect(project_task.get(), &Task::finished, this, [=] {
|
||||||
invalidade_leftover();
|
invalidade_leftover();
|
||||||
project_task->deleteLater();
|
project_task->deleteLater();
|
||||||
m_current_task = nullptr;
|
if (m_current_task)
|
||||||
|
m_current_task.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
m_current_task = project_task;
|
m_current_task = project_task;
|
||||||
@ -153,7 +155,8 @@ void EnsureMetadataTask::executeTask()
|
|||||||
|
|
||||||
connect(version_task.get(), &Task::finished, [=] {
|
connect(version_task.get(), &Task::finished, [=] {
|
||||||
version_task->deleteLater();
|
version_task->deleteLater();
|
||||||
m_current_task = nullptr;
|
if (m_current_task)
|
||||||
|
m_current_task.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (m_mods.size() > 1)
|
if (m_mods.size() > 1)
|
||||||
@ -212,12 +215,12 @@ Task::Ptr EnsureMetadataTask::modrinthVersionsTask()
|
|||||||
{
|
{
|
||||||
auto hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first();
|
auto hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first();
|
||||||
|
|
||||||
auto* response = new QByteArray();
|
auto response = std::make_shared<QByteArray>();
|
||||||
auto ver_task = modrinth_api.currentVersions(m_mods.keys(), hash_type, response);
|
auto ver_task = modrinth_api.currentVersions(m_mods.keys(), hash_type, response);
|
||||||
|
|
||||||
// Prevents unfortunate timings when aborting the task
|
// Prevents unfortunate timings when aborting the task
|
||||||
if (!ver_task)
|
if (!ver_task)
|
||||||
return Task::Ptr{nullptr};
|
return Task::Ptr{ nullptr };
|
||||||
|
|
||||||
connect(ver_task.get(), &Task::succeeded, this, [this, response] {
|
connect(ver_task.get(), &Task::succeeded, this, [this, response] {
|
||||||
QJsonParseError parse_error{};
|
QJsonParseError parse_error{};
|
||||||
@ -264,7 +267,7 @@ Task::Ptr EnsureMetadataTask::modrinthProjectsTask()
|
|||||||
for (auto const& data : m_temp_versions)
|
for (auto const& data : m_temp_versions)
|
||||||
addonIds.insert(data.addonId.toString(), data.hash);
|
addonIds.insert(data.addonId.toString(), data.hash);
|
||||||
|
|
||||||
auto response = new QByteArray();
|
auto response = std::make_shared<QByteArray>();
|
||||||
Task::Ptr proj_task;
|
Task::Ptr proj_task;
|
||||||
|
|
||||||
if (addonIds.isEmpty()) {
|
if (addonIds.isEmpty()) {
|
||||||
@ -277,7 +280,7 @@ Task::Ptr EnsureMetadataTask::modrinthProjectsTask()
|
|||||||
|
|
||||||
// Prevents unfortunate timings when aborting the task
|
// Prevents unfortunate timings when aborting the task
|
||||||
if (!proj_task)
|
if (!proj_task)
|
||||||
return Task::Ptr{nullptr};
|
return Task::Ptr{ nullptr };
|
||||||
|
|
||||||
connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] {
|
connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] {
|
||||||
QJsonParseError parse_error{};
|
QJsonParseError parse_error{};
|
||||||
@ -345,7 +348,7 @@ Task::Ptr EnsureMetadataTask::modrinthProjectsTask()
|
|||||||
// Flame
|
// Flame
|
||||||
Task::Ptr EnsureMetadataTask::flameVersionsTask()
|
Task::Ptr EnsureMetadataTask::flameVersionsTask()
|
||||||
{
|
{
|
||||||
auto* response = new QByteArray();
|
auto response = std::make_shared<QByteArray>();
|
||||||
|
|
||||||
QList<uint> fingerprints;
|
QList<uint> fingerprints;
|
||||||
for (auto& murmur : m_mods.keys()) {
|
for (auto& murmur : m_mods.keys()) {
|
||||||
@ -413,7 +416,7 @@ Task::Ptr EnsureMetadataTask::flameProjectsTask()
|
|||||||
QHash<QString, QString> addonIds;
|
QHash<QString, QString> addonIds;
|
||||||
for (auto const& hash : m_mods.keys()) {
|
for (auto const& hash : m_mods.keys()) {
|
||||||
if (m_temp_versions.contains(hash)) {
|
if (m_temp_versions.contains(hash)) {
|
||||||
auto const& data = m_temp_versions.find(hash).value();
|
auto data = m_temp_versions.find(hash).value();
|
||||||
|
|
||||||
auto id_str = data.addonId.toString();
|
auto id_str = data.addonId.toString();
|
||||||
if (!id_str.isEmpty())
|
if (!id_str.isEmpty())
|
||||||
@ -421,7 +424,7 @@ Task::Ptr EnsureMetadataTask::flameProjectsTask()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto response = new QByteArray();
|
auto response = std::make_shared<QByteArray>();
|
||||||
Task::Ptr proj_task;
|
Task::Ptr proj_task;
|
||||||
|
|
||||||
if (addonIds.isEmpty()) {
|
if (addonIds.isEmpty()) {
|
||||||
@ -434,7 +437,7 @@ Task::Ptr EnsureMetadataTask::flameProjectsTask()
|
|||||||
|
|
||||||
// Prevents unfortunate timings when aborting the task
|
// Prevents unfortunate timings when aborting the task
|
||||||
if (!proj_task)
|
if (!proj_task)
|
||||||
return Task::Ptr{nullptr};
|
return Task::Ptr{ nullptr };
|
||||||
|
|
||||||
connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] {
|
connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] {
|
||||||
QJsonParseError parse_error{};
|
QJsonParseError parse_error{};
|
||||||
|
@ -121,12 +121,12 @@ class ResourceAPI {
|
|||||||
qWarning() << "TODO";
|
qWarning() << "TODO";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
[[nodiscard]] virtual Task::Ptr getProject(QString addonId, QByteArray* response) const
|
[[nodiscard]] virtual Task::Ptr getProject(QString addonId, std::shared_ptr<QByteArray> response) const
|
||||||
{
|
{
|
||||||
qWarning() << "TODO";
|
qWarning() << "TODO";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
[[nodiscard]] virtual Task::Ptr getProjects(QStringList addonIds, QByteArray* response) const
|
[[nodiscard]] virtual Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const
|
||||||
{
|
{
|
||||||
qWarning() << "TODO";
|
qWarning() << "TODO";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -82,9 +82,9 @@ void PackInstallTask::executeTask()
|
|||||||
{
|
{
|
||||||
qDebug() << "PackInstallTask::executeTask: " << QThread::currentThreadId();
|
qDebug() << "PackInstallTask::executeTask: " << QThread::currentThreadId();
|
||||||
NetJob::Ptr netJob{ new NetJob("ATLauncher::VersionFetch", APPLICATION->network()) };
|
NetJob::Ptr netJob{ new NetJob("ATLauncher::VersionFetch", APPLICATION->network()) };
|
||||||
auto searchUrl = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.json")
|
auto searchUrl =
|
||||||
.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::Download::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);
|
||||||
@ -99,11 +99,12 @@ void PackInstallTask::onDownloadSucceeded()
|
|||||||
qDebug() << "PackInstallTask::onDownloadSucceeded: " << QThread::currentThreadId();
|
qDebug() << "PackInstallTask::onDownloadSucceeded: " << QThread::currentThreadId();
|
||||||
jobPtr.reset();
|
jobPtr.reset();
|
||||||
|
|
||||||
QJsonParseError parse_error {};
|
QJsonParseError parse_error{};
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
|
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||||
if(parse_error.error != QJsonParseError::NoError) {
|
if (parse_error.error != QJsonParseError::NoError) {
|
||||||
qWarning() << "Error while parsing JSON response from ATLauncher at " << parse_error.offset << " reason: " << parse_error.errorString();
|
qWarning() << "Error while parsing JSON response from ATLauncher at " << parse_error.offset
|
||||||
qWarning() << response;
|
<< " reason: " << parse_error.errorString();
|
||||||
|
qWarning() << *response.get();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto obj = doc.object();
|
auto obj = doc.object();
|
||||||
|
@ -40,12 +40,13 @@
|
|||||||
#include "ATLPackManifest.h"
|
#include "ATLPackManifest.h"
|
||||||
|
|
||||||
#include "InstanceTask.h"
|
#include "InstanceTask.h"
|
||||||
#include "net/NetJob.h"
|
#include "meta/Version.h"
|
||||||
#include "settings/INISettingsObject.h"
|
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftInstance.h"
|
||||||
#include "minecraft/PackProfile.h"
|
#include "minecraft/PackProfile.h"
|
||||||
#include "meta/Version.h"
|
#include "net/NetJob.h"
|
||||||
|
#include "settings/INISettingsObject.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
namespace ATLauncher {
|
namespace ATLauncher {
|
||||||
@ -57,8 +58,7 @@ enum class InstallMode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class UserInteractionSupport {
|
class UserInteractionSupport {
|
||||||
|
public:
|
||||||
public:
|
|
||||||
/**
|
/**
|
||||||
* Requests a user interaction to select which optional mods should be installed.
|
* Requests a user interaction to select which optional mods should be installed.
|
||||||
*/
|
*/
|
||||||
@ -74,23 +74,27 @@ public:
|
|||||||
* Requests a user interaction to display a message.
|
* Requests a user interaction to display a message.
|
||||||
*/
|
*/
|
||||||
virtual void displayMessage(QString message) = 0;
|
virtual void displayMessage(QString message) = 0;
|
||||||
|
|
||||||
|
virtual ~UserInteractionSupport() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PackInstallTask : public InstanceTask
|
class PackInstallTask : public InstanceTask {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit PackInstallTask(UserInteractionSupport *support, QString packName, QString version, InstallMode installMode = InstallMode::Install);
|
explicit PackInstallTask(UserInteractionSupport* support,
|
||||||
virtual ~PackInstallTask(){}
|
QString packName,
|
||||||
|
QString version,
|
||||||
|
InstallMode installMode = InstallMode::Install);
|
||||||
|
virtual ~PackInstallTask() { delete m_support; }
|
||||||
|
|
||||||
bool canAbort() const override { return true; }
|
bool canAbort() const override { return true; }
|
||||||
bool abort() override;
|
bool abort() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void executeTask() override;
|
virtual void executeTask() override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onDownloadSucceeded();
|
void onDownloadSucceeded();
|
||||||
void onDownloadFailed(QString reason);
|
void onDownloadFailed(QString reason);
|
||||||
void onDownloadAborted();
|
void onDownloadAborted();
|
||||||
@ -98,7 +102,7 @@ private slots:
|
|||||||
void onModsDownloaded();
|
void onModsDownloaded();
|
||||||
void onModsExtracted();
|
void onModsExtracted();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString getDirForModType(ModType type, QString raw);
|
QString getDirForModType(ModType type, QString raw);
|
||||||
QString getVersionForLoader(QString uid);
|
QString getVersionForLoader(QString uid);
|
||||||
QString detectLibrary(VersionLibrary library);
|
QString detectLibrary(VersionLibrary library);
|
||||||
@ -110,20 +114,18 @@ private:
|
|||||||
void installConfigs();
|
void installConfigs();
|
||||||
void extractConfigs();
|
void extractConfigs();
|
||||||
void downloadMods();
|
void downloadMods();
|
||||||
bool extractMods(
|
bool extractMods(const QMap<QString, VersionMod>& toExtract,
|
||||||
const QMap<QString, VersionMod> &toExtract,
|
const QMap<QString, VersionMod>& toDecomp,
|
||||||
const QMap<QString, VersionMod> &toDecomp,
|
const QMap<QString, QString>& toCopy);
|
||||||
const QMap<QString, QString> &toCopy
|
|
||||||
);
|
|
||||||
void install();
|
void install();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UserInteractionSupport *m_support;
|
UserInteractionSupport* m_support;
|
||||||
|
|
||||||
bool abortable = false;
|
bool abortable = false;
|
||||||
|
|
||||||
NetJob::Ptr jobPtr;
|
NetJob::Ptr jobPtr;
|
||||||
QByteArray response;
|
std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>();
|
||||||
|
|
||||||
InstallMode m_install_mode;
|
InstallMode m_install_mode;
|
||||||
QString m_pack_name;
|
QString m_pack_name;
|
||||||
@ -145,7 +147,6 @@ private:
|
|||||||
|
|
||||||
QFuture<bool> m_modExtractFuture;
|
QFuture<bool> m_modExtractFuture;
|
||||||
QFutureWatcher<bool> m_modExtractFutureWatcher;
|
QFutureWatcher<bool> m_modExtractFutureWatcher;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace ATLauncher
|
||||||
|
@ -21,19 +21,24 @@ bool Flame::FileResolvingTask::abort()
|
|||||||
|
|
||||||
void Flame::FileResolvingTask::executeTask()
|
void Flame::FileResolvingTask::executeTask()
|
||||||
{
|
{
|
||||||
|
if (m_toProcess.files.isEmpty()) { // no file to resolve so leave it empty and emit success immediately
|
||||||
|
emitSucceeded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
setStatus(tr("Resolving mod IDs..."));
|
setStatus(tr("Resolving mod IDs..."));
|
||||||
setProgress(0, 3);
|
setProgress(0, 3);
|
||||||
m_dljob.reset(new NetJob("Mod id resolver", m_network));
|
m_dljob.reset(new NetJob("Mod id resolver", m_network));
|
||||||
result.reset(new QByteArray());
|
result.reset(new QByteArray());
|
||||||
//build json data to send
|
// build json data to send
|
||||||
QJsonObject object;
|
QJsonObject object;
|
||||||
|
|
||||||
object["fileIds"] = QJsonArray::fromVariantList(std::accumulate(m_toProcess.files.begin(), m_toProcess.files.end(), QVariantList(), [](QVariantList& l, const File& s) {
|
object["fileIds"] = QJsonArray::fromVariantList(
|
||||||
l.push_back(s.fileId);
|
std::accumulate(m_toProcess.files.begin(), m_toProcess.files.end(), QVariantList(), [](QVariantList& l, const File& s) {
|
||||||
return l;
|
l.push_back(s.fileId);
|
||||||
}));
|
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.get(), data);
|
auto dl = Net::Upload::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>();
|
||||||
@ -87,17 +92,15 @@ void Flame::FileResolvingTask::netJobFinished()
|
|||||||
auto fileid = Json::requireInteger(Json::requireObject(file)["id"]);
|
auto fileid = Json::requireInteger(Json::requireObject(file)["id"]);
|
||||||
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 (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.get());
|
auto dl = Net::Download::makeByteArray(QUrl(url), output);
|
||||||
QObject::connect(dl.get(), &Net::Download::succeeded, [&out]() {
|
QObject::connect(dl.get(), &Net::Download::succeeded, [&out]() { out.resolved = true; });
|
||||||
out.resolved = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
m_checkJob->addNetAction(dl);
|
m_checkJob->addNetAction(dl);
|
||||||
blockedProjects.insert(&out, output);
|
blockedProjects.insert(&out, output);
|
||||||
@ -129,12 +132,13 @@ void Flame::FileResolvingTask::netJobFinished()
|
|||||||
m_checkJob->start();
|
m_checkJob->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Flame::FileResolvingTask::modrinthCheckFinished() {
|
void Flame::FileResolvingTask::modrinthCheckFinished()
|
||||||
|
{
|
||||||
setProgress(2, 3);
|
setProgress(2, 3);
|
||||||
qDebug() << "Finished with blocked mods : " << blockedProjects.size();
|
qDebug() << "Finished with blocked mods : " << blockedProjects.size();
|
||||||
|
|
||||||
for (auto it = blockedProjects.keyBegin(); it != blockedProjects.keyEnd(); it++) {
|
for (auto it = blockedProjects.keyBegin(); it != blockedProjects.keyEnd(); it++) {
|
||||||
auto &out = *it;
|
auto& out = *it;
|
||||||
auto bytes = blockedProjects[out];
|
auto bytes = blockedProjects[out];
|
||||||
if (!out->resolved) {
|
if (!out->resolved) {
|
||||||
continue;
|
continue;
|
||||||
@ -154,28 +158,26 @@ void Flame::FileResolvingTask::modrinthCheckFinished() {
|
|||||||
out->resolved = false;
|
out->resolved = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//copy to an output list and filter out projects found on modrinth
|
// copy to an output list and filter out projects found on modrinth
|
||||||
auto block = std::make_shared<QList<File*>>();
|
auto block = std::make_shared<QList<File*>>();
|
||||||
auto it = blockedProjects.keys();
|
auto it = blockedProjects.keys();
|
||||||
std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File *f) {
|
std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File* f) { return !f->resolved; });
|
||||||
return !f->resolved;
|
// Display not found mods early
|
||||||
});
|
|
||||||
//Display not found mods early
|
|
||||||
if (!block->empty()) {
|
if (!block->empty()) {
|
||||||
//blocked mods found, we need the slug for displaying.... we need another job :D !
|
// blocked mods found, we need the slug for displaying.... we need another job :D !
|
||||||
m_slugJob.reset(new NetJob("Slug Job", m_network));
|
m_slugJob.reset(new NetJob("Slug Job", m_network));
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (auto mod : *block) {
|
for (auto mod : *block) {
|
||||||
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.get());
|
auto dl = Net::Download::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
|
||||||
auto json = QJsonDocument::fromJson(*output);
|
auto json = QJsonDocument::fromJson(*output);
|
||||||
auto base = Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json),"data"),"links"),
|
auto base =
|
||||||
"websiteUrl");
|
Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json), "data"), "links"), "websiteUrl");
|
||||||
auto link = QString("%1/download/%2").arg(base, QString::number(mod->fileId));
|
auto link = QString("%1/download/%2").arg(base, QString::number(mod->fileId));
|
||||||
mod->websiteUrl = link;
|
mod->websiteUrl = link;
|
||||||
});
|
});
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
#include "net/Upload.h"
|
#include "net/Upload.h"
|
||||||
|
|
||||||
Task::Ptr FlameAPI::matchFingerprints(const QList<uint>& fingerprints, QByteArray* response)
|
Task::Ptr FlameAPI::matchFingerprints(const QList<uint>& fingerprints, std::shared_ptr<QByteArray> response)
|
||||||
{
|
{
|
||||||
auto netJob = makeShared<NetJob>(QString("Flame::MatchFingerprints"), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("Flame::MatchFingerprints"), APPLICATION->network());
|
||||||
|
|
||||||
@ -28,8 +28,6 @@ Task::Ptr FlameAPI::matchFingerprints(const QList<uint>& fingerprints, QByteArra
|
|||||||
|
|
||||||
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/fingerprints"), response, body_raw));
|
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/fingerprints"), response, body_raw));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
|
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +41,7 @@ auto FlameAPI::getModFileChangelog(int modId, int fileId) -> QString
|
|||||||
netJob->addNetAction(Net::Download::makeByteArray(
|
netJob->addNetAction(Net::Download::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.get()));
|
response));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &changelog] {
|
QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &changelog] {
|
||||||
QJsonParseError parse_error{};
|
QJsonParseError parse_error{};
|
||||||
@ -75,8 +73,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(Net::Download::makeByteArray(
|
netJob->addNetAction(
|
||||||
QString("https://api.curseforge.com/v1/mods/%1/description").arg(QString::number(modId)), response.get()));
|
Net::Download::makeByteArray(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{};
|
||||||
@ -115,7 +113,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.get()));
|
netJob->addNetAction(Net::Download::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{};
|
||||||
@ -137,7 +135,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe
|
|||||||
for (auto file : arr) {
|
for (auto file : arr) {
|
||||||
auto file_obj = Json::requireObject(file);
|
auto file_obj = Json::requireObject(file);
|
||||||
auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj);
|
auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj);
|
||||||
if(file_tmp.date > ver_tmp.date) {
|
if (file_tmp.date > ver_tmp.date) {
|
||||||
ver_tmp = file_tmp;
|
ver_tmp = file_tmp;
|
||||||
latest_file_obj = file_obj;
|
latest_file_obj = file_obj;
|
||||||
}
|
}
|
||||||
@ -160,7 +158,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe
|
|||||||
return ver;
|
return ver;
|
||||||
}
|
}
|
||||||
|
|
||||||
Task::Ptr FlameAPI::getProjects(QStringList addonIds, QByteArray* response) const
|
Task::Ptr FlameAPI::getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const
|
||||||
{
|
{
|
||||||
auto netJob = makeShared<NetJob>(QString("Flame::GetProjects"), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("Flame::GetProjects"), APPLICATION->network());
|
||||||
|
|
||||||
@ -177,13 +175,12 @@ Task::Ptr FlameAPI::getProjects(QStringList addonIds, QByteArray* response) cons
|
|||||||
|
|
||||||
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods"), response, body_raw));
|
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods"), response, body_raw));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
|
|
||||||
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::getFiles(const QStringList& fileIds, QByteArray* response) const
|
Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const
|
||||||
{
|
{
|
||||||
auto netJob = makeShared<NetJob>(QString("Flame::GetFiles"), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("Flame::GetFiles"), APPLICATION->network());
|
||||||
|
|
||||||
@ -200,7 +197,6 @@ Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, QByteArray* response) c
|
|||||||
|
|
||||||
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods/files"), response, body_raw));
|
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods/files"), response, body_raw));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
|
|
||||||
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;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include "modplatform/ModIndex.h"
|
#include "modplatform/ModIndex.h"
|
||||||
#include "modplatform/helpers/NetworkResourceAPI.h"
|
#include "modplatform/helpers/NetworkResourceAPI.h"
|
||||||
|
|
||||||
@ -14,12 +15,14 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
|
|
||||||
auto getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::IndexedVersion;
|
auto getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::IndexedVersion;
|
||||||
|
|
||||||
Task::Ptr getProjects(QStringList addonIds, QByteArray* response) const override;
|
Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
|
||||||
Task::Ptr matchFingerprints(const QList<uint>& fingerprints, QByteArray* response);
|
Task::Ptr matchFingerprints(const QList<uint>& fingerprints, std::shared_ptr<QByteArray> response);
|
||||||
Task::Ptr getFiles(const QStringList& fileIds, QByteArray* response) const;
|
Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const;
|
||||||
|
|
||||||
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
||||||
|
|
||||||
|
static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int getClassId(ModPlatform::ResourceType type)
|
static int getClassId(ModPlatform::ResourceType type)
|
||||||
{
|
{
|
||||||
@ -41,14 +44,15 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
return 4;
|
return 4;
|
||||||
// TODO: remove this once Quilt drops official Fabric support
|
// TODO: remove this once Quilt drops official Fabric support
|
||||||
if (loaders & Quilt) // NOTE: Most if not all Fabric mods should work *currently*
|
if (loaders & Quilt) // NOTE: Most if not all Fabric mods should work *currently*
|
||||||
return 4; // Quilt would probably be 5
|
return 4; // Quilt would probably be 5
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] std::optional<QString> getSearchURL(SearchArgs const& args) const override
|
[[nodiscard]] std::optional<QString> getSearchURL(SearchArgs const& args) const override
|
||||||
{
|
{
|
||||||
auto gameVersionStr = args.versions.has_value() ? QString("gameVersion=%1").arg(args.versions.value().front().toString()) : QString();
|
auto gameVersionStr =
|
||||||
|
args.versions.has_value() ? QString("gameVersion=%1").arg(args.versions.value().front().toString()) : QString();
|
||||||
|
|
||||||
QStringList get_arguments;
|
QStringList get_arguments;
|
||||||
get_arguments.append(QString("classId=%1").arg(getClassId(args.type)));
|
get_arguments.append(QString("classId=%1").arg(getClassId(args.type)));
|
||||||
@ -73,7 +77,7 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
|
|
||||||
[[nodiscard]] std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override
|
[[nodiscard]] std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override
|
||||||
{
|
{
|
||||||
QString url{QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&").arg(args.pack.addonId.toString())};
|
QString url{ QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&").arg(args.pack.addonId.toString()) };
|
||||||
|
|
||||||
QStringList get_parameters;
|
QStringList get_parameters;
|
||||||
if (args.mcVersions.has_value())
|
if (args.mcVersions.has_value())
|
||||||
|
@ -31,7 +31,7 @@ ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info)
|
|||||||
|
|
||||||
auto get_project_job = new NetJob("Flame::GetProjectJob", APPLICATION->network());
|
auto get_project_job = new NetJob("Flame::GetProjectJob", APPLICATION->network());
|
||||||
|
|
||||||
auto response = new 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::Download::makeByteArray(url, response);
|
||||||
get_project_job->addNetAction(dl);
|
get_project_job->addNetAction(dl);
|
||||||
@ -75,7 +75,7 @@ ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId)
|
|||||||
|
|
||||||
auto get_file_info_job = new NetJob("Flame::GetFileInfoJob", APPLICATION->network());
|
auto get_file_info_job = new NetJob("Flame::GetFileInfoJob", APPLICATION->network());
|
||||||
|
|
||||||
auto response = new 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::Download::makeByteArray(url, response);
|
||||||
get_file_info_job->addNetAction(dl);
|
get_file_info_job->addNetAction(dl);
|
||||||
|
@ -153,6 +153,9 @@ bool FlameCreationTask::updateInstance()
|
|||||||
|
|
||||||
old_files.remove(file.key());
|
old_files.remove(file.key());
|
||||||
files_iterator = files.erase(files_iterator);
|
files_iterator = files.erase(files_iterator);
|
||||||
|
|
||||||
|
if (files_iterator != files.begin())
|
||||||
|
files_iterator--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +182,7 @@ bool FlameCreationTask::updateInstance()
|
|||||||
fileIds.append(QString::number(file.fileId));
|
fileIds.append(QString::number(file.fileId));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* raw_response = new QByteArray;
|
auto raw_response = std::make_shared<QByteArray>();
|
||||||
auto job = api.getFiles(fileIds, raw_response);
|
auto job = api.getFiles(fileIds, raw_response);
|
||||||
|
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
@ -467,8 +470,9 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
|||||||
switch (result.type) {
|
switch (result.type) {
|
||||||
case Flame::File::Type::Folder: {
|
case Flame::File::Type::Folder: {
|
||||||
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
||||||
// fall-through intentional, we treat these as plain old mods and dump them wherever.
|
// fallthrough intentional, we treat these as plain old mods and dump them wherever.
|
||||||
}
|
}
|
||||||
|
/* fallthrough */
|
||||||
case Flame::File::Type::SingleFile:
|
case Flame::File::Type::SingleFile:
|
||||||
case Flame::File::Type::Mod: {
|
case Flame::File::Type::Mod: {
|
||||||
if (!result.url.isEmpty()) {
|
if (!result.url.isEmpty()) {
|
||||||
@ -559,6 +563,8 @@ void FlameCreationTask::validateZIPResouces()
|
|||||||
if (FS::move(localPath, destPath)) {
|
if (FS::move(localPath, destPath)) {
|
||||||
return destPath;
|
return destPath;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "Target folder of" << fileName << "is correct at" << targetFolder;
|
||||||
}
|
}
|
||||||
return localPath;
|
return localPath;
|
||||||
};
|
};
|
||||||
@ -580,6 +586,9 @@ void FlameCreationTask::validateZIPResouces()
|
|||||||
QString worldPath;
|
QString worldPath;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
case PackedResourceType::Mod :
|
||||||
|
validatePath(fileName, targetFolder, "mods");
|
||||||
|
break;
|
||||||
case PackedResourceType::ResourcePack :
|
case PackedResourceType::ResourcePack :
|
||||||
validatePath(fileName, targetFolder, "resourcepacks");
|
validatePath(fileName, targetFolder, "resourcepacks");
|
||||||
break;
|
break;
|
||||||
@ -589,9 +598,6 @@ void FlameCreationTask::validateZIPResouces()
|
|||||||
case PackedResourceType::DataPack :
|
case PackedResourceType::DataPack :
|
||||||
validatePath(fileName, targetFolder, "datapacks");
|
validatePath(fileName, targetFolder, "datapacks");
|
||||||
break;
|
break;
|
||||||
case PackedResourceType::Mod :
|
|
||||||
validatePath(fileName, targetFolder, "mods");
|
|
||||||
break;
|
|
||||||
case PackedResourceType::ShaderPack :
|
case PackedResourceType::ShaderPack :
|
||||||
// in theroy flame API can't do this but who knows, that *may* change ?
|
// in theroy flame API can't do this but who knows, that *may* change ?
|
||||||
// better to handle it if it *does* occure in the future
|
// better to handle it if it *does* occure in the future
|
||||||
|
@ -76,13 +76,8 @@ bool Flame::File::parseFromObject(const QJsonObject& obj, bool throw_on_blocked
|
|||||||
// It is also optional
|
// It is also optional
|
||||||
type = File::Type::SingleFile;
|
type = File::Type::SingleFile;
|
||||||
|
|
||||||
if (fileName.endsWith(".zip")) {
|
targetFolder = "mods";
|
||||||
// this is probably a resource pack
|
|
||||||
targetFolder = "resourcepacks";
|
|
||||||
} else {
|
|
||||||
// this is probably a mod, dunno what else could modpacks download
|
|
||||||
targetFolder = "mods";
|
|
||||||
}
|
|
||||||
// get the hash
|
// get the hash
|
||||||
hash = QString();
|
hash = QString();
|
||||||
auto hashes = Json::ensureArray(obj, "hashes");
|
auto hashes = Json::ensureArray(obj, "hashes");
|
||||||
|
@ -71,6 +71,7 @@ void ModrinthHasher::executeTask()
|
|||||||
emitFailed("Empty hash!");
|
emitFailed("Empty hash!");
|
||||||
} else {
|
} else {
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
|
emit resultsReady(m_hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,12 +89,12 @@ void FlameHasher::executeTask()
|
|||||||
emitFailed("Empty hash!");
|
emitFailed("Empty hash!");
|
||||||
} else {
|
} else {
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
|
emit resultsReady(m_hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BlockedModHasher::BlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider) : Hasher(file_path), provider(provider)
|
||||||
BlockedModHasher::BlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider)
|
{
|
||||||
: Hasher(file_path), provider(provider) {
|
|
||||||
setObjectName(QString("BlockedModHasher: %1").arg(file_path));
|
setObjectName(QString("BlockedModHasher: %1").arg(file_path));
|
||||||
hash_type = ProviderCaps.hashType(provider).first();
|
hash_type = ProviderCaps.hashType(provider).first();
|
||||||
}
|
}
|
||||||
@ -120,14 +121,17 @@ void BlockedModHasher::executeTask()
|
|||||||
emitFailed("Empty hash!");
|
emitFailed("Empty hash!");
|
||||||
} else {
|
} else {
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
|
emit resultsReady(m_hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList BlockedModHasher::getHashTypes() {
|
QStringList BlockedModHasher::getHashTypes()
|
||||||
|
{
|
||||||
return ProviderCaps.hashType(provider);
|
return ProviderCaps.hashType(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BlockedModHasher::useHashType(QString type) {
|
bool BlockedModHasher::useHashType(QString type)
|
||||||
|
{
|
||||||
auto types = ProviderCaps.hashType(provider);
|
auto types = ProviderCaps.hashType(provider);
|
||||||
if (types.contains(type)) {
|
if (types.contains(type)) {
|
||||||
hash_type = type;
|
hash_type = type;
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
namespace Hashing {
|
namespace Hashing {
|
||||||
|
|
||||||
class Hasher : public Task {
|
class Hasher : public Task {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
using Ptr = shared_qobject_ptr<Hasher>;
|
using Ptr = shared_qobject_ptr<Hasher>;
|
||||||
|
|
||||||
@ -21,6 +22,9 @@ class Hasher : public Task {
|
|||||||
QString getResult() const { return m_hash; };
|
QString getResult() const { return m_hash; };
|
||||||
QString getPath() const { return m_path; };
|
QString getPath() const { return m_path; };
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void resultsReady(QString hash);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString m_hash;
|
QString m_hash;
|
||||||
QString m_path;
|
QString m_path;
|
||||||
@ -48,6 +52,7 @@ class BlockedModHasher : public Hasher {
|
|||||||
|
|
||||||
QStringList getHashTypes();
|
QStringList getHashTypes();
|
||||||
bool useHashType(QString type);
|
bool useHashType(QString type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ModPlatform::ResourceProvider provider;
|
ModPlatform::ResourceProvider provider;
|
||||||
QString hash_type;
|
QString hash_type;
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
#include "NetworkResourceAPI.h"
|
#include "NetworkResourceAPI.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
@ -19,12 +20,12 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&
|
|||||||
|
|
||||||
auto search_url = search_url_optional.value();
|
auto search_url = search_url_optional.value();
|
||||||
|
|
||||||
auto response = new 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::Download::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{};
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||||
if (parse_error.error != QJsonParseError::NoError) {
|
if (parse_error.error != QJsonParseError::NoError) {
|
||||||
@ -40,27 +41,21 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&
|
|||||||
callbacks.on_succeed(doc);
|
callbacks.on_succeed(doc);
|
||||||
});
|
});
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::failed, [&netJob, callbacks](QString reason){
|
QObject::connect(netJob.get(), &NetJob::failed, [&netJob, callbacks](QString reason) {
|
||||||
int network_error_code = -1;
|
int network_error_code = -1;
|
||||||
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply)
|
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply)
|
||||||
network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
|
||||||
callbacks.on_fail(reason, network_error_code);
|
callbacks.on_fail(reason, network_error_code);
|
||||||
});
|
});
|
||||||
QObject::connect(netJob.get(), &NetJob::aborted, [callbacks]{
|
QObject::connect(netJob.get(), &NetJob::aborted, [callbacks] { callbacks.on_abort(); });
|
||||||
callbacks.on_abort();
|
|
||||||
});
|
|
||||||
QObject::connect(netJob.get(), &NetJob::finished, [response] {
|
|
||||||
delete response;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
|
||||||
Task::Ptr NetworkResourceAPI::getProjectInfo(ProjectInfoArgs&& args, ProjectInfoCallbacks&& callbacks) const
|
Task::Ptr NetworkResourceAPI::getProjectInfo(ProjectInfoArgs&& args, ProjectInfoCallbacks&& callbacks) const
|
||||||
{
|
{
|
||||||
auto response = new QByteArray();
|
auto response = std::make_shared<QByteArray>();
|
||||||
auto job = getProject(args.pack.addonId.toString(), response);
|
auto job = getProject(args.pack.addonId.toString(), response);
|
||||||
|
|
||||||
QObject::connect(job.get(), &NetJob::succeeded, [response, callbacks, args] {
|
QObject::connect(job.get(), &NetJob::succeeded, [response, callbacks, args] {
|
||||||
@ -88,7 +83,7 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi
|
|||||||
auto versions_url = versions_url_optional.value();
|
auto versions_url = versions_url_optional.value();
|
||||||
|
|
||||||
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 = new QByteArray();
|
auto response = std::make_shared<QByteArray>();
|
||||||
|
|
||||||
netJob->addNetAction(Net::Download::makeByteArray(versions_url, response));
|
netJob->addNetAction(Net::Download::makeByteArray(versions_url, response));
|
||||||
|
|
||||||
@ -105,14 +100,10 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi
|
|||||||
callbacks.on_succeed(doc, args.pack);
|
callbacks.on_succeed(doc, args.pack);
|
||||||
});
|
});
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::finished, [response] {
|
|
||||||
delete response;
|
|
||||||
});
|
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
|
||||||
Task::Ptr NetworkResourceAPI::getProject(QString addonId, QByteArray* response) const
|
Task::Ptr NetworkResourceAPI::getProject(QString addonId, std::shared_ptr<QByteArray> response) const
|
||||||
{
|
{
|
||||||
auto project_url_optional = getInfoURL(addonId);
|
auto project_url_optional = getInfoURL(addonId);
|
||||||
if (!project_url_optional.has_value())
|
if (!project_url_optional.has_value())
|
||||||
@ -124,9 +115,5 @@ Task::Ptr NetworkResourceAPI::getProject(QString addonId, QByteArray* response)
|
|||||||
|
|
||||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(project_url), response));
|
netJob->addNetAction(Net::Download::makeByteArray(QUrl(project_url), response));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::finished, [response] {
|
|
||||||
delete response;
|
|
||||||
});
|
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,14 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include "modplatform/ResourceAPI.h"
|
#include "modplatform/ResourceAPI.h"
|
||||||
|
|
||||||
class NetworkResourceAPI : public ResourceAPI {
|
class NetworkResourceAPI : public ResourceAPI {
|
||||||
public:
|
public:
|
||||||
Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const override;
|
Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const override;
|
||||||
|
|
||||||
Task::Ptr getProject(QString addonId, QByteArray* response) const override;
|
Task::Ptr getProject(QString addonId, std::shared_ptr<QByteArray> response) const override;
|
||||||
|
|
||||||
Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const override;
|
Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const override;
|
||||||
Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const override;
|
Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const override;
|
||||||
|
@ -51,11 +51,11 @@ 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::Download::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();
|
||||||
jobPtr->addNetAction(Net::Download::makeByteArray(thirdPartyUrl, &thirdPartyModpacksXmlFileData));
|
jobPtr->addNetAction(Net::Download::makeByteArray(thirdPartyUrl, thirdPartyModpacksXmlFileData));
|
||||||
|
|
||||||
QObject::connect(jobPtr.get(), &NetJob::succeeded, this, &PackFetchTask::fileDownloadFinished);
|
QObject::connect(jobPtr.get(), &NetJob::succeeded, this, &PackFetchTask::fileDownloadFinished);
|
||||||
QObject::connect(jobPtr.get(), &NetJob::failed, this, &PackFetchTask::fileDownloadFailed);
|
QObject::connect(jobPtr.get(), &NetJob::failed, this, &PackFetchTask::fileDownloadFailed);
|
||||||
@ -64,22 +64,19 @@ void PackFetchTask::fetch()
|
|||||||
jobPtr->start();
|
jobPtr->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PackFetchTask::fetchPrivate(const QStringList & toFetch)
|
void PackFetchTask::fetchPrivate(const QStringList& toFetch)
|
||||||
{
|
{
|
||||||
QString privatePackBaseUrl = BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/%1.xml";
|
QString privatePackBaseUrl = BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/%1.xml";
|
||||||
|
|
||||||
for (auto &packCode: toFetch)
|
for (auto& packCode : toFetch) {
|
||||||
{
|
auto data = std::make_shared<QByteArray>();
|
||||||
QByteArray *data = new 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::Download::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;
|
||||||
parseAndAddPacks(*data, PackType::Private, packs);
|
parseAndAddPacks(*data, PackType::Private, packs);
|
||||||
foreach(Modpack currentPack, packs)
|
foreach (Modpack currentPack, packs) {
|
||||||
{
|
|
||||||
currentPack.packCode = packCode;
|
currentPack.packCode = packCode;
|
||||||
emit privateFileDownloadFinished(currentPack);
|
emit privateFileDownloadFinished(currentPack);
|
||||||
}
|
}
|
||||||
@ -87,24 +84,20 @@ void PackFetchTask::fetchPrivate(const QStringList & toFetch)
|
|||||||
job->deleteLater();
|
job->deleteLater();
|
||||||
|
|
||||||
data->clear();
|
data->clear();
|
||||||
delete data;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
QObject::connect(job, &NetJob::failed, this, [this, job, packCode, data](QString reason)
|
QObject::connect(job, &NetJob::failed, this, [this, job, packCode, data](QString reason) {
|
||||||
{
|
|
||||||
emit privateFileDownloadFailed(reason, packCode);
|
emit privateFileDownloadFailed(reason, packCode);
|
||||||
job->deleteLater();
|
job->deleteLater();
|
||||||
|
|
||||||
data->clear();
|
data->clear();
|
||||||
delete data;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
QObject::connect(job, &NetJob::aborted, this, [this, job, data]{
|
QObject::connect(job, &NetJob::aborted, this, [this, job, data] {
|
||||||
emit aborted();
|
emit aborted();
|
||||||
job->deleteLater();
|
job->deleteLater();
|
||||||
|
|
||||||
data->clear();
|
data->clear();
|
||||||
delete data;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
job->start();
|
job->start();
|
||||||
@ -117,27 +110,22 @@ void PackFetchTask::fileDownloadFinished()
|
|||||||
|
|
||||||
QStringList failedLists;
|
QStringList failedLists;
|
||||||
|
|
||||||
if(!parseAndAddPacks(publicModpacksXmlFileData, PackType::Public, publicPacks))
|
if (!parseAndAddPacks(*publicModpacksXmlFileData, PackType::Public, publicPacks)) {
|
||||||
{
|
|
||||||
failedLists.append(tr("Public Packs"));
|
failedLists.append(tr("Public Packs"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!parseAndAddPacks(thirdPartyModpacksXmlFileData, PackType::ThirdParty, thirdPartyPacks))
|
if (!parseAndAddPacks(*thirdPartyModpacksXmlFileData, PackType::ThirdParty, thirdPartyPacks)) {
|
||||||
{
|
|
||||||
failedLists.append(tr("Third Party Packs"));
|
failedLists.append(tr("Third Party Packs"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(failedLists.size() > 0)
|
if (failedLists.size() > 0) {
|
||||||
{
|
|
||||||
emit failed(tr("Failed to download some pack lists: %1").arg(failedLists.join("\n- ")));
|
emit failed(tr("Failed to download some pack lists: %1").arg(failedLists.join("\n- ")));
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
emit finished(publicPacks, thirdPartyPacks);
|
emit finished(publicPacks, thirdPartyPacks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PackFetchTask::parseAndAddPacks(QByteArray &data, PackType packType, ModpackList &list)
|
bool PackFetchTask::parseAndAddPacks(QByteArray& data, PackType packType, ModpackList& list)
|
||||||
{
|
{
|
||||||
QDomDocument doc;
|
QDomDocument doc;
|
||||||
|
|
||||||
@ -145,8 +133,7 @@ bool PackFetchTask::parseAndAddPacks(QByteArray &data, PackType packType, Modpac
|
|||||||
int errorLine = -1;
|
int errorLine = -1;
|
||||||
int errorCol = -1;
|
int errorCol = -1;
|
||||||
|
|
||||||
if(!doc.setContent(data, false, &errorMsg, &errorLine, &errorCol))
|
if (!doc.setContent(data, false, &errorMsg, &errorLine, &errorCol)) {
|
||||||
{
|
|
||||||
auto fullErrMsg = QString("Failed to fetch modpack data: %1 %2:%3!").arg(errorMsg).arg(errorLine).arg(errorCol);
|
auto fullErrMsg = QString("Failed to fetch modpack data: %1 %2:%3!").arg(errorMsg).arg(errorLine).arg(errorCol);
|
||||||
qWarning() << fullErrMsg;
|
qWarning() << fullErrMsg;
|
||||||
data.clear();
|
data.clear();
|
||||||
@ -154,8 +141,7 @@ bool PackFetchTask::parseAndAddPacks(QByteArray &data, PackType packType, Modpac
|
|||||||
}
|
}
|
||||||
|
|
||||||
QDomNodeList nodes = doc.elementsByTagName("modpack");
|
QDomNodeList nodes = doc.elementsByTagName("modpack");
|
||||||
for(int i = 0; i < nodes.length(); i++)
|
for (int i = 0; i < nodes.length(); i++) {
|
||||||
{
|
|
||||||
QDomElement element = nodes.at(i).toElement();
|
QDomElement element = nodes.at(i).toElement();
|
||||||
|
|
||||||
Modpack modpack;
|
Modpack modpack;
|
||||||
@ -169,26 +155,20 @@ bool PackFetchTask::parseAndAddPacks(QByteArray &data, PackType packType, Modpac
|
|||||||
modpack.broken = false;
|
modpack.broken = false;
|
||||||
modpack.bugged = false;
|
modpack.bugged = false;
|
||||||
|
|
||||||
//remove empty if the xml is bugged
|
// remove empty if the xml is bugged
|
||||||
for(QString curr : modpack.oldVersions)
|
for (QString curr : modpack.oldVersions) {
|
||||||
{
|
if (curr.isNull() || curr.isEmpty()) {
|
||||||
if(curr.isNull() || curr.isEmpty())
|
|
||||||
{
|
|
||||||
modpack.oldVersions.removeAll(curr);
|
modpack.oldVersions.removeAll(curr);
|
||||||
modpack.bugged = true;
|
modpack.bugged = true;
|
||||||
qWarning() << "Removed some empty versions from" << modpack.name;
|
qWarning() << "Removed some empty versions from" << modpack.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(modpack.oldVersions.size() < 1)
|
if (modpack.oldVersions.size() < 1) {
|
||||||
{
|
if (!modpack.currentVersion.isNull() && !modpack.currentVersion.isEmpty()) {
|
||||||
if(!modpack.currentVersion.isNull() && !modpack.currentVersion.isEmpty())
|
|
||||||
{
|
|
||||||
modpack.oldVersions.append(modpack.currentVersion);
|
modpack.oldVersions.append(modpack.currentVersion);
|
||||||
qWarning() << "Added current version to oldVersions because oldVersions was empty! (" + modpack.name + ")";
|
qWarning() << "Added current version to oldVersions because oldVersions was empty! (" + modpack.name + ")";
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
modpack.broken = true;
|
modpack.broken = true;
|
||||||
qWarning() << "Broken pack:" << modpack.name << " => No valid version!";
|
qWarning() << "Broken pack:" << modpack.name << " => No valid version!";
|
||||||
}
|
}
|
||||||
@ -218,4 +198,4 @@ void PackFetchTask::fileDownloadAborted()
|
|||||||
emit aborted();
|
emit aborted();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace LegacyFTB
|
||||||
|
@ -1,41 +1,41 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "net/NetJob.h"
|
|
||||||
#include <QTemporaryDir>
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QTemporaryDir>
|
||||||
|
#include <memory>
|
||||||
#include "PackHelpers.h"
|
#include "PackHelpers.h"
|
||||||
|
#include "net/NetJob.h"
|
||||||
|
|
||||||
namespace LegacyFTB {
|
namespace LegacyFTB {
|
||||||
|
|
||||||
class PackFetchTask : public QObject {
|
class PackFetchTask : public QObject {
|
||||||
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PackFetchTask(shared_qobject_ptr<QNetworkAccessManager> network) : QObject(nullptr), m_network(network) {};
|
PackFetchTask(shared_qobject_ptr<QNetworkAccessManager> network) : QObject(nullptr), m_network(network){};
|
||||||
virtual ~PackFetchTask() = default;
|
virtual ~PackFetchTask() = default;
|
||||||
|
|
||||||
void fetch();
|
void fetch();
|
||||||
void fetchPrivate(const QStringList &toFetch);
|
void fetchPrivate(const QStringList& toFetch);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||||
NetJob::Ptr jobPtr;
|
NetJob::Ptr jobPtr;
|
||||||
|
|
||||||
QByteArray publicModpacksXmlFileData;
|
std::shared_ptr<QByteArray> publicModpacksXmlFileData = std::make_shared<QByteArray>();
|
||||||
QByteArray thirdPartyModpacksXmlFileData;
|
std::shared_ptr<QByteArray> thirdPartyModpacksXmlFileData = std::make_shared<QByteArray>();
|
||||||
|
|
||||||
bool parseAndAddPacks(QByteArray &data, PackType packType, ModpackList &list);
|
bool parseAndAddPacks(QByteArray& data, PackType packType, ModpackList& list);
|
||||||
ModpackList publicPacks;
|
ModpackList publicPacks;
|
||||||
ModpackList thirdPartyPacks;
|
ModpackList thirdPartyPacks;
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void fileDownloadFinished();
|
void fileDownloadFinished();
|
||||||
void fileDownloadFailed(QString reason);
|
void fileDownloadFailed(QString reason);
|
||||||
void fileDownloadAborted();
|
void fileDownloadAborted();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void finished(ModpackList publicPacks, ModpackList thirdPartyPacks);
|
void finished(ModpackList publicPacks, ModpackList thirdPartyPacks);
|
||||||
void failed(QString reason);
|
void failed(QString reason);
|
||||||
void aborted();
|
void aborted();
|
||||||
@ -44,4 +44,4 @@ signals:
|
|||||||
void privateFileDownloadFailed(QString reason, QString packCode);
|
void privateFileDownloadFailed(QString reason, QString packCode);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace LegacyFTB
|
||||||
|
@ -37,16 +37,16 @@
|
|||||||
|
|
||||||
#include <QtConcurrent>
|
#include <QtConcurrent>
|
||||||
|
|
||||||
#include "MMCZip.h"
|
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "settings/INISettingsObject.h"
|
#include "MMCZip.h"
|
||||||
|
#include "minecraft/GradleSpecifier.h"
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftInstance.h"
|
||||||
#include "minecraft/PackProfile.h"
|
#include "minecraft/PackProfile.h"
|
||||||
#include "minecraft/GradleSpecifier.h"
|
#include "settings/INISettingsObject.h"
|
||||||
|
|
||||||
#include "BuildConfig.h"
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "BuildConfig.h"
|
||||||
|
|
||||||
namespace LegacyFTB {
|
namespace LegacyFTB {
|
||||||
|
|
||||||
@ -65,6 +65,7 @@ void PackInstallTask::executeTask()
|
|||||||
void PackInstallTask::downloadPack()
|
void PackInstallTask::downloadPack()
|
||||||
{
|
{
|
||||||
setStatus(tr("Downloading zip for %1").arg(m_pack.name));
|
setStatus(tr("Downloading zip for %1").arg(m_pack.name));
|
||||||
|
setProgress(1, 4);
|
||||||
setAbortable(false);
|
setAbortable(false);
|
||||||
|
|
||||||
archivePath = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file);
|
archivePath = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file);
|
||||||
@ -78,11 +79,10 @@ void PackInstallTask::downloadPack()
|
|||||||
}
|
}
|
||||||
netJobContainer->addNetAction(Net::Download::makeFile(url, archivePath));
|
netJobContainer->addNetAction(Net::Download::makeFile(url, archivePath));
|
||||||
|
|
||||||
connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded);
|
connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip);
|
||||||
connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed);
|
connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed);
|
||||||
connect(netJobContainer.get(), &NetJob::progress, this, &PackInstallTask::onDownloadProgress);
|
|
||||||
connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
|
connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
|
||||||
connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::onDownloadAborted);
|
connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::emitAborted);
|
||||||
|
|
||||||
netJobContainer->start();
|
netJobContainer->start();
|
||||||
|
|
||||||
@ -90,27 +90,6 @@ void PackInstallTask::downloadPack()
|
|||||||
progress(1, 4);
|
progress(1, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PackInstallTask::onDownloadSucceeded()
|
|
||||||
{
|
|
||||||
unzip();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PackInstallTask::onDownloadFailed(QString reason)
|
|
||||||
{
|
|
||||||
emitFailed(reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PackInstallTask::onDownloadProgress(qint64 current, qint64 total)
|
|
||||||
{
|
|
||||||
progress(current, total * 4);
|
|
||||||
setStatus(tr("Downloading zip for %1 (%2%)").arg(m_pack.name).arg(current / 10));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PackInstallTask::onDownloadAborted()
|
|
||||||
{
|
|
||||||
emitAborted();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PackInstallTask::unzip()
|
void PackInstallTask::unzip()
|
||||||
{
|
{
|
||||||
setStatus(tr("Extracting modpack"));
|
setStatus(tr("Extracting modpack"));
|
||||||
@ -120,16 +99,17 @@ void PackInstallTask::unzip()
|
|||||||
QDir extractDir(m_stagingPath);
|
QDir extractDir(m_stagingPath);
|
||||||
|
|
||||||
m_packZip.reset(new QuaZip(archivePath));
|
m_packZip.reset(new QuaZip(archivePath));
|
||||||
if(!m_packZip->open(QuaZip::mdUnzip))
|
if (!m_packZip->open(QuaZip::mdUnzip)) {
|
||||||
{
|
|
||||||
emitFailed(tr("Failed to open modpack file %1!").arg(archivePath));
|
emitFailed(tr("Failed to open modpack file %1!").arg(archivePath));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload<QString, QString>::of(MMCZip::extractDir), archivePath, extractDir.absolutePath() + "/unzip");
|
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload<QString, QString>::of(MMCZip::extractDir), archivePath,
|
||||||
|
extractDir.absolutePath() + "/unzip");
|
||||||
#else
|
#else
|
||||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/unzip");
|
m_extractFuture =
|
||||||
|
QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/unzip");
|
||||||
#endif
|
#endif
|
||||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &PackInstallTask::onUnzipFinished);
|
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &PackInstallTask::onUnzipFinished);
|
||||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &PackInstallTask::onUnzipCanceled);
|
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &PackInstallTask::onUnzipCanceled);
|
||||||
@ -151,11 +131,9 @@ void PackInstallTask::install()
|
|||||||
setStatus(tr("Installing modpack"));
|
setStatus(tr("Installing modpack"));
|
||||||
progress(3, 4);
|
progress(3, 4);
|
||||||
QDir unzipMcDir(m_stagingPath + "/unzip/minecraft");
|
QDir unzipMcDir(m_stagingPath + "/unzip/minecraft");
|
||||||
if(unzipMcDir.exists())
|
if (unzipMcDir.exists()) {
|
||||||
{
|
// ok, found minecraft dir, move contents to instance dir
|
||||||
//ok, found minecraft dir, move contents to instance dir
|
if (!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/.minecraft")) {
|
||||||
if(!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/.minecraft"))
|
|
||||||
{
|
|
||||||
emitFailed(tr("Failed to move unzipped Minecraft!"));
|
emitFailed(tr("Failed to move unzipped Minecraft!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -172,23 +150,20 @@ void PackInstallTask::install()
|
|||||||
|
|
||||||
bool fallback = true;
|
bool fallback = true;
|
||||||
|
|
||||||
//handle different versions
|
// handle different versions
|
||||||
QFile packJson(m_stagingPath + "/.minecraft/pack.json");
|
QFile packJson(m_stagingPath + "/.minecraft/pack.json");
|
||||||
QDir jarmodDir = QDir(m_stagingPath + "/unzip/instMods");
|
QDir jarmodDir = QDir(m_stagingPath + "/unzip/instMods");
|
||||||
if(packJson.exists())
|
if (packJson.exists()) {
|
||||||
{
|
|
||||||
packJson.open(QIODevice::ReadOnly | QIODevice::Text);
|
packJson.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(packJson.readAll());
|
QJsonDocument doc = QJsonDocument::fromJson(packJson.readAll());
|
||||||
packJson.close();
|
packJson.close();
|
||||||
|
|
||||||
//we only care about the libs
|
// we only care about the libs
|
||||||
QJsonArray libs = doc.object().value("libraries").toArray();
|
QJsonArray libs = doc.object().value("libraries").toArray();
|
||||||
|
|
||||||
foreach (const QJsonValue &value, libs)
|
foreach (const QJsonValue& value, libs) {
|
||||||
{
|
|
||||||
QString nameValue = value.toObject().value("name").toString();
|
QString nameValue = value.toObject().value("name").toString();
|
||||||
if(!nameValue.startsWith("net.minecraftforge"))
|
if (!nameValue.startsWith("net.minecraftforge")) {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,16 +174,13 @@ void PackInstallTask::install()
|
|||||||
fallback = false;
|
fallback = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(jarmodDir.exists())
|
if (jarmodDir.exists()) {
|
||||||
{
|
|
||||||
qDebug() << "Found jarmods, installing...";
|
qDebug() << "Found jarmods, installing...";
|
||||||
|
|
||||||
QStringList jarmods;
|
QStringList jarmods;
|
||||||
for (auto info: jarmodDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
|
for (auto info : jarmodDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) {
|
||||||
{
|
|
||||||
qDebug() << "Jarmod:" << info.fileName();
|
qDebug() << "Jarmod:" << info.fileName();
|
||||||
jarmods.push_back(info.absoluteFilePath());
|
jarmods.push_back(info.absoluteFilePath());
|
||||||
}
|
}
|
||||||
@ -217,12 +189,11 @@ void PackInstallTask::install()
|
|||||||
fallback = false;
|
fallback = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//just nuke unzip directory, it s not needed anymore
|
// just nuke unzip directory, it s not needed anymore
|
||||||
FS::deletePath(m_stagingPath + "/unzip");
|
FS::deletePath(m_stagingPath + "/unzip");
|
||||||
|
|
||||||
if(fallback)
|
if (fallback) {
|
||||||
{
|
// TODO: Some fallback mechanism... or just keep failing!
|
||||||
//TODO: Some fallback mechanism... or just keep failing!
|
|
||||||
emitFailed(tr("No installation method found!"));
|
emitFailed(tr("No installation method found!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -232,8 +203,7 @@ void PackInstallTask::install()
|
|||||||
progress(4, 4);
|
progress(4, 4);
|
||||||
|
|
||||||
instance.setName(name());
|
instance.setName(name());
|
||||||
if(m_instIcon == "default")
|
if (m_instIcon == "default") {
|
||||||
{
|
|
||||||
m_instIcon = "ftb_logo";
|
m_instIcon = "ftb_logo";
|
||||||
}
|
}
|
||||||
instance.setIconKey(m_instIcon);
|
instance.setIconKey(m_instIcon);
|
||||||
@ -252,4 +222,4 @@ bool PackInstallTask::abort()
|
|||||||
return InstanceTask::abort();
|
return InstanceTask::abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace LegacyFTB
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "InstanceTask.h"
|
|
||||||
#include "net/NetJob.h"
|
|
||||||
#include <quazip/quazip.h>
|
#include <quazip/quazip.h>
|
||||||
#include <quazip/quazipdir.h>
|
#include <quazip/quazipdir.h>
|
||||||
|
#include "InstanceTask.h"
|
||||||
|
#include "PackHelpers.h"
|
||||||
#include "meta/Index.h"
|
#include "meta/Index.h"
|
||||||
#include "meta/Version.h"
|
#include "meta/Version.h"
|
||||||
#include "meta/VersionList.h"
|
#include "meta/VersionList.h"
|
||||||
#include "PackHelpers.h"
|
#include "net/NetJob.h"
|
||||||
|
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
|
|
||||||
@ -14,36 +14,31 @@
|
|||||||
|
|
||||||
namespace LegacyFTB {
|
namespace LegacyFTB {
|
||||||
|
|
||||||
class PackInstallTask : public InstanceTask
|
class PackInstallTask : public InstanceTask {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit PackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, Modpack pack, QString version);
|
explicit PackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, Modpack pack, QString version);
|
||||||
virtual ~PackInstallTask(){}
|
virtual ~PackInstallTask() {}
|
||||||
|
|
||||||
bool canAbort() const override { return true; }
|
bool canAbort() const override { return true; }
|
||||||
bool abort() override;
|
bool abort() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//! Entry point for tasks.
|
//! Entry point for tasks.
|
||||||
virtual void executeTask() override;
|
virtual void executeTask() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void downloadPack();
|
void downloadPack();
|
||||||
void unzip();
|
void unzip();
|
||||||
void install();
|
void install();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onDownloadSucceeded();
|
|
||||||
void onDownloadFailed(QString reason);
|
|
||||||
void onDownloadProgress(qint64 current, qint64 total);
|
|
||||||
void onDownloadAborted();
|
|
||||||
|
|
||||||
void onUnzipFinished();
|
void onUnzipFinished();
|
||||||
void onUnzipCanceled();
|
void onUnzipCanceled();
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||||
bool abortable = false;
|
bool abortable = false;
|
||||||
std::unique_ptr<QuaZip> m_packZip;
|
std::unique_ptr<QuaZip> m_packZip;
|
||||||
@ -56,4 +51,4 @@ private: /* data */
|
|||||||
QString m_version;
|
QString m_version;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace LegacyFTB
|
||||||
|
@ -9,19 +9,17 @@
|
|||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
#include "net/Upload.h"
|
#include "net/Upload.h"
|
||||||
|
|
||||||
Task::Ptr ModrinthAPI::currentVersion(QString hash, QString hash_format, QByteArray* response)
|
Task::Ptr ModrinthAPI::currentVersion(QString hash, QString hash_format, std::shared_ptr<QByteArray> response)
|
||||||
{
|
{
|
||||||
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::Download::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));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
|
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
|
||||||
Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_format, QByteArray* response)
|
Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_format, std::shared_ptr<QByteArray> response)
|
||||||
{
|
{
|
||||||
auto netJob = makeShared<NetJob>(QString("Modrinth::GetCurrentVersions"), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("Modrinth::GetCurrentVersions"), APPLICATION->network());
|
||||||
|
|
||||||
@ -35,8 +33,6 @@ Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_f
|
|||||||
|
|
||||||
netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), response, body_raw));
|
netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), response, body_raw));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
|
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +40,7 @@ Task::Ptr ModrinthAPI::latestVersion(QString hash,
|
|||||||
QString hash_format,
|
QString hash_format,
|
||||||
std::optional<std::list<Version>> mcVersions,
|
std::optional<std::list<Version>> mcVersions,
|
||||||
std::optional<ModLoaderTypes> loaders,
|
std::optional<ModLoaderTypes> loaders,
|
||||||
QByteArray* response)
|
std::shared_ptr<QByteArray> response)
|
||||||
{
|
{
|
||||||
auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersion"), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersion"), APPLICATION->network());
|
||||||
|
|
||||||
@ -67,8 +63,6 @@ Task::Ptr ModrinthAPI::latestVersion(QString hash,
|
|||||||
netJob->addNetAction(Net::Upload::makeByteArray(
|
netJob->addNetAction(Net::Upload::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));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
|
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +70,7 @@ Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes,
|
|||||||
QString hash_format,
|
QString hash_format,
|
||||||
std::optional<std::list<Version>> mcVersions,
|
std::optional<std::list<Version>> mcVersions,
|
||||||
std::optional<ModLoaderTypes> loaders,
|
std::optional<ModLoaderTypes> loaders,
|
||||||
QByteArray* response)
|
std::shared_ptr<QByteArray> response)
|
||||||
{
|
{
|
||||||
auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersions"), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersions"), APPLICATION->network());
|
||||||
|
|
||||||
@ -101,22 +95,16 @@ Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes,
|
|||||||
|
|
||||||
netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), response, body_raw));
|
netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), response, body_raw));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
|
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
|
||||||
Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, QByteArray* response) const
|
Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const
|
||||||
{
|
{
|
||||||
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::Download::makeByteArray(QUrl(searchUrl), response));
|
||||||
|
|
||||||
QObject::connect(netJob.get(), &NetJob::finished, [response, netJob] {
|
|
||||||
delete response;
|
|
||||||
});
|
|
||||||
|
|
||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,27 +12,23 @@
|
|||||||
|
|
||||||
class ModrinthAPI : public NetworkResourceAPI {
|
class ModrinthAPI : public NetworkResourceAPI {
|
||||||
public:
|
public:
|
||||||
auto currentVersion(QString hash,
|
auto currentVersion(QString hash, QString hash_format, std::shared_ptr<QByteArray> response) -> Task::Ptr;
|
||||||
QString hash_format,
|
|
||||||
QByteArray* response) -> Task::Ptr;
|
|
||||||
|
|
||||||
auto currentVersions(const QStringList& hashes,
|
auto currentVersions(const QStringList& hashes, QString hash_format, std::shared_ptr<QByteArray> response) -> Task::Ptr;
|
||||||
QString hash_format,
|
|
||||||
QByteArray* response) -> Task::Ptr;
|
|
||||||
|
|
||||||
auto latestVersion(QString hash,
|
auto latestVersion(QString hash,
|
||||||
QString hash_format,
|
QString hash_format,
|
||||||
std::optional<std::list<Version>> mcVersions,
|
std::optional<std::list<Version>> mcVersions,
|
||||||
std::optional<ModLoaderTypes> loaders,
|
std::optional<ModLoaderTypes> loaders,
|
||||||
QByteArray* response) -> Task::Ptr;
|
std::shared_ptr<QByteArray> response) -> Task::Ptr;
|
||||||
|
|
||||||
auto latestVersions(const QStringList& hashes,
|
auto latestVersions(const QStringList& hashes,
|
||||||
QString hash_format,
|
QString hash_format,
|
||||||
std::optional<std::list<Version>> mcVersions,
|
std::optional<std::list<Version>> mcVersions,
|
||||||
std::optional<ModLoaderTypes> loaders,
|
std::optional<ModLoaderTypes> loaders,
|
||||||
QByteArray* response) -> Task::Ptr;
|
std::shared_ptr<QByteArray> response) -> Task::Ptr;
|
||||||
|
|
||||||
Task::Ptr getProjects(QStringList addonIds, QByteArray* response) const override;
|
Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
||||||
@ -42,7 +38,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList
|
static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList
|
||||||
{
|
{
|
||||||
QStringList l;
|
QStringList l;
|
||||||
for (auto loader : {Forge, Fabric, Quilt}) {
|
for (auto loader : { Forge, Fabric, Quilt, LiteLoader }) {
|
||||||
if (types & loader) {
|
if (types & loader) {
|
||||||
l << getModLoaderString(loader);
|
l << getModLoaderString(loader);
|
||||||
}
|
}
|
||||||
@ -97,7 +93,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
{
|
{
|
||||||
if (args.loaders.has_value()) {
|
if (args.loaders.has_value()) {
|
||||||
if (!validateModLoaders(args.loaders.value())) {
|
if (!validateModLoaders(args.loaders.value())) {
|
||||||
qWarning() << "Modrinth only have Forge and Fabric-compatible mods!";
|
qWarning() << "Modrinth - or our interface - does not support any the provided mod loaders!";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,9 +142,6 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
return s.isEmpty() ? QString() : s;
|
return s.isEmpty() ? QString() : s;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto validateModLoaders(ModLoaderTypes loaders) const -> bool
|
static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt | LiteLoader); }
|
||||||
{
|
|
||||||
return loaders & (Forge | Fabric | Quilt);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -53,12 +53,11 @@ void ModrinthCheckUpdate::executeTask()
|
|||||||
// (though it will rarely happen, if at all)
|
// (though it will rarely happen, if at all)
|
||||||
if (mod->metadata()->hash_format != best_hash_type) {
|
if (mod->metadata()->hash_format != best_hash_type) {
|
||||||
auto hash_task = Hashing::createModrinthHasher(mod->fileinfo().absoluteFilePath());
|
auto hash_task = Hashing::createModrinthHasher(mod->fileinfo().absoluteFilePath());
|
||||||
connect(hash_task.get(), &Task::succeeded, [&] {
|
connect(hash_task.get(), &Hashing::Hasher::resultsReady, [&hashes, &mappings, mod](QString hash) {
|
||||||
QString hash(hash_task->getResult());
|
|
||||||
hashes.append(hash);
|
hashes.append(hash);
|
||||||
mappings.insert(hash, mod);
|
mappings.insert(hash, mod);
|
||||||
});
|
});
|
||||||
connect(hash_task.get(), &Task::failed, [this, hash_task] { failed("Failed to generate hash"); });
|
connect(hash_task.get(), &Task::failed, [this] { failed("Failed to generate hash"); });
|
||||||
hashing_task.addTask(hash_task);
|
hashing_task.addTask(hash_task);
|
||||||
} else {
|
} else {
|
||||||
hashes.append(hash);
|
hashes.append(hash);
|
||||||
@ -71,7 +70,7 @@ void ModrinthCheckUpdate::executeTask()
|
|||||||
hashing_task.start();
|
hashing_task.start();
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
|
||||||
auto* response = new QByteArray();
|
auto response = std::make_shared<QByteArray>();
|
||||||
auto job = api.latestVersions(hashes, best_hash_type, m_game_versions, m_loaders, response);
|
auto job = api.latestVersions(hashes, best_hash_type, m_game_versions, m_loaders, response);
|
||||||
|
|
||||||
QEventLoop lock;
|
QEventLoop lock;
|
||||||
|
@ -214,7 +214,7 @@ bool ModrinthCreationTask::createInstance()
|
|||||||
|
|
||||||
if (m_instIcon != "default") {
|
if (m_instIcon != "default") {
|
||||||
instance.setIconKey(m_instIcon);
|
instance.setIconKey(m_instIcon);
|
||||||
} else {
|
} else if (!m_managed_id.isEmpty()) {
|
||||||
instance.setIconKey("modrinth");
|
instance.setIconKey("modrinth");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,8 +134,8 @@ void ModrinthPackExportTask::collectHashes()
|
|||||||
QCryptographicHash sha1(QCryptographicHash::Algorithm::Sha1);
|
QCryptographicHash sha1(QCryptographicHash::Algorithm::Sha1);
|
||||||
sha1.addData(data);
|
sha1.addData(data);
|
||||||
|
|
||||||
ResolvedFile file{ sha1.result().toHex(), sha512.result().toHex(), url.toString(), openFile.size() };
|
ResolvedFile resolvedFile{ sha1.result().toHex(), sha512.result().toHex(), url.toEncoded(), openFile.size() };
|
||||||
resolvedFiles[relative] = file;
|
resolvedFiles[relative] = resolvedFile;
|
||||||
|
|
||||||
// nice! we've managed to resolve based on local metadata!
|
// nice! we've managed to resolve based on local metadata!
|
||||||
// no need to enqueue it
|
// no need to enqueue it
|
||||||
@ -157,7 +157,7 @@ void ModrinthPackExportTask::makeApiRequest()
|
|||||||
if (pendingHashes.isEmpty())
|
if (pendingHashes.isEmpty())
|
||||||
buildZip();
|
buildZip();
|
||||||
else {
|
else {
|
||||||
QByteArray* response = new QByteArray;
|
auto response = std::make_shared<QByteArray>();
|
||||||
task = api.currentVersions(pendingHashes.values(), "sha512", response);
|
task = api.currentVersions(pendingHashes.values(), "sha512", response);
|
||||||
connect(task.get(), &NetJob::succeeded, [this, response]() { parseApiResponse(response); });
|
connect(task.get(), &NetJob::succeeded, [this, response]() { parseApiResponse(response); });
|
||||||
connect(task.get(), &NetJob::failed, this, &ModrinthPackExportTask::emitFailed);
|
connect(task.get(), &NetJob::failed, this, &ModrinthPackExportTask::emitFailed);
|
||||||
@ -165,7 +165,7 @@ void ModrinthPackExportTask::makeApiRequest()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModrinthPackExportTask::parseApiResponse(const QByteArray* response)
|
void ModrinthPackExportTask::parseApiResponse(const std::shared_ptr<QByteArray> response)
|
||||||
{
|
{
|
||||||
task = nullptr;
|
task = nullptr;
|
||||||
|
|
||||||
@ -263,13 +263,13 @@ void ModrinthPackExportTask::finish()
|
|||||||
|
|
||||||
QByteArray ModrinthPackExportTask::generateIndex()
|
QByteArray ModrinthPackExportTask::generateIndex()
|
||||||
{
|
{
|
||||||
QJsonObject obj;
|
QJsonObject out;
|
||||||
obj["formatVersion"] = 1;
|
out["formatVersion"] = 1;
|
||||||
obj["game"] = "minecraft";
|
out["game"] = "minecraft";
|
||||||
obj["name"] = name;
|
out["name"] = name;
|
||||||
obj["versionId"] = version;
|
out["versionId"] = version;
|
||||||
if (!summary.isEmpty())
|
if (!summary.isEmpty())
|
||||||
obj["summary"] = summary;
|
out["summary"] = summary;
|
||||||
|
|
||||||
if (mcInstance) {
|
if (mcInstance) {
|
||||||
auto profile = mcInstance->getPackProfile();
|
auto profile = mcInstance->getPackProfile();
|
||||||
@ -290,30 +290,40 @@ QByteArray ModrinthPackExportTask::generateIndex()
|
|||||||
if (forge != nullptr)
|
if (forge != nullptr)
|
||||||
dependencies["forge"] = forge->m_version;
|
dependencies["forge"] = forge->m_version;
|
||||||
|
|
||||||
obj["dependencies"] = dependencies;
|
out["dependencies"] = dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonArray files;
|
QJsonArray filesOut;
|
||||||
QMapIterator<QString, ResolvedFile> iterator(resolvedFiles);
|
for (auto iterator = resolvedFiles.constBegin(); iterator != resolvedFiles.constEnd(); iterator++) {
|
||||||
while (iterator.hasNext()) {
|
QJsonObject fileOut;
|
||||||
iterator.next();
|
|
||||||
|
|
||||||
|
QString path = iterator.key();
|
||||||
const ResolvedFile& value = iterator.value();
|
const ResolvedFile& value = iterator.value();
|
||||||
|
|
||||||
QJsonObject file;
|
// detect disabled mod
|
||||||
file["path"] = iterator.key();
|
const QFileInfo pathInfo(path);
|
||||||
file["downloads"] = QJsonArray({ iterator.value().url });
|
if (pathInfo.suffix() == "disabled") {
|
||||||
|
// rename it
|
||||||
|
path = pathInfo.dir().filePath(pathInfo.completeBaseName());
|
||||||
|
// ...and make it optional
|
||||||
|
QJsonObject env;
|
||||||
|
env["client"] = "optional";
|
||||||
|
env["server"] = "optional";
|
||||||
|
fileOut["env"] = env;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileOut["path"] = path;
|
||||||
|
fileOut["downloads"] = QJsonArray{ iterator.value().url };
|
||||||
|
|
||||||
QJsonObject hashes;
|
QJsonObject hashes;
|
||||||
hashes["sha1"] = value.sha1;
|
hashes["sha1"] = value.sha1;
|
||||||
hashes["sha512"] = value.sha512;
|
hashes["sha512"] = value.sha512;
|
||||||
|
fileOut["hashes"] = hashes;
|
||||||
|
|
||||||
file["hashes"] = hashes;
|
fileOut["fileSize"] = value.size;
|
||||||
file["fileSize"] = value.size;
|
filesOut << fileOut;
|
||||||
|
|
||||||
files << file;
|
|
||||||
}
|
}
|
||||||
obj["files"] = files;
|
out["files"] = filesOut;
|
||||||
|
|
||||||
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
return QJsonDocument(out).toJson(QJsonDocument::Compact);
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ class ModrinthPackExportTask : public Task {
|
|||||||
void collectFiles();
|
void collectFiles();
|
||||||
void collectHashes();
|
void collectHashes();
|
||||||
void makeApiRequest();
|
void makeApiRequest();
|
||||||
void parseApiResponse(const QByteArray* response);
|
void parseApiResponse(const std::shared_ptr<QByteArray> response);
|
||||||
void buildZip();
|
void buildZip();
|
||||||
void finish();
|
void finish();
|
||||||
|
|
||||||
|
@ -37,20 +37,19 @@
|
|||||||
|
|
||||||
#include <FileSystem.h>
|
#include <FileSystem.h>
|
||||||
#include <Json.h>
|
#include <Json.h>
|
||||||
#include <QtConcurrentRun>
|
|
||||||
#include <MMCZip.h>
|
#include <MMCZip.h>
|
||||||
|
#include <QtConcurrentRun>
|
||||||
|
|
||||||
#include "TechnicPackProcessor.h"
|
|
||||||
#include "SolderPackManifest.h"
|
#include "SolderPackManifest.h"
|
||||||
|
#include "TechnicPackProcessor.h"
|
||||||
#include "net/ChecksumValidator.h"
|
#include "net/ChecksumValidator.h"
|
||||||
|
|
||||||
Technic::SolderPackInstallTask::SolderPackInstallTask(
|
Technic::SolderPackInstallTask::SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network,
|
||||||
shared_qobject_ptr<QNetworkAccessManager> network,
|
const QUrl& solderUrl,
|
||||||
const QUrl &solderUrl,
|
const QString& pack,
|
||||||
const QString &pack,
|
const QString& version,
|
||||||
const QString &version,
|
const QString& minecraftVersion)
|
||||||
const QString &minecraftVersion
|
{
|
||||||
) {
|
|
||||||
m_solderUrl = solderUrl;
|
m_solderUrl = solderUrl;
|
||||||
m_pack = pack;
|
m_pack = pack;
|
||||||
m_version = version;
|
m_version = version;
|
||||||
@ -58,9 +57,9 @@ Technic::SolderPackInstallTask::SolderPackInstallTask(
|
|||||||
m_minecraftVersion = minecraftVersion;
|
m_minecraftVersion = minecraftVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Technic::SolderPackInstallTask::abort() {
|
bool Technic::SolderPackInstallTask::abort()
|
||||||
if(m_abortable)
|
{
|
||||||
{
|
if (m_abortable) {
|
||||||
return m_filesNetJob->abort();
|
return m_filesNetJob->abort();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -72,7 +71,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::Download::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);
|
||||||
@ -85,11 +84,11 @@ void Technic::SolderPackInstallTask::fileListSucceeded()
|
|||||||
{
|
{
|
||||||
setStatus(tr("Downloading modpack"));
|
setStatus(tr("Downloading modpack"));
|
||||||
|
|
||||||
QJsonParseError parse_error {};
|
QJsonParseError parse_error{};
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(m_response, &parse_error);
|
QJsonDocument doc = QJsonDocument::fromJson(*m_response, &parse_error);
|
||||||
if (parse_error.error != QJsonParseError::NoError) {
|
if (parse_error.error != QJsonParseError::NoError) {
|
||||||
qWarning() << "Error while parsing JSON response from Solder at " << parse_error.offset << " reason: " << parse_error.errorString();
|
qWarning() << "Error while parsing JSON response from Solder at " << parse_error.offset << " reason: " << parse_error.errorString();
|
||||||
qWarning() << m_response;
|
qWarning() << *m_response;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto obj = doc.object();
|
auto obj = doc.object();
|
||||||
@ -110,7 +109,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded()
|
|||||||
m_filesNetJob.reset(new NetJob(tr("Downloading modpack"), m_network));
|
m_filesNetJob.reset(new NetJob(tr("Downloading modpack"), m_network));
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
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::Download::makeFile(mod.url, path);
|
||||||
|
@ -40,45 +40,48 @@
|
|||||||
#include <tasks/Task.h>
|
#include <tasks/Task.h>
|
||||||
|
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace Technic
|
namespace Technic {
|
||||||
{
|
class SolderPackInstallTask : public InstanceTask {
|
||||||
class SolderPackInstallTask : public InstanceTask
|
Q_OBJECT
|
||||||
{
|
public:
|
||||||
Q_OBJECT
|
explicit SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network,
|
||||||
public:
|
const QUrl& solderUrl,
|
||||||
explicit SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, const QUrl &solderUrl, const QString& pack, const QString& version, const QString &minecraftVersion);
|
const QString& pack,
|
||||||
|
const QString& version,
|
||||||
|
const QString& minecraftVersion);
|
||||||
|
|
||||||
bool canAbort() const override { return true; }
|
bool canAbort() const override { return true; }
|
||||||
bool abort() override;
|
bool abort() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//! Entry point for tasks.
|
//! Entry point for tasks.
|
||||||
virtual void executeTask() override;
|
virtual void executeTask() override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void fileListSucceeded();
|
void fileListSucceeded();
|
||||||
void downloadSucceeded();
|
void downloadSucceeded();
|
||||||
void downloadFailed(QString reason);
|
void downloadFailed(QString reason);
|
||||||
void downloadProgressChanged(qint64 current, qint64 total);
|
void downloadProgressChanged(qint64 current, qint64 total);
|
||||||
void downloadAborted();
|
void downloadAborted();
|
||||||
void extractFinished();
|
void extractFinished();
|
||||||
void extractAborted();
|
void extractAborted();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_abortable = false;
|
bool m_abortable = false;
|
||||||
|
|
||||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||||
|
|
||||||
NetJob::Ptr m_filesNetJob;
|
NetJob::Ptr m_filesNetJob;
|
||||||
QUrl m_solderUrl;
|
QUrl m_solderUrl;
|
||||||
QString m_pack;
|
QString m_pack;
|
||||||
QString m_version;
|
QString m_version;
|
||||||
QString m_minecraftVersion;
|
QString m_minecraftVersion;
|
||||||
QByteArray m_response;
|
std::shared_ptr<QByteArray> m_response = std::make_shared<QByteArray>();
|
||||||
QTemporaryDir m_outputDir;
|
QTemporaryDir m_outputDir;
|
||||||
int m_modCount;
|
int m_modCount;
|
||||||
QFuture<bool> m_extractFuture;
|
QFuture<bool> m_extractFuture;
|
||||||
QFutureWatcher<bool> m_extractFutureWatcher;
|
QFutureWatcher<bool> m_extractFutureWatcher;
|
||||||
};
|
};
|
||||||
}
|
} // namespace Technic
|
||||||
|
@ -1,427 +0,0 @@
|
|||||||
#include "PackageManifest.h"
|
|
||||||
#include <Json.h>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QDirIterator>
|
|
||||||
#include <QCryptographicHash>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace mojang_files {
|
|
||||||
|
|
||||||
const Hash hash_of_empty_string = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
|
|
||||||
|
|
||||||
int Path::compare(const Path& rhs) const
|
|
||||||
{
|
|
||||||
auto left_cursor = begin();
|
|
||||||
auto left_end = end();
|
|
||||||
auto right_cursor = rhs.begin();
|
|
||||||
auto right_end = rhs.end();
|
|
||||||
|
|
||||||
while (left_cursor != left_end && right_cursor != right_end)
|
|
||||||
{
|
|
||||||
if(*left_cursor < *right_cursor)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else if(*left_cursor > *right_cursor)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
left_cursor++;
|
|
||||||
right_cursor++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(left_cursor == left_end)
|
|
||||||
{
|
|
||||||
if(right_cursor == right_end)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Package::addFile(const Path& path, const File& file) {
|
|
||||||
addFolder(path.parent_path());
|
|
||||||
files[path] = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Package::addFolder(Path folder) {
|
|
||||||
if(!folder.has_parent_path()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
folders.insert(folder);
|
|
||||||
folder = folder.parent_path();
|
|
||||||
} while(folder.has_parent_path());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Package::addLink(const Path& path, const Path& target) {
|
|
||||||
addFolder(path.parent_path());
|
|
||||||
symlinks[path] = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Package::addSource(const FileSource& source) {
|
|
||||||
sources[source.hash] = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
void fromJson(QJsonDocument & doc, Package & out) {
|
|
||||||
std::set<Path> seen_paths;
|
|
||||||
if (!doc.isObject())
|
|
||||||
{
|
|
||||||
throw JSONValidationError("file manifest is not an object");
|
|
||||||
}
|
|
||||||
QJsonObject root = doc.object();
|
|
||||||
|
|
||||||
auto filesObj = Json::ensureObject(root, "files");
|
|
||||||
auto iter = filesObj.begin();
|
|
||||||
while (iter != filesObj.end())
|
|
||||||
{
|
|
||||||
Path objectPath = Path(iter.key());
|
|
||||||
auto value = iter.value();
|
|
||||||
iter++;
|
|
||||||
if(seen_paths.count(objectPath)) {
|
|
||||||
throw JSONValidationError("duplicate path inside manifest, the manifest is invalid");
|
|
||||||
}
|
|
||||||
if (!value.isObject())
|
|
||||||
{
|
|
||||||
throw JSONValidationError("file entry inside manifest is not an an object");
|
|
||||||
}
|
|
||||||
seen_paths.insert(objectPath);
|
|
||||||
|
|
||||||
auto fileObject = value.toObject();
|
|
||||||
auto type = Json::requireString(fileObject, "type");
|
|
||||||
if(type == "directory") {
|
|
||||||
out.addFolder(objectPath);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if(type == "file") {
|
|
||||||
FileSource bestSource;
|
|
||||||
File file;
|
|
||||||
file.executable = Json::ensureBoolean(fileObject, QString("executable"), false);
|
|
||||||
auto downloads = Json::requireObject(fileObject, "downloads");
|
|
||||||
for(auto iter2 = downloads.begin(); iter2 != downloads.end(); iter2++) {
|
|
||||||
FileSource source;
|
|
||||||
|
|
||||||
auto downloadObject = Json::requireObject(iter2.value());
|
|
||||||
source.hash = Json::requireString(downloadObject, "sha1");
|
|
||||||
source.size = Json::requireInteger(downloadObject, "size");
|
|
||||||
source.url = Json::requireString(downloadObject, "url");
|
|
||||||
|
|
||||||
auto compression = iter2.key();
|
|
||||||
if(compression == "raw") {
|
|
||||||
file.hash = source.hash;
|
|
||||||
file.size = source.size;
|
|
||||||
source.compression = Compression::Raw;
|
|
||||||
}
|
|
||||||
else if (compression == "lzma") {
|
|
||||||
source.compression = Compression::Lzma;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
bestSource.upgrade(source);
|
|
||||||
}
|
|
||||||
if(bestSource.isBad()) {
|
|
||||||
throw JSONValidationError("No valid compression method for file " + iter.key());
|
|
||||||
}
|
|
||||||
out.addFile(objectPath, file);
|
|
||||||
out.addSource(bestSource);
|
|
||||||
}
|
|
||||||
else if(type == "link") {
|
|
||||||
auto target = Json::requireString(fileObject, "target");
|
|
||||||
out.symlinks[objectPath] = target;
|
|
||||||
out.addLink(objectPath, target);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw JSONValidationError("Invalid item type in manifest: " + type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// make sure the containing folder exists
|
|
||||||
out.folders.insert(Path());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Package Package::fromManifestContents(const QByteArray& contents)
|
|
||||||
{
|
|
||||||
Package out;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto doc = Json::requireDocument(contents, "Manifest");
|
|
||||||
fromJson(doc, out);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
catch (const Exception &e)
|
|
||||||
{
|
|
||||||
qDebug() << QString("Unable to parse manifest: %1").arg(e.cause());
|
|
||||||
out.valid = false;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Package Package::fromManifestFile(const QString & filename) {
|
|
||||||
Package out;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto doc = Json::requireDocument(filename, filename);
|
|
||||||
fromJson(doc, out);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
catch (const Exception &e)
|
|
||||||
{
|
|
||||||
qDebug() << QString("Unable to parse manifest file %1: %2").arg(filename, e.cause());
|
|
||||||
out.valid = false;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
// FIXME: Qt obscures symlink targets by making them absolute. that is useless. this is the workaround - we do it ourselves
|
|
||||||
bool actually_read_symlink_target(const QString & filepath, Path & out)
|
|
||||||
{
|
|
||||||
struct ::stat st;
|
|
||||||
// FIXME: here, we assume the native filesystem encoding. May the Gods have mercy upon our Souls.
|
|
||||||
QByteArray nativePath = filepath.toUtf8();
|
|
||||||
const char * filepath_cstr = nativePath.data();
|
|
||||||
|
|
||||||
if (lstat(filepath_cstr, &st) != 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto size = st.st_size ? st.st_size + 1 : PATH_MAX;
|
|
||||||
std::string temp(size, '\0');
|
|
||||||
// because we don't realiably know how long the damn thing actually is, we loop and expand. POSIX is naff
|
|
||||||
do
|
|
||||||
{
|
|
||||||
auto link_length = ::readlink(filepath_cstr, &temp[0], temp.size());
|
|
||||||
if(link_length == -1)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(std::string::size_type(link_length) < temp.size())
|
|
||||||
{
|
|
||||||
// buffer was long enough and we managed to read the link target. RETURN here.
|
|
||||||
temp.resize(link_length);
|
|
||||||
out = Path(QString::fromUtf8(temp.c_str()));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
temp.resize(temp.size() * 2);
|
|
||||||
} while (true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// FIXME: Qt filesystem abstraction is bad, but ... let's hope it doesn't break too much?
|
|
||||||
// FIXME: The error handling is just DEFICIENT
|
|
||||||
Package Package::fromInspectedFolder(const QString& folderPath)
|
|
||||||
{
|
|
||||||
QDir root(folderPath);
|
|
||||||
|
|
||||||
Package out;
|
|
||||||
QDirIterator iterator(folderPath, QDir::NoDotAndDotDot | QDir::AllEntries | QDir::System | QDir::Hidden, QDirIterator::Subdirectories);
|
|
||||||
while(iterator.hasNext()) {
|
|
||||||
iterator.next();
|
|
||||||
|
|
||||||
auto fileInfo = iterator.fileInfo();
|
|
||||||
auto relPath = root.relativeFilePath(fileInfo.filePath());
|
|
||||||
// FIXME: this is probably completely busted on Windows anyway, so just disable it.
|
|
||||||
// Qt makes shit up and doesn't understand the platform details
|
|
||||||
// TODO: Actually use a filesystem library that isn't terrible and has decen license.
|
|
||||||
// I only know one, and I wrote it. Sadly, currently proprietary. PAIN.
|
|
||||||
#ifndef Q_OS_WIN32
|
|
||||||
if(fileInfo.isSymLink()) {
|
|
||||||
Path targetPath;
|
|
||||||
if(!actually_read_symlink_target(fileInfo.filePath(), targetPath)) {
|
|
||||||
qCritical() << "Folder inspection: Unknown filesystem object:" << fileInfo.absoluteFilePath();
|
|
||||||
out.valid = false;
|
|
||||||
}
|
|
||||||
out.addLink(relPath, targetPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
if(fileInfo.isDir()) {
|
|
||||||
out.addFolder(relPath);
|
|
||||||
}
|
|
||||||
else if(fileInfo.isFile()) {
|
|
||||||
File f;
|
|
||||||
f.executable = fileInfo.isExecutable();
|
|
||||||
f.size = fileInfo.size();
|
|
||||||
// FIXME: async / optimize the hashing
|
|
||||||
QFile input(fileInfo.absoluteFilePath());
|
|
||||||
if(!input.open(QIODevice::ReadOnly)) {
|
|
||||||
qCritical() << "Folder inspection: Failed to open file:" << fileInfo.absoluteFilePath();
|
|
||||||
out.valid = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
f.hash = QCryptographicHash::hash(input.readAll(), QCryptographicHash::Sha1).toHex().constData();
|
|
||||||
out.addFile(relPath, f);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Something else... oh my
|
|
||||||
qCritical() << "Folder inspection: Unknown filesystem object:" << fileInfo.absoluteFilePath();
|
|
||||||
out.valid = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.folders.insert(Path("."));
|
|
||||||
out.valid = true;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
struct shallow_first_sort
|
|
||||||
{
|
|
||||||
bool operator()(const Path &lhs, const Path &rhs) const
|
|
||||||
{
|
|
||||||
auto lhs_depth = lhs.length();
|
|
||||||
auto rhs_depth = rhs.length();
|
|
||||||
if(lhs_depth < rhs_depth)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if(lhs_depth == rhs_depth)
|
|
||||||
{
|
|
||||||
if(lhs < rhs)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct deep_first_sort
|
|
||||||
{
|
|
||||||
bool operator()(const Path &lhs, const Path &rhs) const
|
|
||||||
{
|
|
||||||
auto lhs_depth = lhs.length();
|
|
||||||
auto rhs_depth = rhs.length();
|
|
||||||
if(lhs_depth > rhs_depth)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if(lhs_depth == rhs_depth)
|
|
||||||
{
|
|
||||||
if(lhs < rhs)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateOperations UpdateOperations::resolve(const Package& from, const Package& to)
|
|
||||||
{
|
|
||||||
UpdateOperations out;
|
|
||||||
|
|
||||||
if(!from.valid || !to.valid) {
|
|
||||||
out.valid = false;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Files
|
|
||||||
for(auto iter = from.files.begin(); iter != from.files.end(); iter++) {
|
|
||||||
const auto ¤t_hash = iter->second.hash;
|
|
||||||
const auto ¤t_executable = iter->second.executable;
|
|
||||||
const auto &path = iter->first;
|
|
||||||
|
|
||||||
auto iter2 = to.files.find(path);
|
|
||||||
if(iter2 == to.files.end()) {
|
|
||||||
// removed
|
|
||||||
out.deletes.push_back(path);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto new_hash = iter2->second.hash;
|
|
||||||
auto new_executable = iter2->second.executable;
|
|
||||||
if (current_hash != new_hash) {
|
|
||||||
out.deletes.push_back(path);
|
|
||||||
out.downloads.emplace(
|
|
||||||
std::pair<Path, FileDownload>{
|
|
||||||
path,
|
|
||||||
FileDownload(to.sources.at(iter2->second.hash), iter2->second.executable)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if (current_executable != new_executable) {
|
|
||||||
out.executable_fixes[path] = new_executable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(auto iter = to.files.begin(); iter != to.files.end(); iter++) {
|
|
||||||
auto path = iter->first;
|
|
||||||
if(!from.files.count(path)) {
|
|
||||||
out.downloads.emplace(
|
|
||||||
std::pair<Path, FileDownload>{
|
|
||||||
path,
|
|
||||||
FileDownload(to.sources.at(iter->second.hash), iter->second.executable)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Folders
|
|
||||||
std::set<Path, deep_first_sort> remove_folders;
|
|
||||||
std::set<Path, shallow_first_sort> make_folders;
|
|
||||||
for(auto from_path: from.folders) {
|
|
||||||
auto iter = to.folders.find(from_path);
|
|
||||||
if(iter == to.folders.end()) {
|
|
||||||
remove_folders.insert(from_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(auto & rmdir: remove_folders) {
|
|
||||||
out.rmdirs.push_back(rmdir);
|
|
||||||
}
|
|
||||||
for(auto to_path: to.folders) {
|
|
||||||
auto iter = from.folders.find(to_path);
|
|
||||||
if(iter == from.folders.end()) {
|
|
||||||
make_folders.insert(to_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(auto & mkdir: make_folders) {
|
|
||||||
out.mkdirs.push_back(mkdir);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Symlinks
|
|
||||||
for(auto iter = from.symlinks.begin(); iter != from.symlinks.end(); iter++) {
|
|
||||||
const auto ¤t_target = iter->second;
|
|
||||||
const auto &path = iter->first;
|
|
||||||
|
|
||||||
auto iter2 = to.symlinks.find(path);
|
|
||||||
if(iter2 == to.symlinks.end()) {
|
|
||||||
// removed
|
|
||||||
out.deletes.push_back(path);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const auto &new_target = iter2->second;
|
|
||||||
if (current_target != new_target) {
|
|
||||||
out.deletes.push_back(path);
|
|
||||||
out.mklinks[path] = iter2->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(auto iter = to.symlinks.begin(); iter != to.symlinks.end(); iter++) {
|
|
||||||
auto path = iter->first;
|
|
||||||
if(!from.symlinks.count(path)) {
|
|
||||||
out.mklinks[path] = iter->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.valid = true;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,171 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <QStringList>
|
|
||||||
#include "tasks/Task.h"
|
|
||||||
|
|
||||||
namespace mojang_files {
|
|
||||||
|
|
||||||
using Hash = QString;
|
|
||||||
extern const Hash empty_hash;
|
|
||||||
|
|
||||||
// simple-ish path implementation. assumes always relative and does not allow '..' entries
|
|
||||||
class Path
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using parts_type = QStringList;
|
|
||||||
|
|
||||||
Path() = default;
|
|
||||||
Path(QString string) {
|
|
||||||
auto parts_in = string.split('/');
|
|
||||||
for(auto & part: parts_in) {
|
|
||||||
if(part.isEmpty() || part == ".") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(part == "..") {
|
|
||||||
if(parts.size()) {
|
|
||||||
parts.pop_back();
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
parts.push_back(part);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool has_parent_path() const
|
|
||||||
{
|
|
||||||
return parts.size() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Path parent_path() const
|
|
||||||
{
|
|
||||||
if (parts.empty())
|
|
||||||
return Path();
|
|
||||||
return Path(parts.begin(), std::prev(parts.end()));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool empty() const
|
|
||||||
{
|
|
||||||
return parts.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
int length() const
|
|
||||||
{
|
|
||||||
return parts.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const Path & rhs) const {
|
|
||||||
return parts == rhs.parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const Path & rhs) const {
|
|
||||||
return parts != rhs.parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator<(const Path& rhs) const
|
|
||||||
{
|
|
||||||
return compare(rhs) < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
parts_type::const_iterator begin() const
|
|
||||||
{
|
|
||||||
return parts.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
parts_type::const_iterator end() const
|
|
||||||
{
|
|
||||||
return parts.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString toString() const {
|
|
||||||
return parts.join("/");
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Path(const parts_type::const_iterator & start, const parts_type::const_iterator & end) {
|
|
||||||
auto cursor = start;
|
|
||||||
while(cursor != end) {
|
|
||||||
parts.push_back(*cursor);
|
|
||||||
cursor++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int compare(const Path& p) const;
|
|
||||||
|
|
||||||
parts_type parts;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
enum class Compression {
|
|
||||||
Raw,
|
|
||||||
Lzma,
|
|
||||||
Unknown
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct FileSource
|
|
||||||
{
|
|
||||||
Compression compression = Compression::Unknown;
|
|
||||||
Hash hash;
|
|
||||||
QString url;
|
|
||||||
std::size_t size = 0;
|
|
||||||
void upgrade(const FileSource & other) {
|
|
||||||
if(compression == Compression::Unknown || other.size < size) {
|
|
||||||
*this = other;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool isBad() const {
|
|
||||||
return compression == Compression::Unknown;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct File
|
|
||||||
{
|
|
||||||
Hash hash;
|
|
||||||
bool executable;
|
|
||||||
std::uint64_t size = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Package {
|
|
||||||
static Package fromInspectedFolder(const QString &folderPath);
|
|
||||||
static Package fromManifestFile(const QString &path);
|
|
||||||
static Package fromManifestContents(const QByteArray& contents);
|
|
||||||
|
|
||||||
explicit operator bool() const
|
|
||||||
{
|
|
||||||
return valid;
|
|
||||||
}
|
|
||||||
void addFolder(Path folder);
|
|
||||||
void addFile(const Path & path, const File & file);
|
|
||||||
void addLink(const Path & path, const Path & target);
|
|
||||||
void addSource(const FileSource & source);
|
|
||||||
|
|
||||||
std::map<Hash, FileSource> sources;
|
|
||||||
bool valid = true;
|
|
||||||
std::set<Path> folders;
|
|
||||||
std::map<Path, File> files;
|
|
||||||
std::map<Path, Path> symlinks;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FileDownload : FileSource
|
|
||||||
{
|
|
||||||
FileDownload(const FileSource& source, bool executable) {
|
|
||||||
static_cast<FileSource &> (*this) = source;
|
|
||||||
this->executable = executable;
|
|
||||||
}
|
|
||||||
bool executable = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UpdateOperations {
|
|
||||||
static UpdateOperations resolve(const Package & from, const Package & to);
|
|
||||||
bool valid = false;
|
|
||||||
std::vector<Path> deletes;
|
|
||||||
std::vector<Path> rmdirs;
|
|
||||||
std::vector<Path> mkdirs;
|
|
||||||
std::map<Path, FileDownload> downloads;
|
|
||||||
std::map<Path, Path> mklinks;
|
|
||||||
std::map<Path, bool> executable_fixes;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -1,7 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||||
|
* 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
|
||||||
@ -46,7 +47,7 @@ namespace Net {
|
|||||||
*/
|
*/
|
||||||
class ByteArraySink : public Sink {
|
class ByteArraySink : public Sink {
|
||||||
public:
|
public:
|
||||||
ByteArraySink(QByteArray* output) : m_output(output){};
|
ByteArraySink(std::shared_ptr<QByteArray> output) : m_output(output){};
|
||||||
|
|
||||||
virtual ~ByteArraySink() = default;
|
virtual ~ByteArraySink() = default;
|
||||||
|
|
||||||
@ -93,6 +94,6 @@ class ByteArraySink : public Sink {
|
|||||||
auto hasLocalData() -> bool override { return false; }
|
auto hasLocalData() -> bool override { return false; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QByteArray* m_output;
|
std::shared_ptr<QByteArray> m_output;
|
||||||
};
|
};
|
||||||
} // namespace Net
|
} // namespace Net
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "ByteArraySink.h"
|
#include "ByteArraySink.h"
|
||||||
#include "ChecksumValidator.h"
|
#include "ChecksumValidator.h"
|
||||||
@ -69,7 +70,7 @@ auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Down
|
|||||||
return dl;
|
return dl;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Download::makeByteArray(QUrl url, QByteArray* output, Options options) -> Download::Ptr
|
auto Download::makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Options options) -> Download::Ptr
|
||||||
{
|
{
|
||||||
auto dl = makeShared<Download>();
|
auto dl = makeShared<Download>();
|
||||||
dl->m_url = url;
|
dl->m_url = url;
|
||||||
|
@ -60,7 +60,7 @@ class Download : public NetAction {
|
|||||||
~Download() override = default;
|
~Download() override = default;
|
||||||
|
|
||||||
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;
|
||||||
static auto makeByteArray(QUrl url, 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:
|
public:
|
||||||
|
@ -39,218 +39,226 @@
|
|||||||
#include "Upload.h"
|
#include "Upload.h"
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include "ByteArraySink.h"
|
|
||||||
#include "BuildConfig.h"
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "BuildConfig.h"
|
||||||
|
#include "ByteArraySink.h"
|
||||||
|
|
||||||
#include "net/Logging.h"
|
#include "net/Logging.h"
|
||||||
|
|
||||||
namespace Net {
|
namespace Net {
|
||||||
|
|
||||||
bool Upload::abort()
|
bool Upload::abort()
|
||||||
{
|
{
|
||||||
if (m_reply) {
|
if (m_reply) {
|
||||||
m_reply->abort();
|
m_reply->abort();
|
||||||
} else {
|
} else {
|
||||||
m_state = State::AbortedByUser;
|
m_state = State::AbortedByUser;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Upload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
||||||
|
{
|
||||||
|
setProgress(bytesReceived, bytesTotal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Upload::downloadError(QNetworkReply::NetworkError error)
|
||||||
|
{
|
||||||
|
if (error == QNetworkReply::OperationCanceledError) {
|
||||||
|
qCCritical(taskUploadLogC) << getUid().toString() << "Aborted " << m_url.toString();
|
||||||
|
m_state = State::AbortedByUser;
|
||||||
|
} else {
|
||||||
|
// error happened during download.
|
||||||
|
qCCritical(taskUploadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error;
|
||||||
|
m_state = State::Failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Upload::sslErrors(const QList<QSslError>& errors)
|
||||||
|
{
|
||||||
|
int i = 1;
|
||||||
|
for (const auto& error : errors) {
|
||||||
|
qCCritical(taskUploadLogC) << getUid().toString() << "Upload" << m_url.toString() << "SSL Error #" << i << " : "
|
||||||
|
<< error.errorString();
|
||||||
|
auto cert = error.certificate();
|
||||||
|
qCCritical(taskUploadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Upload::handleRedirect()
|
||||||
|
{
|
||||||
|
QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl();
|
||||||
|
if (!redirect.isValid()) {
|
||||||
|
if (!m_reply->hasRawHeader("Location")) {
|
||||||
|
// no redirect -> it's fine to continue
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
// 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) {
|
||||||
void Upload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
// empty, yet present redirect header? WTF?
|
||||||
setProgress(bytesReceived, bytesTotal);
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
void Upload::downloadError(QNetworkReply::NetworkError error) {
|
|
||||||
if (error == QNetworkReply::OperationCanceledError) {
|
|
||||||
qCCritical(taskUploadLogC) << getUid().toString() << "Aborted " << m_url.toString();
|
|
||||||
m_state = State::AbortedByUser;
|
|
||||||
} else {
|
|
||||||
// error happened during download.
|
|
||||||
qCCritical(taskUploadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error;
|
|
||||||
m_state = State::Failed;
|
|
||||||
}
|
}
|
||||||
}
|
QString redirectStr = QString::fromUtf8(redirectBA);
|
||||||
|
|
||||||
void Upload::sslErrors(const QList<QSslError> &errors) {
|
|
||||||
int i = 1;
|
|
||||||
for (const auto& error : errors) {
|
|
||||||
qCCritical(taskUploadLogC) << getUid().toString() << "Upload" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString();
|
|
||||||
auto cert = error.certificate();
|
|
||||||
qCCritical(taskUploadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText();
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Upload::handleRedirect()
|
|
||||||
{
|
|
||||||
QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl();
|
|
||||||
if (!redirect.isValid()) {
|
|
||||||
if (!m_reply->hasRawHeader("Location")) {
|
|
||||||
// no redirect -> it's fine to continue
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// there is a Location header, but it's not correct. we need to apply some workarounds...
|
|
||||||
QByteArray redirectBA = m_reply->rawHeader("Location");
|
|
||||||
if (redirectBA.size() == 0) {
|
|
||||||
// empty, yet present redirect header? WTF?
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
QString redirectStr = QString::fromUtf8(redirectBA);
|
|
||||||
|
|
||||||
if (redirectStr.startsWith("//")) {
|
|
||||||
/*
|
|
||||||
* IF the URL begins with //, we need to insert the URL scheme.
|
|
||||||
* See: https://bugreports.qt.io/browse/QTBUG-41061
|
|
||||||
* See: http://tools.ietf.org/html/rfc3986#section-4.2
|
|
||||||
*/
|
|
||||||
redirectStr = m_reply->url().scheme() + ":" + redirectStr;
|
|
||||||
} else if (redirectStr.startsWith("/")) {
|
|
||||||
/*
|
|
||||||
* IF the URL begins with /, we need to process it as a relative URL
|
|
||||||
*/
|
|
||||||
auto url = m_reply->url();
|
|
||||||
url.setPath(redirectStr, QUrl::TolerantMode);
|
|
||||||
redirectStr = url.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (redirectStr.startsWith("//")) {
|
||||||
/*
|
/*
|
||||||
* Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues.
|
* IF the URL begins with //, we need to insert the URL scheme.
|
||||||
* FIXME: report Qt bug for this
|
* See: https://bugreports.qt.io/browse/QTBUG-41061
|
||||||
|
* See: http://tools.ietf.org/html/rfc3986#section-4.2
|
||||||
*/
|
*/
|
||||||
redirect = QUrl(redirectStr, QUrl::TolerantMode);
|
redirectStr = m_reply->url().scheme() + ":" + redirectStr;
|
||||||
if (!redirect.isValid()) {
|
} else if (redirectStr.startsWith("/")) {
|
||||||
qCWarning(taskUploadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr;
|
/*
|
||||||
downloadError(QNetworkReply::ProtocolFailure);
|
* IF the URL begins with /, we need to process it as a relative URL
|
||||||
return false;
|
*/
|
||||||
}
|
auto url = m_reply->url();
|
||||||
qCDebug(taskUploadLogC) << getUid().toString() << "Fixed location header:" << redirect;
|
url.setPath(redirectStr, QUrl::TolerantMode);
|
||||||
} else {
|
redirectStr = url.toString();
|
||||||
qCDebug(taskUploadLogC) << getUid().toString() << "Location header:" << redirect;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_url = QUrl(redirect.toString());
|
/*
|
||||||
qCDebug(taskUploadLogC) << getUid().toString() << "Following redirect to " << m_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.
|
||||||
startAction(m_network);
|
* FIXME: report Qt bug for this
|
||||||
return true;
|
*/
|
||||||
|
redirect = QUrl(redirectStr, QUrl::TolerantMode);
|
||||||
|
if (!redirect.isValid()) {
|
||||||
|
qCWarning(taskUploadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr;
|
||||||
|
downloadError(QNetworkReply::ProtocolFailure);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
qCDebug(taskUploadLogC) << getUid().toString() << "Fixed location header:" << redirect;
|
||||||
|
} else {
|
||||||
|
qCDebug(taskUploadLogC) << getUid().toString() << "Location header:" << redirect;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Upload::downloadFinished() {
|
m_url = QUrl(redirect.toString());
|
||||||
// handle HTTP redirection first
|
qCDebug(taskUploadLogC) << getUid().toString() << "Following redirect to " << m_url.toString();
|
||||||
// very unlikely for post requests, still can happen
|
startAction(m_network);
|
||||||
if (handleRedirect()) {
|
return true;
|
||||||
qCDebug(taskUploadLogC) << getUid().toString() << "Upload redirected:" << m_url.toString();
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the download failed before this point ...
|
void Upload::downloadFinished()
|
||||||
if (m_state == State::Succeeded) {
|
{
|
||||||
qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed but we are allowed to proceed:" << m_url.toString();
|
// handle HTTP redirection first
|
||||||
m_sink->abort();
|
// very unlikely for post requests, still can happen
|
||||||
m_reply.reset();
|
if (handleRedirect()) {
|
||||||
emit succeeded();
|
qCDebug(taskUploadLogC) << getUid().toString() << "Upload redirected:" << m_url.toString();
|
||||||
return;
|
return;
|
||||||
} else if (m_state == State::Failed) {
|
}
|
||||||
qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed in previous step:" << m_url.toString();
|
|
||||||
m_sink->abort();
|
|
||||||
m_reply.reset();
|
|
||||||
emit failed("");
|
|
||||||
return;
|
|
||||||
} else if (m_state == State::AbortedByUser) {
|
|
||||||
qCDebug(taskUploadLogC) << getUid().toString() << "Upload aborted in previous step:" << m_url.toString();
|
|
||||||
m_sink->abort();
|
|
||||||
m_reply.reset();
|
|
||||||
emit aborted();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure we got all the remaining data, if any
|
// if the download failed before this point ...
|
||||||
auto data = m_reply->readAll();
|
if (m_state == State::Succeeded) {
|
||||||
if (data.size()) {
|
qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed but we are allowed to proceed:" << m_url.toString();
|
||||||
qCDebug(taskUploadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes";
|
m_sink->abort();
|
||||||
m_state = m_sink->write(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, finalize the whole graph
|
|
||||||
m_state = m_sink->finalize(*m_reply.get());
|
|
||||||
if (m_state != State::Succeeded) {
|
|
||||||
qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed to finalize:" << m_url.toString();
|
|
||||||
m_sink->abort();
|
|
||||||
m_reply.reset();
|
|
||||||
emit failed("");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_reply.reset();
|
m_reply.reset();
|
||||||
qCDebug(taskUploadLogC) << getUid().toString() << "Upload succeeded:" << m_url.toString();
|
|
||||||
emit succeeded();
|
emit succeeded();
|
||||||
|
return;
|
||||||
|
} else if (m_state == State::Failed) {
|
||||||
|
qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed in previous step:" << m_url.toString();
|
||||||
|
m_sink->abort();
|
||||||
|
m_reply.reset();
|
||||||
|
emit failed("");
|
||||||
|
return;
|
||||||
|
} else if (m_state == State::AbortedByUser) {
|
||||||
|
qCDebug(taskUploadLogC) << getUid().toString() << "Upload aborted in previous step:" << m_url.toString();
|
||||||
|
m_sink->abort();
|
||||||
|
m_reply.reset();
|
||||||
|
emit aborted();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Upload::downloadReadyRead() {
|
// make sure we got all the remaining data, if any
|
||||||
if (m_state == State::Running) {
|
auto data = m_reply->readAll();
|
||||||
auto data = m_reply->readAll();
|
if (data.size()) {
|
||||||
m_state = m_sink->write(data);
|
qCDebug(taskUploadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes";
|
||||||
}
|
m_state = m_sink->write(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Upload::executeTask() {
|
// otherwise, finalize the whole graph
|
||||||
setStatus(tr("Uploading %1").arg(m_url.toString()));
|
m_state = m_sink->finalize(*m_reply.get());
|
||||||
|
if (m_state != State::Succeeded) {
|
||||||
|
qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed to finalize:" << m_url.toString();
|
||||||
|
m_sink->abort();
|
||||||
|
m_reply.reset();
|
||||||
|
emit failed("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_reply.reset();
|
||||||
|
qCDebug(taskUploadLogC) << getUid().toString() << "Upload succeeded:" << m_url.toString();
|
||||||
|
emit succeeded();
|
||||||
|
}
|
||||||
|
|
||||||
if (m_state == State::AbortedByUser) {
|
void Upload::downloadReadyRead()
|
||||||
qCWarning(taskUploadLogC) << getUid().toString() << "Attempt to start an aborted Upload:" << m_url.toString();
|
{
|
||||||
emit aborted();
|
if (m_state == State::Running) {
|
||||||
|
auto data = m_reply->readAll();
|
||||||
|
m_state = m_sink->write(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Upload::executeTask()
|
||||||
|
{
|
||||||
|
setStatus(tr("Uploading %1").arg(m_url.toString()));
|
||||||
|
|
||||||
|
if (m_state == State::AbortedByUser) {
|
||||||
|
qCWarning(taskUploadLogC) << getUid().toString() << "Attempt to start an aborted Upload:" << m_url.toString();
|
||||||
|
emit aborted();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QNetworkRequest request(m_url);
|
||||||
|
m_state = m_sink->init(request);
|
||||||
|
switch (m_state) {
|
||||||
|
case State::Succeeded:
|
||||||
|
emitSucceeded();
|
||||||
|
qCDebug(taskUploadLogC) << getUid().toString() << "Upload cache hit " << m_url.toString();
|
||||||
return;
|
return;
|
||||||
}
|
case State::Running:
|
||||||
QNetworkRequest request(m_url);
|
qCDebug(taskUploadLogC) << getUid().toString() << "Uploading " << m_url.toString();
|
||||||
m_state = m_sink->init(request);
|
break;
|
||||||
switch (m_state) {
|
case State::Inactive:
|
||||||
case State::Succeeded:
|
case State::Failed:
|
||||||
emitSucceeded();
|
emitFailed("");
|
||||||
qCDebug(taskUploadLogC) << getUid().toString() << "Upload cache hit " << m_url.toString();
|
return;
|
||||||
return;
|
case State::AbortedByUser:
|
||||||
case State::Running:
|
emitAborted();
|
||||||
qCDebug(taskUploadLogC) << getUid().toString() << "Uploading " << m_url.toString();
|
return;
|
||||||
break;
|
}
|
||||||
case State::Inactive:
|
|
||||||
case State::Failed:
|
|
||||||
emitFailed("");
|
|
||||||
return;
|
|
||||||
case State::AbortedByUser:
|
|
||||||
emitAborted();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8());
|
request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8());
|
||||||
// TODO remove duplication
|
// TODO remove duplication
|
||||||
if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) {
|
if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) {
|
||||||
request.setRawHeader("x-api-key", APPLICATION->getFlameAPIKey().toUtf8());
|
request.setRawHeader("x-api-key", APPLICATION->getFlameAPIKey().toUtf8());
|
||||||
} else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() ||
|
} else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() ||
|
||||||
request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) {
|
request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) {
|
||||||
QString token = APPLICATION->getModrinthAPIToken();
|
QString token = APPLICATION->getModrinthAPIToken();
|
||||||
if (!token.isNull())
|
if (!token.isNull())
|
||||||
request.setRawHeader("Authorization", token.toUtf8());
|
request.setRawHeader("Authorization", token.toUtf8());
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO other types of post requests ?
|
// TODO other types of post requests ?
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
QNetworkReply* rep = m_network->post(request, m_post_data);
|
QNetworkReply* rep = m_network->post(request, m_post_data);
|
||||||
|
|
||||||
m_reply.reset(rep);
|
m_reply.reset(rep);
|
||||||
connect(rep, &QNetworkReply::downloadProgress, this, &Upload::downloadProgress);
|
connect(rep, &QNetworkReply::downloadProgress, this, &Upload::downloadProgress);
|
||||||
connect(rep, &QNetworkReply::finished, this, &Upload::downloadFinished);
|
connect(rep, &QNetworkReply::finished, this, &Upload::downloadFinished);
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15
|
||||||
connect(rep, &QNetworkReply::errorOccurred, this, &Upload::downloadError);
|
connect(rep, &QNetworkReply::errorOccurred, this, &Upload::downloadError);
|
||||||
#else
|
#else
|
||||||
connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &Upload::downloadError);
|
connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &Upload::downloadError);
|
||||||
#endif
|
#endif
|
||||||
connect(rep, &QNetworkReply::sslErrors, this, &Upload::sslErrors);
|
connect(rep, &QNetworkReply::sslErrors, this, &Upload::sslErrors);
|
||||||
connect(rep, &QNetworkReply::readyRead, this, &Upload::downloadReadyRead);
|
connect(rep, &QNetworkReply::readyRead, this, &Upload::downloadReadyRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
Upload::Ptr Upload::makeByteArray(QUrl url, QByteArray *output, QByteArray m_post_data) {
|
Upload::Ptr Upload::makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, QByteArray m_post_data)
|
||||||
auto up = makeShared<Upload>();
|
{
|
||||||
up->m_url = std::move(url);
|
auto up = makeShared<Upload>();
|
||||||
up->m_sink.reset(new ByteArraySink(output));
|
up->m_url = std::move(url);
|
||||||
up->m_post_data = std::move(m_post_data);
|
up->m_sink.reset(new ByteArraySink(output));
|
||||||
return up;
|
up->m_post_data = std::move(m_post_data);
|
||||||
}
|
return up;
|
||||||
} // Net
|
}
|
||||||
|
} // namespace Net
|
||||||
|
@ -42,31 +42,31 @@
|
|||||||
|
|
||||||
namespace Net {
|
namespace Net {
|
||||||
|
|
||||||
class Upload : public NetAction {
|
class Upload : public NetAction {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using Ptr = shared_qobject_ptr<Upload>;
|
using Ptr = shared_qobject_ptr<Upload>;
|
||||||
|
|
||||||
static Upload::Ptr makeByteArray(QUrl url, QByteArray *output, QByteArray m_post_data);
|
static Upload::Ptr makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, QByteArray m_post_data);
|
||||||
auto abort() -> bool override;
|
auto abort() -> bool override;
|
||||||
auto canAbort() const -> bool override { return true; };
|
auto canAbort() const -> bool override { return true; };
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
|
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
|
||||||
void downloadError(QNetworkReply::NetworkError error) override;
|
void downloadError(QNetworkReply::NetworkError error) override;
|
||||||
void sslErrors(const QList<QSslError> & errors) override;
|
void sslErrors(const QList<QSslError>& errors) override;
|
||||||
void downloadFinished() override;
|
void downloadFinished() override;
|
||||||
void downloadReadyRead() override;
|
void downloadReadyRead() override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void executeTask() override;
|
void executeTask() override;
|
||||||
private:
|
|
||||||
std::unique_ptr<Sink> m_sink;
|
|
||||||
QByteArray m_post_data;
|
|
||||||
|
|
||||||
bool handleRedirect();
|
private:
|
||||||
};
|
std::unique_ptr<Sink> m_sink;
|
||||||
|
QByteArray m_post_data;
|
||||||
|
|
||||||
} // Net
|
bool handleRedirect();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Net
|
||||||
|
@ -58,7 +58,7 @@ void NewsChecker::reloadNews()
|
|||||||
qDebug() << "Reloading news.";
|
qDebug() << "Reloading news.";
|
||||||
|
|
||||||
NetJob::Ptr job{ new NetJob("News RSS Feed", m_network) };
|
NetJob::Ptr job{ new NetJob("News RSS Feed", m_network) };
|
||||||
job->addNetAction(Net::Download::makeByteArray(m_feedUrl, &newsData));
|
job->addNetAction(Net::Download::makeByteArray(m_feedUrl, newsData));
|
||||||
QObject::connect(job.get(), &NetJob::succeeded, this, &NewsChecker::rssDownloadFinished);
|
QObject::connect(job.get(), &NetJob::succeeded, this, &NewsChecker::rssDownloadFinished);
|
||||||
QObject::connect(job.get(), &NetJob::failed, this, &NewsChecker::rssDownloadFailed);
|
QObject::connect(job.get(), &NetJob::failed, this, &NewsChecker::rssDownloadFailed);
|
||||||
m_newsNetJob.reset(job);
|
m_newsNetJob.reset(job);
|
||||||
@ -79,32 +79,27 @@ void NewsChecker::rssDownloadFinished()
|
|||||||
int errorCol = -1;
|
int errorCol = -1;
|
||||||
|
|
||||||
// Parse the XML.
|
// Parse the XML.
|
||||||
if (!doc.setContent(newsData, false, &errorMsg, &errorLine, &errorCol))
|
if (!doc.setContent(*newsData, false, &errorMsg, &errorLine, &errorCol)) {
|
||||||
{
|
|
||||||
QString fullErrorMsg = QString("Error parsing RSS feed XML. %1 at %2:%3.").arg(errorMsg).arg(errorLine).arg(errorCol);
|
QString fullErrorMsg = QString("Error parsing RSS feed XML. %1 at %2:%3.").arg(errorMsg).arg(errorLine).arg(errorCol);
|
||||||
fail(fullErrorMsg);
|
fail(fullErrorMsg);
|
||||||
newsData.clear();
|
newsData->clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
newsData.clear();
|
newsData->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the parsing succeeded, read it.
|
// If the parsing succeeded, read it.
|
||||||
QDomNodeList items = doc.elementsByTagName("entry");
|
QDomNodeList items = doc.elementsByTagName("entry");
|
||||||
m_newsEntries.clear();
|
m_newsEntries.clear();
|
||||||
for (int i = 0; i < items.length(); i++)
|
for (int i = 0; i < items.length(); i++) {
|
||||||
{
|
|
||||||
QDomElement element = items.at(i).toElement();
|
QDomElement element = items.at(i).toElement();
|
||||||
NewsEntryPtr entry;
|
NewsEntryPtr entry;
|
||||||
entry.reset(new NewsEntry());
|
entry.reset(new NewsEntry());
|
||||||
QString errorMsg = "An unknown error occurred.";
|
QString errorMsg = "An unknown error occurred.";
|
||||||
if (NewsEntry::fromXmlElement(element, entry.get(), &errorMsg))
|
if (NewsEntry::fromXmlElement(element, entry.get(), &errorMsg)) {
|
||||||
{
|
|
||||||
qDebug() << "Loaded news entry" << entry->title;
|
qDebug() << "Loaded news entry" << entry->title;
|
||||||
m_newsEntries.append(entry);
|
m_newsEntries.append(entry);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
qWarning() << "Failed to load news entry at index" << i << ":" << errorMsg;
|
qWarning() << "Failed to load news entry at index" << i << ":" << errorMsg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ protected: /* data */
|
|||||||
//! True if news has been loaded.
|
//! True if news has been loaded.
|
||||||
bool m_loadedNews;
|
bool m_loadedNews;
|
||||||
|
|
||||||
QByteArray newsData;
|
std::shared_ptr<QByteArray> newsData = std::make_shared<QByteArray>();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Gets the error message that was given last time the news was loaded.
|
* Gets the error message that was given last time the news was loaded.
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
<file>scalable/jarmods.svg</file>
|
<file>scalable/jarmods.svg</file>
|
||||||
<file>scalable/java.svg</file>
|
<file>scalable/java.svg</file>
|
||||||
<file>scalable/language.svg</file>
|
<file>scalable/language.svg</file>
|
||||||
<file>scalable/launcher.svg</file>
|
|
||||||
<file>scalable/loadermods.svg</file>
|
<file>scalable/loadermods.svg</file>
|
||||||
<file>scalable/log.svg</file>
|
<file>scalable/log.svg</file>
|
||||||
<file>scalable/minecraft.svg</file>
|
<file>scalable/minecraft.svg</file>
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
<svg width="48" height="48" version="1.1" viewBox="0 0 12.7 12.7" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
|
||||||
<title>Prism Launcher Logo</title>
|
|
||||||
<g stroke-width=".26458">
|
|
||||||
<path d="m6.35 6.35" fill="#99cd61"/>
|
|
||||||
<path d="m6.35 0.52917-2.5208 4.3656 2.5208 1.4552 2.5203-1.4552 0.10955-3.0996c-1.1511-0.66459-2.3388-1.2661-2.6298-1.2661z" fill="#df6277"/>
|
|
||||||
<path d="m8.9798 1.7952-2.6298 4.5548 2.5203 1.4552 2.5208-4.3656c-0.14552-0.25205-1.2601-0.97975-2.4112-1.6443z" fill="#fb9168"/>
|
|
||||||
<path d="m11.391 3.4396-5.041 2.9104 2.5203 1.4552 2.7389-1.4552c0-1.3292-0.072554-2.6584-0.21808-2.9104z" fill="#f3db6c"/>
|
|
||||||
<path d="m6.35 6.35v2.9104h5.041c0.14552-0.25205 0.21807-1.5812 0.21808-2.9104h-5.2591z" fill="#7ab392"/>
|
|
||||||
<path d="m6.35 6.35v2.9104l2.6298 1.6443c1.1511-0.66459 2.2657-1.3923 2.4112-1.6443l-5.041-2.9104z" fill="#4b7cbc"/>
|
|
||||||
<path d="m6.35 6.35-2.5208 1.4552 2.5208 4.3656c0.29104 0 1.4787-0.60148 2.6298-1.2661l-2.6298-4.5548z" fill="#6f488c"/>
|
|
||||||
<path d="m3.8292 4.8948-2.5203 4.3656c0.29104 0.5041 4.459 2.9104 5.041 2.9104v-5.8208l-2.5208-1.4552z" fill="#4d3f33"/>
|
|
||||||
<path d="m1.309 3.4396c-0.29104 0.5041-0.29104 5.3167 0 5.8208l5.041-2.9104v-2.9104h-5.041z" fill="#7a573b"/>
|
|
||||||
<path d="m6.35 0.52917c-0.58208-2e-8 -4.75 2.4063-5.041 2.9104l5.041 2.9104v-5.8208z" fill="#99cd61"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(.88 0 0 .88 -10.906 -1.2421)">
|
|
||||||
<g transform="translate(13.26 2.2776)">
|
|
||||||
<path transform="matrix(.96975 0 0 .96975 .1921 .1921)" d="m6.3498 2.9393c-0.34105 0-2.7827 1.4099-2.9532 1.7052l2.9532 5.1157 2.9538-5.1157c-0.17052-0.29535-2.6127-1.7052-2.9538-1.7052z" fill="#fff" stroke-width=".26458"/>
|
|
||||||
</g>
|
|
||||||
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
|
|
||||||
</g>
|
|
||||||
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
|
|
||||||
<metadata>
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work rdf:about="">
|
|
||||||
<dc:title>Prism Launcher Logo</dc:title>
|
|
||||||
<dc:date>19/10/2022</dc:date>
|
|
||||||
<dc:creator>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>Prism Launcher</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:creator>
|
|
||||||
<dc:contributor>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:contributor>
|
|
||||||
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
|
||||||
<dc:publisher>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>Prism Launcher</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:publisher>
|
|
||||||
<cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/"/>
|
|
||||||
</cc:Work>
|
|
||||||
<cc:License rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#ShareAlike"/>
|
|
||||||
</cc:License>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.4 KiB |
@ -1,57 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
<svg width="48" height="48" version="1.1" viewBox="0 0 12.7 12.7" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
|
||||||
<title>Prism Launcher Logo</title>
|
|
||||||
<g stroke-width=".26458">
|
|
||||||
<path d="m6.35 6.35" fill="#99cd61"/>
|
|
||||||
<path d="m6.35 0.52917-2.5208 4.3656 2.5208 1.4552 2.5203-1.4552 0.10955-3.0996c-1.1511-0.66459-2.3388-1.2661-2.6298-1.2661z" fill="#df6277"/>
|
|
||||||
<path d="m8.9798 1.7952-2.6298 4.5548 2.5203 1.4552 2.5208-4.3656c-0.14552-0.25205-1.2601-0.97975-2.4112-1.6443z" fill="#fb9168"/>
|
|
||||||
<path d="m11.391 3.4396-5.041 2.9104 2.5203 1.4552 2.7389-1.4552c0-1.3292-0.072554-2.6584-0.21808-2.9104z" fill="#f3db6c"/>
|
|
||||||
<path d="m6.35 6.35v2.9104h5.041c0.14552-0.25205 0.21807-1.5812 0.21808-2.9104h-5.2591z" fill="#7ab392"/>
|
|
||||||
<path d="m6.35 6.35v2.9104l2.6298 1.6443c1.1511-0.66459 2.2657-1.3923 2.4112-1.6443l-5.041-2.9104z" fill="#4b7cbc"/>
|
|
||||||
<path d="m6.35 6.35-2.5208 1.4552 2.5208 4.3656c0.29104 0 1.4787-0.60148 2.6298-1.2661l-2.6298-4.5548z" fill="#6f488c"/>
|
|
||||||
<path d="m3.8292 4.8948-2.5203 4.3656c0.29104 0.5041 4.459 2.9104 5.041 2.9104v-5.8208l-2.5208-1.4552z" fill="#4d3f33"/>
|
|
||||||
<path d="m1.309 3.4396c-0.29104 0.5041-0.29104 5.3167 0 5.8208l5.041-2.9104v-2.9104h-5.041z" fill="#7a573b"/>
|
|
||||||
<path d="m6.35 0.52917c-0.58208-2e-8 -4.75 2.4063-5.041 2.9104l5.041 2.9104v-5.8208z" fill="#99cd61"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(.88 0 0 .88 -10.906 -1.2421)">
|
|
||||||
<g transform="translate(13.26 2.2776)">
|
|
||||||
<path transform="matrix(.96975 0 0 .96975 .1921 .1921)" d="m6.3498 2.9393c-0.34105 0-2.7827 1.4099-2.9532 1.7052l2.9532 5.1157 2.9538-5.1157c-0.17052-0.29535-2.6127-1.7052-2.9538-1.7052z" fill="#fff" stroke-width=".26458"/>
|
|
||||||
</g>
|
|
||||||
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
|
|
||||||
</g>
|
|
||||||
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
|
|
||||||
<metadata>
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work rdf:about="">
|
|
||||||
<dc:title>Prism Launcher Logo</dc:title>
|
|
||||||
<dc:date>19/10/2022</dc:date>
|
|
||||||
<dc:creator>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>Prism Launcher</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:creator>
|
|
||||||
<dc:contributor>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:contributor>
|
|
||||||
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
|
||||||
<dc:publisher>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>Prism Launcher</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:publisher>
|
|
||||||
<cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/"/>
|
|
||||||
</cc:Work>
|
|
||||||
<cc:License rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#ShareAlike"/>
|
|
||||||
</cc:License>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.4 KiB |
@ -18,7 +18,6 @@
|
|||||||
<file>scalable/jarmods.svg</file>
|
<file>scalable/jarmods.svg</file>
|
||||||
<file>scalable/java.svg</file>
|
<file>scalable/java.svg</file>
|
||||||
<file>scalable/language.svg</file>
|
<file>scalable/language.svg</file>
|
||||||
<file>scalable/launcher.svg</file>
|
|
||||||
<file>scalable/loadermods.svg</file>
|
<file>scalable/loadermods.svg</file>
|
||||||
<file>scalable/log.svg</file>
|
<file>scalable/log.svg</file>
|
||||||
<file>scalable/minecraft.svg</file>
|
<file>scalable/minecraft.svg</file>
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
<svg width="48" height="48" version="1.1" viewBox="0 0 12.7 12.7" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
|
||||||
<title>Prism Launcher Logo</title>
|
|
||||||
<g stroke-width=".26458">
|
|
||||||
<path d="m6.35 6.35" fill="#99cd61"/>
|
|
||||||
<path d="m6.35 0.52917-2.5208 4.3656 2.5208 1.4552 2.5203-1.4552 0.10955-3.0996c-1.1511-0.66459-2.3388-1.2661-2.6298-1.2661z" fill="#df6277"/>
|
|
||||||
<path d="m8.9798 1.7952-2.6298 4.5548 2.5203 1.4552 2.5208-4.3656c-0.14552-0.25205-1.2601-0.97975-2.4112-1.6443z" fill="#fb9168"/>
|
|
||||||
<path d="m11.391 3.4396-5.041 2.9104 2.5203 1.4552 2.7389-1.4552c0-1.3292-0.072554-2.6584-0.21808-2.9104z" fill="#f3db6c"/>
|
|
||||||
<path d="m6.35 6.35v2.9104h5.041c0.14552-0.25205 0.21807-1.5812 0.21808-2.9104h-5.2591z" fill="#7ab392"/>
|
|
||||||
<path d="m6.35 6.35v2.9104l2.6298 1.6443c1.1511-0.66459 2.2657-1.3923 2.4112-1.6443l-5.041-2.9104z" fill="#4b7cbc"/>
|
|
||||||
<path d="m6.35 6.35-2.5208 1.4552 2.5208 4.3656c0.29104 0 1.4787-0.60148 2.6298-1.2661l-2.6298-4.5548z" fill="#6f488c"/>
|
|
||||||
<path d="m3.8292 4.8948-2.5203 4.3656c0.29104 0.5041 4.459 2.9104 5.041 2.9104v-5.8208l-2.5208-1.4552z" fill="#4d3f33"/>
|
|
||||||
<path d="m1.309 3.4396c-0.29104 0.5041-0.29104 5.3167 0 5.8208l5.041-2.9104v-2.9104h-5.041z" fill="#7a573b"/>
|
|
||||||
<path d="m6.35 0.52917c-0.58208-2e-8 -4.75 2.4063-5.041 2.9104l5.041 2.9104v-5.8208z" fill="#99cd61"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(.88 0 0 .88 -10.906 -1.2421)">
|
|
||||||
<g transform="translate(13.26 2.2776)">
|
|
||||||
<path transform="matrix(.96975 0 0 .96975 .1921 .1921)" d="m6.3498 2.9393c-0.34105 0-2.7827 1.4099-2.9532 1.7052l2.9532 5.1157 2.9538-5.1157c-0.17052-0.29535-2.6127-1.7052-2.9538-1.7052z" fill="#fff" stroke-width=".26458"/>
|
|
||||||
</g>
|
|
||||||
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
|
|
||||||
</g>
|
|
||||||
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
|
|
||||||
<metadata>
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work rdf:about="">
|
|
||||||
<dc:title>Prism Launcher Logo</dc:title>
|
|
||||||
<dc:date>19/10/2022</dc:date>
|
|
||||||
<dc:creator>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>Prism Launcher</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:creator>
|
|
||||||
<dc:contributor>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:contributor>
|
|
||||||
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
|
||||||
<dc:publisher>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>Prism Launcher</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:publisher>
|
|
||||||
<cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/"/>
|
|
||||||
</cc:Work>
|
|
||||||
<cc:License rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#ShareAlike"/>
|
|
||||||
</cc:License>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.4 KiB |
@ -18,7 +18,6 @@
|
|||||||
<file>scalable/jarmods.svg</file>
|
<file>scalable/jarmods.svg</file>
|
||||||
<file>scalable/java.svg</file>
|
<file>scalable/java.svg</file>
|
||||||
<file>scalable/language.svg</file>
|
<file>scalable/language.svg</file>
|
||||||
<file>scalable/launcher.svg</file>
|
|
||||||
<file>scalable/loadermods.svg</file>
|
<file>scalable/loadermods.svg</file>
|
||||||
<file>scalable/log.svg</file>
|
<file>scalable/log.svg</file>
|
||||||
<file>scalable/minecraft.svg</file>
|
<file>scalable/minecraft.svg</file>
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg width="24" height="24" fill="#eeeeee" version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m20 4h-16v16h16zm0 18h-16c-1.1046 0-2-0.89543-2-2v-16c0-1.1046 0.89543-2 2-2h16c1.1046 0 2 0.89543 2 2v16c0 1.1046-0.89543 2-2 2z"/><path d="m7.2 18c-0.225 0-0.45-0.075-0.6-0.15-0.375-0.225-0.6-0.6-0.6-1.05v-9.6c0-0.45 0.225-0.825 0.6-1.05 0.225-0.15 0.375-0.15 0.6-0.15 0.15 0 0.375 0.075 0.525 0.15l9.6 4.8c0.375 0.225 0.675 0.6 0.675 1.05 0 0.45-0.225 0.9-0.675 1.05l-9.6 4.8c-0.15 0.075-0.375 0.15-0.525 0.15z" clip-rule="evenodd" fill="#eeeeee" fill-rule="evenodd" stroke-width=".99999"/></svg>
|
|
Before Width: | Height: | Size: 660 B |
@ -16,7 +16,6 @@
|
|||||||
<file>scalable/jarmods.svg</file>
|
<file>scalable/jarmods.svg</file>
|
||||||
<file>scalable/java.svg</file>
|
<file>scalable/java.svg</file>
|
||||||
<file>scalable/language.svg</file>
|
<file>scalable/language.svg</file>
|
||||||
<file>scalable/launcher.svg</file>
|
|
||||||
<file>scalable/loadermods.svg</file>
|
<file>scalable/loadermods.svg</file>
|
||||||
<file>scalable/log.svg</file>
|
<file>scalable/log.svg</file>
|
||||||
<file>scalable/minecraft.svg</file>
|
<file>scalable/minecraft.svg</file>
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
<svg width="48" height="48" version="1.1" viewBox="0 0 12.7 12.7" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
|
||||||
<title>Prism Launcher Logo</title>
|
|
||||||
<g stroke-width=".26458">
|
|
||||||
<path d="m6.35 6.35" fill="#99cd61"/>
|
|
||||||
<path d="m6.35 0.52917-2.5208 4.3656 2.5208 1.4552 2.5203-1.4552 0.10955-3.0996c-1.1511-0.66459-2.3388-1.2661-2.6298-1.2661z" fill="#df6277"/>
|
|
||||||
<path d="m8.9798 1.7952-2.6298 4.5548 2.5203 1.4552 2.5208-4.3656c-0.14552-0.25205-1.2601-0.97975-2.4112-1.6443z" fill="#fb9168"/>
|
|
||||||
<path d="m11.391 3.4396-5.041 2.9104 2.5203 1.4552 2.7389-1.4552c0-1.3292-0.072554-2.6584-0.21808-2.9104z" fill="#f3db6c"/>
|
|
||||||
<path d="m6.35 6.35v2.9104h5.041c0.14552-0.25205 0.21807-1.5812 0.21808-2.9104h-5.2591z" fill="#7ab392"/>
|
|
||||||
<path d="m6.35 6.35v2.9104l2.6298 1.6443c1.1511-0.66459 2.2657-1.3923 2.4112-1.6443l-5.041-2.9104z" fill="#4b7cbc"/>
|
|
||||||
<path d="m6.35 6.35-2.5208 1.4552 2.5208 4.3656c0.29104 0 1.4787-0.60148 2.6298-1.2661l-2.6298-4.5548z" fill="#6f488c"/>
|
|
||||||
<path d="m3.8292 4.8948-2.5203 4.3656c0.29104 0.5041 4.459 2.9104 5.041 2.9104v-5.8208l-2.5208-1.4552z" fill="#4d3f33"/>
|
|
||||||
<path d="m1.309 3.4396c-0.29104 0.5041-0.29104 5.3167 0 5.8208l5.041-2.9104v-2.9104h-5.041z" fill="#7a573b"/>
|
|
||||||
<path d="m6.35 0.52917c-0.58208-2e-8 -4.75 2.4063-5.041 2.9104l5.041 2.9104v-5.8208z" fill="#99cd61"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(.88 0 0 .88 -10.906 -1.2421)">
|
|
||||||
<g transform="translate(13.26 2.2776)">
|
|
||||||
<path transform="matrix(.96975 0 0 .96975 .1921 .1921)" d="m6.3498 2.9393c-0.34105 0-2.7827 1.4099-2.9532 1.7052l2.9532 5.1157 2.9538-5.1157c-0.17052-0.29535-2.6127-1.7052-2.9538-1.7052z" fill="#fff" stroke-width=".26458"/>
|
|
||||||
</g>
|
|
||||||
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
|
|
||||||
</g>
|
|
||||||
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
|
|
||||||
<metadata>
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work rdf:about="">
|
|
||||||
<dc:title>Prism Launcher Logo</dc:title>
|
|
||||||
<dc:date>19/10/2022</dc:date>
|
|
||||||
<dc:creator>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>Prism Launcher</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:creator>
|
|
||||||
<dc:contributor>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:contributor>
|
|
||||||
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
|
||||||
<dc:publisher>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>Prism Launcher</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:publisher>
|
|
||||||
<cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/"/>
|
|
||||||
</cc:Work>
|
|
||||||
<cc:License rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#ShareAlike"/>
|
|
||||||
</cc:License>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.4 KiB |
@ -16,7 +16,6 @@
|
|||||||
<file>scalable/jarmods.svg</file>
|
<file>scalable/jarmods.svg</file>
|
||||||
<file>scalable/java.svg</file>
|
<file>scalable/java.svg</file>
|
||||||
<file>scalable/language.svg</file>
|
<file>scalable/language.svg</file>
|
||||||
<file>scalable/launcher.svg</file>
|
|
||||||
<file>scalable/loadermods.svg</file>
|
<file>scalable/loadermods.svg</file>
|
||||||
<file>scalable/log.svg</file>
|
<file>scalable/log.svg</file>
|
||||||
<file>scalable/minecraft.svg</file>
|
<file>scalable/minecraft.svg</file>
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
<svg width="48" height="48" version="1.1" viewBox="0 0 12.7 12.7" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
|
||||||
<title>Prism Launcher Logo</title>
|
|
||||||
<g stroke-width=".26458">
|
|
||||||
<path d="m6.35 6.35" fill="#99cd61"/>
|
|
||||||
<path d="m6.35 0.52917-2.5208 4.3656 2.5208 1.4552 2.5203-1.4552 0.10955-3.0996c-1.1511-0.66459-2.3388-1.2661-2.6298-1.2661z" fill="#df6277"/>
|
|
||||||
<path d="m8.9798 1.7952-2.6298 4.5548 2.5203 1.4552 2.5208-4.3656c-0.14552-0.25205-1.2601-0.97975-2.4112-1.6443z" fill="#fb9168"/>
|
|
||||||
<path d="m11.391 3.4396-5.041 2.9104 2.5203 1.4552 2.7389-1.4552c0-1.3292-0.072554-2.6584-0.21808-2.9104z" fill="#f3db6c"/>
|
|
||||||
<path d="m6.35 6.35v2.9104h5.041c0.14552-0.25205 0.21807-1.5812 0.21808-2.9104h-5.2591z" fill="#7ab392"/>
|
|
||||||
<path d="m6.35 6.35v2.9104l2.6298 1.6443c1.1511-0.66459 2.2657-1.3923 2.4112-1.6443l-5.041-2.9104z" fill="#4b7cbc"/>
|
|
||||||
<path d="m6.35 6.35-2.5208 1.4552 2.5208 4.3656c0.29104 0 1.4787-0.60148 2.6298-1.2661l-2.6298-4.5548z" fill="#6f488c"/>
|
|
||||||
<path d="m3.8292 4.8948-2.5203 4.3656c0.29104 0.5041 4.459 2.9104 5.041 2.9104v-5.8208l-2.5208-1.4552z" fill="#4d3f33"/>
|
|
||||||
<path d="m1.309 3.4396c-0.29104 0.5041-0.29104 5.3167 0 5.8208l5.041-2.9104v-2.9104h-5.041z" fill="#7a573b"/>
|
|
||||||
<path d="m6.35 0.52917c-0.58208-2e-8 -4.75 2.4063-5.041 2.9104l5.041 2.9104v-5.8208z" fill="#99cd61"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(.88 0 0 .88 -10.906 -1.2421)">
|
|
||||||
<g transform="translate(13.26 2.2776)">
|
|
||||||
<path transform="matrix(.96975 0 0 .96975 .1921 .1921)" d="m6.3498 2.9393c-0.34105 0-2.7827 1.4099-2.9532 1.7052l2.9532 5.1157 2.9538-5.1157c-0.17052-0.29535-2.6127-1.7052-2.9538-1.7052z" fill="#fff" stroke-width=".26458"/>
|
|
||||||
</g>
|
|
||||||
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
|
|
||||||
</g>
|
|
||||||
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
|
|
||||||
<metadata>
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work rdf:about="">
|
|
||||||
<dc:title>Prism Launcher Logo</dc:title>
|
|
||||||
<dc:date>19/10/2022</dc:date>
|
|
||||||
<dc:creator>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>Prism Launcher</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:creator>
|
|
||||||
<dc:contributor>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:contributor>
|
|
||||||
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
|
||||||
<dc:publisher>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>Prism Launcher</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:publisher>
|
|
||||||
<cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/"/>
|
|
||||||
</cc:Work>
|
|
||||||
<cc:License rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#ShareAlike"/>
|
|
||||||
</cc:License>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.4 KiB |
@ -16,7 +16,6 @@
|
|||||||
<file>scalable/jarmods.svg</file>
|
<file>scalable/jarmods.svg</file>
|
||||||
<file>scalable/java.svg</file>
|
<file>scalable/java.svg</file>
|
||||||
<file>scalable/language.svg</file>
|
<file>scalable/language.svg</file>
|
||||||
<file>scalable/launcher.svg</file>
|
|
||||||
<file>scalable/loadermods.svg</file>
|
<file>scalable/loadermods.svg</file>
|
||||||
<file>scalable/log.svg</file>
|
<file>scalable/log.svg</file>
|
||||||
<file>scalable/minecraft.svg</file>
|
<file>scalable/minecraft.svg</file>
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
<svg width="48" height="48" version="1.1" viewBox="0 0 12.7 12.7" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
|
||||||
<title>Prism Launcher Logo</title>
|
|
||||||
<g stroke-width=".26458">
|
|
||||||
<path d="m6.35 6.35" fill="#99cd61"/>
|
|
||||||
<path d="m6.35 0.52917-2.5208 4.3656 2.5208 1.4552 2.5203-1.4552 0.10955-3.0996c-1.1511-0.66459-2.3388-1.2661-2.6298-1.2661z" fill="#df6277"/>
|
|
||||||
<path d="m8.9798 1.7952-2.6298 4.5548 2.5203 1.4552 2.5208-4.3656c-0.14552-0.25205-1.2601-0.97975-2.4112-1.6443z" fill="#fb9168"/>
|
|
||||||
<path d="m11.391 3.4396-5.041 2.9104 2.5203 1.4552 2.7389-1.4552c0-1.3292-0.072554-2.6584-0.21808-2.9104z" fill="#f3db6c"/>
|
|
||||||
<path d="m6.35 6.35v2.9104h5.041c0.14552-0.25205 0.21807-1.5812 0.21808-2.9104h-5.2591z" fill="#7ab392"/>
|
|
||||||
<path d="m6.35 6.35v2.9104l2.6298 1.6443c1.1511-0.66459 2.2657-1.3923 2.4112-1.6443l-5.041-2.9104z" fill="#4b7cbc"/>
|
|
||||||
<path d="m6.35 6.35-2.5208 1.4552 2.5208 4.3656c0.29104 0 1.4787-0.60148 2.6298-1.2661l-2.6298-4.5548z" fill="#6f488c"/>
|
|
||||||
<path d="m3.8292 4.8948-2.5203 4.3656c0.29104 0.5041 4.459 2.9104 5.041 2.9104v-5.8208l-2.5208-1.4552z" fill="#4d3f33"/>
|
|
||||||
<path d="m1.309 3.4396c-0.29104 0.5041-0.29104 5.3167 0 5.8208l5.041-2.9104v-2.9104h-5.041z" fill="#7a573b"/>
|
|
||||||
<path d="m6.35 0.52917c-0.58208-2e-8 -4.75 2.4063-5.041 2.9104l5.041 2.9104v-5.8208z" fill="#99cd61"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(.88 0 0 .88 -10.906 -1.2421)">
|
|
||||||
<g transform="translate(13.26 2.2776)">
|
|
||||||
<path transform="matrix(.96975 0 0 .96975 .1921 .1921)" d="m6.3498 2.9393c-0.34105 0-2.7827 1.4099-2.9532 1.7052l2.9532 5.1157 2.9538-5.1157c-0.17052-0.29535-2.6127-1.7052-2.9538-1.7052z" fill="#fff" stroke-width=".26458"/>
|
|
||||||
</g>
|
|
||||||
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
|
|
||||||
</g>
|
|
||||||
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
|
|
||||||
<metadata>
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work rdf:about="">
|
|
||||||
<dc:title>Prism Launcher Logo</dc:title>
|
|
||||||
<dc:date>19/10/2022</dc:date>
|
|
||||||
<dc:creator>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>Prism Launcher</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:creator>
|
|
||||||
<dc:contributor>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:contributor>
|
|
||||||
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
|
||||||
<dc:publisher>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>Prism Launcher</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:publisher>
|
|
||||||
<cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/"/>
|
|
||||||
</cc:Work>
|
|
||||||
<cc:License rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
|
|
||||||
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
|
|
||||||
<cc:requires rdf:resource="http://creativecommons.org/ns#ShareAlike"/>
|
|
||||||
</cc:License>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.4 KiB |