diff options
197 files changed, 2882 insertions, 1992 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c710d54b..fc223a07 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,6 +24,12 @@ on: CACHIX_AUTH_TOKEN: description: Private token for authenticating against Cachix cache required: false + GPG_PRIVATE_KEY: + description: Private key for AppImage signing + required: false + GPG_PRIVATE_KEY_ID: + description: ID for the GPG_PRIVATE_KEY, to select the signing key + required: false jobs: build: @@ -68,7 +74,7 @@ jobs: qt_ver: 6 qt_host: windows qt_arch: '' - qt_version: '6.5.1' + qt_version: '6.5.2' qt_modules: 'qt5compat qtimageformats' qt_tools: '' @@ -80,7 +86,7 @@ jobs: qt_ver: 6 qt_host: windows qt_arch: 'win64_msvc2019_arm64' - qt_version: '6.5.1' + qt_version: '6.5.2' qt_modules: 'qt5compat qtimageformats' qt_tools: '' @@ -90,7 +96,7 @@ jobs: qt_ver: 6 qt_host: mac qt_arch: '' - qt_version: '6.5.0' + qt_version: '6.5.2' qt_modules: 'qt5compat qtimageformats' qt_tools: '' @@ -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 != '' diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index f19b8398..2a46ff5e 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -43,7 +43,8 @@ jobs: mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz - mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage + mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage + mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz @@ -78,9 +79,8 @@ jobs: - name: Create release id: create_release uses: softprops/action-gh-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: + token: ${{ secrets.GITHUB_TOKEN }} tag_name: ${{ github.ref }} name: Prism Launcher ${{ env.VERSION }} draft: true @@ -88,7 +88,8 @@ jobs: files: | PrismLauncher-Linux-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz - PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage + PrismLauncher-Linux-x86_64.AppImage + PrismLauncher-Linux-x86_64.AppImage.zsync PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip diff --git a/CMakeLists.txt b/CMakeLists.txt index af49f665..a2853cb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,13 @@ if(MSVC) # Use /W4 as /Wall includes unnesserey warnings such as added padding to structs set(CMAKE_CXX_FLAGS "/GS /permissive- /W4 ${CMAKE_CXX_FLAGS}") + # /EHs Enables stack unwind semantics for standard C++ exceptions to ensure stackframes are unwound + # and object deconstructors are called when an exception is caught. + # without it memory leaks and a warning is printed + # /EHc tells the compiler to assume that functions declared as extern "C" never throw a C++ exception + # This appears to not always be a defualt compiler option in CMAKE + set(CMAKE_CXX_FLAGS "/EHsc ${CMAKE_CXX_FLAGS}") + # LINK accepts /SUBSYSTEM whics sets if we are a WINDOWS (gui) or a CONSOLE programs # This implicitly selects an entrypoint specific to the subsystem selected # qtmain/QtEntryPointLib provides the correct entrypoint (wWinMain) for gui programs @@ -85,38 +92,39 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0") # set CXXFLAGS for build targets set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}") -option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" on) +option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF) # If this is a Debug build turn on address sanitiser -if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND DEBUG_ADDRESS_SANITIZER) +if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") AND DEBUG_ADDRESS_SANITIZER) message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off") if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") # using clang with clang-cl front end message(STATUS "Address Sanitizer available on Clang MSVC frontend") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-") else() # AppleClang and Clang message(STATUS "Address Sanitizer available on Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") endif() elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # GCC message(STATUS "Address Sanitizer available on GCC") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") link_libraries("asan") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") message(STATUS "Address Sanitizer available on MSVC") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-") else() message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}") endif() endif() + option(ENABLE_LTO "Enable Link Time Optimization" off) if(ENABLE_LTO) @@ -318,6 +326,8 @@ add_subdirectory(program_info) ####################################### Install layout ####################################### +set(Launcher_ENABLE_UPDATER NO) + if(NOT (UNIX AND APPLE)) # Install "portable.txt" if selected component is "portable" install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Portable_File}" DESTINATION "." COMPONENT portable EXCLUDE_FROM_ALL) @@ -343,8 +353,8 @@ if(UNIX AND APPLE) set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns) set(MACOSX_BUNDLE_COPYRIGHT "© 2022-2023 ${Launcher_Copyright_Mac}") - set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=") - set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml") + set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed") + set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed") set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0/Sparkle-2.1.0.tar.xz" CACHE STRING "URL to Sparkle release archive") set(MACOSX_SPARKLE_SHA256 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive") @@ -353,8 +363,12 @@ if(UNIX AND APPLE) # directories to look for dependencies set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${MACOSX_SPARKLE_DIR}) + if(NOT MACOSX_SPARKLE_UPDATE_PUBLIC_KEY STREQUAL "" AND NOT MACOSX_SPARKLE_UPDATE_FEED_URL STREQUAL "") + set(Launcher_ENABLE_UPDATER YES) + endif() + # install as bundle - set(INSTALL_BUNDLE "full") + set(INSTALL_BUNDLE "full" CACHE STRING "Use fixup_bundle to bundle dependencies") # Add the icon install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns) @@ -367,7 +381,7 @@ elseif(UNIX) set(JARS_DEST_DIR "share/${Launcher_Name}") # install as bundle with no dependencies included - set(INSTALL_BUNDLE "nodeps") + set(INSTALL_BUNDLE "nodeps" CACHE STRING "Use fixup_bundle to bundle dependencies") # Set RPATH SET(Launcher_BINARY_RPATH "$ORIGIN/") @@ -401,7 +415,7 @@ elseif(WIN32) set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) # install as bundle - set(INSTALL_BUNDLE "full") + set(INSTALL_BUNDLE "full" CACHE STRING "Use fixup_bundle to bundle dependencies") else() message(FATAL_ERROR "Platform not supported") endif() diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake new file mode 100644 index 00000000..635e5428 --- /dev/null +++ b/cmake/CompilerWarnings.cmake @@ -0,0 +1,152 @@ +# +# Function to set compiler warnings with reasonable defaults at the project level. +# Taken from https://github.com/aminya/project_options/blob/main/src/CompilerWarnings.cmake +# under the folowing license: +# +# MIT License +# +# Copyright (c) 2022-2100 Amin Yahyaabadi +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +include_guard() + +function(_set_project_warnings_add_target_link_option TARGET OPTIONS) + target_link_options(${_project_name} INTERFACE ${OPTIONS}) +endfunction() + +# Set the compiler warnings +# +# https://clang.llvm.org/docs/DiagnosticsReference.html +# https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md +function( + set_project_warnings + _project_name + MSVC_WARNINGS + CLANG_WARNINGS + GCC_WARNINGS +) + if("${MSVC_WARNINGS}" STREQUAL "") + set(MSVC_WARNINGS + /W4 # Baseline reasonable warnings + /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data + /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /w14263 # 'function': member function does not override any base class virtual member function + /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not + # be destructed correctly + /w14287 # 'operator': unsigned/negative constant mismatch + /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside + # the for-loop scope + /w14296 # 'operator': expression is always 'boolean_value' + /w14311 # 'variable': pointer truncation from 'type1' to 'type2' + /w14545 # expression before comma evaluates to a function which is missing an argument list + /w14546 # function call before comma missing argument list + /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect + /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'? + /w14555 # expression has no effect; expected expression with side- effect + /w14619 # pragma warning: there is no warning number 'number' + /w14640 # Enable warning on thread un-safe static member initialization + /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior. + /w14905 # wide string literal cast to 'LPSTR' + /w14906 # string literal cast to 'LPWSTR' + /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied + /permissive- # standards conformance mode for MSVC compiler. + ) + endif() + + if("${CLANG_WARNINGS}" STREQUAL "") + set(CLANG_WARNINGS + -Wall + -Wextra # reasonable and standard + -Wextra-semi # Warn about semicolon after in-class function definition. + -Wshadow # warn the user if a variable declaration shadows one from a parent context + -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps + # catch hard to track down memory errors + -Wold-style-cast # warn for c-style casts + -Wcast-align # warn for potential performance problem casts + -Wunused # warn on anything being unused + -Woverloaded-virtual # warn if you overload (not override) a virtual function + -Wpedantic # warn if non-standard C++ is used + -Wconversion # warn on type conversions that may lose data + -Wsign-conversion # warn on sign conversions + -Wnull-dereference # warn if a null dereference is detected + -Wdouble-promotion # warn if float is implicit promoted to double + -Wformat=2 # warn on security issues around functions that format output (ie printf) + -Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation + ) + endif() + + if("${GCC_WARNINGS}" STREQUAL "") + set(GCC_WARNINGS + ${CLANG_WARNINGS} + -Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist + -Wduplicated-cond # warn if if / else chain has duplicated conditions + -Wduplicated-branches # warn if if / else branches have duplicated code + -Wlogical-op # warn about logical operations being used where bitwise were probably wanted + -Wuseless-cast # warn if you perform a cast to the same type + ) + endif() + + if(MSVC) + set(PROJECT_WARNINGS_CXX ${MSVC_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(PROJECT_WARNINGS_CXX ${CLANG_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(PROJECT_WARNINGS_CXX ${GCC_WARNINGS}) + else() + message(AUTHOR_WARNING "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'") + # TODO support Intel compiler + endif() + + # Add C warnings + set(PROJECT_WARNINGS_C "${PROJECT_WARNINGS_CXX}") + list( + REMOVE_ITEM + PROJECT_WARNINGS_C + -Wnon-virtual-dtor + -Wold-style-cast + -Woverloaded-virtual + -Wuseless-cast + -Wextra-semi + ) + + target_compile_options( + ${_project_name} + INTERFACE # C++ warnings + $<$<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() diff --git a/launcher/Application.cpp b/launcher/Application.cpp index c594a297..e89b7659 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -131,17 +131,12 @@ #include "MangoHud.h" #endif -#ifdef Q_OS_MAC +#if defined(Q_OS_MAC) && defined(SPARKLE_ENABLED) #include "updater/MacSparkleUpdater.h" #endif - #if defined Q_OS_WIN32 -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include <windows.h> -#include <stdio.h> +#include "WindowsConsole.h" #endif #define STRINGIFY(x) #x @@ -168,31 +163,15 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QSt fflush(stderr); } -} +} // namespace -Application::Application(int &argc, char **argv) : QApplication(argc, argv) + + +Application::Application(int& argc, char** argv) : QApplication(argc, argv) { #if defined Q_OS_WIN32 - // attach the parent console - if(AttachConsole(ATTACH_PARENT_PROCESS)) - { - // if attach succeeds, reopen and sync all the i/o - if(freopen("CON", "w", stdout)) - { - std::cout.sync_with_stdio(); - } - if(freopen("CON", "w", stderr)) - { - std::cerr.sync_with_stdio(); - } - if(freopen("CON", "r", stdin)) - { - std::cin.sync_with_stdio(); - } - auto out = GetStdHandle (STD_OUTPUT_HANDLE); - DWORD written; - const char * endline = "\n"; - WriteConsole(out, endline, strlen(endline), &written, NULL); + // attach the parent console if stdout not already captured + if (AttachWindowsConsole()) { consoleAttached = true; } #endif @@ -710,7 +689,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) QUrl metaUrl(m_settings->get("MetaURLOverride").toString()); // get rid of invalid meta urls - if (!metaUrl.isValid() || metaUrl.scheme() != "http" || metaUrl.scheme() != "https") + if (!metaUrl.isValid() || (metaUrl.scheme() != "http" && metaUrl.scheme() != "https")) m_settings->reset("MetaURLOverride"); } @@ -782,7 +761,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) if(BuildConfig.UPDATER_ENABLED) { qDebug() << "Initializing updater"; -#ifdef Q_OS_MAC +#if defined(Q_OS_MAC) && defined(SPARKLE_ENABLED) m_updater.reset(new MacSparkleUpdater()); #endif qDebug() << "<> Updater started."; diff --git a/launcher/BaseInstance.h b/launcher/BaseInstance.h index 83a8064f..f6b5a7a5 100644 --- a/launcher/BaseInstance.h +++ b/launcher/BaseInstance.h @@ -88,7 +88,7 @@ public: /* types */ public: /// virtual destructor to make sure the destruction is COMPLETE - virtual ~BaseInstance() {}; + virtual ~BaseInstance() {} virtual void saveNow() = 0; @@ -154,7 +154,7 @@ public: virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString &line, MessageLevel::Enum level) { return level; - }; + } virtual QStringList extraArguments(); @@ -291,7 +291,7 @@ public: 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; } diff --git a/launcher/BaseVersion.h b/launcher/BaseVersion.h index c7cedbe1..2837ff3a 100644 --- a/launcher/BaseVersion.h +++ b/launcher/BaseVersion.h @@ -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) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 6b9ae4d5..84edab59 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -136,6 +136,15 @@ set(NET_SOURCES net/Validator.h net/Upload.cpp net/Upload.h + net/HeaderProxy.h + net/RawHeaderProxy.h + net/ApiHeaderProxy.h + net/ApiDownload.h + net/ApiDownload.cpp + net/ApiUpload.cpp + net/ApiUpload.h + net/NetRequest.cpp + net/NetRequest.h ) # Game launch logic @@ -566,6 +575,9 @@ set(ATLAUNCHER_SOURCES ) set(LINKEXE_SOURCES + WindowsConsole.cpp + WindowsConsole.h + filelink/FileLink.h filelink/FileLink.cpp FileSystem.h @@ -671,7 +683,7 @@ set(LOGIC_SOURCES ${ATLAUNCHER_SOURCES} ) -if(APPLE) +if(APPLE AND Launcher_ENABLE_UPDATER) set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES}) endif() @@ -962,6 +974,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 +1040,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 +1131,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 @@ -1150,17 +1179,23 @@ if(APPLE) set(CMAKE_MACOSX_RPATH 1) set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/") - file(DOWNLOAD ${MACOSX_SPARKLE_DOWNLOAD_URL} ${CMAKE_BINARY_DIR}/Sparkle.tar.xz EXPECTED_HASH SHA256=${MACOSX_SPARKLE_SHA256}) - file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle) + if(Launcher_ENABLE_UPDATER) + file(DOWNLOAD ${MACOSX_SPARKLE_DOWNLOAD_URL} ${CMAKE_BINARY_DIR}/Sparkle.tar.xz EXPECTED_HASH SHA256=${MACOSX_SPARKLE_SHA256}) + file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle) + + find_library(SPARKLE_FRAMEWORK Sparkle "${CMAKE_BINARY_DIR}/frameworks/Sparkle") + add_compile_definitions(SPARKLE_ENABLED) + endif() - find_library(SPARKLE_FRAMEWORK Sparkle "${CMAKE_BINARY_DIR}/frameworks/Sparkle") target_link_libraries(Launcher_logic "-framework AppKit" "-framework Carbon" "-framework Foundation" "-framework ApplicationServices" ) - target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK}) + if(Launcher_ENABLE_UPDATER) + target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK}) + endif() endif() target_link_libraries(Launcher_logic) @@ -1189,6 +1224,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 @@ -1222,7 +1262,7 @@ if(WIN32) ) endif() -if (UNIX AND APPLE) +if (UNIX AND APPLE AND Launcher_ENABLE_UPDATER) # Add Sparkle updater # It has to be copied here instead of just allowing fixup_bundle to install it, otherwise essential parts of # the framework aren't installed diff --git a/launcher/DesktopServices.cpp b/launcher/DesktopServices.cpp index 81362f09..0a0934fb 100644 --- a/launcher/DesktopServices.cpp +++ b/launcher/DesktopServices.cpp @@ -104,7 +104,7 @@ 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; diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index f8a82bae..2a4b2620 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -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; }; @@ -475,8 +475,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; } @@ -492,7 +492,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; }; diff --git a/launcher/Filter.cpp b/launcher/Filter.cpp index c65ca0ce..f9530597 100644 --- a/launcher/Filter.cpp +++ b/launcher/Filter.cpp @@ -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) { diff --git a/launcher/Filter.h b/launcher/Filter.h index b55067ac..d3cee2d8 100644 --- a/launcher/Filter.h +++ b/launcher/Filter.h @@ -30,6 +30,16 @@ private: 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: diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index d6a96deb..98ed14b9 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -51,6 +51,8 @@ #include "settings/INISettingsObject.h" +#include "net/ApiDownload.h" + #include <QtConcurrentRun> #include <algorithm> @@ -95,7 +97,7 @@ void InstanceImportTask::executeTask() m_archivePath = entry->getFullPath(); m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); - m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); + m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry)); connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged); diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index 0485db19..a5950659 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -41,9 +41,9 @@ #include <QJsonArray> #include <QJsonDocument> #include <QMimeData> +#include <QPair> #include <QSet> #include <QStack> -#include <QPair> #include <QTextStream> #include <QThread> #include <QTimer> @@ -96,7 +96,11 @@ Qt::DropActions InstanceList::supportedDropActions() const return Qt::MoveAction; } -bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const +bool InstanceList::canDropMimeData(const QMimeData* data, + [[maybe_unused]] Qt::DropAction action, + [[maybe_unused]] int row, + [[maybe_unused]] int column, + [[maybe_unused]] const QModelIndex& parent) const { if (data && data->hasFormat("application/x-instanceid")) { return true; @@ -104,7 +108,11 @@ bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action, return false; } -bool InstanceList::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) +bool InstanceList::dropMimeData(const QMimeData* data, + [[maybe_unused]] Qt::DropAction action, + [[maybe_unused]] int row, + [[maybe_unused]] int column, + [[maybe_unused]] const QModelIndex& parent) { if (data && data->hasFormat("application/x-instanceid")) { return true; @@ -129,7 +137,7 @@ QMimeData* InstanceList::mimeData(const QModelIndexList& indexes) const return mimeData; } -QStringList InstanceList::getLinkedInstancesById(const QString &id) const +QStringList InstanceList::getLinkedInstancesById(const QString& id) const { QStringList linkedInstances; for (auto inst : m_instances) { @@ -158,42 +166,34 @@ QVariant InstanceList::data(const QModelIndex& index, int role) const if (!index.isValid()) { return QVariant(); } - BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer()); - switch (role) - { - case InstancePointerRole: - { - QVariant v = QVariant::fromValue((void *)pdata); - return v; - } - case InstanceIDRole: - { - return pdata->id(); - } - case Qt::EditRole: - case Qt::DisplayRole: - { - return pdata->name(); - } - case Qt::AccessibleTextRole: - { - return tr("%1 Instance").arg(pdata->name()); - } - case Qt::ToolTipRole: - { - return pdata->instanceRoot(); - } - case Qt::DecorationRole: - { - return pdata->iconKey(); - } - // HACK: see InstanceView.h in gui! - case GroupRole: - { - return getInstanceGroup(pdata->id()); - } - default: - break; + BaseInstance* pdata = static_cast<BaseInstance*>(index.internalPointer()); + switch (role) { + case InstancePointerRole: { + QVariant v = QVariant::fromValue((void*)pdata); + return v; + } + case InstanceIDRole: { + return pdata->id(); + } + case Qt::EditRole: + case Qt::DisplayRole: { + return pdata->name(); + } + case Qt::AccessibleTextRole: { + return tr("%1 Instance").arg(pdata->name()); + } + case Qt::ToolTipRole: { + return pdata->instanceRoot(); + } + case Qt::DecorationRole: { + return pdata->iconKey(); + } + // HACK: see InstanceView.h in gui! + case GroupRole: { + return getInstanceGroup(pdata->id()); + } + default: + break; } return QVariant(); } @@ -320,16 +320,18 @@ bool InstanceList::trashInstance(const InstanceId& id) } qDebug() << "Instance" << id << "has been trashed by the launcher."; - m_trashHistory.push({id, inst->instanceRoot(), trashedLoc, cachedGroupId}); - + m_trashHistory.push({ id, inst->instanceRoot(), trashedLoc, cachedGroupId }); + return true; } -bool InstanceList::trashedSomething() { +bool InstanceList::trashedSomething() +{ return !m_trashHistory.empty(); } -void InstanceList::undoTrashInstance() { +void InstanceList::undoTrashInstance() +{ if (m_trashHistory.empty()) { qWarning() << "Nothing to recover from trash."; return; @@ -558,7 +560,7 @@ InstancePtr InstanceList::getInstanceByManagedName(const QString& managed_name) return {}; } -QModelIndex InstanceList::getInstanceIndexById(const QString &id) const +QModelIndex InstanceList::getInstanceIndexById(const QString& id) const { return index(getInstIndex(getInstanceById(id).get())); } @@ -597,13 +599,11 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id) QString inst_type = instanceSettings->get("InstanceType").toString(); - // NOTE: Some PolyMC versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a OneSix instance - if (inst_type == "OneSix" || inst_type.isEmpty()) - { + // NOTE: Some PolyMC versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a OneSix + // instance + if (inst_type == "OneSix" || inst_type.isEmpty()) { inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot)); - } - else - { + } else { inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot)); } qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot(); @@ -759,7 +759,7 @@ void InstanceList::instanceDirContentsChanged(const QString& path) emit instancesChanged(); } -void InstanceList::on_InstFolderChanged(const Setting& setting, QVariant value) +void InstanceList::on_InstFolderChanged( [[maybe_unused]] const Setting& setting, QVariant value) { QString newInstDir = QDir(value.toString()).canonicalPath(); if (newInstDir != m_instDir) { @@ -787,12 +787,17 @@ class InstanceStaging : public Task { Q_OBJECT const unsigned minBackoff = 1; const unsigned maxBackoff = 16; + public: InstanceStaging(InstanceList* parent, InstanceTask* child, QString stagingPath, InstanceName const& instanceName, QString groupName) - : m_parent(parent), backoff(minBackoff, maxBackoff), m_stagingPath(std::move(stagingPath)), m_instance_name(std::move(instanceName)), m_groupName(std::move(groupName)) + : m_parent(parent) + , backoff(minBackoff, maxBackoff) + , m_stagingPath(std::move(stagingPath)) + , m_instance_name(std::move(instanceName)) + , m_groupName(std::move(groupName)) { m_child.reset(child); - connect(child, &Task::succeeded, this, &InstanceStaging::childSucceded); + connect(child, &Task::succeeded, this, &InstanceStaging::childSucceeded); connect(child, &Task::failed, this, &InstanceStaging::childFailed); connect(child, &Task::aborted, this, &InstanceStaging::childAborted); connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable); @@ -800,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(){}; @@ -815,21 +820,17 @@ class InstanceStaging : public Task { return Task::abort(); } - bool canAbort() const override - { - return (m_child && m_child->canAbort()); - } + bool canAbort() const override { return (m_child && m_child->canAbort()); } protected: virtual void executeTask() override { m_child->start(); } QStringList warnings() const override { return m_child->warnings(); } private slots: - void childSucceded() + void childSucceeded() { unsigned sleepTime = backoff(); - if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get())) - { + if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get())) { emitSucceeded(); return; } @@ -847,13 +848,10 @@ class InstanceStaging : public Task { emitFailed(reason); } - void childAborted() - { - emitAborted(); - } + void childAborted() { emitAborted(); } -private: - InstanceList * m_parent; + private: + InstanceList* m_parent; /* * WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows. * Basically, it starts messing things up while the launcher is extracting/creating instances @@ -892,7 +890,10 @@ QString InstanceList::getStagedInstancePath() return path; } -bool InstanceList::commitStagedInstance(const QString& path, InstanceName const& instanceName, const QString& groupName, InstanceTask const& commiting) +bool InstanceList::commitStagedInstance(const QString& path, + InstanceName const& instanceName, + const QString& groupName, + InstanceTask const& commiting) { QDir dir; QString instID; diff --git a/launcher/NullInstance.h b/launcher/NullInstance.h index 53edfa0b..0f7434bb 100644 --- a/launcher/NullInstance.h +++ b/launcher/NullInstance.h @@ -70,7 +70,7 @@ public: { return nullptr; } - shared_qobject_ptr< Task > createUpdateTask(Net::Mode mode) override + shared_qobject_ptr< Task > createUpdateTask([[maybe_unused]] Net::Mode mode) override { return nullptr; } diff --git a/launcher/ProblemProvider.h b/launcher/ProblemProvider.h index 2d3b4338..d7d4c4e5 100644 --- a/launcher/ProblemProvider.h +++ b/launcher/ProblemProvider.h @@ -19,7 +19,7 @@ struct PatchProblem class ProblemProvider { public: - virtual ~ProblemProvider() {}; + virtual ~ProblemProvider() {} virtual const QList<PatchProblem> getProblems() const = 0; virtual ProblemSeverity getProblemSeverity() const = 0; }; diff --git a/launcher/RecursiveFileSystemWatcher.cpp b/launcher/RecursiveFileSystemWatcher.cpp index b7417cdf..be4d1fde 100644 --- a/launcher/RecursiveFileSystemWatcher.cpp +++ b/launcher/RecursiveFileSystemWatcher.cpp @@ -105,7 +105,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)); } diff --git a/launcher/ResourceDownloadTask.cpp b/launcher/ResourceDownloadTask.cpp index 8bb9b64e..a02151ca 100644 --- a/launcher/ResourceDownloadTask.cpp +++ b/launcher/ResourceDownloadTask.cpp @@ -24,6 +24,8 @@ #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourceFolderModel.h" +#include "net/ApiDownload.h" + ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion version, const std::shared_ptr<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); diff --git a/launcher/Usable.h b/launcher/Usable.h index a3e880f3..f5818cbb 100644 --- a/launcher/Usable.h +++ b/launcher/Usable.h @@ -12,28 +12,20 @@ class Usable; * * @see UseLock */ -class Usable -{ +class Usable { friend class UseLock; -public: - std::size_t useCount() const - { - return m_useCount; - } - bool isInUse() const - { - return m_useCount > 0; - } -protected: - virtual void decrementUses() - { - m_useCount--; - } - virtual void incrementUses() - { - m_useCount++; - } -private: + + public: + virtual ~Usable() {} + + std::size_t useCount() const { return m_useCount; } + bool isInUse() const { return m_useCount > 0; } + + protected: + virtual void decrementUses() { m_useCount--; } + virtual void incrementUses() { m_useCount++; } + + private: std::size_t m_useCount = 0; }; diff --git a/launcher/Version.h b/launcher/Version.h index 659f8e54..df379082 100644 --- a/launcher/Version.h +++ b/launcher/Version.h @@ -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; diff --git a/launcher/VersionProxyModel.cpp b/launcher/VersionProxyModel.cpp index 63a43465..e7c00fda 100644 --- a/launcher/VersionProxyModel.cpp +++ b/launcher/VersionProxyModel.cpp @@ -218,16 +218,12 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const { 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()) - { + } else if (hasLatest) { + auto latest = sourceModel()->data(parentIndex, BaseVersionList::LatestRole); + if (latest.toBool()) { return APPLICATION->getThemedIcon("bug"); } } @@ -260,7 +256,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(); } @@ -459,7 +455,9 @@ void VersionProxyModel::sourceRowsAboutToBeInserted(const QModelIndex& parent, i beginInsertRows(parent, first, last); } -void VersionProxyModel::sourceRowsInserted(const QModelIndex& parent, int first, int last) +void VersionProxyModel::sourceRowsInserted([[maybe_unused]] const QModelIndex& parent, + [[maybe_unused]] int first, + [[maybe_unused]] int last) { endInsertRows(); } @@ -469,7 +467,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(); } diff --git a/launcher/WindowsConsole.cpp b/launcher/WindowsConsole.cpp new file mode 100644 index 00000000..860af01f --- /dev/null +++ b/launcher/WindowsConsole.cpp @@ -0,0 +1,132 @@ +/* + * 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; +} + + diff --git a/launcher/WindowsConsole.h b/launcher/WindowsConsole.h new file mode 100644 index 00000000..ab53864b --- /dev/null +++ b/launcher/WindowsConsole.h @@ -0,0 +1,25 @@ +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#pragma once + +void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr); +bool AttachWindowsConsole(); diff --git a/launcher/filelink/FileLink.cpp b/launcher/filelink/FileLink.cpp index c9599b82..3774b382 100644 --- a/launcher/filelink/FileLink.cpp +++ b/launcher/filelink/FileLink.cpp @@ -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 } diff --git a/launcher/icons/IconList.cpp b/launcher/icons/IconList.cpp index 13174f6e..5576b974 100644 --- a/launcher/icons/IconList.cpp +++ b/launcher/icons/IconList.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <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 @@ -35,32 +36,30 @@ #include "IconList.h" #include <FileSystem.h> -#include <QMap> +#include <QDebug> #include <QEventLoop> -#include <QMimeData> -#include <QUrl> #include <QFileSystemWatcher> +#include <QMap> +#include <QMimeData> #include <QSet> -#include <QDebug> +#include <QUrl> +#include "icons/IconUtils.h" #define MAX_SIZE 1024 -IconList::IconList(const QStringList &builtinPaths, QString path, QObject *parent) : QAbstractListModel(parent) +IconList::IconList(const QStringList& builtinPaths, QString path, QObject* parent) : QAbstractListModel(parent) { QSet<QString> builtinNames; // add builtin icons - for(auto & builtinPath: builtinPaths) - { + for (auto& builtinPath : builtinPaths) { QDir instance_icons(builtinPath); auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name); - for (auto file_info : file_info_list) - { + for (auto file_info : file_info_list) { builtinNames.insert(file_info.completeBaseName()); } } - for(auto & builtinName : builtinNames) - { + for (auto& builtinName : builtinNames) { addThemeIcon(builtinName); } @@ -78,31 +77,27 @@ IconList::IconList(const QStringList &builtinPaths, QString path, QObject *paren void IconList::sortIconList() { qDebug() << "Sorting icon list..."; - std::sort(icons.begin(), icons.end(), [](const MMCIcon& a, const MMCIcon& b) { - return a.m_key.localeAwareCompare(b.m_key) < 0; - }); + std::sort(icons.begin(), icons.end(), [](const MMCIcon& a, const MMCIcon& b) { return a.m_key.localeAwareCompare(b.m_key) < 0; }); reindex(); } -void IconList::directoryChanged(const QString &path) +void IconList::directoryChanged(const QString& path) { - QDir new_dir (path); - if(m_dir.absolutePath() != new_dir.absolutePath()) - { + QDir new_dir(path); + if (m_dir.absolutePath() != new_dir.absolutePath()) { m_dir.setPath(path); m_dir.refresh(); - if(is_watching) + if (is_watching) stopWatching(); startWatching(); } - if(!m_dir.exists()) - if(!FS::ensureFolderPathExists(m_dir.absolutePath())) + if (!m_dir.exists()) + if (!FS::ensureFolderPathExists(m_dir.absolutePath())) return; m_dir.refresh(); auto new_list = m_dir.entryList(QDir::Files, QDir::Name); - for (auto it = new_list.begin(); it != new_list.end(); it++) - { - QString &foo = (*it); + for (auto it = new_list.begin(); it != new_list.end(); it++) { + QString& foo = (*it); foo = m_dir.filePath(foo); } #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) @@ -111,8 +106,7 @@ void IconList::directoryChanged(const QString &path) auto new_set = new_list.toSet(); #endif QList<QString> current_list; - for (auto &it : icons) - { + for (auto& it : icons) { if (!it.has(IconType::FileBased)) continue; current_list.push_back(it.m_images[IconType::FileBased].filename); @@ -129,38 +123,33 @@ void IconList::directoryChanged(const QString &path) QSet<QString> to_add = new_set; to_add -= current_set; - for (auto remove : to_remove) - { + for (auto remove : to_remove) { qDebug() << "Removing " << remove; QFileInfo rmfile(remove); QString key = rmfile.completeBaseName(); QString suffix = rmfile.suffix(); // The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well - if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif") + if (!IconUtils::isIconSuffix(suffix)) key = rmfile.fileName(); int idx = getIconIndex(key); if (idx == -1) continue; icons[idx].remove(IconType::FileBased); - if (icons[idx].type() == IconType::ToBeDeleted) - { + if (icons[idx].type() == IconType::ToBeDeleted) { beginRemoveRows(QModelIndex(), idx, idx); icons.remove(idx); reindex(); endRemoveRows(); - } - else - { + } else { dataChanged(index(idx), index(idx)); } m_watcher->removePath(remove); emit iconUpdated(key); } - for (auto add : to_add) - { + for (auto add : to_add) { qDebug() << "Adding " << add; QFileInfo addfile(add); @@ -168,11 +157,10 @@ void IconList::directoryChanged(const QString &path) QString suffix = addfile.suffix(); // The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well - if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif") + if (!IconUtils::isIconSuffix(suffix)) key = addfile.fileName(); - if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased)) - { + if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased)) { m_watcher->addPath(add); emit iconUpdated(key); } @@ -181,7 +169,7 @@ void IconList::directoryChanged(const QString &path) sortIconList(); } -void IconList::fileChanged(const QString &path) +void IconList::fileChanged(const QString& path) { qDebug() << "Checking " << path; QFileInfo checkfile(path); @@ -200,9 +188,9 @@ void IconList::fileChanged(const QString &path) emit iconUpdated(key); } -void IconList::SettingChanged(const Setting &setting, QVariant value) +void IconList::SettingChanged(const Setting& setting, QVariant value) { - if(setting.id() != "IconsDir") + if (setting.id() != "IconsDir") return; directoryChanged(value.toString()); @@ -213,12 +201,9 @@ void IconList::startWatching() auto abs_path = m_dir.absolutePath(); FS::ensureFolderPathExists(abs_path); is_watching = m_watcher->addPath(abs_path); - if (is_watching) - { + if (is_watching) { qDebug() << "Started watching " << abs_path; - } - else - { + } else { qDebug() << "Failed to start watching " << abs_path; } } @@ -241,7 +226,11 @@ Qt::DropActions IconList::supportedDropActions() const return Qt::CopyAction; } -bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[maybe_unused]] int row, [[maybe_unused]] int column, [[maybe_unused]] const QModelIndex &parent) +bool IconList::dropMimeData(const QMimeData* data, + Qt::DropAction action, + [[maybe_unused]] int row, + [[maybe_unused]] int column, + [[maybe_unused]] const QModelIndex& parent) { if (action == Qt::IgnoreAction) return true; @@ -250,12 +239,10 @@ bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[mayb return false; // files dropped from outside? - if (data->hasUrls()) - { + if (data->hasUrls()) { auto urls = data->urls(); QStringList iconFiles; - for (auto url : urls) - { + for (auto url : urls) { // only local files may be dropped... if (!url.isLocalFile()) continue; @@ -267,16 +254,13 @@ bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[mayb return false; } -Qt::ItemFlags IconList::flags(const QModelIndex &index) const +Qt::ItemFlags IconList::flags(const QModelIndex& index) const { Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); - if (index.isValid()) - return Qt::ItemIsDropEnabled | defaultFlags; - else - return Qt::ItemIsDropEnabled | defaultFlags; + return Qt::ItemIsDropEnabled | defaultFlags; } -QVariant IconList::data(const QModelIndex &index, int role) const +QVariant IconList::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); @@ -286,64 +270,49 @@ QVariant IconList::data(const QModelIndex &index, int role) const if (row < 0 || row >= icons.size()) return QVariant(); - switch (role) - { - case Qt::DecorationRole: - return icons[row].icon(); - case Qt::DisplayRole: - return icons[row].name(); - case Qt::UserRole: - return icons[row].m_key; - default: - return QVariant(); + switch (role) { + case Qt::DecorationRole: + return icons[row].icon(); + case Qt::DisplayRole: + return icons[row].name(); + case Qt::UserRole: + return icons[row].m_key; + default: + return QVariant(); } } -int IconList::rowCount(const QModelIndex &parent) const +int IconList::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : icons.size(); } -void IconList::installIcons(const QStringList &iconFiles) +void IconList::installIcons(const QStringList& iconFiles) { for (QString file : iconFiles) - { - QFileInfo fileinfo(file); - if (!fileinfo.isReadable() || !fileinfo.isFile()) - continue; - QString target = FS::PathCombine(getDirectory(), fileinfo.fileName()); - - QString suffix = fileinfo.suffix(); - if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif") - continue; - - if (!QFile::copy(file, target)) - continue; - } + installIcon(file, {}); } -void IconList::installIcon(const QString &file, const QString &name) +void IconList::installIcon(const QString& file, const QString& name) { QFileInfo fileinfo(file); - if(!fileinfo.isReadable() || !fileinfo.isFile()) + if (!fileinfo.isReadable() || !fileinfo.isFile()) return; - QString target = FS::PathCombine(getDirectory(), name); + if (!IconUtils::isIconSuffix(fileinfo.suffix())) + return; + QString target = FS::PathCombine(getDirectory(), name.isEmpty() ? fileinfo.fileName() : name); QFile::copy(file, target); } -bool IconList::iconFileExists(const QString &key) const +bool IconList::iconFileExists(const QString& key) const { auto iconEntry = icon(key); - if(!iconEntry) - { - return false; - } - return iconEntry->has(IconType::FileBased); + return iconEntry && iconEntry->has(IconType::FileBased); } -const MMCIcon *IconList::icon(const QString &key) const +const MMCIcon* IconList::icon(const QString& key) const { int iconIdx = getIconIndex(key); if (iconIdx == -1) @@ -351,100 +320,84 @@ const MMCIcon *IconList::icon(const QString &key) const return &icons[iconIdx]; } -bool IconList::deleteIcon(const QString &key) +bool IconList::deleteIcon(const QString& key) { - if (!iconFileExists(key)) - return false; - - return QFile::remove(icon(key)->getFilePath()); + return iconFileExists(key) && QFile::remove(icon(key)->getFilePath()); } -bool IconList::trashIcon(const QString &key) +bool IconList::trashIcon(const QString& key) { - if (!iconFileExists(key)) - return false; - - return FS::trash(icon(key)->getFilePath(), nullptr); + return iconFileExists(key) && FS::trash(icon(key)->getFilePath(), nullptr); } bool IconList::addThemeIcon(const QString& key) { auto iter = name_index.find(key); - if (iter != name_index.end()) - { - auto &oldOne = icons[*iter]; + if (iter != name_index.end()) { + auto& oldOne = icons[*iter]; oldOne.replace(Builtin, key); dataChanged(index(*iter), index(*iter)); return true; } - else + // add a new icon + beginInsertRows(QModelIndex(), icons.size(), icons.size()); { - // add a new icon - beginInsertRows(QModelIndex(), icons.size(), icons.size()); - { - MMCIcon mmc_icon; - mmc_icon.m_name = key; - mmc_icon.m_key = key; - mmc_icon.replace(Builtin, key); - icons.push_back(mmc_icon); - name_index[key] = icons.size() - 1; - } - endInsertRows(); - return true; + MMCIcon mmc_icon; + mmc_icon.m_name = key; + mmc_icon.m_key = key; + mmc_icon.replace(Builtin, key); + icons.push_back(mmc_icon); + name_index[key] = icons.size() - 1; } + endInsertRows(); + return true; } -bool IconList::addIcon(const QString &key, const QString &name, const QString &path, const IconType type) +bool IconList::addIcon(const QString& key, const QString& name, const QString& path, const IconType type) { // replace the icon even? is the input valid? QIcon icon(path); if (icon.isNull()) return false; auto iter = name_index.find(key); - if (iter != name_index.end()) - { - auto &oldOne = icons[*iter]; + if (iter != name_index.end()) { + auto& oldOne = icons[*iter]; oldOne.replace(type, icon, path); dataChanged(index(*iter), index(*iter)); return true; } - else + // add a new icon + beginInsertRows(QModelIndex(), icons.size(), icons.size()); { - // add a new icon - beginInsertRows(QModelIndex(), icons.size(), icons.size()); - { - MMCIcon mmc_icon; - mmc_icon.m_name = name; - mmc_icon.m_key = key; - mmc_icon.replace(type, icon, path); - icons.push_back(mmc_icon); - name_index[key] = icons.size() - 1; - } - endInsertRows(); - return true; + MMCIcon mmc_icon; + mmc_icon.m_name = name; + mmc_icon.m_key = key; + mmc_icon.replace(type, icon, path); + icons.push_back(mmc_icon); + name_index[key] = icons.size() - 1; } + endInsertRows(); + return true; } -void IconList::saveIcon(const QString &key, const QString &path, const char * format) const +void IconList::saveIcon(const QString& key, const QString& path, const char* format) const { auto icon = getIcon(key); auto pixmap = icon.pixmap(128, 128); pixmap.save(path, format); } - void IconList::reindex() { name_index.clear(); int i = 0; - for (auto &iter : icons) - { + for (auto& iter : icons) { name_index[iter.m_key] = i; i++; } } -QIcon IconList::getIcon(const QString &key) const +QIcon IconList::getIcon(const QString& key) const { int icon_index = getIconIndex(key); @@ -459,7 +412,7 @@ QIcon IconList::getIcon(const QString &key) const return QIcon(); } -int IconList::getIconIndex(const QString &key) const +int IconList::getIconIndex(const QString& key) const { auto iter = name_index.find(key == "default" ? "grass" : key); if (iter != name_index.end()) @@ -472,5 +425,3 @@ QString IconList::getDirectory() const { return m_dir.absolutePath(); } - -//#include "IconList.moc" diff --git a/launcher/icons/IconList.h b/launcher/icons/IconList.h index 97141e4a..8afd0557 100644 --- a/launcher/icons/IconList.h +++ b/launcher/icons/IconList.h @@ -1,24 +1,43 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 <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 <QMutex> #include <QAbstractListModel> -#include <QFile> #include <QDir> +#include <QFile> +#include <QMutex> #include <QtGui/QIcon> #include <memory> @@ -29,58 +48,58 @@ class QFileSystemWatcher; -class IconList : public QAbstractListModel -{ +class IconList : public QAbstractListModel { Q_OBJECT -public: - explicit IconList(const QStringList &builtinPaths, QString path, QObject *parent = 0); - virtual ~IconList() {}; + public: + explicit IconList(const QStringList& builtinPaths, QString path, QObject* parent = 0); + virtual ~IconList(){}; - QIcon getIcon(const QString &key) const; - int getIconIndex(const QString &key) const; + QIcon getIcon(const QString& key) const; + int getIconIndex(const QString& key) const; QString getDirectory() const; - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; virtual QStringList mimeTypes() const override; virtual Qt::DropActions supportedDropActions() const override; - virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; - virtual Qt::ItemFlags flags(const QModelIndex &index) const override; + virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; - bool addThemeIcon(const QString &key); - bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type); - void saveIcon(const QString &key, const QString &path, const char * format) const; - bool deleteIcon(const QString &key); - bool trashIcon(const QString &key); - bool iconFileExists(const QString &key) const; + bool addThemeIcon(const QString& key); + bool addIcon(const QString& key, const QString& name, const QString& path, const IconType type); + void saveIcon(const QString& key, const QString& path, const char* format) const; + bool deleteIcon(const QString& key); + bool trashIcon(const QString& key); + bool iconFileExists(const QString& key) const; - void installIcons(const QStringList &iconFiles); - void installIcon(const QString &file, const QString &name); + void installIcons(const QStringList& iconFiles); + void installIcon(const QString& file, const QString& name); - const MMCIcon * icon(const QString &key) const; + const MMCIcon* icon(const QString& key) const; void startWatching(); void stopWatching(); -signals: + signals: void iconUpdated(QString key); -private: + private: // hide copy constructor - IconList(const IconList &) = delete; + IconList(const IconList&) = delete; // hide assign op - IconList &operator=(const IconList &) = delete; + IconList& operator=(const IconList&) = delete; void reindex(); void sortIconList(); -public slots: - void directoryChanged(const QString &path); + public slots: + void directoryChanged(const QString& path); + + protected slots: + void fileChanged(const QString& path); + void SettingChanged(const Setting& setting, QVariant value); -protected slots: - void fileChanged(const QString &path); - void SettingChanged(const Setting & setting, QVariant value); -private: + private: shared_qobject_ptr<QFileSystemWatcher> m_watcher; bool is_watching; QMap<QString, int> name_index; diff --git a/launcher/icons/IconUtils.cpp b/launcher/icons/IconUtils.cpp index bf530c16..99c38f47 100644 --- a/launcher/icons/IconUtils.cpp +++ b/launcher/icons/IconUtils.cpp @@ -1,25 +1,51 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 <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 "FileSystem.h" #include <QDirIterator> - -#include <array> +#include "FileSystem.h" namespace { -std::array<const char *, 6> validIconExtensions = {{ - "svg", - "png", - "ico", - "gif", - "jpg", - "jpeg" -}}; +static const QStringList validIconExtensions = { { "svg", "png", "ico", "gif", "jpg", "jpeg" } }; } -namespace IconUtils{ +namespace IconUtils { -QString findBestIconIn(const QString &folder, const QString & iconKey) { - int best_found = validIconExtensions.size(); +QString findBestIconIn(const QString& folder, const QString& iconKey) +{ QString best_filename; QDirIterator it(folder, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::NoIteratorFlags); @@ -27,36 +53,20 @@ QString findBestIconIn(const QString &folder, const QString & iconKey) { it.next(); auto fileInfo = it.fileInfo(); - if(fileInfo.completeBaseName() != iconKey) - continue; - - auto extension = fileInfo.suffix(); - - for(int i = 0; i < best_found; i++) { - if(extension == validIconExtensions[i]) { - best_found = i; - qDebug() << i << " : " << fileInfo.fileName(); - best_filename = fileInfo.fileName(); - } - } + if (fileInfo.completeBaseName() == iconKey && isIconSuffix(fileInfo.suffix())) + return fileInfo.absoluteFilePath(); } - return FS::PathCombine(folder, best_filename); + return {}; } -QString getIconFilter() { - QString out; - QTextStream stream(&out); - stream << '('; - for(size_t i = 0; i < validIconExtensions.size() - 1; i++) { - if(i > 0) { - stream << " "; - } - stream << "*." << validIconExtensions[i]; - } - stream << " *." << validIconExtensions[validIconExtensions.size() - 1]; - stream << ')'; - return out; +QString getIconFilter() +{ + return "(*." + validIconExtensions.join(" *.") + ")"; } +bool isIconSuffix(QString suffix) +{ + return validIconExtensions.contains(suffix); } +} // namespace IconUtils diff --git a/launcher/icons/IconUtils.h b/launcher/icons/IconUtils.h index be93d914..90cdfe5a 100644 --- a/launcher/icons/IconUtils.h +++ b/launcher/icons/IconUtils.h @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 <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> @@ -5,9 +40,10 @@ namespace IconUtils { // Given a folder and an icon key, find 'best' of the icons with the given key in there and return its path -QString findBestIconIn(const QString &folder, const QString & iconKey); +QString findBestIconIn(const QString& folder, const QString& iconKey); // Get icon file type filter for file browser dialogs QString getIconFilter(); -} +bool isIconSuffix(QString suffix); +} // namespace IconUtils diff --git a/launcher/icons/MMCIcon.cpp b/launcher/icons/MMCIcon.cpp index 436ef75f..991b470c 100644 --- a/launcher/icons/MMCIcon.cpp +++ b/launcher/icons/MMCIcon.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <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 @@ -37,23 +38,21 @@ #include <QFileInfo> #include <QIcon> -IconType operator--(IconType &t, int) +IconType operator--(IconType& t, int) { IconType temp = t; - switch (t) - { - case IconType::Builtin: - t = IconType::ToBeDeleted; - break; - case IconType::Transient: - t = IconType::Builtin; - break; - case IconType::FileBased: - t = IconType::Transient; - break; - default: - { - } + switch (t) { + case IconType::Builtin: + t = IconType::ToBeDeleted; + break; + case IconType::Transient: + t = IconType::Builtin; + break; + case IconType::FileBased: + t = IconType::Transient; + break; + default: + break; } return temp; } @@ -79,8 +78,8 @@ QIcon MMCIcon::icon() const { if (m_current_type == IconType::ToBeDeleted) return QIcon(); - auto & icon = m_images[m_current_type].icon; - if(!icon.isNull()) + auto& icon = m_images[m_current_type].icon; + if (!icon.isNull()) return icon; // FIXME: inject this. return QIcon::fromTheme(m_images[m_current_type].key); @@ -90,10 +89,8 @@ void MMCIcon::remove(IconType rm_type) { m_images[rm_type].filename = QString(); m_images[rm_type].icon = QIcon(); - for (auto iter = rm_type; iter != IconType::ToBeDeleted; iter--) - { - if (m_images[iter].present()) - { + for (auto iter = rm_type; iter != IconType::ToBeDeleted; iter--) { + if (m_images[iter].present()) { m_current_type = iter; return; } @@ -103,8 +100,7 @@ void MMCIcon::remove(IconType rm_type) void MMCIcon::replace(IconType new_type, QIcon icon, QString path) { - if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted) - { + if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted) { m_current_type = new_type; } m_images[new_type].icon = icon; @@ -114,8 +110,7 @@ void MMCIcon::replace(IconType new_type, QIcon icon, QString path) void MMCIcon::replace(IconType new_type, const QString& key) { - if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted) - { + if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted) { m_current_type = new_type; } m_images[new_type].icon = QIcon(); @@ -125,13 +120,12 @@ void MMCIcon::replace(IconType new_type, const QString& key) QString MMCIcon::getFilePath() const { - if(m_current_type == IconType::ToBeDeleted){ + if (m_current_type == IconType::ToBeDeleted) { return QString(); } return m_images[m_current_type].filename; } - bool MMCIcon::isBuiltIn() const { return m_current_type == IconType::Builtin; diff --git a/launcher/icons/MMCIcon.h b/launcher/icons/MMCIcon.h index 13d99318..a6e30566 100644 --- a/launcher/icons/MMCIcon.h +++ b/launcher/icons/MMCIcon.h @@ -1,45 +1,52 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 <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 <QString> #include <QDateTime> #include <QIcon> +#include <QString> -enum IconType : unsigned -{ - Builtin, - Transient, - FileBased, - ICONS_TOTAL, - ToBeDeleted -}; +enum IconType : unsigned { Builtin, Transient, FileBased, ICONS_TOTAL, ToBeDeleted }; -struct MMCImage -{ +struct MMCImage { QIcon icon; QString key; QString filename; - bool present() const - { - return !icon.isNull() || !key.isEmpty(); - } + bool present() const { return !icon.isNull() || !key.isEmpty(); } }; -struct MMCIcon -{ +struct MMCIcon { QString m_key; QString m_name; MMCImage m_images[ICONS_TOTAL]; @@ -51,7 +58,7 @@ struct MMCIcon QIcon icon() const; void remove(IconType rm_type); void replace(IconType new_type, QIcon icon, QString path = QString()); - void replace(IconType new_type, const QString &key); + void replace(IconType new_type, const QString& key); bool isBuiltIn() const; QString getFilePath() const; }; diff --git a/launcher/java/JavaVersion.h b/launcher/java/JavaVersion.h index c051a794..d74e0148 100644 --- a/launcher/java/JavaVersion.h +++ b/launcher/java/JavaVersion.h @@ -14,7 +14,7 @@ class JavaVersion { friend class JavaVersionTest; public: - JavaVersion() {}; + JavaVersion() {} JavaVersion(const QString & rhs); JavaVersion & operator=(const QString & rhs); diff --git a/launcher/meta/BaseEntity.cpp b/launcher/meta/BaseEntity.cpp index 97815eba..7d83db2a 100644 --- a/launcher/meta/BaseEntity.cpp +++ b/launcher/meta/BaseEntity.cpp @@ -15,7 +15,7 @@ #include "BaseEntity.h" -#include "net/Download.h" +#include "net/ApiDownload.h" #include "net/HttpMetaCache.h" #include "net/NetJob.h" #include "Json.h" @@ -40,7 +40,7 @@ public: /* methods */ } bool write(QByteArray & data) override { - this->data.append(data); + this->m_data.append(data); return true; } bool abort() override @@ -52,7 +52,7 @@ public: /* methods */ 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; @@ -65,7 +65,7 @@ public: /* methods */ } private: /* data */ - QByteArray data; + QByteArray m_data; Meta::BaseEntity *m_entity; }; @@ -130,7 +130,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. diff --git a/launcher/minecraft/AssetsUtils.cpp b/launcher/minecraft/AssetsUtils.cpp index 16fdfdb1..65ad6da6 100644 --- a/launcher/minecraft/AssetsUtils.cpp +++ b/launcher/minecraft/AssetsUtils.cpp @@ -45,7 +45,7 @@ #include "AssetsUtils.h" #include "FileSystem.h" -#include "net/Download.h" +#include "net/ApiDownload.h" #include "net/ChecksumValidator.h" #include "BuildConfig.h" @@ -311,7 +311,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()); diff --git a/launcher/minecraft/Component.h b/launcher/minecraft/Component.h index ef7c9947..e7419c41 100644 --- a/launcher/minecraft/Component.h +++ b/launcher/minecraft/Component.h @@ -27,7 +27,8 @@ public: 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(); diff --git a/launcher/minecraft/LaunchProfile.h b/launcher/minecraft/LaunchProfile.h index 49c1217d..79e68824 100644 --- a/launcher/minecraft/LaunchProfile.h +++ b/launcher/minecraft/LaunchProfile.h @@ -42,7 +42,7 @@ class LaunchProfile: public ProblemProvider { public: - virtual ~LaunchProfile() {}; + virtual ~LaunchProfile() {} public: /* application of profile variables from patches */ void applyMinecraftVersion(const QString& id); diff --git a/launcher/minecraft/Library.cpp b/launcher/minecraft/Library.cpp index cb2b5254..e0318ef2 100644 --- a/launcher/minecraft/Library.cpp +++ b/launcher/minecraft/Library.cpp @@ -36,7 +36,7 @@ #include "Library.h" #include "MinecraftInstance.h" -#include <net/Download.h> +#include <net/ApiDownload.h> #include <net/ChecksumValidator.h> #include <FileSystem.h> #include <BuildConfig.h> @@ -129,14 +129,14 @@ QList<NetAction::Ptr> Library::getDownloads( 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; diff --git a/launcher/minecraft/MojangDownloadInfo.h b/launcher/minecraft/MojangDownloadInfo.h index 13e27e15..6831d8d8 100644 --- a/launcher/minecraft/MojangDownloadInfo.h +++ b/launcher/minecraft/MojangDownloadInfo.h @@ -23,8 +23,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; @@ -57,20 +57,20 @@ struct MojangAssetIndexInfo : public MojangDownloadInfo { } - 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; } diff --git a/launcher/minecraft/OneSixVersionFormat.cpp b/launcher/minecraft/OneSixVersionFormat.cpp index b586198b..ddd98110 100644 --- a/launcher/minecraft/OneSixVersionFormat.cpp +++ b/launcher/minecraft/OneSixVersionFormat.cpp @@ -413,7 +413,7 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch } LibraryPtr OneSixVersionFormat::plusJarModFromJson( - ProblemContainer & problems, + [[maybe_unused]] ProblemContainer & problems, const QJsonObject &libObj, const QString &filename, const QString &originalName diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 7cdd47ae..1f210d69 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -226,11 +226,11 @@ static bool loadPackProfile(PackProfile * parent, const QString & filename, cons 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(); @@ -415,7 +415,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(); @@ -428,7 +428,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") { @@ -446,7 +446,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) @@ -533,9 +533,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; } @@ -609,7 +609,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())) { diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h index d144d875..06741c2d 100644 --- a/launcher/minecraft/PackProfile.h +++ b/launcher/minecraft/PackProfile.h @@ -145,7 +145,7 @@ public: 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 diff --git a/launcher/minecraft/ProfileUtils.cpp b/launcher/minecraft/ProfileUtils.cpp index 03f8c198..14557003 100644 --- a/launcher/minecraft/ProfileUtils.cpp +++ b/launcher/minecraft/ProfileUtils.cpp @@ -92,7 +92,7 @@ bool readOverrideOrders(QString path, PatchOrder &order) 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"; diff --git a/launcher/minecraft/Rule.h b/launcher/minecraft/Rule.h index 846e8e42..70fd70b6 100644 --- a/launcher/minecraft/Rule.h +++ b/launcher/minecraft/Rule.h @@ -63,7 +63,7 @@ public: Rule(RuleAction result) : m_result(result) { } - virtual ~Rule() {}; + virtual ~Rule() {} virtual QJsonObject toJson() = 0; RuleAction apply(const Library *parent, const RuntimeContext & runtimeContext) { diff --git a/launcher/minecraft/World.cpp b/launcher/minecraft/World.cpp index 54fb9434..21f2c439 100644 --- a/launcher/minecraft/World.cpp +++ b/launcher/minecraft/World.cpp @@ -408,13 +408,13 @@ 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."; @@ -434,13 +434,13 @@ 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."; @@ -460,13 +460,13 @@ 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."; diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp index 0feee299..fe089378 100644 --- a/launcher/minecraft/WorldList.cpp +++ b/launcher/minecraft/WorldList.cpp @@ -278,7 +278,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) { @@ -320,7 +320,6 @@ QVariant WorldList::headerData(int section, Qt::Orientation orientation, int rol default: return QVariant(); } - return QVariant(); } QStringList WorldList::mimeTypes() const @@ -373,7 +372,7 @@ 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) @@ -381,13 +380,13 @@ QMimeData *WorldList::mimeData(const QModelIndexList &indexes) const 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 diff --git a/launcher/minecraft/auth/AccountData.cpp b/launcher/minecraft/auth/AccountData.cpp index 0b78cb0c..707483ef 100644 --- a/launcher/minecraft/auth/AccountData.cpp +++ b/launcher/minecraft/auth/AccountData.cpp @@ -310,7 +310,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."; @@ -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]; diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index d6f42b75..14e3ca7c 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -369,7 +369,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) { diff --git a/launcher/minecraft/auth/Parsers.cpp b/launcher/minecraft/auth/Parsers.cpp index f3d9ad56..98326272 100644 --- a/launcher/minecraft/auth/Parsers.cpp +++ b/launcher/minecraft/auth/Parsers.cpp @@ -107,16 +107,16 @@ bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, QString na 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; } diff --git a/launcher/minecraft/auth/steps/EntitlementsStep.cpp b/launcher/minecraft/auth/steps/EntitlementsStep.cpp index bd604292..b5682708 100644 --- a/launcher/minecraft/auth/steps/EntitlementsStep.cpp +++ b/launcher/minecraft/auth/steps/EntitlementsStep.cpp @@ -35,9 +35,9 @@ void EntitlementsStep::rehydrate() { } void EntitlementsStep::onRequestDone( - QNetworkReply::NetworkError error, + [[maybe_unused]] QNetworkReply::NetworkError error, QByteArray data, - QList<QNetworkReply::RawHeaderPair> headers + [[maybe_unused]] QList<QNetworkReply::RawHeaderPair> headers ) { auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); requestor->deleteLater(); diff --git a/launcher/minecraft/gameoptions/GameOptions.cpp b/launcher/minecraft/gameoptions/GameOptions.cpp index e547b32a..ce0988cc 100644 --- a/launcher/minecraft/gameoptions/GameOptions.cpp +++ b/launcher/minecraft/gameoptions/GameOptions.cpp @@ -112,12 +112,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 diff --git a/launcher/minecraft/launch/LauncherPartLaunch.cpp b/launcher/minecraft/launch/LauncherPartLaunch.cpp index 8ecf715d..01df8543 100644 --- a/launcher/minecraft/launch/LauncherPartLaunch.cpp +++ b/launcher/minecraft/launch/LauncherPartLaunch.cpp @@ -54,7 +54,7 @@ LauncherPartLaunch::LauncherPartLaunch(LaunchTask *parent) : LaunchStep(parent) 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) { diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index b67bd465..ef1895e1 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -73,7 +73,7 @@ public: 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. */ diff --git a/launcher/minecraft/mod/ModDetails.h b/launcher/minecraft/mod/ModDetails.h index b4e59d52..ae36baa8 100644 --- a/launcher/minecraft/mod/ModDetails.h +++ b/launcher/minecraft/mod/ModDetails.h @@ -59,17 +59,17 @@ struct ModLicense { 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,12 +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) diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 51383edf..7b6d936c 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -61,6 +61,7 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") }); m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME , SortType::VERSION, SortType::DATE, SortType::PROVIDER}; m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents}; + m_columnsHideable = { false, true, false, true, true, true }; } QVariant ModFolderModel::data(const QModelIndex &index, int role) const @@ -139,7 +140,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) { diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 39a61067..f48ddb64 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -447,7 +447,7 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const } } -bool ResourceFolderModel::setData(const QModelIndex& index, const QVariant& value, int role) +bool ResourceFolderModel::setData(const QModelIndex& index, [[maybe_unused]] const QVariant& value, int role) { int row = index.row(); if (row < 0 || row >= rowCount(index.parent()) || !index.isValid()) @@ -471,7 +471,7 @@ bool ResourceFolderModel::setData(const QModelIndex& index, const QVariant& valu return false; } -QVariant ResourceFolderModel::headerData(int section, Qt::Orientation orientation, int role) const +QVariant ResourceFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const { switch (role) { case Qt::DisplayRole: @@ -552,6 +552,8 @@ 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); @@ -586,7 +588,7 @@ 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) diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 454b84c3..e7884894 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -49,8 +49,8 @@ class ResourceFolderModel : public QAbstractListModel { bool stopWatching(const QStringList paths); /* Helper methods for subclasses, using a predetermined list of paths. */ - virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); }; - virtual bool stopWatching() { return stopWatching({ m_dir.absolutePath() }); }; + virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); } + virtual bool stopWatching() { return stopWatching({ m_dir.absolutePath() }); } /** Given a path in the system, install that resource, moving it to its place in the * instance file hierarchy. @@ -78,7 +78,7 @@ class ResourceFolderModel : public QAbstractListModel { /** Creates a new parse task, if needed, for 'res' and start it.*/ virtual void resolveResource(Resource* res); - [[nodiscard]] size_t size() const { return m_resources.size(); }; + [[nodiscard]] qsizetype size() const { return m_resources.size(); } [[nodiscard]] bool empty() const { return size() == 0; } [[nodiscard]] Resource& at(int index) { return *m_resources.at(index); } [[nodiscard]] Resource const& at(int index) const { return *m_resources.at(index); } @@ -97,10 +97,10 @@ class ResourceFolderModel : public QAbstractListModel { /* Basic columns */ enum Columns { ACTIVE_COLUMN = 0, NAME_COLUMN, DATE_COLUMN, NUM_COLUMNS }; - QStringList columnNames(bool translated = true) const { return translated ? m_column_names_translated : m_column_names; }; + QStringList columnNames(bool translated = true) const { return translated ? m_column_names_translated : m_column_names; } - [[nodiscard]] int rowCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : static_cast<int>(size()); } - [[nodiscard]] int columnCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : NUM_COLUMNS; }; + [[nodiscard]] int rowCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : size(); } + [[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. * @@ -202,6 +202,7 @@ class ResourceFolderModel : public QAbstractListModel { QStringList m_column_names = {"Enable", "Name", "Last Modified"}; QStringList m_column_names_translated = {tr("Enable"), tr("Name"), tr("Last Modified")}; QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch, QHeaderView::ResizeToContents }; + QList<bool> m_columnsHideable = { false, false, true }; QDir m_dir; BaseInstance* m_instance; @@ -223,15 +224,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()); \ } \ diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index 41455599..0d316e36 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -54,7 +54,7 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstanc m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") }); m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE}; m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; - + m_columnsHideable = { false, true, false, true, true }; } QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const @@ -128,7 +128,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 +165,6 @@ QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orient default: return {}; } - return {}; } int ResourcePackFolderModel::columnCount(const QModelIndex& parent) const diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index 531a7023..7dbedc3f 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -49,6 +49,7 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") }); m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE }; m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents}; + m_columnsHideable = { false, true, false, true }; } @@ -114,7 +115,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: diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index f8ecdb33..0a0f57bf 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -61,7 +61,7 @@ GetModDependenciesTask::GetModDependenciesTask(QObject* parent, if (auto meta = mod->metadata(); meta) m_mods.append(meta); prepare(); -}; +} void GetModDependenciesTask::prepare() { @@ -130,7 +130,7 @@ QList<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()) { @@ -215,27 +215,27 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen return; } if (level == 0) { - qWarning() << "Dependency cycle exeeded"; + qWarning() << "Dependency cycle exceeded"; return; } if (dep.addonId.toString().isEmpty() && !pDep->version.addonId.toString().isEmpty()) { pDep->pack->addonId = pDep->version.addonId; - auto dep = getOverride({ pDep->version.addonId, pDep->dependency.type }, provider.name); - if (dep.addonId != pDep->version.addonId) { + auto dep_ = getOverride({ pDep->version.addonId, pDep->dependency.type }, provider.name); + if (dep_.addonId != pDep->version.addonId) { removePack(pDep->version.addonId); - addTask(prepareDependencyTask(dep, provider.name, level)); + addTask(prepareDependencyTask(dep_, provider.name, level)); } else addTask(getProjectInfoTask(pDep)); } - for (auto dep : getDependenciesForVersion(pDep->version, provider.name)) { - addTask(prepareDependencyTask(dep, provider.name, level - 1)); + for (auto dep_ : getDependenciesForVersion(pDep->version, provider.name)) { + addTask(prepareDependencyTask(dep_, provider.name, level - 1)); } }; auto version = provider.api->getDependencyVersion(std::move(args), std::move(callbacks)); tasks->addTask(version); return tasks; -}; +} void GetModDependenciesTask::removePack(const QVariant addonId) { diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp index 60389753..4c0188a8 100644 --- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp @@ -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; diff --git a/launcher/minecraft/services/CapeChange.cpp b/launcher/minecraft/services/CapeChange.cpp index 1d5ea36d..30155055 100644 --- a/launcher/minecraft/services/CapeChange.cpp +++ b/launcher/minecraft/services/CapeChange.cpp @@ -45,7 +45,7 @@ CapeChange::CapeChange(QObject *parent, QString token, QString cape) { } -void CapeChange::setCape(QString& cape) { +void CapeChange::setCape([[maybe_unused]] QString& cape) { QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active")); auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId); request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); diff --git a/launcher/minecraft/update/AssetUpdateTask.cpp b/launcher/minecraft/update/AssetUpdateTask.cpp index fda85ba8..ec0c90b7 100644 --- a/launcher/minecraft/update/AssetUpdateTask.cpp +++ b/launcher/minecraft/update/AssetUpdateTask.cpp @@ -7,6 +7,8 @@ #include "Application.h" +#include "net/ApiDownload.h" + AssetUpdateTask::AssetUpdateTask(MinecraftInstance * inst) { m_inst = inst; @@ -34,7 +36,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); diff --git a/launcher/minecraft/update/FMLLibrariesTask.cpp b/launcher/minecraft/update/FMLLibrariesTask.cpp index d9fa0595..51b6d74c 100644 --- a/launcher/minecraft/update/FMLLibrariesTask.cpp +++ b/launcher/minecraft/update/FMLLibrariesTask.cpp @@ -8,6 +8,8 @@ #include "BuildConfig.h" #include "Application.h" +#include "net/ApiDownload.h" + FMLLibrariesTask::FMLLibrariesTask(MinecraftInstance * inst) { m_inst = inst; @@ -68,7 +70,7 @@ void FMLLibrariesTask::executeTask() { 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); diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 2aa91602..e8d544fc 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -113,7 +113,7 @@ struct IndexedPack { ExtraPackData extraData; // For internal use, not provided by APIs - [[nodiscard]] bool isVersionSelected(size_t index) const + [[nodiscard]] bool isVersionSelected(int index) const { if (!versionsLoaded) return false; @@ -144,9 +144,12 @@ inline auto getOverrideDeps() -> QList<OverrideDep> { "qvIfYCYJ", "P7dR8mSH", "API", ModPlatform::ResourceProvider::MODRINTH }, { "lwVhp9o5", "Ha28R6CL", "KotlinLibraries", ModPlatform::ResourceProvider::MODRINTH } }; -}; + +} + QString getMetaURL(ResourceProvider provider, QVariant projectID); + } // namespace ModPlatform Q_DECLARE_METATYPE(ModPlatform::IndexedPack) diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index d3277761..baba550f 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -128,28 +128,28 @@ class ResourceAPI { public slots: [[nodiscard]] virtual Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const { - qWarning() << "TODO"; + qWarning() << "TODO: ResourceAPI::searchProjects"; return nullptr; } - [[nodiscard]] virtual Task::Ptr getProject(QString addonId, std::shared_ptr<QByteArray> response) const + [[nodiscard]] virtual Task::Ptr getProject([[maybe_unused]] QString addonId, [[maybe_unused]] std::shared_ptr<QByteArray> response) const { - qWarning() << "TODO"; + qWarning() << "TODO: ResourceAPI::getProject"; return nullptr; } - [[nodiscard]] virtual Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const + [[nodiscard]] virtual Task::Ptr getProjects([[maybe_unused]] QStringList addonIds, [[maybe_unused]] std::shared_ptr<QByteArray> response) const { - qWarning() << "TODO"; + qWarning() << "TODO: ResourceAPI::getProjects"; return nullptr; } [[nodiscard]] virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const { - qWarning() << "TODO"; + qWarning() << "TODO: ResourceAPI::getProjectInfo"; return nullptr; } [[nodiscard]] virtual Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const { - qWarning() << "TODO"; + qWarning() << "TODO: ResourceAPI::getProjectVersions"; return nullptr; } diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 82a51c27..8260a25c 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -53,6 +53,8 @@ #include "meta/Version.h" #include "meta/VersionList.h" +#include "net/ApiDownload.h" + #include "BuildConfig.h" #include "Application.h" @@ -82,9 +84,9 @@ void PackInstallTask::executeTask() { qDebug() << "PackInstallTask::executeTask: " << QThread::currentThreadId(); NetJob::Ptr netJob{ new NetJob("ATLauncher::VersionFetch", APPLICATION->network()) }; - auto searchUrl = + auto searchUrl = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.json").arg(m_pack_safe_name).arg(m_version_name); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response)); QObject::connect(netJob.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded); QObject::connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed); @@ -659,7 +661,7 @@ void PackInstallTask::installConfigs() auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", path); entry->setStale(true); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); if (!m_version.configs.sha1.isEmpty()) { auto rawSha1 = QByteArray::fromHex(m_version.configs.sha1.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); @@ -782,7 +784,7 @@ void PackInstallTask::downloadMods() entry->setStale(true); modsToExtract.insert(entry->getFullPath(), mod); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); @@ -794,7 +796,7 @@ void PackInstallTask::downloadMods() entry->setStale(true); modsToDecomp.insert(entry->getFullPath(), mod); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); @@ -808,7 +810,7 @@ void PackInstallTask::downloadMods() auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", cacheName); entry->setStale(true); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index ae168bbd..9c0cb35b 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -1,7 +1,9 @@ #include "FileResolvingTask.h" #include "Json.h" +#include "net/ApiUpload.h" #include "net/Upload.h" +#include "net/ApiDownload.h" #include "modplatform/modrinth/ModrinthPackIndex.h" @@ -38,7 +40,7 @@ void Flame::FileResolvingTask::executeTask() return l; })); QByteArray data = Json::toText(object); - auto dl = Net::Upload::makeByteArray(QUrl("https://api.curseforge.com/v1/mods/files"), result, data); + auto dl = Net::ApiUpload::makeByteArray(QUrl("https://api.curseforge.com/v1/mods/files"), result, data); m_dljob->addNetAction(dl); auto step_progress = std::make_shared<TaskStepProgress>(); @@ -93,13 +95,13 @@ void Flame::FileResolvingTask::netJobFinished() auto& out = m_toProcess.files[fileid]; try { out.parseFromObject(Json::requireObject(file)); - } catch (const JSONValidationError& e) { + } catch ([[maybe_unused]] const JSONValidationError& e) { qDebug() << "Blocked mod on curseforge" << out.fileName; auto hash = out.hash; if (!hash.isEmpty()) { auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash); auto output = std::make_shared<QByteArray>(); - auto dl = Net::Download::makeByteArray(QUrl(url), output); + auto dl = Net::ApiDownload::makeByteArray(QUrl(url), output); QObject::connect(dl.get(), &Net::Download::succeeded, [&out]() { out.resolved = true; }); m_checkJob->addNetAction(dl); @@ -171,7 +173,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished() auto projectId = mod->projectId; auto output = std::make_shared<QByteArray>(); auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId); - auto dl = Net::Download::makeByteArray(url, output); + auto dl = Net::ApiDownload::makeByteArray(url, output); qDebug() << "Fetching url slug for file:" << mod->fileName; QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() { auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index 5b0b1d8b..7231b466 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -8,7 +8,9 @@ #include "Application.h" #include "BuildConfig.h" #include "Json.h" +#include "net/ApiUpload.h" #include "net/NetJob.h" +#include "net/ApiDownload.h" #include "net/Upload.h" Task::Ptr FlameAPI::matchFingerprints(const QList<uint>& fingerprints, std::shared_ptr<QByteArray> response) @@ -26,7 +28,7 @@ Task::Ptr FlameAPI::matchFingerprints(const QList<uint>& fingerprints, std::shar QJsonDocument body(body_obj); auto body_raw = body.toJson(); - netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/fingerprints"), response, body_raw)); + netJob->addNetAction(Net::ApiUpload::makeByteArray(QString("https://api.curseforge.com/v1/fingerprints"), response, body_raw)); return netJob; } @@ -38,7 +40,7 @@ auto FlameAPI::getModFileChangelog(int modId, int fileId) -> QString auto netJob = makeShared<NetJob>(QString("Flame::FileChangelog"), APPLICATION->network()); auto response = std::make_shared<QByteArray>(); - netJob->addNetAction(Net::Download::makeByteArray( + netJob->addNetAction(Net::ApiDownload::makeByteArray( QString("https://api.curseforge.com/v1/mods/%1/files/%2/changelog") .arg(QString::fromStdString(std::to_string(modId)), QString::fromStdString(std::to_string(fileId))), response)); @@ -74,7 +76,7 @@ auto FlameAPI::getModDescription(int modId) -> QString auto netJob = makeShared<NetJob>(QString("Flame::ModDescription"), APPLICATION->network()); auto response = std::make_shared<QByteArray>(); netJob->addNetAction( - Net::Download::makeByteArray(QString("https://api.curseforge.com/v1/mods/%1/description").arg(QString::number(modId)), response)); + Net::ApiDownload::makeByteArray(QString("https://api.curseforge.com/v1/mods/%1/description").arg(QString::number(modId)), response)); QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &description] { QJsonParseError parse_error{}; @@ -113,7 +115,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe auto response = std::make_shared<QByteArray>(); ModPlatform::IndexedVersion ver; - netJob->addNetAction(Net::Download::makeByteArray(versions_url, response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response)); QObject::connect(netJob.get(), &NetJob::succeeded, [response, args, &ver] { QJsonParseError parse_error{}; @@ -173,7 +175,7 @@ Task::Ptr FlameAPI::getProjects(QStringList addonIds, std::shared_ptr<QByteArray QJsonDocument body(body_obj); auto body_raw = body.toJson(); - netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods"), response, body_raw)); + netJob->addNetAction(Net::ApiUpload::makeByteArray(QString("https://api.curseforge.com/v1/mods"), response, body_raw)); QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; }); @@ -195,7 +197,7 @@ Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, std::shared_ptr<QByteAr QJsonDocument body(body_obj); auto body_raw = body.toJson(); - netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods/files"), response, body_raw)); + netJob->addNetAction(Net::ApiUpload::makeByteArray(QString("https://api.curseforge.com/v1/mods/files"), response, body_raw)); QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; }); diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index a2628e34..e10fedc3 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -13,6 +13,8 @@ #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourceFolderModel.h" +#include "net/ApiDownload.h" + static FlameAPI api; bool FlameCheckUpdate::abort() @@ -33,7 +35,7 @@ ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info) auto response = std::make_shared<QByteArray>(); auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(ver_info.addonId.toString()); - auto dl = Net::Download::makeByteArray(url, response); + auto dl = Net::ApiDownload::makeByteArray(url, response); get_project_job->addNetAction(dl); QObject::connect(get_project_job, &NetJob::succeeded, [response, &pack]() { @@ -77,7 +79,7 @@ ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId) auto response = std::make_shared<QByteArray>(); auto url = QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(QString::number(addonId), QString::number(fileId)); - auto dl = Net::Download::makeByteArray(url, response); + auto dl = Net::ApiDownload::makeByteArray(url, response); get_file_info_job->addNetAction(dl); QObject::connect(get_file_info_job, &NetJob::succeeded, [response, &ver]() { diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index e17cf1c2..c170a4f5 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -61,6 +61,7 @@ #include "meta/VersionList.h" #include "minecraft/World.h" #include "minecraft/mod/tasks/LocalResourceParse.h" +#include "net/ApiDownload.h" static const FlameAPI api; @@ -523,7 +524,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) case Flame::File::Type::Mod: { if (!result.url.isEmpty()) { qDebug() << "Will download" << result.url << "to" << path; - auto dl = Net::Download::makeFile(result.url, path); + auto dl = Net::ApiDownload::makeFile(result.url, path); m_files_job->addNetAction(dl); } break; diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 227ce489..2215de19 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -54,7 +54,7 @@ void FlameMod::loadURLs(ModPlatform::IndexedPack& pack, QJsonObject& obj) pack.extraDataLoaded = true; } -void FlameMod::loadBody(ModPlatform::IndexedPack& pack, QJsonObject& obj) +void FlameMod::loadBody(ModPlatform::IndexedPack& pack, [[maybe_unused]] QJsonObject& obj) { pack.extraData.body = api.getModDescription(pack.addonId.toInt()); @@ -75,7 +75,7 @@ static QString enumToString(int hash_algorithm) void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, - const shared_qobject_ptr<QNetworkAccessManager>& network, + [[maybe_unused]] const shared_qobject_ptr<QNetworkAccessManager>& network, const BaseInstance* inst) { QVector<ModPlatform::IndexedVersion> unsortedVersions; @@ -193,4 +193,5 @@ ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform:: }; std::sort(versions.begin(), versions.end(), orderSortPredicate); return versions.front(); -}; +} + diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp index 87bf780c..f5f3af37 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.cpp +++ b/launcher/modplatform/flame/FlamePackExportTask.cpp @@ -26,6 +26,7 @@ #include <QMessageBox> #include <QtConcurrentRun> #include <algorithm> +#include <iterator> #include <memory> #include "Json.h" #include "MMCZip.h" @@ -64,20 +65,11 @@ void FlamePackExportTask::executeTask() bool FlamePackExportTask::abort() { - if (task != nullptr) { + if (task) { task->abort(); - task = nullptr; emitAborted(); return true; } - - if (buildZipFuture.isRunning()) { - buildZipFuture.cancel(); - // NOTE: Here we don't do `emitAborted()` because it will be done when `buildZipFuture` actually cancels, which may not occur - // immediately. - return true; - } - return false; } @@ -336,89 +328,40 @@ void FlamePackExportTask::buildZip() setStatus(tr("Adding files...")); setProgress(4, 5); - buildZipFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { - QuaZip zip(output); - if (!zip.open(QuaZip::mdCreate)) { - QFile::remove(output); - return BuildZipResult(tr("Could not create file")); - } - - if (buildZipFuture.isCanceled()) - return BuildZipResult(); + auto zipTask = makeShared<MMCZip::ExportToZipTask>(output, gameRoot, files, "overrides/", true); + zipTask->addExtraFile("manifest.json", generateIndex()); + zipTask->addExtraFile("modlist.html", generateHTML()); - QuaZipFile indexFile(&zip); - if (!indexFile.open(QIODevice::WriteOnly, QuaZipNewInfo("manifest.json"))) { - QFile::remove(output); - return BuildZipResult(tr("Could not create index")); - } - indexFile.write(generateIndex()); + QStringList exclude; + std::transform(resolvedFiles.keyBegin(), resolvedFiles.keyEnd(), std::back_insert_iterator(exclude), + [this](QString file) { return gameRoot.relativeFilePath(file); }); + zipTask->setExcludeFiles(exclude); - QuaZipFile modlist(&zip); - if (!modlist.open(QIODevice::WriteOnly, QuaZipNewInfo("modlist.html"))) { - QFile::remove(output); - return BuildZipResult(tr("Could not create index")); - } - QString content = ""; - for (auto mod : resolvedFiles) { - if (mod.isMod) { - content += QString(TEMPLATE) - .replace("{name}", mod.name.toHtmlEscaped()) - .replace("{url}", ModPlatform::getMetaURL(ModPlatform::ResourceProvider::FLAME, mod.addonId).toHtmlEscaped()) - .replace("{authors}", !mod.authors.isEmpty() ? QString(" (by %1)").arg(mod.authors).toHtmlEscaped() : ""); - } - } - content = "<ul>" + content + "</ul>"; - modlist.write(content.toUtf8()); - - auto progressStep = std::make_shared<TaskStepProgress>(); - - size_t progress = 0; - for (const QFileInfo& file : files) { - if (buildZipFuture.isCanceled()) { - QFile::remove(output); - progressStep->state = TaskStepState::Failed; - stepProgress(*progressStep); - return BuildZipResult(); - } - progressStep->update(progress, files.length()); - stepProgress(*progressStep); - - const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath()); - if (!resolvedFiles.contains(file.absoluteFilePath()) && - !JlCompress::compressFile(&zip, file.absoluteFilePath(), "overrides/" + relative)) { - QFile::remove(output); - return BuildZipResult(tr("Could not read and compress %1").arg(relative)); - } - progress++; - } - - zip.close(); - - if (zip.getZipError() != 0) { - QFile::remove(output); - progressStep->state = TaskStepState::Failed; - stepProgress(*progressStep); - return BuildZipResult(tr("A zip error occurred")); - } + auto progressStep = std::make_shared<TaskStepProgress>(); + connect(zipTask.get(), &Task::finished, this, [this, progressStep] { progressStep->state = TaskStepState::Succeeded; stepProgress(*progressStep); - return BuildZipResult(); }); - connect(&buildZipWatcher, &QFutureWatcher<BuildZipResult>::finished, this, &FlamePackExportTask::finish); - buildZipWatcher.setFuture(buildZipFuture); -} -void FlamePackExportTask::finish() -{ - if (buildZipFuture.isCanceled()) - emitAborted(); - else { - const BuildZipResult result = buildZipFuture.result(); - if (result.has_value()) - emitFailed(result.value()); - else - emitSucceeded(); - } + connect(zipTask.get(), &Task::succeeded, this, &FlamePackExportTask::emitSucceeded); + connect(zipTask.get(), &Task::aborted, this, &FlamePackExportTask::emitAborted); + connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { + progressStep->state = TaskStepState::Failed; + stepProgress(*progressStep); + emitFailed(reason); + }); + connect(zipTask.get(), &Task::stepProgress, this, &FlamePackExportTask::propagateStepProgress); + + connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { + progressStep->update(current, total); + stepProgress(*progressStep); + }); + connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) { + progressStep->status = status; + stepProgress(*progressStep); + }); + task.reset(zipTask); + zipTask->start(); } QByteArray FlamePackExportTask::generateIndex() @@ -471,3 +414,18 @@ QByteArray FlamePackExportTask::generateIndex() return QJsonDocument(obj).toJson(QJsonDocument::Compact); } + +QByteArray FlamePackExportTask::generateHTML() +{ + QString content = ""; + for (auto mod : resolvedFiles) { + if (mod.isMod) { + content += QString(TEMPLATE) + .replace("{name}", mod.name.toHtmlEscaped()) + .replace("{url}", ModPlatform::getMetaURL(ModPlatform::ResourceProvider::FLAME, mod.addonId).toHtmlEscaped()) + .replace("{authors}", !mod.authors.isEmpty() ? QString(" (by %1)").arg(mod.authors).toHtmlEscaped() : ""); + } + } + content = "<ul>" + content + "</ul>"; + return content.toUtf8(); +}
\ No newline at end of file diff --git a/launcher/modplatform/flame/FlamePackExportTask.h b/launcher/modplatform/flame/FlamePackExportTask.h index 3dee0a7e..d3dc6281 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.h +++ b/launcher/modplatform/flame/FlamePackExportTask.h @@ -19,8 +19,6 @@ #pragma once -#include <QFuture> -#include <QFutureWatcher> #include "BaseInstance.h" #include "MMCZip.h" #include "minecraft/MinecraftInstance.h" @@ -52,7 +50,6 @@ class FlamePackExportTask : public Task { const QString output; const MMCZip::FilterFunction filter; - typedef std::optional<QString> BuildZipResult; struct ResolvedFile { int addonId; int version; @@ -76,15 +73,13 @@ class FlamePackExportTask : public Task { QMap<QString, HashInfo> pendingHashes{}; QMap<QString, ResolvedFile> resolvedFiles{}; Task::Ptr task; - QFuture<BuildZipResult> buildZipFuture; - QFutureWatcher<BuildZipResult> buildZipWatcher; void collectFiles(); void collectHashes(); void makeApiRequest(); void getProjectsInfo(); void buildZip(); - void finish(); QByteArray generateIndex(); + QByteArray generateHTML(); }; diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.cpp b/launcher/modplatform/helpers/NetworkResourceAPI.cpp index c278f800..5c3f2003 100644 --- a/launcher/modplatform/helpers/NetworkResourceAPI.cpp +++ b/launcher/modplatform/helpers/NetworkResourceAPI.cpp @@ -10,6 +10,8 @@ #include "modplatform/ModIndex.h" +#include "net/ApiDownload.h" + Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&& callbacks) const { auto search_url_optional = getSearchURL(args); @@ -23,7 +25,7 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks& auto response = std::make_shared<QByteArray>(); auto netJob = makeShared<NetJob>(QString("%1::Search").arg(debugName()), APPLICATION->network()); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(search_url), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(search_url), response)); QObject::connect(netJob.get(), &NetJob::succeeded, [this, response, callbacks] { QJsonParseError parse_error{}; @@ -85,7 +87,7 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi auto netJob = makeShared<NetJob>(QString("%1::Versions").arg(args.pack.name), APPLICATION->network()); auto response = std::make_shared<QByteArray>(); - netJob->addNetAction(Net::Download::makeByteArray(versions_url, response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response)); QObject::connect(netJob.get(), &NetJob::succeeded, [response, callbacks, args] { QJsonParseError parse_error{}; @@ -113,7 +115,7 @@ Task::Ptr NetworkResourceAPI::getProject(QString addonId, std::shared_ptr<QByteA auto netJob = makeShared<NetJob>(QString("%1::GetProject").arg(addonId), APPLICATION->network()); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(project_url), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(project_url), response)); return netJob; } @@ -145,4 +147,5 @@ Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args, }); return netJob; -}; +} + diff --git a/launcher/modplatform/legacy_ftb/PackFetchTask.cpp b/launcher/modplatform/legacy_ftb/PackFetchTask.cpp index a8a0fc2c..e5ad8244 100644 --- a/launcher/modplatform/legacy_ftb/PackFetchTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackFetchTask.cpp @@ -40,6 +40,8 @@ #include "BuildConfig.h" #include "Application.h" +#include "net/ApiDownload.h" + namespace LegacyFTB { void PackFetchTask::fetch() @@ -51,7 +53,7 @@ void PackFetchTask::fetch() QUrl publicPacksUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/modpacks.xml"); qDebug() << "Downloading public version info from" << publicPacksUrl.toString(); - jobPtr->addNetAction(Net::Download::makeByteArray(publicPacksUrl, publicModpacksXmlFileData)); + jobPtr->addNetAction(Net::ApiDownload::makeByteArray(publicPacksUrl, publicModpacksXmlFileData)); QUrl thirdPartyUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/thirdparty.xml"); qDebug() << "Downloading thirdparty version info from" << thirdPartyUrl.toString(); @@ -71,7 +73,7 @@ void PackFetchTask::fetchPrivate(const QStringList& toFetch) for (auto& packCode : toFetch) { auto data = std::make_shared<QByteArray>(); NetJob* job = new NetJob("Fetching private pack", m_network); - job->addNetAction(Net::Download::makeByteArray(privatePackBaseUrl.arg(packCode), data)); + job->addNetAction(Net::ApiDownload::makeByteArray(privatePackBaseUrl.arg(packCode), data)); QObject::connect(job, &NetJob::succeeded, this, [this, job, data, packCode] { ModpackList packs; diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp index 1afe5783..beb295dc 100644 --- a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp @@ -48,6 +48,8 @@ #include "Application.h" #include "BuildConfig.h" +#include "net/ApiDownload.h" + namespace LegacyFTB { PackInstallTask::PackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, Modpack pack, QString version) @@ -77,7 +79,7 @@ void PackInstallTask::downloadPack() } else { url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "modpacks/%1").arg(archivePath); } - netJobContainer->addNetAction(Net::Download::makeFile(url, archivePath)); + netJobContainer->addNetAction(Net::ApiDownload::makeFile(url, archivePath)); connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip); connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed); diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp index 364cf3f3..466c5b10 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.cpp +++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp @@ -6,6 +6,8 @@ #include "Application.h" #include "Json.h" +#include "net/ApiDownload.h" +#include "net/ApiUpload.h" #include "net/NetJob.h" #include "net/Upload.h" @@ -13,7 +15,7 @@ Task::Ptr ModrinthAPI::currentVersion(QString hash, QString hash_format, std::sh { auto netJob = makeShared<NetJob>(QString("Modrinth::GetCurrentVersion"), APPLICATION->network()); - netJob->addNetAction(Net::Download::makeByteArray( + netJob->addNetAction(Net::ApiDownload::makeByteArray( QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1?algorithm=%2").arg(hash, hash_format), response)); return netJob; @@ -31,7 +33,7 @@ Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_f QJsonDocument body(body_obj); auto body_raw = body.toJson(); - netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), response, body_raw)); + netJob->addNetAction(Net::ApiUpload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), response, body_raw)); return netJob; } @@ -60,7 +62,7 @@ Task::Ptr ModrinthAPI::latestVersion(QString hash, QJsonDocument body(body_obj); auto body_raw = body.toJson(); - netJob->addNetAction(Net::Upload::makeByteArray( + netJob->addNetAction(Net::ApiUpload::makeByteArray( QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1/update?algorithm=%2").arg(hash, hash_format), response, body_raw)); return netJob; @@ -93,7 +95,8 @@ Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes, QJsonDocument body(body_obj); auto body_raw = body.toJson(); - netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), response, body_raw)); + netJob->addNetAction( + Net::ApiUpload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), response, body_raw)); return netJob; } @@ -103,7 +106,7 @@ Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, std::shared_ptr<QByteAr auto netJob = makeShared<NetJob>(QString("Modrinth::GetProjects"), APPLICATION->network()); auto searchUrl = getMultipleModInfoURL(addonIds); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response)); return netJob; } diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index bd0b828c..2e42d6e7 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -12,6 +12,7 @@ #include "net/ChecksumValidator.h" #include "net/NetJob.h" +#include "net/ApiDownload.h" #include "settings/INISettingsObject.h" #include "ui/dialogs/CustomMessageBox.h" @@ -238,7 +239,7 @@ bool ModrinthCreationTask::createInstance() } qDebug() << "Will try to download" << file.downloads.front() << "to" << file_path; - auto dl = Net::Download::makeFile(file.downloads.dequeue(), file_path); + auto dl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path); dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash)); m_files_job->addNetAction(dl); @@ -247,7 +248,7 @@ bool ModrinthCreationTask::createInstance() // MultipleOptionsTask's , once those exist :) auto param = dl.toWeakRef(); connect(dl.get(), &NetAction::failed, [this, &file, file_path, param] { - auto ndl = Net::Download::makeFile(file.downloads.dequeue(), file_path); + auto ndl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path); ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash)); m_files_job->addNetAction(ndl); if (auto shared = param.lock()) shared->succeeded(); diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp index 30fe566d..64d31f42 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp @@ -55,20 +55,11 @@ void ModrinthPackExportTask::executeTask() bool ModrinthPackExportTask::abort() { - if (task != nullptr) { + if (task) { task->abort(); - task = nullptr; emitAborted(); return true; } - - if (buildZipFuture.isRunning()) { - buildZipFuture.cancel(); - // NOTE: Here we don't do `emitAborted()` because it will be done when `buildZipFuture` actually cancels, which may not occur - // immediately. - return true; - } - return false; } @@ -183,10 +174,10 @@ void ModrinthPackExportTask::parseApiResponse(const std::shared_ptr<QByteArray> if (obj.isEmpty()) continue; - const QJsonArray files = obj["files"].toArray(); - if (auto fileIter = std::find_if(files.begin(), files.end(), + const QJsonArray files_array = obj["files"].toArray(); + if (auto fileIter = std::find_if(files_array.begin(), files_array.end(), [&iterator](const QJsonValue& file) { return file["hashes"]["sha512"] == iterator.value(); }); - fileIter != files.end()) { + fileIter != files_array.end()) { // map the file to the url resolvedFiles[iterator.key()] = ResolvedFile{ fileIter->toObject()["hashes"].toObject()["sha1"].toString(), iterator.value(), @@ -205,63 +196,36 @@ void ModrinthPackExportTask::buildZip() { setStatus(tr("Adding files...")); - buildZipFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { - QuaZip zip(output); - if (!zip.open(QuaZip::mdCreate)) { - QFile::remove(output); - return BuildZipResult(tr("Could not create file")); - } - - if (buildZipFuture.isCanceled()) - return BuildZipResult(); - - QuaZipFile indexFile(&zip); - if (!indexFile.open(QIODevice::WriteOnly, QuaZipNewInfo("modrinth.index.json"))) { - QFile::remove(output); - return BuildZipResult(tr("Could not create index")); - } - indexFile.write(generateIndex()); - - size_t progress = 0; - for (const QFileInfo& file : files) { - if (buildZipFuture.isCanceled()) { - QFile::remove(output); - return BuildZipResult(); - } - - setProgress(progress, files.length()); - const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath()); - if (!resolvedFiles.contains(relative) && !JlCompress::compressFile(&zip, file.absoluteFilePath(), "overrides/" + relative)) { - QFile::remove(output); - return BuildZipResult(tr("Could not read and compress %1").arg(relative)); - } - progress++; - } + auto zipTask = makeShared<MMCZip::ExportToZipTask>(output, gameRoot, files, "overrides/", true); + zipTask->addExtraFile("modrinth.index.json", generateIndex()); - zip.close(); + zipTask->setExcludeFiles(resolvedFiles.keys()); - if (zip.getZipError() != 0) { - QFile::remove(output); - return BuildZipResult(tr("A zip error occurred")); - } + auto progressStep = std::make_shared<TaskStepProgress>(); + connect(zipTask.get(), &Task::finished, this, [this, progressStep] { + progressStep->state = TaskStepState::Succeeded; + stepProgress(*progressStep); + }); - return BuildZipResult(); + connect(zipTask.get(), &Task::succeeded, this, &ModrinthPackExportTask::emitSucceeded); + connect(zipTask.get(), &Task::aborted, this, &ModrinthPackExportTask::emitAborted); + connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { + progressStep->state = TaskStepState::Failed; + stepProgress(*progressStep); + emitFailed(reason); }); - connect(&buildZipWatcher, &QFutureWatcher<BuildZipResult>::finished, this, &ModrinthPackExportTask::finish); - buildZipWatcher.setFuture(buildZipFuture); -} + connect(zipTask.get(), &Task::stepProgress, this, &ModrinthPackExportTask::propagateStepProgress); -void ModrinthPackExportTask::finish() -{ - if (buildZipFuture.isCanceled()) - emitAborted(); - else { - const BuildZipResult result = buildZipFuture.result(); - if (result.has_value()) - emitFailed(result.value()); - else - emitSucceeded(); - } + connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { + progressStep->update(current, total); + stepProgress(*progressStep); + }); + connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) { + progressStep->status = status; + stepProgress(*progressStep); + }); + task.reset(zipTask); + zipTask->start(); } QByteArray ModrinthPackExportTask::generateIndex() @@ -296,6 +260,7 @@ QByteArray ModrinthPackExportTask::generateIndex() out["dependencies"] = dependencies; } + QJsonArray filesOut; for (auto iterator = resolvedFiles.constBegin(); iterator != resolvedFiles.constEnd(); iterator++) { QJsonObject fileOut; @@ -323,6 +288,7 @@ QByteArray ModrinthPackExportTask::generateIndex() hashes["sha512"] = value.sha512; fileOut["hashes"] = hashes; + fileOut["fileSize"] = value.size; filesOut << fileOut; } diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.h b/launcher/modplatform/modrinth/ModrinthPackExportTask.h index 96f292c1..1f9e0eb7 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.h +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.h @@ -56,22 +56,17 @@ class ModrinthPackExportTask : public Task { const QString output; const MMCZip::FilterFunction filter; - typedef std::optional<QString> BuildZipResult; - ModrinthAPI api; QFileInfoList files; QMap<QString, QString> pendingHashes; QMap<QString, ResolvedFile> resolvedFiles; Task::Ptr task; - QFuture<BuildZipResult> buildZipFuture; - QFutureWatcher<BuildZipResult> buildZipWatcher; void collectFiles(); void collectHashes(); void makeApiRequest(); void parseApiResponse(const std::shared_ptr<QByteArray> response); void buildZip(); - void finish(); QByteArray generateIndex(); }; diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index b4037349..969018a9 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -95,7 +95,7 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, - const shared_qobject_ptr<QNetworkAccessManager>& network, + [[maybe_unused]] const shared_qobject_ptr<QNetworkAccessManager>& network, const BaseInstance* inst) { QVector<ModPlatform::IndexedVersion> unsortedVersions; @@ -218,7 +218,7 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t return {}; } -auto Modrinth::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion +auto Modrinth::loadDependencyVersions([[maybe_unused]] const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion { QVector<ModPlatform::IndexedVersion> versions; @@ -235,4 +235,4 @@ auto Modrinth::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArr }; std::sort(versions.begin(), versions.end(), orderSortPredicate); return versions.length() != 0 ? versions.front() : ModPlatform::IndexedVersion(); -}
\ No newline at end of file +} diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 510c7309..82deef8b 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -89,7 +89,8 @@ auto intEntry(toml::table table, QString entry_name) -> int return node.value_or(0); } -auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod +auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) + -> Mod { Mod mod; @@ -114,7 +115,7 @@ auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, Mo return mod; } -auto V1::createModFormat(QDir& index_dir, ::Mod& internal_mod, QString slug) -> Mod +auto V1::createModFormat(QDir& index_dir, [[maybe_unused]] ::Mod& internal_mod, QString slug) -> Mod { // Try getting metadata if it exists Mod mod{ getIndexForMod(index_dir, slug) }; @@ -241,12 +242,13 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod return {}; } #else - table = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname))); - if (!table) { + toml::parse_result result = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname))); + if (!result) { qWarning() << QString("Could not open file %1!").arg(normalized_fname); - qWarning() << "Reason: " << QString(table.error().what()); + qWarning() << "Reason: " << result.error().description(); return {}; } + table = result.table(); #endif // index_file.close(); diff --git a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp index ab91c466..b2097c1d 100644 --- a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp +++ b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp @@ -23,6 +23,8 @@ #include "Application.h" +#include "net/ApiDownload.h" + Technic::SingleZipPackInstallTask::SingleZipPackInstallTask(const QUrl &sourceUrl, const QString &minecraftVersion) { m_sourceUrl = sourceUrl; @@ -45,7 +47,7 @@ void Technic::SingleZipPackInstallTask::executeTask() auto entry = APPLICATION->metacache()->resolveEntry("general", path); entry->setStale(true); m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); - m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); + m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry)); m_archivePath = entry->getFullPath(); auto job = m_filesNetJob.get(); connect(job, &NetJob::succeeded, this, &Technic::SingleZipPackInstallTask::downloadSucceeded); diff --git a/launcher/modplatform/technic/SolderPackInstallTask.cpp b/launcher/modplatform/technic/SolderPackInstallTask.cpp index cc1d261e..d7e9f640 100644 --- a/launcher/modplatform/technic/SolderPackInstallTask.cpp +++ b/launcher/modplatform/technic/SolderPackInstallTask.cpp @@ -43,6 +43,7 @@ #include "SolderPackManifest.h" #include "TechnicPackProcessor.h" #include "net/ChecksumValidator.h" +#include "net/ApiDownload.h" Technic::SolderPackInstallTask::SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, const QUrl& solderUrl, @@ -71,7 +72,7 @@ void Technic::SolderPackInstallTask::executeTask() m_filesNetJob.reset(new NetJob(tr("Resolving modpack files"), m_network)); auto sourceUrl = QString("%1/modpack/%2/%3").arg(m_solderUrl.toString(), m_pack, m_version); - m_filesNetJob->addNetAction(Net::Download::makeByteArray(sourceUrl, m_response)); + m_filesNetJob->addNetAction(Net::ApiDownload::makeByteArray(sourceUrl, m_response)); auto job = m_filesNetJob.get(); connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::fileListSucceeded); @@ -112,7 +113,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded() for (const auto& mod : build.mods) { auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i)); - auto dl = Net::Download::makeFile(mod.url, path); + auto dl = Net::ApiDownload::makeFile(mod.url, path); if (!mod.md5.isEmpty()) { auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); diff --git a/launcher/modplatform/technic/TechnicPackProcessor.cpp b/launcher/modplatform/technic/TechnicPackProcessor.cpp index df713a72..e473c170 100644 --- a/launcher/modplatform/technic/TechnicPackProcessor.cpp +++ b/launcher/modplatform/technic/TechnicPackProcessor.cpp @@ -26,7 +26,12 @@ #include <memory> -void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const QString &instName, const QString &instIcon, const QString &stagingPath, const QString &minecraftVersion, const bool isSolder) +void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, + const QString& instName, + const QString& instIcon, + const QString& stagingPath, + const QString& minecraftVersion, + [[maybe_unused]] const bool isSolder) { QString minecraftPath = FS::PathCombine(stagingPath, ".minecraft"); QString configPath = FS::PathCombine(stagingPath, "instance.cfg"); @@ -151,17 +156,16 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const { QJsonDocument doc = Json::requireDocument(data); QJsonObject root = Json::requireObject(doc, "version.json"); - QString minecraftVersion = Json::ensureString(root, "inheritsFrom", QString(), ""); - if (minecraftVersion.isEmpty()) - { + QString packMinecraftVersion = Json::ensureString(root, "inheritsFrom", QString(), ""); + if (packMinecraftVersion.isEmpty()) { if (fmlMinecraftVersion.isEmpty()) { emit failed(tr("Could not understand \"version.json\":\ninheritsFrom is missing")); return; } - minecraftVersion = fmlMinecraftVersion; + packMinecraftVersion = fmlMinecraftVersion; } - components->setComponentVersion("net.minecraft", minecraftVersion, true); + components->setComponentVersion("net.minecraft", packMinecraftVersion, true); for (auto library: Json::ensureArray(root, "libraries", {})) { if (!library.isObject()) diff --git a/launcher/net/ApiDownload.cpp b/launcher/net/ApiDownload.cpp new file mode 100644 index 00000000..aaa8ff65 --- /dev/null +++ b/launcher/net/ApiDownload.cpp @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#include "net/ApiDownload.h" +#include "ByteArraySink.h" +#include "ChecksumValidator.h" +#include "MetaCacheSink.h" +#include "net/NetAction.h" + +namespace Net { + +auto ApiDownload::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr +{ + auto dl = makeShared<ApiDownload>(); + dl->m_url = url; + dl->setObjectName(QString("CACHE:") + url.toString()); + dl->m_options = options; + auto md5Node = new ChecksumValidator(QCryptographicHash::Md5); + auto cachedNode = new MetaCacheSink(entry, md5Node, options.testFlag(Option::MakeEternal)); + dl->m_sink.reset(cachedNode); + return dl; +} + +auto ApiDownload::makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Options options) -> Download::Ptr +{ + auto dl = makeShared<ApiDownload>(); + dl->m_url = url; + dl->setObjectName(QString("BYTES:") + url.toString()); + dl->m_options = options; + dl->m_sink.reset(new ByteArraySink(output)); + return dl; +} + +auto ApiDownload::makeFile(QUrl url, QString path, Options options) -> Download::Ptr +{ + auto dl = makeShared<ApiDownload>(); + dl->m_url = url; + dl->setObjectName(QString("FILE:") + url.toString()); + dl->m_options = options; + dl->m_sink.reset(new FileSink(path)); + return dl; +} + +void ApiDownload::init() +{ + qDebug() << "Setting up api download"; + auto api_headers = new ApiHeaderProxy(); + addHeaderProxy(api_headers); +} +} // namespace Net diff --git a/launcher/net/ApiDownload.h b/launcher/net/ApiDownload.h new file mode 100644 index 00000000..638c94e1 --- /dev/null +++ b/launcher/net/ApiDownload.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#pragma once + +#include "ApiHeaderProxy.h" +#include "Download.h" + +namespace Net { + +class ApiDownload : public Download { + public: + virtual ~ApiDownload() = default; + + static auto makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions) -> Download::Ptr; + static auto makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Options options = Option::NoOptions) -> Download::Ptr; + static auto makeFile(QUrl url, QString path, Options options = Option::NoOptions) -> Download::Ptr; + + void init() override; +}; + +} // namespace Net diff --git a/launcher/net/ApiHeaderProxy.h b/launcher/net/ApiHeaderProxy.h new file mode 100644 index 00000000..789a6fad --- /dev/null +++ b/launcher/net/ApiHeaderProxy.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#pragma once + +#include "Application.h" +#include "BuildConfig.h" +#include "net/HeaderProxy.h" + +namespace Net { + +class ApiHeaderProxy : public HeaderProxy { + public: + ApiHeaderProxy() : HeaderProxy() {} + virtual ~ApiHeaderProxy() = default; + + public: + virtual QList<HeaderPair> headers(const QNetworkRequest& request) const override + { + QList<HeaderPair> hdrs; + if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) { + hdrs.append({ "x-api-key", APPLICATION->getFlameAPIKey().toUtf8() }); + } else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || + request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { + QString token = APPLICATION->getModrinthAPIToken(); + if (!token.isNull()) + hdrs.append({ "Authorization", token.toUtf8() }); + } + return hdrs; + }; +}; + +} // namespace Net diff --git a/launcher/net/ApiUpload.cpp b/launcher/net/ApiUpload.cpp new file mode 100644 index 00000000..c1221b76 --- /dev/null +++ b/launcher/net/ApiUpload.cpp @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#include "net/ApiUpload.h" +#include "ByteArraySink.h" +#include "ChecksumValidator.h" +#include "MetaCacheSink.h" +#include "net/NetAction.h" + +namespace Net { + +Upload::Ptr ApiUpload::makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, QByteArray m_post_data) +{ + auto up = makeShared<ApiUpload>(); + up->m_url = std::move(url); + up->m_sink.reset(new ByteArraySink(output)); + up->m_post_data = std::move(m_post_data); + return up; +} + +void ApiUpload::init() +{ + qDebug() << "Setting up api upload"; + auto api_headers = new ApiHeaderProxy(); + addHeaderProxy(api_headers); +} +} // namespace Net diff --git a/launcher/net/ApiUpload.h b/launcher/net/ApiUpload.h new file mode 100644 index 00000000..b12842b0 --- /dev/null +++ b/launcher/net/ApiUpload.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#pragma once + +#include "ApiHeaderProxy.h" +#include "Upload.h" + +namespace Net { + +class ApiUpload : public Upload { + public: + virtual ~ApiUpload() = default; + + static Upload::Ptr makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, QByteArray m_post_data); + + void init() override; +}; + +} // namespace Net diff --git a/launcher/net/ByteArraySink.h b/launcher/net/ByteArraySink.h index d6b17d60..7b8f0f8a 100644 --- a/launcher/net/ByteArraySink.h +++ b/launcher/net/ByteArraySink.h @@ -42,8 +42,6 @@ namespace Net { /* * Sink object for downloads that uses an external QByteArray it doesn't own as a target. - * FIXME: It is possible that the QByteArray is freed while we're doing some operation on it, - * causing a segmentation fault. */ class ByteArraySink : public Sink { public: diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index 4ea45c63..d25447b2 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -47,17 +47,11 @@ #include "ChecksumValidator.h" #include "MetaCacheSink.h" -#include "Application.h" -#include "BuildConfig.h" - -#include "net/Logging.h" #include "net/NetAction.h" -#include "MMCTime.h" -#include "StringUtils.h" - namespace Net { +#if defined(LAUNCHER_APPLICATION) auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr { auto dl = makeShared<Download>(); @@ -69,6 +63,7 @@ auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Down dl->m_sink.reset(cachedNode); return dl; } +#endif auto Download::makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Options options) -> Download::Ptr { @@ -90,260 +85,8 @@ auto Download::makeFile(QUrl url, QString path, Options options) -> Download::Pt return dl; } -void Download::addValidator(Validator* v) -{ - m_sink->addValidator(v); -} - -void Download::executeTask() -{ - setStatus(tr("Downloading %1").arg(StringUtils::truncateUrlHumanFriendly(m_url, 80))); - - if (getState() == Task::State::AbortedByUser) { - qCWarning(taskDownloadLogC) << getUid().toString() << "Attempt to start an aborted Download:" << m_url.toString(); - emitAborted(); - return; - } - - QNetworkRequest request(m_url); - m_state = m_sink->init(request); - switch (m_state) { - case State::Succeeded: - emit succeeded(); - qCDebug(taskDownloadLogC) << getUid().toString() << "Download cache hit " << m_url.toString(); - return; - case State::Running: - qCDebug(taskDownloadLogC) << getUid().toString() << "Downloading " << m_url.toString(); - break; - case State::Inactive: - case State::Failed: - emitFailed(); - return; - case State::AbortedByUser: - emitAborted(); - return; - } - - request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8()); - // TODO remove duplication - if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) { - request.setRawHeader("x-api-key", APPLICATION->getFlameAPIKey().toUtf8()); - } else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || - request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { - QString token = APPLICATION->getModrinthAPIToken(); - if (!token.isNull()) - request.setRawHeader("Authorization", token.toUtf8()); - } - -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) - request.setTransferTimeout(); -#endif - - m_last_progress_time = m_clock.now(); - m_last_progress_bytes = 0; - - QNetworkReply* rep = m_network->get(request); - m_reply.reset(rep); - connect(rep, &QNetworkReply::downloadProgress, this, &Download::downloadProgress); - connect(rep, &QNetworkReply::finished, this, &Download::downloadFinished); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &Download::downloadError); -#else - connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &Download::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &Download::sslErrors); - connect(rep, &QNetworkReply::readyRead, this, &Download::downloadReadyRead); -} - -void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) -{ - auto now = m_clock.now(); - auto elapsed = now - m_last_progress_time; - - // use milliseconds for speed precision - auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed); - auto bytes_received_since = bytesReceived - m_last_progress_bytes; - auto dl_speed_bps = (double)bytes_received_since / elapsed_ms.count() * 1000; - auto remaing_time_s = (bytesTotal - bytesReceived) / dl_speed_bps; - - //: Current amount of bytes downloaded, out of the total amount of bytes in the download - QString dl_progress = - tr("%1 / %2").arg(StringUtils::humanReadableFileSize(bytesReceived)).arg(StringUtils::humanReadableFileSize(bytesTotal)); - - QString dl_speed_str; - if (elapsed_ms.count() > 0) { - auto str_eta = bytesTotal > 0 ? Time::humanReadableDuration(remaing_time_s) : tr("unknown"); - //: Download speed, in bytes per second (remaining download time in parenthesis) - dl_speed_str = - tr("%1 /s (%2)").arg(StringUtils::humanReadableFileSize(dl_speed_bps)).arg(str_eta); - } else { - //: Download speed at 0 bytes per second - dl_speed_str = tr("0 B/s"); - } - - setDetails(dl_progress + "\n" + dl_speed_str); - - setProgress(bytesReceived, bytesTotal); -} - -void Download::downloadError(QNetworkReply::NetworkError error) -{ - if (error == QNetworkReply::OperationCanceledError) { - qCCritical(taskDownloadLogC) << getUid().toString() << "Aborted " << m_url.toString(); - m_state = State::AbortedByUser; - } else { - if (m_options & Option::AcceptLocalFiles) { - if (m_sink->hasLocalData()) { - m_state = State::Succeeded; - return; - } - } - // error happened during download. - qCCritical(taskDownloadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error; - m_state = State::Failed; - } -} - -void Download::sslErrors(const QList<QSslError>& errors) -{ - int i = 1; - for (auto error : errors) { - qCCritical(taskDownloadLogC) << getUid().toString() << "Download" << m_url.toString() << "SSL Error #" << i << " : " - << error.errorString(); - auto cert = error.certificate(); - qCCritical(taskDownloadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -auto Download::handleRedirect() -> bool -{ - QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl(); - if (!redirect.isValid()) { - if (!m_reply->hasRawHeader("Location")) { - // no redirect -> it's fine to continue - return false; - } - // there is a Location header, but it's not correct. we need to apply some workarounds... - QByteArray redirectBA = m_reply->rawHeader("Location"); - if (redirectBA.size() == 0) { - // empty, yet present redirect header? WTF? - return false; - } - QString redirectStr = QString::fromUtf8(redirectBA); - - if (redirectStr.startsWith("//")) { - /* - * IF the URL begins with //, we need to insert the URL scheme. - * See: https://bugreports.qt.io/browse/QTBUG-41061 - * See: http://tools.ietf.org/html/rfc3986#section-4.2 - */ - redirectStr = m_reply->url().scheme() + ":" + redirectStr; - } else if (redirectStr.startsWith("/")) { - /* - * IF the URL begins with /, we need to process it as a relative URL - */ - auto url = m_reply->url(); - url.setPath(redirectStr, QUrl::TolerantMode); - redirectStr = url.toString(); - } - - /* - * Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues. - * FIXME: report Qt bug for this - */ - redirect = QUrl(redirectStr, QUrl::TolerantMode); - if (!redirect.isValid()) { - qCWarning(taskDownloadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr; - downloadError(QNetworkReply::ProtocolFailure); - return false; - } - qCDebug(taskDownloadLogC) << getUid().toString() << "Fixed location header:" << redirect; - } else { - qCDebug(taskDownloadLogC) << getUid().toString() << "Location header:" << redirect; - } - - m_url = QUrl(redirect.toString()); - qCDebug(taskDownloadLogC) << getUid().toString() << "Following redirect to " << m_url.toString(); - startAction(m_network); - - return true; -} - -void Download::downloadFinished() -{ - // handle HTTP redirection first - if (handleRedirect()) { - qCDebug(taskDownloadLogC) << getUid().toString() << "Download redirected:" << m_url.toString(); - return; - } - - // if the download failed before this point ... - if (m_state == State::Succeeded) // pretend to succeed so we continue processing :) - { - qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed but we are allowed to proceed:" << m_url.toString(); - m_sink->abort(); - m_reply.reset(); - emit succeeded(); - return; - } else if (m_state == State::Failed) { - qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed in previous step:" << m_url.toString(); - m_sink->abort(); - m_reply.reset(); - emit failed(""); - return; - } else if (m_state == State::AbortedByUser) { - qCDebug(taskDownloadLogC) << getUid().toString() << "Download aborted in previous step:" << m_url.toString(); - m_sink->abort(); - m_reply.reset(); - emit aborted(); - return; - } - - // make sure we got all the remaining data, if any - auto data = m_reply->readAll(); - if (data.size()) { - qCDebug(taskDownloadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes"; - m_state = m_sink->write(data); - } - - // otherwise, finalize the whole graph - m_state = m_sink->finalize(*m_reply.get()); - if (m_state != State::Succeeded) { - qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed to finalize:" << m_url.toString(); - m_sink->abort(); - m_reply.reset(); - emit failed(""); - return; - } - - m_reply.reset(); - qCDebug(taskDownloadLogC) << getUid().toString() << "Download succeeded:" << m_url.toString(); - emit succeeded(); -} - -void Download::downloadReadyRead() -{ - if (m_state == State::Running) { - auto data = m_reply->readAll(); - m_state = m_sink->write(data); - if (m_state == State::Failed) { - qCCritical(taskDownloadLogC) << getUid().toString() << "Failed to process response chunk"; - } - // qDebug() << "Download" << m_url.toString() << "gained" << data.size() << "bytes"; - } else { - qCCritical(taskDownloadLogC) << getUid().toString() << "Cannot write download data! illegal status " << m_status; - } -} - -} // namespace Net - -auto Net::Download::abort() -> bool +QNetworkReply* Download::getReply(QNetworkRequest& request) { - if (m_reply) { - m_reply->abort(); - } else { - m_state = State::AbortedByUser; - } - return true; + return m_network->get(request); } +} // namespace Net
\ No newline at end of file diff --git a/launcher/net/Download.h b/launcher/net/Download.h index 2e861732..5f6a5caf 100644 --- a/launcher/net/Download.h +++ b/launcher/net/Download.h @@ -38,57 +38,26 @@ #pragma once -#include <chrono> - #include "HttpMetaCache.h" -#include "NetAction.h" -#include "Sink.h" -#include "Validator.h" #include "QObjectPtr.h" +#include "net/NetRequest.h" namespace Net { -class Download : public NetAction { +class Download : public NetRequest { Q_OBJECT - public: using Ptr = shared_qobject_ptr<class Download>; - enum class Option { NoOptions = 0, AcceptLocalFiles = 1, MakeEternal = 2 }; - Q_DECLARE_FLAGS(Options, Option) - - public: - ~Download() override = default; + explicit Download() : NetRequest() { logCat = taskDownloadLogC; } +#if defined(LAUNCHER_APPLICATION) static auto makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions) -> Download::Ptr; +#endif + static auto makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Options options = Option::NoOptions) -> Download::Ptr; static auto makeFile(QUrl url, QString path, Options options = Option::NoOptions) -> Download::Ptr; - public: - void addValidator(Validator* v); - auto abort() -> bool override; - auto canAbort() const -> bool override { return true; }; - - private: - auto handleRedirect() -> bool; - - protected slots: - void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; - void downloadError(QNetworkReply::NetworkError error) override; - void sslErrors(const QList<QSslError>& errors) override; - void downloadFinished() override; - void downloadReadyRead() override; - - public slots: - void executeTask() override; - - private: - std::unique_ptr<Sink> m_sink; - Options m_options; - - std::chrono::steady_clock m_clock; - std::chrono::time_point<std::chrono::steady_clock> m_last_progress_time; - qint64 m_last_progress_bytes; + protected: + virtual QNetworkReply* getReply(QNetworkRequest&) override; }; } // namespace Net - -Q_DECLARE_OPERATORS_FOR_FLAGS(Net::Download::Options) diff --git a/launcher/net/HeaderProxy.h b/launcher/net/HeaderProxy.h new file mode 100644 index 00000000..213c04c5 --- /dev/null +++ b/launcher/net/HeaderProxy.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#pragma once + +#include <QDebug> +#include <QNetworkRequest> + +namespace Net { + +struct HeaderPair { + QByteArray headerName; + QByteArray headerValue; +}; + +class HeaderProxy { + public: + HeaderProxy(){} + virtual ~HeaderProxy(){} + + public: + virtual QList<HeaderPair> headers(const QNetworkRequest& request) const = 0; + + public: + void writeHeaders(QNetworkRequest& request) + { + for (auto header : headers(request)) { + request.setRawHeader(header.headerName, header.headerValue); + } + } +}; + +} // namespace Net diff --git a/launcher/net/HttpMetaCache.h b/launcher/net/HttpMetaCache.h index 0dcb5668..036a8dd9 100644 --- a/launcher/net/HttpMetaCache.h +++ b/launcher/net/HttpMetaCache.h @@ -74,7 +74,7 @@ class MetaEntry { auto getMaximumAge() -> qint64 { return m_max_age; } void setMaximumAge(qint64 age) { m_max_age = age; } - bool isExpired(qint64 offset) { return !m_is_eternal && (m_current_age >= m_max_age - offset); }; + bool isExpired(qint64 offset) { return !m_is_eternal && (m_current_age >= m_max_age - offset); } protected: QString m_baseId; diff --git a/launcher/net/NetAction.h b/launcher/net/NetAction.h index ab9322c2..247b34c0 100644 --- a/launcher/net/NetAction.h +++ b/launcher/net/NetAction.h @@ -42,10 +42,12 @@ #include "QObjectPtr.h" #include "tasks/Task.h" +#include "HeaderProxy.h" + class NetAction : public Task { Q_OBJECT protected: - explicit NetAction() : Task(){}; + explicit NetAction() : Task(){} public: using Ptr = shared_qobject_ptr<NetAction>; @@ -56,13 +58,17 @@ class NetAction : public Task { void setNetwork(shared_qobject_ptr<QNetworkAccessManager> network) { m_network = network; } + void addHeaderProxy(Net::HeaderProxy* proxy) { m_headerProxies.push_back(std::shared_ptr<Net::HeaderProxy>(proxy)); } + virtual void init() = 0; + protected slots: virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0; virtual void downloadError(QNetworkReply::NetworkError error) = 0; virtual void downloadFinished() = 0; virtual void downloadReadyRead() = 0; - virtual void sslErrors(const QList<QSslError>& errors) { + virtual void sslErrors(const QList<QSslError>& errors) + { int i = 1; for (auto error : errors) { qCritical() << "Network SSL Error #" << i << " : " << error.errorString(); @@ -70,8 +76,7 @@ class NetAction : public Task { qCritical() << "Certificate in question:\n" << cert.toText(); i++; } - - }; + } public slots: void startAction(shared_qobject_ptr<QNetworkAccessManager> network) @@ -81,7 +86,7 @@ class NetAction : public Task { } protected: - void executeTask() override{}; + void executeTask() override{} public: shared_qobject_ptr<QNetworkAccessManager> m_network; @@ -91,4 +96,5 @@ class NetAction : public Task { /// source URL QUrl m_url; + std::vector<std::shared_ptr<Net::HeaderProxy>> m_headerProxies; }; diff --git a/launcher/net/NetRequest.cpp b/launcher/net/NetRequest.cpp new file mode 100644 index 00000000..ff59da18 --- /dev/null +++ b/launcher/net/NetRequest.cpp @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln <flowlnlnln@gmail.com> + * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NetRequest.h" +#include <QUrl> + +#include <QDateTime> +#include <QFileInfo> +#include <memory> + +#if defined(LAUNCHER_APPLICATION) +#include "Application.h" +#endif + +#include "net/NetAction.h" + +#include "MMCTime.h" +#include "StringUtils.h" + +namespace Net { + +void NetRequest::addValidator(Validator* v) +{ + m_sink->addValidator(v); +} + +void NetRequest::executeTask() +{ + init(); + + setStatus(tr("Requesting %1").arg(StringUtils::truncateUrlHumanFriendly(m_url, 80))); + + if (getState() == Task::State::AbortedByUser) { + qCWarning(logCat) << getUid().toString() << "Attempt to start an aborted Request:" << m_url.toString(); + emitAborted(); + return; + } + + QNetworkRequest request(m_url); + m_state = m_sink->init(request); + switch (m_state) { + case State::Succeeded: + qCDebug(logCat) << getUid().toString() << "Request cache hit " << m_url.toString(); + emit succeeded(); + emit finished(); + return; + case State::Running: + qCDebug(logCat) << getUid().toString() << "Runninng " << m_url.toString(); + break; + case State::Inactive: + case State::Failed: + emitFailed(); + return; + case State::AbortedByUser: + emitAborted(); + return; + } + +#if defined(LAUNCHER_APPLICATION) + auto user_agent = APPLICATION->getUserAgent(); +#else + auto user_agent = BuildConfig.USER_AGENT; +#endif + + request.setHeader(QNetworkRequest::UserAgentHeader, user_agent.toUtf8()); + for (auto& header_proxy : m_headerProxies) { + header_proxy->writeHeaders(request); + } + // TODO remove duplication + +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + request.setTransferTimeout(); +#endif + + m_last_progress_time = m_clock.now(); + m_last_progress_bytes = 0; + + QNetworkReply* rep = getReply(request); + m_reply.reset(rep); + connect(rep, &QNetworkReply::downloadProgress, this, &NetRequest::downloadProgress); + connect(rep, &QNetworkReply::finished, this, &NetRequest::downloadFinished); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 + connect(rep, &QNetworkReply::errorOccurred, this, &NetRequest::downloadError); +#else + connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &NetRequest::downloadError); +#endif + connect(rep, &QNetworkReply::sslErrors, this, &NetRequest::sslErrors); + connect(rep, &QNetworkReply::readyRead, this, &NetRequest::downloadReadyRead); +} + +void NetRequest::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + auto now = m_clock.now(); + auto elapsed = now - m_last_progress_time; + + // use milliseconds for speed precision + auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed); + auto bytes_received_since = bytesReceived - m_last_progress_bytes; + auto dl_speed_bps = (double)bytes_received_since / elapsed_ms.count() * 1000; + auto remaining_time_s = (bytesTotal - bytesReceived) / dl_speed_bps; + + //: Current amount of bytes downloaded, out of the total amount of bytes in the download + QString dl_progress = + tr("%1 / %2").arg(StringUtils::humanReadableFileSize(bytesReceived)).arg(StringUtils::humanReadableFileSize(bytesTotal)); + + QString dl_speed_str; + if (elapsed_ms.count() > 0) { + auto str_eta = bytesTotal > 0 ? Time::humanReadableDuration(remaining_time_s) : tr("unknown"); + //: Download speed, in bytes per second (remaining download time in parenthesis) + dl_speed_str = tr("%1 /s (%2)").arg(StringUtils::humanReadableFileSize(dl_speed_bps)).arg(str_eta); + } else { + //: Download speed at 0 bytes per second + dl_speed_str = tr("0 B/s"); + } + + setDetails(dl_progress + "\n" + dl_speed_str); + + setProgress(bytesReceived, bytesTotal); +} + +void NetRequest::downloadError(QNetworkReply::NetworkError error) +{ + if (error == QNetworkReply::OperationCanceledError) { + qCCritical(logCat) << getUid().toString() << "Aborted " << m_url.toString(); + m_state = State::Failed; + } else { + if (m_options & Option::AcceptLocalFiles) { + if (m_sink->hasLocalData()) { + m_state = State::Succeeded; + return; + } + } + // error happened during download. + qCCritical(logCat) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error; + m_state = State::Failed; + } +} + +void NetRequest::sslErrors(const QList<QSslError>& errors) +{ + int i = 1; + for (auto error : errors) { + qCCritical(logCat) << getUid().toString() << "Request" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString(); + auto cert = error.certificate(); + qCCritical(logCat) << getUid().toString() << "Certificate in question:\n" << cert.toText(); + i++; + } +} + +auto NetRequest::handleRedirect() -> bool +{ + QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl(); + if (!redirect.isValid()) { + if (!m_reply->hasRawHeader("Location")) { + // no redirect -> it's fine to continue + return false; + } + // there is a Location header, but it's not correct. we need to apply some workarounds... + QByteArray redirectBA = m_reply->rawHeader("Location"); + if (redirectBA.size() == 0) { + // empty, yet present redirect header? WTF? + return false; + } + QString redirectStr = QString::fromUtf8(redirectBA); + + if (redirectStr.startsWith("//")) { + /* + * IF the URL begins with //, we need to insert the URL scheme. + * See: https://bugreports.qt.io/browse/QTBUG-41061 + * See: http://tools.ietf.org/html/rfc3986#section-4.2 + */ + redirectStr = m_reply->url().scheme() + ":" + redirectStr; + } else if (redirectStr.startsWith("/")) { + /* + * IF the URL begins with /, we need to process it as a relative URL + */ + auto url = m_reply->url(); + url.setPath(redirectStr, QUrl::TolerantMode); + redirectStr = url.toString(); + } + + /* + * Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues. + * FIXME: report Qt bug for this + */ + redirect = QUrl(redirectStr, QUrl::TolerantMode); + if (!redirect.isValid()) { + qCWarning(logCat) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr; + downloadError(QNetworkReply::ProtocolFailure); + return false; + } + qCDebug(logCat) << getUid().toString() << "Fixed location header:" << redirect; + } else { + qCDebug(logCat) << getUid().toString() << "Location header:" << redirect; + } + + m_url = QUrl(redirect.toString()); + qCDebug(logCat) << getUid().toString() << "Following redirect to " << m_url.toString(); + startAction(m_network); + + return true; +} + +void NetRequest::downloadFinished() +{ + // handle HTTP redirection first + if (handleRedirect()) { + qCDebug(logCat) << getUid().toString() << "Request redirected:" << m_url.toString(); + return; + } + + // if the download failed before this point ... + if (m_state == State::Succeeded) // pretend to succeed so we continue processing :) + { + qCDebug(logCat) << getUid().toString() << "Request failed but we are allowed to proceed:" << m_url.toString(); + m_sink->abort(); + m_reply.reset(); + emit succeeded(); + emit finished(); + return; + } else if (m_state == State::Failed) { + qCDebug(logCat) << getUid().toString() << "Request failed in previous step:" << m_url.toString(); + m_sink->abort(); + m_reply.reset(); + emit failed(""); + emit finished(); + return; + } else if (m_state == State::AbortedByUser) { + qCDebug(logCat) << getUid().toString() << "Request aborted in previous step:" << m_url.toString(); + m_sink->abort(); + m_reply.reset(); + emit aborted(); + emit finished(); + return; + } + + // make sure we got all the remaining data, if any + auto data = m_reply->readAll(); + if (data.size()) { + qCDebug(logCat) << getUid().toString() << "Writing extra" << data.size() << "bytes"; + m_state = m_sink->write(data); + if (m_state != State::Succeeded) { + qCDebug(logCat) << getUid().toString() << "Request failed to write:" << m_url.toString(); + m_sink->abort(); + emit failed(""); + emit finished(); + return; + } + } + + // otherwise, finalize the whole graph + m_state = m_sink->finalize(*m_reply.get()); + if (m_state != State::Succeeded) { + qCDebug(logCat) << getUid().toString() << "Request failed to finalize:" << m_url.toString(); + m_sink->abort(); + m_reply.reset(); + emit failed(""); + emit finished(); + return; + } + + m_reply.reset(); + qCDebug(logCat) << getUid().toString() << "Request succeeded:" << m_url.toString(); + emit succeeded(); + emit finished(); +} + +void NetRequest::downloadReadyRead() +{ + if (m_state == State::Running) { + auto data = m_reply->readAll(); + m_state = m_sink->write(data); + if (m_state == State::Failed) { + qCCritical(logCat) << getUid().toString() << "Failed to process response chunk"; + } + // qDebug() << "Request" << m_url.toString() << "gained" << data.size() << "bytes"; + } else { + qCCritical(logCat) << getUid().toString() << "Cannot write download data! illegal status " << m_status; + } +} + +auto NetRequest::abort() -> bool +{ + m_state = State::AbortedByUser; + if (m_reply) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 + disconnect(m_reply.get(), &QNetworkReply::errorOccurred, nullptr, nullptr); +#else + disconnect(m_reply.get(), QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), nullptr, nullptr); +#endif + m_reply->abort(); + } + return true; +} + +} // namespace Net diff --git a/launcher/net/NetRequest.h b/launcher/net/NetRequest.h new file mode 100644 index 00000000..f91207ef --- /dev/null +++ b/launcher/net/NetRequest.h @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln <flowlnlnln@gmail.com> + * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * 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 <qloggingcategory.h> +#include <chrono> + +#include "NetAction.h" +#include "Sink.h" +#include "Validator.h" + +#include "QObjectPtr.h" +#include "net/Logging.h" + +namespace Net { +class NetRequest : public NetAction { + Q_OBJECT + protected: + explicit NetRequest() : NetAction(){} + + public: + using Ptr = shared_qobject_ptr<class NetRequest>; + enum class Option { NoOptions = 0, AcceptLocalFiles = 1, MakeEternal = 2 }; + Q_DECLARE_FLAGS(Options, Option) + + public: + ~NetRequest() override = default; + + void init() override{} + + public: + void addValidator(Validator* v); + auto abort() -> bool override; + auto canAbort() const -> bool override { return true; } + + private: + auto handleRedirect() -> bool; + virtual QNetworkReply* getReply(QNetworkRequest&) = 0; + + protected slots: + void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; + void downloadError(QNetworkReply::NetworkError error) override; + void sslErrors(const QList<QSslError>& errors) override; + void downloadFinished() override; + void downloadReadyRead() override; + + public slots: + void executeTask() override; + + protected: + std::unique_ptr<Sink> m_sink; + Options m_options; + + typedef const QLoggingCategory& (*logCatFunc)(); + logCatFunc logCat = taskUploadLogC; + + std::chrono::steady_clock m_clock; + std::chrono::time_point<std::chrono::steady_clock> m_last_progress_time; + qint64 m_last_progress_bytes; +}; +} // namespace Net + +Q_DECLARE_OPERATORS_FOR_FLAGS(Net::NetRequest::Options) diff --git a/launcher/net/RawHeaderProxy.h b/launcher/net/RawHeaderProxy.h new file mode 100644 index 00000000..09b3d4d0 --- /dev/null +++ b/launcher/net/RawHeaderProxy.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln <flowlnlnln@gmail.com> + * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#pragma once + +#include "net/HeaderProxy.h" + +namespace Net { + +class RawHeaderProxy : public HeaderProxy { + public: + RawHeaderProxy() : HeaderProxy() {} + virtual ~RawHeaderProxy() = default; + + public: + virtual QList<HeaderPair> headers(const QNetworkRequest&) const override { return m_headers; }; + + void addHeader(const HeaderPair& header) { m_headers.append(header); } + void addHeader(const QByteArray& headerName, const QByteArray& headerValue) { m_headers.append({ headerName, headerValue }); } + void addHeaders(const QList<HeaderPair>& headers) { m_headers.append(headers); } + + private: + QList<HeaderPair> m_headers; +}; + +} // namespace Net diff --git a/launcher/net/Upload.cpp b/launcher/net/Upload.cpp index 3f6f5829..726572e5 100644 --- a/launcher/net/Upload.cpp +++ b/launcher/net/Upload.cpp @@ -38,219 +38,16 @@ #include "Upload.h" +#include <memory> #include <utility> -#include "Application.h" -#include "BuildConfig.h" #include "ByteArraySink.h" -#include "net/Logging.h" - namespace Net { -bool Upload::abort() -{ - if (m_reply) { - m_reply->abort(); - } else { - m_state = State::AbortedByUser; - } - return true; -} - -void Upload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) -{ - setProgress(bytesReceived, bytesTotal); -} - -void Upload::downloadError(QNetworkReply::NetworkError error) -{ - if (error == QNetworkReply::OperationCanceledError) { - qCCritical(taskUploadLogC) << getUid().toString() << "Aborted " << m_url.toString(); - m_state = State::AbortedByUser; - } else { - // error happened during download. - qCCritical(taskUploadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error; - m_state = State::Failed; - } -} - -void Upload::sslErrors(const QList<QSslError>& errors) -{ - int i = 1; - for (const auto& error : errors) { - qCCritical(taskUploadLogC) << getUid().toString() << "Upload" << m_url.toString() << "SSL Error #" << i << " : " - << error.errorString(); - auto cert = error.certificate(); - qCCritical(taskUploadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -bool Upload::handleRedirect() -{ - QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl(); - if (!redirect.isValid()) { - if (!m_reply->hasRawHeader("Location")) { - // no redirect -> it's fine to continue - return false; - } - // there is a Location header, but it's not correct. we need to apply some workarounds... - QByteArray redirectBA = m_reply->rawHeader("Location"); - if (redirectBA.size() == 0) { - // empty, yet present redirect header? WTF? - return false; - } - QString redirectStr = QString::fromUtf8(redirectBA); - - if (redirectStr.startsWith("//")) { - /* - * IF the URL begins with //, we need to insert the URL scheme. - * See: https://bugreports.qt.io/browse/QTBUG-41061 - * See: http://tools.ietf.org/html/rfc3986#section-4.2 - */ - redirectStr = m_reply->url().scheme() + ":" + redirectStr; - } else if (redirectStr.startsWith("/")) { - /* - * IF the URL begins with /, we need to process it as a relative URL - */ - auto url = m_reply->url(); - url.setPath(redirectStr, QUrl::TolerantMode); - redirectStr = url.toString(); - } - - /* - * Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues. - * FIXME: report Qt bug for this - */ - redirect = QUrl(redirectStr, QUrl::TolerantMode); - if (!redirect.isValid()) { - qCWarning(taskUploadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr; - downloadError(QNetworkReply::ProtocolFailure); - return false; - } - qCDebug(taskUploadLogC) << getUid().toString() << "Fixed location header:" << redirect; - } else { - qCDebug(taskUploadLogC) << getUid().toString() << "Location header:" << redirect; - } - - m_url = QUrl(redirect.toString()); - qCDebug(taskUploadLogC) << getUid().toString() << "Following redirect to " << m_url.toString(); - startAction(m_network); - return true; -} - -void Upload::downloadFinished() -{ - // handle HTTP redirection first - // very unlikely for post requests, still can happen - if (handleRedirect()) { - qCDebug(taskUploadLogC) << getUid().toString() << "Upload redirected:" << m_url.toString(); - return; - } - - // if the download failed before this point ... - if (m_state == State::Succeeded) { - qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed but we are allowed to proceed:" << m_url.toString(); - m_sink->abort(); - m_reply.reset(); - emit succeeded(); - return; - } else if (m_state == State::Failed) { - qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed in previous step:" << m_url.toString(); - m_sink->abort(); - m_reply.reset(); - emit failed(""); - return; - } else if (m_state == State::AbortedByUser) { - qCDebug(taskUploadLogC) << getUid().toString() << "Upload aborted in previous step:" << m_url.toString(); - m_sink->abort(); - m_reply.reset(); - emit aborted(); - return; - } - - // make sure we got all the remaining data, if any - auto data = m_reply->readAll(); - if (data.size()) { - qCDebug(taskUploadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes"; - m_state = m_sink->write(data); - } - - // otherwise, finalize the whole graph - m_state = m_sink->finalize(*m_reply.get()); - if (m_state != State::Succeeded) { - qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed to finalize:" << m_url.toString(); - m_sink->abort(); - m_reply.reset(); - emit failed(""); - return; - } - m_reply.reset(); - qCDebug(taskUploadLogC) << getUid().toString() << "Upload succeeded:" << m_url.toString(); - emit succeeded(); -} - -void Upload::downloadReadyRead() -{ - if (m_state == State::Running) { - auto data = m_reply->readAll(); - m_state = m_sink->write(data); - } -} - -void Upload::executeTask() +QNetworkReply* Upload::getReply(QNetworkRequest& request) { - setStatus(tr("Uploading %1").arg(m_url.toString())); - - if (m_state == State::AbortedByUser) { - qCWarning(taskUploadLogC) << getUid().toString() << "Attempt to start an aborted Upload:" << m_url.toString(); - emit aborted(); - return; - } - QNetworkRequest request(m_url); - m_state = m_sink->init(request); - switch (m_state) { - case State::Succeeded: - emitSucceeded(); - qCDebug(taskUploadLogC) << getUid().toString() << "Upload cache hit " << m_url.toString(); - return; - case State::Running: - qCDebug(taskUploadLogC) << getUid().toString() << "Uploading " << m_url.toString(); - break; - case State::Inactive: - case State::Failed: - emitFailed(""); - return; - case State::AbortedByUser: - emitAborted(); - return; - } - - request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8()); - // TODO remove duplication - if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) { - request.setRawHeader("x-api-key", APPLICATION->getFlameAPIKey().toUtf8()); - } else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || - request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { - QString token = APPLICATION->getModrinthAPIToken(); - if (!token.isNull()) - request.setRawHeader("Authorization", token.toUtf8()); - } - - // TODO other types of post requests ? request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QNetworkReply* rep = m_network->post(request, m_post_data); - - m_reply.reset(rep); - connect(rep, &QNetworkReply::downloadProgress, this, &Upload::downloadProgress); - connect(rep, &QNetworkReply::finished, this, &Upload::downloadFinished); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &Upload::downloadError); -#else - connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &Upload::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &Upload::sslErrors); - connect(rep, &QNetworkReply::readyRead, this, &Upload::downloadReadyRead); + return m_network->post(request, m_post_data); } Upload::Ptr Upload::makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, QByteArray m_post_data) diff --git a/launcher/net/Upload.h b/launcher/net/Upload.h index 0b0c9497..f920e556 100644 --- a/launcher/net/Upload.h +++ b/launcher/net/Upload.h @@ -37,36 +37,21 @@ #pragma once -#include "NetAction.h" -#include "Sink.h" +#include "net/NetRequest.h" namespace Net { -class Upload : public NetAction { +class Upload : public NetRequest { Q_OBJECT - public: using Ptr = shared_qobject_ptr<Upload>; + explicit Upload() : NetRequest() { logCat = taskUploadLogC; }; static Upload::Ptr makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, QByteArray m_post_data); - auto abort() -> bool override; - auto canAbort() const -> bool override { return true; }; - - protected slots: - void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; - void downloadError(QNetworkReply::NetworkError error) override; - void sslErrors(const QList<QSslError>& errors) override; - void downloadFinished() override; - void downloadReadyRead() override; - public slots: - void executeTask() override; - - private: - std::unique_ptr<Sink> m_sink; + protected: + virtual QNetworkReply* getReply(QNetworkRequest&) override; QByteArray m_post_data; - - bool handleRedirect(); }; } // namespace Net diff --git a/launcher/net/Validator.h b/launcher/net/Validator.h index 6b3d4635..c8ce7922 100644 --- a/launcher/net/Validator.h +++ b/launcher/net/Validator.h @@ -40,8 +40,9 @@ namespace Net { class Validator { public: /* con/des */ - Validator() {}; - virtual ~Validator() {}; + Validator() {} + + virtual ~Validator() {} public: /* methods */ virtual bool init(QNetworkRequest & request) = 0; diff --git a/launcher/news/NewsEntry.cpp b/launcher/news/NewsEntry.cpp index cfe07e86..9726cab2 100644 --- a/launcher/news/NewsEntry.cpp +++ b/launcher/news/NewsEntry.cpp @@ -42,8 +42,8 @@ inline QString childValue(const QDomElement& element, const QString& childName, QDomNodeList nodes = element.elementsByTagName(childName); if (nodes.count() > 0) { - QDomElement element = nodes.at(0).toElement(); - return element.text(); + QDomElement elem = nodes.at(0).toElement(); + return elem.text(); } else { @@ -51,7 +51,7 @@ inline QString childValue(const QDomElement& element, const QString& childName, } } -bool NewsEntry::fromXmlElement(const QDomElement& element, NewsEntry* entry, QString* errorMsg) +bool NewsEntry::fromXmlElement(const QDomElement& element, NewsEntry* entry, [[maybe_unused]] QString* errorMsg) { QString title = childValue(element, "title", tr("Untitled")); QString content = childValue(element, "content", tr("No content.")); @@ -62,4 +62,3 @@ bool NewsEntry::fromXmlElement(const QDomElement& element, NewsEntry* entry, QSt entry->link = link; return true; } - diff --git a/launcher/pathmatcher/IPathMatcher.h b/launcher/pathmatcher/IPathMatcher.h index 192782d7..1457d3e5 100644 --- a/launcher/pathmatcher/IPathMatcher.h +++ b/launcher/pathmatcher/IPathMatcher.h @@ -8,6 +8,6 @@ public: typedef std::shared_ptr<IPathMatcher> Ptr; public: - virtual ~IPathMatcher(){}; + virtual ~IPathMatcher() {} virtual bool matches(const QString &string) const = 0; }; diff --git a/launcher/pathmatcher/RegexpMatcher.h b/launcher/pathmatcher/RegexpMatcher.h index 825d488c..6da651ef 100644 --- a/launcher/pathmatcher/RegexpMatcher.h +++ b/launcher/pathmatcher/RegexpMatcher.h @@ -4,7 +4,7 @@ class RegexpMatcher : public IPathMatcher { public: - virtual ~RegexpMatcher() {}; + virtual ~RegexpMatcher() {} RegexpMatcher(const QString ®exp) { m_regexp.setPattern(regexp); diff --git a/launcher/resources/multimc/128x128/instances/forge.png b/launcher/resources/multimc/128x128/instances/forge.png Binary files differnew file mode 100644 index 00000000..d8ff79a5 --- /dev/null +++ b/launcher/resources/multimc/128x128/instances/forge.png diff --git a/launcher/resources/multimc/128x128/instances/liteloader.png b/launcher/resources/multimc/128x128/instances/liteloader.png Binary files differnew file mode 100644 index 00000000..646217de --- /dev/null +++ b/launcher/resources/multimc/128x128/instances/liteloader.png diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc index 2c00f28f..8f079bb3 100644 --- a/launcher/resources/multimc/multimc.qrc +++ b/launcher/resources/multimc/multimc.qrc @@ -347,5 +347,10 @@ <file>scalable/export.svg</file> <file>scalable/launch.svg</file> <file>scalable/server.svg</file> + + <file>scalable/instances/quiltmc.svg</file> <!-- CC0 QuiltMC --> + <file>scalable/instances/fabricmc.svg</file> <!-- CC0 unascribed, https://github.com/FabricMC/community/blob/main/media/unascribed/README.md --> + <file>128x128/instances/forge.png</file> <!-- LGPL3 Forge Development LLC --> + <file>128x128/instances/liteloader.png</file> <!-- CC-BY-SA 4.0 LiteLoader --> </qresource> </RCC> diff --git a/launcher/resources/multimc/scalable/instances/fabricmc.svg b/launcher/resources/multimc/scalable/instances/fabricmc.svg new file mode 100644 index 00000000..7bfc7548 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/fabricmc.svg @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="128" + height="128" + version="1.1" + viewBox="0 0 33.867 33.867" + xml:space="preserve" + id="svg252" + sodipodi:docname="fabricmc.svg" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"><defs + id="defs256" /><sodipodi:namedview + id="namedview254" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" + showgrid="false" + inkscape:zoom="2.8284271" + inkscape:cx="39.421203" + inkscape:cy="132.22897" + inkscape:window-width="2560" + inkscape:window-height="1386" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="g2166" /><g + id="g2166"><path + style="fill:#38342a;fill-opacity:1;stroke-width:0;stroke-linejoin:round" + d="m 16.9336,2.1165994 c 0.705567,0 1.411134,0 2.116701,0 0,0.7055667 0,1.4111333 0,2.1167 0.705566,0 1.411132,0 2.116698,0 0,0.7055665 0,1.4111331 0,2.1166996 0.705567,0 1.411134,0 2.116701,0 0,0.7055667 0,1.4111333 0,2.1167 0.705567,0 1.411133,0 2.1167,0 0,0.705567 0,1.411134 0,2.116701 0.705567,0 1.411133,0 2.1167,0 0,0.705567 0,1.411133 0,2.1167 0.705567,0 1.411133,0 2.1167,0 0,1.4111 0,2.8222 0,4.2333 -0.7056,-3.3e-5 -1.4112,-6.7e-5 -2.1168,-10e-5 0,0.705567 0,1.411133 0,2.1167 -1.4111,0 -2.8222,0 -4.2333,0 0,0.705567 0,1.411133 0,2.1167 -0.705567,0 -1.411134,0 -2.116701,0 0,0.705567 0,1.411133 0,2.1167 -0.705566,0 -1.411132,0 -2.116698,0 0,0.705567 0,1.411134 0,2.116701 -0.705567,0 -1.411134,0 -2.116701,0 -3.4e-5,1.411133 -6.7e-5,2.822266 -1.01e-4,4.233399 -0.705567,0 -1.411133,0 -2.1167,0 0,0.705567 0,1.411134 0,2.116701 -1.4111,0 -2.822199,0 -4.233299,0 3.4e-5,-0.705567 6.7e-5,-1.411134 1.01e-4,-2.116701 -0.705567,0 -1.411134,0 -2.116701,0 0,-0.705567 0,-1.411134 0,-2.116701 -0.705567,0 -1.411134,0 -2.116701,0 0,-0.705566 0,-1.411132 0,-2.116698 -0.705567,0 -1.411134,0 -2.116701,0 0,-0.705567 0,-1.411134 0,-2.116701 -0.705566,0 -1.411132,0 -2.116698,0 0,-1.4111 0,-2.8222 0,-4.2333 0.705566,0 1.411132,0 2.116698,0 0,-0.705567 0,-1.411133 0,-2.1167 0.705567,0 1.411134,0 2.116701,0 0,-0.705567 0,-1.411133 0,-2.1167 0.705567,0 1.411134,0 2.116701,0 0,-0.705567 0,-1.411133 0,-2.1167 0.705567,0 1.411134,0 2.116701,0 0,-0.705567 0,-1.411133 0,-2.1167 0.705566,0 1.411132,0 2.116698,0 0,-0.705567 0,-1.411134 0,-2.116701 0.705567,0 1.411134,0 2.116701,0 -3.4e-5,-1.4110999 -6.7e-5,-2.8221997 -1.01e-4,-4.2332996 0.705567,0 1.411134,0 2.116701,0 0,-0.7055667 0,-1.4111333 0,-2.1167 z" + id="path1587-0" /><rect + x="25.400299" + y="14.8166" + width="2.1166999" + height="2.1166999" + fill="#807a6d" + id="rect246-7" + style="stroke-width:0" /><path + id="path1670" + style="fill:#dbd0b4;fill-opacity:1;stroke-width:0;stroke-linejoin:round" + d="m 4.233501,21.166701 2.1167,-2.117 6.09e-4,-2.1165 2.1161,-10e-5 6.09e-4,-2.1166 2.116099,-10e-5 6.09e-4,-2.1166 h 2.115899 l 2.1168,-2.117 6.72e-4,-2.1164 2.1159,-10e-5 v 2.1167 l 2.1171,10e-5 -3.97e-4,2.1166 2.1171,1e-4 v 2.1167 l 2.1167,2.11665 -11.642055,11.642008" + sodipodi:nodetypes="cccccccccccccccccc" /><path + style="fill:#dbd0b4;fill-opacity:1;stroke-width:0;stroke-linejoin:round" + d="m 16.9336,4.2332985 c 0.705333,-6.65e-5 1.410667,-1.329e-4 2.116,-1.994e-4 0,0.7055666 0,1.4111333 0,2.1166999 0.7058,6.67e-5 1.411599,1.333e-4 2.117399,2e-4 -2.29e-4,0.7055 -4.59e-4,1.411 -6.88e-4,2.1165 0.7058,6.63e-5 1.4116,1.327e-4 2.1174,1.99e-4 -2.29e-4,0.7055003 -4.59e-4,1.4110007 -6.88e-4,2.116501 0.705567,0 1.411133,0 2.1167,0 -0.0492,0.747877 0.09844,1.542199 -0.07194,2.26148 -0.32867,0.65734 -0.65734,1.31468 -0.98601,1.97202 -0.429296,-0.748959 -0.696496,-1.614758 -1.230307,-2.288382 -2.059217,-2.059636 -4.118734,-4.1189729 -6.178143,-6.178418 9.3e-5,-0.7055335 1.87e-4,-1.411067 2.8e-4,-2.1166005 z" + id="path2115" /><path + d="m 12.700401,25.400025 h 2.1167 v 2.1167 h -2.1167 z m 2.1167,-4.2333 v 4.2333 h 2.1167 v -2.1167 h 2.1167 v -2.1167 c -1.411133,-1.9e-5 -2.822267,1.4e-5 -4.2334,10e-5 z m 6.35,-6.35 v 2.1167 h -2.1167 v 4.2333 h 2.1167 v -2.1167 h 2.1167 v -4.2333 z m 2.1167,-2.1167 h 2.1167 v 2.1167 h -2.1167 z m -2.1167,-2.1167 h 2.1167 v 2.1167 h -2.1167 z m -2.1167,-2.1167 h 2.1167 v 2.1167 h -2.1167 z m -2.1167,-2.1167 h 2.1167 v 2.1167 h -2.1167 z" + fill="#bcb29c" + id="path238-2" + style="stroke-width:0" + sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccccccc" /><path + d="m 10.583601,27.516801 c 0.705567,0 1.411133,0 2.1167,0 0,0.705567 0,1.411133 0,2.1167 -0.705567,0 -1.411133,0 -2.1167,0 0,-0.705567 0,-1.411133 0,-2.1167 z m -2.1167,-2.1167 c 0.7055667,0 1.4111333,0 2.1167,0 0,0.705567 0,1.411133 0,2.1167 -0.7055667,0 -1.4111333,0 -2.1167,0 0,-0.705567 0,-1.411133 0,-2.1167 z m -2.1167,-2.1167 c 0.7055667,0 1.4111333,0 2.1167,0 0,0.705567 0,1.411133 0,2.1167 -0.7055667,0 -1.4111333,0 -2.1167,0 0,-0.705567 0,-1.411133 0,-2.1167 z m -2.1167,-2.1167 c 0.7055667,0 1.4111333,0 2.1167,0 0,0.705567 0,1.411133 0,2.1167 -0.7055667,0 -1.4111333,0 -2.1167,0 0,-0.705567 0,-1.411133 0,-2.1167 z" + fill="#9a927e" + id="path240-6" + style="stroke-width:0" /><path + d="m 10.583701,25.400025 c 0.705567,0 1.411133,0 2.1167,0 0,0.705567 0,1.411133 0,2.1167 -0.705567,0 -1.411133,0 -2.1167,0 0,-0.705567 0,-1.411133 0,-2.1167 z m -2.1167,-2.1167 c 0.7055667,0 1.4111333,0 2.1167,0 0,0.705567 0,1.411133 0,2.1167 -0.7055667,0 -1.4111333,0 -2.1167,0 0,-0.705567 0,-1.411133 0,-2.1167 z m -2.1167,-2.1167 c 0.7055667,0 1.4111333,0 2.1167,0 0,0.705567 0,1.411133 0,2.1167 -0.7055667,0 -1.4111333,0 -2.1167,0 0,-0.705567 0,-1.411133 0,-2.1167 z m -2.1167,-2.1167 c 0.7055667,0 1.4111333,0 2.1167,0 0,0.705567 0,1.411133 0,2.1167 -0.7055667,0 -1.4111333,0 -2.1167,0 0,-0.705567 0,-1.411133 0,-2.1167 z" + fill="#aea694" + id="path244-1" + style="stroke-width:0" /><path + d="m 10.583751,16.933325 h 2.1167 v 2.1167 h -2.1167 z m 2.1167,2.1167 v 2.1167 h 4.2333 v -2.1167 z m 4.2333,-4.2333 v 2.1167 h 4.2333 v -2.1167 z m -2.1167,-2.1167 h 2.1167 v 2.1167 h -2.1167 z m -2.1167,-2.1167 h 2.1167 v 2.1167 h -2.1167 z" + fill="#c6bca5" + id="path248-8" + style="stroke-width:0" + sodipodi:nodetypes="ccccccccccccccccccccccccc" /></g></svg> diff --git a/launcher/resources/multimc/scalable/instances/quiltmc.svg b/launcher/resources/multimc/scalable/instances/quiltmc.svg new file mode 100644 index 00000000..a7aaca53 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/quiltmc.svg @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + id="Layer_1" + data-name="Layer 1" + viewBox="0 0 23.999999 23.999999" + version="1.1" + sodipodi:docname="quiltmc.svg" + width="24" + height="24" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview27" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" + showgrid="false" + inkscape:zoom="10.390684" + inkscape:cx="24.685575" + inkscape:cy="9.5277659" + inkscape:window-width="1499" + inkscape:window-height="749" + inkscape:window-x="100" + inkscape:window-y="118" + inkscape:window-maximized="0" + inkscape:current-layer="Layer_1" /> + <defs + id="defs4"> + <style + id="style2">.cls-1{fill:#1b112b;}.cls-2{fill:#9722ff;}.cls-3{fill:#dc29dd;}.cls-4{fill:#27a2fd;}.cls-5{fill:#34f;}</style> + </defs> + <rect + class="cls-1" + width="24" + height="24" + rx="5.9670944" + id="rect6" + x="0" + y="0" + style="stroke-width:0.0262717" /> + <path + class="cls-2" + d="M 8.53568,6.3874419 A 0.27217496,0.27217496 0 0 0 8.8075922,6.6593542 H 9.2699744 V 7.6928834 H 8.8075922 a 0.27191225,0.27191225 0 0 0 0,0.5438245 H 9.2699744 V 9.0293255 A 0.24091162,0.24091162 0 0 1 9.0290628,9.2702371 H 8.2364452 V 8.807855 a 0.27191225,0.27191225 0 0 0 -0.5438245,0 V 9.2702371 H 6.6590914 V 8.807855 a 0.27191224,0.27191224 0 0 0 -0.5438244,0 V 9.2702371 H 5.3226493 A 0.24117434,0.24117434 0 0 1 5.0817377,9.0293255 V 5.322912 A 0.24091162,0.24091162 0 0 1 5.3226493,5.0820004 H 9.0290628 A 0.2406489,0.2406489 0 0 1 9.2699744,5.322912 V 6.115267 H 8.8075922 A 0.27217496,0.27217496 0 0 0 8.53568,6.3874419 Z" + id="path8" + style="stroke-width:0.0262717" /> + <path + class="cls-3" + d="m 13.267216,6.3874419 a 0.27191224,0.27191224 0 0 0 0.271912,0.2719123 h 0.463696 v 1.0335292 h -0.462382 a 0.27191225,0.27191225 0 0 0 0,0.5438245 h 0.462382 V 9.0293255 A 0.24117434,0.24117434 0 0 1 13.761912,9.2702371 H 10.054973 A 0.24091162,0.24091162 0 0 1 9.8140616,9.0293255 V 8.2367079 h 0.4621194 a 0.27191225,0.27191225 0 1 0 0,-0.5438245 H 9.8140616 V 6.6593542 h 0.4621194 a 0.27217496,0.27217496 0 1 0 0,-0.5440872 H 9.8140616 V 5.322912 A 0.2406489,0.2406489 0 0 1 10.054973,5.0820004 h 3.706414 a 0.24091162,0.24091162 0 0 1 0.240911,0.2409116 v 0.792355 h -0.462382 a 0.27191224,0.27191224 0 0 0 -0.2727,0.2721749 z" + id="path10" + style="stroke-width:0.0262717" /> + <path + class="cls-4" + d="M 18.73436,5.322912 V 9.0293255 A 0.24091162,0.24091162 0 0 1 18.493448,9.2702371 H 17.70083 V 8.807855 a 0.27191224,0.27191224 0 0 0 -0.543824,0 V 9.2702371 H 16.123477 V 8.807855 a 0.27191251,0.27191251 0 0 0 -0.543825,0 V 9.2702371 H 14.787034 A 0.24117434,0.24117434 0 0 1 14.546123,9.0293255 V 8.2367079 h 0.462382 a 0.27191225,0.27191225 0 1 0 0,-0.5438245 H 14.546123 V 6.6593542 h 0.462382 a 0.27217496,0.27217496 0 0 0 0,-0.5440872 H 14.546123 V 5.322912 a 0.24091162,0.24091162 0 0 1 0.240911,-0.2409116 h 3.706414 A 0.2406489,0.2406489 0 0 1 18.73436,5.322912 Z" + id="path12" + style="stroke-width:0.0262717" /> + <path + class="cls-3" + d="m 9.2699744,10.054973 v 3.706414 A 0.2406489,0.2406489 0 0 1 9.0290628,14.002298 H 8.2364452 v -0.462382 a 0.27191225,0.27191225 0 1 0 -0.5438245,0 v 0.462908 H 6.6590914 v -0.462382 a 0.27191224,0.27191224 0 0 0 -0.5438244,0 v 0.462382 H 5.3226493 A 0.24091162,0.24091162 0 0 1 5.0817377,13.761912 V 10.054973 A 0.24117434,0.24117434 0 0 1 5.3226493,9.8140616 H 6.115267 v 0.4623824 a 0.27191224,0.27191224 0 0 0 0.5438244,0 V 9.8140616 h 1.0335293 v 0.4623824 a 0.27191225,0.27191225 0 1 0 0.5438245,0 V 9.8140616 h 0.7926176 a 0.24091162,0.24091162 0 0 1 0.2409116,0.2409114 z" + id="path14" + style="stroke-width:0.0262717" /> + <path + class="cls-4" + d="m 13.267216,11.119503 a 0.27191224,0.27191224 0 0 0 0.271912,0.271912 h 0.463696 v 1.03353 h -0.462382 a 0.27191224,0.27191224 0 0 0 0,0.543824 h 0.462382 v 0.792618 a 0.24091162,0.24091162 0 0 1 -0.240912,0.240911 h -0.793143 v -0.462382 a 0.27191224,0.27191224 0 1 0 -0.543824,0 v 0.462908 h -1.03353 v -0.462382 a 0.27217496,0.27217496 0 0 0 -0.544087,0 v 0.462382 H 10.054973 A 0.2406489,0.2406489 0 0 1 9.8140616,13.761912 V 10.054973 A 0.24091162,0.24091162 0 0 1 10.054973,9.8140616 h 3.706414 a 0.24117434,0.24117434 0 0 1 0.240911,0.2409114 v 0.792618 h -0.462382 a 0.27191224,0.27191224 0 0 0 -0.2727,0.271912 z" + id="path16" + style="stroke-width:0.0262717" /> + <path + class="cls-5" + d="m 18.73436,10.054973 v 3.706414 a 0.2406489,0.2406489 0 0 1 -0.240912,0.240911 h -3.706414 a 0.24091162,0.24091162 0 0 1 -0.240911,-0.240911 v -0.792618 h 0.462382 a 0.27191224,0.27191224 0 1 0 0,-0.543824 h -0.462382 v -1.03353 h 0.462382 a 0.27191224,0.27191224 0 0 0 0,-0.543824 h -0.462382 v -0.792618 a 0.24117434,0.24117434 0 0 1 0.240911,-0.2409114 h 0.792093 v 0.4623824 a 0.27191224,0.27191224 0 0 0 0.543824,0 V 9.8140616 h 1.033529 v 0.4623824 a 0.27191251,0.27191251 0 0 0 0.543825,0 V 9.8140616 h 0.792617 a 0.24091162,0.24091162 0 0 1 0.241438,0.2409114 z" + id="path18" + style="stroke-width:0.0262717" /> + <path + class="cls-2" + d="m 8.53568,15.851827 a 0.27217496,0.27217496 0 0 0 0.2719122,0.271912 h 0.4623822 v 1.033267 H 8.8075922 a 0.27217496,0.27217496 0 0 0 0,0.544087 h 0.4623822 v 0.792618 A 0.24091162,0.24091162 0 0 1 9.0290628,18.734622 H 5.3226493 A 0.24117434,0.24117434 0 0 1 5.0817377,18.493711 V 14.787297 A 0.24091162,0.24091162 0 0 1 5.3226493,14.546386 H 6.115267 v 0.462119 a 0.27191224,0.27191224 0 1 0 0.5438244,0 v -0.462119 h 1.0335293 v 0.462119 a 0.27191225,0.27191225 0 1 0 0.5438245,0 v -0.462119 h 0.7926176 a 0.2406489,0.2406489 0 0 1 0.2409116,0.240911 v 0.79183 H 8.8075922 a 0.27217496,0.27217496 0 0 0 -0.2719122,0.2727 z" + id="path20" + style="stroke-width:0.0262717" /> + <path + class="cls-5" + d="m 14.002824,14.787297 v 3.706414 a 0.24117434,0.24117434 0 0 1 -0.240912,0.240911 H 10.054973 A 0.24091162,0.24091162 0 0 1 9.8140616,18.493711 v -0.792618 h 0.4621194 a 0.27217496,0.27217496 0 1 0 0,-0.544087 H 9.8140616 v -1.033267 h 0.4621194 a 0.27217496,0.27217496 0 1 0 0,-0.544087 H 9.8140616 v -0.792355 a 0.2406489,0.2406489 0 0 1 0.2409114,-0.240911 h 0.792355 v 0.462119 a 0.27217496,0.27217496 0 1 0 0.544087,0 v -0.462119 h 1.03353 v 0.462119 a 0.27191224,0.27191224 0 1 0 0.543824,0 v -0.462119 h 0.792618 a 0.24091162,0.24091162 0 0 1 0.241437,0.240911 z" + id="path22" + style="stroke-width:0.0262717" /> + <rect + class="cls-2" + x="-2.0941913" + y="22.665482" + width="4.1882367" + height="4.1882367" + rx="0.24091162" + transform="rotate(-45)" + id="rect24" + style="stroke-width:0.0262717" /> +</svg> diff --git a/launcher/screenshots/ImgurAlbumCreation.cpp b/launcher/screenshots/ImgurAlbumCreation.cpp index ab425f1a..78bf111e 100644 --- a/launcher/screenshots/ImgurAlbumCreation.cpp +++ b/launcher/screenshots/ImgurAlbumCreation.cpp @@ -82,7 +82,7 @@ void ImgurAlbumCreation::executeTask() connect(rep, &QNetworkReply::sslErrors, this, &ImgurAlbumCreation::sslErrors); } -void ImgurAlbumCreation::downloadError(QNetworkReply::NetworkError error) +void ImgurAlbumCreation::downloadError([[maybe_unused]] QNetworkReply::NetworkError error) { qDebug() << m_reply->errorString(); m_state = State::Failed; diff --git a/launcher/screenshots/ImgurAlbumCreation.h b/launcher/screenshots/ImgurAlbumCreation.h index 0228b6e4..a2b70d8b 100644 --- a/launcher/screenshots/ImgurAlbumCreation.h +++ b/launcher/screenshots/ImgurAlbumCreation.h @@ -57,6 +57,8 @@ public: return m_id; } + void init() override {}; + protected slots: void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; diff --git a/launcher/screenshots/ImgurUpload.cpp b/launcher/screenshots/ImgurUpload.cpp index a50f9afa..4f758767 100644 --- a/launcher/screenshots/ImgurUpload.cpp +++ b/launcher/screenshots/ImgurUpload.cpp @@ -97,7 +97,7 @@ void ImgurUpload::executeTask() connect(rep, &QNetworkReply::sslErrors, this, &ImgurUpload::sslErrors); } -void ImgurUpload::downloadError(QNetworkReply::NetworkError error) +void ImgurUpload::downloadError([[maybe_unused]] QNetworkReply::NetworkError error) { qCritical() << "ImgurUpload failed with error" << m_reply->errorString() << "Server reply:\n" << m_reply->readAll(); if(finished) diff --git a/launcher/screenshots/ImgurUpload.h b/launcher/screenshots/ImgurUpload.h index 404dc876..e8a6d8d7 100644 --- a/launcher/screenshots/ImgurUpload.h +++ b/launcher/screenshots/ImgurUpload.h @@ -46,6 +46,7 @@ public: static Ptr make(ScreenShot::Ptr shot) { return Ptr(new ImgurUpload(shot)); } + void init() override {}; protected slots: diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp index 9aada5e6..0f835369 100644 --- a/launcher/tasks/ConcurrentTask.cpp +++ b/launcher/tasks/ConcurrentTask.cpp @@ -171,7 +171,7 @@ void ConcurrentTask::subTaskSucceeded(Task::Ptr task) startNext(); } -void ConcurrentTask::subTaskFailed(Task::Ptr task, const QString& msg) +void ConcurrentTask::subTaskFailed(Task::Ptr task, [[maybe_unused]] const QString& msg) { m_done.insert(task.get(), task); m_failed.insert(task.get(), task); diff --git a/launcher/tasks/ConcurrentTask.h b/launcher/tasks/ConcurrentTask.h index 6325fc9e..481ebf85 100644 --- a/launcher/tasks/ConcurrentTask.h +++ b/launcher/tasks/ConcurrentTask.h @@ -53,7 +53,7 @@ class ConcurrentTask : public Task { bool canAbort() const override { return true; } - inline auto isMultiStep() const -> bool override { return totalSize() > 1; }; + inline auto isMultiStep() const -> bool override { return totalSize() > 1; } auto getStepProgress() const -> TaskStepProgressList override; void addTask(Task::Ptr task); @@ -80,7 +80,7 @@ class ConcurrentTask : public Task { protected: // NOTE: This is not thread-safe. - [[nodiscard]] unsigned int totalSize() const { return m_queue.size() + m_doing.size() + m_done.size(); } + [[nodiscard]] unsigned int totalSize() const { return static_cast<unsigned int>(m_queue.size() + m_doing.size() + m_done.size()); } enum class Operation { ADDED, REMOVED, CHANGED }; void updateStepProgress(TaskStepProgress const& changed_progress, Operation); diff --git a/launcher/tasks/Task.h b/launcher/tasks/Task.h index 57177697..25c7f8be 100644 --- a/launcher/tasks/Task.h +++ b/launcher/tasks/Task.h @@ -64,19 +64,20 @@ struct TaskStepProgress { QString status = ""; QString details = ""; TaskStepState state = TaskStepState::Waiting; + TaskStepProgress() { this->uid = QUuid::createUuid(); } - TaskStepProgress(QUuid uid) { - this->uid = uid; - } + + TaskStepProgress(QUuid uid_): uid(uid_) {} + bool isDone() const { return (state == TaskStepState::Failed) || (state == TaskStepState::Succeeded); } - void update(qint64 current, qint64 total) { + void update(qint64 new_current, qint64 new_total) { this->old_current = this->current; this->old_total = this->total; - this->current = current; - this->total = total; + this->current = new_current; + this->total = new_total; this->state = TaskStepState::Running; } }; @@ -155,7 +156,7 @@ class Task : public QObject, public QRunnable { void run() override { start(); } virtual void start(); - virtual bool abort() { if(canAbort()) emitAborted(); return canAbort(); }; + virtual bool abort() { if(canAbort()) emitAborted(); return canAbort(); } void setAbortable(bool can_abort) { m_can_abort = can_abort; emit abortStatusChanged(can_abort); } diff --git a/launcher/tools/JProfiler.cpp b/launcher/tools/JProfiler.cpp index 15c0cab6..f08ee73d 100644 --- a/launcher/tools/JProfiler.cpp +++ b/launcher/tools/JProfiler.cpp @@ -34,7 +34,7 @@ void JProfiler::profilerStarted() emit readyToLaunch(tr("Listening on port: %1").arg(listeningPort)); } -void JProfiler::profilerFinished(int exit, QProcess::ExitStatus status) +void JProfiler::profilerFinished([[maybe_unused]] int exit, QProcess::ExitStatus status) { if (status == QProcess::CrashExit) { diff --git a/launcher/tools/JVisualVM.cpp b/launcher/tools/JVisualVM.cpp index 28ffb9cd..2734f5ab 100644 --- a/launcher/tools/JVisualVM.cpp +++ b/launcher/tools/JVisualVM.cpp @@ -32,7 +32,7 @@ void JVisualVM::profilerStarted() emit readyToLaunch(tr("JVisualVM started")); } -void JVisualVM::profilerFinished(int exit, QProcess::ExitStatus status) +void JVisualVM::profilerFinished([[maybe_unused]] int exit, QProcess::ExitStatus status) { if (status == QProcess::CrashExit) { diff --git a/launcher/translations/POTranslator.cpp b/launcher/translations/POTranslator.cpp index c77ae45d..910a7f2c 100644 --- a/launcher/translations/POTranslator.cpp +++ b/launcher/translations/POTranslator.cpp @@ -334,7 +334,7 @@ POTranslator::~POTranslator() delete d; } -QString POTranslator::translate(const char* context, const char* sourceText, const char* disambiguation, int n) const +QString POTranslator::translate(const char* context, const char* sourceText, const char* disambiguation, [[maybe_unused]] int n) const { if(disambiguation) { diff --git a/launcher/translations/TranslationsModel.cpp b/launcher/translations/TranslationsModel.cpp index 2763cca2..4c633e15 100644 --- a/launcher/translations/TranslationsModel.cpp +++ b/launcher/translations/TranslationsModel.cpp @@ -268,14 +268,11 @@ void readIndex(const QString & path, QMap<QString, Language>& languages) try { data = FS::read(path); - } - catch (const Exception &e) - { + } catch ([[maybe_unused]] const Exception& e) { qCritical() << "Translations Download Failed: index file not readable"; return; } - int index = 1; try { auto toplevel_doc = Json::requireDocument(data); @@ -308,11 +305,8 @@ void readIndex(const QString & path, QMap<QString, Language>& languages) lang.file_size = Json::requireInteger(langObj, "size"); languages.insert(lang.key, lang); - index++; } - } - catch (Json::JsonException & e) - { + } catch ([[maybe_unused]] Json::JsonException& e) { qCritical() << "Translations Download Failed: index file could not be parsed as json"; } } @@ -502,12 +496,12 @@ QVariant TranslationsModel::headerData(int section, Qt::Orientation orientation, return QAbstractListModel::headerData(section, orientation, role); } -int TranslationsModel::rowCount(const QModelIndex& parent) const +int TranslationsModel::rowCount([[maybe_unused]] const QModelIndex& parent) const { return d->m_languages.size(); } -int TranslationsModel::columnCount(const QModelIndex& parent) const +int TranslationsModel::columnCount([[maybe_unused]] const QModelIndex& parent) const { return 2; } diff --git a/launcher/ui/InstanceWindow.cpp b/launcher/ui/InstanceWindow.cpp index c62b370f..d784a40f 100644 --- a/launcher/ui/InstanceWindow.cpp +++ b/launcher/ui/InstanceWindow.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <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 @@ -263,6 +264,11 @@ bool InstanceWindow::selectPage(QString pageId) return m_container->selectPage(pageId); } +BasePage* InstanceWindow::selectedPage() const +{ + return m_container->selectedPage(); +} + void InstanceWindow::refreshContainer() { m_container->refreshContainer(); diff --git a/launcher/ui/InstanceWindow.h b/launcher/ui/InstanceWindow.h index 554c4c74..582cff13 100644 --- a/launcher/ui/InstanceWindow.h +++ b/launcher/ui/InstanceWindow.h @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <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 @@ -56,6 +57,7 @@ public: virtual ~InstanceWindow(); bool selectPage(QString pageId) override; + BasePage* selectedPage() const override; void refreshContainer() override; QString instanceId(); diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index e342e833..423cd48d 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -514,10 +514,11 @@ void MainWindow::showInstanceContextMenu(const QPoint& pos) QAction* actionCreateInstance = new QAction(tr("Create instance"), this); actionCreateInstance->setToolTip(ui->actionAddInstance->toolTip()); - if (!group.isNull()) { - QVariantMap data; - data["group"] = group; - actionCreateInstance->setData(data); + if(!group.isNull()) + { + QVariantMap instance_action_data; + instance_action_data["group"] = group; + actionCreateInstance->setData(instance_action_data); } connect(actionCreateInstance, SIGNAL(triggered(bool)), SLOT(on_actionAddInstance_triggered())); @@ -525,11 +526,12 @@ void MainWindow::showInstanceContextMenu(const QPoint& pos) actions.prepend(actionSep); actions.prepend(actionVoid); actions.append(actionCreateInstance); - if (!group.isNull()) { - QAction* actionDeleteGroup = new QAction(tr("Delete group '%1'").arg(group), this); - QVariantMap data; - data["group"] = group; - actionDeleteGroup->setData(data); + if(!group.isNull()) + { + QAction *actionDeleteGroup = new QAction(tr("Delete group '%1'").arg(group), this); + QVariantMap delete_group_action_data; + delete_group_action_data["group"] = group; + actionDeleteGroup->setData(delete_group_action_data); connect(actionDeleteGroup, SIGNAL(triggered(bool)), SLOT(deleteGroup())); actions.append(actionDeleteGroup); } @@ -744,10 +746,10 @@ void MainWindow::changeActiveAccount() if (sAction->data().type() != QVariant::Type::Int) return; - QVariant data = sAction->data(); + QVariant action_data = sAction->data(); bool valid = false; - int index = data.toInt(&valid); - if (!valid) { + int index = action_data.toInt(&valid); + if(!valid) { index = -1; } auto accounts = APPLICATION->accounts(); @@ -1060,10 +1062,11 @@ void MainWindow::on_actionChangeInstIcon_triggered() void MainWindow::iconUpdated(QString icon) { - if (icon == m_currentInstIcon) { - auto icon = APPLICATION->icons()->getIcon(m_currentInstIcon); - ui->actionChangeInstIcon->setIcon(icon); - changeIconButton->setIcon(icon); + if (icon == m_currentInstIcon) + { + auto new_icon = APPLICATION->icons()->getIcon(m_currentInstIcon); + ui->actionChangeInstIcon->setIcon(new_icon); + changeIconButton->setIcon(new_icon); } } @@ -1590,7 +1593,7 @@ void MainWindow::startTask(Task* task) task->start(); } -void MainWindow::instanceChanged(const QModelIndex& current, const QModelIndex& previous) +void MainWindow::instanceChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { if (!current.isValid()) { APPLICATION->settings()->set("SelectedInstance", QString()); @@ -1725,8 +1728,9 @@ void MainWindow::setInstanceActionsEnabled(bool enabled) ui->actionCreateInstanceShortcut->setEnabled(enabled); } -void MainWindow::refreshCurrentInstance(bool running) +void MainWindow::refreshCurrentInstance([[maybe_unused]] bool running) { auto current = view->selectionModel()->currentIndex(); instanceChanged(current, current); } + diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp index d75bb5fe..ffa1e31b 100644 --- a/launcher/ui/dialogs/CopyInstanceDialog.cpp +++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp @@ -220,7 +220,7 @@ void CopyInstanceDialog::on_iconButton_clicked() } } -void CopyInstanceDialog::on_instNameTextBox_textChanged(const QString& arg1) +void CopyInstanceDialog::on_instNameTextBox_textChanged([[maybe_unused]] const QString& arg1) { updateDialogState(); } diff --git a/launcher/ui/dialogs/ExportInstanceDialog.cpp b/launcher/ui/dialogs/ExportInstanceDialog.cpp index d6a503cc..703736d6 100644 --- a/launcher/ui/dialogs/ExportInstanceDialog.cpp +++ b/launcher/ui/dialogs/ExportInstanceDialog.cpp @@ -195,8 +195,8 @@ void ExportInstanceDialog::loadPackIgnore() if (!ignoreFile.open(QIODevice::ReadOnly)) { return; } - auto data = ignoreFile.readAll(); - auto string = QString::fromUtf8(data); + auto ignoreData = ignoreFile.readAll(); + auto string = QString::fromUtf8(ignoreData); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) proxyModel->setBlockedPaths(string.split('\n', Qt::SkipEmptyParts)); #else @@ -206,10 +206,10 @@ void ExportInstanceDialog::loadPackIgnore() void ExportInstanceDialog::savePackIgnore() { - auto data = proxyModel->blockedPaths().toStringList().join('\n').toUtf8(); + auto ignoreData = proxyModel->blockedPaths().toStringList().join('\n').toUtf8(); auto filename = ignoreFileName(); try { - FS::write(filename, data); + FS::write(filename, ignoreData); } catch (const Exception& e) { qWarning() << e.cause(); } diff --git a/launcher/ui/dialogs/InstallLoaderDialog.cpp b/launcher/ui/dialogs/InstallLoaderDialog.cpp new file mode 100644 index 00000000..840a328f --- /dev/null +++ b/launcher/ui/dialogs/InstallLoaderDialog.cpp @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 TheKodeToad <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 + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "InstallLoaderDialog.h" + +#include <QDialogButtonBox> +#include <QPushButton> +#include <QVBoxLayout> +#include "Application.h" +#include "BuildConfig.h" +#include "DesktopServices.h" +#include "meta/Index.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/PackProfile.h" +#include "ui/widgets/PageContainer.h" +#include "ui/widgets/VersionSelectWidget.h" + +class InstallLoaderPage : public VersionSelectWidget, public BasePage { + public: + InstallLoaderPage(const QString& id, + const QString& iconName, + const QString& name, + const Version& oldestVersion, + const std::shared_ptr<PackProfile> profile) + : VersionSelectWidget(nullptr), uid(id), iconName(iconName), name(name) + { + const QString minecraftVersion = profile->getComponentVersion("net.minecraft"); + setEmptyString(tr("No versions are currently available for Minecraft %1").arg(minecraftVersion)); + setExactIfPresentFilter(BaseVersionList::ParentVersionRole, minecraftVersion); + + if (oldestVersion != Version() && Version(minecraftVersion) < oldestVersion) + setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); + + if (const QString currentVersion = profile->getComponentVersion(id); !currentVersion.isNull()) + setCurrentVersion(currentVersion); + } + + QString id() const override { return uid; } + QString displayName() const override { return name; } + QIcon icon() const override { return APPLICATION->getThemedIcon(iconName); } + + void openedImpl() override + { + if (loaded) + return; + + const auto versions = APPLICATION->metadataIndex()->get(uid); + if (!versions) + return; + + initialize(versions.get()); + loaded = true; + } + + void setParentContainer(BasePageContainer* container) override + { + auto dialog = dynamic_cast<QDialog*>(dynamic_cast<PageContainer*>(container)->parent()); + connect(view(), &QAbstractItemView::doubleClicked, dialog, &QDialog::accept); + } + + private: + const QString uid; + const QString iconName; + const QString name; + bool loaded = false; +}; + +static InstallLoaderPage* pageCast(BasePage* page) +{ + auto result = dynamic_cast<InstallLoaderPage*>(page); + Q_ASSERT(result != nullptr); + return result; +} + +InstallLoaderDialog::InstallLoaderDialog(std::shared_ptr<PackProfile> profile, const QString& uid, QWidget* parent) + : QDialog(parent), profile(std::move(profile)), container(new PageContainer(this, QString(), this)), buttons(new QDialogButtonBox(this)) +{ + auto layout = new QVBoxLayout(this); + + container->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + layout->addWidget(container); + + auto buttonLayout = new QHBoxLayout(this); + + auto refreshButton = new QPushButton(tr("&Refresh"), this); + connect(refreshButton, &QPushButton::clicked, this, [this] { pageCast(container->selectedPage())->loadList(); }); + buttonLayout->addWidget(refreshButton); + + buttons->setOrientation(Qt::Horizontal); + buttons->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); + connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); + buttonLayout->addWidget(buttons); + + layout->addLayout(buttonLayout); + + setWindowTitle(dialogTitle()); + setWindowModality(Qt::WindowModal); + resize(520, 347); + + for (BasePage* page : container->getPages()) { + if (page->id() == uid) + container->selectPage(page->id()); + + connect(pageCast(page), &VersionSelectWidget::selectedVersionChanged, this, [this, page] { + if (page->id() == container->selectedPage()->id()) + validate(container->selectedPage()); + }); + } + connect(container, &PageContainer::selectedPageChanged, this, [this](BasePage* previous, BasePage* current) { validate(current); }); + pageCast(container->selectedPage())->selectSearch(); + validate(container->selectedPage()); +} + +QList<BasePage*> InstallLoaderDialog::getPages() +{ + return { // Forge + new InstallLoaderPage("net.minecraftforge", "forge", tr("Forge"), {}, profile), + // Fabric + new InstallLoaderPage("net.fabricmc.fabric-loader", "fabricmc", tr("Fabric"), Version("1.14"), profile), + // Quilt + new InstallLoaderPage("org.quiltmc.quilt-loader", "quiltmc", tr("Quilt"), Version("1.14"), profile), + // LiteLoader + new InstallLoaderPage("com.mumfrey.liteloader", "liteloader", tr("LiteLoader"), {}, profile) + }; +} + +QString InstallLoaderDialog::dialogTitle() +{ + return tr("Install Loader"); +} + +void InstallLoaderDialog::validate(BasePage* page) +{ + buttons->button(QDialogButtonBox::Ok)->setEnabled(pageCast(page)->selectedVersion() != nullptr); +} + +void InstallLoaderDialog::done(int result) +{ + if (result == Accepted) { + auto* page = pageCast(container->selectedPage()); + if (page->selectedVersion()) { + profile->setComponentVersion(page->id(), page->selectedVersion()->descriptor()); + profile->resolve(Net::Mode::Online); + } + } + + QDialog::done(result); +} diff --git a/launcher/ui/dialogs/InstallLoaderDialog.h b/launcher/ui/dialogs/InstallLoaderDialog.h new file mode 100644 index 00000000..86cb3bdd --- /dev/null +++ b/launcher/ui/dialogs/InstallLoaderDialog.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 TheKodeToad <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 + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <QDialog> +#include "ui/pages/BasePageProvider.h" + +class MinecraftInstance; +class PageContainer; +class PackProfile; +class QDialogButtonBox; + +class InstallLoaderDialog final : public QDialog, protected BasePageProvider { + Q_OBJECT + + public: + explicit InstallLoaderDialog(std::shared_ptr<PackProfile> instance, const QString& uid = QString(), QWidget* parent = nullptr); + + QList<BasePage*> getPages() override; + QString dialogTitle() override; + + void validate(BasePage* page); + void done(int result) override; + + private: + std::shared_ptr<PackProfile> profile; + PageContainer* container; + QDialogButtonBox* buttons; +}; diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp index 76b139fc..3e6456bc 100644 --- a/launcher/ui/dialogs/NewInstanceDialog.cpp +++ b/launcher/ui/dialogs/NewInstanceDialog.cpp @@ -284,28 +284,27 @@ QString NewInstanceDialog::iconKey() const void NewInstanceDialog::on_iconButton_clicked() { - importIconNow(); //so the user can switch back + importIconNow(); // so the user can switch back IconPickerDialog dlg(this); dlg.execWithSelection(InstIconKey); - if (dlg.result() == QDialog::Accepted) - { + if (dlg.result() == QDialog::Accepted) { InstIconKey = dlg.selectedIconKey; ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey)); importIcon = false; } } -void NewInstanceDialog::on_instNameTextBox_textChanged(const QString &arg1) +void NewInstanceDialog::on_instNameTextBox_textChanged([[maybe_unused]] const QString& arg1) { updateDialogState(); } void NewInstanceDialog::importIconNow() { - if(importIcon) { + if (importIcon) { APPLICATION->icons()->installIcon(importIconPath, importIconName); - InstIconKey = importIconName; + InstIconKey = importIconName.mid(0, importIconName.lastIndexOf('.')); importIcon = false; } APPLICATION->settings()->set("NewInstanceGeometry", saveGeometry().toBase64()); diff --git a/launcher/ui/dialogs/ProfileSetupDialog.cpp b/launcher/ui/dialogs/ProfileSetupDialog.cpp index 64c0b924..758f7f23 100644 --- a/launcher/ui/dialogs/ProfileSetupDialog.cpp +++ b/launcher/ui/dialogs/ProfileSetupDialog.cpp @@ -167,14 +167,14 @@ void ProfileSetupDialog::checkName(const QString &name) { void ProfileSetupDialog::checkFinished( QNetworkReply::NetworkError error, - QByteArray data, - QList<QNetworkReply::RawHeaderPair> headers + QByteArray profileData, + [[maybe_unused]] QList<QNetworkReply::RawHeaderPair> headers ) { auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); requestor->deleteLater(); if(error == QNetworkReply::NoError) { - auto doc = QJsonDocument::fromJson(data); + auto doc = QJsonDocument::fromJson(profileData); auto root = doc.object(); auto statusValue = root.value("status").toString("INVALID"); if(statusValue == "AVAILABLE") { @@ -210,11 +210,11 @@ void ProfileSetupDialog::setupProfile(const QString &profileName) { request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toUtf8()); QString payloadTemplate("{\"profileName\":\"%1\"}"); - auto data = payloadTemplate.arg(profileName).toUtf8(); + auto profileData = payloadTemplate.arg(profileName).toUtf8(); AuthRequest *requestor = new AuthRequest(this); connect(requestor, &AuthRequest::finished, this, &ProfileSetupDialog::setupProfileFinished); - requestor->post(request, data); + requestor->post(request, profileData); isWorking = true; auto button = ui->buttonBox->button(QDialogButtonBox::Cancel); @@ -251,8 +251,8 @@ struct MojangError{ void ProfileSetupDialog::setupProfileFinished( QNetworkReply::NetworkError error, - QByteArray data, - QList<QNetworkReply::RawHeaderPair> headers + QByteArray errorData, + [[maybe_unused]] QList<QNetworkReply::RawHeaderPair> headers ) { auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); requestor->deleteLater(); @@ -266,7 +266,7 @@ void ProfileSetupDialog::setupProfileFinished( accept(); } else { - auto parsedError = MojangError::fromJSON(data); + auto parsedError = MojangError::fromJSON(errorData); ui->errorLabel->setVisible(true); ui->errorLabel->setText(tr("The server returned the following error:") + "\n\n" + parsedError.errorMessage); qDebug() << parsedError.rawError; diff --git a/launcher/ui/dialogs/ProgressDialog.cpp b/launcher/ui/dialogs/ProgressDialog.cpp index 4243e291..222ce973 100644 --- a/launcher/ui/dialogs/ProgressDialog.cpp +++ b/launcher/ui/dialogs/ProgressDialog.cpp @@ -87,7 +87,7 @@ void ProgressDialog::on_skipButton_clicked(bool checked) { Q_UNUSED(checked); if (ui->skipButton->isEnabled()) // prevent other triggers from aborting - task->abort(); + m_task->abort(); } ProgressDialog::~ProgressDialog() @@ -132,7 +132,7 @@ void ProgressDialog::updateSize(bool recenterParent) int ProgressDialog::execWithTask(Task* task) { - this->task = task; + this->m_task = task; if (!task) { qDebug() << "Programmer error: Progress dialog created with null task."; @@ -184,8 +184,8 @@ int ProgressDialog::execWithTask(std::unique_ptr<Task>& task) bool ProgressDialog::handleImmediateResult(QDialog::DialogCode& result) { - if (task->isFinished()) { - if (task->wasSuccessful()) { + if (m_task->isFinished()) { + if (m_task->wasSuccessful()) { result = QDialog::Accepted; } else { result = QDialog::Rejected; @@ -197,12 +197,12 @@ bool ProgressDialog::handleImmediateResult(QDialog::DialogCode& result) Task* ProgressDialog::getTask() { - return task; + return m_task; } void ProgressDialog::onTaskStarted() {} -void ProgressDialog::onTaskFailed(QString failure) +void ProgressDialog::onTaskFailed([[maybe_unused]] QString failure) { reject(); hide(); @@ -214,13 +214,14 @@ void ProgressDialog::onTaskSucceeded() hide(); } -void ProgressDialog::changeStatus(const QString& status) +void ProgressDialog::changeStatus([[maybe_unused]] const QString& status) { - ui->globalStatusLabel->setText(task->getStatus()); + ui->globalStatusLabel->setText(m_task->getStatus()); ui->globalStatusLabel->adjustSize(); - ui->globalStatusDetailsLabel->setText(task->getDetails()); + ui->globalStatusDetailsLabel->setText(m_task->getDetails()); ui->globalStatusDetailsLabel->adjustSize(); + updateSize(); } @@ -287,7 +288,7 @@ void ProgressDialog::keyPressEvent(QKeyEvent* e) void ProgressDialog::closeEvent(QCloseEvent* e) { - if (task && task->isRunning()) { + if (m_task && m_task->isRunning()) { e->ignore(); } else { QDialog::closeEvent(e); diff --git a/launcher/ui/dialogs/ProgressDialog.h b/launcher/ui/dialogs/ProgressDialog.h index f062be08..51700d87 100644 --- a/launcher/ui/dialogs/ProgressDialog.h +++ b/launcher/ui/dialogs/ProgressDialog.h @@ -98,7 +98,7 @@ private: private: Ui::ProgressDialog *ui; - Task *task; + Task *m_task; bool m_is_multi_step = false; QHash<QUuid, SubTaskProgressBar*> taskProgress; diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index b17eced3..4b82c0c5 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -2,7 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <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 @@ -209,15 +209,17 @@ bool ResourceDownloadDialog::selectPage(QString pageId) return m_container->selectPage(pageId); } -ResourcePage* ResourceDownloadDialog::getSelectedPage() +ResourcePage* ResourceDownloadDialog::selectedPage() { - return m_selectedPage; + ResourcePage* result = dynamic_cast<ResourcePage*>(m_container->selectedPage()); + Q_ASSERT(result != nullptr); + return result; } void ResourceDownloadDialog::addResource(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& ver) { removeResource(pack->name); - m_selectedPage->addResourceToPage(pack, ver, getBaseModel()); + selectedPage()->addResourceToPage(pack, ver, getBaseModel()); setButtonStatus(); } @@ -257,14 +259,8 @@ void ResourceDownloadDialog::selectedPageChanged(BasePage* previous, BasePage* s return; } - m_selectedPage = dynamic_cast<ResourcePage*>(selected); - if (!m_selectedPage) { - qCritical() << "Page '" << selected->displayName() << "' in ResourceDownloadDialog is not a ResourcePage!"; - return; - } - // Same effect as having a global search bar - m_selectedPage->setSearchTerm(prev_page->getSearchTerm()); + selectedPage()->setSearchTerm(prev_page->getSearchTerm()); } ModDownloadDialog::ModDownloadDialog(QWidget* parent, const std::shared_ptr<ModFolderModel>& mods, BaseInstance* instance) @@ -290,8 +286,6 @@ QList<BasePage*> ModDownloadDialog::getPages() if (APPLICATION->capabilities() & Application::SupportsFlame && FlameAPI::validateModLoaders(loaders)) pages.append(FlameModPage::create(this, *m_instance)); - m_selectedPage = dynamic_cast<ModPage*>(pages[0]); - return pages; } @@ -306,7 +300,7 @@ GetModDependenciesTask::Ptr ModDownloadDialog::getModDependenciesTask() return makeShared<GetModDependenciesTask>(this, m_instance, model, selectedVers); } return nullptr; -}; +} ResourcePackDownloadDialog::ResourcePackDownloadDialog(QWidget* parent, const std::shared_ptr<ResourcePackFolderModel>& resource_packs, @@ -330,8 +324,6 @@ QList<BasePage*> ResourcePackDownloadDialog::getPages() if (APPLICATION->capabilities() & Application::SupportsFlame) pages.append(FlameResourcePackPage::create(this, *m_instance)); - m_selectedPage = dynamic_cast<ResourcePackResourcePage*>(pages[0]); - return pages; } @@ -357,8 +349,6 @@ QList<BasePage*> TexturePackDownloadDialog::getPages() if (APPLICATION->capabilities() & Application::SupportsFlame) pages.append(FlameTexturePackPage::create(this, *m_instance)); - m_selectedPage = dynamic_cast<TexturePackResourcePage*>(pages[0]); - return pages; } @@ -379,11 +369,7 @@ ShaderPackDownloadDialog::ShaderPackDownloadDialog(QWidget* parent, QList<BasePage*> ShaderPackDownloadDialog::getPages() { QList<BasePage*> pages; - pages.append(ModrinthShaderPackPage::create(this, *m_instance)); - - m_selectedPage = dynamic_cast<ShaderPackResourcePage*>(pages[0]); - return pages; } diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.h b/launcher/ui/dialogs/ResourceDownloadDialog.h index f65daaa3..e9d2cfbe 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.h +++ b/launcher/ui/dialogs/ResourceDownloadDialog.h @@ -2,7 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <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 @@ -61,7 +61,7 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { QString dialogTitle() override { return tr("Download %1").arg(resourcesString()); }; bool selectPage(QString pageId); - ResourcePage* getSelectedPage(); + ResourcePage* selectedPage(); void addResource(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&); void removeResource(const QString&); @@ -88,7 +88,6 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { const std::shared_ptr<ResourceFolderModel> m_base_model; PageContainer* m_container = nullptr; - ResourcePage* m_selectedPage = nullptr; QDialogButtonBox m_buttons; QVBoxLayout m_vertical_layout; diff --git a/launcher/ui/dialogs/ReviewMessageBox.cpp b/launcher/ui/dialogs/ReviewMessageBox.cpp index 7b33765f..78c2542f 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.cpp +++ b/launcher/ui/dialogs/ReviewMessageBox.cpp @@ -5,7 +5,7 @@ #include <QPushButton> -ReviewMessageBox::ReviewMessageBox(QWidget* parent, QString const& title, QString const& icon) +ReviewMessageBox::ReviewMessageBox(QWidget* parent, [[maybe_unused]] QString const& title, [[maybe_unused]] QString const& icon) : QDialog(parent), ui(new Ui::ReviewMessageBox) { ui->setupUi(this); diff --git a/launcher/ui/dialogs/SkinUploadDialog.cpp b/launcher/ui/dialogs/SkinUploadDialog.cpp index 8180ac1f..1ff15c87 100644 --- a/launcher/ui/dialogs/SkinUploadDialog.cpp +++ b/launcher/ui/dialogs/SkinUploadDialog.cpp @@ -155,15 +155,15 @@ SkinUploadDialog::SkinUploadDialog(MinecraftAccountPtr acct, QWidget *parent) ui->setupUi(this); // FIXME: add a model for this, download/refresh the capes on demand - auto &data = *acct->accountData(); + auto &accountData = *acct->accountData(); int index = 0; ui->capeCombo->addItem(tr("No Cape"), QVariant()); - auto currentCape = data.minecraftProfile.currentCape; + auto currentCape = accountData.minecraftProfile.currentCape; if(currentCape.isEmpty()) { ui->capeCombo->setCurrentIndex(index); } - for(auto & cape: data.minecraftProfile.capes) { + for(auto & cape: accountData.minecraftProfile.capes) { index++; if(cape.data.size()) { QPixmap capeImage; diff --git a/launcher/ui/dialogs/SkinUploadDialog.ui b/launcher/ui/dialogs/SkinUploadDialog.ui index c7b16645..2c81a7fe 100644 --- a/launcher/ui/dialogs/SkinUploadDialog.ui +++ b/launcher/ui/dialogs/SkinUploadDialog.ui @@ -42,7 +42,7 @@ </size> </property> <property name="text"> - <string notr="true">...</string> + <string>Browse</string> </property> </widget> </item> diff --git a/launcher/ui/dialogs/VersionSelectDialog.cpp b/launcher/ui/dialogs/VersionSelectDialog.cpp index 5feb70d2..9fa3c90f 100644 --- a/launcher/ui/dialogs/VersionSelectDialog.cpp +++ b/launcher/ui/dialogs/VersionSelectDialog.cpp @@ -55,7 +55,7 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, m_verticalLayout = new QVBoxLayout(this); m_verticalLayout->setObjectName(QStringLiteral("verticalLayout")); - m_versionWidget = new VersionSelectWidget(true, parent); + m_versionWidget = new VersionSelectWidget(parent); m_verticalLayout->addWidget(m_versionWidget); m_horizontalLayout = new QHBoxLayout(); @@ -75,8 +75,9 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, retranslate(); - QObject::connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(accept())); - QObject::connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(m_versionWidget->view(), &QAbstractItemView::doubleClicked, this, &QDialog::accept); + connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); QMetaObject::connectSlotsByName(this); setWindowModality(Qt::WindowModal); @@ -123,6 +124,7 @@ int VersionSelectDialog::exec() { QDialog::open(); m_versionWidget->initialize(m_vlist); + m_versionWidget->selectSearch(); if(resizeOnColumn != -1) { m_versionWidget->setResizeOn(resizeOnColumn); @@ -150,6 +152,10 @@ void VersionSelectDialog::setExactFilter(BaseVersionList::ModelRoles role, QStri m_versionWidget->setExactFilter(role, filter); } +void VersionSelectDialog::setExactIfPresentFilter(BaseVersionList::ModelRoles role, QString filter) { + m_versionWidget->setExactIfPresentFilter(role, filter); +} + void VersionSelectDialog::setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter) { m_versionWidget->setFuzzyFilter(role, filter); diff --git a/launcher/ui/dialogs/VersionSelectDialog.h b/launcher/ui/dialogs/VersionSelectDialog.h index 18a50cdb..c20a9ed5 100644 --- a/launcher/ui/dialogs/VersionSelectDialog.h +++ b/launcher/ui/dialogs/VersionSelectDialog.h @@ -49,6 +49,7 @@ public: void setCurrentVersion(const QString & version); void setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter); void setExactFilter(BaseVersionList::ModelRoles role, QString filter); + void setExactIfPresentFilter(BaseVersionList::ModelRoles role, QString filter); void setEmptyString(QString emptyString); void setEmptyErrorString(QString emptyErrorString); void setResizeOn(int column); diff --git a/launcher/ui/instanceview/InstanceDelegate.cpp b/launcher/ui/instanceview/InstanceDelegate.cpp index 137cc8d5..384c33f3 100644 --- a/launcher/ui/instanceview/InstanceDelegate.cpp +++ b/launcher/ui/instanceview/InstanceDelegate.cpp @@ -400,7 +400,9 @@ signals: void editingDone(); }; -void ListViewDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const +void ListViewDelegate::updateEditorGeometry(QWidget* editor, + const QStyleOptionViewItem& option, + [[maybe_unused]] const QModelIndex& index) const { const int iconSize = 48; QRect textRect = option.rect; @@ -412,17 +414,17 @@ void ListViewDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionV void ListViewDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const { auto text = index.data(Qt::EditRole).toString(); - QTextEdit * realeditor = qobject_cast<NoReturnTextEdit *>(editor); - realeditor->setAlignment(Qt::AlignHCenter | Qt::AlignTop); - realeditor->append(text); - realeditor->selectAll(); - realeditor->document()->clearUndoRedoStacks(); + QTextEdit* realEditor = qobject_cast<NoReturnTextEdit*>(editor); + realEditor->setAlignment(Qt::AlignHCenter | Qt::AlignTop); + realEditor->append(text); + realEditor->selectAll(); + realEditor->document()->clearUndoRedoStacks(); } void ListViewDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { - QTextEdit * realeditor = qobject_cast<NoReturnTextEdit *>(editor); - QString text = realeditor->toPlainText(); + QTextEdit * realEditor = qobject_cast<NoReturnTextEdit *>(editor); + QString text = realEditor->toPlainText(); text.replace(QChar('\n'), QChar(' ')); text = text.trimmed(); // Prevent instance names longer than 128 chars @@ -433,7 +435,9 @@ void ListViewDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, } } -QWidget * ListViewDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const +QWidget* ListViewDelegate::createEditor(QWidget* parent, + [[maybe_unused]] const QStyleOptionViewItem& option, + [[maybe_unused]] const QModelIndex& index) const { auto editor = new NoReturnTextEdit(parent); connect(editor, &NoReturnTextEdit::editingDone, this, &ListViewDelegate::editingDone); diff --git a/launcher/ui/instanceview/InstanceView.cpp b/launcher/ui/instanceview/InstanceView.cpp index 05f0004d..6cbada3e 100644 --- a/launcher/ui/instanceview/InstanceView.cpp +++ b/launcher/ui/instanceview/InstanceView.cpp @@ -89,16 +89,18 @@ void InstanceView::setModel(QAbstractItemModel *model) connect(model, &QAbstractItemModel::rowsRemoved, this, &InstanceView::rowsRemoved); } -void InstanceView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) +void InstanceView::dataChanged([[maybe_unused]] const QModelIndex& topLeft, + [[maybe_unused]] const QModelIndex& bottomRight, + [[maybe_unused]] const QVector<int>& roles) { scheduleDelayedItemsLayout(); } -void InstanceView::rowsInserted(const QModelIndex &parent, int start, int end) +void InstanceView::rowsInserted([[maybe_unused]] const QModelIndex& parent, [[maybe_unused]] int start, [[maybe_unused]] int end) { scheduleDelayedItemsLayout(); } -void InstanceView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +void InstanceView::rowsAboutToBeRemoved([[maybe_unused]] const QModelIndex& parent, [[maybe_unused]] int start, [[maybe_unused]] int end) { scheduleDelayedItemsLayout(); } @@ -264,9 +266,9 @@ QString InstanceView::groupNameAt(const QPoint &point) { executeDelayedItemsLayout(); - VisualGroup::HitResults hitresult; - auto group = categoryAt(point + offset(), hitresult); - if(group && (hitresult & (VisualGroup::HeaderHit | VisualGroup::BodyHit))) + VisualGroup::HitResults hitResult; + auto group = categoryAt(point + offset(), hitResult); + if(group && (hitResult & (VisualGroup::HeaderHit | VisualGroup::BodyHit))) { return group->text; } @@ -301,9 +303,9 @@ void InstanceView::mousePressEvent(QMouseEvent *event) m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex); m_pressedPosition = geometryPos; - VisualGroup::HitResults hitresult; - m_pressedCategory = categoryAt(geometryPos, hitresult); - if (m_pressedCategory && hitresult & VisualGroup::CheckboxHit) + VisualGroup::HitResults hitResult; + m_pressedCategory = categoryAt(geometryPos, hitResult); + if (m_pressedCategory && hitResult & VisualGroup::CheckboxHit) { setState(m_pressedCategory->collapsed ? ExpandingState : CollapsingState); event->accept(); @@ -403,10 +405,10 @@ void InstanceView::mouseReleaseEvent(QMouseEvent *event) QPoint geometryPos = event->pos() + offset(); QPersistentModelIndex index = indexAt(visualPos); - VisualGroup::HitResults hitresult; + VisualGroup::HitResults hitResult; bool click = (index == m_pressedIndex && index.isValid()) || - (m_pressedCategory && m_pressedCategory == categoryAt(geometryPos, hitresult)); + (m_pressedCategory && m_pressedCategory == categoryAt(geometryPos, hitResult)); if (click && m_pressedCategory) { @@ -499,6 +501,7 @@ void InstanceView::mouseDoubleClickEvent(QMouseEvent *event) } } + void InstanceView::setPaintCat(bool visible) { m_catVisible = visible; @@ -508,7 +511,7 @@ void InstanceView::setPaintCat(bool visible) m_catPixmap = QPixmap(); } -void InstanceView::paintEvent(QPaintEvent* event) +void InstanceView::paintEvent([[maybe_unused]] QPaintEvent *event) { executeDelayedItemsLayout(); @@ -612,7 +615,7 @@ void InstanceView::paintEvent(QPaintEvent* event) #endif } -void InstanceView::resizeEvent(QResizeEvent *event) +void InstanceView::resizeEvent([[maybe_unused]] QResizeEvent *event) { int newItemsPerRow = calculateItemsPerRow(); if(newItemsPerRow != m_currentItemsPerRow) @@ -653,7 +656,7 @@ void InstanceView::dragMoveEvent(QDragMoveEvent *event) event->accept(); } -void InstanceView::dragLeaveEvent(QDragLeaveEvent *event) +void InstanceView::dragLeaveEvent([[maybe_unused]] QDragLeaveEvent *event) { executeDelayedItemsLayout(); @@ -678,9 +681,9 @@ void InstanceView::dropEvent(QDropEvent *event) { std::pair<VisualGroup *, VisualGroup::HitResults> dropPos = rowDropPos(event->pos()); const VisualGroup *group = dropPos.first; - auto hitresult = dropPos.second; + auto hitResult = dropPos.second; - if (hitresult == VisualGroup::HitResult::NoHit) + if (hitResult == VisualGroup::HitResult::NoHit) { viewport()->update(); return; @@ -720,8 +723,8 @@ void InstanceView::startDrag(Qt::DropActions supportedActions) if(indexes.count() == 0) return; - QMimeData *data = model()->mimeData(indexes); - if (!data) + QMimeData *mimeData = model()->mimeData(indexes); + if (!mimeData) { return; } @@ -729,7 +732,7 @@ void InstanceView::startDrag(Qt::DropActions supportedActions) QPixmap pixmap = renderToPixmap(indexes, &rect); QDrag *drag = new QDrag(this); drag->setPixmap(pixmap); - drag->setMimeData(data); + drag->setMimeData(mimeData); drag->setHotSpot(m_pressedPosition - rect.topLeft()); Qt::DropAction defaultDropAction = Qt::IgnoreAction; if (this->defaultDropAction() != Qt::IgnoreAction && (supportedActions & this->defaultDropAction())) @@ -856,16 +859,16 @@ QList<std::pair<QRect, QModelIndex>> InstanceView::draggablePaintPairs(const QMo return ret; } -bool InstanceView::isDragEventAccepted(QDropEvent *event) +bool InstanceView::isDragEventAccepted([[maybe_unused]] QDropEvent *event) { return true; } std::pair<VisualGroup *, VisualGroup::HitResults> InstanceView::rowDropPos(const QPoint &pos) { - VisualGroup::HitResults hitresult; - auto group = categoryAt(pos + offset(), hitresult); - return std::make_pair(group, hitresult); + VisualGroup::HitResults hitResult; + auto group = categoryAt(pos + offset(), hitResult); + return std::make_pair(group, hitResult); } QPoint InstanceView::offset() const @@ -894,7 +897,7 @@ QRegion InstanceView::visualRegionForSelection(const QItemSelection &selection) return region; } -QModelIndex InstanceView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) +QModelIndex InstanceView::moveCursor(QAbstractItemView::CursorAction cursorAction, [[maybe_unused]] Qt::KeyboardModifiers modifiers) { auto current = currentIndex(); if(!current.isValid()) @@ -919,23 +922,23 @@ QModelIndex InstanceView::moveCursor(QAbstractItemView::CursorAction cursorActio { if(row == 0) { - int prevgroupindex = group_index-1; - while(prevgroupindex >= 0) + int prevGroupIndex = group_index-1; + while(prevGroupIndex >= 0) { - auto prevgroup = m_groups[prevgroupindex]; - if(prevgroup->collapsed) + auto prevGroup = m_groups[prevGroupIndex]; + if(prevGroup->collapsed) { - prevgroupindex--; + prevGroupIndex--; continue; } - int newRow = prevgroup->numRows() - 1; - int newRowSize = prevgroup->rows[newRow].size(); + int newRow = prevGroup->numRows() - 1; + int newRowSize = prevGroup->rows[newRow].size(); int newColumn = m_currentCursorColumn; if (m_currentCursorColumn >= newRowSize) { newColumn = newRowSize - 1; } - return prevgroup->rows[newRow][newColumn]; + return prevGroup->rows[newRow][newColumn]; } } else @@ -955,22 +958,22 @@ QModelIndex InstanceView::moveCursor(QAbstractItemView::CursorAction cursorActio { if(row == cat->rows.size() - 1) { - int nextgroupindex = group_index+1; - while (nextgroupindex < m_groups.size()) + int nextGroupIndex = group_index+1; + while (nextGroupIndex < m_groups.size()) { - auto nextgroup = m_groups[nextgroupindex]; - if(nextgroup->collapsed) + auto nextGroup = m_groups[nextGroupIndex]; + if(nextGroup->collapsed) { - nextgroupindex++; + nextGroupIndex++; continue; } - int newRowSize = nextgroup->rows[0].size(); + int newRowSize = nextGroup->rows[0].size(); int newColumn = m_currentCursorColumn; if (m_currentCursorColumn >= newRowSize) { newColumn = newRowSize - 1; } - return nextgroup->rows[0][newColumn]; + return nextGroup->rows[0][newColumn]; } } else @@ -1054,7 +1057,7 @@ void InstanceView::scrollTo(const QModelIndex &index, ScrollHint hint) verticalScrollBar()->setValue(verticalScrollToValue(index, rect, hint)); } -int InstanceView::verticalScrollToValue(const QModelIndex &index, const QRect &rect, QListView::ScrollHint hint) const +int InstanceView::verticalScrollToValue([[maybe_unused]] const QModelIndex &index, const QRect &rect, QListView::ScrollHint hint) const { const QRect area = viewport()->rect(); const bool above = (hint == QListView::EnsureVisible && rect.top() < area.top()); diff --git a/launcher/ui/pages/BasePageContainer.h b/launcher/ui/pages/BasePageContainer.h index b41fe12a..eb3c9427 100644 --- a/launcher/ui/pages/BasePageContainer.h +++ b/launcher/ui/pages/BasePageContainer.h @@ -7,6 +7,7 @@ class BasePageContainer public: virtual ~BasePageContainer(){}; virtual bool selectPage(QString pageId) = 0; + virtual BasePage* selectedPage() const = 0; virtual BasePage* getPage(QString pageId) { return nullptr; }; virtual void refreshContainer() = 0; virtual bool requestClose() = 0; diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index fced5ff4..37df1a31 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -84,9 +84,8 @@ AccountListPage::AccountListPage(QWidget *parent) QItemSelectionModel *selectionModel = ui->listView->selectionModel(); - connect(selectionModel, &QItemSelectionModel::selectionChanged, [this](const QItemSelection &sel, const QItemSelection &dsel) { - updateButtonStates(); - }); + connect(selectionModel, &QItemSelectionModel::selectionChanged, + [this]([[maybe_unused]] const QItemSelection& sel, [[maybe_unused]] const QItemSelection& dsel) { updateButtonStates(); }); connect(ui->listView, &VersionListView::customContextMenuRequested, this, &AccountListPage::ShowContextMenu); connect(m_accounts.get(), &AccountList::listChanged, this, &AccountListPage::listChanged); diff --git a/launcher/ui/pages/global/ExternalToolsPage.ui b/launcher/ui/pages/global/ExternalToolsPage.ui index 3643094d..47c77842 100644 --- a/launcher/ui/pages/global/ExternalToolsPage.ui +++ b/launcher/ui/pages/global/ExternalToolsPage.ui @@ -47,7 +47,7 @@ <item> <widget class="QPushButton" name="jprofilerPathBtn"> <property name="text"> - <string notr="true">...</string> + <string>Browse</string> </property> </widget> </item> @@ -84,7 +84,7 @@ <item> <widget class="QPushButton" name="jvisualvmPathBtn"> <property name="text"> - <string notr="true">...</string> + <string>Browse</string> </property> </widget> </item> @@ -121,7 +121,7 @@ <item> <widget class="QPushButton" name="mceditPathBtn"> <property name="text"> - <string notr="true">...</string> + <string>Browse</string> </property> </widget> </item> @@ -166,7 +166,7 @@ <item row="0" column="2"> <widget class="QToolButton" name="jsonEditorBrowseBtn"> <property name="text"> - <string notr="true">...</string> + <string>Browse</string> </property> </widget> </item> diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 81dd4cc1..1f4dcc8d 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -176,7 +176,7 @@ void JavaPage::on_javaTestBtn_clicked() checker->run(); } -void JavaPage::on_maxMemSpinBox_valueChanged(int i) +void JavaPage::on_maxMemSpinBox_valueChanged([[maybe_unused]] int i) { updateThresholds(); } diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index 561cf79b..5a547637 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -160,37 +160,73 @@ <string>Java Runtime</string> </property> <layout class="QGridLayout" name="gridLayout_3"> - <item row="3" column="1"> - <widget class="QPushButton" name="javaDetectBtn"> + <item row="7" column="0" colspan="3"> + <widget class="QPlainTextEdit" name="jvmArgsTextBox"> + <property name="enabled"> + <bool>true</bool> + </property> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="text"> - <string>&Auto-detect...</string> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>100</height> + </size> </property> </widget> </item> - <item row="2" column="0"> - <widget class="QLabel" name="labelJVMArgs"> + <item row="4" column="0"> + <widget class="QCheckBox" name="skipCompatibilityCheckbox"> <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="text"> - <string>JVM arguments:</string> + <property name="toolTip"> + <string>If enabled, the launcher will not check if an instance is compatible with the selected Java version.</string> </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + <property name="text"> + <string>&Skip Java compatibility checks</string> </property> </widget> </item> - <item row="0" column="0"> - <widget class="QLabel" name="labelJavaPath"> + <item row="2" column="0" colspan="3"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QPushButton" name="javaDetectBtn"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Auto-detect...</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="javaTestBtn"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Test</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="labelJVMArgs"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -198,69 +234,50 @@ </sizepolicy> </property> <property name="text"> - <string>&Java path:</string> - </property> - <property name="buddy"> - <cstring>javaPathTextBox</cstring> - </property> - </widget> - </item> - <item row="3" column="2"> - <widget class="QPushButton" name="javaTestBtn"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <string>JVM arguments:</string> </property> - <property name="text"> - <string>&Test</string> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> </property> </widget> </item> - <item row="0" column="1" colspan="2"> + <item row="1" column="0" colspan="3"> <layout class="QHBoxLayout" name="horizontalLayout"> <item> + <widget class="QLabel" name="labelJavaPath"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Java path:</string> + </property> + <property name="buddy"> + <cstring>javaPathTextBox</cstring> + </property> + </widget> + </item> + <item> <widget class="QLineEdit" name="javaPathTextBox"/> </item> <item> <widget class="QPushButton" name="javaBrowseBtn"> <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="maximumSize"> - <size> - <width>28</width> - <height>16777215</height> - </size> - </property> <property name="text"> - <string notr="true">...</string> + <string>Browse</string> </property> </widget> </item> </layout> </item> - <item row="4" column="1"> - <widget class="QCheckBox" name="skipCompatibilityCheckbox"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="toolTip"> - <string>If enabled, the launcher will not check if an instance is compatible with the selected Java version.</string> - </property> - <property name="text"> - <string>&Skip Java compatibility checks</string> - </property> - </widget> - </item> - <item row="5" column="1"> + <item row="5" column="0"> <widget class="QCheckBox" name="skipJavaWizardCheckbox"> <property name="toolTip"> <string>If enabled, the launcher will not prompt you to choose a Java version if one isn't found.</string> @@ -270,25 +287,6 @@ </property> </widget> </item> - <item row="2" column="1" colspan="2"> - <widget class="QPlainTextEdit" name="jvmArgsTextBox"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maximumSize"> - <size> - <width>16777215</width> - <height>100</height> - </size> - </property> - </widget> - </item> </layout> </widget> </item> @@ -317,8 +315,6 @@ <tabstop>permGenSpinBox</tabstop> <tabstop>javaBrowseBtn</tabstop> <tabstop>javaPathTextBox</tabstop> - <tabstop>javaDetectBtn</tabstop> - <tabstop>javaTestBtn</tabstop> <tabstop>tabWidget</tabstop> </tabstops> <resources/> diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index 26408f44..bc259a9b 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -99,7 +99,7 @@ <item row="3" column="2"> <widget class="QToolButton" name="downloadsDirBrowseBtn"> <property name="text"> - <string notr="true">...</string> + <string>Browse</string> </property> </widget> </item> @@ -109,7 +109,7 @@ <item row="1" column="2"> <widget class="QToolButton" name="modsDirBrowseBtn"> <property name="text"> - <string notr="true">...</string> + <string>Browse</string> </property> </widget> </item> @@ -126,14 +126,14 @@ <item row="0" column="2"> <widget class="QToolButton" name="instDirBrowseBtn"> <property name="text"> - <string notr="true">...</string> + <string>Browse</string> </property> </widget> </item> <item row="2" column="2"> <widget class="QToolButton" name="iconsDirBrowseBtn"> <property name="text"> - <string notr="true">...</string> + <string>Browse</string> </property> </widget> </item> diff --git a/launcher/ui/pages/global/ProxyPage.cpp b/launcher/ui/pages/global/ProxyPage.cpp index ffff8456..5ccdb152 100644 --- a/launcher/ui/pages/global/ProxyPage.cpp +++ b/launcher/ui/pages/global/ProxyPage.cpp @@ -73,7 +73,7 @@ void ProxyPage::updateCheckboxStuff() ui->proxyAuthBox->setEnabled(enableEditing); } -void ProxyPage::proxyGroupChanged(QAbstractButton *button) +void ProxyPage::proxyGroupChanged([[maybe_unused]] QAbstractButton *button) { updateCheckboxStuff(); } diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 12038f88..719db879 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -305,7 +305,7 @@ bool ExternalResourcesPage::current(const QModelIndex& current, const QModelInde return onSelectionChanged(current, previous); } -bool ExternalResourcesPage::onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) +bool ExternalResourcesPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { auto sourceCurrent = m_filterModel->mapToSource(current); int row = sourceCurrent.row(); diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 25cc1a0d..718c628c 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -491,7 +491,7 @@ void InstanceSettingsPage::changeInstanceAccount(int index) } } -void InstanceSettingsPage::on_maxMemSpinBox_valueChanged(int i) +void InstanceSettingsPage::on_maxMemSpinBox_valueChanged([[maybe_unused]] int i) { updateThresholds(); } diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 245433fe..380d8c88 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -61,31 +61,7 @@ <bool>false</bool> </property> <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0" colspan="3"> - <widget class="QLineEdit" name="javaPathTextBox"/> - </item> - <item row="1" column="0"> - <widget class="QPushButton" name="javaDetectBtn"> - <property name="text"> - <string>Auto-detect...</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QPushButton" name="javaBrowseBtn"> - <property name="text"> - <string>Browse...</string> - </property> - </widget> - </item> - <item row="1" column="2"> - <widget class="QPushButton" name="javaTestBtn"> - <property name="text"> - <string>Test</string> - </property> - </widget> - </item> - <item row="2" column="0"> + <item row="4" column="0"> <widget class="QCheckBox" name="skipCompatibilityCheckbox"> <property name="toolTip"> <string>If enabled, the launcher will not check if an instance is compatible with the selected Java version.</string> @@ -95,6 +71,38 @@ </property> </widget> </item> + <item row="1" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLineEdit" name="javaPathTextBox"/> + </item> + <item> + <widget class="QPushButton" name="javaBrowseBtn"> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="2" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QPushButton" name="javaDetectBtn"> + <property name="text"> + <string>Auto-detect...</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="javaTestBtn"> + <property name="text"> + <string>Test</string> + </property> + </widget> + </item> + </layout> + </item> </layout> </widget> </item> @@ -699,10 +707,6 @@ <tabstop>openGlobalJavaSettingsButton</tabstop> <tabstop>settingsTabs</tabstop> <tabstop>javaSettingsGroupBox</tabstop> - <tabstop>javaPathTextBox</tabstop> - <tabstop>javaDetectBtn</tabstop> - <tabstop>javaBrowseBtn</tabstop> - <tabstop>javaTestBtn</tabstop> <tabstop>memoryGroupBox</tabstop> <tabstop>minMemSpinBox</tabstop> <tabstop>maxMemSpinBox</tabstop> diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index 0fc0c986..82de9742 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -23,6 +23,8 @@ #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" +#include "net/ApiDownload.h" + /** This is just to override the combo box popup behavior so that the combo box doesn't take the whole screen. * ... thanks Qt. */ @@ -226,7 +228,7 @@ void ModrinthManagedPackPage::parseManagedPack() QString id = m_inst->getManagedPackID(); m_fetch_job->addNetAction( - Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); + Net::ApiDownload::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] { QJsonParseError parse_error{}; @@ -376,7 +378,7 @@ void FlameManagedPackPage::parseManagedPack() QString id = m_inst->getManagedPackID(); - m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/mods/%2/files").arg(BuildConfig.FLAME_BASE_URL, id), response)); + m_fetch_job->addNetAction(Net::ApiDownload::makeByteArray(QString("%1/mods/%2/files").arg(BuildConfig.FLAME_BASE_URL, id), response)); QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] { QJsonParseError parse_error{}; diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index cef292bd..0f5e29cb 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -127,7 +127,7 @@ bool ModFolderPage::shouldDisplay() const return true; } -bool ModFolderPage::onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) +bool ModFolderPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { auto sourceCurrent = m_filterModel->mapToSource(current); int row = sourceCurrent.row(); diff --git a/launcher/ui/pages/instance/OtherLogsPage.cpp b/launcher/ui/pages/instance/OtherLogsPage.cpp index bbdd7324..f2b13f32 100644 --- a/launcher/ui/pages/instance/OtherLogsPage.cpp +++ b/launcher/ui/pages/instance/OtherLogsPage.cpp @@ -287,23 +287,23 @@ void OtherLogsPage::on_btnClean_clicked() } if(!failed.empty()) { - QMessageBox *messageBox = new QMessageBox(this); - messageBox->setWindowTitle(tr("Error")); + QMessageBox *messageBoxFailure = new QMessageBox(this); + messageBoxFailure->setWindowTitle(tr("Error")); if(failed.size() > 5) { - messageBox->setText(tr("Couldn't delete some files!")); - messageBox->setDetailedText(failed.join('\n')); + messageBoxFailure->setText(tr("Couldn't delete some files!")); + messageBoxFailure->setDetailedText(failed.join('\n')); } else { - messageBox->setText(tr("Couldn't delete some files:\n%1").arg(failed.join('\n'))); + messageBoxFailure->setText(tr("Couldn't delete some files:\n%1").arg(failed.join('\n'))); } - messageBox->setStandardButtons(QMessageBox::Ok); - messageBox->setDefaultButton(QMessageBox::Ok); - messageBox->setTextInteractionFlags(Qt::TextSelectableByMouse); - messageBox->setIcon(QMessageBox::Critical); - messageBox->setTextInteractionFlags(Qt::TextBrowserInteraction); - messageBox->exec(); + messageBoxFailure->setStandardButtons(QMessageBox::Ok); + messageBoxFailure->setDefaultButton(QMessageBox::Ok); + messageBoxFailure->setTextInteractionFlags(Qt::TextSelectableByMouse); + messageBoxFailure->setIcon(QMessageBox::Critical); + messageBoxFailure->setTextInteractionFlags(Qt::TextBrowserInteraction); + messageBoxFailure->exec(); } } diff --git a/launcher/ui/pages/instance/ResourcePackPage.cpp b/launcher/ui/pages/instance/ResourcePackPage.cpp index 12b371df..26c14ca4 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.cpp +++ b/launcher/ui/pages/instance/ResourcePackPage.cpp @@ -55,7 +55,7 @@ ResourcePackPage::ResourcePackPage(MinecraftInstance* instance, std::shared_ptr< ui->actionViewConfigs->setVisible(false); } -bool ResourcePackPage::onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) +bool ResourcePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { auto sourceCurrent = m_filterModel->mapToSource(current); int row = sourceCurrent.row(); diff --git a/launcher/ui/pages/instance/ServersPage.cpp b/launcher/ui/pages/instance/ServersPage.cpp index 4b1fa08a..09ed9e40 100644 --- a/launcher/ui/pages/instance/ServersPage.cpp +++ b/launcher/ui/pages/instance/ServersPage.cpp @@ -684,7 +684,7 @@ void ServersPage::runningStateChanged(bool running) updateState(); } -void ServersPage::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +void ServersPage::currentChanged(const QModelIndex ¤t, [[maybe_unused]] const QModelIndex &previous) { int nextServer = -1; if (!current.isValid()) @@ -700,7 +700,7 @@ void ServersPage::currentChanged(const QModelIndex ¤t, const QModelIndex & } // WARNING: this is here because currentChanged is not accurate when removing rows. the current item needs to be fixed up after removal. -void ServersPage::rowsRemoved(const QModelIndex& parent, int first, int last) +void ServersPage::rowsRemoved([[maybe_unused]] const QModelIndex& parent, int first, int last) { if(currentServer < first) { diff --git a/launcher/ui/pages/instance/TexturePackPage.cpp b/launcher/ui/pages/instance/TexturePackPage.cpp index e477ceda..fa478a9a 100644 --- a/launcher/ui/pages/instance/TexturePackPage.cpp +++ b/launcher/ui/pages/instance/TexturePackPage.cpp @@ -57,7 +57,7 @@ TexturePackPage::TexturePackPage(MinecraftInstance* instance, std::shared_ptr<Te ui->actionViewConfigs->setVisible(false); } -bool TexturePackPage::onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) +bool TexturePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { auto sourceCurrent = m_filterModel->mapToSource(current); int row = sourceCurrent.row(); diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index a180c804..1cc036ac 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -6,7 +6,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> * Copyright (C) 2022-2023 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 @@ -51,6 +51,7 @@ #include <QUrl> #include "VersionPage.h" +#include "ui/dialogs/InstallLoaderDialog.h" #include "ui_VersionPage.h" #include "ui/dialogs/CustomMessageBox.h" @@ -188,7 +189,8 @@ void VersionPage::showContextMenu(const QPoint& pos) delete menu; } -void VersionPage::packageCurrent(const QModelIndex& current, const QModelIndex& previous) + +void VersionPage::packageCurrent(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { if (!current.isValid()) { ui->frame->clear(); @@ -226,18 +228,6 @@ void VersionPage::packageCurrent(const QModelIndex& current, const QModelIndex& void VersionPage::updateVersionControls() { - // FIXME: this is a dirty hack - auto minecraftVersion = Version(m_profile->getComponentVersion("net.minecraft")); - - bool supportsFabric = minecraftVersion >= Version("1.14"); - ui->actionInstall_Fabric->setEnabled(supportsFabric); - - bool supportsQuilt = minecraftVersion >= Version("1.14"); - ui->actionInstall_Quilt->setEnabled(supportsQuilt); - - bool supportsLiteLoader = minecraftVersion <= Version("1.12.2"); - ui->actionInstall_LiteLoader->setEnabled(supportsLiteLoader); - updateButtons(); } @@ -389,20 +379,14 @@ void VersionPage::on_actionChange_version_triggered() return; } auto uid = list->uid(); - // FIXME: this is a horrible HACK. Get version filtering information from the actual metadata... - if (uid == "net.minecraftforge") { - on_actionInstall_Forge_triggered(); - return; - } else if (uid == "com.mumfrey.liteloader") { - on_actionInstall_LiteLoader_triggered(); - return; - } + VersionSelectDialog vselect(list.get(), tr("Change %1 version").arg(name), this); if (uid == "net.fabricmc.intermediary" || uid == "org.quiltmc.hashed") { vselect.setEmptyString(tr("No intermediary mappings versions are currently available.")); vselect.setEmptyErrorString(tr("Couldn't load or download the intermediary mappings version lists!")); - vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft")); } + vselect.setExactIfPresentFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft")); + auto currentVersion = patch->getVersion(); if (!currentVersion.isEmpty()) { vselect.setCurrentVersion(currentVersion); @@ -443,79 +427,11 @@ void VersionPage::on_actionDownload_All_triggered() m_container->refreshContainer(); } -void VersionPage::on_actionInstall_Forge_triggered() -{ - auto vlist = APPLICATION->metadataIndex()->get("net.minecraftforge"); - if (!vlist) { - return; - } - VersionSelectDialog vselect(vlist.get(), tr("Select Forge version"), this); - vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft")); - vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") + - m_profile->getComponentVersion("net.minecraft")); - vselect.setEmptyErrorString(tr("Couldn't load or download the Forge version lists!")); - - auto currentVersion = m_profile->getComponentVersion("net.minecraftforge"); - if (!currentVersion.isEmpty()) { - vselect.setCurrentVersion(currentVersion); - } - - if (vselect.exec() && vselect.selectedVersion()) { - auto vsn = vselect.selectedVersion(); - m_profile->setComponentVersion("net.minecraftforge", vsn->descriptor()); - m_profile->resolve(Net::Mode::Online); - // m_profile->installVersion(); - preselect(m_profile->rowCount(QModelIndex()) - 1); - m_container->refreshContainer(); - } -} - -void VersionPage::on_actionInstall_Fabric_triggered() -{ - auto vlist = APPLICATION->metadataIndex()->get("net.fabricmc.fabric-loader"); - if (!vlist) { - return; - } - VersionSelectDialog vselect(vlist.get(), tr("Select Fabric Loader version"), this); - vselect.setEmptyString(tr("No Fabric Loader versions are currently available.")); - vselect.setEmptyErrorString(tr("Couldn't load or download the Fabric Loader version lists!")); - - auto currentVersion = m_profile->getComponentVersion("net.fabricmc.fabric-loader"); - if (!currentVersion.isEmpty()) { - vselect.setCurrentVersion(currentVersion); - } - - if (vselect.exec() && vselect.selectedVersion()) { - auto vsn = vselect.selectedVersion(); - m_profile->setComponentVersion("net.fabricmc.fabric-loader", vsn->descriptor()); - m_profile->resolve(Net::Mode::Online); - preselect(m_profile->rowCount(QModelIndex()) - 1); - m_container->refreshContainer(); - } -} - -void VersionPage::on_actionInstall_Quilt_triggered() +void VersionPage::on_actionInstall_Loader_triggered() { - auto vlist = APPLICATION->metadataIndex()->get("org.quiltmc.quilt-loader"); - if (!vlist) { - return; - } - VersionSelectDialog vselect(vlist.get(), tr("Select Quilt Loader version"), this); - vselect.setEmptyString(tr("No Quilt Loader versions are currently available.")); - vselect.setEmptyErrorString(tr("Couldn't load or download the Quilt Loader version lists!")); - - auto currentVersion = m_profile->getComponentVersion("org.quiltmc.quilt-loader"); - if (!currentVersion.isEmpty()) { - vselect.setCurrentVersion(currentVersion); - } - - if (vselect.exec() && vselect.selectedVersion()) { - auto vsn = vselect.selectedVersion(); - m_profile->setComponentVersion("org.quiltmc.quilt-loader", vsn->descriptor()); - m_profile->resolve(Net::Mode::Online); - preselect(m_profile->rowCount(QModelIndex()) - 1); - m_container->refreshContainer(); - } + InstallLoaderDialog dialog(m_inst->getPackProfile(), QString(), this); + dialog.exec(); + m_container->refreshContainer(); } void VersionPage::on_actionAdd_Empty_triggered() @@ -534,33 +450,6 @@ void VersionPage::on_actionAdd_Empty_triggered() } } -void VersionPage::on_actionInstall_LiteLoader_triggered() -{ - auto vlist = APPLICATION->metadataIndex()->get("com.mumfrey.liteloader"); - if (!vlist) { - return; - } - VersionSelectDialog vselect(vlist.get(), tr("Select LiteLoader version"), this); - vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft")); - vselect.setEmptyString(tr("No LiteLoader versions are currently available for Minecraft ") + - m_profile->getComponentVersion("net.minecraft")); - vselect.setEmptyErrorString(tr("Couldn't load or download the LiteLoader version lists!")); - - auto currentVersion = m_profile->getComponentVersion("com.mumfrey.liteloader"); - if (!currentVersion.isEmpty()) { - vselect.setCurrentVersion(currentVersion); - } - - if (vselect.exec() && vselect.selectedVersion()) { - auto vsn = vselect.selectedVersion(); - m_profile->setComponentVersion("com.mumfrey.liteloader", vsn->descriptor()); - m_profile->resolve(Net::Mode::Online); - // m_profile->installVersion(vselect.selectedVersion()); - preselect(m_profile->rowCount(QModelIndex()) - 1); - m_container->refreshContainer(); - } -} - void VersionPage::on_actionLibrariesFolder_triggered() { DesktopServices::openDirectory(m_inst->getLocalLibraryPath(), true); @@ -571,7 +460,7 @@ void VersionPage::on_actionMinecraftFolder_triggered() DesktopServices::openDirectory(m_inst->gameRoot(), true); } -void VersionPage::versionCurrent(const QModelIndex& current, const QModelIndex& previous) +void VersionPage::versionCurrent(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { currentIdx = current.row(); updateButtons(currentIdx); diff --git a/launcher/ui/pages/instance/VersionPage.h b/launcher/ui/pages/instance/VersionPage.h index 45d383f4..6915be88 100644 --- a/launcher/ui/pages/instance/VersionPage.h +++ b/launcher/ui/pages/instance/VersionPage.h @@ -68,11 +68,8 @@ class VersionPage : public QMainWindow, public BasePage { private slots: void on_actionChange_version_triggered(); - void on_actionInstall_Forge_triggered(); - void on_actionInstall_Fabric_triggered(); - void on_actionInstall_Quilt_triggered(); + void on_actionInstall_Loader_triggered(); void on_actionAdd_Empty_triggered(); - void on_actionInstall_LiteLoader_triggered(); void on_actionReload_triggered(); void on_actionRemove_triggered(); void on_actionMove_up_triggered(); diff --git a/launcher/ui/pages/instance/VersionPage.ui b/launcher/ui/pages/instance/VersionPage.ui index a73c42d6..9be21d49 100644 --- a/launcher/ui/pages/instance/VersionPage.ui +++ b/launcher/ui/pages/instance/VersionPage.ui @@ -98,11 +98,7 @@ <addaction name="actionEdit"/> <addaction name="actionRevert"/> <addaction name="separator"/> - <addaction name="actionInstall_Forge"/> - <addaction name="actionInstall_Fabric"/> - <addaction name="actionInstall_Quilt"/> - <addaction name="actionInstall_LiteLoader"/> - <addaction name="separator"/> + <addaction name="actionInstall_Loader"/> <addaction name="actionAdd_to_Minecraft_jar"/> <addaction name="actionReplace_Minecraft_jar"/> <addaction name="actionAdd_Agents"/> @@ -116,26 +112,26 @@ </widget> <action name="actionChange_version"> <property name="text"> - <string>Change version</string> + <string>Change Version</string> </property> <property name="toolTip"> - <string>Change version of the selected package.</string> + <string>Change version of the selected component.</string> </property> </action> <action name="actionMove_up"> <property name="text"> - <string>Move up</string> + <string>Move Up</string> </property> <property name="toolTip"> - <string>Make the selected package apply sooner.</string> + <string>Make the selected component apply sooner.</string> </property> </action> <action name="actionMove_down"> <property name="text"> - <string>Move down</string> + <string>Move Down</string> </property> <property name="toolTip"> - <string>Make the selected package apply later.</string> + <string>Make the selected component apply later.</string> </property> </action> <action name="actionRemove"> @@ -143,7 +139,7 @@ <string>Remove</string> </property> <property name="toolTip"> - <string>Remove selected package from the instance.</string> + <string>Remove selected component from the instance.</string> </property> </action> <action name="actionCustomize"> @@ -151,7 +147,7 @@ <string>Customize</string> </property> <property name="toolTip"> - <string>Customize selected package.</string> + <string>Customize selected component.</string> </property> </action> <action name="actionEdit"> @@ -159,7 +155,7 @@ <string>Edit</string> </property> <property name="toolTip"> - <string>Edit selected package.</string> + <string>Edit selected component.</string> </property> </action> <action name="actionRevert"> @@ -167,39 +163,15 @@ <string>Revert</string> </property> <property name="toolTip"> - <string>Revert the selected package to default.</string> - </property> - </action> - <action name="actionInstall_Forge"> - <property name="text"> - <string>Install Forge</string> - </property> - <property name="toolTip"> - <string>Install the Minecraft Forge package.</string> - </property> - </action> - <action name="actionInstall_Fabric"> - <property name="text"> - <string>Install Fabric</string> - </property> - <property name="toolTip"> - <string>Install the Fabric Loader package.</string> - </property> - </action> - <action name="actionInstall_Quilt"> - <property name="text"> - <string>Install Quilt</string> - </property> - <property name="toolTip"> - <string>Install the Quilt Loader package.</string> + <string>Revert the selected component to default.</string> </property> </action> - <action name="actionInstall_LiteLoader"> + <action name="actionInstall_Loader"> <property name="text"> - <string>Install LiteLoader</string> + <string>Install Loader</string> </property> <property name="toolTip"> - <string>Install the LiteLoader package.</string> + <string>Install a mod loader.</string> </property> </action> <action name="actionAdd_to_Minecraft_jar"> @@ -228,7 +200,7 @@ <string>Add Empty</string> </property> <property name="toolTip"> - <string>Add an empty custom package.</string> + <string>Add an empty custom component.</string> </property> </action> <action name="actionReload"> @@ -236,12 +208,12 @@ <string>Reload</string> </property> <property name="toolTip"> - <string>Reload all packages.</string> + <string>Reload all components.</string> </property> </action> <action name="actionDownload_All"> <property name="text"> - <string>Download All</string> + <string>Download all</string> </property> <property name="toolTip"> <string>Download the files needed to launch the instance now.</string> diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp index b2200b1a..8eb681d2 100644 --- a/launcher/ui/pages/instance/WorldListPage.cpp +++ b/launcher/ui/pages/instance/WorldListPage.cpp @@ -353,7 +353,7 @@ void WorldListPage::mceditState(LoggedProcess::State state) } } -void WorldListPage::worldChanged(const QModelIndex ¤t, const QModelIndex &previous) +void WorldListPage::worldChanged([[maybe_unused]] const QModelIndex ¤t, [[maybe_unused]] const QModelIndex &previous) { QModelIndex index = getSelectedWorld(); bool enable = index.isValid(); diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp index 30196aad..8f30781e 100644 --- a/launcher/ui/pages/modplatform/ImportPage.cpp +++ b/launcher/ui/pages/modplatform/ImportPage.cpp @@ -50,7 +50,7 @@ class UrlValidator : public QValidator public: using QValidator::QValidator; - State validate(QString &in, int &pos) const + State validate(QString &in, [[maybe_unused]] int &pos) const { const QUrl url(in); if (url.isValid() && !url.isRelative() && !url.isEmpty()) @@ -118,8 +118,8 @@ void ImportPage::updateState() if(fi.exists() && (isZip || isMRPack)) { - QFileInfo fi(url.fileName()); - dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url,this)); + QFileInfo file_info(url.fileName()); + dialog->setSuggestedPack(file_info.completeBaseName(), new InstanceImportTask(url,this)); dialog->setSuggestedIcon("default"); } } diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 49405a02..0a7edb7b 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -17,7 +17,7 @@ #include "BuildConfig.h" #include "Json.h" -#include "net/Download.h" +#include "net/ApiDownload.h" #include "net/NetJob.h" #include "modplatform/ModIndex.h" @@ -102,7 +102,7 @@ QHash<int, QByteArray> ResourceModel::roleNames() const return roles; } -bool ResourceModel::setData(const QModelIndex& index, const QVariant& value, int role) +bool ResourceModel::setData(const QModelIndex& index, const QVariant& value, [[maybe_unused]] int role) { int pos = index.row(); if (pos >= m_packs.size() || pos < 0 || !index.isValid()) @@ -281,7 +281,7 @@ std::optional<QIcon> ResourceModel::getIcon(QModelIndex& index, const QUrl& url) auto cache_entry = APPLICATION->metacache()->resolveEntry( metaEntryBase(), QString("logos/%1").arg(QString(QCryptographicHash::hash(url.toEncoded(), QCryptographicHash::Algorithm::Sha1).toHex()))); - auto icon_fetch_action = Net::Download::makeCached(url, cache_entry); + auto icon_fetch_action = Net::ApiDownload::makeCached(url, cache_entry); auto full_file_path = cache_entry->getFullPath(); connect(icon_fetch_action.get(), &NetAction::succeeded, this, [=] { @@ -310,7 +310,7 @@ std::optional<QIcon> ResourceModel::getIcon(QModelIndex& index, const QUrl& url) #define NEED_FOR_CALLBACK_ASSERT(name) \ Q_ASSERT_X(0 != 0, #name, "You NEED to re-implement this if you intend on using the default callbacks.") -QJsonArray ResourceModel::documentToArray(QJsonDocument& doc) const +QJsonArray ResourceModel::documentToArray([[maybe_unused]] QJsonDocument& doc) const { NEED_FOR_CALLBACK_ASSERT("documentToArray"); return {}; @@ -372,7 +372,7 @@ void ResourceModel::searchRequestSucceeded(QJsonDocument& doc) endInsertRows(); } -void ResourceModel::searchRequestFailed(QString reason, int network_error_code) +void ResourceModel::searchRequestFailed([[maybe_unused]] QString reason, int network_error_code) { switch (network_error_code) { default: diff --git a/launcher/ui/pages/modplatform/ResourcePackModel.cpp b/launcher/ui/pages/modplatform/ResourcePackModel.cpp index 18c14bf8..d436f320 100644 --- a/launcher/ui/pages/modplatform/ResourcePackModel.cpp +++ b/launcher/ui/pages/modplatform/ResourcePackModel.cpp @@ -9,7 +9,8 @@ namespace ResourceDownload { ResourcePackResourceModel::ResourcePackResourceModel(BaseInstance const& base_inst, ResourceAPI* api) - : ResourceModel(api), m_base_instance(base_inst){}; + : ResourceModel(api), m_base_instance(base_inst) +{} /******** Make data requests ********/ diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index 48afbd90..c087e2be 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -4,7 +4,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <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 @@ -280,7 +280,7 @@ void ResourcePage::updateVersionList() updateSelectionButton(); } -void ResourcePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) +void ResourcePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelIndex prev) { if (!curr.isValid()) { return; @@ -307,9 +307,9 @@ void ResourcePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) updateUi(); } -void ResourcePage::onVersionSelectionChanged(QString data) +void ResourcePage::onVersionSelectionChanged(QString versionData) { - if (data.isNull() || data.isEmpty()) { + if (versionData.isNull() || versionData.isEmpty()) { m_selected_version_index = -1; return; } @@ -395,7 +395,7 @@ void ResourcePage::openUrl(const QUrl& url) if (auto current_pack = getCurrentPack(); current_pack && slug != current_pack->slug) { m_parent_dialog->selectPage(page); - auto newPage = m_parent_dialog->getSelectedPage(); + auto newPage = m_parent_dialog->selectedPage(); QLineEdit* searchEdit = newPage->m_ui->searchEdit; auto model = newPage->m_model; diff --git a/launcher/ui/pages/modplatform/ResourcePage.h b/launcher/ui/pages/modplatform/ResourcePage.h index b4a87f57..3e8bb7d8 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.h +++ b/launcher/ui/pages/modplatform/ResourcePage.h @@ -97,7 +97,10 @@ class ResourcePage : public QWidget, public BasePage { virtual void openUrl(const QUrl&); /** Whether the version is opted out or not. Currently only makes sense in CF. */ - virtual bool optedOut(ModPlatform::IndexedVersion& ver) const { return false; }; + virtual bool optedOut(ModPlatform::IndexedVersion& ver) const { + Q_UNUSED(ver); + return false; + }; public: BaseInstance& m_base_instance; diff --git a/launcher/ui/pages/modplatform/ShaderPackModel.cpp b/launcher/ui/pages/modplatform/ShaderPackModel.cpp index aabd3be6..8c913657 100644 --- a/launcher/ui/pages/modplatform/ShaderPackModel.cpp +++ b/launcher/ui/pages/modplatform/ShaderPackModel.cpp @@ -9,7 +9,8 @@ namespace ResourceDownload { ShaderPackResourceModel::ShaderPackResourceModel(BaseInstance const& base_inst, ResourceAPI* api) - : ResourceModel(api), m_base_instance(base_inst){}; + : ResourceModel(api), m_base_instance(base_inst) +{} /******** Make data requests ********/ diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp index c6b087d6..39f4f346 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp @@ -20,6 +20,8 @@ #include <BuildConfig.h> #include <Json.h> +#include "net/ApiDownload.h" + namespace Atl { ListModel::ListModel(QObject* parent) : QAbstractListModel(parent) {} @@ -75,7 +77,7 @@ void ListModel::request() auto netJob = makeShared<NetJob>("Atl::Request", APPLICATION->network()); auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/json/packsnew.json"); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(url), response)); jobPtr = netJob; jobPtr->start(); @@ -137,8 +139,7 @@ void ListModel::requestFailed(QString reason) void ListModel::getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback) { if (m_logoMap.contains(logo)) { - callback( - APPLICATION->metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath()); + callback(APPLICATION->metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(logo))->getFullPath()); } else { requestLogo(logo, logoUrl); } @@ -168,9 +169,9 @@ void ListModel::requestLogo(QString file, QString url) return; } - MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(file.section(".", 0, 0))); + MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(file)); auto job = new NetJob(QString("ATLauncher Icon Download %1").arg(file), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); QObject::connect(job, &NetJob::succeeded, this, [this, file, fullPath, job] { diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp index 7b61daa7..d9f3274d 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp @@ -43,6 +43,8 @@ #include "modplatform/atlauncher/ATLShareCode.h" #include "Application.h" +#include "net/ApiDownload.h" + AtlOptionalModListModel::AtlOptionalModListModel(QWidget* parent, ATLauncher::PackVersion version, QVector<ATLauncher::VersionMod> mods) : QAbstractListModel(parent) , m_version(version) @@ -114,7 +116,7 @@ QVariant AtlOptionalModListModel::data(const QModelIndex &index, int role) const return {}; } -bool AtlOptionalModListModel::setData(const QModelIndex &index, const QVariant &value, int role) { +bool AtlOptionalModListModel::setData(const QModelIndex &index, [[maybe_unused]] const QVariant &value, int role) { if (role == Qt::CheckStateRole) { auto row = index.row(); auto mod = m_mods.at(row); @@ -152,7 +154,7 @@ Qt::ItemFlags AtlOptionalModListModel::flags(const QModelIndex &index) const { void AtlOptionalModListModel::useShareCode(const QString& code) { m_jobPtr.reset(new NetJob("Atl::Request", APPLICATION->network())); auto url = QString(BuildConfig.ATL_API_BASE_URL + "share-codes/" + code); - m_jobPtr->addNetAction(Net::Download::makeByteArray(QUrl(url), m_response)); + m_jobPtr->addNetAction(Net::ApiDownload::makeByteArray(QUrl(url), m_response)); connect(m_jobPtr.get(), &NetJob::succeeded, this, &AtlOptionalModListModel::shareCodeSuccess); @@ -206,7 +208,7 @@ void AtlOptionalModListModel::shareCodeSuccess() { AtlOptionalModListModel::index(m_mods.size() - 1, EnabledColumn)); } -void AtlOptionalModListModel::shareCodeFailure(const QString& reason) { +void AtlOptionalModListModel::shareCodeFailure([[maybe_unused]] const QString& reason) { m_jobPtr.reset(); // fixme: plumb in an error message @@ -277,16 +279,16 @@ void AtlOptionalModListModel::setMod(ATLauncher::VersionMod mod, int index, bool // if the dependency is 'effectively hidden', then track which mods // depend on it - so we can efficiently disable it when no more dependents // depend on it. - auto dependants = m_dependants[dependencyName]; + auto dependents = m_dependents[dependencyName]; if (enable) { - dependants.append(mod.name); + dependents.append(mod.name); } else { - dependants.removeAll(mod.name); + dependents.removeAll(mod.name); // if there are no longer any dependents, let's disable the mod - if (dependencyMod.effectively_hidden && dependants.isEmpty()) { + if (dependencyMod.effectively_hidden && dependents.isEmpty()) { setMod(dependencyMod, dependencyIndex, false, shouldEmit); } } @@ -294,8 +296,8 @@ void AtlOptionalModListModel::setMod(ATLauncher::VersionMod mod, int index, bool // disable mods that depend on this one, if disabling if (!enable) { - auto dependants = m_dependants[mod.name]; - for (const auto& dependencyName : dependants) { + auto dependents = m_dependents[mod.name]; + for (const auto& dependencyName : dependents) { auto dependencyIndex = m_index[dependencyName]; auto dependencyMod = m_mods.at(dependencyIndex); diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h index 639f0d48..fe24d821 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h +++ b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h @@ -91,7 +91,7 @@ private: QMap<QString, bool> m_selection; QMap<QString, int> m_index; - QMap<QString, QVector<QString>> m_dependants; + QMap<QString, QVector<QString>> m_dependents; }; class AtlOptionalModDialog : public QDialog { diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp index 87544445..7c80ed2d 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp @@ -132,13 +132,13 @@ void AtlPage::triggerSearch() filterModel->setSearchTerm(ui->searchEdit->text()); } -void AtlPage::onSortingSelectionChanged(QString data) +void AtlPage::onSortingSelectionChanged(QString sort) { - auto toSet = filterModel->getAvailableSortings().value(data); + auto toSet = filterModel->getAvailableSortings().value(sort); filterModel->setSorting(toSet); } -void AtlPage::onSelectionChanged(QModelIndex first, QModelIndex second) +void AtlPage::onSelectionChanged(QModelIndex first, [[maybe_unused]] QModelIndex second) { ui->versionSelectionBox->clear(); @@ -162,14 +162,14 @@ void AtlPage::onSelectionChanged(QModelIndex first, QModelIndex second) suggestCurrent(); } -void AtlPage::onVersionSelectionChanged(QString data) +void AtlPage::onVersionSelectionChanged(QString version) { - if(data.isNull() || data.isEmpty()) + if(version.isNull() || version.isEmpty()) { selectedVersion = ""; return; } - selectedVersion = data; + selectedVersion = version; suggestCurrent(); } diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp index fa55aa68..ff21d010 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp @@ -3,6 +3,8 @@ #include "Application.h" #include "ui/widgets/ProjectItem.h" +#include "net/ApiDownload.h" + #include <Version.h> #include <QtMath> @@ -40,14 +42,16 @@ QVariant ListModel::data(const QModelIndex& index, int role) const return edit; } return pack.description; - } case Qt::DecorationRole: { + } + case Qt::DecorationRole: { if (m_logoMap.contains(pack.logoName)) { return (m_logoMap.value(pack.logoName)); } QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder"); ((ListModel*)this)->requestLogo(pack.logoName, pack.logoUrl); return icon; - } case Qt::UserRole: { + } + case Qt::UserRole: { QVariant v; v.setValue(pack); return v; @@ -68,7 +72,7 @@ QVariant ListModel::data(const QModelIndex& index, int role) const return QVariant(); } -bool ListModel::setData(const QModelIndex &index, const QVariant &value, int role) +bool ListModel::setData(const QModelIndex& index, const QVariant& value, [[maybe_unused]] int role) { int pos = index.row(); if (pos >= modpacks.size() || pos < 0 || !index.isValid()) @@ -102,9 +106,9 @@ void ListModel::requestLogo(QString logo, QString url) return; } - MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FlamePacks", QString("logos/%1").arg(logo.section(".", 0, 0))); + MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FlamePacks", QString("logos/%1").arg(logo)); auto job = new NetJob(QString("Flame Icon Download %1").arg(logo), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job] { @@ -128,7 +132,7 @@ void ListModel::requestLogo(QString logo, QString url) void ListModel::getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback) { if (m_logoMap.contains(logo)) { - callback(APPLICATION->metacache()->resolveEntry("FlamePacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath()); + callback(APPLICATION->metacache()->resolveEntry("FlamePacks", QString("logos/%1").arg(logo))->getFullPath()); } else { requestLogo(logo, logoUrl); } @@ -139,7 +143,7 @@ Qt::ItemFlags ListModel::flags(const QModelIndex& index) const return QAbstractListModel::flags(index); } -bool ListModel::canFetchMore(const QModelIndex& parent) const +bool ListModel::canFetchMore([[maybe_unused]] const QModelIndex& parent) const { return searchState == CanPossiblyFetchMore; } @@ -171,7 +175,7 @@ void ListModel::performPaginatedSearch() .arg(currentSearchTerm) .arg(currentSort + 1); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response)); jobPtr = netJob; jobPtr->start(); QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::searchRequestFinished); diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index cef26bb6..ead79c6c 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -42,9 +42,11 @@ #include "FlameModel.h" #include "InstanceImportTask.h" #include "Json.h" +#include "modplatform/flame/FlameAPI.h" #include "ui/dialogs/NewInstanceDialog.h" #include "ui/widgets/ProjectItem.h" -#include "modplatform/flame/FlameAPI.h" + +#include "net/ApiDownload.h" static FlameAPI api; @@ -114,7 +116,7 @@ void FlamePage::triggerSearch() listModel->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex()); } -void FlamePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) +void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelIndex prev) { ui->versionSelectionBox->clear(); @@ -132,7 +134,7 @@ void FlamePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) auto netJob = new NetJob(QString("Flame::PackVersions(%1)").arg(current.name), APPLICATION->network()); auto response = std::make_shared<QByteArray>(); int addonId = current.addonId; - netJob->addNetAction(Net::Download::makeByteArray(QString("https://api.curseforge.com/v1/mods/%1/files").arg(addonId), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QString("https://api.curseforge.com/v1/mods/%1/files").arg(addonId), response)); QObject::connect(netJob, &NetJob::succeeded, this, [this, response, addonId, curr] { if (addonId != current.addonId) { @@ -207,17 +209,17 @@ void FlamePage::suggestCurrent() dialog->setSuggestedPack(current.name, new InstanceImportTask(version.downloadUrl, this, std::move(extra_info))); QString editedLogoName; - editedLogoName = "curseforge_" + current.logoName.section(".", 0, 0); + editedLogoName = "curseforge_" + current.logoName; listModel->getLogo(current.logoName, current.logoUrl, [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); } -void FlamePage::onVersionSelectionChanged(QString data) +void FlamePage::onVersionSelectionChanged(QString version) { bool is_blocked = false; ui->versionSelectionBox->currentData().toInt(&is_blocked); - if (data.isNull() || data.isEmpty() || is_blocked) { + if (version.isNull() || version.isEmpty() || is_blocked) { m_selected_version_index = -1; return; } @@ -252,10 +254,8 @@ void FlamePage::updateUi() text += "<br>" + tr(" by ") + authorStrs.join(", "); } - if(current.extraInfoLoaded) { - if (!current.extra.issuesUrl.isEmpty() - || !current.extra.sourceUrl.isEmpty() - || !current.extra.wikiUrl.isEmpty()) { + if (current.extraInfoLoaded) { + if (!current.extra.issuesUrl.isEmpty() || !current.extra.sourceUrl.isEmpty() || !current.extra.wikiUrl.isEmpty()) { text += "<br><br>" + tr("External links:") + "<br>"; } @@ -267,7 +267,6 @@ void FlamePage::updateUi() text += "- " + tr("Source code: <a href=%1>%1</a>").arg(current.extra.sourceUrl) + "<br>"; } - text += "<hr>"; text += api.getModDescription(current.addonId).toUtf8(); diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp index 0fb67c50..2b020c48 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp @@ -32,7 +32,7 @@ void FlameModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonAr auto FlameModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion { return FlameMod::loadDependencyVersions(m, arr); -}; +} auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray { diff --git a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp index 330dd4fb..29b17ab7 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp @@ -37,6 +37,7 @@ #include "Application.h" #include "net/HttpMetaCache.h" #include "net/NetJob.h" +#include "net/ApiDownload.h" #include <Version.h> #include "StringUtils.h" @@ -76,7 +77,7 @@ bool FilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) co return true; } -bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const +bool FilterModel::filterAcceptsRow([[maybe_unused]] int sourceRow, [[maybe_unused]] const QModelIndex& sourceParent) const { return true; } @@ -173,10 +174,10 @@ QVariant ListModel::data(const QModelIndex& index, int role) const return QVariant(); } -void ListModel::fill(ModpackList modpacks) +void ListModel::fill(ModpackList modpacks_) { beginResetModel(); - this->modpacks = modpacks; + this->modpacks = modpacks_; endResetModel(); } @@ -229,9 +230,9 @@ void ListModel::requestLogo(QString file) return; } - MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FTBPacks", QString("logos/%1").arg(file.section(".", 0, 0))); + MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FTBPacks", QString("logos/%1").arg(file)); NetJob* job = new NetJob(QString("FTB Icon Download for %1").arg(file), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/%1").arg(file)), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/%1").arg(file)), entry)); auto fullPath = entry->getFullPath(); QObject::connect(job, &NetJob::finished, this, [this, file, fullPath, job] { @@ -255,7 +256,7 @@ void ListModel::requestLogo(QString file) void ListModel::getLogo(const QString& logo, LogoCallback callback) { if (m_logoMap.contains(logo)) { - callback(APPLICATION->metacache()->resolveEntry("FTBPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath()); + callback(APPLICATION->metacache()->resolveEntry("FTBPacks", QString("logos/%1").arg(logo))->getFullPath()); } else { requestLogo(logo); } diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp index b3f6261f..d4174ff1 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp @@ -234,7 +234,7 @@ void Page::ftbPrivatePackDataDownloadSuccessfully(Modpack pack) privateListModel->addPack(pack); } -void Page::ftbPrivatePackDataDownloadFailed(QString reason, QString packCode) +void Page::ftbPrivatePackDataDownloadFailed([[maybe_unused]] QString reason, QString packCode) { auto reply = QMessageBox::question( this, @@ -247,7 +247,7 @@ void Page::ftbPrivatePackDataDownloadFailed(QString reason, QString packCode) } } -void Page::onPublicPackSelectionChanged(QModelIndex now, QModelIndex prev) +void Page::onPublicPackSelectionChanged(QModelIndex now, [[maybe_unused]] QModelIndex prev) { if(!now.isValid()) { @@ -258,7 +258,7 @@ void Page::onPublicPackSelectionChanged(QModelIndex now, QModelIndex prev) onPackSelectionChanged(&selectedPack); } -void Page::onThirdPartyPackSelectionChanged(QModelIndex now, QModelIndex prev) +void Page::onThirdPartyPackSelectionChanged(QModelIndex now, [[maybe_unused]] QModelIndex prev) { if(!now.isValid()) { @@ -269,7 +269,7 @@ void Page::onThirdPartyPackSelectionChanged(QModelIndex now, QModelIndex prev) onPackSelectionChanged(&selectedPack); } -void Page::onPrivatePackSelectionChanged(QModelIndex now, QModelIndex prev) +void Page::onPrivatePackSelectionChanged(QModelIndex now, [[maybe_unused]] QModelIndex prev) { if(!now.isValid()) { @@ -318,21 +318,21 @@ void Page::onPackSelectionChanged(Modpack* pack) suggestCurrent(); } -void Page::onVersionSelectionItemChanged(QString data) +void Page::onVersionSelectionItemChanged(QString version) { - if(data.isNull() || data.isEmpty()) + if(version.isNull() || version.isEmpty()) { selectedVersion = ""; return; } - selectedVersion = data; + selectedVersion = version; suggestCurrent(); } -void Page::onSortingSelectionChanged(QString data) +void Page::onSortingSelectionChanged(QString sort) { - FilterModel::Sorting toSet = publicFilterModel->getAvailableSortings().value(data); + FilterModel::Sorting toSet = publicFilterModel->getAvailableSortings().value(sort); publicFilterModel->setSorting(toSet); thirdPartyFilterModel->setSorting(toSet); privateFilterModel->setSorting(toSet); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index e0046d88..823439d3 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -42,6 +42,8 @@ #include "minecraft/PackProfile.h" #include "ui/widgets/ProjectItem.h" +#include "net/ApiDownload.h" + #include <QMessageBox> namespace Modrinth { @@ -115,7 +117,7 @@ auto ModpackListModel::data(const QModelIndex& index, int role) const -> QVarian return {}; } -bool ModpackListModel::setData(const QModelIndex &index, const QVariant &value, int role) +bool ModpackListModel::setData(const QModelIndex &index, const QVariant &value, [[maybe_unused]] int role) { int pos = index.row(); if (pos >= modpacks.size() || pos < 0 || !index.isValid()) @@ -142,7 +144,7 @@ void ModpackListModel::performPaginatedSearch() .arg(currentSearchTerm) .arg(currentSort); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchAllUrl), m_all_response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchAllUrl), m_all_response)); QObject::connect(netJob.get(), &NetJob::succeeded, this, [this] { QJsonParseError parse_error_all{}; @@ -194,8 +196,6 @@ static auto sortFromIndex(int index) -> QString case 4: return "updated"; } - - return {}; } void ModpackListModel::searchWithTerm(const QString& term, const int sort) @@ -218,9 +218,7 @@ void ModpackListModel::searchWithTerm(const QString& term, const int sort) void ModpackListModel::getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback) { if (m_logoMap.contains(logo)) { - callback(APPLICATION->metacache() - ->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo.section(".", 0, 0))) - ->getFullPath()); + callback(APPLICATION->metacache()->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo))->getFullPath()); } else { requestLogo(logo, logoUrl); } @@ -232,10 +230,9 @@ void ModpackListModel::requestLogo(QString logo, QString url) return; } - MetaEntryPtr entry = - APPLICATION->metacache()->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo.section(".", 0, 0))); + MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo)); auto job = new NetJob(QString("%1 Icon Download %2").arg(m_parent->debugName()).arg(logo), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job] { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index c71dd903..d5c089b4 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -46,6 +46,8 @@ #include "ui/widgets/ProjectItem.h" +#include "net/ApiDownload.h" + #include <QComboBox> #include <QKeyEvent> #include <QPushButton> @@ -105,7 +107,7 @@ bool ModrinthPage::eventFilter(QObject* watched, QEvent* event) return QObject::eventFilter(watched, event); } -void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev) +void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelIndex prev) { ui->versionSelectionBox->clear(); @@ -127,7 +129,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev) QString id = current.id; - netJob->addNetAction(Net::Download::makeByteArray(QString("%1/project/%2").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QString("%1/project/%2").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); QObject::connect(netJob, &NetJob::succeeded, this, [this, response, id, curr] { if (id != current.id) { @@ -176,7 +178,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev) QString id = current.id; netJob->addNetAction( - Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); + Net::ApiDownload::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); QObject::connect(netJob, &NetJob::succeeded, this, [this, response, id, curr] { if (id != current.id) { @@ -309,9 +311,9 @@ void ModrinthPage::triggerSearch() m_model->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex()); } -void ModrinthPage::onVersionSelectionChanged(QString data) +void ModrinthPage::onVersionSelectionChanged(QString version) { - if (data.isNull() || data.isEmpty()) { + if (version.isNull() || version.isEmpty()) { selectedVersion = ""; return; } diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp index 8aa64989..512eb027 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp @@ -45,7 +45,7 @@ void ModrinthModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJso auto ModrinthModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion { return ::Modrinth::loadDependencyVersions(m, arr); -}; +} auto ModrinthModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray { diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp index f08eb289..5ba804db 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp @@ -38,6 +38,8 @@ #include "BuildConfig.h" #include "Json.h" +#include "net/ApiDownload.h" + #include <QIcon> Technic::ListModel::ListModel(QObject* parent) : QAbstractListModel(parent) {} @@ -116,7 +118,7 @@ void Technic::ListModel::performSearch() QString("%1search?build=%2&q=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD, currentSearchTerm); searchMode = List; } - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response)); jobPtr = netJob; jobPtr->start(); QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::searchRequestFinished); @@ -157,7 +159,7 @@ void Technic::ListModel::searchRequestFinished() pack.logoName = "null"; } else { pack.logoUrl = rawURL; - pack.logoName = rawURL.section(QLatin1Char('/'), -1).section(QLatin1Char('.'), 0, 0); + pack.logoName = rawURL.section(QLatin1Char('/'), -1); } pack.broken = false; newList.append(pack); @@ -179,7 +181,7 @@ void Technic::ListModel::searchRequestFinished() auto iconUrl = Json::requireString(iconObj, "url"); pack.logoUrl = iconUrl; - pack.logoName = iconUrl.section(QLatin1Char('/'), -1).section(QLatin1Char('.'), 0, 0); + pack.logoName = iconUrl.section(QLatin1Char('/'), -1); } else { pack.logoUrl = "null"; pack.logoName = "null"; @@ -254,7 +256,7 @@ void Technic::ListModel::requestLogo(QString logo, QString url) MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("TechnicPacks", QString("logos/%1").arg(logo)); auto job = new NetJob(QString("Technic Icon Download %1").arg(logo), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp index fc678fa2..93390454 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp @@ -49,6 +49,8 @@ #include "Application.h" #include "modplatform/technic/SolderPackManifest.h" +#include "net/ApiDownload.h" + TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget *parent) : QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog) { @@ -100,7 +102,7 @@ void TechnicPage::triggerSearch() { model->searchWithTerm(ui->searchEdit->text()); } -void TechnicPage::onSelectionChanged(QModelIndex first, QModelIndex second) +void TechnicPage::onSelectionChanged(QModelIndex first, [[maybe_unused]] QModelIndex second) { ui->versionSelectionBox->clear(); @@ -129,21 +131,18 @@ void TechnicPage::suggestCurrent() return; } - QString editedLogoName = "technic_" + current.logoName.section(".", 0, 0); - model->getLogo(current.logoName, current.logoUrl, [this, editedLogoName](QString logo) - { - dialog->setSuggestedIconFromFile(logo, editedLogoName); - }); + QString editedLogoName = "technic_" + current.logoName; + model->getLogo(current.logoName, current.logoUrl, + [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); - if (current.metadataLoaded) - { + if (current.metadataLoaded) { metadataLoaded(); return; } auto netJob = makeShared<NetJob>(QString("Technic::PackMeta(%1)").arg(current.name), APPLICATION->network()); QString slug = current.slug; - netJob->addNetAction(Net::Download::makeByteArray(QString("%1modpack/%2?build=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, slug, BuildConfig.TECHNIC_API_BUILD), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QString("%1modpack/%2?build=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, slug, BuildConfig.TECHNIC_API_BUILD), response)); QObject::connect(netJob.get(), &NetJob::succeeded, this, [this, slug] { jobPtr.reset(); @@ -249,7 +248,7 @@ void TechnicPage::metadataLoaded() auto netJob = makeShared<NetJob>(QString("Technic::SolderMeta(%1)").arg(current.name), APPLICATION->network()); auto url = QString("%1/modpack/%2").arg(current.url, current.slug); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(url), response)); QObject::connect(netJob.get(), &NetJob::succeeded, this, &TechnicPage::onSolderLoaded); @@ -319,12 +318,12 @@ void TechnicPage::onSolderLoaded() { metadataLoaded(); } -void TechnicPage::onVersionSelectionChanged(QString data) { - if (data.isNull() || data.isEmpty()) { +void TechnicPage::onVersionSelectionChanged(QString version) { + if (version.isNull() || version.isEmpty()) { selectedVersion = ""; return; } - selectedVersion = data; + selectedVersion = version; selectVersion(); } diff --git a/launcher/ui/themes/CatPack.h b/launcher/ui/themes/CatPack.h index b03a19f0..fdd117a7 100644 --- a/launcher/ui/themes/CatPack.h +++ b/launcher/ui/themes/CatPack.h @@ -52,8 +52,8 @@ class BasicCatPack : public CatPack { public: BasicCatPack(QString id, QString name) : m_id(id), m_name(name) {} BasicCatPack(QString id) : BasicCatPack(id, id) {} - virtual QString id() { return m_id; }; - virtual QString name() { return m_name; }; + virtual QString id() { return m_id; } + virtual QString name() { return m_name; } virtual QString path(); protected: diff --git a/launcher/ui/themes/CustomTheme.cpp b/launcher/ui/themes/CustomTheme.cpp index 177edefa..8e28ba4f 100644 --- a/launcher/ui/themes/CustomTheme.cpp +++ b/launcher/ui/themes/CustomTheme.cpp @@ -148,7 +148,7 @@ static bool writeThemeJson(const QString& path, try { Json::write(rootObj, path); return true; - } catch (const Exception& e) { + } catch ([[maybe_unused]] const Exception& e) { themeWarningLog() << "Failed to write theme json to" << path; return false; } diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index a0fda952..1f03f9ea 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -348,7 +348,7 @@ void InfoFrame::setImage(QPixmap img) } } -void InfoFrame::descriptionEllipsisHandler(QString link) +void InfoFrame::descriptionEllipsisHandler([[maybe_unused]] QString link) { if (!m_current_box) { m_current_box = CustomMessageBox::selectable(this, "", m_description); @@ -359,7 +359,7 @@ void InfoFrame::descriptionEllipsisHandler(QString link) } } -void InfoFrame::licenseEllipsisHandler(QString link) +void InfoFrame::licenseEllipsisHandler([[maybe_unused]] QString link) { if (!m_current_box) { m_current_box = CustomMessageBox::selectable(this, "", m_license); @@ -370,7 +370,7 @@ void InfoFrame::licenseEllipsisHandler(QString link) } } -void InfoFrame::boxClosed(int result) +void InfoFrame::boxClosed([[maybe_unused]] int result) { m_current_box = nullptr; } diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index c94fdd8d..d77e0fa0 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -46,7 +46,7 @@ void JavaSettingsWidget::setupUi() m_verticalLayout = new QVBoxLayout(this); m_verticalLayout->setObjectName(QStringLiteral("verticalLayout")); - m_versionWidget = new VersionSelectWidget(true, this); + m_versionWidget = new VersionSelectWidget(this); m_verticalLayout->addWidget(m_versionWidget); m_horizontalLayout = new QHBoxLayout(); @@ -126,6 +126,7 @@ void JavaSettingsWidget::setupUi() void JavaSettingsWidget::initialize() { m_versionWidget->initialize(APPLICATION->javalist().get()); + m_versionWidget->selectSearch(); m_versionWidget->setResizeOn(2); auto s = APPLICATION->settings(); // Memory diff --git a/launcher/ui/widgets/PageContainer.cpp b/launcher/ui/widgets/PageContainer.cpp index b98c9796..dbbed36a 100644 --- a/launcher/ui/widgets/PageContainer.cpp +++ b/launcher/ui/widgets/PageContainer.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <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 @@ -141,7 +142,12 @@ BasePage* PageContainer::getPage(QString pageId) return m_model->findPageEntryById(pageId); } -const QList<BasePage*> PageContainer::getPages() const +BasePage* PageContainer::selectedPage() const +{ + return m_currentPage; +} + +const QList<BasePage*>& PageContainer::getPages() const { return m_model->pages(); } diff --git a/launcher/ui/widgets/PageContainer.h b/launcher/ui/widgets/PageContainer.h index ad74d43a..eac59723 100644 --- a/launcher/ui/widgets/PageContainer.h +++ b/launcher/ui/widgets/PageContainer.h @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield <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 @@ -78,9 +79,10 @@ public: return false; } - virtual bool selectPage(QString pageId) override; + bool selectPage(QString pageId) override; + BasePage* selectedPage() const override; BasePage* getPage(QString pageId) override; - const QList<BasePage*> getPages() const; + const QList<BasePage*>& getPages() const; void refreshContainer() override; virtual void setParentContainer(BasePageContainer * container) diff --git a/launcher/ui/widgets/PageContainer_p.h b/launcher/ui/widgets/PageContainer_p.h index da1a66f4..8bf26618 100644 --- a/launcher/ui/widgets/PageContainer_p.h +++ b/launcher/ui/widgets/PageContainer_p.h @@ -103,6 +103,8 @@ public: setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding); setItemDelegate(new PageViewDelegate(this)); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + // Adjust margins when using Breeze theme + setProperty("_kde_side_panel_view", true); } virtual QSize sizeHint() const diff --git a/launcher/ui/widgets/SubTaskProgressBar.cpp b/launcher/ui/widgets/SubTaskProgressBar.cpp index 84ea5f20..96f4fe24 100644 --- a/launcher/ui/widgets/SubTaskProgressBar.cpp +++ b/launcher/ui/widgets/SubTaskProgressBar.cpp @@ -27,7 +27,7 @@ unique_qobject_ptr<SubTaskProgressBar> SubTaskProgressBar::create(QWidget* paren } SubTaskProgressBar::SubTaskProgressBar(QWidget* parent) - : ui(new Ui::SubTaskProgressBar) + : QWidget(parent), ui(new Ui::SubTaskProgressBar) { ui->setupUi(this); } diff --git a/launcher/ui/widgets/VariableSizedImageObject.cpp b/launcher/ui/widgets/VariableSizedImageObject.cpp index 991b4a04..3aa3343d 100644 --- a/launcher/ui/widgets/VariableSizedImageObject.cpp +++ b/launcher/ui/widgets/VariableSizedImageObject.cpp @@ -26,6 +26,7 @@ #include "Application.h" #include "net/NetJob.h" +#include "net/ApiDownload.h" enum FormatProperties { ImageData = QTextFormat::UserProperty + 1 }; @@ -97,7 +98,7 @@ void VariableSizedImageObject::loadImage(QTextDocument* doc, const QUrl& source, QString("images/%1").arg(QString(QCryptographicHash::hash(source.toEncoded(), QCryptographicHash::Algorithm::Sha1).toHex()))); auto job = new NetJob(QString("Load Image: %1").arg(source.fileName()), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(source, entry)); + job->addNetAction(Net::ApiDownload::makeCached(source, entry)); auto full_entry_path = entry->getFullPath(); auto source_url = source; diff --git a/launcher/ui/widgets/VersionSelectWidget.cpp b/launcher/ui/widgets/VersionSelectWidget.cpp index a956ddb3..2b22a4a9 100644 --- a/launcher/ui/widgets/VersionSelectWidget.cpp +++ b/launcher/ui/widgets/VersionSelectWidget.cpp @@ -11,10 +11,8 @@ #include "ui/dialogs/CustomMessageBox.h" -VersionSelectWidget::VersionSelectWidget(QWidget* parent) : VersionSelectWidget(false, parent) {} - -VersionSelectWidget::VersionSelectWidget(bool focusSearch, QWidget* parent) - : QWidget(parent), focusSearch(focusSearch) +VersionSelectWidget::VersionSelectWidget(QWidget* parent) + : QWidget(parent) { setObjectName(QStringLiteral("VersionSelectWidget")); verticalLayout = new QVBoxLayout(this); @@ -116,9 +114,6 @@ void VersionSelectWidget::initialize(BaseVersionList *vlist) listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch); - if (focusSearch) - search->setFocus(); - if (!m_vlist->isLoaded()) { loadList(); @@ -210,6 +205,16 @@ void VersionSelectWidget::selectCurrent() } } +void VersionSelectWidget::selectSearch() +{ + search->setFocus(); +} + +VersionListView* VersionSelectWidget::view() +{ + return listView; +} + void VersionSelectWidget::selectRecommended() { auto idx = m_proxyModel->getRecommended(); @@ -233,14 +238,19 @@ BaseVersion::Ptr VersionSelectWidget::selectedVersion() const return variant.value<BaseVersion::Ptr>(); } +void VersionSelectWidget::setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter) +{ + m_proxyModel->setFilter(role, new ContainsFilter(filter)); +} + void VersionSelectWidget::setExactFilter(BaseVersionList::ModelRoles role, QString filter) { m_proxyModel->setFilter(role, new ExactFilter(filter)); } -void VersionSelectWidget::setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter) +void VersionSelectWidget::setExactIfPresentFilter(BaseVersionList::ModelRoles role, QString filter) { - m_proxyModel->setFilter(role, new ContainsFilter(filter)); + m_proxyModel->setFilter(role, new ExactIfPresentFilter(filter)); } void VersionSelectWidget::setFilter(BaseVersionList::ModelRoles role, Filter *filter) diff --git a/launcher/ui/widgets/VersionSelectWidget.h b/launcher/ui/widgets/VersionSelectWidget.h index be4ba768..598e1059 100644 --- a/launcher/ui/widgets/VersionSelectWidget.h +++ b/launcher/ui/widgets/VersionSelectWidget.h @@ -52,7 +52,6 @@ class VersionSelectWidget: public QWidget Q_OBJECT public: explicit VersionSelectWidget(QWidget *parent); - explicit VersionSelectWidget(bool focusSearch = false, QWidget *parent = 0); ~VersionSelectWidget(); //! loads the list if needed. @@ -65,15 +64,19 @@ public: BaseVersion::Ptr selectedVersion() const; void selectRecommended(); void selectCurrent(); + void selectSearch(); + VersionListView* view(); void setCurrentVersion(const QString & version); void setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter); void setExactFilter(BaseVersionList::ModelRoles role, QString filter); + void setExactIfPresentFilter(BaseVersionList::ModelRoles role, QString filter); void setFilter(BaseVersionList::ModelRoles role, Filter *filter); void setEmptyString(QString emptyString); void setEmptyErrorString(QString emptyErrorString); void setEmptyMode(VersionListView::EmptyMode mode); void setResizeOn(int column); + bool eventFilter(QObject* watched, QEvent* event) override; signals: @@ -98,7 +101,6 @@ private: int resizeOnColumn = 0; Task * loadTask; bool preselectedAlready = false; - bool focusSearch; QVBoxLayout *verticalLayout = nullptr; VersionListView *listView = nullptr; diff --git a/launcher/ui/widgets/WideBar.cpp b/launcher/ui/widgets/WideBar.cpp index a77c45fe..3b57ee89 100644 --- a/launcher/ui/widgets/WideBar.cpp +++ b/launcher/ui/widgets/WideBar.cpp @@ -204,8 +204,10 @@ static void copyAction(QAction* from, QAction* to) void WideBar::showVisibilityMenu(QPoint const& position) { - if (!m_bar_menu) + if (!m_bar_menu) { m_bar_menu = std::make_unique<QMenu>(this); + m_bar_menu->setTearOffEnabled(true); + } if (m_menu_state == MenuState::Dirty) { for (auto* old_action : m_bar_menu->actions()) diff --git a/tests/DummyResourceAPI.h b/tests/DummyResourceAPI.h index 0cc90958..35de9515 100644 --- a/tests/DummyResourceAPI.h +++ b/tests/DummyResourceAPI.h @@ -32,7 +32,7 @@ class DummyResourceAPI : public ResourceAPI { } DummyResourceAPI() : ResourceAPI() {} - [[nodiscard]] auto getSortingMethods() const -> QList<SortingMethod> override { return {}; }; + [[nodiscard]] auto getSortingMethods() const -> QList<SortingMethod> override { return {}; } [[nodiscard]] Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&& callbacks) const override { diff --git a/tests/FileSystem_test.cpp b/tests/FileSystem_test.cpp index a41345c2..fb35c874 100644 --- a/tests/FileSystem_test.cpp +++ b/tests/FileSystem_test.cpp @@ -95,10 +95,10 @@ class LinkTask : public Task { emitSucceeded(); } - }; + } FS::create_link *m_lnk; - bool m_useHard = false; + [[maybe_unused]] bool m_useHard = false; bool m_linkRecursive = true; }; @@ -352,7 +352,7 @@ slots: void test_link() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); - auto f = [&folder, this]() + auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); @@ -648,7 +648,7 @@ slots: void test_link_with_max_depth() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); - auto f = [&folder, this]() + auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); diff --git a/tests/GZip_test.cpp b/tests/GZip_test.cpp index 82503d81..ccaa3610 100644 --- a/tests/GZip_test.cpp +++ b/tests/GZip_test.cpp @@ -29,7 +29,7 @@ slots: // initialize random buffer for(int i = 0; i < size; i++) { - random.append((char)idis(eng)); + random.append(static_cast<char>(idis(eng))); } // initialize fibonacci diff --git a/tests/ResourceModel_test.cpp b/tests/ResourceModel_test.cpp index 30353d3f..11629cb8 100644 --- a/tests/ResourceModel_test.cpp +++ b/tests/ResourceModel_test.cpp @@ -40,17 +40,17 @@ class DummyResourceModel : public ResourceModel { DummyResourceModel() : ResourceModel(new DummyResourceAPI) {} ~DummyResourceModel() {} - [[nodiscard]] auto metaEntryBase() const -> QString override { return ""; }; + [[nodiscard]] auto metaEntryBase() const -> QString override { return ""; } - ResourceAPI::SearchArgs createSearchArguments() override { return {}; }; - ResourceAPI::VersionSearchArgs createVersionsArguments(QModelIndex&) override { return {}; }; - ResourceAPI::ProjectInfoArgs createInfoArguments(QModelIndex&) override { return {}; }; + ResourceAPI::SearchArgs createSearchArguments() override { return {}; } + ResourceAPI::VersionSearchArgs createVersionsArguments(QModelIndex&) override { return {}; } + ResourceAPI::ProjectInfoArgs createInfoArguments(QModelIndex&) override { return {}; } QJsonArray documentToArray(QJsonDocument& doc) const override { return doc.object().value("hits").toArray(); } void loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) override { - pack.authors.append({ Json::requireString(obj, "author") }); + pack.authors.append({ Json::requireString(obj, "author"), "" }); pack.description = Json::requireString(obj, "description"); pack.addonId = Json::requireString(obj, "project_id"); } diff --git a/tests/Task_test.cpp b/tests/Task_test.cpp index c59d4bb7..5bc73765 100644 --- a/tests/Task_test.cpp +++ b/tests/Task_test.cpp @@ -19,7 +19,7 @@ class BasicTask : public Task { BasicTask(bool show_debug_log = true) : Task(nullptr, show_debug_log) {} private: - void executeTask() override { emitSucceeded(); }; + void executeTask() override { emitSucceeded(); } }; /* Does nothing. Only used for testing. */ @@ -31,7 +31,7 @@ class BasicTask_MultiStep : public Task { private: auto isMultiStep() const -> bool override { return true; } - void executeTask() override{}; + void executeTask() override {} }; class BigConcurrentTask : public ConcurrentTask { |