Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into feat/acknowledge_release_type
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
commit
9acbf98f94
4
.github/workflows/backport.yml
vendored
4
.github/workflows/backport.yml
vendored
@ -20,11 +20,11 @@ jobs:
|
||||
if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Create backport PRs
|
||||
uses: korthout/backport-action@v1.3.1
|
||||
uses: korthout/backport-action@v1.4.0
|
||||
with:
|
||||
# Config README: https://github.com/korthout/backport-action#backport-action
|
||||
pull_description: |-
|
||||
|
62
.github/workflows/build.yml
vendored
62
.github/workflows/build.yml
vendored
@ -24,6 +24,12 @@ on:
|
||||
CACHIX_AUTH_TOKEN:
|
||||
description: Private token for authenticating against Cachix cache
|
||||
required: false
|
||||
GPG_PRIVATE_KEY:
|
||||
description: Private key for AppImage signing
|
||||
required: false
|
||||
GPG_PRIVATE_KEY_ID:
|
||||
description: ID for the GPG_PRIVATE_KEY, to select the signing key
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -119,7 +125,7 @@ jobs:
|
||||
# PREPARE
|
||||
##
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
@ -158,7 +164,7 @@ jobs:
|
||||
|
||||
- name: Retrieve ccache cache (Windows MinGW-w64)
|
||||
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
||||
uses: actions/cache@v3.3.1
|
||||
uses: actions/cache@v3.3.2
|
||||
with:
|
||||
path: '${{ github.workspace }}\.ccache'
|
||||
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
|
||||
@ -249,6 +255,8 @@ jobs:
|
||||
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage"
|
||||
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage"
|
||||
|
||||
wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage"
|
||||
|
||||
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
|
||||
sudo apt install libopengl0
|
||||
|
||||
@ -387,8 +395,8 @@ jobs:
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
if ("${{ matrix.qt_ver }}" -eq "5")
|
||||
{
|
||||
Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll
|
||||
Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll
|
||||
Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll
|
||||
Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll
|
||||
}
|
||||
cd ${{ github.workspace }}
|
||||
|
||||
@ -425,7 +433,7 @@ jobs:
|
||||
run: |
|
||||
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||
|
||||
|
||||
Get-ChildItem ${{ env.INSTALL_PORTABLE_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_PORTABLE_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
||||
|
||||
- name: Package (Windows, installer)
|
||||
@ -466,11 +474,15 @@ jobs:
|
||||
- name: Package AppImage (Linux)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
shell: bash
|
||||
env:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
|
||||
|
||||
mv ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.metainfo.xml ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml
|
||||
export "NO_APPSTREAM=1" # we have to skip appstream checking because appstream on ubuntu 20.04 is outdated
|
||||
export OUTPUT="PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
||||
|
||||
export OUTPUT="PrismLauncher-Linux-x86_64.AppImage"
|
||||
|
||||
chmod +x linuxdeploy-*.AppImage
|
||||
|
||||
@ -481,8 +493,8 @@ jobs:
|
||||
|
||||
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk
|
||||
|
||||
cp -r /home/runner/work/PrismLauncher/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
||||
|
||||
cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
||||
|
||||
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||
cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||
@ -494,8 +506,33 @@ jobs:
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
|
||||
export LD_LIBRARY_PATH
|
||||
|
||||
chmod +x AppImageUpdate-x86_64.AppImage
|
||||
./AppImageUpdate-x86_64.AppImage --appimage-extract
|
||||
|
||||
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional
|
||||
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins
|
||||
|
||||
cp -r squashfs-root/usr/bin/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin
|
||||
cp -r squashfs-root/usr/lib/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib
|
||||
cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional
|
||||
cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins
|
||||
|
||||
export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-x86_64.AppImage.zsync"
|
||||
|
||||
if [ '${{ secrets.GPG_PRIVATE_KEY_ID }}' != '' ]; then
|
||||
export SIGN=1
|
||||
export SIGN_KEY=${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||
mkdir -p ~/.gnupg/
|
||||
printf "$GPG_PRIVATE_KEY" | base64 --decode > ~/.gnupg/private.key
|
||||
gpg --import ~/.gnupg/private.key
|
||||
else
|
||||
echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.prismlauncher.PrismLauncher.svg
|
||||
|
||||
mv "PrismLauncher-Linux-x86_64.AppImage" "PrismLauncher-Linux-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
||||
|
||||
##
|
||||
# UPLOAD BUILDS
|
||||
##
|
||||
@ -562,6 +599,13 @@ jobs:
|
||||
with:
|
||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
|
||||
- name: Upload AppImage Zsync (Linux)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync
|
||||
path: PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||
|
||||
- name: ccache stats (Windows MinGW-w64)
|
||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||
@ -576,7 +620,7 @@ jobs:
|
||||
options: --privileged
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
if: inputs.build_type == 'Debug'
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
|
11
.github/workflows/trigger_release.yml
vendored
11
.github/workflows/trigger_release.yml
vendored
@ -26,7 +26,7 @@ jobs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
path: 'PrismLauncher-source'
|
||||
@ -43,7 +43,8 @@ jobs:
|
||||
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage
|
||||
mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||
mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
||||
|
||||
@ -78,9 +79,8 @@ jobs:
|
||||
- name: Create release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag_name: ${{ github.ref }}
|
||||
name: Prism Launcher ${{ env.VERSION }}
|
||||
draft: true
|
||||
@ -88,7 +88,8 @@ jobs:
|
||||
files: |
|
||||
PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||
PrismLauncher-Linux-x86_64.AppImage
|
||||
PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
|
||||
|
8
.github/workflows/update-flake.yml
vendored
8
.github/workflows/update-flake.yml
vendored
@ -16,13 +16,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011 # v23
|
||||
|
||||
- uses: DeterminateSystems/update-flake-lock@v19
|
||||
- uses: DeterminateSystems/update-flake-lock@v20
|
||||
with:
|
||||
commit-msg: "chore(nix): update lockfile"
|
||||
pr-title: "chore(nix): update lockfile"
|
||||
pr-labels: |
|
||||
Linux
|
||||
packaging
|
||||
simple change
|
||||
changelog:omit
|
||||
|
@ -33,6 +33,13 @@ if(MSVC)
|
||||
# Use /W4 as /Wall includes unnesserey warnings such as added padding to structs
|
||||
set(CMAKE_CXX_FLAGS "/GS /permissive- /W4 ${CMAKE_CXX_FLAGS}")
|
||||
|
||||
# /EHs Enables stack unwind semantics for standard C++ exceptions to ensure stackframes are unwound
|
||||
# and object deconstructors are called when an exception is caught.
|
||||
# without it memory leaks and a warning is printed
|
||||
# /EHc tells the compiler to assume that functions declared as extern "C" never throw a C++ exception
|
||||
# This appears to not always be a defualt compiler option in CMAKE
|
||||
set(CMAKE_CXX_FLAGS "/EHsc ${CMAKE_CXX_FLAGS}")
|
||||
|
||||
# LINK accepts /SUBSYSTEM whics sets if we are a WINDOWS (gui) or a CONSOLE programs
|
||||
# This implicitly selects an entrypoint specific to the subsystem selected
|
||||
# qtmain/QtEntryPointLib provides the correct entrypoint (wWinMain) for gui programs
|
||||
@ -88,35 +95,36 @@ set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}"
|
||||
option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF)
|
||||
|
||||
# If this is a Debug build turn on address sanitiser
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND DEBUG_ADDRESS_SANITIZER)
|
||||
if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") AND DEBUG_ADDRESS_SANITIZER)
|
||||
message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||
# using clang with clang-cl front end
|
||||
message(STATUS "Address Sanitizer available on Clang MSVC frontend")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
||||
else()
|
||||
# AppleClang and Clang
|
||||
message(STATUS "Address Sanitizer available on Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||
endif()
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
# GCC
|
||||
message(STATUS "Address Sanitizer available on GCC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||
link_libraries("asan")
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
message(STATUS "Address Sanitizer available on MSVC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
||||
else()
|
||||
message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
option(ENABLE_LTO "Enable Link Time Optimization" off)
|
||||
|
||||
if(ENABLE_LTO)
|
||||
@ -208,6 +216,18 @@ set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL
|
||||
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
|
||||
set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against")
|
||||
|
||||
# Native libraries
|
||||
if(UNIX AND APPLE)
|
||||
set(Launcher_GLFW_LIBRARY_NAME "libglfw.dylib" CACHE STRING "Name of native glfw library")
|
||||
set(Launcher_OPENAL_LIBRARY_NAME "libopenal.dylib" CACHE STRING "Name of native openal library")
|
||||
elseif(UNIX)
|
||||
set(Launcher_GLFW_LIBRARY_NAME "libglfw.so" CACHE STRING "Name of native glfw library")
|
||||
set(Launcher_OPENAL_LIBRARY_NAME "libopenal.so" CACHE STRING "Name of native openal library")
|
||||
elseif(WIN32)
|
||||
set(Launcher_GLFW_LIBRARY_NAME "glfw.dll" CACHE STRING "Name of native glfw library")
|
||||
set(Launcher_OPENAL_LIBRARY_NAME "OpenAL.dll" CACHE STRING "Name of native openal library")
|
||||
endif()
|
||||
|
||||
# API Keys
|
||||
# NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service
|
||||
# of these platforms, please change these API keys beforehand.
|
||||
|
@ -110,6 +110,9 @@ Config::Config()
|
||||
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
||||
META_URL = "@Launcher_META_URL@";
|
||||
|
||||
GLFW_LIBRARY_NAME = "@Launcher_GLFW_LIBRARY_NAME@";
|
||||
OPENAL_LIBRARY_NAME = "@Launcher_OPENAL_LIBRARY_NAME@";
|
||||
|
||||
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
|
||||
TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@";
|
||||
MATRIX_URL = "@Launcher_MATRIX_URL@";
|
||||
|
@ -134,6 +134,9 @@ class Config {
|
||||
*/
|
||||
QString META_URL;
|
||||
|
||||
QString GLFW_LIBRARY_NAME;
|
||||
QString OPENAL_LIBRARY_NAME;
|
||||
|
||||
QString BUG_TRACKER_URL;
|
||||
QString TRANSLATIONS_URL;
|
||||
QString MATRIX_URL;
|
||||
|
151
cmake/CompilerWarnings.cmake
Normal file
151
cmake/CompilerWarnings.cmake
Normal file
@ -0,0 +1,151 @@
|
||||
#
|
||||
# Function to set compiler warnings with reasonable defaults at the project level.
|
||||
# Taken from https://github.com/aminya/project_options/blob/main/src/CompilerWarnings.cmake
|
||||
# under the folowing license:
|
||||
#
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2022-2100 Amin Yahyaabadi
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
#
|
||||
|
||||
include_guard()
|
||||
|
||||
function(_set_project_warnings_add_target_link_option TARGET OPTIONS)
|
||||
target_link_options(${_project_name} INTERFACE ${OPTIONS})
|
||||
endfunction()
|
||||
|
||||
# Set the compiler warnings
|
||||
#
|
||||
# https://clang.llvm.org/docs/DiagnosticsReference.html
|
||||
# https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md
|
||||
function(
|
||||
set_project_warnings
|
||||
_project_name
|
||||
MSVC_WARNINGS
|
||||
CLANG_WARNINGS
|
||||
GCC_WARNINGS
|
||||
)
|
||||
if("${MSVC_WARNINGS}" STREQUAL "")
|
||||
set(MSVC_WARNINGS
|
||||
/W4 # Baseline reasonable warnings
|
||||
/w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data
|
||||
/w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/w14263 # 'function': member function does not override any base class virtual member function
|
||||
/w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not
|
||||
# be destructed correctly
|
||||
/w14287 # 'operator': unsigned/negative constant mismatch
|
||||
/we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside
|
||||
# the for-loop scope
|
||||
/w14296 # 'operator': expression is always 'boolean_value'
|
||||
/w14311 # 'variable': pointer truncation from 'type1' to 'type2'
|
||||
/w14545 # expression before comma evaluates to a function which is missing an argument list
|
||||
/w14546 # function call before comma missing argument list
|
||||
/w14547 # 'operator': operator before comma has no effect; expected operator with side-effect
|
||||
/w14549 # 'operator': operator before comma has no effect; did you intend 'operator'?
|
||||
/w14555 # expression has no effect; expected expression with side- effect
|
||||
/w14619 # pragma warning: there is no warning number 'number'
|
||||
/w14640 # Enable warning on thread un-safe static member initialization
|
||||
/w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior.
|
||||
/w14905 # wide string literal cast to 'LPSTR'
|
||||
/w14906 # string literal cast to 'LPWSTR'
|
||||
/w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied
|
||||
/permissive- # standards conformance mode for MSVC compiler.
|
||||
)
|
||||
endif()
|
||||
|
||||
if("${CLANG_WARNINGS}" STREQUAL "")
|
||||
set(CLANG_WARNINGS
|
||||
-Wall
|
||||
-Wextra # reasonable and standard
|
||||
-Wshadow # warn the user if a variable declaration shadows one from a parent context
|
||||
-Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps
|
||||
# catch hard to track down memory errors
|
||||
-Wold-style-cast # warn for c-style casts
|
||||
-Wcast-align # warn for potential performance problem casts
|
||||
-Wunused # warn on anything being unused
|
||||
-Woverloaded-virtual # warn if you overload (not override) a virtual function
|
||||
-Wpedantic # warn if non-standard C++ is used
|
||||
-Wconversion # warn on type conversions that may lose data
|
||||
-Wsign-conversion # warn on sign conversions
|
||||
-Wnull-dereference # warn if a null dereference is detected
|
||||
-Wdouble-promotion # warn if float is implicit promoted to double
|
||||
-Wformat=2 # warn on security issues around functions that format output (ie printf)
|
||||
-Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation
|
||||
)
|
||||
endif()
|
||||
|
||||
if("${GCC_WARNINGS}" STREQUAL "")
|
||||
set(GCC_WARNINGS
|
||||
${CLANG_WARNINGS}
|
||||
-Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist
|
||||
-Wduplicated-cond # warn if if / else chain has duplicated conditions
|
||||
-Wduplicated-branches # warn if if / else branches have duplicated code
|
||||
-Wlogical-op # warn about logical operations being used where bitwise were probably wanted
|
||||
-Wuseless-cast # warn if you perform a cast to the same type
|
||||
)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set(PROJECT_WARNINGS_CXX ${MSVC_WARNINGS})
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
|
||||
set(PROJECT_WARNINGS_CXX ${CLANG_WARNINGS})
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(PROJECT_WARNINGS_CXX ${GCC_WARNINGS})
|
||||
else()
|
||||
message(AUTHOR_WARNING "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'")
|
||||
# TODO support Intel compiler
|
||||
endif()
|
||||
|
||||
# Add C warnings
|
||||
set(PROJECT_WARNINGS_C "${PROJECT_WARNINGS_CXX}")
|
||||
list(
|
||||
REMOVE_ITEM
|
||||
PROJECT_WARNINGS_C
|
||||
-Wnon-virtual-dtor
|
||||
-Wold-style-cast
|
||||
-Woverloaded-virtual
|
||||
-Wuseless-cast
|
||||
-Wextra-semi
|
||||
)
|
||||
|
||||
target_compile_options(
|
||||
${_project_name}
|
||||
INTERFACE # C++ warnings
|
||||
$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_WARNINGS_CXX}>
|
||||
# C warnings
|
||||
$<$<COMPILE_LANGUAGE:C>:${PROJECT_WARNINGS_C}>
|
||||
)
|
||||
|
||||
# If we are using the compiler as a linker driver pass the warnings to it
|
||||
# (most useful when using LTO or warnings as errors)
|
||||
if(CMAKE_CXX_LINK_EXECUTABLE MATCHES "^<CMAKE_CXX_COMPILER>")
|
||||
_set_project_warnings_add_target_link_option(
|
||||
${_project_name} "$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_WARNINGS_CXX}>"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CMAKE_C_LINK_EXECUTABLE MATCHES "^<CMAKE_C_COMPILER>")
|
||||
_set_project_warnings_add_target_link_option(
|
||||
${_project_name} "$<$<COMPILE_LANGUAGE:C>:${PROJECT_WARNINGS_C}>"
|
||||
)
|
||||
endif()
|
||||
|
||||
endfunction()
|
@ -67,5 +67,16 @@
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>Curseforge</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>curseforge</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
40
flake.lock
generated
40
flake.lock
generated
@ -21,11 +21,11 @@
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1690933134,
|
||||
"narHash": "sha256-ab989mN63fQZBFrkk4Q8bYxQCktuHmBIBqUG1jl6/FQ=",
|
||||
"lastModified": 1693611461,
|
||||
"narHash": "sha256-aPODl8vAgGQ0ZYFIRisxYG5MOGSkIczvu2Cd8Gb9+1Y=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "59cf3f1447cfc75087e7273b04b31e689a8599fb",
|
||||
"rev": "7f53fdb7bdc5bb237da7fefef12d099e4fd611ca",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -89,13 +89,28 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-filter": {
|
||||
"locked": {
|
||||
"lastModified": 1694857738,
|
||||
"narHash": "sha256-bxxNyLHjhu0N8T3REINXQ2ZkJco0ABFPn6PIe2QUfqo=",
|
||||
"owner": "numtide",
|
||||
"repo": "nix-filter",
|
||||
"rev": "41fd48e00c22b4ced525af521ead8792402de0ea",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "nix-filter",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1691218994,
|
||||
"narHash": "sha256-46GJ5vLf9H+Oh7Jii2gJI9GATJHGbx2iQpon5nUSFPI=",
|
||||
"lastModified": 1695318763,
|
||||
"narHash": "sha256-FHVPDRP2AfvsxAdc+AsgFJevMz5VBmnZglFUMlxBkcY=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0d2fb29f5071a12d7983319c2c2576be6a130582",
|
||||
"rev": "e12483116b3b51a185a33a272bf351e357ba9a99",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -108,11 +123,11 @@
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"dir": "lib",
|
||||
"lastModified": 1690881714,
|
||||
"narHash": "sha256-h/nXluEqdiQHs1oSgkOOWF+j8gcJMWhwnZ9PFabN6q0=",
|
||||
"lastModified": 1693471703,
|
||||
"narHash": "sha256-0l03ZBL8P1P6z8MaSDS/MvuU8E75rVxe5eE1N6gxeTo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9e1960bc196baf6881340d53dccb203a951745a2",
|
||||
"rev": "3e52e76b70d5508f3cec70b882a29199f4d1ee85",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -138,11 +153,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1691256628,
|
||||
"narHash": "sha256-M0YXHemR3zbyhM7PvJa5lzGhWVf6kM/fpZ4cWe/VIhI=",
|
||||
"lastModified": 1694364351,
|
||||
"narHash": "sha256-oadhSCqopYXxURwIA6/Anpe5IAG11q2LhvTJNP5zE6o=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "3139c4d1f7732cab89f06492bdd4677b877e3785",
|
||||
"rev": "4f883a76282bc28eb952570afc3d8a1bf6f481d7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -156,6 +171,7 @@
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-parts": "flake-parts",
|
||||
"libnbtplusplus": "libnbtplusplus",
|
||||
"nix-filter": "nix-filter",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
}
|
||||
|
25
flake.nix
25
flake.nix
@ -4,6 +4,7 @@
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
nix-filter.url = "github:numtide/nix-filter";
|
||||
pre-commit-hooks = {
|
||||
url = "github:cachix/pre-commit-hooks.nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
@ -20,8 +21,24 @@
|
||||
};
|
||||
};
|
||||
|
||||
outputs = inputs:
|
||||
inputs.flake-parts.lib.mkFlake
|
||||
{inherit inputs;}
|
||||
{imports = [./nix];};
|
||||
outputs = {
|
||||
flake-parts,
|
||||
pre-commit-hooks,
|
||||
...
|
||||
} @ inputs:
|
||||
flake-parts.lib.mkFlake {inherit inputs;} {
|
||||
imports = [
|
||||
pre-commit-hooks.flakeModule
|
||||
|
||||
./nix/dev.nix
|
||||
./nix/distribution.nix
|
||||
];
|
||||
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
};
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
* Copyright (C) 2022 Tayou <git@tayou.org>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -136,11 +135,7 @@
|
||||
#endif
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include "WindowsConsole.h"
|
||||
#endif
|
||||
|
||||
#define STRINGIFY(x) #x
|
||||
@ -172,22 +167,8 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt
|
||||
Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
{
|
||||
#if defined Q_OS_WIN32
|
||||
// attach the parent console
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||
// if attach succeeds, reopen and sync all the i/o
|
||||
if (freopen("CON", "w", stdout)) {
|
||||
std::cout.sync_with_stdio();
|
||||
}
|
||||
if (freopen("CON", "w", stderr)) {
|
||||
std::cerr.sync_with_stdio();
|
||||
}
|
||||
if (freopen("CON", "r", stdin)) {
|
||||
std::cin.sync_with_stdio();
|
||||
}
|
||||
auto out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD written;
|
||||
const char* endline = "\n";
|
||||
WriteConsole(out, endline, strlen(endline), &written, NULL);
|
||||
// attach the parent console if stdout not already captured
|
||||
if (AttachWindowsConsole()) {
|
||||
consoleAttached = true;
|
||||
}
|
||||
#endif
|
||||
@ -212,8 +193,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
|
||||
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
|
||||
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
|
||||
{ { "I", "import" }, "Import instance from specified zip (local path or URL)", "file" },
|
||||
{ { "I", "import" }, "Import instance or resource from specified local path or URL", "url" },
|
||||
{ "show", "Opens the window for the specified instance (by instance ID)", "show" } });
|
||||
// Has to be positional for some OS to handle that properly
|
||||
parser.addPositionalArgument("URL", "Import the resource(s) at the given URL(s) (same as -I / --import)", "[URL...]");
|
||||
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
|
||||
@ -226,13 +210,13 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
|
||||
m_instanceIdToShowWindowOf = parser.value("show");
|
||||
|
||||
for (auto zip_path : parser.values("import")) {
|
||||
m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath()));
|
||||
for (auto url : parser.values("import")) {
|
||||
m_urlsToImport.append(normalizeImportUrl(url));
|
||||
}
|
||||
|
||||
// treat unspecified positional arguments as import urls
|
||||
for (auto zip_path : parser.positionalArguments()) {
|
||||
m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath()));
|
||||
for (auto url : parser.positionalArguments()) {
|
||||
m_urlsToImport.append(normalizeImportUrl(url));
|
||||
}
|
||||
|
||||
// error if --launch is missing with --server or --profile
|
||||
@ -331,11 +315,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
activate.command = "activate";
|
||||
m_peerInstance->sendMessage(activate.serialize(), timeout);
|
||||
|
||||
if (!m_zipsToImport.isEmpty()) {
|
||||
for (auto zip_url : m_zipsToImport) {
|
||||
if (!m_urlsToImport.isEmpty()) {
|
||||
for (auto url : m_urlsToImport) {
|
||||
ApplicationMessage import;
|
||||
import.command = "import";
|
||||
import.args.insert("path", zip_url.toString());
|
||||
import.args.insert("url", url.toString());
|
||||
m_peerInstance->sendMessage(import.serialize(), timeout);
|
||||
}
|
||||
}
|
||||
@ -510,7 +494,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_settings.reset(new INISettingsObject({ BuildConfig.LAUNCHER_CONFIGFILE, "polymc.cfg", "multimc.cfg" }, this));
|
||||
|
||||
// Theming
|
||||
m_settings->registerSetting("IconTheme", QString("pe_colored"));
|
||||
m_settings->registerSetting("IconTheme", QString());
|
||||
m_settings->registerSetting("ApplicationTheme", QString());
|
||||
m_settings->registerSetting("BackgroundCat", QString("kitteh"));
|
||||
|
||||
@ -595,12 +579,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_settings->registerSetting("IgnoreJavaCompatibility", false);
|
||||
m_settings->registerSetting("IgnoreJavaWizard", false);
|
||||
|
||||
// Mod loader settings
|
||||
m_settings->registerSetting("DisableQuiltBeacon", false);
|
||||
|
||||
// Native library workarounds
|
||||
m_settings->registerSetting("UseNativeOpenAL", false);
|
||||
m_settings->registerSetting("CustomOpenALPath", "");
|
||||
m_settings->registerSetting("UseNativeGLFW", false);
|
||||
m_settings->registerSetting("CustomGLFWPath", "");
|
||||
|
||||
// Peformance related options
|
||||
m_settings->registerSetting("EnableFeralGamemode", false);
|
||||
@ -611,6 +594,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_settings->registerSetting("ShowGameTime", true);
|
||||
m_settings->registerSetting("ShowGlobalGameTime", true);
|
||||
m_settings->registerSetting("RecordGameTime", true);
|
||||
m_settings->registerSetting("ShowGameTimeWithoutDays", false);
|
||||
|
||||
// Minecraft mods
|
||||
m_settings->registerSetting("ModMetadataDisabled", false);
|
||||
@ -772,7 +756,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
}
|
||||
|
||||
// Themes
|
||||
m_themeManager = std::make_unique<ThemeManager>(m_mainWindow);
|
||||
m_themeManager = std::make_unique<ThemeManager>();
|
||||
|
||||
// initialize and load all instances
|
||||
{
|
||||
@ -858,14 +842,15 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
}
|
||||
});
|
||||
|
||||
applyCurrentlySelectedTheme(true);
|
||||
|
||||
updateCapabilities();
|
||||
|
||||
detectLibraries();
|
||||
|
||||
if (createSetupWizard()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_themeManager->applyCurrentlySelectedTheme(true);
|
||||
performMainStartupAction();
|
||||
}
|
||||
|
||||
@ -891,10 +876,20 @@ bool Application::createSetupWizard()
|
||||
}();
|
||||
bool languageRequired = settings()->get("Language").toString().isEmpty();
|
||||
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
||||
bool themeInterventionRequired = settings()->get("ApplicationTheme") == "";
|
||||
bool validWidgets = m_themeManager->isValidApplicationTheme(settings()->get("ApplicationTheme").toString());
|
||||
bool validIcons = m_themeManager->isValidIconTheme(settings()->get("IconTheme").toString());
|
||||
bool themeInterventionRequired = !validWidgets || !validIcons;
|
||||
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
|
||||
|
||||
if (wizardRequired) {
|
||||
// set default theme after going into theme wizard
|
||||
if (!validIcons)
|
||||
settings()->set("IconTheme", QString("pe_colored"));
|
||||
if (!validWidgets)
|
||||
settings()->set("ApplicationTheme", QString("system"));
|
||||
|
||||
m_themeManager->applyCurrentlySelectedTheme(true);
|
||||
|
||||
m_setupWizard = new SetupWizard(nullptr);
|
||||
if (languageRequired) {
|
||||
m_setupWizard->addPage(new LanguageWizardPage(m_setupWizard));
|
||||
@ -909,9 +904,9 @@ bool Application::createSetupWizard()
|
||||
}
|
||||
|
||||
if (themeInterventionRequired) {
|
||||
settings()->set("ApplicationTheme", QString("system")); // set default theme after going into theme wizard
|
||||
m_setupWizard->addPage(new ThemeWizardPage(m_setupWizard));
|
||||
}
|
||||
|
||||
connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished);
|
||||
m_setupWizard->show();
|
||||
return true;
|
||||
@ -970,7 +965,7 @@ void Application::performMainStartupAction()
|
||||
qDebug() << " Launching with account" << m_profileToUse;
|
||||
}
|
||||
|
||||
launch(inst, true, false, nullptr, serverToJoin, accountToUse);
|
||||
launch(inst, true, false, serverToJoin, accountToUse);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -987,9 +982,9 @@ void Application::performMainStartupAction()
|
||||
showMainWindow(false);
|
||||
qDebug() << "<> Main window shown.";
|
||||
}
|
||||
if (!m_zipsToImport.isEmpty()) {
|
||||
qDebug() << "<> Importing from zip:" << m_zipsToImport;
|
||||
m_mainWindow->processURLs(m_zipsToImport);
|
||||
if (!m_urlsToImport.isEmpty()) {
|
||||
qDebug() << "<> Importing from url:" << m_urlsToImport;
|
||||
m_mainWindow->processURLs(m_urlsToImport);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1031,12 +1026,12 @@ void Application::messageReceived(const QByteArray& message)
|
||||
if (command == "activate") {
|
||||
showMainWindow();
|
||||
} else if (command == "import") {
|
||||
QString path = received.args["path"];
|
||||
if (path.isEmpty()) {
|
||||
QString url = received.args["url"];
|
||||
if (url.isEmpty()) {
|
||||
qWarning() << "Received" << command << "message without a zip path/URL.";
|
||||
return;
|
||||
}
|
||||
m_mainWindow->processURLs({ QUrl::fromLocalFile(QFileInfo(path).absoluteFilePath()) });
|
||||
m_mainWindow->processURLs({ normalizeImportUrl(url) });
|
||||
} else if (command == "launch") {
|
||||
QString id = received.args["id"];
|
||||
QString server = received.args["server"];
|
||||
@ -1069,7 +1064,7 @@ void Application::messageReceived(const QByteArray& message)
|
||||
}
|
||||
}
|
||||
|
||||
launch(instance, true, false, nullptr, serverObject, accountObject);
|
||||
launch(instance, true, false, serverObject, accountObject);
|
||||
} else {
|
||||
qWarning() << "Received invalid message" << message;
|
||||
}
|
||||
@ -1088,26 +1083,6 @@ std::shared_ptr<JavaInstallList> Application::javalist()
|
||||
return m_javalist;
|
||||
}
|
||||
|
||||
QList<ITheme*> Application::getValidApplicationThemes()
|
||||
{
|
||||
return m_themeManager->getValidApplicationThemes();
|
||||
}
|
||||
|
||||
void Application::applyCurrentlySelectedTheme(bool initial)
|
||||
{
|
||||
m_themeManager->applyCurrentlySelectedTheme(initial);
|
||||
}
|
||||
|
||||
void Application::setApplicationTheme(const QString& name)
|
||||
{
|
||||
m_themeManager->setApplicationTheme(name);
|
||||
}
|
||||
|
||||
void Application::setIconTheme(const QString& name)
|
||||
{
|
||||
m_themeManager->setIconTheme(name);
|
||||
}
|
||||
|
||||
QIcon Application::getThemedIcon(const QString& name)
|
||||
{
|
||||
if (name == "logo") {
|
||||
@ -1116,16 +1091,6 @@ QIcon Application::getThemedIcon(const QString& name)
|
||||
return QIcon::fromTheme(name);
|
||||
}
|
||||
|
||||
QList<CatPack*> Application::getValidCatPacks()
|
||||
{
|
||||
return m_themeManager->getValidCatPacks();
|
||||
}
|
||||
|
||||
QString Application::getCatPack(QString catName)
|
||||
{
|
||||
return m_themeManager->getCatPack(catName);
|
||||
}
|
||||
|
||||
bool Application::openJsonEditor(const QString& filename)
|
||||
{
|
||||
const QString file = QDir::current().absoluteFilePath(filename);
|
||||
@ -1140,7 +1105,6 @@ bool Application::openJsonEditor(const QString& filename)
|
||||
bool Application::launch(InstancePtr instance,
|
||||
bool online,
|
||||
bool demo,
|
||||
BaseProfilerFactory* profiler,
|
||||
MinecraftServerTargetPtr serverToJoin,
|
||||
MinecraftAccountPtr accountToUse)
|
||||
{
|
||||
@ -1148,7 +1112,7 @@ bool Application::launch(InstancePtr instance,
|
||||
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
|
||||
} else if (instance->canLaunch()) {
|
||||
auto& extras = m_instanceExtras[instance->id()];
|
||||
auto& window = extras.window;
|
||||
auto window = extras.window;
|
||||
if (window) {
|
||||
if (!window->saveAll()) {
|
||||
return false;
|
||||
@ -1159,7 +1123,7 @@ bool Application::launch(InstancePtr instance,
|
||||
controller->setInstance(instance);
|
||||
controller->setOnline(online);
|
||||
controller->setDemo(demo);
|
||||
controller->setProfiler(profiler);
|
||||
controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
|
||||
controller->setServerToJoin(serverToJoin);
|
||||
controller->setAccountToUse(accountToUse);
|
||||
if (window) {
|
||||
@ -1451,6 +1415,15 @@ void Application::updateCapabilities()
|
||||
#endif
|
||||
}
|
||||
|
||||
void Application::detectLibraries()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
m_detectedGLFWPath = MangoHud::findLibrary(BuildConfig.GLFW_LIBRARY_NAME);
|
||||
m_detectedOpenALPath = MangoHud::findLibrary(BuildConfig.OPENAL_LIBRARY_NAME);
|
||||
qDebug() << "Detected native libraries:" << m_detectedGLFWPath << m_detectedOpenALPath;
|
||||
#endif
|
||||
}
|
||||
|
||||
QString Application::getJarPath(QString jarFile)
|
||||
{
|
||||
QStringList potentialPaths = {
|
||||
@ -1629,3 +1602,13 @@ void Application::triggerUpdateCheck()
|
||||
qDebug() << "Updater not available.";
|
||||
}
|
||||
}
|
||||
|
||||
QUrl Application::normalizeImportUrl(QString const& url)
|
||||
{
|
||||
auto local_file = QFileInfo(url);
|
||||
if (local_file.exists()) {
|
||||
return QUrl::fromLocalFile(local_file.absoluteFilePath());
|
||||
} else {
|
||||
return QUrl::fromUserInput(url);
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ class TranslationsModel;
|
||||
class ITheme;
|
||||
class MCEditTool;
|
||||
class ThemeManager;
|
||||
class IconTheme;
|
||||
|
||||
namespace Meta {
|
||||
class Index;
|
||||
@ -109,17 +110,7 @@ class Application : public QApplication {
|
||||
|
||||
QIcon getThemedIcon(const QString& name);
|
||||
|
||||
void setIconTheme(const QString& name);
|
||||
|
||||
void applyCurrentlySelectedTheme(bool initial = false);
|
||||
|
||||
QList<ITheme*> getValidApplicationThemes();
|
||||
|
||||
void setApplicationTheme(const QString& name);
|
||||
|
||||
QList<CatPack*> getValidCatPacks();
|
||||
|
||||
QString getCatPack(QString catName = "");
|
||||
ThemeManager* themeManager() { return m_themeManager.get(); }
|
||||
|
||||
shared_qobject_ptr<ExternalUpdater> updater() { return m_updater; }
|
||||
|
||||
@ -151,6 +142,8 @@ class Application : public QApplication {
|
||||
|
||||
void updateCapabilities();
|
||||
|
||||
void detectLibraries();
|
||||
|
||||
/*!
|
||||
* Finds and returns the full path to a jar file.
|
||||
* Returns a null-string if it could not be found.
|
||||
@ -186,6 +179,8 @@ class Application : public QApplication {
|
||||
|
||||
int suitableMaxMem();
|
||||
|
||||
QUrl normalizeImportUrl(QString const& url);
|
||||
|
||||
signals:
|
||||
void updateAllowedChanged(bool status);
|
||||
void globalSettingsAboutToOpen();
|
||||
@ -200,7 +195,6 @@ class Application : public QApplication {
|
||||
bool launch(InstancePtr instance,
|
||||
bool online = true,
|
||||
bool demo = false,
|
||||
BaseProfilerFactory* profiler = nullptr,
|
||||
MinecraftServerTargetPtr serverToJoin = nullptr,
|
||||
MinecraftAccountPtr accountToUse = nullptr);
|
||||
bool kill(InstancePtr instance);
|
||||
@ -284,11 +278,13 @@ class Application : public QApplication {
|
||||
SetupWizard* m_setupWizard = nullptr;
|
||||
|
||||
public:
|
||||
QString m_detectedGLFWPath;
|
||||
QString m_detectedOpenALPath;
|
||||
QString m_instanceIdToLaunch;
|
||||
QString m_serverToJoin;
|
||||
QString m_profileToUse;
|
||||
bool m_liveCheck = false;
|
||||
QList<QUrl> m_zipsToImport;
|
||||
QList<QUrl> m_urlsToImport;
|
||||
QString m_instanceIdToShowWindowOf;
|
||||
std::unique_ptr<QFile> logFile;
|
||||
};
|
||||
|
@ -3,6 +3,7 @@
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* 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
|
||||
@ -100,6 +101,8 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
||||
m_settings->registerSetting("ManagedPackName", "");
|
||||
m_settings->registerSetting("ManagedPackVersionID", "");
|
||||
m_settings->registerSetting("ManagedPackVersionName", "");
|
||||
|
||||
m_settings->registerSetting("Profiler", "");
|
||||
}
|
||||
|
||||
QString BaseInstance::getPreLaunchCommand()
|
||||
|
@ -3,6 +3,7 @@
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -38,6 +39,7 @@
|
||||
#include <cassert>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QMenu>
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
#include <QSet>
|
||||
@ -86,7 +88,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||
|
||||
public:
|
||||
/// virtual destructor to make sure the destruction is COMPLETE
|
||||
virtual ~BaseInstance(){};
|
||||
virtual ~BaseInstance() {}
|
||||
|
||||
virtual void saveNow() = 0;
|
||||
|
||||
@ -146,7 +148,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||
void copyManagedPack(BaseInstance& other);
|
||||
|
||||
/// guess log level from a line of game log
|
||||
virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString& line, MessageLevel::Enum level) { return level; };
|
||||
virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString& line, MessageLevel::Enum level) { return level; }
|
||||
|
||||
virtual QStringList extraArguments();
|
||||
|
||||
@ -246,6 +248,8 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||
virtual bool canEdit() const = 0;
|
||||
virtual bool canExport() const = 0;
|
||||
|
||||
virtual void populateLaunchMenu(QMenu* menu) = 0;
|
||||
|
||||
bool reloadSettings();
|
||||
|
||||
/**
|
||||
@ -267,7 +271,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||
protected:
|
||||
void changeStatus(Status newStatus);
|
||||
|
||||
SettingsObjectPtr globalSettings() const { return m_global_settings.lock(); };
|
||||
SettingsObjectPtr globalSettings() const { return m_global_settings.lock(); }
|
||||
|
||||
bool isSpecificSettingsLoaded() const { return m_specific_settings_loaded; }
|
||||
void setSpecificSettingsLoaded(bool loaded) { m_specific_settings_loaded = loaded; }
|
||||
@ -282,6 +286,8 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||
|
||||
void runningStatusChanged(bool running);
|
||||
|
||||
void profilerChanged();
|
||||
|
||||
void statusChanged(Status from, Status to);
|
||||
|
||||
protected slots:
|
||||
|
@ -43,9 +43,8 @@ class BaseVersion {
|
||||
* the kind of version this is (Stable, Beta, Snapshot, whatever)
|
||||
*/
|
||||
virtual QString typeString() const = 0;
|
||||
|
||||
virtual bool operator<(BaseVersion& a) { return name() < a.name(); };
|
||||
virtual bool operator>(BaseVersion& a) { return name() > a.name(); };
|
||||
virtual bool operator<(BaseVersion& a) { return name() < a.name(); }
|
||||
virtual bool operator>(BaseVersion& a) { return name() > a.name(); }
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(BaseVersion::Ptr)
|
||||
|
@ -136,6 +136,15 @@ set(NET_SOURCES
|
||||
net/Validator.h
|
||||
net/Upload.cpp
|
||||
net/Upload.h
|
||||
net/HeaderProxy.h
|
||||
net/RawHeaderProxy.h
|
||||
net/ApiHeaderProxy.h
|
||||
net/ApiDownload.h
|
||||
net/ApiDownload.cpp
|
||||
net/ApiUpload.cpp
|
||||
net/ApiUpload.h
|
||||
net/NetRequest.cpp
|
||||
net/NetRequest.h
|
||||
)
|
||||
|
||||
# Game launch logic
|
||||
@ -566,6 +575,9 @@ set(ATLAUNCHER_SOURCES
|
||||
)
|
||||
|
||||
set(LINKEXE_SOURCES
|
||||
WindowsConsole.cpp
|
||||
WindowsConsole.h
|
||||
|
||||
filelink/FileLink.h
|
||||
filelink/FileLink.cpp
|
||||
FileSystem.h
|
||||
@ -762,6 +774,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/themes/ITheme.h
|
||||
ui/themes/SystemTheme.cpp
|
||||
ui/themes/SystemTheme.h
|
||||
ui/themes/IconTheme.cpp
|
||||
ui/themes/IconTheme.h
|
||||
ui/themes/ThemeManager.cpp
|
||||
ui/themes/ThemeManager.h
|
||||
ui/themes/CatPack.cpp
|
||||
@ -962,6 +976,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/dialogs/ChooseProviderDialog.cpp
|
||||
ui/dialogs/ModUpdateDialog.cpp
|
||||
ui/dialogs/ModUpdateDialog.h
|
||||
ui/dialogs/InstallLoaderDialog.cpp
|
||||
ui/dialogs/InstallLoaderDialog.h
|
||||
|
||||
# GUI - widgets
|
||||
ui/widgets/Common.cpp
|
||||
@ -1026,6 +1042,14 @@ SET(LAUNCHER_SOURCES
|
||||
ui/instanceview/VisualGroup.h
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
set(LAUNCHER_SOURCES
|
||||
WindowsConsole.cpp
|
||||
WindowsConsole.h
|
||||
${LAUNCHER_SOURCES}
|
||||
)
|
||||
endif()
|
||||
|
||||
qt_wrap_ui(LAUNCHER_UI
|
||||
ui/MainWindow.ui
|
||||
ui/setupwizard/PasteWizardPage.ui
|
||||
@ -1109,8 +1133,15 @@ if(WIN32)
|
||||
set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC})
|
||||
endif()
|
||||
|
||||
include(CompilerWarnings)
|
||||
|
||||
# Add executable
|
||||
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
|
||||
set_project_warnings(Launcher_logic
|
||||
"${Launcher_MSVC_WARNINGS}"
|
||||
"${Launcher_CLANG_WARNINGS}"
|
||||
"${Launcher_GCC_WARNINGS}")
|
||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
||||
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(Launcher_logic
|
||||
systeminfo
|
||||
@ -1195,6 +1226,11 @@ install(TARGETS ${Launcher_Name}
|
||||
|
||||
if(WIN32)
|
||||
add_library(filelink_logic STATIC ${LINKEXE_SOURCES})
|
||||
set_project_warnings(filelink_logic
|
||||
"${Launcher_MSVC_WARNINGS}"
|
||||
"${Launcher_CLANG_WARNINGS}"
|
||||
"${Launcher_GCC_WARNINGS}")
|
||||
|
||||
target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(filelink_logic
|
||||
systeminfo
|
||||
|
@ -96,12 +96,12 @@ bool IndirectOpen(T callable, qint64* pid_forked = nullptr)
|
||||
#endif
|
||||
|
||||
namespace DesktopServices {
|
||||
bool openDirectory(const QString& path, bool ensureExists)
|
||||
bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists)
|
||||
{
|
||||
qDebug() << "Opening directory" << path;
|
||||
QDir parentPath;
|
||||
QDir dir(path);
|
||||
if (!dir.exists()) {
|
||||
if (ensureExists && !dir.exists()) {
|
||||
parentPath.mkpath(dir.absolutePath());
|
||||
}
|
||||
auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); };
|
||||
|
@ -267,10 +267,7 @@ bool FileIgnoreProxy::filterAcceptsRow(int sourceRow, const QModelIndex& sourceP
|
||||
|
||||
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);
|
||||
return m_ignoreFiles.contains(fileInfo.fileName()) || m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath()));
|
||||
}
|
||||
|
||||
bool FileIgnoreProxy::filterFile(const QString& fileName) const
|
||||
|
@ -112,8 +112,8 @@ class copy : public QObject {
|
||||
|
||||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||
|
||||
int totalCopied() { return m_copied; }
|
||||
int totalFailed() { return m_failedPaths.length(); }
|
||||
qsizetype totalCopied() { return m_copied; }
|
||||
qsizetype totalFailed() { return m_failedPaths.length(); }
|
||||
QStringList failed() { return m_failedPaths; }
|
||||
|
||||
signals:
|
||||
@ -130,7 +130,7 @@ class copy : public QObject {
|
||||
bool m_whitelist = false;
|
||||
QDir m_src;
|
||||
QDir m_dst;
|
||||
int m_copied;
|
||||
qsizetype m_copied;
|
||||
QStringList m_failedPaths;
|
||||
};
|
||||
|
||||
@ -474,8 +474,8 @@ class clone : public QObject {
|
||||
|
||||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||
|
||||
int totalCloned() { return m_cloned; }
|
||||
int totalFailed() { return m_failedClones.length(); }
|
||||
qsizetype totalCloned() { return m_cloned; }
|
||||
qsizetype totalFailed() { return m_failedClones.length(); }
|
||||
|
||||
QList<QPair<QString, QString>> failed() { return m_failedClones; }
|
||||
|
||||
@ -491,7 +491,7 @@ class clone : public QObject {
|
||||
bool m_whitelist = false;
|
||||
QDir m_src;
|
||||
QDir m_dst;
|
||||
int m_cloned;
|
||||
qsizetype m_cloned;
|
||||
QList<QPair<QString, QString>> m_failedClones;
|
||||
};
|
||||
|
||||
|
@ -16,6 +16,12 @@ bool ExactFilter::accepts(const QString& value)
|
||||
return value == pattern;
|
||||
}
|
||||
|
||||
ExactIfPresentFilter::ExactIfPresentFilter(const QString& pattern) : pattern(pattern) {}
|
||||
bool ExactIfPresentFilter::accepts(const QString& value)
|
||||
{
|
||||
return value.isEmpty() || value == pattern;
|
||||
}
|
||||
|
||||
RegexpFilter::RegexpFilter(const QString& regexp, bool invert) : invert(invert)
|
||||
{
|
||||
pattern.setPattern(regexp);
|
||||
|
@ -29,6 +29,16 @@ class ExactFilter : public Filter {
|
||||
QString pattern;
|
||||
};
|
||||
|
||||
class ExactIfPresentFilter : public Filter {
|
||||
public:
|
||||
ExactIfPresentFilter(const QString& pattern);
|
||||
~ExactIfPresentFilter() override = default;
|
||||
bool accepts(const QString& value) override;
|
||||
|
||||
private:
|
||||
QString pattern;
|
||||
};
|
||||
|
||||
class RegexpFilter : public Filter {
|
||||
public:
|
||||
RegexpFilter(const QString& regexp, bool invert);
|
||||
|
@ -50,6 +50,9 @@
|
||||
#include "modplatform/technic/TechnicPackProcessor.h"
|
||||
|
||||
#include "settings/INISettingsObject.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
#include "net/ApiDownload.h"
|
||||
|
||||
#include <QtConcurrentRun>
|
||||
#include <algorithm>
|
||||
@ -88,25 +91,27 @@ void InstanceImportTask::executeTask()
|
||||
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
||||
m_downloadRequired = true;
|
||||
|
||||
const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path());
|
||||
|
||||
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
||||
entry->setStale(true);
|
||||
m_archivePath = entry->getFullPath();
|
||||
|
||||
m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
|
||||
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
|
||||
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
||||
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
|
||||
|
||||
m_filesNetJob->start();
|
||||
downloadFromUrl();
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceImportTask::downloadFromUrl()
|
||||
{
|
||||
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
|
||||
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
||||
entry->setStale(true);
|
||||
m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
|
||||
m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
|
||||
m_archivePath = entry->getFullPath();
|
||||
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
||||
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
|
||||
m_filesNetJob->start();
|
||||
}
|
||||
|
||||
void InstanceImportTask::downloadSucceeded()
|
||||
{
|
||||
processZipPack();
|
||||
|
@ -101,4 +101,5 @@ class InstanceImportTask : public InstanceTask {
|
||||
|
||||
// FIXME: nuke
|
||||
QWidget* m_parent;
|
||||
void downloadFromUrl();
|
||||
};
|
||||
|
@ -96,7 +96,11 @@ Qt::DropActions InstanceList::supportedDropActions() const
|
||||
return Qt::MoveAction;
|
||||
}
|
||||
|
||||
bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const
|
||||
bool InstanceList::canDropMimeData(const QMimeData* data,
|
||||
[[maybe_unused]] Qt::DropAction action,
|
||||
[[maybe_unused]] int row,
|
||||
[[maybe_unused]] int column,
|
||||
[[maybe_unused]] const QModelIndex& parent) const
|
||||
{
|
||||
if (data && data->hasFormat("application/x-instanceid")) {
|
||||
return true;
|
||||
@ -104,7 +108,11 @@ bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InstanceList::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
|
||||
bool InstanceList::dropMimeData(const QMimeData* data,
|
||||
[[maybe_unused]] Qt::DropAction action,
|
||||
[[maybe_unused]] int row,
|
||||
[[maybe_unused]] int column,
|
||||
[[maybe_unused]] const QModelIndex& parent)
|
||||
{
|
||||
if (data && data->hasFormat("application/x-instanceid")) {
|
||||
return true;
|
||||
@ -751,7 +759,7 @@ void InstanceList::instanceDirContentsChanged(const QString& path)
|
||||
emit instancesChanged();
|
||||
}
|
||||
|
||||
void InstanceList::on_InstFolderChanged(const Setting& setting, QVariant value)
|
||||
void InstanceList::on_InstFolderChanged([[maybe_unused]] const Setting& setting, QVariant value)
|
||||
{
|
||||
QString newInstDir = QDir(value.toString()).canonicalPath();
|
||||
if (newInstDir != m_instDir) {
|
||||
@ -789,7 +797,7 @@ class InstanceStaging : public Task {
|
||||
, m_groupName(std::move(groupName))
|
||||
{
|
||||
m_child.reset(child);
|
||||
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceded);
|
||||
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceeded);
|
||||
connect(child, &Task::failed, this, &InstanceStaging::childFailed);
|
||||
connect(child, &Task::aborted, this, &InstanceStaging::childAborted);
|
||||
connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable);
|
||||
@ -797,7 +805,7 @@ class InstanceStaging : public Task {
|
||||
connect(child, &Task::details, this, &InstanceStaging::setDetails);
|
||||
connect(child, &Task::progress, this, &InstanceStaging::setProgress);
|
||||
connect(child, &Task::stepProgress, this, &InstanceStaging::propagateStepProgress);
|
||||
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded);
|
||||
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceeded);
|
||||
}
|
||||
|
||||
virtual ~InstanceStaging(){};
|
||||
@ -819,7 +827,7 @@ class InstanceStaging : public Task {
|
||||
QStringList warnings() const override { return m_child->warnings(); }
|
||||
|
||||
private slots:
|
||||
void childSucceded()
|
||||
void childSucceeded()
|
||||
{
|
||||
unsigned sleepTime = backoff();
|
||||
if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get())) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* 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
|
||||
@ -361,22 +362,21 @@ void LaunchController::readyForLaunch()
|
||||
QString error;
|
||||
if (!m_profiler->check(&error)) {
|
||||
m_launcher->abort();
|
||||
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't start profiler: %1").arg(error));
|
||||
emitFailed("Profiler startup failed!");
|
||||
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Profiler check for %1 failed: %2").arg(m_profiler->name(), error));
|
||||
return;
|
||||
}
|
||||
BaseProfiler* profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this);
|
||||
|
||||
connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString& message) {
|
||||
QMessageBox msg;
|
||||
QMessageBox msg(m_parentWidget);
|
||||
msg.setText(tr("The game launch is delayed until you press the "
|
||||
"button. This is the right time to setup the profiler, as the "
|
||||
"profiler server is running now.\n\n%1")
|
||||
.arg(message));
|
||||
msg.setWindowTitle(tr("Waiting."));
|
||||
msg.setIcon(QMessageBox::Information);
|
||||
msg.addButton(tr("Launch"), QMessageBox::AcceptRole);
|
||||
msg.setModal(true);
|
||||
msg.addButton(tr("&Launch"), QMessageBox::AcceptRole);
|
||||
msg.exec();
|
||||
m_launcher->proceed();
|
||||
});
|
||||
|
@ -65,14 +65,9 @@ QStringList LoggedProcess::reprocess(const QByteArray& data, QTextDecoder& decod
|
||||
m_leftover_line = "";
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, QString::SkipEmptyParts);
|
||||
#else
|
||||
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, Qt::SkipEmptyParts);
|
||||
#endif
|
||||
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed);
|
||||
|
||||
if (!str.endsWith(QChar::LineFeed))
|
||||
m_leftover_line = lines.takeLast();
|
||||
m_leftover_line = lines.takeLast();
|
||||
return lines;
|
||||
}
|
||||
|
||||
|
@ -16,19 +16,20 @@
|
||||
*/
|
||||
|
||||
#include <MMCTime.h>
|
||||
#include <qobject.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QTextStream>
|
||||
|
||||
QString Time::prettifyDuration(int64_t duration)
|
||||
QString Time::prettifyDuration(int64_t duration, bool noDays)
|
||||
{
|
||||
int seconds = (int)(duration % 60);
|
||||
duration /= 60;
|
||||
int minutes = (int)(duration % 60);
|
||||
duration /= 60;
|
||||
int hours = (int)(duration % 24);
|
||||
int days = (int)(duration / 24);
|
||||
int hours = (int)(noDays ? duration : (duration % 24));
|
||||
int days = (int)(noDays ? 0 : (duration / 24));
|
||||
if ((hours == 0) && (days == 0)) {
|
||||
return QObject::tr("%1min %2s").arg(minutes).arg(seconds);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
namespace Time {
|
||||
|
||||
QString prettifyDuration(int64_t duration);
|
||||
QString prettifyDuration(int64_t duration, bool noDays = false);
|
||||
|
||||
/**
|
||||
* @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms`.
|
||||
|
@ -16,6 +16,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
@ -26,6 +27,15 @@
|
||||
#include "Json.h"
|
||||
#include "MangoHud.h"
|
||||
|
||||
#ifdef __GLIBC__
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#define UNDEF_GNU_SOURCE
|
||||
#endif
|
||||
#include <dlfcn.h>
|
||||
#include <linux/limits.h>
|
||||
#endif
|
||||
|
||||
namespace MangoHud {
|
||||
|
||||
QString getLibraryString()
|
||||
@ -106,4 +116,37 @@ QString getLibraryString()
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString findLibrary(QString libName)
|
||||
{
|
||||
#ifdef __GLIBC__
|
||||
const char* library = libName.toLocal8Bit().constData();
|
||||
|
||||
void* handle = dlopen(library, RTLD_NOW);
|
||||
if (!handle) {
|
||||
qCritical() << "dlopen() failed:" << dlerror();
|
||||
return {};
|
||||
}
|
||||
|
||||
char path[PATH_MAX];
|
||||
if (dlinfo(handle, RTLD_DI_ORIGIN, path) == -1) {
|
||||
qCritical() << "dlinfo() failed:" << dlerror();
|
||||
dlclose(handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto fullPath = FS::PathCombine(QString(path), libName);
|
||||
|
||||
dlclose(handle);
|
||||
return fullPath;
|
||||
#else
|
||||
qWarning() << "MangoHud::findLibrary is not implemented on this platform";
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
} // namespace MangoHud
|
||||
|
||||
#ifdef UNDEF_GNU_SOURCE
|
||||
#undef _GNU_SOURCE
|
||||
#undef UNDEF_GNU_SOURCE
|
||||
#endif
|
||||
|
@ -24,4 +24,6 @@
|
||||
namespace MangoHud {
|
||||
|
||||
QString getLibraryString();
|
||||
}
|
||||
|
||||
QString findLibrary(QString libName);
|
||||
} // namespace MangoHud
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* 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
|
||||
@ -52,7 +53,7 @@ class NullInstance : public BaseInstance {
|
||||
QSet<QString> traits() const override { return {}; };
|
||||
QString instanceConfigFolder() const override { return instanceRoot(); };
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override { return nullptr; }
|
||||
shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override { return nullptr; }
|
||||
shared_qobject_ptr<Task> createUpdateTask([[maybe_unused]] Net::Mode mode) override { return nullptr; }
|
||||
QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); }
|
||||
QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); }
|
||||
QMap<QString, QString> getVariables() override { return QMap<QString, QString>(); }
|
||||
@ -62,6 +63,7 @@ class NullInstance : public BaseInstance {
|
||||
bool canExport() const override { return false; }
|
||||
bool canEdit() const override { return false; }
|
||||
bool canLaunch() const override { return false; }
|
||||
void populateLaunchMenu(QMenu* menu) override {}
|
||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override
|
||||
{
|
||||
QStringList out;
|
||||
|
@ -12,7 +12,7 @@ struct PatchProblem {
|
||||
|
||||
class ProblemProvider {
|
||||
public:
|
||||
virtual ~ProblemProvider(){};
|
||||
virtual ~ProblemProvider() {}
|
||||
virtual const QList<PatchProblem> getProblems() const = 0;
|
||||
virtual ProblemSeverity getProblemSeverity() const = 0;
|
||||
};
|
||||
|
@ -90,7 +90,7 @@ void RecursiveFileSystemWatcher::fileChange(const QString& path)
|
||||
{
|
||||
emit fileChanged(path);
|
||||
}
|
||||
void RecursiveFileSystemWatcher::directoryChange(const QString& path)
|
||||
void RecursiveFileSystemWatcher::directoryChange([[maybe_unused]] const QString& path)
|
||||
{
|
||||
setFiles(scanRecursive(m_root));
|
||||
}
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "minecraft/mod/ModFolderModel.h"
|
||||
#include "minecraft/mod/ResourceFolderModel.h"
|
||||
|
||||
#include "net/ApiDownload.h"
|
||||
|
||||
ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
||||
ModPlatform::IndexedVersion version,
|
||||
const std::shared_ptr<ResourceFolderModel> packs,
|
||||
@ -51,7 +53,7 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
||||
}
|
||||
}
|
||||
|
||||
m_filesNetJob->addNetAction(Net::Download::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename())));
|
||||
m_filesNetJob->addNetAction(Net::ApiDownload::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename())));
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded);
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged);
|
||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propagateStepProgress);
|
||||
|
@ -35,6 +35,11 @@ QPixmap getFaceFromCache(QString username, int height, int width)
|
||||
QPixmap skinTexture(fskin.fileName());
|
||||
if (!skinTexture.isNull()) {
|
||||
QPixmap skin = QPixmap(8, 8);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
skin.fill(QColorConstants::Transparent);
|
||||
#else
|
||||
skin.fill(QColor(0, 0, 0, 0));
|
||||
#endif
|
||||
QPainter painter(&skin);
|
||||
painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8));
|
||||
painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8));
|
||||
|
@ -16,6 +16,8 @@ class Usable {
|
||||
friend class UseLock;
|
||||
|
||||
public:
|
||||
virtual ~Usable() {}
|
||||
|
||||
std::size_t useCount() const { return m_useCount; }
|
||||
bool isInUse() const { return m_useCount > 0; }
|
||||
|
||||
|
@ -63,7 +63,7 @@ class Version {
|
||||
struct Section {
|
||||
explicit Section(QString fullString) : m_fullString(std::move(fullString))
|
||||
{
|
||||
int cutoff = m_fullString.size();
|
||||
qsizetype cutoff = m_fullString.size();
|
||||
for (int i = 0; i < m_fullString.size(); i++) {
|
||||
if (!m_fullString[i].isDigit()) {
|
||||
cutoff = i;
|
||||
@ -103,14 +103,8 @@ class Version {
|
||||
|
||||
QString m_fullString;
|
||||
|
||||
[[nodiscard]] inline bool isAppendix() const
|
||||
{
|
||||
return m_stringPart.startsWith('+');
|
||||
}
|
||||
[[nodiscard]] inline bool isPreRelease() const
|
||||
{
|
||||
return m_stringPart.startsWith('-') && m_stringPart.length() > 1;
|
||||
}
|
||||
[[nodiscard]] inline bool isAppendix() const { return m_stringPart.startsWith('+'); }
|
||||
[[nodiscard]] inline bool isPreRelease() const { return m_stringPart.startsWith('-') && m_stringPart.length() > 1; }
|
||||
|
||||
inline bool operator==(const Section& other) const
|
||||
{
|
||||
@ -156,14 +150,8 @@ class Version {
|
||||
return m_fullString < other.m_fullString;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Section& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
inline bool operator>(const Section& other) const
|
||||
{
|
||||
return !(*this < other || *this == other);
|
||||
}
|
||||
inline bool operator!=(const Section& other) const { return !(*this == other); }
|
||||
inline bool operator>(const Section& other) const { return !(*this < other || *this == other); }
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -194,12 +194,12 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
|
||||
switch (column) {
|
||||
case Name: {
|
||||
if (hasRecommended) {
|
||||
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
|
||||
if (value.toBool()) {
|
||||
auto recommenced = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
|
||||
if (recommenced.toBool()) {
|
||||
return APPLICATION->getThemedIcon("star");
|
||||
} else if (hasLatest) {
|
||||
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
|
||||
if (value.toBool()) {
|
||||
auto latest = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
|
||||
if (latest.toBool()) {
|
||||
return APPLICATION->getThemedIcon("bug");
|
||||
}
|
||||
}
|
||||
@ -228,7 +228,7 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex VersionProxyModel::parent(const QModelIndex& child) const
|
||||
QModelIndex VersionProxyModel::parent([[maybe_unused]] const QModelIndex& child) const
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
@ -408,7 +408,9 @@ void VersionProxyModel::sourceRowsAboutToBeInserted(const QModelIndex& parent, i
|
||||
beginInsertRows(parent, first, last);
|
||||
}
|
||||
|
||||
void VersionProxyModel::sourceRowsInserted(const QModelIndex& parent, int first, int last)
|
||||
void VersionProxyModel::sourceRowsInserted([[maybe_unused]] const QModelIndex& parent,
|
||||
[[maybe_unused]] int first,
|
||||
[[maybe_unused]] int last)
|
||||
{
|
||||
endInsertRows();
|
||||
}
|
||||
@ -418,7 +420,7 @@ void VersionProxyModel::sourceRowsAboutToBeRemoved(const QModelIndex& parent, in
|
||||
beginRemoveRows(parent, first, last);
|
||||
}
|
||||
|
||||
void VersionProxyModel::sourceRowsRemoved(const QModelIndex& parent, int first, int last)
|
||||
void VersionProxyModel::sourceRowsRemoved([[maybe_unused]] const QModelIndex& parent, [[maybe_unused]] int first, [[maybe_unused]] int last)
|
||||
{
|
||||
endRemoveRows();
|
||||
}
|
||||
|
128
launcher/WindowsConsole.cpp
Normal file
128
launcher/WindowsConsole.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <iostream>
|
||||
|
||||
void RedirectHandle(DWORD handle, FILE* stream, const char* mode)
|
||||
{
|
||||
HANDLE stdHandle = GetStdHandle(handle);
|
||||
if (stdHandle != INVALID_HANDLE_VALUE) {
|
||||
int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT);
|
||||
if (fileDescriptor != -1) {
|
||||
FILE* file = _fdopen(fileDescriptor, mode);
|
||||
if (file != NULL) {
|
||||
int dup2Result = _dup2(_fileno(file), _fileno(stream));
|
||||
if (dup2Result == 0) {
|
||||
setvbuf(stream, NULL, _IONBF, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// taken from https://stackoverflow.com/a/25927081
|
||||
// getting a proper output to console with redirection support on windows is apparently hell
|
||||
void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr)
|
||||
{
|
||||
// Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been
|
||||
// observed that the file number of our standard handle file objects can be assigned internally to a value of -2
|
||||
// when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our
|
||||
// call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value
|
||||
// before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to
|
||||
// use the "nul" device, which will place them into a valid state, after which we can redirect them to our target
|
||||
// using the "_dup2" function.
|
||||
if (bindStdIn) {
|
||||
FILE* dummyFile;
|
||||
freopen_s(&dummyFile, "nul", "r", stdin);
|
||||
}
|
||||
if (bindStdOut) {
|
||||
FILE* dummyFile;
|
||||
freopen_s(&dummyFile, "nul", "w", stdout);
|
||||
}
|
||||
if (bindStdErr) {
|
||||
FILE* dummyFile;
|
||||
freopen_s(&dummyFile, "nul", "w", stderr);
|
||||
}
|
||||
|
||||
// Redirect unbuffered stdin from the current standard input handle
|
||||
if (bindStdIn) {
|
||||
RedirectHandle(STD_INPUT_HANDLE, stdin, "r");
|
||||
}
|
||||
|
||||
// Redirect unbuffered stdout to the current standard output handle
|
||||
if (bindStdOut) {
|
||||
RedirectHandle(STD_OUTPUT_HANDLE, stdout, "w");
|
||||
}
|
||||
|
||||
// Redirect unbuffered stderr to the current standard error handle
|
||||
if (bindStdErr) {
|
||||
RedirectHandle(STD_ERROR_HANDLE, stderr, "w");
|
||||
}
|
||||
|
||||
// Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the
|
||||
// standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In
|
||||
// versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything
|
||||
// has been read from or written to the targets or not.
|
||||
if (bindStdIn) {
|
||||
std::wcin.clear();
|
||||
std::cin.clear();
|
||||
}
|
||||
if (bindStdOut) {
|
||||
std::wcout.clear();
|
||||
std::cout.clear();
|
||||
}
|
||||
if (bindStdErr) {
|
||||
std::wcerr.clear();
|
||||
std::cerr.clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool AttachWindowsConsole()
|
||||
{
|
||||
auto stdinType = GetFileType(GetStdHandle(STD_INPUT_HANDLE));
|
||||
auto stdoutType = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE));
|
||||
auto stderrType = GetFileType(GetStdHandle(STD_ERROR_HANDLE));
|
||||
|
||||
bool bindStdIn = false;
|
||||
bool bindStdOut = false;
|
||||
bool bindStdErr = false;
|
||||
|
||||
if (stdinType == FILE_TYPE_CHAR || stdinType == FILE_TYPE_UNKNOWN) {
|
||||
bindStdIn = true;
|
||||
}
|
||||
if (stdoutType == FILE_TYPE_CHAR || stdoutType == FILE_TYPE_UNKNOWN) {
|
||||
bindStdOut = true;
|
||||
}
|
||||
if (stderrType == FILE_TYPE_CHAR || stderrType == FILE_TYPE_UNKNOWN) {
|
||||
bindStdErr = true;
|
||||
}
|
||||
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||
BindCrtHandlesToStdHandles(bindStdIn, bindStdOut, bindStdErr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
25
launcher/WindowsConsole.h
Normal file
25
launcher/WindowsConsole.h
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr);
|
||||
bool AttachWindowsConsole();
|
@ -37,11 +37,7 @@
|
||||
#include <sys.h>
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include "WindowsConsole.h"
|
||||
#endif
|
||||
|
||||
// Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header
|
||||
@ -67,21 +63,7 @@ FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv),
|
||||
{
|
||||
#if defined Q_OS_WIN32
|
||||
// attach the parent console
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||
// if attach succeeds, reopen and sync all the i/o
|
||||
if (freopen("CON", "w", stdout)) {
|
||||
std::cout.sync_with_stdio();
|
||||
}
|
||||
if (freopen("CON", "w", stderr)) {
|
||||
std::cerr.sync_with_stdio();
|
||||
}
|
||||
if (freopen("CON", "r", stdin)) {
|
||||
std::cin.sync_with_stdio();
|
||||
}
|
||||
auto out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD written;
|
||||
const char* endline = "\n";
|
||||
WriteConsole(out, endline, strlen(endline), &written, NULL);
|
||||
if (AttachWindowsConsole()) {
|
||||
consoleAttached = true;
|
||||
}
|
||||
#endif
|
||||
@ -188,7 +170,7 @@ void FileLinkApp::runLink()
|
||||
FS::LinkResult result = { src_path, dst_path, QString::fromStdString(os_err.message()), os_err.value() };
|
||||
m_path_results.append(result);
|
||||
} else {
|
||||
FS::LinkResult result = { src_path, dst_path };
|
||||
FS::LinkResult result = { src_path, dst_path, "", 0 };
|
||||
m_path_results.append(result);
|
||||
}
|
||||
}
|
||||
@ -248,7 +230,7 @@ void FileLinkApp::readPathPairs()
|
||||
in >> numLinks;
|
||||
qDebug() << "numLinks" << numLinks;
|
||||
|
||||
for (int i = 0; i < numLinks; i++) {
|
||||
for (quint32 i = 0; i < numLinks; i++) {
|
||||
FS::LinkPair pair;
|
||||
in >> pair.src;
|
||||
in >> pair.dst;
|
||||
@ -271,7 +253,6 @@ FileLinkApp::~FileLinkApp()
|
||||
fclose(stdout);
|
||||
fclose(stdin);
|
||||
fclose(stderr);
|
||||
FreeConsole();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -42,6 +43,7 @@
|
||||
#include <QMimeData>
|
||||
#include <QSet>
|
||||
#include <QUrl>
|
||||
#include "icons/IconUtils.h"
|
||||
|
||||
#define MAX_SIZE 1024
|
||||
|
||||
@ -128,7 +130,7 @@ void IconList::directoryChanged(const QString& path)
|
||||
|
||||
QString suffix = rmfile.suffix();
|
||||
// The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well
|
||||
if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif")
|
||||
if (!IconUtils::isIconSuffix(suffix))
|
||||
key = rmfile.fileName();
|
||||
|
||||
int idx = getIconIndex(key);
|
||||
@ -155,7 +157,7 @@ void IconList::directoryChanged(const QString& path)
|
||||
|
||||
QString suffix = addfile.suffix();
|
||||
// The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well
|
||||
if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif")
|
||||
if (!IconUtils::isIconSuffix(suffix))
|
||||
key = addfile.fileName();
|
||||
|
||||
if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased)) {
|
||||
@ -255,10 +257,7 @@ bool IconList::dropMimeData(const QMimeData* data,
|
||||
Qt::ItemFlags IconList::flags(const QModelIndex& index) const
|
||||
{
|
||||
Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
|
||||
if (index.isValid())
|
||||
return Qt::ItemIsDropEnabled | defaultFlags;
|
||||
else
|
||||
return Qt::ItemIsDropEnabled | defaultFlags;
|
||||
return Qt::ItemIsDropEnabled | defaultFlags;
|
||||
}
|
||||
|
||||
QVariant IconList::data(const QModelIndex& index, int role) const
|
||||
@ -290,19 +289,8 @@ int IconList::rowCount(const QModelIndex& parent) const
|
||||
|
||||
void IconList::installIcons(const QStringList& iconFiles)
|
||||
{
|
||||
for (QString file : iconFiles) {
|
||||
QFileInfo fileinfo(file);
|
||||
if (!fileinfo.isReadable() || !fileinfo.isFile())
|
||||
continue;
|
||||
QString target = FS::PathCombine(getDirectory(), fileinfo.fileName());
|
||||
|
||||
QString suffix = fileinfo.suffix();
|
||||
if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif")
|
||||
continue;
|
||||
|
||||
if (!QFile::copy(file, target))
|
||||
continue;
|
||||
}
|
||||
for (QString file : iconFiles)
|
||||
installIcon(file, {});
|
||||
}
|
||||
|
||||
void IconList::installIcon(const QString& file, const QString& name)
|
||||
@ -311,18 +299,17 @@ void IconList::installIcon(const QString& file, const QString& name)
|
||||
if (!fileinfo.isReadable() || !fileinfo.isFile())
|
||||
return;
|
||||
|
||||
QString target = FS::PathCombine(getDirectory(), name);
|
||||
if (!IconUtils::isIconSuffix(fileinfo.suffix()))
|
||||
return;
|
||||
|
||||
QString target = FS::PathCombine(getDirectory(), name.isEmpty() ? fileinfo.fileName() : name);
|
||||
QFile::copy(file, target);
|
||||
}
|
||||
|
||||
bool IconList::iconFileExists(const QString& key) const
|
||||
{
|
||||
auto iconEntry = icon(key);
|
||||
if (!iconEntry) {
|
||||
return false;
|
||||
}
|
||||
return iconEntry->has(IconType::FileBased);
|
||||
return iconEntry && iconEntry->has(IconType::FileBased);
|
||||
}
|
||||
|
||||
const MMCIcon* IconList::icon(const QString& key) const
|
||||
@ -335,18 +322,12 @@ const MMCIcon* IconList::icon(const QString& key) const
|
||||
|
||||
bool IconList::deleteIcon(const QString& key)
|
||||
{
|
||||
if (!iconFileExists(key))
|
||||
return false;
|
||||
|
||||
return QFile::remove(icon(key)->getFilePath());
|
||||
return iconFileExists(key) && QFile::remove(icon(key)->getFilePath());
|
||||
}
|
||||
|
||||
bool IconList::trashIcon(const QString& key)
|
||||
{
|
||||
if (!iconFileExists(key))
|
||||
return false;
|
||||
|
||||
return FS::trash(icon(key)->getFilePath(), nullptr);
|
||||
return iconFileExists(key) && FS::trash(icon(key)->getFilePath(), nullptr);
|
||||
}
|
||||
|
||||
bool IconList::addThemeIcon(const QString& key)
|
||||
@ -357,20 +338,19 @@ bool IconList::addThemeIcon(const QString& key)
|
||||
oldOne.replace(Builtin, key);
|
||||
dataChanged(index(*iter), index(*iter));
|
||||
return true;
|
||||
} else {
|
||||
// add a new icon
|
||||
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
||||
{
|
||||
MMCIcon mmc_icon;
|
||||
mmc_icon.m_name = key;
|
||||
mmc_icon.m_key = key;
|
||||
mmc_icon.replace(Builtin, key);
|
||||
icons.push_back(mmc_icon);
|
||||
name_index[key] = icons.size() - 1;
|
||||
}
|
||||
endInsertRows();
|
||||
return true;
|
||||
}
|
||||
// add a new icon
|
||||
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
||||
{
|
||||
MMCIcon mmc_icon;
|
||||
mmc_icon.m_name = key;
|
||||
mmc_icon.m_key = key;
|
||||
mmc_icon.replace(Builtin, key);
|
||||
icons.push_back(mmc_icon);
|
||||
name_index[key] = icons.size() - 1;
|
||||
}
|
||||
endInsertRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IconList::addIcon(const QString& key, const QString& name, const QString& path, const IconType type)
|
||||
@ -385,20 +365,19 @@ bool IconList::addIcon(const QString& key, const QString& name, const QString& p
|
||||
oldOne.replace(type, icon, path);
|
||||
dataChanged(index(*iter), index(*iter));
|
||||
return true;
|
||||
} else {
|
||||
// add a new icon
|
||||
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
||||
{
|
||||
MMCIcon mmc_icon;
|
||||
mmc_icon.m_name = name;
|
||||
mmc_icon.m_key = key;
|
||||
mmc_icon.replace(type, icon, path);
|
||||
icons.push_back(mmc_icon);
|
||||
name_index[key] = icons.size() - 1;
|
||||
}
|
||||
endInsertRows();
|
||||
return true;
|
||||
}
|
||||
// add a new icon
|
||||
beginInsertRows(QModelIndex(), icons.size(), icons.size());
|
||||
{
|
||||
MMCIcon mmc_icon;
|
||||
mmc_icon.m_name = name;
|
||||
mmc_icon.m_key = key;
|
||||
mmc_icon.replace(type, icon, path);
|
||||
icons.push_back(mmc_icon);
|
||||
name_index[key] = icons.size() - 1;
|
||||
}
|
||||
endInsertRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
void IconList::saveIcon(const QString& key, const QString& path, const char* format) const
|
||||
@ -446,5 +425,3 @@ QString IconList::getDirectory() const
|
||||
{
|
||||
return m_dir.absolutePath();
|
||||
}
|
||||
|
||||
//#include "IconList.moc"
|
||||
|
@ -1,18 +1,37 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
@ -1,19 +1,51 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "IconUtils.h"
|
||||
|
||||
#include <QDirIterator>
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace {
|
||||
std::array<const char*, 6> validIconExtensions = { { "svg", "png", "ico", "gif", "jpg", "jpeg" } };
|
||||
static const QStringList validIconExtensions = { { "svg", "png", "ico", "gif", "jpg", "jpeg" } };
|
||||
}
|
||||
|
||||
namespace IconUtils {
|
||||
|
||||
QString findBestIconIn(const QString& folder, const QString& iconKey)
|
||||
{
|
||||
int best_found = validIconExtensions.size();
|
||||
QString best_filename;
|
||||
|
||||
QDirIterator it(folder, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::NoIteratorFlags);
|
||||
@ -21,36 +53,20 @@ QString findBestIconIn(const QString& folder, const QString& iconKey)
|
||||
it.next();
|
||||
auto fileInfo = it.fileInfo();
|
||||
|
||||
if (fileInfo.completeBaseName() != iconKey)
|
||||
continue;
|
||||
|
||||
auto extension = fileInfo.suffix();
|
||||
|
||||
for (int i = 0; i < best_found; i++) {
|
||||
if (extension == validIconExtensions[i]) {
|
||||
best_found = i;
|
||||
qDebug() << i << " : " << fileInfo.fileName();
|
||||
best_filename = fileInfo.fileName();
|
||||
}
|
||||
}
|
||||
if (fileInfo.completeBaseName() == iconKey && isIconSuffix(fileInfo.suffix()))
|
||||
return fileInfo.absoluteFilePath();
|
||||
}
|
||||
return FS::PathCombine(folder, best_filename);
|
||||
return {};
|
||||
}
|
||||
|
||||
QString getIconFilter()
|
||||
{
|
||||
QString out;
|
||||
QTextStream stream(&out);
|
||||
stream << '(';
|
||||
for (size_t i = 0; i < validIconExtensions.size() - 1; i++) {
|
||||
if (i > 0) {
|
||||
stream << " ";
|
||||
}
|
||||
stream << "*." << validIconExtensions[i];
|
||||
}
|
||||
stream << " *." << validIconExtensions[validIconExtensions.size() - 1];
|
||||
stream << ')';
|
||||
return out;
|
||||
return "(*." + validIconExtensions.join(" *.") + ")";
|
||||
}
|
||||
|
||||
bool isIconSuffix(QString suffix)
|
||||
{
|
||||
return validIconExtensions.contains(suffix);
|
||||
}
|
||||
|
||||
} // namespace IconUtils
|
||||
|
@ -1,3 +1,38 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
@ -10,4 +45,5 @@ QString findBestIconIn(const QString& folder, const QString& iconKey);
|
||||
// Get icon file type filter for file browser dialogs
|
||||
QString getIconFilter();
|
||||
|
||||
bool isIconSuffix(QString suffix);
|
||||
} // namespace IconUtils
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -50,8 +51,8 @@ IconType operator--(IconType& t, int)
|
||||
case IconType::FileBased:
|
||||
t = IconType::Transient;
|
||||
break;
|
||||
default: {
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
@ -1,18 +1,37 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <QDateTime>
|
||||
#include <QIcon>
|
||||
|
@ -14,7 +14,7 @@ class JavaVersion {
|
||||
friend class JavaVersionTest;
|
||||
|
||||
public:
|
||||
JavaVersion(){};
|
||||
JavaVersion() {}
|
||||
JavaVersion(const QString& rhs);
|
||||
|
||||
JavaVersion& operator=(const QString& rhs);
|
||||
|
@ -30,7 +30,7 @@ class LogModel : public QAbstractListModel {
|
||||
|
||||
enum Roles { LevelRole = Qt::UserRole };
|
||||
|
||||
private /* types */:
|
||||
private /* types */:
|
||||
struct entry {
|
||||
MessageLevel::Enum level;
|
||||
QString line;
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "BaseEntity.h"
|
||||
|
||||
#include "Json.h"
|
||||
#include "net/Download.h"
|
||||
#include "net/ApiDownload.h"
|
||||
#include "net/HttpMetaCache.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
@ -32,7 +32,7 @@ class ParsingValidator : public Net::Validator {
|
||||
bool init(QNetworkRequest&) override { return true; }
|
||||
bool write(QByteArray& data) override
|
||||
{
|
||||
this->data.append(data);
|
||||
this->m_data.append(data);
|
||||
return true;
|
||||
}
|
||||
bool abort() override { return true; }
|
||||
@ -40,7 +40,7 @@ class ParsingValidator : public Net::Validator {
|
||||
{
|
||||
auto fname = m_entity->localFilename();
|
||||
try {
|
||||
auto doc = Json::requireDocument(data, fname);
|
||||
auto doc = Json::requireDocument(m_data, fname);
|
||||
auto obj = Json::requireObject(doc, fname);
|
||||
m_entity->parse(obj);
|
||||
return true;
|
||||
@ -51,7 +51,7 @@ class ParsingValidator : public Net::Validator {
|
||||
}
|
||||
|
||||
private: /* data */
|
||||
QByteArray data;
|
||||
QByteArray m_data;
|
||||
Meta::BaseEntity* m_entity;
|
||||
};
|
||||
|
||||
@ -104,7 +104,7 @@ void Meta::BaseEntity::load(Net::Mode loadType)
|
||||
auto url = this->url();
|
||||
auto entry = APPLICATION->metacache()->resolveEntry("meta", localFilename());
|
||||
entry->setStale(true);
|
||||
auto dl = Net::Download::makeCached(url, entry);
|
||||
auto dl = Net::ApiDownload::makeCached(url, entry);
|
||||
/*
|
||||
* The validator parses the file and loads it into the object.
|
||||
* If that fails, the file is not written to storage.
|
||||
|
@ -46,8 +46,8 @@
|
||||
#include "AssetsUtils.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "FileSystem.h"
|
||||
#include "net/ApiDownload.h"
|
||||
#include "net/ChecksumValidator.h"
|
||||
#include "net/Download.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
@ -279,7 +279,7 @@ NetAction::Ptr AssetObject::getDownloadAction()
|
||||
{
|
||||
QFileInfo objectFile(getLocalPath());
|
||||
if ((!objectFile.isFile()) || (objectFile.size() != size)) {
|
||||
auto objectDL = Net::Download::makeFile(getUrl(), objectFile.filePath());
|
||||
auto objectDL = Net::ApiDownload::makeFile(getUrl(), objectFile.filePath());
|
||||
if (hash.size()) {
|
||||
auto rawHash = QByteArray::fromHex(hash.toLatin1());
|
||||
objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash));
|
||||
|
@ -25,7 +25,8 @@ class Component : public QObject, public ProblemProvider {
|
||||
Component(PackProfile* parent, std::shared_ptr<Meta::Version> version);
|
||||
Component(PackProfile* parent, const QString& uid, std::shared_ptr<VersionFile> file);
|
||||
|
||||
virtual ~Component(){};
|
||||
virtual ~Component() {}
|
||||
|
||||
void applyTo(LaunchProfile* profile);
|
||||
|
||||
bool isEnabled();
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
#include "Component.h"
|
||||
#include "ComponentUpdateTask_p.h"
|
||||
#include "OneSixVersionFormat.h"
|
||||
#include "PackProfile.h"
|
||||
#include "PackProfile_p.h"
|
||||
#include "Version.h"
|
||||
#include "cassert"
|
||||
#include "meta/Index.h"
|
||||
#include "meta/Version.h"
|
||||
#include "meta/VersionList.h"
|
||||
#include "minecraft/OneSixVersionFormat.h"
|
||||
#include "minecraft/ProfileUtils.h"
|
||||
#include "net/Mode.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
@ -41,7 +41,7 @@
|
||||
|
||||
class LaunchProfile : public ProblemProvider {
|
||||
public:
|
||||
virtual ~LaunchProfile(){};
|
||||
virtual ~LaunchProfile() {}
|
||||
|
||||
public: /* application of profile variables from patches */
|
||||
void applyMinecraftVersion(const QString& id);
|
||||
|
@ -38,8 +38,8 @@
|
||||
|
||||
#include <BuildConfig.h>
|
||||
#include <FileSystem.h>
|
||||
#include <net/ApiDownload.h>
|
||||
#include <net/ChecksumValidator.h>
|
||||
#include <net/Download.h>
|
||||
|
||||
void Library::getApplicableFiles(const RuntimeContext& runtimeContext,
|
||||
QStringList& jar,
|
||||
@ -115,12 +115,12 @@ QList<NetAction::Ptr> Library::getDownloads(const RuntimeContext& runtimeContext
|
||||
|
||||
if (sha1.size()) {
|
||||
auto rawSha1 = QByteArray::fromHex(sha1.toLatin1());
|
||||
auto dl = Net::Download::makeCached(url, entry, options);
|
||||
auto dl = Net::ApiDownload::makeCached(url, entry, options);
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
|
||||
qDebug() << "Checksummed Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url;
|
||||
out.append(dl);
|
||||
} else {
|
||||
out.append(Net::Download::makeCached(url, entry, options));
|
||||
out.append(Net::ApiDownload::makeCached(url, entry, options));
|
||||
qDebug() << "Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url;
|
||||
}
|
||||
return true;
|
||||
|
@ -3,8 +3,7 @@
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||
* Copyright (c) 2023 seth <getchoo at tuta dot io>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* 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
|
||||
@ -88,6 +87,10 @@
|
||||
#include "minecraft/gameoptions/GameOptions.h"
|
||||
#include "minecraft/update/FoldersTask.h"
|
||||
|
||||
#include "tools/BaseProfiler.h"
|
||||
|
||||
#include <QActionGroup>
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#include "MangoHud.h"
|
||||
#endif
|
||||
@ -166,7 +169,9 @@ void MinecraftInstance::loadSpecificSettings()
|
||||
// Native library workarounds
|
||||
auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false);
|
||||
m_settings->registerOverride(global_settings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride);
|
||||
m_settings->registerOverride(global_settings->getSetting("CustomOpenALPath"), nativeLibraryWorkaroundsOverride);
|
||||
m_settings->registerOverride(global_settings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride);
|
||||
m_settings->registerOverride(global_settings->getSetting("CustomGLFWPath"), nativeLibraryWorkaroundsOverride);
|
||||
|
||||
// Peformance related options
|
||||
auto performanceOverride = m_settings->registerSetting("OverridePerformance", false);
|
||||
@ -179,10 +184,6 @@ void MinecraftInstance::loadSpecificSettings()
|
||||
m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride);
|
||||
m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride);
|
||||
|
||||
// Mod loader specific options
|
||||
auto modLoaderSettings = m_settings->registerSetting("OverrideModLoaderSettings", false);
|
||||
m_settings->registerOverride(global_settings->getSetting("DisableQuiltBeacon"), modLoaderSettings);
|
||||
|
||||
m_settings->set("InstanceType", "OneSix");
|
||||
}
|
||||
|
||||
@ -194,6 +195,12 @@ void MinecraftInstance::loadSpecificSettings()
|
||||
m_settings->registerSetting("UseAccountForInstance", false);
|
||||
m_settings->registerSetting("InstanceAccountId", "");
|
||||
|
||||
m_settings->registerSetting("ExportName", "");
|
||||
m_settings->registerSetting("ExportVersion", "1.0.0");
|
||||
m_settings->registerSetting("ExportSummary", "");
|
||||
m_settings->registerSetting("ExportAuthor", "");
|
||||
m_settings->registerSetting("ExportOptionalFiles", true);
|
||||
|
||||
qDebug() << "Instance-type specific settings were loaded!";
|
||||
|
||||
setSpecificSettingsLoaded(true);
|
||||
@ -229,6 +236,50 @@ QSet<QString> MinecraftInstance::traits() const
|
||||
return profile->getTraits();
|
||||
}
|
||||
|
||||
// FIXME: move UI code out of MinecraftInstance
|
||||
void MinecraftInstance::populateLaunchMenu(QMenu* menu)
|
||||
{
|
||||
QAction* normalLaunch = menu->addAction(tr("&Launch"));
|
||||
normalLaunch->setShortcut(QKeySequence::Open);
|
||||
QAction* normalLaunchOffline = menu->addAction(tr("Launch &Offline"));
|
||||
normalLaunchOffline->setShortcut(QKeySequence(tr("Ctrl+Shift+O")));
|
||||
QAction* normalLaunchDemo = menu->addAction(tr("Launch &Demo"));
|
||||
normalLaunchDemo->setShortcut(QKeySequence(tr("Ctrl+Alt+O")));
|
||||
|
||||
normalLaunchDemo->setEnabled(supportsDemo());
|
||||
|
||||
connect(normalLaunch, &QAction::triggered, [this] { APPLICATION->launch(shared_from_this()); });
|
||||
connect(normalLaunchOffline, &QAction::triggered, [this] { APPLICATION->launch(shared_from_this(), false, false); });
|
||||
connect(normalLaunchDemo, &QAction::triggered, [this] { APPLICATION->launch(shared_from_this(), false, true); });
|
||||
|
||||
QString profilersTitle = tr("Profilers");
|
||||
menu->addSeparator()->setText(profilersTitle);
|
||||
|
||||
auto profilers = new QActionGroup(menu);
|
||||
profilers->setExclusive(true);
|
||||
connect(profilers, &QActionGroup::triggered, [this](QAction* action) {
|
||||
settings()->set("Profiler", action->data());
|
||||
emit profilerChanged();
|
||||
});
|
||||
|
||||
QAction* noProfilerAction = menu->addAction(tr("&No Profiler"));
|
||||
noProfilerAction->setData("");
|
||||
noProfilerAction->setCheckable(true);
|
||||
noProfilerAction->setChecked(true);
|
||||
profilers->addAction(noProfilerAction);
|
||||
|
||||
for (auto profiler = APPLICATION->profilers().begin(); profiler != APPLICATION->profilers().end(); profiler++) {
|
||||
QAction* profilerAction = menu->addAction(profiler.value()->name());
|
||||
profilers->addAction(profilerAction);
|
||||
profilerAction->setData(profiler.key());
|
||||
profilerAction->setCheckable(true);
|
||||
profilerAction->setChecked(settings()->get("Profiler").toString() == profiler.key());
|
||||
|
||||
QString error;
|
||||
profilerAction->setEnabled(profiler.value()->check(&error));
|
||||
}
|
||||
}
|
||||
|
||||
QString MinecraftInstance::gameRoot() const
|
||||
{
|
||||
QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
|
||||
@ -260,7 +311,7 @@ QString MinecraftInstance::getLocalLibraryPath() const
|
||||
bool MinecraftInstance::supportsDemo() const
|
||||
{
|
||||
Version instance_ver{ getPackProfile()->getComponentVersion("net.minecraft") };
|
||||
// Demo mode was introduced in 1.3.1: https://minecraft.fandom.com/wiki/Demo_mode#History
|
||||
// Demo mode was introduced in 1.3.1: https://minecraft.wiki/w/Demo_mode#History
|
||||
// FIXME: Due to Version constraints atm, this can't handle well non-release versions
|
||||
return instance_ver >= Version("1.3.1");
|
||||
}
|
||||
@ -385,10 +436,31 @@ QStringList MinecraftInstance::extraArguments()
|
||||
}
|
||||
|
||||
{
|
||||
const auto loaders = version->getModLoaders();
|
||||
if (loaders.has_value() && loaders.value() & ResourceAPI::Quilt && settings()->get("DisableQuiltBeacon").toBool())
|
||||
list.append("-Dloader.disable_beacon=true");
|
||||
QString openALPath;
|
||||
QString glfwPath;
|
||||
|
||||
if (settings()->get("UseNativeOpenAL").toBool()) {
|
||||
openALPath = APPLICATION->m_detectedOpenALPath;
|
||||
auto customPath = settings()->get("CustomOpenALPath").toString();
|
||||
if (!customPath.isEmpty())
|
||||
openALPath = customPath;
|
||||
}
|
||||
if (settings()->get("UseNativeGLFW").toBool()) {
|
||||
glfwPath = APPLICATION->m_detectedGLFWPath;
|
||||
auto customPath = settings()->get("CustomGLFWPath").toString();
|
||||
if (!customPath.isEmpty())
|
||||
glfwPath = customPath;
|
||||
}
|
||||
|
||||
QFileInfo openALInfo(openALPath);
|
||||
QFileInfo glfwInfo(glfwPath);
|
||||
|
||||
if (!openALPath.isEmpty() && openALInfo.exists())
|
||||
list.append("-Dorg.lwjgl.openal.libname=" + openALInfo.absoluteFilePath());
|
||||
if (!glfwPath.isEmpty() && glfwInfo.exists())
|
||||
list.append("-Dorg.lwjgl.glfw.libname=" + glfwInfo.absoluteFilePath());
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@ -868,13 +940,16 @@ QString MinecraftInstance::getStatusbarDescription()
|
||||
if (m_settings->get("ShowGameTime").toBool()) {
|
||||
if (lastTimePlayed() > 0) {
|
||||
QDateTime lastLaunchTime = QDateTime::fromMSecsSinceEpoch(lastLaunch());
|
||||
description.append(tr(", last played on %1 for %2")
|
||||
.arg(QLocale().toString(lastLaunchTime, QLocale::ShortFormat))
|
||||
.arg(Time::prettifyDuration(lastTimePlayed())));
|
||||
description.append(
|
||||
tr(", last played on %1 for %2")
|
||||
.arg(QLocale().toString(lastLaunchTime, QLocale::ShortFormat))
|
||||
.arg(Time::prettifyDuration(lastTimePlayed(), APPLICATION->settings()->get("ShowGameTimeWithoutDays").toBool())));
|
||||
}
|
||||
|
||||
if (totalTimePlayed() > 0) {
|
||||
description.append(tr(", total played for %1").arg(Time::prettifyDuration(totalTimePlayed())));
|
||||
description.append(
|
||||
tr(", total played for %1")
|
||||
.arg(Time::prettifyDuration(totalTimePlayed(), APPLICATION->settings()->get("ShowGameTimeWithoutDays").toBool())));
|
||||
}
|
||||
}
|
||||
if (hasCrashed()) {
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* 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
|
||||
@ -70,6 +70,8 @@ class MinecraftInstance : public BaseInstance {
|
||||
|
||||
bool canExport() const override { return true; }
|
||||
|
||||
void populateLaunchMenu(QMenu* menu) override;
|
||||
|
||||
////// Directories and files //////
|
||||
QString jarModsDir() const;
|
||||
QString resourcePacksDir() const;
|
||||
|
@ -19,8 +19,8 @@ struct MojangDownloadInfo {
|
||||
};
|
||||
|
||||
struct MojangLibraryDownloadInfo {
|
||||
MojangLibraryDownloadInfo(MojangDownloadInfo::Ptr artifact) : artifact(artifact){};
|
||||
MojangLibraryDownloadInfo(){};
|
||||
MojangLibraryDownloadInfo(MojangDownloadInfo::Ptr artifact_) : artifact(artifact_) {}
|
||||
MojangLibraryDownloadInfo() {}
|
||||
|
||||
// types
|
||||
typedef std::shared_ptr<MojangLibraryDownloadInfo> Ptr;
|
||||
@ -47,18 +47,18 @@ struct MojangAssetIndexInfo : public MojangDownloadInfo {
|
||||
// methods
|
||||
MojangAssetIndexInfo() {}
|
||||
|
||||
MojangAssetIndexInfo(QString id)
|
||||
MojangAssetIndexInfo(QString id_)
|
||||
{
|
||||
this->id = id;
|
||||
this->id = id_;
|
||||
// HACK: ignore assets from other version files than Minecraft
|
||||
// workaround for stupid assets issue caused by amazon:
|
||||
// https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/
|
||||
if (id == "legacy") {
|
||||
if (id_ == "legacy") {
|
||||
url = "https://piston-meta.mojang.com/mc/assets/legacy/c0fd82e8ce9fbc93119e40d96d5a4e62cfa3f729/legacy.json";
|
||||
}
|
||||
// HACK
|
||||
else {
|
||||
url = "https://s3.amazonaws.com/Minecraft.Download/indexes/" + id + ".json";
|
||||
url = "https://s3.amazonaws.com/Minecraft.Download/indexes/" + id_ + ".json";
|
||||
}
|
||||
known = false;
|
||||
}
|
||||
|
@ -350,7 +350,7 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr& patch
|
||||
}
|
||||
}
|
||||
|
||||
LibraryPtr OneSixVersionFormat::plusJarModFromJson(ProblemContainer& problems,
|
||||
LibraryPtr OneSixVersionFormat::plusJarModFromJson([[maybe_unused]] ProblemContainer& problems,
|
||||
const QJsonObject& libObj,
|
||||
const QString& filename,
|
||||
const QString& originalName)
|
||||
|
@ -58,14 +58,14 @@
|
||||
#include "ComponentUpdateTask.h"
|
||||
#include "PackProfile.h"
|
||||
#include "PackProfile_p.h"
|
||||
#include "minecraft/mod/Mod.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
|
||||
static const QMap<QString, ResourceAPI::ModLoaderType> modloaderMapping{ { "net.minecraftforge", ResourceAPI::Forge },
|
||||
{ "net.fabricmc.fabric-loader", ResourceAPI::Fabric },
|
||||
{ "org.quiltmc.quilt-loader", ResourceAPI::Quilt },
|
||||
{ "com.mumfrey.liteloader", ResourceAPI::LiteLoader } };
|
||||
static const QMap<QString, ModPlatform::ModLoaderType> modloaderMapping{ { "net.neoforged", ModPlatform::NeoForge },
|
||||
{ "net.minecraftforge", ModPlatform::Forge },
|
||||
{ "net.fabricmc.fabric-loader", ModPlatform::Fabric },
|
||||
{ "org.quiltmc.quilt-loader", ModPlatform::Quilt },
|
||||
{ "com.mumfrey.liteloader", ModPlatform::LiteLoader } };
|
||||
|
||||
PackProfile::PackProfile(MinecraftInstance* instance) : QAbstractListModel()
|
||||
{
|
||||
@ -204,10 +204,10 @@ static bool loadPackProfile(PackProfile* parent,
|
||||
}
|
||||
auto orderArray = Json::requireArray(obj.value("components"));
|
||||
for (auto item : orderArray) {
|
||||
auto obj = Json::requireObject(item, "Component must be an object.");
|
||||
container.append(componentFromJsonV1(parent, componentJsonPattern, obj));
|
||||
auto comp_obj = Json::requireObject(item, "Component must be an object.");
|
||||
container.append(componentFromJsonV1(parent, componentJsonPattern, comp_obj));
|
||||
}
|
||||
} catch (const JSONValidationError& err) {
|
||||
} catch ([[maybe_unused]] const JSONValidationError& err) {
|
||||
qCritical() << "Couldn't parse" << componentsFile.fileName() << ": bad file format";
|
||||
container.clear();
|
||||
return false;
|
||||
@ -377,7 +377,7 @@ void PackProfile::insertComponent(size_t index, ComponentPtr component)
|
||||
qWarning() << "Attempt to add a component that is already present!";
|
||||
return;
|
||||
}
|
||||
beginInsertRows(QModelIndex(), index, index);
|
||||
beginInsertRows(QModelIndex(), static_cast<int>(index), static_cast<int>(index));
|
||||
d->components.insert(index, component);
|
||||
d->componentIndex[id] = component;
|
||||
endInsertRows();
|
||||
@ -389,7 +389,7 @@ void PackProfile::componentDataChanged()
|
||||
{
|
||||
auto objPtr = qobject_cast<Component*>(sender());
|
||||
if (!objPtr) {
|
||||
qWarning() << "PackProfile got dataChenged signal from a non-Component!";
|
||||
qWarning() << "PackProfile got dataChanged signal from a non-Component!";
|
||||
return;
|
||||
}
|
||||
if (objPtr->getID() == "net.minecraft") {
|
||||
@ -405,7 +405,7 @@ void PackProfile::componentDataChanged()
|
||||
}
|
||||
index++;
|
||||
}
|
||||
qWarning() << "PackProfile got dataChenged signal from a Component which does not belong to it!";
|
||||
qWarning() << "PackProfile got dataChanged signal from a Component which does not belong to it!";
|
||||
}
|
||||
|
||||
bool PackProfile::remove(const int index)
|
||||
@ -483,9 +483,9 @@ ComponentPtr PackProfile::getComponent(const QString& id)
|
||||
return (*iter);
|
||||
}
|
||||
|
||||
ComponentPtr PackProfile::getComponent(int index)
|
||||
ComponentPtr PackProfile::getComponent(size_t index)
|
||||
{
|
||||
if (index < 0 || index >= d->components.size()) {
|
||||
if (index >= static_cast<size_t>(d->components.size())) {
|
||||
return nullptr;
|
||||
}
|
||||
return d->components[index];
|
||||
@ -547,7 +547,7 @@ QVariant PackProfile::data(const QModelIndex& index, int role) const
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool PackProfile::setData(const QModelIndex& index, const QVariant& value, int role)
|
||||
bool PackProfile::setData(const QModelIndex& index, [[maybe_unused]] const QVariant& value, int role)
|
||||
{
|
||||
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index.parent())) {
|
||||
return false;
|
||||
@ -989,12 +989,12 @@ void PackProfile::disableInteraction(bool disable)
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<ResourceAPI::ModLoaderTypes> PackProfile::getModLoaders()
|
||||
std::optional<ModPlatform::ModLoaderTypes> PackProfile::getModLoaders()
|
||||
{
|
||||
ResourceAPI::ModLoaderTypes result;
|
||||
ModPlatform::ModLoaderTypes result;
|
||||
bool has_any_loader = false;
|
||||
|
||||
QMapIterator<QString, ResourceAPI::ModLoaderType> i(modloaderMapping);
|
||||
QMapIterator<QString, ModPlatform::ModLoaderType> i(modloaderMapping);
|
||||
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
@ -1008,3 +1008,18 @@ std::optional<ResourceAPI::ModLoaderTypes> PackProfile::getModLoaders()
|
||||
return {};
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<ModPlatform::ModLoaderTypes> PackProfile::getSupportedModLoaders()
|
||||
{
|
||||
auto loadersOpt = getModLoaders();
|
||||
if (!loadersOpt.has_value())
|
||||
return loadersOpt;
|
||||
auto loaders = loadersOpt.value();
|
||||
// TODO: remove this or add version condition once Quilt drops official Fabric support
|
||||
if (loaders & ModPlatform::Quilt)
|
||||
loaders |= ModPlatform::Fabric;
|
||||
// TODO: remove this or add version condition once NeoForge drops official Forge support
|
||||
if (loaders & ModPlatform::NeoForge)
|
||||
loaders |= ModPlatform::Forge;
|
||||
return loaders;
|
||||
}
|
||||
|
@ -44,14 +44,11 @@
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "BaseVersion.h"
|
||||
#include "Component.h"
|
||||
#include "LaunchProfile.h"
|
||||
#include "Library.h"
|
||||
#include "MojangDownloadInfo.h"
|
||||
#include "ProfileUtils.h"
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "net/Mode.h"
|
||||
|
||||
class MinecraftInstance;
|
||||
@ -140,13 +137,15 @@ class PackProfile : public QAbstractListModel {
|
||||
ComponentPtr getComponent(const QString& id);
|
||||
|
||||
/// get the profile component by index
|
||||
ComponentPtr getComponent(int index);
|
||||
ComponentPtr getComponent(size_t index);
|
||||
|
||||
/// Add the component to the internal list of patches
|
||||
// todo(merged): is this the best approach
|
||||
void appendComponent(ComponentPtr component);
|
||||
|
||||
std::optional<ResourceAPI::ModLoaderTypes> getModLoaders();
|
||||
std::optional<ModPlatform::ModLoaderTypes> getModLoaders();
|
||||
// this returns aditional loaders(Quilt supports fabric and NeoForge supports Forge)
|
||||
std::optional<ModPlatform::ModLoaderTypes> getSupportedModLoaders();
|
||||
|
||||
private:
|
||||
void scheduleSave();
|
||||
|
@ -82,7 +82,7 @@ bool readOverrideOrders(QString path, PatchOrder& order)
|
||||
for (auto item : orderArray) {
|
||||
order.append(Json::requireString(item));
|
||||
}
|
||||
} catch (const JSONValidationError& err) {
|
||||
} catch ([[maybe_unused]] const JSONValidationError& err) {
|
||||
qCritical() << "Couldn't parse" << orderFile.fileName() << ": bad file format";
|
||||
qWarning() << "Ignoring overriden order";
|
||||
order.clear();
|
||||
|
@ -55,7 +55,7 @@ class Rule {
|
||||
|
||||
public:
|
||||
Rule(RuleAction result) : m_result(result) {}
|
||||
virtual ~Rule(){};
|
||||
virtual ~Rule() {}
|
||||
virtual QJsonObject toJson() = 0;
|
||||
RuleAction apply(const Library* parent, const RuntimeContext& runtimeContext)
|
||||
{
|
||||
|
@ -368,11 +368,11 @@ optional<QString> read_string(nbt::value& parent, const char* name)
|
||||
}
|
||||
auto& tag_str = namedValue.as<nbt::tag_string>();
|
||||
return QString::fromStdString(tag_str.get());
|
||||
} catch (const std::out_of_range& e) {
|
||||
} catch ([[maybe_unused]] const std::out_of_range& e) {
|
||||
// fallback for old world formats
|
||||
qWarning() << "String NBT tag" << name << "could not be found.";
|
||||
return nullopt;
|
||||
} catch (const std::bad_cast& e) {
|
||||
} catch ([[maybe_unused]] const std::bad_cast& e) {
|
||||
// type mismatch
|
||||
qWarning() << "NBT tag" << name << "could not be converted to string.";
|
||||
return nullopt;
|
||||
@ -388,11 +388,11 @@ optional<int64_t> read_long(nbt::value& parent, const char* name)
|
||||
}
|
||||
auto& tag_str = namedValue.as<nbt::tag_long>();
|
||||
return tag_str.get();
|
||||
} catch (const std::out_of_range& e) {
|
||||
} catch ([[maybe_unused]] const std::out_of_range& e) {
|
||||
// fallback for old world formats
|
||||
qWarning() << "Long NBT tag" << name << "could not be found.";
|
||||
return nullopt;
|
||||
} catch (const std::bad_cast& e) {
|
||||
} catch ([[maybe_unused]] const std::bad_cast& e) {
|
||||
// type mismatch
|
||||
qWarning() << "NBT tag" << name << "could not be converted to long.";
|
||||
return nullopt;
|
||||
@ -408,11 +408,11 @@ optional<int> read_int(nbt::value& parent, const char* name)
|
||||
}
|
||||
auto& tag_str = namedValue.as<nbt::tag_int>();
|
||||
return tag_str.get();
|
||||
} catch (const std::out_of_range& e) {
|
||||
} catch ([[maybe_unused]] const std::out_of_range& e) {
|
||||
// fallback for old world formats
|
||||
qWarning() << "Int NBT tag" << name << "could not be found.";
|
||||
return nullopt;
|
||||
} catch (const std::bad_cast& e) {
|
||||
} catch ([[maybe_unused]] const std::bad_cast& e) {
|
||||
// type mismatch
|
||||
qWarning() << "NBT tag" << name << "could not be converted to int.";
|
||||
return nullopt;
|
||||
|
@ -255,7 +255,7 @@ QVariant WorldList::data(const QModelIndex& index, int role) const
|
||||
}
|
||||
}
|
||||
|
||||
QVariant WorldList::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
QVariant WorldList::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
@ -294,7 +294,6 @@ QVariant WorldList::headerData(int section, Qt::Orientation orientation, int rol
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QStringList WorldList::mimeTypes() const
|
||||
@ -339,19 +338,19 @@ QMimeData* WorldList::mimeData(const QModelIndexList& indexes) const
|
||||
if (indexes.size() == 0)
|
||||
return new QMimeData();
|
||||
|
||||
QList<World> worlds;
|
||||
QList<World> worlds_;
|
||||
for (auto idx : indexes) {
|
||||
if (idx.column() != 0)
|
||||
continue;
|
||||
int row = idx.row();
|
||||
if (row < 0 || row >= this->worlds.size())
|
||||
continue;
|
||||
worlds.append(this->worlds[row]);
|
||||
worlds_.append(this->worlds[row]);
|
||||
}
|
||||
if (!worlds.size()) {
|
||||
if (!worlds_.size()) {
|
||||
return new QMimeData();
|
||||
}
|
||||
return new WorldMimeData(worlds);
|
||||
return new WorldMimeData(worlds_);
|
||||
}
|
||||
|
||||
Qt::ItemFlags WorldList::flags(const QModelIndex& index) const
|
||||
|
@ -311,7 +311,7 @@ bool AccountData::resumeStateFromV2(QJsonObject data)
|
||||
QJsonObject profileObject = profileVal.toObject();
|
||||
QString id = profileObject.value("id").toString("");
|
||||
QString name = profileObject.value("name").toString("");
|
||||
bool legacy = profileObject.value("legacy").toBool(false);
|
||||
bool legacy_ = profileObject.value("legacy").toBool(false);
|
||||
if (id.isEmpty() || name.isEmpty()) {
|
||||
qWarning() << "Unable to load a profile" << name << "because it was missing an ID or a name.";
|
||||
continue;
|
||||
@ -319,7 +319,7 @@ bool AccountData::resumeStateFromV2(QJsonObject data)
|
||||
if (id == currentProfile) {
|
||||
currentProfileIndex = index;
|
||||
}
|
||||
profiles.append({ id, name, legacy });
|
||||
profiles.append({ id, name, legacy_ });
|
||||
}
|
||||
auto& profile = profiles[currentProfileIndex];
|
||||
|
||||
|
@ -353,7 +353,7 @@ QVariant AccountList::data(const QModelIndex& index, int role) const
|
||||
}
|
||||
}
|
||||
|
||||
QVariant AccountList::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
QVariant AccountList::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
@ -415,7 +415,7 @@ Qt::ItemFlags AccountList::flags(const QModelIndex& index) const
|
||||
|
||||
bool AccountList::setData(const QModelIndex& idx, const QVariant& value, int role)
|
||||
{
|
||||
if (idx.row() < 0 || idx.row() >= rowCount(idx) || !idx.isValid()) {
|
||||
if (idx.row() < 0 || idx.row() >= rowCount(idx.parent()) || !idx.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -423,7 +423,8 @@ bool AccountList::setData(const QModelIndex& idx, const QVariant& value, int rol
|
||||
if (value == Qt::Checked) {
|
||||
MinecraftAccountPtr account = at(idx.row());
|
||||
setDefaultAccount(account);
|
||||
}
|
||||
} else if (m_defaultAccount == at(idx.row()))
|
||||
setDefaultAccount(nullptr);
|
||||
}
|
||||
|
||||
emit dataChanged(idx, index(idx.row(), columnCount(QModelIndex()) - 1));
|
||||
|
@ -37,6 +37,7 @@
|
||||
|
||||
#include "MinecraftAccount.h"
|
||||
|
||||
#include <QColor>
|
||||
#include <QCryptographicHash>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
@ -126,6 +127,11 @@ QPixmap MinecraftAccount::getFace() const
|
||||
return QPixmap();
|
||||
}
|
||||
QPixmap skin = QPixmap(8, 8);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
skin.fill(QColorConstants::Transparent);
|
||||
#else
|
||||
skin.fill(QColor(0, 0, 0, 0));
|
||||
#endif
|
||||
QPainter painter(&skin);
|
||||
painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8));
|
||||
painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8));
|
||||
|
@ -113,16 +113,16 @@ bool parseXTokenResponse(QByteArray& data, Katabasis::Token& output, QString nam
|
||||
if (!item.isObject()) {
|
||||
continue;
|
||||
}
|
||||
auto obj = item.toObject();
|
||||
if (obj.contains("uhs")) {
|
||||
auto obj_ = item.toObject();
|
||||
if (obj_.contains("uhs")) {
|
||||
foundUHS = true;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
// consume all 'display claims' ... whatever that means
|
||||
for (auto iter = obj.begin(); iter != obj.end(); iter++) {
|
||||
for (auto iter = obj_.begin(); iter != obj_.end(); iter++) {
|
||||
QString claim;
|
||||
if (!getString(obj.value(iter.key()), claim)) {
|
||||
if (!getString(obj_.value(iter.key()), claim)) {
|
||||
qWarning() << "display claim " << iter.key() << " is not a string...";
|
||||
return false;
|
||||
}
|
||||
|
@ -36,7 +36,9 @@ void EntitlementsStep::rehydrate()
|
||||
// NOOP, for now. We only save bools and there's nothing to check.
|
||||
}
|
||||
|
||||
void EntitlementsStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair> headers)
|
||||
void EntitlementsStep::onRequestDone([[maybe_unused]] QNetworkReply::NetworkError error,
|
||||
QByteArray data,
|
||||
[[maybe_unused]] QList<QNetworkReply::RawHeaderPair> headers)
|
||||
{
|
||||
auto requestor = qobject_cast<AuthRequest*>(QObject::sender());
|
||||
requestor->deleteLater();
|
||||
|
@ -97,12 +97,11 @@ QVariant GameOptions::data(const QModelIndex& index, int role) const
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
int GameOptions::rowCount(const QModelIndex&) const
|
||||
{
|
||||
return contents.size();
|
||||
return static_cast<int>(contents.size());
|
||||
}
|
||||
|
||||
int GameOptions::columnCount(const QModelIndex&) const
|
||||
|
@ -39,7 +39,7 @@ static QString replaceSuffix(QString target, const QString& suffix, const QStrin
|
||||
return target + replacement;
|
||||
}
|
||||
|
||||
static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack, bool nativeOpenAL, bool nativeGLFW)
|
||||
static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack)
|
||||
{
|
||||
QuaZip zip(source);
|
||||
if (!zip.open(QuaZip::mdUnzip)) {
|
||||
@ -52,12 +52,6 @@ static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibH
|
||||
do {
|
||||
QString name = zip.getCurrentFileName();
|
||||
auto lowercase = name.toLower();
|
||||
if (nativeGLFW && name.contains("glfw")) {
|
||||
continue;
|
||||
}
|
||||
if (nativeOpenAL && name.contains("openal")) {
|
||||
continue;
|
||||
}
|
||||
if (applyJnilibHack) {
|
||||
name = replaceSuffix(name, ".jnilib", ".dylib");
|
||||
}
|
||||
@ -83,14 +77,12 @@ void ExtractNatives::executeTask()
|
||||
return;
|
||||
}
|
||||
auto settings = minecraftInstance->settings();
|
||||
bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool();
|
||||
bool nativeGLFW = settings->get("UseNativeGLFW").toBool();
|
||||
|
||||
auto outputPath = minecraftInstance->getNativePath();
|
||||
auto javaVersion = minecraftInstance->getJavaVersion();
|
||||
bool jniHackEnabled = javaVersion.major() >= 8;
|
||||
for (const auto& source : toExtract) {
|
||||
if (!unzipNatives(source, outputPath, jniHackEnabled, nativeOpenAL, nativeGLFW)) {
|
||||
if (!unzipNatives(source, outputPath, jniHackEnabled)) {
|
||||
const char* reason = QT_TR_NOOP("Couldn't extract native jar '%1' to destination '%2'");
|
||||
emit logLine(QString(reason).arg(source, outputPath), MessageLevel::Fatal);
|
||||
emitFailed(tr(reason).arg(source, outputPath));
|
||||
|
@ -53,7 +53,7 @@ LauncherPartLaunch::LauncherPartLaunch(LaunchTask* parent) : LaunchStep(parent)
|
||||
auto instance = parent->instance();
|
||||
if (instance->settings()->get("CloseAfterLaunch").toBool()) {
|
||||
std::shared_ptr<QMetaObject::Connection> connection{ new QMetaObject::Connection };
|
||||
*connection = connect(&m_process, &LoggedProcess::log, this, [=](QStringList lines, MessageLevel::Enum level) {
|
||||
*connection = connect(&m_process, &LoggedProcess::log, this, [=](QStringList lines, [[maybe_unused]] MessageLevel::Enum level) {
|
||||
qDebug() << lines;
|
||||
if (lines.filter(QRegularExpression(".*Setting user.+", QRegularExpression::CaseInsensitiveOption)).length() != 0) {
|
||||
APPLICATION->closeAllWindows();
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include "Version.h"
|
||||
|
||||
// Values taken from:
|
||||
// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_data_pack#%22pack_format%22
|
||||
// https://minecraft.wiki/w/Tutorials/Creating_a_data_pack#%22pack_format%22
|
||||
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = {
|
||||
{ 4, { Version("1.13"), Version("1.14.4") } }, { 5, { Version("1.15"), Version("1.16.1") } },
|
||||
{ 6, { Version("1.16.2"), Version("1.16.5") } }, { 7, { Version("1.17"), Version("1.17.1") } },
|
||||
|
@ -63,7 +63,7 @@ class DataPack : public Resource {
|
||||
mutable QMutex m_data_lock;
|
||||
|
||||
/* The 'version' of a data pack, as defined in the pack.mcmeta file.
|
||||
* See https://minecraft.fandom.com/wiki/Data_pack#pack.mcmeta
|
||||
* See https://minecraft.wiki/w/Data_pack#pack.mcmeta
|
||||
*/
|
||||
int m_pack_format = 0;
|
||||
|
||||
|
@ -132,17 +132,23 @@ auto Mod::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) -
|
||||
if (!preserve_metadata) {
|
||||
qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name());
|
||||
|
||||
if (metadata()) {
|
||||
Metadata::remove(index_dir, metadata()->slug);
|
||||
} else {
|
||||
auto n = name();
|
||||
Metadata::remove(index_dir, n);
|
||||
}
|
||||
destroyMetadata(index_dir);
|
||||
}
|
||||
|
||||
return Resource::destroy(attempt_trash);
|
||||
}
|
||||
|
||||
void Mod::destroyMetadata(QDir& index_dir)
|
||||
{
|
||||
if (metadata()) {
|
||||
Metadata::remove(index_dir, metadata()->slug);
|
||||
} else {
|
||||
auto n = name();
|
||||
Metadata::remove(index_dir, n);
|
||||
}
|
||||
m_local_details.metadata = nullptr;
|
||||
}
|
||||
|
||||
auto Mod::details() const -> const ModDetails&
|
||||
{
|
||||
return m_local_details;
|
||||
@ -246,7 +252,8 @@ void Mod::setIcon(QImage new_image) const
|
||||
PixmapCache::remove(m_pack_image_cache_key.key);
|
||||
|
||||
// scale the image to avoid flooding the pixmapcache
|
||||
auto pixmap = QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding));
|
||||
auto pixmap =
|
||||
QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding, Qt::SmoothTransformation));
|
||||
|
||||
m_pack_image_cache_key.key = PixmapCache::insert(pixmap);
|
||||
m_pack_image_cache_key.was_ever_used = true;
|
||||
@ -259,7 +266,7 @@ QPixmap Mod::icon(QSize size, Qt::AspectRatioMode mode) const
|
||||
if (PixmapCache::find(m_pack_image_cache_key.key, &cached_image)) {
|
||||
if (size.isNull())
|
||||
return cached_image;
|
||||
return cached_image.scaled(size, mode);
|
||||
return cached_image.scaled(size, mode, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
// No valid image we can get
|
||||
|
@ -72,7 +72,7 @@ class Mod : public Resource {
|
||||
auto metaurl() const -> QString;
|
||||
|
||||
/** Get the intneral path to the mod's icon file*/
|
||||
QString iconPath() const { return m_local_details.icon_file; };
|
||||
QString iconPath() const { return m_local_details.icon_file; }
|
||||
/** Gets the icon of the mod, converted to a QPixmap for drawing, and scaled to size. */
|
||||
[[nodiscard]] QPixmap icon(QSize size, Qt::AspectRatioMode mode = Qt::AspectRatioMode::IgnoreAspectRatio) const;
|
||||
/** Thread-safe. */
|
||||
@ -93,6 +93,8 @@ class Mod : public Resource {
|
||||
|
||||
// Delete all the files of this mod
|
||||
auto destroy(QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool;
|
||||
// Delete the metadata only
|
||||
void destroyMetadata(QDir& index_dir);
|
||||
|
||||
void finishResolvingWithDetails(ModDetails&& details);
|
||||
|
||||
|
@ -60,17 +60,17 @@ struct ModLicense {
|
||||
|
||||
ModLicense(const QString license)
|
||||
{
|
||||
// FIXME: come up with a better license parseing.
|
||||
// FIXME: come up with a better license parsing.
|
||||
// handle SPDX identifiers? https://spdx.org/licenses/
|
||||
auto parts = license.split(' ');
|
||||
QStringList notNameParts = {};
|
||||
for (auto part : parts) {
|
||||
auto url = QUrl(part);
|
||||
auto _url = QUrl(part);
|
||||
if (part.startsWith("(") && part.endsWith(")"))
|
||||
url = QUrl(part.mid(1, part.size() - 2));
|
||||
_url = QUrl(part.mid(1, part.size() - 2));
|
||||
|
||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||
this->url = url.toString();
|
||||
if (_url.isValid() && !_url.scheme().isEmpty() && !_url.host().isEmpty()) {
|
||||
this->url = _url.toString();
|
||||
notNameParts.append(part);
|
||||
continue;
|
||||
}
|
||||
@ -89,13 +89,9 @@ struct ModLicense {
|
||||
}
|
||||
}
|
||||
|
||||
ModLicense(const QString name, const QString id, const QString url, const QString description)
|
||||
{
|
||||
this->name = name;
|
||||
this->id = id;
|
||||
this->url = url;
|
||||
this->description = description;
|
||||
}
|
||||
ModLicense(const QString& name_, const QString& id_, const QString& url_, const QString& description_)
|
||||
: name(name_), id(id_), url(url_), description(description_)
|
||||
{}
|
||||
|
||||
ModLicense(const ModLicense& other) : name(other.name), id(other.id), url(other.url), description(other.description) {}
|
||||
|
||||
|
@ -51,8 +51,13 @@
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include "Json.h"
|
||||
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
||||
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
|
||||
#include "minecraft/mod/tasks/ModFolderLoadTask.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "modplatform/flame/FlameModIndex.h"
|
||||
|
||||
ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir)
|
||||
: ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed)
|
||||
@ -62,6 +67,7 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool
|
||||
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER };
|
||||
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch,
|
||||
QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents };
|
||||
m_columnsHideable = { false, true, false, true, true, true };
|
||||
}
|
||||
|
||||
QVariant ModFolderModel::data(const QModelIndex& index, int role) const
|
||||
@ -137,7 +143,7 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const
|
||||
}
|
||||
}
|
||||
|
||||
QVariant ModFolderModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
@ -227,6 +233,25 @@ bool ModFolderModel::deleteMods(const QModelIndexList& indexes)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModFolderModel::deleteModsMetadata(const QModelIndexList& indexes)
|
||||
{
|
||||
if (indexes.isEmpty())
|
||||
return true;
|
||||
|
||||
for (auto i : indexes) {
|
||||
if (i.column() != 0) {
|
||||
continue;
|
||||
}
|
||||
auto m = at(i.row());
|
||||
auto index_dir = indexDir();
|
||||
m->destroyMetadata(index_dir);
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModFolderModel::isValid()
|
||||
{
|
||||
return m_dir.exists() && m_dir.isReadable();
|
||||
@ -308,3 +333,47 @@ void ModFolderModel::onParseSucceeded(int ticket, QString mod_id)
|
||||
|
||||
emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1));
|
||||
}
|
||||
|
||||
static const FlameAPI flameAPI;
|
||||
bool ModFolderModel::installMod(QString file_path, ModPlatform::IndexedVersion& vers)
|
||||
{
|
||||
if (vers.addonId.isValid()) {
|
||||
ModPlatform::IndexedPack pack{
|
||||
vers.addonId,
|
||||
ModPlatform::ResourceProvider::FLAME,
|
||||
};
|
||||
|
||||
QEventLoop loop;
|
||||
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
auto job = flameAPI.getProject(vers.addonId.toString(), response);
|
||||
|
||||
QObject::connect(job.get(), &Task::failed, [&loop] { loop.quit(); });
|
||||
QObject::connect(job.get(), &Task::aborted, &loop, &QEventLoop::quit);
|
||||
QObject::connect(job.get(), &Task::succeeded, [response, this, &vers, &loop, &pack] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qDebug() << *response;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
auto obj = Json::requireObject(Json::requireObject(doc), "data");
|
||||
FlameMod::loadIndexedPack(pack, obj);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << doc;
|
||||
qWarning() << "Error while reading mod info: " << e.cause();
|
||||
}
|
||||
LocalModUpdateTask update_metadata(indexDir(), pack, vers);
|
||||
QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit);
|
||||
update_metadata.start();
|
||||
});
|
||||
|
||||
job->start();
|
||||
|
||||
loop.exec();
|
||||
}
|
||||
return ResourceFolderModel::installResource(file_path);
|
||||
}
|
||||
|
@ -48,6 +48,7 @@
|
||||
|
||||
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
||||
#include "minecraft/mod/tasks/ModFolderLoadTask.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
class LegacyInstance;
|
||||
class BaseInstance;
|
||||
@ -75,10 +76,12 @@ class ModFolderModel : public ResourceFolderModel {
|
||||
[[nodiscard]] Task* createParseTask(Resource&) override;
|
||||
|
||||
bool installMod(QString file_path) { return ResourceFolderModel::installResource(file_path); }
|
||||
bool installMod(QString file_path, ModPlatform::IndexedVersion& vers);
|
||||
bool uninstallMod(const QString& filename, bool preserve_metadata = false);
|
||||
|
||||
/// Deletes all the selected mods
|
||||
bool deleteMods(const QModelIndexList& indexes);
|
||||
bool deleteModsMetadata(const QModelIndexList& indexes);
|
||||
|
||||
bool isValid();
|
||||
|
||||
|
@ -447,7 +447,7 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const
|
||||
}
|
||||
}
|
||||
|
||||
bool ResourceFolderModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
||||
bool ResourceFolderModel::setData(const QModelIndex& index, [[maybe_unused]] const QVariant& value, int role)
|
||||
{
|
||||
int row = index.row();
|
||||
if (row < 0 || row >= rowCount(index.parent()) || !index.isValid())
|
||||
@ -471,7 +471,7 @@ bool ResourceFolderModel::setData(const QModelIndex& index, const QVariant& valu
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant ResourceFolderModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
QVariant ResourceFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
@ -551,6 +551,9 @@ QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree)
|
||||
menu->addSeparator()->setText(tr("Show / Hide Columns"));
|
||||
|
||||
for (int col = 0; col < columnCount(); ++col) {
|
||||
// Skip creating actions for columns that should not be hidden
|
||||
if (!m_columnsHideable.at(col))
|
||||
continue;
|
||||
auto act = new QAction(menu);
|
||||
setupHeaderAction(act, col);
|
||||
|
||||
@ -584,7 +587,8 @@ SortType ResourceFolderModel::columnToSortKey(size_t column) const
|
||||
}
|
||||
|
||||
/* Standard Proxy Model for createFilterProxyModel */
|
||||
[[nodiscard]] bool ResourceFolderModel::ProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
|
||||
[[nodiscard]] bool ResourceFolderModel::ProxyModel::filterAcceptsRow(int source_row,
|
||||
[[maybe_unused]] const QModelIndex& source_parent) const
|
||||
{
|
||||
auto* model = qobject_cast<ResourceFolderModel*>(sourceModel());
|
||||
if (!model)
|
||||
|
@ -49,8 +49,8 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
bool stopWatching(const QStringList paths);
|
||||
|
||||
/* Helper methods for subclasses, using a predetermined list of paths. */
|
||||
virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); };
|
||||
virtual bool stopWatching() { return stopWatching({ m_dir.absolutePath() }); };
|
||||
virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); }
|
||||
virtual bool stopWatching() { return stopWatching({ m_dir.absolutePath() }); }
|
||||
|
||||
/** Given a path in the system, install that resource, moving it to its place in the
|
||||
* instance file hierarchy.
|
||||
@ -78,7 +78,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
/** Creates a new parse task, if needed, for 'res' and start it.*/
|
||||
virtual void resolveResource(Resource* res);
|
||||
|
||||
[[nodiscard]] size_t size() const { return m_resources.size(); };
|
||||
[[nodiscard]] qsizetype size() const { return m_resources.size(); }
|
||||
[[nodiscard]] bool empty() const { return size() == 0; }
|
||||
[[nodiscard]] Resource& at(int index) { return *m_resources.at(index); }
|
||||
[[nodiscard]] Resource const& at(int index) const { return *m_resources.at(index); }
|
||||
@ -97,10 +97,10 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
|
||||
/* Basic columns */
|
||||
enum Columns { ACTIVE_COLUMN = 0, NAME_COLUMN, DATE_COLUMN, NUM_COLUMNS };
|
||||
QStringList columnNames(bool translated = true) const { return translated ? m_column_names_translated : m_column_names; };
|
||||
QStringList columnNames(bool translated = true) const { return translated ? m_column_names_translated : m_column_names; }
|
||||
|
||||
[[nodiscard]] int rowCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : static_cast<int>(size()); }
|
||||
[[nodiscard]] int columnCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : NUM_COLUMNS; };
|
||||
[[nodiscard]] int columnCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : NUM_COLUMNS; }
|
||||
|
||||
[[nodiscard]] Qt::DropActions supportedDropActions() const override;
|
||||
|
||||
@ -159,7 +159,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
* This task should load and parse all heavy info needed by a resource, such as parsing a manifest. It gets executed
|
||||
* in the background, so it slowly updates the UI as tasks get done.
|
||||
*/
|
||||
[[nodiscard]] virtual Task* createParseTask(Resource&) { return nullptr; };
|
||||
[[nodiscard]] virtual Task* createParseTask(Resource&) { return nullptr; }
|
||||
|
||||
/** Standard implementation of the model update logic.
|
||||
*
|
||||
@ -203,6 +203,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified") };
|
||||
QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch,
|
||||
QHeaderView::ResizeToContents };
|
||||
QList<bool> m_columnsHideable = { false, false, true };
|
||||
|
||||
QDir m_dir;
|
||||
BaseInstance* m_instance;
|
||||
@ -224,15 +225,15 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
|
||||
/* A macro to define useful functions to handle Resource* -> T* more easily on derived classes */
|
||||
#define RESOURCE_HELPERS(T) \
|
||||
[[nodiscard]] T* operator[](size_t index) \
|
||||
[[nodiscard]] T* operator[](int index) \
|
||||
{ \
|
||||
return static_cast<T*>(m_resources[index].get()); \
|
||||
} \
|
||||
[[nodiscard]] T* at(size_t index) \
|
||||
[[nodiscard]] T* at(int index) \
|
||||
{ \
|
||||
return static_cast<T*>(m_resources[index].get()); \
|
||||
} \
|
||||
[[nodiscard]] const T* at(size_t index) const \
|
||||
[[nodiscard]] const T* at(int index) const \
|
||||
{ \
|
||||
return static_cast<const T*>(m_resources.at(index).get()); \
|
||||
} \
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "minecraft/mod/tasks/LocalResourcePackParseTask.h"
|
||||
|
||||
// Values taken from:
|
||||
// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
||||
// https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
||||
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = {
|
||||
{ 1, { Version("1.6.1"), Version("1.8.9") } }, { 2, { Version("1.9"), Version("1.10.2") } },
|
||||
{ 3, { Version("1.11"), Version("1.12.2") } }, { 4, { Version("1.13"), Version("1.14.4") } },
|
||||
@ -50,7 +50,8 @@ void ResourcePack::setImage(QImage new_image) const
|
||||
PixmapCache::instance().remove(m_pack_image_cache_key.key);
|
||||
|
||||
// scale the image to avoid flooding the pixmapcache
|
||||
auto pixmap = QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding));
|
||||
auto pixmap =
|
||||
QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding, Qt::SmoothTransformation));
|
||||
|
||||
m_pack_image_cache_key.key = PixmapCache::instance().insert(pixmap);
|
||||
m_pack_image_cache_key.was_ever_used = true;
|
||||
@ -68,7 +69,7 @@ QPixmap ResourcePack::image(QSize size, Qt::AspectRatioMode mode) const
|
||||
if (PixmapCache::instance().find(m_pack_image_cache_key.key, &cached_image)) {
|
||||
if (size.isNull())
|
||||
return cached_image;
|
||||
return cached_image.scaled(size, mode);
|
||||
return cached_image.scaled(size, mode, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
// No valid image we can get
|
||||
|
@ -51,7 +51,7 @@ class ResourcePack : public Resource {
|
||||
mutable QMutex m_data_lock;
|
||||
|
||||
/* The 'version' of a resource pack, as defined in the pack.mcmeta file.
|
||||
* See https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
||||
* See https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
||||
*/
|
||||
int m_pack_format = 0;
|
||||
|
||||
|
@ -54,6 +54,7 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstanc
|
||||
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE };
|
||||
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents,
|
||||
QHeaderView::ResizeToContents };
|
||||
m_columnsHideable = { false, true, false, true, true };
|
||||
}
|
||||
|
||||
QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
@ -128,7 +129,7 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
}
|
||||
}
|
||||
|
||||
QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
QVariant ResourcePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
@ -165,7 +166,6 @@ QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orient
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
int ResourcePackFolderModel::columnCount(const QModelIndex& parent) const
|
||||
|
@ -44,7 +44,8 @@ void TexturePack::setImage(QImage new_image) const
|
||||
PixmapCache::remove(m_pack_image_cache_key.key);
|
||||
|
||||
// scale the image to avoid flooding the pixmapcache
|
||||
auto pixmap = QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding));
|
||||
auto pixmap =
|
||||
QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding, Qt::SmoothTransformation));
|
||||
|
||||
m_pack_image_cache_key.key = PixmapCache::insert(pixmap);
|
||||
m_pack_image_cache_key.was_ever_used = true;
|
||||
@ -56,7 +57,7 @@ QPixmap TexturePack::image(QSize size, Qt::AspectRatioMode mode) const
|
||||
if (PixmapCache::find(m_pack_image_cache_key.key, &cached_image)) {
|
||||
if (size.isNull())
|
||||
return cached_image;
|
||||
return cached_image.scaled(size, mode);
|
||||
return cached_image.scaled(size, mode, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
// No valid image we can get
|
||||
|
@ -49,6 +49,7 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance*
|
||||
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE };
|
||||
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch,
|
||||
QHeaderView::ResizeToContents };
|
||||
m_columnsHideable = { false, true, false, true };
|
||||
}
|
||||
|
||||
Task* TexturePackFolderModel::createUpdateTask()
|
||||
@ -113,7 +114,7 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
}
|
||||
}
|
||||
|
||||
QVariant TexturePackFolderModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
QVariant TexturePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
|
@ -39,9 +39,9 @@ static Version mcVersion(BaseInstance* inst)
|
||||
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion();
|
||||
}
|
||||
|
||||
static ResourceAPI::ModLoaderTypes mcLoaders(BaseInstance* inst)
|
||||
static ModPlatform::ModLoaderTypes mcLoaders(BaseInstance* inst)
|
||||
{
|
||||
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getModLoaders().value();
|
||||
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getSupportedModLoaders().value();
|
||||
}
|
||||
|
||||
GetModDependenciesTask::GetModDependenciesTask(QObject* parent,
|
||||
@ -61,7 +61,7 @@ GetModDependenciesTask::GetModDependenciesTask(QObject* parent,
|
||||
if (auto meta = mod->metadata(); meta)
|
||||
m_mods.append(meta);
|
||||
prepare();
|
||||
};
|
||||
}
|
||||
|
||||
void GetModDependenciesTask::prepare()
|
||||
{
|
||||
@ -75,7 +75,7 @@ void GetModDependenciesTask::prepare()
|
||||
ModPlatform::Dependency GetModDependenciesTask::getOverride(const ModPlatform::Dependency& dep,
|
||||
const ModPlatform::ResourceProvider providerName)
|
||||
{
|
||||
if (auto isQuilt = m_loaderType & ResourceAPI::Quilt; isQuilt || m_loaderType & ResourceAPI::Fabric) {
|
||||
if (auto isQuilt = m_loaderType & ModPlatform::Quilt; isQuilt || m_loaderType & ModPlatform::Fabric) {
|
||||
auto overide = ModPlatform::getOverrideDeps();
|
||||
auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, providerName, isQuilt](auto o) {
|
||||
return o.provider == providerName && dep.addonId == (isQuilt ? o.fabric : o.quilt);
|
||||
@ -130,7 +130,7 @@ QList<ModPlatform::Dependency> GetModDependenciesTask::getDependenciesForVersion
|
||||
c_dependencies.append(getOverride(ver_dep, providerName));
|
||||
}
|
||||
return c_dependencies;
|
||||
};
|
||||
}
|
||||
|
||||
Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr<PackDependency> pDep)
|
||||
{
|
||||
@ -181,7 +181,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
|
||||
ResourceAPI::DependencySearchArgs args = { dep, m_version, m_loaderType };
|
||||
ResourceAPI::DependencySearchCallbacks callbacks;
|
||||
|
||||
callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, auto& pack) {
|
||||
callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, [[maybe_unused]] auto& pack) {
|
||||
try {
|
||||
QJsonArray arr;
|
||||
if (dep.version.length() != 0 && doc.isObject()) {
|
||||
@ -191,7 +191,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
|
||||
}
|
||||
pDep->version = provider.mod->loadDependencyVersions(dep, arr);
|
||||
if (!pDep->version.addonId.isValid()) {
|
||||
if (m_loaderType & ResourceAPI::Quilt) { // falback for quilt
|
||||
if (m_loaderType & ModPlatform::Quilt) { // falback for quilt
|
||||
auto overide = ModPlatform::getOverrideDeps();
|
||||
auto over = std::find_if(overide.cbegin(), overide.cend(),
|
||||
[dep, provider](auto o) { return o.provider == provider.name && dep.addonId == o.quilt; });
|
||||
@ -201,6 +201,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
|
||||
return;
|
||||
}
|
||||
}
|
||||
removePack(dep.addonId);
|
||||
qWarning() << "Error while reading mod version empty ";
|
||||
qDebug() << doc;
|
||||
return;
|
||||
@ -215,27 +216,27 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
|
||||
return;
|
||||
}
|
||||
if (level == 0) {
|
||||
qWarning() << "Dependency cycle exeeded";
|
||||
qWarning() << "Dependency cycle exceeded";
|
||||
return;
|
||||
}
|
||||
if (dep.addonId.toString().isEmpty() && !pDep->version.addonId.toString().isEmpty()) {
|
||||
pDep->pack->addonId = pDep->version.addonId;
|
||||
auto dep = getOverride({ pDep->version.addonId, pDep->dependency.type }, provider.name);
|
||||
if (dep.addonId != pDep->version.addonId) {
|
||||
auto dep_ = getOverride({ pDep->version.addonId, pDep->dependency.type }, provider.name);
|
||||
if (dep_.addonId != pDep->version.addonId) {
|
||||
removePack(pDep->version.addonId);
|
||||
addTask(prepareDependencyTask(dep, provider.name, level));
|
||||
addTask(prepareDependencyTask(dep_, provider.name, level));
|
||||
} else
|
||||
addTask(getProjectInfoTask(pDep));
|
||||
}
|
||||
for (auto dep : getDependenciesForVersion(pDep->version, provider.name)) {
|
||||
addTask(prepareDependencyTask(dep, provider.name, level - 1));
|
||||
for (auto dep_ : getDependenciesForVersion(pDep->version, provider.name)) {
|
||||
addTask(prepareDependencyTask(dep_, provider.name, level - 1));
|
||||
}
|
||||
};
|
||||
|
||||
auto version = provider.api->getDependencyVersion(std::move(args), std::move(callbacks));
|
||||
tasks->addTask(version);
|
||||
return tasks;
|
||||
};
|
||||
}
|
||||
|
||||
void GetModDependenciesTask::removePack(const QVariant addonId)
|
||||
{
|
||||
|
@ -80,5 +80,5 @@ class GetModDependenciesTask : public SequentialTask {
|
||||
Provider m_modrinth_provider;
|
||||
|
||||
Version m_version;
|
||||
ResourceAPI::ModLoaderTypes m_loaderType;
|
||||
ModPlatform::ModLoaderTypes m_loaderType;
|
||||
};
|
||||
|
@ -133,7 +133,7 @@ bool processZIP(DataPack& pack, ProcessingLevel level)
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://minecraft.fandom.com/wiki/Data_pack#pack.mcmeta
|
||||
// https://minecraft.wiki/w/Data_pack#pack.mcmeta
|
||||
bool processMCMeta(DataPack& pack, QByteArray&& raw_data)
|
||||
{
|
||||
try {
|
||||
|
@ -104,14 +104,15 @@ ModDetails ReadMCModTOML(QByteArray contents)
|
||||
#if TOML_EXCEPTIONS
|
||||
try {
|
||||
tomlData = toml::parse(contents.toStdString());
|
||||
} catch (const toml::parse_error& err) {
|
||||
} catch ([[maybe_unused]] const toml::parse_error& err) {
|
||||
return {};
|
||||
}
|
||||
#else
|
||||
tomlData = toml::parse(contents.toStdString());
|
||||
if (!tomlData) {
|
||||
toml::parse_result result = toml::parse(contents.toStdString());
|
||||
if (!result) {
|
||||
return {};
|
||||
}
|
||||
tomlData = result.table();
|
||||
#endif
|
||||
|
||||
// array defined by [[mods]]
|
||||
@ -151,8 +152,8 @@ ModDetails ReadMCModTOML(QByteArray contents)
|
||||
QString authors = "";
|
||||
if (auto authorsDatum = tomlData["authors"].as_string()) {
|
||||
authors = QString::fromStdString(authorsDatum->get());
|
||||
} else if (auto authorsDatum = (*modsTable)["authors"].as_string()) {
|
||||
authors = QString::fromStdString(authorsDatum->get());
|
||||
} else if (auto authorsDatumMods = (*modsTable)["authors"].as_string()) {
|
||||
authors = QString::fromStdString(authorsDatumMods->get());
|
||||
}
|
||||
if (!authors.isEmpty()) {
|
||||
details.authors.append(authors);
|
||||
@ -161,8 +162,8 @@ ModDetails ReadMCModTOML(QByteArray contents)
|
||||
QString homeurl = "";
|
||||
if (auto homeurlDatum = tomlData["displayURL"].as_string()) {
|
||||
homeurl = QString::fromStdString(homeurlDatum->get());
|
||||
} else if (auto homeurlDatum = (*modsTable)["displayURL"].as_string()) {
|
||||
homeurl = QString::fromStdString(homeurlDatum->get());
|
||||
} else if (auto homeurlDatumMods = (*modsTable)["displayURL"].as_string()) {
|
||||
homeurl = QString::fromStdString(homeurlDatumMods->get());
|
||||
}
|
||||
// fix up url.
|
||||
if (!homeurl.isEmpty() && !homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://")) {
|
||||
@ -173,16 +174,16 @@ ModDetails ReadMCModTOML(QByteArray contents)
|
||||
QString issueTrackerURL = "";
|
||||
if (auto issueTrackerURLDatum = tomlData["issueTrackerURL"].as_string()) {
|
||||
issueTrackerURL = QString::fromStdString(issueTrackerURLDatum->get());
|
||||
} else if (auto issueTrackerURLDatum = (*modsTable)["issueTrackerURL"].as_string()) {
|
||||
issueTrackerURL = QString::fromStdString(issueTrackerURLDatum->get());
|
||||
} else if (auto issueTrackerURLDatumMods = (*modsTable)["issueTrackerURL"].as_string()) {
|
||||
issueTrackerURL = QString::fromStdString(issueTrackerURLDatumMods->get());
|
||||
}
|
||||
details.issue_tracker = issueTrackerURL;
|
||||
|
||||
QString license = "";
|
||||
if (auto licenseDatum = tomlData["license"].as_string()) {
|
||||
license = QString::fromStdString(licenseDatum->get());
|
||||
} else if (auto licenseDatum = (*modsTable)["license"].as_string()) {
|
||||
license = QString::fromStdString(licenseDatum->get());
|
||||
} else if (auto licenseDatumMods = (*modsTable)["license"].as_string()) {
|
||||
license = QString::fromStdString(licenseDatumMods->get());
|
||||
}
|
||||
if (!license.isEmpty())
|
||||
details.licenses.append(ModLicense(license));
|
||||
@ -190,8 +191,8 @@ ModDetails ReadMCModTOML(QByteArray contents)
|
||||
QString logoFile = "";
|
||||
if (auto logoFileDatum = tomlData["logoFile"].as_string()) {
|
||||
logoFile = QString::fromStdString(logoFileDatum->get());
|
||||
} else if (auto logoFileDatum = (*modsTable)["logoFile"].as_string()) {
|
||||
logoFile = QString::fromStdString(logoFileDatum->get());
|
||||
} else if (auto logoFileDatumMods = (*modsTable)["logoFile"].as_string()) {
|
||||
logoFile = QString::fromStdString(logoFileDatumMods->get());
|
||||
}
|
||||
details.icon_file = logoFile;
|
||||
|
||||
@ -458,7 +459,7 @@ bool process(Mod& mod, ProcessingLevel level)
|
||||
}
|
||||
}
|
||||
|
||||
bool processZIP(Mod& mod, ProcessingLevel level)
|
||||
bool processZIP(Mod& mod, [[maybe_unused]] ProcessingLevel level)
|
||||
{
|
||||
ModDetails details;
|
||||
|
||||
@ -591,7 +592,7 @@ bool processZIP(Mod& mod, ProcessingLevel level)
|
||||
return false; // no valid mod found in archive
|
||||
}
|
||||
|
||||
bool processFolder(Mod& mod, ProcessingLevel level)
|
||||
bool processFolder(Mod& mod, [[maybe_unused]] ProcessingLevel level)
|
||||
{
|
||||
ModDetails details;
|
||||
|
||||
@ -612,7 +613,7 @@ bool processFolder(Mod& mod, ProcessingLevel level)
|
||||
return false; // no valid mcmod.info file found
|
||||
}
|
||||
|
||||
bool processLitemod(Mod& mod, ProcessingLevel level)
|
||||
bool processLitemod(Mod& mod, [[maybe_unused]] ProcessingLevel level)
|
||||
{
|
||||
ModDetails details;
|
||||
|
||||
|
@ -178,7 +178,7 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level)
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
||||
// https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
||||
bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data)
|
||||
{
|
||||
try {
|
||||
|
@ -42,7 +42,7 @@
|
||||
|
||||
CapeChange::CapeChange(QObject* parent, QString token, QString cape) : Task(parent), m_capeId(cape), m_token(token) {}
|
||||
|
||||
void CapeChange::setCape(QString& cape)
|
||||
void CapeChange::setCape([[maybe_unused]] QString& cape)
|
||||
{
|
||||
QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active"));
|
||||
auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId);
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include "net/ApiDownload.h"
|
||||
|
||||
AssetUpdateTask::AssetUpdateTask(MinecraftInstance* inst)
|
||||
{
|
||||
m_inst = inst;
|
||||
@ -29,7 +31,7 @@ void AssetUpdateTask::executeTask()
|
||||
entry->setStale(true);
|
||||
auto hexSha1 = assets->sha1.toLatin1();
|
||||
qDebug() << "Asset index SHA1:" << hexSha1;
|
||||
auto dl = Net::Download::makeCached(indexUrl, entry);
|
||||
auto dl = Net::ApiDownload::makeCached(indexUrl, entry);
|
||||
auto rawSha1 = QByteArray::fromHex(assets->sha1.toLatin1());
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
|
||||
job->addNetAction(dl);
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
|
||||
#include "net/ApiDownload.h"
|
||||
|
||||
FMLLibrariesTask::FMLLibrariesTask(MinecraftInstance* inst)
|
||||
{
|
||||
m_inst = inst;
|
||||
@ -62,7 +64,7 @@ void FMLLibrariesTask::executeTask()
|
||||
for (auto& lib : fmlLibsToProcess) {
|
||||
auto entry = metacache->resolveEntry("fmllibs", lib.filename);
|
||||
QString urlString = BuildConfig.FMLLIBS_BASE_URL + lib.filename;
|
||||
dljob->addNetAction(Net::Download::makeCached(QUrl(urlString), entry, options));
|
||||
dljob->addNetAction(Net::ApiDownload::makeCached(QUrl(urlString), entry, options));
|
||||
}
|
||||
|
||||
connect(dljob.get(), &NetJob::succeeded, this, &FMLLibrariesTask::fmllibsFinished);
|
||||
|
@ -14,7 +14,7 @@ class CheckUpdateTask : public Task {
|
||||
public:
|
||||
CheckUpdateTask(QList<Mod*>& mods,
|
||||
std::list<Version>& mcVersions,
|
||||
std::optional<ResourceAPI::ModLoaderTypes> loaders,
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||
std::shared_ptr<ModFolderModel> mods_folder)
|
||||
: Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder){};
|
||||
|
||||
@ -62,7 +62,7 @@ class CheckUpdateTask : public Task {
|
||||
protected:
|
||||
QList<Mod*>& m_mods;
|
||||
std::list<Version>& m_game_versions;
|
||||
std::optional<ResourceAPI::ModLoaderTypes> m_loaders;
|
||||
std::optional<ModPlatform::ModLoaderTypes> m_loaders;
|
||||
std::shared_ptr<ModFolderModel> m_mods_folder;
|
||||
|
||||
std::vector<UpdatableMod> m_updatable;
|
||||
|
@ -134,4 +134,25 @@ QString getMetaURL(ResourceProvider provider, QVariant projectID)
|
||||
projectID.toString();
|
||||
}
|
||||
|
||||
auto getModLoaderString(ModLoaderType type) -> const QString
|
||||
{
|
||||
switch (type) {
|
||||
case NeoForge:
|
||||
return "neoforge";
|
||||
case Forge:
|
||||
return "forge";
|
||||
case Cauldron:
|
||||
return "cauldron";
|
||||
case LiteLoader:
|
||||
return "liteloader";
|
||||
case Fabric:
|
||||
return "fabric";
|
||||
case Quilt:
|
||||
return "quilt";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace ModPlatform
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user