Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into current_time

This commit is contained in:
Trial97 2023-11-18 10:45:24 +02:00
commit 9f39e45b28
No known key found for this signature in database
GPG Key ID: 55EF5DA53DB36318
182 changed files with 12640 additions and 1229 deletions

4
.clang-tidy Normal file
View File

@ -0,0 +1,4 @@
Checks:
- modernize-use-using
SystemHeaders: false

View File

@ -24,7 +24,7 @@ jobs:
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
- name: Create backport PRs - name: Create backport PRs
uses: korthout/backport-action@v1.4.0 uses: korthout/backport-action@v2.1.1
with: with:
# Config README: https://github.com/korthout/backport-action#backport-action # Config README: https://github.com/korthout/backport-action#backport-action
pull_description: |- pull_description: |-

View File

@ -37,44 +37,43 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- os: ubuntu-20.04 - os: ubuntu-20.04
qt_ver: 5 qt_ver: 5
- os: ubuntu-20.04 - os: ubuntu-20.04
qt_ver: 6 qt_ver: 6
qt_host: linux qt_host: linux
qt_arch: '' qt_arch: ""
qt_version: '6.2.4' qt_version: "6.2.4"
qt_modules: 'qt5compat qtimageformats' qt_modules: "qt5compat qtimageformats"
qt_tools: '' qt_tools: ""
- os: windows-2022 - os: windows-2022
name: "Windows-MinGW-w64" name: "Windows-MinGW-w64"
msystem: clang64 msystem: clang64
vcvars_arch: 'amd64_x86' vcvars_arch: "amd64_x86"
- os: windows-2022 - os: windows-2022
name: "Windows-MSVC" name: "Windows-MSVC"
msystem: '' msystem: ""
architecture: 'x64' architecture: "x64"
vcvars_arch: 'amd64' vcvars_arch: "amd64"
qt_ver: 6 qt_ver: 6
qt_host: windows qt_host: windows
qt_arch: '' qt_arch: ''
qt_version: '6.5.2' qt_version: '6.6.0'
qt_modules: 'qt5compat qtimageformats' qt_modules: 'qt5compat qtimageformats'
qt_tools: '' qt_tools: ''
- os: windows-2022 - os: windows-2022
name: "Windows-MSVC-arm64" name: "Windows-MSVC-arm64"
msystem: '' msystem: ""
architecture: 'arm64' architecture: "arm64"
vcvars_arch: 'amd64_arm64' vcvars_arch: "amd64_arm64"
qt_ver: 6 qt_ver: 6
qt_host: windows qt_host: windows
qt_arch: 'win64_msvc2019_arm64' qt_arch: 'win64_msvc2019_arm64'
qt_version: '6.5.2' qt_version: '6.6.0'
qt_modules: 'qt5compat qtimageformats' qt_modules: 'qt5compat qtimageformats'
qt_tools: '' qt_tools: ''
@ -84,7 +83,7 @@ jobs:
qt_ver: 6 qt_ver: 6
qt_host: mac qt_host: mac
qt_arch: '' qt_arch: ''
qt_version: '6.5.2' qt_version: '6.6.0'
qt_modules: 'qt5compat qtimageformats' qt_modules: 'qt5compat qtimageformats'
qt_tools: '' qt_tools: ''
@ -93,9 +92,9 @@ jobs:
macosx_deployment_target: 10.13 macosx_deployment_target: 10.13
qt_ver: 5 qt_ver: 5
qt_host: mac qt_host: mac
qt_version: '5.15.2' qt_version: "5.15.2"
qt_modules: '' qt_modules: ""
qt_tools: '' qt_tools: ""
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -115,9 +114,9 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
submodules: 'true' submodules: "true"
- name: 'Setup MSYS2' - name: "Setup MSYS2"
if: runner.os == 'Windows' && matrix.msystem != '' if: runner.os == 'Windows' && matrix.msystem != ''
uses: msys2/setup-msys2@v2 uses: msys2/setup-msys2@v2
with: with:
@ -157,7 +156,7 @@ jobs:
path: '${{ github.workspace }}\.ccache' path: '${{ github.workspace }}\.ccache'
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }} key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
restore-keys: | restore-keys: |
${{ matrix.os }}-mingw-w64-ccache ${{ matrix.os }}-mingw-w64-ccache
- name: Setup ccache (Windows MinGW-w64) - name: Setup ccache (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug' if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
@ -202,35 +201,35 @@ jobs:
if: runner.os == 'Windows' && matrix.architecture == 'arm64' if: runner.os == 'Windows' && matrix.architecture == 'arm64'
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v3
with: with:
aqtversion: '==3.1.*' aqtversion: "==3.1.*"
py7zrversion: '>=0.20.2' py7zrversion: ">=0.20.2"
version: ${{ matrix.qt_version }} version: ${{ matrix.qt_version }}
host: 'windows' host: "windows"
target: 'desktop' target: "desktop"
arch: '' arch: ""
modules: ${{ matrix.qt_modules }} modules: ${{ matrix.qt_modules }}
tools: ${{ matrix.qt_tools }} tools: ${{ matrix.qt_tools }}
cache: ${{ inputs.is_qt_cached }} cache: ${{ inputs.is_qt_cached }}
cache-key-prefix: host-qt-arm64-windows cache-key-prefix: host-qt-arm64-windows
dir: ${{ github.workspace }}\HostQt dir: ${{ github.workspace }}\HostQt
set-env: false set-env: false
- name: Install Qt (macOS, Linux, Qt 6 & Windows MSVC) - name: Install Qt (macOS, Linux, Qt 6 & Windows MSVC)
if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '') if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '')
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v3
with: with:
aqtversion: '==3.1.*' aqtversion: "==3.1.*"
py7zrversion: '>=0.20.2' py7zrversion: ">=0.20.2"
version: ${{ matrix.qt_version }} version: ${{ matrix.qt_version }}
host: ${{ matrix.qt_host }} host: ${{ matrix.qt_host }}
target: 'desktop' target: "desktop"
arch: ${{ matrix.qt_arch }} arch: ${{ matrix.qt_arch }}
modules: ${{ matrix.qt_modules }} modules: ${{ matrix.qt_modules }}
tools: ${{ matrix.qt_tools }} tools: ${{ matrix.qt_tools }}
cache: ${{ inputs.is_qt_cached }} cache: ${{ inputs.is_qt_cached }}
- name: Install MSVC (Windows MSVC) - name: Install MSVC (Windows MSVC)
if: runner.os == 'Windows' # We want this for MinGW builds as well, as we need SignTool if: runner.os == 'Windows' # We want this for MinGW builds as well, as we need SignTool
uses: ilammy/msvc-dev-cmd@v1 uses: ilammy/msvc-dev-cmd@v1
with: with:
vsversion: 2022 vsversion: 2022
@ -271,12 +270,12 @@ jobs:
if: runner.os == 'Windows' && matrix.msystem != '' if: runner.os == 'Windows' && matrix.msystem != ''
shell: msys2 {0} shell: msys2 {0}
run: | run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja
- name: Configure CMake (Windows MSVC) - name: Configure CMake (Windows MSVC)
if: runner.os == 'Windows' && matrix.msystem == '' if: runner.os == 'Windows' && matrix.msystem == ''
run: | run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }}
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix) # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
if ("${{ env.CCACHE_VAR }}") if ("${{ env.CCACHE_VAR }}")
{ {
@ -291,7 +290,7 @@ jobs:
- name: Configure CMake (Linux) - name: Configure CMake (Linux)
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: | run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja
## ##
# BUILD # BUILD
@ -390,10 +389,9 @@ jobs:
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
- name: Fetch codesign certificate (Windows) - name: Fetch codesign certificate (Windows)
if: runner.os == 'Windows' if: runner.os == 'Windows'
shell: bash # yes, we are not using MSYS2 or PowerShell here shell: bash # yes, we are not using MSYS2 or PowerShell here
run: | run: |
echo '${{ secrets.WINDOWS_CODESIGN_CERT }}' | base64 --decode > codesign.pfx echo '${{ secrets.WINDOWS_CODESIGN_CERT }}' | base64 --decode > codesign.pfx
@ -403,7 +401,7 @@ jobs:
if (Get-Content ./codesign.pfx){ if (Get-Content ./codesign.pfx){
cd ${{ env.INSTALL_DIR }} cd ${{ env.INSTALL_DIR }}
# We ship the exact same executable for portable and non-portable editions, so signing just once is fine # We ship the exact same executable for portable and non-portable editions, so signing just once is fine
SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_filelink.exe SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_updater.exe prismlauncher_filelink.exe
} else { } else {
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY ":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
} }
@ -495,15 +493,7 @@ jobs:
export LD_LIBRARY_PATH export LD_LIBRARY_PATH
chmod +x AppImageUpdate-x86_64.AppImage chmod +x AppImageUpdate-x86_64.AppImage
./AppImageUpdate-x86_64.AppImage --appimage-extract cp AppImageUpdate-x86_64.AppImage ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin
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" export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-x86_64.AppImage.zsync"
@ -511,7 +501,7 @@ jobs:
export SIGN=1 export SIGN=1
export SIGN_KEY=${{ secrets.GPG_PRIVATE_KEY_ID }} export SIGN_KEY=${{ secrets.GPG_PRIVATE_KEY_ID }}
mkdir -p ~/.gnupg/ mkdir -p ~/.gnupg/
printf "$GPG_PRIVATE_KEY" | base64 --decode > ~/.gnupg/private.key echo "$GPG_PRIVATE_KEY" > ~/.gnupg/private.key
gpg --import ~/.gnupg/private.key gpg --import ~/.gnupg/private.key
else else
echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY
@ -557,14 +547,14 @@ jobs:
if: runner.os == 'Linux' && matrix.qt_ver != 6 if: runner.os == 'Linux' && matrix.qt_ver != 6
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }} name: PrismLauncher-${{ runner.os }}-Qt5-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher.tar.gz path: PrismLauncher.tar.gz
- name: Upload binary tarball (Linux, portable, Qt 5) - name: Upload binary tarball (Linux, portable, Qt 5)
if: runner.os == 'Linux' && matrix.qt_ver != 6 if: runner.os == 'Linux' && matrix.qt_ver != 6
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: PrismLauncher-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }} name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher-portable.tar.gz path: PrismLauncher-portable.tar.gz
- name: Upload binary tarball (Linux, Qt 6) - name: Upload binary tarball (Linux, Qt 6)
@ -611,7 +601,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
if: inputs.build_type == 'Debug' if: inputs.build_type == 'Debug'
with: with:
submodules: 'true' submodules: "true"
- name: Build Flatpak (Linux) - name: Build Flatpak (Linux)
if: inputs.build_type == 'Debug' if: inputs.build_type == 'Debug'
uses: flatpak/flatpak-github-actions/flatpak-builder@v6 uses: flatpak/flatpak-github-actions/flatpak-builder@v6

View File

@ -3,26 +3,25 @@ name: Build Application
on: on:
push: push:
branches-ignore: branches-ignore:
- 'renovate/**' - "renovate/**"
paths-ignore: paths-ignore:
- '**.md' - "**.md"
- '**/LICENSE' - "**/LICENSE"
- 'flake.lock' - "flake.lock"
- 'packages/**' - "packages/**"
- '.github/ISSUE_TEMPLATE/**' - ".github/ISSUE_TEMPLATE/**"
- '.markdownlint**' - ".markdownlint**"
pull_request: pull_request:
paths-ignore: paths-ignore:
- '**.md' - "**.md"
- '**/LICENSE' - "**/LICENSE"
- 'flake.lock' - "flake.lock"
- 'packages/**' - "packages/**"
- '.github/ISSUE_TEMPLATE/**' - ".github/ISSUE_TEMPLATE/**"
- '.markdownlint**' - ".markdownlint**"
workflow_dispatch: workflow_dispatch:
jobs: jobs:
build_debug: build_debug:
name: Build Debug name: Build Debug
uses: ./.github/workflows/build.yml uses: ./.github/workflows/build.yml
@ -34,3 +33,5 @@ jobs:
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }} WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }} WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}

View File

@ -17,6 +17,8 @@ jobs:
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }} WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }} WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
create_release: create_release:
needs: build_release needs: build_release
@ -40,8 +42,8 @@ jobs:
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }} mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux-Qt5*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-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-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
@ -85,8 +87,8 @@ jobs:
draft: true draft: true
prerelease: false prerelease: false
files: | files: |
PrismLauncher-Linux-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-x86_64.AppImage PrismLauncher-Linux-x86_64.AppImage
PrismLauncher-Linux-x86_64.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz

View File

@ -188,8 +188,11 @@ set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_M
# Build platform. # Build platform.
set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.") set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
# Channel list URL # Github repo URL with releases for updater
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.") set(Launcher_UPDATER_GITHUB_REPO "https://github.com/PrismLauncher/PrismLauncher" CACHE STRING "Base github URL for the updater.")
# Name to help updater identify valid artifacts
set(Launcher_BUILD_ARTIFACT "" CACHE STRING "Artifact name to help the updater identify valid artifacts.")
# The metadata server # The metadata server
set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.") set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
@ -245,6 +248,11 @@ set(Launcher_MSA_CLIENT_ID "c36a9fb6-4f2a-41ff-90bd-ae7cc92031eb" CACHE STRING "
# This key was issued specifically for Prism Launcher # This key was issued specifically for Prism Launcher
set(Launcher_CURSEFORGE_API_KEY "$2a$10$wuAJuNZuted3NORVmpgUC.m8sI.pv1tOPKZyBgLFGjxFp/br0lZCC" CACHE STRING "API key for the CurseForge platform") set(Launcher_CURSEFORGE_API_KEY "$2a$10$wuAJuNZuted3NORVmpgUC.m8sI.pv1tOPKZyBgLFGjxFp/br0lZCC" CACHE STRING "API key for the CurseForge platform")
set(Launcher_COMPILER_NAME ${CMAKE_CXX_COMPILER_ID})
set(Launcher_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION})
set(Launcher_COMPILER_TARGET_SYSTEM ${CMAKE_SYSTEM_NAME})
set(Launcher_COMPILER_TARGET_SYSTEM_VERSION ${CMAKE_SYSTEM_VERSION})
set(Launcher_COMPILER_TARGET_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR})
#### Check the current Git commit and branch #### Check the current Git commit and branch
include(GetGitRevisionDescription) include(GetGitRevisionDescription)
@ -339,6 +347,11 @@ add_subdirectory(program_info)
####################################### Install layout ####################################### ####################################### Install layout #######################################
set(Launcher_ENABLE_UPDATER NO) set(Launcher_ENABLE_UPDATER NO)
set(Launcher_BUILD_UPDATER NO)
if (NOT APPLE AND (NOT Launcher_UPDATER_GITHUB_REPO STREQUAL "" AND NOT Launcher_BUILD_ARTIFACT STREQUAL ""))
set(Launcher_BUILD_UPDATER YES)
endif()
if(NOT (UNIX AND APPLE)) if(NOT (UNIX AND APPLE))
# Install "portable.txt" if selected component is "portable" # Install "portable.txt" if selected component is "portable"

View File

@ -18,11 +18,18 @@
</a> </a>
- All downloads and instructions for Prism Launcher can be found on our [Website](https://prismlauncher.org/download). - All downloads and instructions for Prism Launcher can be found on our [Website](https://prismlauncher.org/download).
- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions). - Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) tab (this also includes the pull requests status).
### Development Builds ### Development Builds
There are development builds available [here](https://github.com/PrismLauncher/PrismLauncher/actions). These have debug information in the binaries, so their file sizes are relatively larger. Please understand that these builds are not intended for most users. There may be bugs, and other instabilities. You have been warned.
There are development builds available through:
- [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) (includes builds from pull requests opened by contribuitors)
- [nightly.link](https://nightly.link/PrismLauncher/PrismLauncher/workflows/trigger_builds/develop) (this will always point only to the latest version of develop)
These have debug information in the binaries, so their file sizes are relatively larger.
Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**. Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**.
@ -30,7 +37,7 @@ For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**,
[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=COPR&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=COPR&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher)
These packages are also availiable to all the distributions based on the ones mentioned above. These packages are also available to all the distributions based on the ones mentioned above.
## Community & Support ## Community & Support

View File

@ -33,6 +33,7 @@
* limitations under the License. * limitations under the License.
*/ */
#include <qstringliteral.h>
#include "BuildConfig.h" #include "BuildConfig.h"
#include <QObject> #include <QObject>
@ -59,8 +60,16 @@ Config::Config()
VERSION_MINOR = @Launcher_VERSION_MINOR@; VERSION_MINOR = @Launcher_VERSION_MINOR@;
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@"; BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@";
BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@"; BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@";
UPDATER_BASE = "@Launcher_UPDATER_BASE@"; UPDATER_GITHUB_REPO = "@Launcher_UPDATER_GITHUB_REPO@";
COMPILER_NAME = "@Launcher_COMPILER_NAME@";
COMPILER_VERSION = "@Launcher_COMPILER_VERSION@";
COMPILER_TARGET_SYSTEM = "@Launcher_COMPILER_TARGET_SYSTEM@";
COMPILER_TARGET_SYSTEM_VERSION = "@Launcher_COMPILER_TARGET_SYSTEM_VERSION@";
COMPILER_TARGET_SYSTEM_PROCESSOR = "@Launcher_COMPILER_TARGET_PROCESSOR@";
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@"; MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@"; MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
@ -68,6 +77,8 @@ Config::Config()
if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty()) if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
{ {
UPDATER_ENABLED = true; UPDATER_ENABLED = true;
} else if(!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_ARTIFACT.isEmpty()) {
UPDATER_ENABLED = true;
} }
GIT_COMMIT = "@Launcher_GIT_COMMIT@"; GIT_COMMIT = "@Launcher_GIT_COMMIT@";
@ -89,9 +100,6 @@ Config::Config()
{ {
VERSION_CHANNEL = GIT_REFSPEC; VERSION_CHANNEL = GIT_REFSPEC;
VERSION_CHANNEL.remove("refs/heads/"); VERSION_CHANNEL.remove("refs/heads/");
if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty()) {
UPDATER_ENABLED = true;
}
} }
else if (!GIT_COMMIT.isEmpty()) else if (!GIT_COMMIT.isEmpty())
{ {
@ -136,3 +144,16 @@ QString Config::printableVersionString() const
} }
return vstr; return vstr;
} }
QString Config::compilerID() const
{
if (COMPILER_VERSION.isEmpty())
return COMPILER_NAME;
return QStringLiteral("%1 - %2").arg(COMPILER_NAME).arg(COMPILER_VERSION);
}
QString Config::systemID() const
{
return QStringLiteral("%1 %2 %3").arg(COMPILER_TARGET_SYSTEM, COMPILER_TARGET_SYSTEM_VERSION, COMPILER_TARGET_SYSTEM_PROCESSOR);
}

View File

@ -71,11 +71,29 @@ class Config {
/// A short string identifying this build's platform or distribution. /// A short string identifying this build's platform or distribution.
QString BUILD_PLATFORM; QString BUILD_PLATFORM;
/// A short string identifying this build's valid artifacts int he updater. For example, "lin64" or "win32".
QString BUILD_ARTIFACT;
/// A string containing the build timestamp /// A string containing the build timestamp
QString BUILD_DATE; QString BUILD_DATE;
/// A string identifying the compiler use to build
QString COMPILER_NAME;
/// A string identifying the compiler version used to build
QString COMPILER_VERSION;
/// A string identifying the compiler target system os
QString COMPILER_TARGET_SYSTEM;
/// A String identifying the compiler target system version
QString COMPILER_TARGET_SYSTEM_VERSION;
/// A String identifying the compiler target processor
QString COMPILER_TARGET_SYSTEM_PROCESSOR;
/// URL for the updater's channel /// URL for the updater's channel
QString UPDATER_BASE; QString UPDATER_GITHUB_REPO;
/// The public key used to sign releases for the Sparkle updater appcast /// The public key used to sign releases for the Sparkle updater appcast
QString MAC_SPARKLE_PUB_KEY; QString MAC_SPARKLE_PUB_KEY;
@ -175,6 +193,18 @@ class Config {
* \return The version number in string format (major.minor.revision.build). * \return The version number in string format (major.minor.revision.build).
*/ */
QString printableVersionString() const; QString printableVersionString() const;
/**
* \brief Compiler ID String
* \return a string of the form "Name - Version" of just "Name" if the version is empty
*/
QString compilerID() const;
/**
* \brief System ID String
* \return a string of the form "OS Verison Processor"
*/
QString systemID() const;
}; };
extern const Config BuildConfig; extern const Config BuildConfig;

30
flake.lock generated
View File

@ -21,11 +21,11 @@
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1696343447, "lastModified": 1698882062,
"narHash": "sha256-B2xAZKLkkeRFG5XcHHSXXcP7To9Xzr59KXeZiRf4vdQ=", "narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "c9afaba3dfa4085dbd2ccb38dfade5141e33d9d4", "rev": "8c9fa2545007b49a5db5f650ae91f227672c3877",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -76,11 +76,11 @@
"libnbtplusplus": { "libnbtplusplus": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1690036783, "lastModified": 1699286814,
"narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=", "narHash": "sha256-yy0q+bky80LtK1GWzz7qpM+aAGrOqLuewbid8WT1ilk=",
"owner": "PrismLauncher", "owner": "PrismLauncher",
"repo": "libnbtplusplus", "repo": "libnbtplusplus",
"rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f", "rev": "23b955121b8217c1c348a9ed2483167a6f3ff4ad",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -106,11 +106,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1697009197, "lastModified": 1699343069,
"narHash": "sha256-viVRhBTFT8fPJTb1N3brQIpFZnttmwo3JVKNuWRVc3s=", "narHash": "sha256-s7BBhyLA6MI6FuJgs4F/SgpntHBzz40/qV0xLPW6A1Q=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "01441e14af5e29c9d27ace398e6dd0b293e25a54", "rev": "ec750fd01963ab6b20ee1f0cb488754e8036d89d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -123,11 +123,11 @@
"nixpkgs-lib": { "nixpkgs-lib": {
"locked": { "locked": {
"dir": "lib", "dir": "lib",
"lastModified": 1696019113, "lastModified": 1698611440,
"narHash": "sha256-X3+DKYWJm93DRSdC5M6K5hLqzSya9BjibtBsuARoPco=", "narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "f5892ddac112a1e9b3612c39af1b72987ee5783a", "rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -153,11 +153,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1696846637, "lastModified": 1699271226,
"narHash": "sha256-0hv4kbXxci2+pxhuXlVgftj/Jq79VSmtAyvfabCCtYk=", "narHash": "sha256-8Jt1KW3xTjolD6c6OjJm9USx/jmL+VVmbooADCkdDfU=",
"owner": "cachix", "owner": "cachix",
"repo": "pre-commit-hooks.nix", "repo": "pre-commit-hooks.nix",
"rev": "42e1b6095ef80a51f79595d9951eb38e91c4e6ca", "rev": "ea758da1a6dcde6dc36db348ed690d09b9864128",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -58,6 +58,7 @@
#include "ui/pages/global/APIPage.h" #include "ui/pages/global/APIPage.h"
#include "ui/pages/global/AccountListPage.h" #include "ui/pages/global/AccountListPage.h"
#include "ui/pages/global/CustomCommandsPage.h" #include "ui/pages/global/CustomCommandsPage.h"
#include "ui/pages/global/EnvironmentVariablesPage.h"
#include "ui/pages/global/ExternalToolsPage.h" #include "ui/pages/global/ExternalToolsPage.h"
#include "ui/pages/global/JavaPage.h" #include "ui/pages/global/JavaPage.h"
#include "ui/pages/global/LanguagePage.h" #include "ui/pages/global/LanguagePage.h"
@ -122,6 +123,7 @@
#include <FileSystem.h> #include <FileSystem.h>
#include <LocalPeer.h> #include <LocalPeer.h>
#include <stdlib.h>
#include <sys.h> #include <sys.h>
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
@ -130,9 +132,13 @@
#include "gamemode_client.h" #include "gamemode_client.h"
#endif #endif
#if defined(Q_OS_MAC) && defined(SPARKLE_ENABLED) #if defined(Q_OS_MAC)
#if defined(SPARKLE_ENABLED)
#include "updater/MacSparkleUpdater.h" #include "updater/MacSparkleUpdater.h"
#endif #endif
#else
#include "updater/PrismExternalUpdater.h"
#endif
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
#include "WindowsConsole.h" #include "WindowsConsole.h"
@ -164,6 +170,34 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt
} // namespace } // namespace
std::tuple<QDateTime, QString, QString, QString, QString> read_lock_File(const QString& path)
{
auto contents = QString(FS::read(path));
auto lines = contents.split('\n');
QDateTime timestamp;
QString from, to, target, data_path;
for (auto line : lines) {
auto index = line.indexOf("=");
if (index < 0)
continue;
auto left = line.left(index);
auto right = line.mid(index + 1);
if (left.toLower() == "timestamp") {
timestamp = QDateTime::fromString(right, Qt::ISODate);
} else if (left.toLower() == "from") {
from = right;
} else if (left.toLower() == "to") {
to = right;
} else if (left.toLower() == "target") {
target = right;
} else if (left.toLower() == "data_path") {
data_path = right;
}
}
return std::make_tuple(timestamp, from, to, target, data_path);
}
Application::Application(int& argc, char** argv) : QApplication(argc, argv) Application::Application(int& argc, char** argv) : QApplication(argc, argv)
{ {
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
@ -296,6 +330,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
.arg(dataPath)); .arg(dataPath));
return; return;
} }
m_dataPath = dataPath;
/* /*
* Establish the mechanism for communication with an already running PrismLauncher that uses the same data path. * Establish the mechanism for communication with an already running PrismLauncher that uses the same data path.
@ -450,11 +485,16 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
} }
{ {
qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT; qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << ", (c) 2022-2023 "
<< qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
qDebug() << "Version : " << BuildConfig.printableVersionString(); qDebug() << "Version : " << BuildConfig.printableVersionString();
qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM; qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM;
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC;
qDebug() << "Compiled for : " << BuildConfig.systemID();
qDebug() << "Compiled by : " << BuildConfig.compilerID();
qDebug() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT;
qDebug() << "Updates Enabled : " << (updaterEnabled() ? "Yes" : "No");
if (adjustedBy.size()) { if (adjustedBy.size()) {
qDebug() << "Work dir before adjustment : " << origcwdPath; qDebug() << "Work dir before adjustment : " << origcwdPath;
qDebug() << "Work dir after adjustment : " << QDir::currentPath(); qDebug() << "Work dir after adjustment : " << QDir::currentPath();
@ -582,6 +622,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("IgnoreJavaCompatibility", false); m_settings->registerSetting("IgnoreJavaCompatibility", false);
m_settings->registerSetting("IgnoreJavaWizard", false); m_settings->registerSetting("IgnoreJavaWizard", false);
// Legacy settings
m_settings->registerSetting("OnlineFixes", false);
// Native library workarounds // Native library workarounds
m_settings->registerSetting("UseNativeOpenAL", false); m_settings->registerSetting("UseNativeOpenAL", false);
m_settings->registerSetting("CustomOpenALPath", ""); m_settings->registerSetting("CustomOpenALPath", "");
@ -601,6 +644,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// Minecraft mods // Minecraft mods
m_settings->registerSetting("ModMetadataDisabled", false); m_settings->registerSetting("ModMetadataDisabled", false);
m_settings->registerSetting("ModDependenciesDisabled", false);
// Minecraft offline player name // Minecraft offline player name
m_settings->registerSetting("LastOfflinePlayerName", ""); m_settings->registerSetting("LastOfflinePlayerName", "");
@ -677,6 +721,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("CloseAfterLaunch", false); m_settings->registerSetting("CloseAfterLaunch", false);
m_settings->registerSetting("QuitAfterGameStop", false); m_settings->registerSetting("QuitAfterGameStop", false);
m_settings->registerSetting("Env", QVariant(QMap<QString, QVariant>()));
// Custom Microsoft Authentication Client ID // Custom Microsoft Authentication Client ID
m_settings->registerSetting("MSAClientIDOverride", ""); m_settings->registerSetting("MSAClientIDOverride", "");
@ -702,6 +748,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_globalSettingsProvider->addPage<JavaPage>(); m_globalSettingsProvider->addPage<JavaPage>();
m_globalSettingsProvider->addPage<LanguagePage>(); m_globalSettingsProvider->addPage<LanguagePage>();
m_globalSettingsProvider->addPage<CustomCommandsPage>(); m_globalSettingsProvider->addPage<CustomCommandsPage>();
m_globalSettingsProvider->addPage<EnvironmentVariablesPage>();
m_globalSettingsProvider->addPage<ProxyPage>(); m_globalSettingsProvider->addPage<ProxyPage>();
m_globalSettingsProvider->addPage<ExternalToolsPage>(); m_globalSettingsProvider->addPage<ExternalToolsPage>();
m_globalSettingsProvider->addPage<AccountListPage>(); m_globalSettingsProvider->addPage<AccountListPage>();
@ -738,15 +785,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
qDebug() << "<> Translations loaded."; qDebug() << "<> Translations loaded.";
} }
// initialize the updater
if (BuildConfig.UPDATER_ENABLED) {
qDebug() << "Initializing updater";
#if defined(Q_OS_MAC) && defined(SPARKLE_ENABLED)
m_updater.reset(new MacSparkleUpdater());
#endif
qDebug() << "<> Updater started.";
}
// Instance icons // Instance icons
{ {
auto setting = APPLICATION->settings()->getSetting("IconsDir"); auto setting = APPLICATION->settings()->getSetting("IconsDir");
@ -849,6 +887,107 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
detectLibraries(); detectLibraries();
// check update locks
{
auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log");
auto update_lock = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"));
if (update_lock.exists()) {
auto [timestamp, from, to, target, data_path] = read_lock_File(update_lock.absoluteFilePath());
auto infoMsg = tr("This installation has a update lock file present at: %1\n"
"\n"
"Timestamp: %2\n"
"Updating from version %3 to %4\n"
"Target install path: %5\n"
"Data Path: %6"
"\n"
"This likely means that a update attempt failed. Please ensure your installation is in working order before "
"proceeding.\n"
"Check the Prism Launcher updater log at: \n"
"%7\n"
"for details on the last update attempt.\n"
"\n"
"To delete this lock and proceed select \"Ignore\" below.")
.arg(update_lock.absoluteFilePath())
.arg(timestamp.toString(Qt::ISODate), from, to, target, data_path)
.arg(update_log_path);
auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update In Progress"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort);
msgBox.setDefaultButton(QMessageBox::Abort);
msgBox.setModal(true);
msgBox.setDetailedText(FS::read(update_log_path));
msgBox.setMinimumWidth(460);
msgBox.adjustSize();
auto res = msgBox.exec();
switch (res) {
case QMessageBox::Ignore: {
FS::deletePath(update_lock.absoluteFilePath());
break;
}
case QMessageBox::Abort:
[[fallthrough]];
default: {
qDebug() << "Exiting because update lockfile is present";
QMetaObject::invokeMethod(
this, []() { exit(1); }, Qt::QueuedConnection);
return;
}
}
}
auto update_fail_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.fail"));
if (update_fail_marker.exists()) {
auto infoMsg = tr("An update attempt failed\n"
"\n"
"Please ensure your installation is in working order before "
"proceeding.\n"
"Check the Prism Launcher updater log at: \n"
"%1\n"
"for details on the last update attempt.")
.arg(update_log_path);
auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Failed"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort);
msgBox.setDefaultButton(QMessageBox::Abort);
msgBox.setModal(true);
msgBox.setDetailedText(FS::read(update_log_path));
msgBox.setMinimumWidth(460);
msgBox.adjustSize();
auto res = msgBox.exec();
switch (res) {
case QMessageBox::Ignore: {
FS::deletePath(update_fail_marker.absoluteFilePath());
break;
}
case QMessageBox::Abort:
[[fallthrough]];
default: {
qDebug() << "Exiting because update lockfile is present";
QMetaObject::invokeMethod(
this, []() { exit(1); }, Qt::QueuedConnection);
return;
}
}
}
auto update_success_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.success"));
if (update_success_marker.exists()) {
auto infoMsg = tr("Update succeeded\n"
"\n"
"You are now running %1 .\n"
"Check the Prism Launcher updater log at: \n"
"%1\n"
"for details.")
.arg(BuildConfig.printableVersionString())
.arg(update_log_path);
auto msgBox = new QMessageBox(QMessageBox::Information, tr("Update Succeeded"), infoMsg, QMessageBox::Ok);
msgBox->setDefaultButton(QMessageBox::Ok);
msgBox->setDetailedText(FS::read(update_log_path));
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->setMinimumWidth(460);
msgBox->adjustSize();
msgBox->open();
FS::deletePath(update_success_marker.absoluteFilePath());
}
}
if (createSetupWizard()) { if (createSetupWizard()) {
return; return;
} }
@ -917,6 +1056,26 @@ bool Application::createSetupWizard()
return false; return false;
} }
bool Application::updaterEnabled()
{
#if defined(Q_OS_MAC)
return BuildConfig.UPDATER_ENABLED;
#else
return BuildConfig.UPDATER_ENABLED && QFileInfo(FS::PathCombine(m_rootPath, updaterBinaryName())).isFile();
#endif
}
QString Application::updaterBinaryName()
{
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
#if defined Q_OS_WIN32
exe_name.append(".exe");
#else
exe_name.prepend("bin/");
#endif
return exe_name;
}
bool Application::event(QEvent* event) bool Application::event(QEvent* event)
{ {
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
@ -985,6 +1144,20 @@ void Application::performMainStartupAction()
showMainWindow(false); showMainWindow(false);
qDebug() << "<> Main window shown."; qDebug() << "<> Main window shown.";
} }
// initialize the updater
if (updaterEnabled()) {
qDebug() << "Initializing updater";
#ifdef Q_OS_MAC
#if defined(SPARKLE_ENABLED)
m_updater.reset(new MacSparkleUpdater());
#endif
#else
m_updater.reset(new PrismExternalUpdater(m_mainWindow, m_rootPath, m_dataPath));
#endif
qDebug() << "<> Updater started.";
}
if (!m_urlsToImport.isEmpty()) { if (!m_urlsToImport.isEmpty()) {
qDebug() << "<> Importing from url:" << m_urlsToImport; qDebug() << "<> Importing from url:" << m_urlsToImport;
m_mainWindow->processURLs(m_urlsToImport); m_mainWindow->processURLs(m_urlsToImport);

View File

@ -159,6 +159,9 @@ class Application : public QApplication {
/// this is the root of the 'installation'. Used for automatic updates /// this is the root of the 'installation'. Used for automatic updates
const QString& root() { return m_rootPath; } const QString& root() { return m_rootPath; }
/// the data path the application is using
const QString& dataRoot() { return m_dataPath; }
bool isPortable() { return m_portable; } bool isPortable() { return m_portable; }
const Capabilities capabilities() { return m_capabilities; } const Capabilities capabilities() { return m_capabilities; }
@ -179,6 +182,9 @@ class Application : public QApplication {
int suitableMaxMem(); int suitableMaxMem();
bool updaterEnabled();
QString updaterBinaryName();
QUrl normalizeImportUrl(QString const& url); QUrl normalizeImportUrl(QString const& url);
signals: signals:
@ -244,6 +250,7 @@ class Application : public QApplication {
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers; QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
QString m_rootPath; QString m_rootPath;
QString m_dataPath;
Status m_status = Application::StartingUp; Status m_status = Application::StartingUp;
Capabilities m_capabilities; Capabilities m_capabilities;
bool m_portable = false; bool m_portable = false;

View File

@ -390,7 +390,7 @@ QString BaseInstance::name() const
QString BaseInstance::windowTitle() const QString BaseInstance::windowTitle() const
{ {
return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name().replace(QRegularExpression("\\s+"), " "); return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name();
} }
// FIXME: why is this here? move it to MinecraftInstance!!! // FIXME: why is this here? move it to MinecraftInstance!!!

View File

@ -64,7 +64,7 @@ class LaunchTask;
class BaseInstance; class BaseInstance;
// pointer for lazy people // pointer for lazy people
typedef std::shared_ptr<BaseInstance> InstancePtr; using InstancePtr = std::shared_ptr<BaseInstance>;
/*! /*!
* \brief Base class for instances. * \brief Base class for instances.

View File

@ -51,7 +51,7 @@ class BaseVersionList : public QAbstractListModel {
ArchitectureRole, ArchitectureRole,
SortRole SortRole
}; };
typedef QList<int> RoleList; using RoleList = QList<int>;
explicit BaseVersionList(QObject* parent = 0); explicit BaseVersionList(QObject* parent = 0);

View File

@ -139,6 +139,7 @@ set(NET_SOURCES
net/HeaderProxy.h net/HeaderProxy.h
net/RawHeaderProxy.h net/RawHeaderProxy.h
net/ApiHeaderProxy.h net/ApiHeaderProxy.h
net/StaticHeaderProxy.h
net/ApiDownload.h net/ApiDownload.h
net/ApiDownload.cpp net/ApiDownload.cpp
net/ApiUpload.cpp net/ApiUpload.cpp
@ -181,6 +182,11 @@ set(MAC_UPDATE_SOURCES
updater/MacSparkleUpdater.mm updater/MacSparkleUpdater.mm
) )
set(PRISM_UPDATE_SOURCES
updater/PrismExternalUpdater.h
updater/PrismExternalUpdater.cpp
)
# Backend for the news bar... there's usually no news. # Backend for the news bar... there's usually no news.
set(NEWS_SOURCES set(NEWS_SOURCES
# News System # News System
@ -579,6 +585,63 @@ set(LINKEXE_SOURCES
DesktopServices.cpp DesktopServices.cpp
) )
set(PRISMUPDATER_SOURCES
updater/prismupdater/PrismUpdater.h
updater/prismupdater/PrismUpdater.cpp
updater/prismupdater/UpdaterDialogs.h
updater/prismupdater/UpdaterDialogs.cpp
updater/prismupdater/GitHubRelease.h
updater/prismupdater/GitHubRelease.cpp
Json.h
Json.cpp
FileSystem.h
FileSystem.cpp
StringUtils.h
StringUtils.cpp
DesktopServices.h
DesktopServices.cpp
Version.h
Version.cpp
Markdown.h
Markdown.cpp
# Zip
MMCZip.h
MMCZip.cpp
# Time
MMCTime.h
MMCTime.cpp
net/ByteArraySink.h
net/ChecksumValidator.h
net/Download.cpp
net/Download.h
net/FileSink.cpp
net/FileSink.h
net/HttpMetaCache.cpp
net/HttpMetaCache.h
net/Logging.h
net/Logging.cpp
net/NetAction.h
net/NetRequest.cpp
net/NetRequest.h
net/NetJob.cpp
net/NetJob.h
net/NetUtils.h
net/Sink.h
net/Validator.h
net/HeaderProxy.h
net/RawHeaderProxy.h
ui/dialogs/ProgressDialog.cpp
ui/dialogs/ProgressDialog.h
ui/widgets/SubTaskProgressBar.h
ui/widgets/SubTaskProgressBar.cpp
)
######## Logging categories ######## ######## Logging categories ########
ecm_qt_declare_logging_category(CORE_SOURCES ecm_qt_declare_logging_category(CORE_SOURCES
@ -675,6 +738,8 @@ set(LOGIC_SOURCES
if(APPLE AND Launcher_ENABLE_UPDATER) if(APPLE AND Launcher_ENABLE_UPDATER)
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES}) set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES})
else()
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${PRISM_UPDATE_SOURCES})
endif() endif()
SET(LAUNCHER_SOURCES SET(LAUNCHER_SOURCES
@ -824,6 +889,8 @@ SET(LAUNCHER_SOURCES
ui/pages/global/AccountListPage.h ui/pages/global/AccountListPage.h
ui/pages/global/CustomCommandsPage.cpp ui/pages/global/CustomCommandsPage.cpp
ui/pages/global/CustomCommandsPage.h ui/pages/global/CustomCommandsPage.h
ui/pages/global/EnvironmentVariablesPage.cpp
ui/pages/global/EnvironmentVariablesPage.h
ui/pages/global/ExternalToolsPage.cpp ui/pages/global/ExternalToolsPage.cpp
ui/pages/global/ExternalToolsPage.h ui/pages/global/ExternalToolsPage.h
ui/pages/global/JavaPage.cpp ui/pages/global/JavaPage.cpp
@ -975,6 +1042,8 @@ SET(LAUNCHER_SOURCES
ui/widgets/Common.h ui/widgets/Common.h
ui/widgets/CustomCommands.cpp ui/widgets/CustomCommands.cpp
ui/widgets/CustomCommands.h ui/widgets/CustomCommands.h
ui/widgets/EnvironmentVariables.cpp
ui/widgets/EnvironmentVariables.h
ui/widgets/DropLabel.cpp ui/widgets/DropLabel.cpp
ui/widgets/DropLabel.h ui/widgets/DropLabel.h
ui/widgets/FocusLineEdit.cpp ui/widgets/FocusLineEdit.cpp
@ -1033,6 +1102,15 @@ SET(LAUNCHER_SOURCES
ui/instanceview/VisualGroup.h ui/instanceview/VisualGroup.h
) )
if (NOT Apple)
set(LAUNCHER_SOURCES
${LAUNCHER_SOURCES}
ui/dialogs/UpdateAvailableDialog.h
ui/dialogs/UpdateAvailableDialog.cpp
)
endif()
if(WIN32) if(WIN32)
set(LAUNCHER_SOURCES set(LAUNCHER_SOURCES
WindowsConsole.cpp WindowsConsole.cpp
@ -1076,6 +1154,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/modplatform/technic/TechnicPage.ui ui/pages/modplatform/technic/TechnicPage.ui
ui/widgets/InstanceCardWidget.ui ui/widgets/InstanceCardWidget.ui
ui/widgets/CustomCommands.ui ui/widgets/CustomCommands.ui
ui/widgets/EnvironmentVariables.ui
ui/widgets/InfoFrame.ui ui/widgets/InfoFrame.ui
ui/widgets/ModFilterWidget.ui ui/widgets/ModFilterWidget.ui
ui/widgets/SubTaskProgressBar.ui ui/widgets/SubTaskProgressBar.ui
@ -1103,6 +1182,14 @@ qt_wrap_ui(LAUNCHER_UI
ui/dialogs/ChooseProviderDialog.ui ui/dialogs/ChooseProviderDialog.ui
) )
qt_wrap_ui(PRISM_UPDATE_UI
ui/dialogs/UpdateAvailableDialog.ui
)
if (NOT Apple)
set (LAUNCHER_UI ${LAUNCHER_UI} ${PRISM_UPDATE_UI})
endif()
qt_add_resources(LAUNCHER_RESOURCES qt_add_resources(LAUNCHER_RESOURCES
resources/backgrounds/backgrounds.qrc resources/backgrounds/backgrounds.qrc
resources/multimc/multimc.qrc resources/multimc/multimc.qrc
@ -1119,6 +1206,12 @@ qt_add_resources(LAUNCHER_RESOURCES
../${Launcher_Branding_LogoQRC} ../${Launcher_Branding_LogoQRC}
) )
qt_wrap_ui(PRISMUPDATER_UI
updater/prismupdater/SelectReleaseDialog.ui
ui/widgets/SubTaskProgressBar.ui
ui/dialogs/ProgressDialog.ui
)
######## Windows resource files ######## ######## Windows resource files ########
if(WIN32) if(WIN32)
set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC}) set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC})
@ -1137,6 +1230,7 @@ set_project_warnings(Launcher_logic
"${Launcher_GCC_WARNINGS}") "${Launcher_GCC_WARNINGS}")
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION) target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
target_link_libraries(Launcher_logic target_link_libraries(Launcher_logic
systeminfo systeminfo
Launcher_murmur2 Launcher_murmur2
@ -1218,7 +1312,45 @@ install(TARGETS ${Launcher_Name}
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
) )
if(WIN32) if(Launcher_BUILD_UPDATER)
# Updater
add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI})
target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(prism_updater_logic
QuaZip::QuaZip
${ZLIB_LIBRARIES}
systeminfo
BuildConfig
ghcFilesystem::ghc_filesystem
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Network
${Launcher_QT_LIBS}
cmark::cmark
Katabasis
)
add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp)
target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest)
target_link_libraries("${Launcher_Name}_updater" prism_updater_logic)
if(DEFINED Launcher_APP_BINARY_NAME)
set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater")
endif()
if(DEFINED Launcher_BINARY_RPATH)
SET_TARGET_PROPERTIES("${Launcher_Name}_updater" PROPERTIES INSTALL_RPATH "${Launcher_BINARY_RPATH}")
endif()
install(TARGETS "${Launcher_Name}_updater"
BUNDLE DESTINATION "." COMPONENT Runtime
LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime
RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
)
endif()
if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
# File link
add_library(filelink_logic STATIC ${LINKEXE_SOURCES}) add_library(filelink_logic STATIC ${LINKEXE_SOURCES})
set_project_warnings(filelink_logic set_project_warnings(filelink_logic
"${Launcher_MSVC_WARNINGS}" "${Launcher_MSVC_WARNINGS}"
@ -1237,7 +1369,7 @@ if(WIN32)
${Launcher_QT_LIBS} ${Launcher_QT_LIBS}
) )
add_executable("${Launcher_Name}_filelink" WIN32 filelink/main.cpp) add_executable("${Launcher_Name}_filelink" WIN32 filelink/filelink_main.cpp)
target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest) target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest)

View File

@ -123,26 +123,35 @@ namespace fs = ghc::filesystem;
#if defined(__MINGW32__) #if defined(__MINGW32__)
typedef struct _DUPLICATE_EXTENTS_DATA { struct _DUPLICATE_EXTENTS_DATA {
HANDLE FileHandle; HANDLE FileHandle;
LARGE_INTEGER SourceFileOffset; LARGE_INTEGER SourceFileOffset;
LARGE_INTEGER TargetFileOffset; LARGE_INTEGER TargetFileOffset;
LARGE_INTEGER ByteCount; LARGE_INTEGER ByteCount;
} DUPLICATE_EXTENTS_DATA, *PDUPLICATE_EXTENTS_DATA; };
typedef struct _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER { using DUPLICATE_EXTENTS_DATA = _DUPLICATE_EXTENTS_DATA;
using PDUPLICATE_EXTENTS_DATA = _DUPLICATE_EXTENTS_DATA*;
struct _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER {
WORD ChecksumAlgorithm; // Checksum algorithm. e.g. CHECKSUM_TYPE_UNCHANGED, CHECKSUM_TYPE_NONE, CHECKSUM_TYPE_CRC32 WORD ChecksumAlgorithm; // Checksum algorithm. e.g. CHECKSUM_TYPE_UNCHANGED, CHECKSUM_TYPE_NONE, CHECKSUM_TYPE_CRC32
WORD Reserved; // Must be 0 WORD Reserved; // Must be 0
DWORD Flags; // FSCTL_INTEGRITY_FLAG_xxx DWORD Flags; // FSCTL_INTEGRITY_FLAG_xxx
DWORD ChecksumChunkSizeInBytes; DWORD ChecksumChunkSizeInBytes;
DWORD ClusterSizeInBytes; DWORD ClusterSizeInBytes;
} FSCTL_GET_INTEGRITY_INFORMATION_BUFFER, *PFSCTL_GET_INTEGRITY_INFORMATION_BUFFER; };
typedef struct _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER { using FSCTL_GET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER;
using PFSCTL_GET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER*;
struct _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER {
WORD ChecksumAlgorithm; // Checksum algorithm. e.g. CHECKSUM_TYPE_UNCHANGED, CHECKSUM_TYPE_NONE, CHECKSUM_TYPE_CRC32 WORD ChecksumAlgorithm; // Checksum algorithm. e.g. CHECKSUM_TYPE_UNCHANGED, CHECKSUM_TYPE_NONE, CHECKSUM_TYPE_CRC32
WORD Reserved; // Must be 0 WORD Reserved; // Must be 0
DWORD Flags; // FSCTL_INTEGRITY_FLAG_xxx DWORD Flags; // FSCTL_INTEGRITY_FLAG_xxx
} FSCTL_SET_INTEGRITY_INFORMATION_BUFFER, *PFSCTL_SET_INTEGRITY_INFORMATION_BUFFER; };
using FSCTL_SET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER;
using PFSCTL_SET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER*;
#endif #endif
@ -194,6 +203,40 @@ void write(const QString& filename, const QByteArray& data)
} }
} }
void appendSafe(const QString& filename, const QByteArray& data)
{
ensureExists(QFileInfo(filename).dir());
QByteArray buffer;
try {
buffer = read(filename);
} catch (FileSystemException&) {
buffer = QByteArray();
}
buffer.append(data);
QSaveFile file(filename);
if (!file.open(QSaveFile::WriteOnly)) {
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
}
if (buffer.size() != file.write(buffer)) {
throw FileSystemException("Error writing data to " + filename + ": " + file.errorString());
}
if (!file.commit()) {
throw FileSystemException("Error while committing data to " + filename + ": " + file.errorString());
}
}
void append(const QString& filename, const QByteArray& data)
{
ensureExists(QFileInfo(filename).dir());
QFile file(filename);
if (!file.open(QFile::Append)) {
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
}
if (data.size() != file.write(data)) {
throw FileSystemException("Error writing data to " + filename + ": " + file.errorString());
}
}
QByteArray read(const QString& filename) QByteArray read(const QString& filename)
{ {
QFile file(filename); QFile file(filename);
@ -238,6 +281,28 @@ bool ensureFolderPathExists(QString foldernamepath)
return success; return success;
} }
bool copyFileAttributes(QString src, QString dst)
{
#ifdef Q_OS_WIN32
auto attrs = GetFileAttributesW(src.toStdWString().c_str());
if (attrs == INVALID_FILE_ATTRIBUTES)
return false;
return SetFileAttributesW(dst.toStdWString().c_str(), attrs);
#endif
return true;
}
// needs folders to exists
void copyFolderAttributes(QString src, QString dst, QString relative)
{
auto path = PathCombine(src, relative);
QDir dsrc(src);
while ((path = QFileInfo(path).path()).length() >= src.length()) {
auto dst_path = PathCombine(dst, dsrc.relativeFilePath(path));
copyFileAttributes(path, dst_path);
}
}
/** /**
* @brief Copies a directory and it's contents from src to dest * @brief Copies a directory and it's contents from src to dest
* @param offset subdirectory form src to copy to dest * @param offset subdirectory form src to copy to dest
@ -265,6 +330,9 @@ bool copy::operator()(const QString& offset, bool dryRun)
if (!m_followSymlinks) if (!m_followSymlinks)
opt |= copy_opts::copy_symlinks; opt |= copy_opts::copy_symlinks;
if (m_overwrite)
opt |= copy_opts::overwrite_existing;
// Function that'll do the actual copying // Function that'll do the actual copying
auto copy_file = [&](QString src_path, QString relative_dst_path) { auto copy_file = [&](QString src_path, QString relative_dst_path) {
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist)) if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
@ -273,6 +341,9 @@ bool copy::operator()(const QString& offset, bool dryRun)
auto dst_path = PathCombine(dst, relative_dst_path); auto dst_path = PathCombine(dst, relative_dst_path);
if (!dryRun) { if (!dryRun) {
ensureFilePathExists(dst_path); ensureFilePathExists(dst_path);
#ifdef Q_OS_WIN32
copyFolderAttributes(src, dst, relative_dst_path);
#endif
fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err); fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err);
} }
if (err) { if (err) {
@ -872,6 +943,8 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
<< "\n"; << "\n";
stream << "Type=Application" stream << "Type=Application"
<< "\n"; << "\n";
stream << "Categories=Game;ActionGame;AdventureGame;Simulation"
<< "\n";
stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n"; stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n";
stream << "Name=" << name.toLocal8Bit() << "\n"; stream << "Name=" << name.toLocal8Bit() << "\n";
if (!icon.isEmpty()) { if (!icon.isEmpty()) {

View File

@ -61,6 +61,16 @@ class FileSystemException : public ::Exception {
*/ */
void write(const QString& filename, const QByteArray& data); void write(const QString& filename, const QByteArray& data);
/**
* append data to a file safely
*/
void appendSafe(const QString& filename, const QByteArray& data);
/**
* append data to a file
*/
void append(const QString& filename, const QByteArray& data);
/** /**
* read data from a file safely\ * read data from a file safely\
*/ */
@ -109,6 +119,11 @@ class copy : public QObject {
m_whitelist = whitelist; m_whitelist = whitelist;
return *this; return *this;
} }
copy& overwrite(const bool overwrite)
{
m_overwrite = overwrite;
return *this;
}
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
@ -128,6 +143,7 @@ class copy : public QObject {
bool m_followSymlinks = true; bool m_followSymlinks = true;
const IPathMatcher* m_matcher = nullptr; const IPathMatcher* m_matcher = nullptr;
bool m_whitelist = false; bool m_whitelist = false;
bool m_overwrite = false;
QDir m_src; QDir m_src;
QDir m_dst; QDir m_dst;
qsizetype m_copied; qsizetype m_copied;

View File

@ -2,6 +2,7 @@
/* /*
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -237,8 +238,11 @@ GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
return GroupId(); return GroupId();
} }
void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name) void InstanceList::setInstanceGroup(const InstanceId& id, GroupId name)
{ {
if (name.isEmpty() && !name.isNull())
name = QString();
auto inst = getInstanceById(id); auto inst = getInstanceById(id);
if (!inst) { if (!inst) {
qDebug() << "Attempt to set a null instance's group"; qDebug() << "Attempt to set a null instance's group";
@ -249,6 +253,7 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
auto iter = m_instanceGroupIndex.find(inst->id()); auto iter = m_instanceGroupIndex.find(inst->id());
if (iter != m_instanceGroupIndex.end()) { if (iter != m_instanceGroupIndex.end()) {
if (*iter != name) { if (*iter != name) {
decreaseGroupCount(*iter);
*iter = name; *iter = name;
changed = true; changed = true;
} }
@ -258,7 +263,7 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
} }
if (changed) { if (changed) {
m_groupNameCache.insert(name); increaseGroupCount(name);
auto idx = getInstIndex(inst.get()); auto idx = getInstIndex(inst.get());
emit dataChanged(index(idx), index(idx), { GroupRole }); emit dataChanged(index(idx), index(idx), { GroupRole });
saveGroupList(); saveGroupList();
@ -267,29 +272,55 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
QStringList InstanceList::getGroups() QStringList InstanceList::getGroups()
{ {
return m_groupNameCache.values(); return m_groupNameCache.keys();
} }
void InstanceList::deleteGroup(const QString& name) void InstanceList::deleteGroup(const GroupId& name)
{ {
m_groupNameCache.remove(name);
m_collapsedGroups.remove(name);
bool removed = false; bool removed = false;
qDebug() << "Delete group" << name; qDebug() << "Delete group" << name;
for (auto& instance : m_instances) { for (auto& instance : m_instances) {
const auto& instID = instance->id(); const QString& instID = instance->id();
auto instGroupName = getInstanceGroup(instID); const QString instGroupName = getInstanceGroup(instID);
if (instGroupName == name) { if (instGroupName == name) {
m_instanceGroupIndex.remove(instID); m_instanceGroupIndex.remove(instID);
qDebug() << "Remove" << instID << "from group" << name; qDebug() << "Remove" << instID << "from group" << name;
removed = true; removed = true;
auto idx = getInstIndex(instance.get()); auto idx = getInstIndex(instance.get());
if (idx > 0) { if (idx >= 0)
emit dataChanged(index(idx), index(idx), { GroupRole }); emit dataChanged(index(idx), index(idx), { GroupRole });
}
} }
} }
if (removed) { if (removed)
saveGroupList(); saveGroupList();
}
void InstanceList::renameGroup(const QString& src, const QString& dst)
{
m_groupNameCache.remove(src);
if (m_collapsedGroups.remove(src))
m_collapsedGroups.insert(dst);
bool modified = false;
qDebug() << "Rename group" << src << "to" << dst;
for (auto& instance : m_instances) {
const QString& instID = instance->id();
const QString instGroupName = getInstanceGroup(instID);
if (instGroupName == src) {
m_instanceGroupIndex[instID] = dst;
increaseGroupCount(dst);
qDebug() << "Set" << instID << "group to" << dst;
modified = true;
auto idx = getInstIndex(instance.get());
if (idx >= 0)
emit dataChanged(index(idx), index(idx), { GroupRole });
}
} }
if (modified)
saveGroupList();
} }
bool InstanceList::isGroupCollapsed(const QString& group) bool InstanceList::isGroupCollapsed(const QString& group)
@ -305,12 +336,13 @@ bool InstanceList::trashInstance(const InstanceId& id)
return false; return false;
} }
auto cachedGroupId = m_instanceGroupIndex[id]; QString cachedGroupId = m_instanceGroupIndex[id];
qDebug() << "Will trash instance" << id; qDebug() << "Will trash instance" << id;
QString trashedLoc; QString trashedLoc;
if (m_instanceGroupIndex.remove(id)) { if (m_instanceGroupIndex.remove(id)) {
decreaseGroupCount(cachedGroupId);
saveGroupList(); saveGroupList();
} }
@ -348,7 +380,7 @@ void InstanceList::undoTrashInstance()
QFile(top.trashPath).rename(top.polyPath); QFile(top.trashPath).rename(top.polyPath);
m_instanceGroupIndex[top.id] = top.groupName; m_instanceGroupIndex[top.id] = top.groupName;
m_groupNameCache.insert(top.groupName); increaseGroupCount(top.groupName);
saveGroupList(); saveGroupList();
emit instancesChanged(); emit instancesChanged();
@ -362,7 +394,10 @@ void InstanceList::deleteInstance(const InstanceId& id)
return; return;
} }
QString cachedGroupId = m_instanceGroupIndex[id];
if (m_instanceGroupIndex.remove(id)) { if (m_instanceGroupIndex.remove(id)) {
decreaseGroupCount(cachedGroupId);
saveGroupList(); saveGroupList();
} }
@ -610,6 +645,25 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id)
return inst; return inst;
} }
void InstanceList::increaseGroupCount(const QString& group)
{
if (group.isEmpty())
return;
++m_groupNameCache[group];
}
void InstanceList::decreaseGroupCount(const QString& group)
{
if (group.isEmpty())
return;
if (--m_groupNameCache[group] < 1) {
m_groupNameCache.remove(group);
m_collapsedGroups.remove(group);
}
}
void InstanceList::saveGroupList() void InstanceList::saveGroupList()
{ {
qDebug() << "Will save group list now."; qDebug() << "Will save group list now.";
@ -621,7 +675,7 @@ void InstanceList::saveGroupList()
QString groupFileName = m_instDir + "/instgroups.json"; QString groupFileName = m_instDir + "/instgroups.json";
QMap<QString, QSet<QString>> reverseGroupMap; QMap<QString, QSet<QString>> reverseGroupMap;
for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++) { for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++) {
QString id = iter.key(); const QString& id = iter.key();
QString group = iter.value(); QString group = iter.value();
if (group.isEmpty()) if (group.isEmpty())
continue; continue;
@ -711,17 +765,22 @@ void InstanceList::loadGroupList()
return; return;
} }
QSet<QString> groupSet;
m_instanceGroupIndex.clear(); m_instanceGroupIndex.clear();
m_groupNameCache.clear();
// Iterate through all the groups. // Iterate through all the groups.
QJsonObject groupMapping = rootObj.value("groups").toObject(); QJsonObject groupMapping = rootObj.value("groups").toObject();
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++) { for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++) {
QString groupName = iter.key(); QString groupName = iter.key();
if (iter.key().isEmpty()) {
qWarning() << "Redundant empty group found";
continue;
}
// If not an object, complain and skip to the next one. // If not an object, complain and skip to the next one.
if (!iter.value().isObject()) { if (!iter.value().isObject()) {
qWarning() << QString("Group '%1' in the group list should be an object.").arg(groupName).toUtf8(); qWarning() << QString("Group '%1' in the group list should be an object").arg(groupName).toUtf8();
continue; continue;
} }
@ -733,23 +792,19 @@ void InstanceList::loadGroupList()
continue; continue;
} }
// keep a list/set of groups for choosing
groupSet.insert(groupName);
auto hidden = groupObj.value("hidden").toBool(false); auto hidden = groupObj.value("hidden").toBool(false);
if (hidden) { if (hidden)
m_collapsedGroups.insert(groupName); m_collapsedGroups.insert(groupName);
}
// Iterate through the list of instances in the group. // Iterate through the list of instances in the group.
QJsonArray instancesArray = groupObj.value("instances").toArray(); QJsonArray instancesArray = groupObj.value("instances").toArray();
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); iter2++) { for (auto value : instancesArray) {
m_instanceGroupIndex[(*iter2).toString()] = groupName; m_instanceGroupIndex[value.toString()] = groupName;
increaseGroupCount(groupName);
} }
} }
m_groupsLoaded = true; m_groupsLoaded = true;
m_groupNameCache.unite(groupSet);
qDebug() << "Group list loaded."; qDebug() << "Group list loaded.";
} }
@ -892,9 +947,12 @@ QString InstanceList::getStagedInstancePath()
bool InstanceList::commitStagedInstance(const QString& path, bool InstanceList::commitStagedInstance(const QString& path,
InstanceName const& instanceName, InstanceName const& instanceName,
const QString& groupName, QString groupName,
InstanceTask const& commiting) InstanceTask const& commiting)
{ {
if (groupName.isEmpty() && !groupName.isNull())
groupName = QString();
QDir dir; QDir dir;
QString instID; QString instID;
InstancePtr inst; InstancePtr inst;
@ -925,7 +983,7 @@ bool InstanceList::commitStagedInstance(const QString& path,
} }
m_instanceGroupIndex[instID] = groupName; m_instanceGroupIndex[instID] = groupName;
m_groupNameCache.insert(groupName); increaseGroupCount(groupName);
} }
instanceSet.insert(instID); instanceSet.insert(instID);

View File

@ -1,16 +1,36 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * This program is free software: you can redistribute it and/or modify
* you may not use this file except in compliance with the License. * it under the terms of the GNU General Public License as published by
* You may obtain a copy of the License at * the Free Software Foundation, version 3.
* *
* http://www.apache.org/licenses/LICENSE-2.0 * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* *
* Unless required by applicable law or agreed to in writing, software * You should have received a copy of the GNU General Public License
* distributed under the License is distributed on an "AS IS" BASIS, * along with this program. If not, see <https://www.gnu.org/licenses/>.
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and * This file incorporates work covered by the following copyright and
* limitations under the License. * permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
#pragma once #pragma once
@ -86,9 +106,10 @@ class InstanceList : public QAbstractListModel {
bool isGroupCollapsed(const QString& groupName); bool isGroupCollapsed(const QString& groupName);
GroupId getInstanceGroup(const InstanceId& id) const; GroupId getInstanceGroup(const InstanceId& id) const;
void setInstanceGroup(const InstanceId& id, const GroupId& name); void setInstanceGroup(const InstanceId& id, GroupId name);
void deleteGroup(const GroupId& name); void deleteGroup(const GroupId& name);
void renameGroup(const GroupId& src, const GroupId& dst);
bool trashInstance(const InstanceId& id); bool trashInstance(const InstanceId& id);
bool trashedSomething(); bool trashedSomething();
void undoTrashInstance(); void undoTrashInstance();
@ -109,7 +130,7 @@ class InstanceList : public QAbstractListModel {
* should_override is used when another similar instance already exists, and we want to override it * should_override is used when another similar instance already exists, and we want to override it
* - for instance, when updating it. * - for instance, when updating it.
*/ */
bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, const QString& groupName, const InstanceTask&); bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, QString groupName, const InstanceTask&);
/** /**
* Destroy a previously created staging area given by @keyPath - used when creation fails. * Destroy a previously created staging area given by @keyPath - used when creation fails.
@ -158,12 +179,16 @@ class InstanceList : public QAbstractListModel {
QList<InstanceId> discoverInstances(); QList<InstanceId> discoverInstances();
InstancePtr loadInstance(const InstanceId& id); InstancePtr loadInstance(const InstanceId& id);
void increaseGroupCount(const QString& group);
void decreaseGroupCount(const QString& group);
private: private:
int m_watchLevel = 0; int m_watchLevel = 0;
int totalPlayTime = 0; int totalPlayTime = 0;
bool m_dirty = false; bool m_dirty = false;
QList<InstancePtr> m_instances; QList<InstancePtr> m_instances;
QSet<QString> m_groupNameCache; // id -> refs
QMap<QString, int> m_groupNameCache;
SettingsObjectPtr m_globalSettings; SettingsObjectPtr m_globalSettings;
QString m_instDir; QString m_instDir;

View File

@ -89,7 +89,7 @@ void LaunchController::decideAccount()
// Tell the user they need to log in at least one account in order to play. // Tell the user they need to log in at least one account in order to play.
auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"), auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"),
tr("In order to play Minecraft, you must have at least one Microsoft " tr("In order to play Minecraft, you must have at least one Microsoft "
"account which owns Minecraft logged in." "account which owns Minecraft logged in. "
"Would you like to open the account manager to add an account now?"), "Would you like to open the account manager to add an account now?"),
QMessageBox::Information, QMessageBox::Yes | QMessageBox::No) QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)
->exec(); ->exec();
@ -106,7 +106,7 @@ void LaunchController::decideAccount()
// Select the account to use. If the instance has a specific account set, that will be used. Otherwise, the default account will be used // Select the account to use. If the instance has a specific account set, that will be used. Otherwise, the default account will be used
auto instanceAccountId = m_instance->settings()->get("InstanceAccountId").toString(); auto instanceAccountId = m_instance->settings()->get("InstanceAccountId").toString();
auto instanceAccountIndex = accounts->findAccountByProfileId(instanceAccountId); auto instanceAccountIndex = accounts->findAccountByProfileId(instanceAccountId);
if (instanceAccountIndex == -1) { if (instanceAccountIndex == -1 || instanceAccountId.isEmpty()) {
m_accountToUse = accounts->defaultAccount(); m_accountToUse = accounts->defaultAccount();
} else { } else {
m_accountToUse = accounts->at(instanceAccountIndex); m_accountToUse = accounts->at(instanceAccountIndex);

View File

@ -42,7 +42,11 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
#include <QUrl>
#if defined(LAUNCHER_APPLICATION)
#include <QtConcurrentRun> #include <QtConcurrentRun>
#endif
namespace MMCZip { namespace MMCZip {
// ours // ours
@ -132,6 +136,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files,
return result; return result;
} }
#if defined(LAUNCHER_APPLICATION)
// ours // ours
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods) bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods)
{ {
@ -217,6 +222,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
} }
return true; return true;
} }
#endif
// ours // ours
QString findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root) QString findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root)
@ -422,6 +428,7 @@ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, Q
return true; return true;
} }
#if defined(LAUNCHER_APPLICATION)
void ExportToZipTask::executeTask() void ExportToZipTask::executeTask()
{ {
setStatus("Adding files..."); setStatus("Adding files...");
@ -500,5 +507,6 @@ bool ExportToZipTask::abort()
} }
return false; return false;
} }
#endif
} // namespace MMCZip } // namespace MMCZip

View File

@ -48,7 +48,10 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <optional> #include <optional>
#if defined(LAUNCHER_APPLICATION)
#include "minecraft/mod/Mod.h" #include "minecraft/mod/Mod.h"
#endif
#include "tasks/Task.h" #include "tasks/Task.h"
namespace MMCZip { namespace MMCZip {
@ -79,11 +82,12 @@ bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool follow
*/ */
bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false); bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false);
#if defined(LAUNCHER_APPLICATION)
/** /**
* take a source jar, add mods to it, resulting in target jar * take a source jar, add mods to it, resulting in target jar
*/ */
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods); bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods);
#endif
/** /**
* Find a single file in archive by file name (not path) * Find a single file in archive by file name (not path)
* *
@ -147,6 +151,7 @@ bool extractFile(QString fileCompressed, QString file, QString dir);
*/ */
bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter); bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter);
#if defined(LAUNCHER_APPLICATION)
class ExportToZipTask : public Task { class ExportToZipTask : public Task {
public: public:
ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false)
@ -167,7 +172,7 @@ class ExportToZipTask : public Task {
void setExcludeFiles(QStringList excludeFiles) { m_exclude_files = excludeFiles; } void setExcludeFiles(QStringList excludeFiles) { m_exclude_files = excludeFiles; }
void addExtraFile(QString fileName, QByteArray data) { m_extra_files.insert(fileName, data); } void addExtraFile(QString fileName, QByteArray data) { m_extra_files.insert(fileName, data); }
typedef std::optional<QString> ZipResult; using ZipResult = std::optional<QString>;
protected: protected:
virtual void executeTask() override; virtual void executeTask() override;
@ -189,4 +194,5 @@ class ExportToZipTask : public Task {
QFuture<ZipResult> m_build_zip_future; QFuture<ZipResult> m_build_zip_future;
QFutureWatcher<ZipResult> m_build_zip_watcher; QFutureWatcher<ZipResult> m_build_zip_watcher;
}; };
#endif
} // namespace MMCZip } // namespace MMCZip

View File

@ -5,6 +5,7 @@
#include <QPixmapCache> #include <QPixmapCache>
#include <QThread> #include <QThread>
#include <QTime> #include <QTime>
#include <limits>
#define GET_TYPE() \ #define GET_TYPE() \
Qt::ConnectionType type; \ Qt::ConnectionType type; \
@ -100,10 +101,14 @@ class PixmapCache final : public QObject {
*/ */
bool _markCacheMissByEviciton() bool _markCacheMissByEviciton()
{ {
static constexpr uint maxInt = static_cast<uint>(std::numeric_limits<int>::max());
static constexpr uint step = 10240;
static constexpr int oneSecond = 1000;
auto now = QTime::currentTime(); auto now = QTime::currentTime();
if (!m_last_cache_miss_by_eviciton.isNull()) { if (!m_last_cache_miss_by_eviciton.isNull()) {
auto diff = m_last_cache_miss_by_eviciton.msecsTo(now); auto diff = m_last_cache_miss_by_eviciton.msecsTo(now);
if (diff < 1000) { // less than a second ago if (diff < oneSecond) { // less than a second ago
++m_consecutive_fast_evicitons; ++m_consecutive_fast_evicitons;
} else { } else {
m_consecutive_fast_evicitons = 0; m_consecutive_fast_evicitons = 0;
@ -111,11 +116,17 @@ class PixmapCache final : public QObject {
} }
m_last_cache_miss_by_eviciton = now; m_last_cache_miss_by_eviciton = now;
if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) { if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) {
// double the cache size // increase the cache size
auto newSize = _cacheLimit() * 2; uint newSize = _cacheLimit() + step;
qDebug() << m_consecutive_fast_evicitons << "pixmap cache misses by eviction happened too fast, doubling cache size to" if (newSize >= maxInt) { // increase it until you overflow :D
<< newSize; newSize = maxInt;
_setCacheLimit(newSize); qDebug() << m_consecutive_fast_evicitons
<< tr("pixmap cache misses by eviction happened too fast, doing nothing as the cache size reached it's limit");
} else {
qDebug() << m_consecutive_fast_evicitons
<< tr("pixmap cache misses by eviction happened too fast, increasing cache size to") << static_cast<int>(newSize);
}
_setCacheLimit(static_cast<int>(newSize));
m_consecutive_fast_evicitons = 0; m_consecutive_fast_evicitons = 0;
return true; return true;
} }

View File

@ -35,6 +35,7 @@
*/ */
#include "StringUtils.h" #include "StringUtils.h"
#include <qpair.h>
#include <QRegularExpression> #include <QRegularExpression>
#include <QUuid> #include <QUuid>
@ -149,7 +150,7 @@ QString StringUtils::truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_
} }
if ((url_compact.length() >= max_len) && hard_limit) { if ((url_compact.length() >= max_len) && hard_limit) {
// still too long, truncate normaly // still too long, truncate normally
url_compact = QString(str_url); url_compact = QString(str_url);
auto to_remove = url_compact.length() - max_len + 3; auto to_remove = url_compact.length() - max_len + 3;
url_compact.remove(url_compact.length() - to_remove - 1, to_remove); url_compact.remove(url_compact.length() - to_remove - 1, to_remove);
@ -182,3 +183,32 @@ QString StringUtils::getRandomAlphaNumeric()
{ {
return QUuid::createUuid().toString(QUuid::Id128); return QUuid::createUuid().toString(QUuid::Id128);
} }
QPair<QString, QString> StringUtils::splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs)
{
QString left, right;
auto index = s.indexOf(sep, 0, cs);
left = s.mid(0, index);
right = s.mid(index + sep.length());
return qMakePair(left, right);
}
QPair<QString, QString> StringUtils::splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs)
{
QString left, right;
auto index = s.indexOf(sep, 0, cs);
left = s.mid(0, index);
right = s.mid(left.length() + 1);
return qMakePair(left, right);
}
QPair<QString, QString> StringUtils::splitFirst(const QString& s, const QRegularExpression& re)
{
QString left, right;
QRegularExpressionMatch match;
auto index = s.indexOf(re, 0, &match);
left = s.mid(0, index);
auto end = match.hasMatch() ? left.length() + match.capturedLength() : left.length() + 1;
right = s.mid(end);
return qMakePair(left, right);
}

View File

@ -36,8 +36,10 @@
#pragma once #pragma once
#include <QPair>
#include <QString> #include <QString>
#include <QUrl> #include <QUrl>
#include <utility>
namespace StringUtils { namespace StringUtils {
@ -70,12 +72,17 @@ int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs)
/** /**
* @brief Truncate a url while keeping its readability py placing the `...` in the middle of the path * @brief Truncate a url while keeping its readability py placing the `...` in the middle of the path
* @param url Url to truncate * @param url Url to truncate
* @param max_len max lenght of url in charaters * @param max_len max length of url in characters
* @param hard_limit if truncating the path can't get the url short enough, truncate it normaly. * @param hard_limit if truncating the path can't get the url short enough, truncate it normally.
*/ */
QString truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_limit = false); QString truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_limit = false);
QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1); QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1);
QString getRandomAlphaNumeric(); QString getRandomAlphaNumeric();
QPair<QString, QString> splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs = Qt::CaseSensitive);
QPair<QString, QString> splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive);
QPair<QString, QString> splitFirst(const QString& s, const QRegularExpression& re);
} // namespace StringUtils } // namespace StringUtils

View File

@ -56,6 +56,7 @@ class Version {
bool operator!=(const Version& other) const; bool operator!=(const Version& other) const;
QString toString() const { return m_string; } QString toString() const { return m_string; }
bool isEmpty() const { return m_string.isEmpty(); }
friend QDebug operator<<(QDebug debug, const Version& v); friend QDebug operator<<(QDebug debug, const Version& v);

View File

@ -10,7 +10,7 @@ class VersionProxyModel : public QAbstractProxyModel {
Q_OBJECT Q_OBJECT
public: public:
enum Column { Name, ParentVersion, Branch, Type, Architecture, Path, Time }; enum Column { Name, ParentVersion, Branch, Type, Architecture, Path, Time };
typedef QHash<BaseVersionList::ModelRoles, std::shared_ptr<Filter>> FilterMap; using FilterMap = QHash<BaseVersionList::ModelRoles, std::shared_ptr<Filter>>;
public: public:
VersionProxyModel(QObject* parent = 0); VersionProxyModel(QObject* parent = 0);

View File

@ -93,6 +93,7 @@ FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv),
joinServer(serverToJoin); joinServer(serverToJoin);
} else { } else {
qDebug() << "no server to join"; qDebug() << "no server to join";
m_status = Failed;
exit(); exit();
} }
} }
@ -108,6 +109,7 @@ void FileLinkApp::joinServer(QString server)
connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs); connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs);
connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError) { connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError) {
m_status = Failed;
switch (socketError) { switch (socketError) {
case QLocalSocket::ServerNotFoundError: case QLocalSocket::ServerNotFoundError:
qDebug() qDebug()
@ -132,6 +134,7 @@ void FileLinkApp::joinServer(QString server)
connect(&socket, &QLocalSocket::disconnected, this, [&]() { connect(&socket, &QLocalSocket::disconnected, this, [&]() {
qDebug() << "disconnected from server, should exit"; qDebug() << "disconnected from server, should exit";
m_status = Succeeded;
exit(); exit();
}); });

View File

@ -41,8 +41,10 @@ class FileLinkApp : public QCoreApplication {
// friends for the purpose of limiting access to deprecated stuff // friends for the purpose of limiting access to deprecated stuff
Q_OBJECT Q_OBJECT
public: public:
enum Status { Starting, Failed, Succeeded, Initialized };
FileLinkApp(int& argc, char** argv); FileLinkApp(int& argc, char** argv);
virtual ~FileLinkApp(); virtual ~FileLinkApp();
Status status() const { return m_status; }
private: private:
void joinServer(QString server); void joinServer(QString server);
@ -50,6 +52,8 @@ class FileLinkApp : public QCoreApplication {
void runLink(); void runLink();
void sendResults(); void sendResults();
Status m_status = Status::Starting;
bool m_useHardLinks = false; bool m_useHardLinks = false;
QDateTime m_startTime; QDateTime m_startTime;

View File

@ -26,5 +26,16 @@ int main(int argc, char* argv[])
{ {
FileLinkApp ldh(argc, argv); FileLinkApp ldh(argc, argv);
return ldh.exec(); switch (ldh.status()) {
case FileLinkApp::Starting:
case FileLinkApp::Initialized: {
return ldh.exec();
}
case FileLinkApp::Failed:
return 1;
case FileLinkApp::Succeeded:
return 0;
default:
return -1;
}
} }

View File

@ -22,8 +22,8 @@ struct JavaCheckResult {
enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored; enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored;
}; };
typedef shared_qobject_ptr<QProcess> QProcessPtr; using QProcessPtr = shared_qobject_ptr<QProcess>;
typedef shared_qobject_ptr<JavaChecker> JavaCheckerPtr; using JavaCheckerPtr = shared_qobject_ptr<JavaChecker>;
class JavaChecker : public QObject { class JavaChecker : public QObject {
Q_OBJECT Q_OBJECT
public: public:

View File

@ -20,7 +20,7 @@
#include "tasks/Task.h" #include "tasks/Task.h"
class JavaCheckerJob; class JavaCheckerJob;
typedef shared_qobject_ptr<JavaCheckerJob> JavaCheckerJobPtr; using JavaCheckerJobPtr = shared_qobject_ptr<JavaCheckerJob>;
// FIXME: this just seems horribly redundant // FIXME: this just seems horribly redundant
class JavaCheckerJob : public Task { class JavaCheckerJob : public Task {

View File

@ -42,4 +42,4 @@ struct JavaInstall : public BaseVersion {
bool recommended = false; bool recommended = false;
}; };
typedef std::shared_ptr<JavaInstall> JavaInstallPtr; using JavaInstallPtr = std::shared_ptr<JavaInstall>;

View File

@ -34,6 +34,7 @@
*/ */
#include <QDir> #include <QDir>
#include <QFileInfo>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
@ -335,6 +336,7 @@ QList<QString> JavaUtils::FindJavaPaths()
} }
} }
candidates.append(getMinecraftJavaBundle());
candidates = addJavasFromEnv(candidates); candidates = addJavasFromEnv(candidates);
candidates.removeDuplicates(); candidates.removeDuplicates();
return candidates; return candidates;
@ -360,6 +362,7 @@ QList<QString> JavaUtils::FindJavaPaths()
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
} }
javas.append(getMinecraftJavaBundle());
javas = addJavasFromEnv(javas); javas = addJavasFromEnv(javas);
javas.removeDuplicates(); javas.removeDuplicates();
return javas; return javas;
@ -411,6 +414,7 @@ QList<QString> JavaUtils::FindJavaPaths()
// javas downloaded by sdkman // javas downloaded by sdkman
scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java")); scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java"));
javas.append(getMinecraftJavaBundle());
javas = addJavasFromEnv(javas); javas = addJavasFromEnv(javas);
javas.removeDuplicates(); javas.removeDuplicates();
return javas; return javas;
@ -423,6 +427,7 @@ QList<QString> JavaUtils::FindJavaPaths()
QList<QString> javas; QList<QString> javas;
javas.append(this->GetDefaultJava()->path); javas.append(this->GetDefaultJava()->path);
javas.append(getMinecraftJavaBundle());
return addJavasFromEnv(javas); return addJavasFromEnv(javas);
} }
#endif #endif
@ -431,3 +436,50 @@ QString JavaUtils::getJavaCheckPath()
{ {
return APPLICATION->getJarPath("JavaCheck.jar"); return APPLICATION->getJarPath("JavaCheck.jar");
} }
QStringList getMinecraftJavaBundle()
{
QString partialPath;
QString executable = "java";
QStringList processpaths;
#if defined(Q_OS_OSX)
partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support");
#elif defined(Q_OS_WIN32)
partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", "");
executable += "w.exe";
// add the microsoft store version of the launcher to the search. the current path is:
// C:\Users\USERNAME\AppData\Local\Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local\runtime
auto minecraftMSStorePath =
FS::PathCombine(QFileInfo(partialPath).absolutePath(), "Local", "Packages", "Microsoft.4297127D64EC6_8wekyb3d8bbwe");
minecraftMSStorePath = FS::PathCombine(minecraftMSStorePath, "LocalCache", "Local", "runtime");
processpaths << minecraftMSStorePath;
#else
partialPath = QDir::homePath();
#endif
auto minecraftDataPath = FS::PathCombine(partialPath, ".minecraft", "runtime");
processpaths << minecraftDataPath;
QStringList javas;
while (!processpaths.isEmpty()) {
auto dirPath = processpaths.takeFirst();
QDir dir(dirPath);
if (!dir.exists())
continue;
auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
auto binFound = false;
for (auto& entry : entries) {
if (entry.baseName() == "bin") {
javas.append(FS::PathCombine(entry.canonicalFilePath(), executable));
binFound = true;
break;
}
}
if (!binFound) {
for (auto& entry : entries) {
processpaths << entry.canonicalFilePath();
}
}
}
return javas;
}

View File

@ -26,6 +26,7 @@
QString stripVariableEntries(QString name, QString target, QString remove); QString stripVariableEntries(QString name, QString target, QString remove);
QProcessEnvironment CleanEnviroment(); QProcessEnvironment CleanEnviroment();
QStringList getMinecraftJavaBundle();
class JavaUtils : public QObject { class JavaUtils : public QObject {
Q_OBJECT Q_OBJECT

View File

@ -45,10 +45,12 @@ QString JavaVersion::toString() const
bool JavaVersion::requiresPermGen() bool JavaVersion::requiresPermGen()
{ {
if (m_parseable) { return !m_parseable || m_major < 8;
return m_major < 8; }
}
return true; bool JavaVersion::isModular()
{
return m_parseable && m_major >= 9;
} }
bool JavaVersion::operator<(const JavaVersion& rhs) bool JavaVersion::operator<(const JavaVersion& rhs)

View File

@ -25,6 +25,8 @@ class JavaVersion {
bool requiresPermGen(); bool requiresPermGen();
bool isModular();
QString toString() const; QString toString() const;
int major() { return m_major; } int major() { return m_major; }

View File

@ -6,7 +6,7 @@
class Agent; class Agent;
typedef std::shared_ptr<Agent> AgentPtr; using AgentPtr = std::shared_ptr<Agent>;
class Agent { class Agent {
public: public:

View File

@ -48,6 +48,7 @@
#include "FileSystem.h" #include "FileSystem.h"
#include "net/ApiDownload.h" #include "net/ApiDownload.h"
#include "net/ChecksumValidator.h" #include "net/ChecksumValidator.h"
#include "net/Download.h"
#include "Application.h" #include "Application.h"

View File

@ -105,4 +105,4 @@ class Component : public QObject, public ProblemProvider {
bool m_loaded = false; bool m_loaded = false;
}; };
typedef shared_qobject_ptr<Component> ComponentPtr; using ComponentPtr = shared_qobject_ptr<Component>;

View File

@ -52,7 +52,7 @@
class Library; class Library;
class MinecraftInstance; class MinecraftInstance;
typedef std::shared_ptr<Library> LibraryPtr; using LibraryPtr = std::shared_ptr<Library>;
class Library { class Library {
friend class OneSixVersionFormat; friend class OneSixVersionFormat;

View File

@ -184,6 +184,13 @@ void MinecraftInstance::loadSpecificSettings()
m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride); m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride);
m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride); m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride);
// Legacy-related options
auto legacySettings = m_settings->registerSetting("OverrideLegacySettings", false);
m_settings->registerOverride(global_settings->getSetting("OnlineFixes"), legacySettings);
auto envSetting = m_settings->registerSetting("OverrideEnv", false);
m_settings->registerOverride(global_settings->getSetting("Env"), envSetting);
m_settings->set("InstanceType", "OneSix"); m_settings->set("InstanceType", "OneSix");
} }
@ -513,20 +520,28 @@ QStringList MinecraftInstance::javaArguments()
args << "-Duser.language=en"; args << "-Duser.language=en";
if (javaVersion.isModular() && shouldApplyOnlineFixes())
// allow reflective access to java.net - required by the skin fix
args << "--add-opens"
<< "java.base/java.net=ALL-UNNAMED";
return args; return args;
} }
QString MinecraftInstance::getLauncher() QString MinecraftInstance::getLauncher()
{ {
auto profile = m_components->getProfile();
// use legacy launcher if the traits are set // use legacy launcher if the traits are set
if (profile->getTraits().contains("legacyLaunch") || profile->getTraits().contains("alphaLaunch")) if (traits().contains("legacyLaunch") || traits().contains("alphaLaunch"))
return "legacy"; return "legacy";
return "standard"; return "standard";
} }
bool MinecraftInstance::shouldApplyOnlineFixes()
{
return traits().contains("legacyServices") && settings()->get("OnlineFixes").toBool();
}
QMap<QString, QString> MinecraftInstance::getVariables() QMap<QString, QString> MinecraftInstance::getVariables()
{ {
QMap<QString, QString> out; QMap<QString, QString> out;
@ -536,6 +551,7 @@ QMap<QString, QString> MinecraftInstance::getVariables()
out.insert("INST_MC_DIR", QDir::toNativeSeparators(QDir(gameRoot()).absolutePath())); out.insert("INST_MC_DIR", QDir::toNativeSeparators(QDir(gameRoot()).absolutePath()));
out.insert("INST_JAVA", settings()->get("JavaPath").toString()); out.insert("INST_JAVA", settings()->get("JavaPath").toString());
out.insert("INST_JAVA_ARGS", javaArguments().join(' ')); out.insert("INST_JAVA_ARGS", javaArguments().join(' '));
out.insert("NO_COLOR", "1");
return out; return out;
} }
@ -559,15 +575,20 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud) { if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud) {
auto preloadList = env.value("LD_PRELOAD").split(QLatin1String(":")); QStringList preloadList;
auto libPaths = env.value("LD_LIBRARY_PATH").split(QLatin1String(":")); if (auto value = env.value("LD_PRELOAD"); !value.isEmpty())
preloadList = value.split(QLatin1String(":"));
QStringList libPaths;
if (auto value = env.value("LD_LIBRARY_PATH"); !value.isEmpty())
libPaths = value.split(QLatin1String(":"));
auto mangoHudLibString = MangoHud::getLibraryString(); auto mangoHudLibString = MangoHud::getLibraryString();
if (!mangoHudLibString.isEmpty()) { if (!mangoHudLibString.isEmpty()) {
QFileInfo mangoHudLib(mangoHudLibString); QFileInfo mangoHudLib(mangoHudLibString);
// dlsym variant is only needed for OpenGL and not included in the vulkan layer // dlsym variant is only needed for OpenGL and not included in the vulkan layer
preloadList << "libMangoHud_dlsym.so" << mangoHudLib.fileName(); preloadList << "libMangoHud_dlsym.so"
<< "libMangoHud_opengl.so" << mangoHudLib.fileName();
libPaths << mangoHudLib.absolutePath(); libPaths << mangoHudLib.absolutePath();
} }
@ -586,6 +607,23 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
} }
#endif #endif
// custom env
auto insertEnv = [&env](QMap<QString, QVariant> envMap) {
if (envMap.isEmpty())
return;
for (auto iter = envMap.begin(); iter != envMap.end(); iter++)
env.insert(iter.key(), iter.value().toString());
};
bool overrideEnv = settings()->get("OverrideEnv").toBool();
if (!overrideEnv)
insertEnv(APPLICATION->settings()->get("Env").toMap());
else
insertEnv(settings()->get("Env").toMap());
return env; return env;
} }
@ -698,7 +736,7 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
{ {
QString windowParams; QString windowParams;
if (settings()->get("LaunchMaximized").toBool()) if (settings()->get("LaunchMaximized").toBool())
windowParams = "max"; windowParams = "maximized";
else else
windowParams = windowParams =
QString("%1x%2").arg(settings()->get("MinecraftWinWidth").toInt()).arg(settings()->get("MinecraftWinHeight").toInt()); QString("%1x%2").arg(settings()->get("MinecraftWinWidth").toInt()).arg(settings()->get("MinecraftWinHeight").toInt());
@ -706,6 +744,19 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
launchScript += "windowParams " + windowParams + "\n"; launchScript += "windowParams " + windowParams + "\n";
} }
// launcher info
{
launchScript += "launcherBrand " + BuildConfig.LAUNCHER_NAME + "\n";
launchScript += "launcherVersion " + BuildConfig.printableVersionString() + "\n";
}
// instance info
{
launchScript += "instanceName " + name() + "\n";
launchScript += "instanceIconKey " + name() + "\n";
launchScript += "instanceIconPath icon.png\n"; // we already save a copy here
}
// legacy auth // legacy auth
if (session) { if (session) {
launchScript += "userName " + session->player_name + "\n"; launchScript += "userName " + session->player_name + "\n";
@ -716,6 +767,9 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
launchScript += "traits " + trait + "\n"; launchScript += "traits " + trait + "\n";
} }
if (shouldApplyOnlineFixes())
launchScript += "onlineFixes true\n";
launchScript += "launcher " + getLauncher() + "\n"; launchScript += "launcher " + getLauncher() + "\n";
// qDebug() << "Generated launch script:" << launchScript; // qDebug() << "Generated launch script:" << launchScript;

View File

@ -129,6 +129,7 @@ class MinecraftInstance : public BaseInstance {
/// get arguments passed to java /// get arguments passed to java
QStringList javaArguments(); QStringList javaArguments();
QString getLauncher(); QString getLauncher();
bool shouldApplyOnlineFixes();
/// get variables for launch command variable substitution/environment /// get variables for launch command variable substitution/environment
QMap<QString, QString> getVariables() override; QMap<QString, QString> getVariables() override;
@ -173,4 +174,4 @@ class MinecraftInstance : public BaseInstance {
mutable std::shared_ptr<GameOptions> m_game_options; mutable std::shared_ptr<GameOptions> m_game_options;
}; };
typedef std::shared_ptr<MinecraftInstance> MinecraftInstancePtr; using MinecraftInstancePtr = std::shared_ptr<MinecraftInstance>;

View File

@ -5,7 +5,7 @@
struct MojangDownloadInfo { struct MojangDownloadInfo {
// types // types
typedef std::shared_ptr<MojangDownloadInfo> Ptr; using Ptr = std::shared_ptr<MojangDownloadInfo>;
// data // data
/// Local filesystem path. WARNING: not used, only here so we can pass through mojang files unmolested! /// Local filesystem path. WARNING: not used, only here so we can pass through mojang files unmolested!
@ -23,7 +23,7 @@ struct MojangLibraryDownloadInfo {
MojangLibraryDownloadInfo() {} MojangLibraryDownloadInfo() {}
// types // types
typedef std::shared_ptr<MojangLibraryDownloadInfo> Ptr; using Ptr = std::shared_ptr<MojangLibraryDownloadInfo>;
// methods // methods
MojangDownloadInfo* getDownloadInfo(QString classifier) MojangDownloadInfo* getDownloadInfo(QString classifier)
@ -42,7 +42,7 @@ struct MojangLibraryDownloadInfo {
struct MojangAssetIndexInfo : public MojangDownloadInfo { struct MojangAssetIndexInfo : public MojangDownloadInfo {
// types // types
typedef std::shared_ptr<MojangAssetIndexInfo> Ptr; using Ptr = std::shared_ptr<MojangAssetIndexInfo>;
// methods // methods
MojangAssetIndexInfo() {} MojangAssetIndexInfo() {}

View File

@ -38,7 +38,7 @@
#include "VersionFile.h" #include "VersionFile.h"
namespace ProfileUtils { namespace ProfileUtils {
typedef QStringList PatchOrder; using PatchOrder = QStringList;
/// Read and parse a OneSix format order file /// Read and parse a OneSix format order file
bool readOverrideOrders(QString path, PatchOrder& order); bool readOverrideOrders(QString path, PatchOrder& order);

View File

@ -43,4 +43,4 @@ struct AuthSession {
bool demo = false; bool demo = false;
}; };
typedef std::shared_ptr<AuthSession> AuthSessionPtr; using AuthSessionPtr = std::shared_ptr<AuthSession>;

View File

@ -54,7 +54,7 @@ class Task;
class AccountTask; class AccountTask;
class MinecraftAccount; class MinecraftAccount;
typedef shared_qobject_ptr<MinecraftAccount> MinecraftAccountPtr; using MinecraftAccountPtr = shared_qobject_ptr<MinecraftAccount>;
Q_DECLARE_METATYPE(MinecraftAccountPtr) Q_DECLARE_METATYPE(MinecraftAccountPtr)
/** /**

View File

@ -105,6 +105,17 @@ void LauncherPartLaunch::executeTask()
auto instance = m_parent->instance(); auto instance = m_parent->instance();
std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance); std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
QString legacyJarPath;
if (minecraftInstance->getLauncher() == "legacy" || minecraftInstance->shouldApplyOnlineFixes()) {
legacyJarPath = APPLICATION->getJarPath("NewLaunchLegacy.jar");
if (legacyJarPath.isEmpty()) {
const char* reason = QT_TR_NOOP("Legacy launcher library could not be found. Please check your installation.");
emit logLine(tr(reason), MessageLevel::Fatal);
emitFailed(tr(reason));
return;
}
}
m_launchScript = minecraftInstance->createLaunchScript(m_session, m_serverToJoin); m_launchScript = minecraftInstance->createLaunchScript(m_session, m_serverToJoin);
QStringList args = minecraftInstance->javaArguments(); QStringList args = minecraftInstance->javaArguments();
QString allArgs = args.join(", "); QString allArgs = args.join(", ");
@ -120,6 +131,9 @@ void LauncherPartLaunch::executeTask()
auto classPath = minecraftInstance->getClassPath(); auto classPath = minecraftInstance->getClassPath();
classPath.prepend(jarPath); classPath.prepend(jarPath);
if (!legacyJarPath.isEmpty())
classPath.prepend(legacyJarPath);
auto natPath = minecraftInstance->getNativePath(); auto natPath = minecraftInstance->getNativePath();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
if (!fitsInLocal8bit(natPath)) { if (!fitsInLocal8bit(natPath)) {

View File

@ -26,4 +26,4 @@ struct MinecraftServerTarget {
static MinecraftServerTarget parse(const QString& fullAddress); static MinecraftServerTarget parse(const QString& fullAddress);
}; };
typedef std::shared_ptr<MinecraftServerTarget> MinecraftServerTargetPtr; using MinecraftServerTargetPtr = std::shared_ptr<MinecraftServerTarget>;

View File

@ -37,9 +37,9 @@
#include "ModFolderModel.h" #include "ModFolderModel.h"
#include <FileSystem.h> #include <FileSystem.h>
#include <qheaderview.h>
#include <QDebug> #include <QDebug>
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
#include <QHeaderView>
#include <QIcon> #include <QIcon>
#include <QMimeData> #include <QMimeData>
#include <QString> #include <QString>
@ -65,8 +65,8 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool
m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" }); m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") }); 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_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER };
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch,
QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive };
m_columnsHideable = { false, true, false, true, true, true }; m_columnsHideable = { false, true, false, true, true, true };
} }
@ -131,6 +131,11 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const
} }
return {}; return {};
} }
case Qt::SizeHintRole:
if (column == ImageColumn) {
return QSize(32, 32);
}
return {};
case Qt::CheckStateRole: case Qt::CheckStateRole:
switch (column) { switch (column) {
case ActiveColumn: case ActiveColumn:

View File

@ -4,6 +4,7 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
#include <QFileInfo> #include <QFileInfo>
#include <QHeaderView>
#include <QIcon> #include <QIcon>
#include <QMenu> #include <QMenu>
#include <QMimeData> #include <QMimeData>
@ -516,36 +517,22 @@ void ResourceFolderModel::setupHeaderAction(QAction* act, int column)
act->setText(columnNames().at(column)); act->setText(columnNames().at(column));
} }
void ResourceFolderModel::saveHiddenColumn(int column, bool hidden) void ResourceFolderModel::saveColumns(QTreeView* tree)
{ {
auto const setting_name = QString("UI/%1_Page/HiddenColumns").arg(id()); auto const setting_name = QString("UI/%1_Page/Columns").arg(id());
auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name) auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name)
: m_instance->settings()->registerSetting(setting_name); : m_instance->settings()->registerSetting(setting_name);
auto hiddenColumns = setting->get().toStringList(); setting->set(tree->header()->saveState());
auto name = columnNames(false).at(column);
auto index = hiddenColumns.indexOf(name);
if (index >= 0 && !hidden) {
hiddenColumns.removeAt(index);
} else if (index < 0 && hidden) {
hiddenColumns.append(name);
}
setting->set(hiddenColumns);
} }
void ResourceFolderModel::loadHiddenColumns(QTreeView* tree) void ResourceFolderModel::loadColumns(QTreeView* tree)
{ {
auto const setting_name = QString("UI/%1_Page/HiddenColumns").arg(id()); auto const setting_name = QString("UI/%1_Page/Columns").arg(id());
auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name) auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name)
: m_instance->settings()->registerSetting(setting_name); : m_instance->settings()->registerSetting(setting_name);
auto hiddenColumns = setting->get().toStringList(); tree->header()->restoreState(setting->get().toByteArray());
auto col_names = columnNames(false);
for (auto col_name : hiddenColumns) {
auto index = col_names.indexOf(col_name);
if (index >= 0)
tree->setColumnHidden(index, true);
}
} }
QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree) QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree)
@ -570,7 +557,7 @@ QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree)
if (m_column_resize_modes.at(c) == QHeaderView::ResizeToContents) if (m_column_resize_modes.at(c) == QHeaderView::ResizeToContents)
tree->resizeColumnToContents(c); tree->resizeColumnToContents(c);
} }
saveHiddenColumn(col, !toggled); saveColumns(tree);
}); });
menu->addAction(act); menu->addAction(act);

View File

@ -117,8 +117,8 @@ class ResourceFolderModel : public QAbstractListModel {
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
void setupHeaderAction(QAction* act, int column); void setupHeaderAction(QAction* act, int column);
void saveHiddenColumn(int column, bool hidden); void saveColumns(QTreeView* tree);
void loadHiddenColumns(QTreeView* tree); void loadColumns(QTreeView* tree);
QMenu* createHeaderContextMenu(QTreeView* tree); QMenu* createHeaderContextMenu(QTreeView* tree);
/** This creates a proxy model to filter / sort the model for a UI. /** This creates a proxy model to filter / sort the model for a UI.
@ -201,8 +201,7 @@ class ResourceFolderModel : public QAbstractListModel {
QList<SortType> m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE }; QList<SortType> m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE };
QStringList m_column_names = { "Enable", "Name", "Last Modified" }; QStringList m_column_names = { "Enable", "Name", "Last Modified" };
QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified") }; QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified") };
QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch, QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive };
QHeaderView::ResizeToContents };
QList<bool> m_columnsHideable = { false, false, true }; QList<bool> m_columnsHideable = { false, false, true };
QDir m_dir; QDir m_dir;

View File

@ -52,8 +52,8 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstanc
m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" }); m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") }); 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_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE };
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive,
QHeaderView::ResizeToContents }; QHeaderView::Interactive };
m_columnsHideable = { false, true, false, true, true }; m_columnsHideable = { false, true, false, true, true };
} }
@ -117,6 +117,11 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
} }
return m_resources[row]->internal_id(); return m_resources[row]->internal_id();
} }
case Qt::SizeHintRole:
if (column == ImageColumn) {
return QSize(32, 32);
}
return {};
case Qt::CheckStateRole: case Qt::CheckStateRole:
switch (column) { switch (column) {
case ActiveColumn: case ActiveColumn:

View File

@ -47,8 +47,7 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance*
m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified" }); m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") }); 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_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE };
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive };
QHeaderView::ResizeToContents };
m_columnsHideable = { false, true, false, true }; m_columnsHideable = { false, true, false, true };
} }
@ -104,6 +103,11 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const
} }
return {}; return {};
} }
case Qt::SizeHintRole:
if (column == ImageColumn) {
return QSize(32, 32);
}
return {};
case Qt::CheckStateRole: case Qt::CheckStateRole:
if (column == ActiveColumn) { if (column == ActiveColumn) {
return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked; return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked;

View File

@ -688,6 +688,7 @@ bool loadIconFile(const Mod& mod)
return png_invalid(); // icon invalid return png_invalid(); // icon invalid
} }
} }
return false;
} }
case ResourceType::ZIPFILE: { case ResourceType::ZIPFILE: {
QuaZip zip(mod.fileinfo().filePath()); QuaZip zip(mod.fileinfo().filePath());
@ -714,6 +715,7 @@ bool loadIconFile(const Mod& mod)
} else { } else {
return png_invalid(); // could not set icon as current file. return png_invalid(); // could not set icon as current file.
} }
return false;
} }
case ResourceType::LITEMOD: { case ResourceType::LITEMOD: {
return false; // can lightmods even have icons? return false; // can lightmods even have icons?

View File

@ -232,10 +232,9 @@ bool processPackPNG(const ResourcePack& pack)
} else { } else {
return png_invalid(); // pack.png does not exists or is not a valid file. return png_invalid(); // pack.png does not exists or is not a valid file.
} }
return false; // not processed correctly; https://github.com/PrismLauncher/PrismLauncher/issues/1740
} }
case ResourceType::ZIPFILE: { case ResourceType::ZIPFILE: {
Q_ASSERT(pack.type() == ResourceType::ZIPFILE);
QuaZip zip(pack.fileinfo().filePath()); QuaZip zip(pack.fileinfo().filePath());
if (!zip.open(QuaZip::mdUnzip)) if (!zip.open(QuaZip::mdUnzip))
return false; // can't open zip file return false; // can't open zip file
@ -259,6 +258,7 @@ bool processPackPNG(const ResourcePack& pack)
} else { } else {
return png_invalid(); // could not set pack.mcmeta as current file. return png_invalid(); // could not set pack.mcmeta as current file.
} }
return false; // not processed correctly; https://github.com/PrismLauncher/PrismLauncher/issues/1740
} }
default: default:
qWarning() << "Invalid type for resource pack parse task!"; qWarning() << "Invalid type for resource pack parse task!";

View File

@ -186,10 +186,9 @@ bool processPackPNG(const TexturePack& pack)
} else { } else {
return png_invalid(); // pack.png does not exists or is not a valid file. return png_invalid(); // pack.png does not exists or is not a valid file.
} }
return false;
} }
case ResourceType::ZIPFILE: { case ResourceType::ZIPFILE: {
Q_ASSERT(pack.type() == ResourceType::ZIPFILE);
QuaZip zip(pack.fileinfo().filePath()); QuaZip zip(pack.fileinfo().filePath());
if (!zip.open(QuaZip::mdUnzip)) if (!zip.open(QuaZip::mdUnzip))
return false; // can't open zip file return false; // can't open zip file
@ -215,6 +214,7 @@ bool processPackPNG(const TexturePack& pack)
zip.close(); zip.close();
return png_invalid(); // could not set pack.mcmeta as current file. return png_invalid(); // could not set pack.mcmeta as current file.
} }
return false;
} }
default: default:
qWarning() << "Invalid type for resource pack parse task!"; qWarning() << "Invalid type for resource pack parse task!";

View File

@ -4,7 +4,7 @@
#include <QtNetwork/QtNetwork> #include <QtNetwork/QtNetwork>
#include "tasks/Task.h" #include "tasks/Task.h"
typedef shared_qobject_ptr<class SkinDelete> SkinDeletePtr; using SkinDeletePtr = shared_qobject_ptr<class SkinDelete>;
class SkinDelete : public Task { class SkinDelete : public Task {
Q_OBJECT Q_OBJECT

View File

@ -5,7 +5,7 @@
#include <memory> #include <memory>
#include "tasks/Task.h" #include "tasks/Task.h"
typedef shared_qobject_ptr<class SkinUpload> SkinUploadPtr; using SkinUploadPtr = shared_qobject_ptr<class SkinUpload>;
class SkinUpload : public Task { class SkinUpload : public Task {
Q_OBJECT Q_OBJECT

View File

@ -37,6 +37,7 @@
#include "ATLPackInstallTask.h" #include "ATLPackInstallTask.h"
#include <QtConcurrent> #include <QtConcurrent>
#include <algorithm>
#include <quazip/quazip.h> #include <quazip/quazip.h>
@ -50,6 +51,7 @@
#include "minecraft/MinecraftInstance.h" #include "minecraft/MinecraftInstance.h"
#include "minecraft/OneSixVersionFormat.h" #include "minecraft/OneSixVersionFormat.h"
#include "minecraft/PackProfile.h" #include "minecraft/PackProfile.h"
#include "modplatform/atlauncher/ATLPackManifest.h"
#include "net/ChecksumValidator.h" #include "net/ChecksumValidator.h"
#include "settings/INISettingsObject.h" #include "settings/INISettingsObject.h"
@ -57,6 +59,7 @@
#include "Application.h" #include "Application.h"
#include "BuildConfig.h" #include "BuildConfig.h"
#include "ui/dialogs/BlockedModsDialog.h"
namespace ATLauncher { namespace ATLauncher {
@ -717,6 +720,8 @@ void PackInstallTask::downloadMods()
jarmods.clear(); jarmods.clear();
jobPtr.reset(new NetJob(tr("Mod download"), APPLICATION->network())); jobPtr.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
QList<VersionMod> blocked_mods;
for (const auto& mod : m_version.mods) { for (const auto& mod : m_version.mods) {
// skip non-client mods // skip non-client mods
if (!mod.client) if (!mod.client)
@ -731,9 +736,10 @@ void PackInstallTask::downloadMods()
case DownloadType::Server: case DownloadType::Server:
url = BuildConfig.ATL_DOWNLOAD_SERVER_URL + mod.url; url = BuildConfig.ATL_DOWNLOAD_SERVER_URL + mod.url;
break; break;
case DownloadType::Browser: case DownloadType::Browser: {
emitFailed(tr("Unsupported download type: %1").arg(mod.download_raw)); blocked_mods.append(mod);
return; continue;
}
case DownloadType::Direct: case DownloadType::Direct:
url = mod.url; url = mod.url;
break; break;
@ -805,24 +811,86 @@ void PackInstallTask::downloadMods()
modsToCopy[entry->getFullPath()] = path; modsToCopy[entry->getFullPath()] = path;
} }
} }
if (!blocked_mods.isEmpty()) {
QList<BlockedMod> mods;
for (auto mod : blocked_mods) {
BlockedMod blocked_mod;
blocked_mod.name = mod.file;
blocked_mod.websiteUrl = mod.url;
blocked_mod.hash = mod.md5;
blocked_mod.matched = false;
blocked_mod.localPath = "";
mods.append(blocked_mod);
}
qWarning() << "Blocked mods found, displaying mod list";
BlockedModsDialog message_dialog(nullptr, tr("Blocked mods found"),
tr("The following files are not available for download in third party launchers.<br/>"
"You will need to manually download them and add them to the instance."),
mods, "md5");
message_dialog.setModal(true);
if (message_dialog.exec()) {
qDebug() << "Post dialog blocked mods list: " << mods;
for (auto blocked : mods) {
if (!blocked.matched) {
qDebug() << blocked.name << "was not matched to a local file, skipping copy";
continue;
}
auto modIter = std::find_if(blocked_mods.begin(), blocked_mods.end(),
[blocked](const VersionMod& mod) { return mod.url == blocked.websiteUrl; });
if (modIter == blocked_mods.end())
continue;
auto mod = *modIter;
if (mod.type == ModType::Extract || mod.type == ModType::TexturePackExtract || mod.type == ModType::ResourcePackExtract) {
modsToExtract.insert(blocked.localPath, mod);
} else if (mod.type == ModType::Decomp) {
modsToDecomp.insert(blocked.localPath, mod);
} else {
auto relpath = getDirForModType(mod.type, mod.type_raw);
if (relpath == Q_NULLPTR)
continue;
auto path = FS::PathCombine(m_stagingPath, "minecraft", relpath, mod.file);
if (mod.type == ModType::Forge) {
auto ver = getComponentVersion("net.minecraftforge", mod.version);
if (ver) {
componentsToInstall.insert("net.minecraftforge", ver);
continue;
}
qDebug() << "Jarmod: " + path;
jarmods.push_back(path);
}
if (mod.type == ModType::Jar) {
qDebug() << "Jarmod: " + path;
jarmods.push_back(path);
}
modsToCopy[blocked.localPath] = path;
}
}
} else {
emitFailed(tr("Unknown download type: %1").arg("browser"));
return;
}
}
connect(jobPtr.get(), &NetJob::succeeded, this, &PackInstallTask::onModsDownloaded); connect(jobPtr.get(), &NetJob::succeeded, this, &PackInstallTask::onModsDownloaded);
connect(jobPtr.get(), &NetJob::failed, [&](QString reason) { connect(jobPtr.get(), &NetJob::progress, [this](qint64 current, qint64 total) {
abortable = false;
jobPtr.reset();
emitFailed(reason);
});
connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
abortable = true; abortable = true;
setProgress(current, total); setProgress(current, total);
}); });
connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress); connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress);
connect(jobPtr.get(), &NetJob::aborted, [&] { connect(jobPtr.get(), &NetJob::aborted, &PackInstallTask::emitAborted);
abortable = false; connect(jobPtr.get(), &NetJob::failed, &PackInstallTask::emitFailed);
jobPtr.reset();
emitAborted();
});
jobPtr->start(); jobPtr->start();
} }
@ -843,7 +911,7 @@ void PackInstallTask::onModsDownloaded()
QtConcurrent::run(QThreadPool::globalInstance(), this, &PackInstallTask::extractMods, modsToExtract, modsToDecomp, modsToCopy); QtConcurrent::run(QThreadPool::globalInstance(), this, &PackInstallTask::extractMods, modsToExtract, modsToDecomp, modsToCopy);
#endif #endif
connect(&m_modExtractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &PackInstallTask::onModsExtracted); connect(&m_modExtractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &PackInstallTask::onModsExtracted);
connect(&m_modExtractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, [&]() { emitAborted(); }); connect(&m_modExtractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &PackInstallTask::emitAborted);
m_modExtractFutureWatcher.setFuture(m_modExtractFuture); m_modExtractFutureWatcher.setFuture(m_modExtractFuture);
} else { } else {
install(); install();

View File

@ -208,17 +208,15 @@ Task::Ptr FlameAPI::getFile(const QString& addonId, const QString& fileId, std::
return netJob; return netJob;
} }
// https://docs.curseforge.com/?python#tocS_ModsSearchSortField
static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") },
{ 2, "Popularity", QObject::tr("Sort by Popularity") },
{ 3, "LastUpdated", QObject::tr("Sort by Last Updated") },
{ 4, "Name", QObject::tr("Sort by Name") },
{ 5, "Author", QObject::tr("Sort by Author") },
{ 6, "TotalDownloads", QObject::tr("Sort by Downloads") },
{ 7, "Category", QObject::tr("Sort by Category") },
{ 8, "GameVersion", QObject::tr("Sort by Game Version") } };
QList<ResourceAPI::SortingMethod> FlameAPI::getSortingMethods() const QList<ResourceAPI::SortingMethod> FlameAPI::getSortingMethods() const
{ {
return s_sorts; // https://docs.curseforge.com/?python#tocS_ModsSearchSortField
return { { 1, "Featured", QObject::tr("Sort by Featured") },
{ 2, "Popularity", QObject::tr("Sort by Popularity") },
{ 3, "LastUpdated", QObject::tr("Sort by Last Updated") },
{ 4, "Name", QObject::tr("Sort by Name") },
{ 5, "Author", QObject::tr("Sort by Author") },
{ 6, "TotalDownloads", QObject::tr("Sort by Downloads") },
{ 7, "Category", QObject::tr("Sort by Category") },
{ 8, "GameVersion", QObject::tr("Sort by Game Version") } };
} }

View File

@ -568,7 +568,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
m_mod_id_resolver.reset(); m_mod_id_resolver.reset();
connect(m_files_job.get(), &NetJob::succeeded, this, [&]() { connect(m_files_job.get(), &NetJob::succeeded, this, [&]() {
m_files_job.reset(); m_files_job.reset();
validateZIPResouces(); validateZIPResources();
}); });
connect(m_files_job.get(), &NetJob::failed, [&](QString reason) { connect(m_files_job.get(), &NetJob::failed, [&](QString reason) {
m_files_job.reset(); m_files_job.reset();
@ -617,7 +617,7 @@ void FlameCreationTask::copyBlockedMods(QList<BlockedMod> const& blocked_mods)
setAbortable(true); setAbortable(true);
} }
void FlameCreationTask::validateZIPResouces() void FlameCreationTask::validateZIPResources()
{ {
qDebug() << "Validating whether resources stored as .zip are in the right place"; qDebug() << "Validating whether resources stored as .zip are in the right place";
for (auto [fileName, targetFolder] : m_ZIP_resources) { for (auto [fileName, targetFolder] : m_ZIP_resources) {
@ -670,8 +670,8 @@ void FlameCreationTask::validateZIPResouces()
validatePath(fileName, targetFolder, "datapacks"); validatePath(fileName, targetFolder, "datapacks");
break; break;
case PackedResourceType::ShaderPack: case PackedResourceType::ShaderPack:
// in theroy flame API can't do this but who knows, that *may* change ? // in theory flame API can't do this but who knows, that *may* change ?
// better to handle it if it *does* occure in the future // better to handle it if it *does* occur in the future
validatePath(fileName, targetFolder, "shaderpacks"); validatePath(fileName, targetFolder, "shaderpacks");
break; break;
case PackedResourceType::WorldSave: case PackedResourceType::WorldSave:

View File

@ -74,7 +74,7 @@ class FlameCreationTask final : public InstanceCreationTask {
void idResolverSucceeded(QEventLoop&); void idResolverSucceeded(QEventLoop&);
void setupDownloadJob(QEventLoop&); void setupDownloadJob(QEventLoop&);
void copyBlockedMods(QList<BlockedMod> const& blocked_mods); void copyBlockedMods(QList<BlockedMod> const& blocked_mods);
void validateZIPResouces(); void validateZIPResources();
QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion); QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion);
private: private:

View File

@ -45,7 +45,7 @@ struct Modpack {
QIcon icon; QIcon icon;
}; };
typedef QList<Modpack> ModpackList; using ModpackList = QList<Modpack>;
Modpack parseDirectory(QString path); Modpack parseDirectory(QString path);

View File

@ -31,7 +31,7 @@ struct Modpack {
QString packCode; QString packCode;
}; };
typedef QList<Modpack> ModpackList; using ModpackList = QList<Modpack>;
} // namespace LegacyFTB } // namespace LegacyFTB

View File

@ -111,14 +111,12 @@ Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, std::shared_ptr<QByteAr
return netJob; return netJob;
} }
// https://docs.modrinth.com/api-spec/#tag/projects/operation/searchProjects
static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "relevance", QObject::tr("Sort by Relevance") },
{ 2, "downloads", QObject::tr("Sort by Downloads") },
{ 3, "follows", QObject::tr("Sort by Follows") },
{ 4, "newest", QObject::tr("Sort by Last Updated") },
{ 5, "updated", QObject::tr("Sort by Newest") } };
QList<ResourceAPI::SortingMethod> ModrinthAPI::getSortingMethods() const QList<ResourceAPI::SortingMethod> ModrinthAPI::getSortingMethods() const
{ {
return s_sorts; // https://docs.modrinth.com/api-spec/#tag/projects/operation/searchProjects
return { { 1, "relevance", QObject::tr("Sort by Relevance") },
{ 2, "downloads", QObject::tr("Sort by Downloads") },
{ 3, "follows", QObject::tr("Sort by Follows") },
{ 4, "newest", QObject::tr("Sort by Newest") },
{ 5, "updated", QObject::tr("Sort by Last Updated") } };
} }

View File

@ -36,11 +36,16 @@
*/ */
#include "NetJob.h" #include "NetJob.h"
#if defined(LAUNCHER_APPLICATION)
#include "Application.h" #include "Application.h"
#endif
NetJob::NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network) NetJob::NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network) : ConcurrentTask(nullptr, job_name), m_network(network)
: ConcurrentTask(nullptr, job_name, APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()), m_network(network) {
{} #if defined(LAUNCHER_APPLICATION)
setMaxConcurrent(APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
#endif
}
auto NetJob::addNetAction(NetAction::Ptr action) -> bool auto NetJob::addNetAction(NetAction::Ptr action) -> bool
{ {

View File

@ -46,6 +46,7 @@
#if defined(LAUNCHER_APPLICATION) #if defined(LAUNCHER_APPLICATION)
#include "Application.h" #include "Application.h"
#endif #endif
#include "BuildConfig.h"
#include "net/NetAction.h" #include "net/NetAction.h"
@ -111,6 +112,8 @@ void NetRequest::executeTask()
m_last_progress_bytes = 0; m_last_progress_bytes = 0;
QNetworkReply* rep = getReply(request); QNetworkReply* rep = getReply(request);
if (rep == nullptr) // it failed
return;
m_reply.reset(rep); m_reply.reset(rep);
connect(rep, &QNetworkReply::downloadProgress, this, &NetRequest::downloadProgress); connect(rep, &QNetworkReply::downloadProgress, this, &NetRequest::downloadProgress);
connect(rep, &QNetworkReply::finished, this, &NetRequest::downloadFinished); connect(rep, &QNetworkReply::finished, this, &NetRequest::downloadFinished);

View File

@ -87,7 +87,7 @@ class NetRequest : public NetAction {
std::unique_ptr<Sink> m_sink; std::unique_ptr<Sink> m_sink;
Options m_options; Options m_options;
typedef const QLoggingCategory& (*logCatFunc)(); using logCatFunc = const QLoggingCategory& (*)();
logCatFunc logCat = taskUploadLogC; logCatFunc logCat = taskUploadLogC;
std::chrono::steady_clock m_clock; std::chrono::steady_clock m_clock;

View File

@ -0,0 +1,39 @@
// 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 "net/HeaderProxy.h"
namespace Net {
class StaticHeaderProxy : public HeaderProxy {
public:
StaticHeaderProxy(QList<HeaderPair> hdrs = {}) : HeaderProxy(), m_hdrs(hdrs){};
virtual ~StaticHeaderProxy() = default;
public:
virtual QList<HeaderPair> headers(const QNetworkRequest&) const override { return m_hdrs; };
void setHeaders(QList<HeaderPair> hdrs) { m_hdrs = hdrs; };
private:
QList<HeaderPair> m_hdrs;
};
} // namespace Net

View File

@ -51,4 +51,4 @@ class NewsEntry : public QObject {
QString link; QString link;
}; };
typedef std::shared_ptr<NewsEntry> NewsEntryPtr; using NewsEntryPtr = std::shared_ptr<NewsEntry>;

View File

@ -4,7 +4,7 @@
class IPathMatcher { class IPathMatcher {
public: public:
typedef std::shared_ptr<IPathMatcher> Ptr; using Ptr = std::shared_ptr<IPathMatcher>;
public: public:
virtual ~IPathMatcher() {} virtual ~IPathMatcher() {}

View File

@ -9,6 +9,7 @@
<file>scalable/copy.svg</file> <file>scalable/copy.svg</file>
<file>scalable/coremods.svg</file> <file>scalable/coremods.svg</file>
<file>scalable/custom-commands.svg</file> <file>scalable/custom-commands.svg</file>
<file>scalable/environment-variables.svg</file>
<file>scalable/discord.svg</file> <file>scalable/discord.svg</file>
<file>scalable/externaltools.svg</file> <file>scalable/externaltools.svg</file>
<file>scalable/help.svg</file> <file>scalable/help.svg</file>

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#eff0f1;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 6 2 C 4.895478 2 4 2.8954778 4 4 L 4 6 L 4 7 C 4 7.2761493 3.7761423 7.5 3.5 7.5 L 3 7.5 L 3 8.5 L 3.5 8.5 C 3.7761423 8.5 4 8.7238507 4 9 L 4 10 L 4 11 L 4 12 C 4 13.104597 4.8954307 14 6 14 L 7 14 L 7 13 L 6 13 C 5.4477157 13 5 12.552299 5 12 L 5 11 L 5 10 L 5 9 C 5 8.617501 4.8607153 8.2649743 4.625 8 C 4.8607153 7.7350257 5 7.382499 5 7 L 5 6 L 5 5.71875 L 5 4 C 5 3.4477014 5.4477765 3 6 3 L 7 3 L 7 2 L 6 2 z M 9 2 L 9 3 L 10 3 C 10.552224 3 11 3.4477014 11 4 L 11 5.71875 L 11 6 L 11 7 C 11 7.382499 11.139285 7.7350257 11.375 8 C 11.139285 8.2649743 11 8.617501 11 9 L 11 10 L 11 11 L 11 12 C 11 12.552299 10.552284 13 10 13 L 9 13 L 9 14 L 10 14 C 11.104569 14 12 13.104597 12 12 L 12 11 L 12 10 L 12 9 C 12 8.7238507 12.223858 8.5 12.5 8.5 L 13 8.5 L 13 7.5 L 12.5 7.5 C 12.223858 7.5 12 7.2761493 12 7 L 12 6 L 12 4 C 12 2.8954778 11.104522 2 10 2 L 9 2 z "
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -9,6 +9,7 @@
<file>scalable/copy.svg</file> <file>scalable/copy.svg</file>
<file>scalable/coremods.svg</file> <file>scalable/coremods.svg</file>
<file>scalable/custom-commands.svg</file> <file>scalable/custom-commands.svg</file>
<file>scalable/environment-variables.svg</file>
<file>scalable/discord.svg</file> <file>scalable/discord.svg</file>
<file>scalable/externaltools.svg</file> <file>scalable/externaltools.svg</file>
<file>scalable/help.svg</file> <file>scalable/help.svg</file>

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#232629;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 6 2 C 4.895478 2 4 2.8954778 4 4 L 4 6 L 4 7 C 4 7.2761493 3.7761423 7.5 3.5 7.5 L 3 7.5 L 3 8.5 L 3.5 8.5 C 3.7761423 8.5 4 8.7238507 4 9 L 4 10 L 4 11 L 4 12 C 4 13.104597 4.8954307 14 6 14 L 7 14 L 7 13 L 6 13 C 5.4477157 13 5 12.552299 5 12 L 5 11 L 5 10 L 5 9 C 5 8.617501 4.8607153 8.2649743 4.625 8 C 4.8607153 7.7350257 5 7.382499 5 7 L 5 6 L 5 5.71875 L 5 4 C 5 3.4477014 5.4477765 3 6 3 L 7 3 L 7 2 L 6 2 z M 9 2 L 9 3 L 10 3 C 10.552224 3 11 3.4477014 11 4 L 11 5.71875 L 11 6 L 11 7 C 11 7.382499 11.139285 7.7350257 11.375 8 C 11.139285 8.2649743 11 8.617501 11 9 L 11 10 L 11 11 L 11 12 C 11 12.552299 10.552284 13 10 13 L 9 13 L 9 14 L 10 14 C 11.104569 14 12 13.104597 12 12 L 12 11 L 12 10 L 12 9 C 12 8.7238507 12.223858 8.5 12.5 8.5 L 13 8.5 L 13 7.5 L 12.5 7.5 C 12.223858 7.5 12 7.2761493 12 7 L 12 6 L 12 4 C 12 2.8954778 11.104522 2 10 2 L 9 2 z "
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -11,6 +11,7 @@
<file>scalable/copy.svg</file> <file>scalable/copy.svg</file>
<file>scalable/coremods.svg</file> <file>scalable/coremods.svg</file>
<file>scalable/custom-commands.svg</file> <file>scalable/custom-commands.svg</file>
<file>scalable/environment-variables.svg</file>
<file>scalable/discord.svg</file> <file>scalable/discord.svg</file>
<file>scalable/externaltools.svg</file> <file>scalable/externaltools.svg</file>
<file>scalable/help.svg</file> <file>scalable/help.svg</file>

View File

@ -1,86 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#757575"><g><rect fill="none" height="24" width="24"/></g><g><path d="M20,4H4C2.89,4,2,4.9,2,6v12c0,1.1,0.89,2,2,2h16c1.1,0,2-0.9,2-2V6C22,4.9,21.11,4,20,4z M20,18H4V8h16V18z M18,17h-6v-2 h6V17z M7.5,17l-1.41-1.41L8.67,13l-2.59-2.59L7.5,9l4,4L7.5,17z"/></g></svg>
<svg
fill="#757575"
height="24"
viewBox="0 0 24 24"
width="24"
version="1.1"
id="svg4"
sodipodi:docname="custom-commands.svg"
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
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"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3440"
inkscape:window-height="1382"
id="namedview6"
showgrid="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:zoom="39.333333"
inkscape:cx="11.38983"
inkscape:cy="13.283898"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="svg4"
inkscape:pagecheckerboard="0">
<inkscape:grid
type="xygrid"
id="grid981" />
<sodipodi:guide
position="-8,11.440678"
orientation="1,0"
id="guide1060"
inkscape:locked="false" />
<sodipodi:guide
position="-28.34375,24"
orientation="0,1"
id="guide1062"
inkscape:locked="false" />
</sodipodi:namedview>
<g
style="fill:#000000"
id="g821"
transform="matrix(0.0322459,0,0,0.0322459,-17.878956,30.647558)">
<g
style="fill:#000000"
id="g819" />
</g>
<g
id="g503">
<path
d="M 0,0 H 24 V 24 H 0 Z"
fill="none"
id="path491" />
<path
d="M 8.7,15.9 4.8,12 8.7,8.1 C 9.09,7.71 9.09,7.09 8.7,6.7 8.31,6.31 7.69,6.31 7.3,6.7 l -4.59,4.59 c -0.39,0.39 -0.39,1.02 0,1.41 l 4.59,4.6 c 0.39,0.39 1.01,0.39 1.4,0 0.39,-0.39 0.39,-1.01 0,-1.4 z m 6.6,0 3.9,-3.9 -3.9,-3.9 c -0.39,-0.39 -0.39,-1.01 0,-1.4 0.39,-0.39 1.01,-0.39 1.4,0 l 4.59,4.59 c 0.39,0.39 0.39,1.02 0,1.41 l -4.59,4.6 c -0.39,0.39 -1.01,0.39 -1.4,0 -0.39,-0.39 -0.39,-1.01 0,-1.4 z"
id="path493" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 391 B

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
fill="#757575"
height="24"
viewBox="0 0 24 24"
width="24"
version="1.1"
id="svg4"
sodipodi:docname="custom-commands.svg"
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
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"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3440"
inkscape:window-height="1382"
id="namedview6"
showgrid="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:zoom="39.333333"
inkscape:cx="11.38983"
inkscape:cy="13.283898"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="svg4"
inkscape:pagecheckerboard="0">
<inkscape:grid
type="xygrid"
id="grid981" />
<sodipodi:guide
position="-8,11.440678"
orientation="1,0"
id="guide1060"
inkscape:locked="false" />
<sodipodi:guide
position="-28.34375,24"
orientation="0,1"
id="guide1062"
inkscape:locked="false" />
</sodipodi:namedview>
<g
style="fill:#000000"
id="g821"
transform="matrix(0.0322459,0,0,0.0322459,-17.878956,30.647558)">
<g
style="fill:#000000"
id="g819" />
</g>
<g
id="g503">
<path
d="M 0,0 H 24 V 24 H 0 Z"
fill="none"
id="path491" />
<path
d="M 8.7,15.9 4.8,12 8.7,8.1 C 9.09,7.71 9.09,7.09 8.7,6.7 8.31,6.31 7.69,6.31 7.3,6.7 l -4.59,4.59 c -0.39,0.39 -0.39,1.02 0,1.41 l 4.59,4.6 c 0.39,0.39 1.01,0.39 1.4,0 0.39,-0.39 0.39,-1.01 0,-1.4 z m 6.6,0 3.9,-3.9 -3.9,-3.9 c -0.39,-0.39 -0.39,-1.01 0,-1.4 0.39,-0.39 1.01,-0.39 1.4,0 l 4.59,4.59 c 0.39,0.39 0.39,1.02 0,1.41 l -4.59,4.6 c -0.39,0.39 -1.01,0.39 -1.4,0 -0.39,-0.39 -0.39,-1.01 0,-1.4 z"
id="path493" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -11,6 +11,7 @@
<file>scalable/copy.svg</file> <file>scalable/copy.svg</file>
<file>scalable/coremods.svg</file> <file>scalable/coremods.svg</file>
<file>scalable/custom-commands.svg</file> <file>scalable/custom-commands.svg</file>
<file>scalable/environment-variables.svg</file>
<file>scalable/discord.svg</file> <file>scalable/discord.svg</file>
<file>scalable/externaltools.svg</file> <file>scalable/externaltools.svg</file>
<file>scalable/help.svg</file> <file>scalable/help.svg</file>

View File

@ -1,86 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#eeeeee"><g><rect fill="none" height="24" width="24"/></g><g><path d="M20,4H4C2.89,4,2,4.9,2,6v12c0,1.1,0.89,2,2,2h16c1.1,0,2-0.9,2-2V6C22,4.9,21.11,4,20,4z M20,18H4V8h16V18z M18,17h-6v-2 h6V17z M7.5,17l-1.41-1.41L8.67,13l-2.59-2.59L7.5,9l4,4L7.5,17z"/></g></svg>
<svg
fill="#eeeeee"
height="24"
viewBox="0 0 24 24"
width="24"
version="1.1"
id="svg4"
sodipodi:docname="custom-commands.svg"
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
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"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3440"
inkscape:window-height="1382"
id="namedview6"
showgrid="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:zoom="39.333333"
inkscape:cx="11.38983"
inkscape:cy="13.283898"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="svg4"
inkscape:pagecheckerboard="0">
<inkscape:grid
type="xygrid"
id="grid981" />
<sodipodi:guide
position="-8,11.440678"
orientation="1,0"
id="guide1060"
inkscape:locked="false" />
<sodipodi:guide
position="-28.34375,24"
orientation="0,1"
id="guide1062"
inkscape:locked="false" />
</sodipodi:namedview>
<g
style="fill:#000000"
id="g821"
transform="matrix(0.0322459,0,0,0.0322459,-17.878956,30.647558)">
<g
style="fill:#000000"
id="g819" />
</g>
<g
id="g503">
<path
d="M 0,0 H 24 V 24 H 0 Z"
fill="none"
id="path491" />
<path
d="M 8.7,15.9 4.8,12 8.7,8.1 C 9.09,7.71 9.09,7.09 8.7,6.7 8.31,6.31 7.69,6.31 7.3,6.7 l -4.59,4.59 c -0.39,0.39 -0.39,1.02 0,1.41 l 4.59,4.6 c 0.39,0.39 1.01,0.39 1.4,0 0.39,-0.39 0.39,-1.01 0,-1.4 z m 6.6,0 3.9,-3.9 -3.9,-3.9 c -0.39,-0.39 -0.39,-1.01 0,-1.4 0.39,-0.39 1.01,-0.39 1.4,0 l 4.59,4.59 c 0.39,0.39 0.39,1.02 0,1.41 l -4.59,4.6 c -0.39,0.39 -1.01,0.39 -1.4,0 -0.39,-0.39 -0.39,-1.01 0,-1.4 z"
id="path493" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 391 B

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
fill="#eeeeee"
height="24"
viewBox="0 0 24 24"
width="24"
version="1.1"
id="svg4"
sodipodi:docname="custom-commands.svg"
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
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"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3440"
inkscape:window-height="1382"
id="namedview6"
showgrid="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:zoom="39.333333"
inkscape:cx="11.38983"
inkscape:cy="13.283898"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="svg4"
inkscape:pagecheckerboard="0">
<inkscape:grid
type="xygrid"
id="grid981" />
<sodipodi:guide
position="-8,11.440678"
orientation="1,0"
id="guide1060"
inkscape:locked="false" />
<sodipodi:guide
position="-28.34375,24"
orientation="0,1"
id="guide1062"
inkscape:locked="false" />
</sodipodi:namedview>
<g
style="fill:#000000"
id="g821"
transform="matrix(0.0322459,0,0,0.0322459,-17.878956,30.647558)">
<g
style="fill:#000000"
id="g819" />
</g>
<g
id="g503">
<path
d="M 0,0 H 24 V 24 H 0 Z"
fill="none"
id="path491" />
<path
d="M 8.7,15.9 4.8,12 8.7,8.1 C 9.09,7.71 9.09,7.09 8.7,6.7 8.31,6.31 7.69,6.31 7.3,6.7 l -4.59,4.59 c -0.39,0.39 -0.39,1.02 0,1.41 l 4.59,4.6 c 0.39,0.39 1.01,0.39 1.4,0 0.39,-0.39 0.39,-1.01 0,-1.4 z m 6.6,0 3.9,-3.9 -3.9,-3.9 c -0.39,-0.39 -0.39,-1.01 0,-1.4 0.39,-0.39 1.01,-0.39 1.4,0 l 4.59,4.59 c 0.39,0.39 0.39,1.02 0,1.41 l -4.59,4.6 c -0.39,0.39 -1.01,0.39 -1.4,0 -0.39,-0.39 -0.39,-1.01 0,-1.4 z"
id="path493" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -73,8 +73,8 @@
<file>64x64/screenshots.png</file> <file>64x64/screenshots.png</file>
<file>scalable/screenshots.svg</file> <file>scalable/screenshots.svg</file>
<!-- Custom commands. -->
<file>scalable/custom-commands.svg</file> <file>scalable/custom-commands.svg</file>
<file>scalable/environment-variables.svg</file>
<!-- The cat button. Freeware, http://findicons.com/icon/73096/black_cat --> <!-- The cat button. Freeware, http://findicons.com/icon/73096/black_cat -->
<file>16x16/cat.png</file> <file>16x16/cat.png</file>

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View File

@ -0,0 +1,346 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="128"
height="128"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.47pre3 r22311"
version="1.0"
sodipodi:docname="code-context.svgz"
inkscape:output_extension="org.inkscape.output.svgz.inkscape"
inkscape:export-filename="/home/pinheiro/pics/oxygen-icons/scalable/actions/small/48x48/context.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient5440">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop5442" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop5444" />
</linearGradient>
<linearGradient
id="linearGradient5342">
<stop
style="stop-color:#232221;stop-opacity:1;"
offset="0"
id="stop5344" />
<stop
id="stop5394"
offset="0.10646833"
style="stop-color:#555350;stop-opacity:1;" />
<stop
style="stop-color:#918d88;stop-opacity:1;"
offset="1"
id="stop5346" />
</linearGradient>
<linearGradient
id="linearGradient3857"
inkscape:collect="always">
<stop
id="stop3859"
offset="0"
style="stop-color:#ffffff;stop-opacity:1;" />
<stop
id="stop3861"
offset="1"
style="stop-color:#ffffff;stop-opacity:0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5342"
id="linearGradient5348"
x1="80.151077"
y1="-120.00011"
x2="80.151077"
y2="-15.919633"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.85712143,0,0,0.85712149,15.99961,-9.1458159)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3857"
id="linearGradient5378"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.85712141,0,0,-0.85442216,15.99961,9.407114)"
x1="76"
y1="-4"
x2="76"
y2="-124" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3857"
id="radialGradient5386"
cx="64"
cy="136"
fx="64"
fy="136"
r="48"
gradientTransform="matrix(1.3086619,0,0,0.65227042,-19.755964,19.661784)"
gradientUnits="userSpaceOnUse" />
<filter
inkscape:collect="always"
id="filter5416"
x="-0.21917803"
width="1.4383561"
y="-0.21917814"
height="1.4383563">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="1.4611876"
id="feGaussianBlur5418" />
</filter>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath5420">
<path
style="fill:url(#radialGradient5424);fill-opacity:1;stroke:none"
d="m 108,8 c -52,0 -36,4 0,4 3.57143,0 8,2.700959 8,8 0,24 4,24 4,0 0,-5.923499 -4.70207,-12 -12,-12 z"
id="path5422"
sodipodi:nodetypes="ccccc" />
</clipPath>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3857"
id="radialGradient5424"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.70743936,0.70677405,-0.99807448,0.99901401,48.73805,-79.148369)"
cx="112.00771"
cy="15.99981"
fx="112.00771"
fy="15.99981"
r="8.0077057" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3857"
id="radialGradient5426"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.70727305,0.70694048,-1.9971123,1.9980518,64.741093,-95.151426)"
cx="112.00771"
cy="15.99981"
fx="112.00771"
fy="15.99981"
r="8.0077057" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3857"
id="radialGradient5430"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.70727305,0.70694048,-1.9971123,1.9980518,64.741093,-95.151426)"
cx="112.00771"
cy="15.99981"
fx="112.00771"
fy="15.99981"
r="8.0077057" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3857"
id="radialGradient5436"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.70727305,0.70694048,-1.9971123,1.9980518,64.741093,-95.151426)"
cx="112.00771"
cy="15.99981"
fx="112.00771"
fy="15.99981"
r="8.0077057" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3857"
id="radialGradient5438"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.70727305,0.70694048,-1.9971123,1.9980518,64.741093,-95.151426)"
cx="112.00771"
cy="15.99981"
fx="112.00771"
fy="15.99981"
r="8.0077057" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5440"
id="linearGradient5451"
gradientUnits="userSpaceOnUse"
x1="71.937897"
y1="31.666744"
x2="71.937897"
y2="184.00002"
gradientTransform="matrix(0.49730883,0,0,0.49574271,32.172236,26.323555)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3857"
id="linearGradient5453"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.85712141,0,0,-0.85442216,15.99961,9.25759)"
x1="76"
y1="-8"
x2="76"
y2="-72.117546" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3857"
id="radialGradient5480"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.70727305,0.70694048,-1.9971123,1.9980518,64.741093,-95.151426)"
cx="112.00771"
cy="15.99981"
fx="112.00771"
fy="15.99981"
r="8.0077057" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3857"
id="radialGradient5482"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.70727305,0.70694048,-1.9971123,1.9980518,64.741093,-95.151426)"
cx="112.00771"
cy="15.99981"
fx="112.00771"
fy="15.99981"
r="8.0077057" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
gridtolerance="10000"
guidetolerance="10"
objecttolerance="10"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.4029514"
inkscape:cx="135.41032"
inkscape:cy="66.337315"
inkscape:document-units="px"
inkscape:current-layer="layer1"
width="128px"
height="128px"
showgrid="true"
inkscape:window-width="1040"
inkscape:window-height="728"
inkscape:window-x="374"
inkscape:window-y="333"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-nodes="false"
inkscape:snap-bbox="true"
inkscape:window-maximized="0">
<inkscape:grid
id="GridFromPre046Settings"
type="xygrid"
originx="0px"
originy="0px"
spacingx="2.6666px"
spacingy="2.6666px"
color="#0000ff"
empcolor="#0000ff"
opacity="0.2"
empopacity="0.4"
empspacing="2"
visible="true"
enabled="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:url(#linearGradient5348);fill-opacity:1;stroke:none"
id="rect3888"
width="95.997589"
height="95.997597"
x="15.999609"
y="-112.0004"
rx="10.285457"
ry="10.285458"
transform="scale(1,-1)" />
<path
style="opacity:0.60465118;fill:url(#linearGradient5453);fill-opacity:1;stroke:none"
d="m 25.485068,18.710656 c -3.798763,0 -6.856972,3.048578 -6.856972,6.835377 l 0.8,37.219744 c 6.539821,0.759062 16.86475,1.174831 23.9994,1.174831 25.561145,0 52.250262,-5.22011 65.841214,-13.190143 l 0,-25.104432 c 0,-3.786799 -3.05819,-6.835377 -6.85697,-6.835377 l -76.926672,-0.1 z"
id="rect5352"
sodipodi:nodetypes="cccscccc" />
<path
style="font-size:113.80991364px;fill:url(#linearGradient5451);fill-opacity:1"
d="m 56.043058,38.22138 c -4.502355,0.02208 -8.022848,0.601203 -9.573194,1.921002 -1.562598,1.327923 -2.331149,4.004842 -2.331134,8.024838 l 0,6.537605 c -1.5e-5,2.710357 -0.456344,4.708001 -1.460845,5.763007 -0.985927,1.036861 -2.157584,1.549216 -4.817681,1.549196 l -1.7095,0 0,3.965943 1.7095,0 c 2.641495,1.6e-5 3.81315,0.463195 4.817681,1.518211 1.004501,1.055044 1.46083,2.861382 1.460845,5.608091 l 0,2.556172 c -1.5e-5,4.020026 0.768536,10.660189 2.331134,12.006269 1.550346,1.319827 5.070839,2.077935 9.573194,2.106906 l 0,-3.965941 c -2.581373,-0.01061 -5.752521,-0.464158 -6.54272,-1.254849 -0.799914,-0.800372 -1.19667,-6.451506 -1.196649,-9.016319 l 0,-2.788555 C 48.303668,69.915298 47.928575,67.94064 47.091497,66.649126 46.254384,65.357638 45.51975,64.49115 43.454931,64 c 2.046218,-0.454739 2.768369,-1.342112 3.605484,-2.633634 0.855681,-1.291479 1.243253,-3.464838 1.243274,-6.32072 l 0,-6.769983 c -2.1e-5,-2.564784 0.396735,-4.249978 1.196649,-5.050381 0.790199,-0.790653 3.961347,-1.030684 6.54272,-1.037961 l 0,-3.965941 z m 15.913882,0 0,3.965941 c 2.581375,0.0072 5.752523,0.247308 6.542722,1.037961 0.799912,0.800403 1.19667,2.485597 1.196648,5.050381 l 0,6.769983 c 2.2e-5,2.855882 0.387593,5.029241 1.243273,6.32072 0.837117,1.291522 1.55927,2.178895 3.605488,2.633634 -2.064821,0.49115 -2.799455,1.357638 -3.636568,2.649126 -0.837078,1.291514 -1.212171,3.266172 -1.212193,6.10383 l 0,2.788555 c 2.2e-5,2.564813 -0.396736,8.215947 -1.196648,9.016319 -0.790199,0.790691 -3.961347,1.244092 -6.542722,1.254849 l 0,3.965941 c 4.502355,-0.02897 8.02285,-0.787079 9.573197,-2.106906 1.562597,-1.34608 2.33115,-7.986243 2.331135,-12.006269 l 0,-2.556172 c 1.5e-5,-2.746709 0.456342,-4.553047 1.460846,-5.608091 1.004528,-1.055016 2.176183,-1.518195 4.817678,-1.518211 l 1.7095,0 0,-3.965943 -1.7095,0 c -2.660094,2e-5 -3.831749,-0.512335 -4.817678,-1.549196 -1.004504,-1.055006 -1.460831,-3.05265 -1.460846,-5.763007 l 0,-6.537605 c 1.5e-5,-4.019996 -0.768538,-6.696915 -2.331135,-8.024838 C 79.97979,38.822583 76.459295,38.243453 71.95694,38.22138 z"
id="path3006"
sodipodi:nodetypes="csccsccccscccccsccsccccsccccsccccsccscccccsccccsccsc" />
<path
style="fill:url(#linearGradient5378);fill-opacity:1;stroke:none"
d="m 107.87589,25.774638 c 0.72539,1.084602 1.12497,2.38464 1.12497,3.791498 l 0,20.986745 c -13.590951,7.970031 -40.28007,13.190143 -65.841216,13.190143 -7.038772,0 -13.841689,-0.408385 -20.303062,-1.148132 l 0,0.320409 c 6.539821,0.759061 13.436264,1.17483 20.570914,1.17483 25.561145,0 52.250262,-5.22011 65.841214,-13.19014 l 0,-20.986745 c 0,-1.559831 -0.517,-2.990759 -1.39282,-4.138608 z"
id="path5369"
sodipodi:nodetypes="cccsccsccc" />
<path
id="path5380"
d="m 25.485068,109.37056 c -3.798763,0 -6.856972,-3.04858 -6.856972,-6.83539 l 0,-33.002043 c 6.539821,-0.759061 13.436266,-1.174829 20.570912,-1.174829 25.561149,0 56.57875,-25.058477 70.169702,-17.088444 l 0,51.265316 c 0,3.78681 -3.05819,6.83539 -6.85697,6.83539 l -77.026672,0 z"
style="opacity:0.4;fill:url(#radialGradient5386);fill-opacity:1;stroke:none"
sodipodi:nodetypes="cccscccc" />
<path
sodipodi:nodetypes="ccccc"
id="path5414"
d="m 108,8 -4,8 c 3.30365,-0.176594 7.9579,2.481149 8,8 l 8,-4 C 120.007,14.388483 115.55603,8.131068 108,8 z"
style="fill:url(#radialGradient5426);fill-opacity:1;stroke:none;filter:url(#filter5416)"
clip-path="url(#clipPath5420)"
transform="matrix(0.85712141,0,0,0.85442216,9.142639,9.25759)" />
<path
clip-path="url(#clipPath5420)"
style="fill:url(#radialGradient5430);fill-opacity:1;stroke:none;filter:url(#filter5416)"
d="m 108,8 -4,8 c 3.30365,-0.176594 7.9579,2.481149 8,8 l 8,-4 C 120.007,14.388483 115.55603,8.131068 108,8 z"
id="path5428"
sodipodi:nodetypes="ccccc"
transform="matrix(-0.85712141,0,0,0.85442216,118.85417,9.25759)" />
<path
clip-path="url(#clipPath5420)"
style="opacity:0.52558139;fill:url(#radialGradient5436);fill-opacity:1;stroke:none;filter:url(#filter5416)"
d="m 108,8 -4,8 c 3.30365,-0.176594 7.9579,2.481149 8,8 l 8,-4 C 120.007,14.388483 115.55603,8.131068 108,8 z"
id="path5432"
sodipodi:nodetypes="ccccc"
transform="matrix(0.85712141,0,0,-0.85442216,9.142639,118.83578)" />
<path
transform="matrix(-0.85712141,0,0,-0.85442216,118.85417,118.83578)"
sodipodi:nodetypes="ccccc"
id="path5434"
d="m 108,8 -4,8 c 3.30365,-0.176594 7.9579,2.481149 8,8 l 8,-4 C 120.007,14.388483 115.55603,8.131068 108,8 z"
style="opacity:0.52558139;fill:url(#radialGradient5438);fill-opacity:1;stroke:none;filter:url(#filter5416)"
clip-path="url(#clipPath5420)" />
<path
clip-path="url(#clipPath5420)"
style="fill:url(#radialGradient5482);fill-opacity:1;stroke:none;filter:url(#filter5416)"
d="m 108,8 -4,8 c 3.30365,-0.176594 7.9579,2.481149 8,8 l 8,-4 C 120.007,14.388483 115.55603,8.131068 108,8 z"
id="path5455"
sodipodi:nodetypes="ccccc"
transform="matrix(0.85712141,0,0,0.85442216,9.142639,9.25759)" />
<path
transform="matrix(-0.85712141,0,0,0.85442216,118.85417,9.25759)"
sodipodi:nodetypes="ccccc"
id="path5457"
d="m 108,8 -4,8 c 3.30365,-0.176594 7.9579,2.481149 8,8 l 8,-4 C 120.007,14.388483 115.55603,8.131068 108,8 z"
style="fill:url(#radialGradient5480);fill-opacity:1;stroke:none;filter:url(#filter5416)"
clip-path="url(#clipPath5420)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -10,6 +10,7 @@
<file>scalable/copy.svg</file> <file>scalable/copy.svg</file>
<file>scalable/coremods.svg</file> <file>scalable/coremods.svg</file>
<file>scalable/custom-commands.svg</file> <file>scalable/custom-commands.svg</file>
<file>scalable/environment-variables.svg</file>
<file>scalable/externaltools.svg</file> <file>scalable/externaltools.svg</file>
<file>scalable/help.svg</file> <file>scalable/help.svg</file>
<file>scalable/instance-settings.svg</file> <file>scalable/instance-settings.svg</file>

View File

@ -0,0 +1,345 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="svg2"
height="32"
width="32"
version="1.1"
sodipodi:docname="environment-variables.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:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
pagecolor="#505050"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1011"
id="namedview52"
showgrid="false"
inkscape:zoom="7.375"
inkscape:cx="21.559322"
inkscape:cy="-2.9830509"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:bbox-nodes="true"
inkscape:pagecheckerboard="true"
inkscape:showpageshadow="2"
inkscape:deskcolor="#505050">
<inkscape:grid
type="xygrid"
id="grid858" />
</sodipodi:namedview>
<defs
id="defs4">
<linearGradient
id="linearGradient3931">
<stop
offset="0"
style="stop-color:#ffffff;stop-opacity:0"
id="stop3933" />
<stop
offset="0.69999987"
style="stop-color:#ffffff;stop-opacity:0.10396039"
id="stop3939" />
<stop
offset="1"
style="stop-color:#ffffff;stop-opacity:0.14356436"
id="stop3935" />
</linearGradient>
<linearGradient
id="linearGradient3900">
<stop
offset="0"
style="stop-color:#f6f6f6;stop-opacity:1"
id="stop3902" />
<stop
offset="0.75714284"
style="stop-color:#494949;stop-opacity:1"
id="stop3904" />
<stop
offset="1"
style="stop-color:#2c2c2c;stop-opacity:1"
id="stop3906" />
</linearGradient>
<linearGradient
id="linearGradient3808">
<stop
offset="0"
style="stop-color:#333333;stop-opacity:1"
id="stop3810" />
<stop
offset="1"
style="stop-color:#c8c8c8;stop-opacity:1"
id="stop3812" />
</linearGradient>
<linearGradient
id="linearGradient3030">
<stop
offset="0"
style="stop-color:#000000;stop-opacity:1"
id="stop3032" />
<stop
offset="0.75714284"
style="stop-color:#333333;stop-opacity:1"
id="stop3038" />
<stop
offset="1"
style="stop-color:#4d4d4d;stop-opacity:1"
id="stop3034" />
</linearGradient>
<radialGradient
gradientTransform="matrix(0.67596238,0.94191445,-0.76796117,0.55112488,7.7178628,-19.890271)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3030"
id="radialGradient3036"
fy="14.242621"
fx="29.381905"
r="16.375"
cy="14.242621"
cx="29.381905" />
<linearGradient
gradientTransform="matrix(1.5,0,0,1,-16,4)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3808"
id="linearGradient3824"
y2="1033.8622"
x2="34"
y1="1033.8622"
x1="30" />
<linearGradient
gradientTransform="matrix(0.82142857,0,0,1.500001,6.7142857,-522.68214)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3808"
id="linearGradient3834"
y2="1039.3622"
x2="32"
y1="1043.3622"
x1="32" />
<radialGradient
gradientTransform="matrix(6.479993,1.9525666,-10.415476,2.1794781,10657.845,-1282.8793)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3900"
id="radialGradient3844"
fy="1039.813"
fx="30.724609"
r="3"
cy="1039.813"
cx="30.724609" />
<radialGradient
gradientTransform="matrix(2.5191507,2.9862959,-4.0491019,3.333339,4186.8847,-2518.44)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3900"
id="radialGradient3852"
fy="1039.813"
fx="30.724609"
r="3"
cy="1039.813"
cx="30.724609" />
<radialGradient
gradientTransform="matrix(-2.5191507,2.9863064,4.0491022,3.3333507,-4122.8849,-2518.4524)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3900"
id="radialGradient3857"
fy="1039.813"
fx="30.724609"
r="3"
cy="1039.813"
cx="30.724609" />
<radialGradient
gradientTransform="matrix(-0.69414478,2.3073251,-1.6952184,-0.67174747,96.941544,960.82172)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3900"
id="radialGradient3937"
fy="21.976955"
fx="31.946348"
r="19.25"
cy="21.976955"
cx="31.946348" />
</defs>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#3366cc"
d="M 26,32 H 6 C 2.7,32 0,29.3 0,26 V 6 C 0,2.7 2.7,0 6,0 h 20 c 3.3,0 6,2.7 6,6 v 20 c 0,3.3 -2.7,6 -6,6 z"
id="path2" />
<path
fill="#daeeff"
fill-rule="evenodd"
clip-rule="evenodd"
d="M 28,6 C 28,4.9 27.1,4 26,4 H 6 C 4.9,4 4,4.9 4,6 v 20 c 0,1.1 0.9,2 2,2 h 20 c 1.1,0 2,-0.9 2,-2 z"
id="path4" />
<g
style="fill:#008000"
id="g869"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g871"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g873"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g875"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g877"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g879"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g881"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g883"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g885"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g887"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g889"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g891"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g893"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g895"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g897"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
id="g856"
style="fill:#c1272d;fill-opacity:1"
transform="matrix(0.0361121,0,0,0.0361121,6.5218379,6.0218363)">
<g
id="g854"
style="fill:#c1272d;fill-opacity:1">
<path
inkscape:connector-curvature="0"
id="path850"
d="m -14.4505,276.30781 c 0,-6.995 2.705,-13.403 7.846,-18.556 l 156.788,-156.782 c 5.128,-5.140997 11.554,-7.851997 18.568,-7.851997 7.026,0 13.452,2.717 18.556,7.845997 l 16.83,16.83 c 5.129,5.135 7.84,11.549 7.84,18.538 0,7.026 -2.717,13.452 -7.846,18.556 l -121.415001,121.42 121.427001,121.433 c 5.129,5.135 7.84,11.555 7.84,18.55 0,7.02 -2.717,13.439 -7.846,18.544 l -16.775,16.774 c -5.116,5.165 -11.555,7.895 -18.611,7.895 -7.044,0 -13.47,-2.723 -18.556,-7.846 l -156.813,-156.8 c -5.128,-5.14 -7.833,-11.549 -7.833,-18.55 z"
style="fill:#c1272d;fill-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path322"
d="m 539.38069,276.30781 c 0,-6.995 -2.705,-13.403 -7.846,-18.556 l -156.788,-156.782 c -5.128,-5.140997 -11.554,-7.851997 -18.568,-7.851997 -7.026,0 -13.452,2.717 -18.556,7.845997 l -16.83,16.83 c -5.129,5.135 -7.84,11.549 -7.84,18.538 0,7.026 2.717,13.452 7.846,18.556 l 121.415,121.42 -121.427,121.433 c -5.129,5.135 -7.84,11.555 -7.84,18.55 0,7.02 2.717,13.439 7.846,18.544 l 16.775,16.774 c 5.116,5.165 11.555,7.895 18.611,7.895 7.044,0 13.47,-2.723 18.556,-7.846 l 156.813,-156.8 c 5.128,-5.14 7.833,-11.549 7.833,-18.55 z"
style="fill:#c1272d;fill-opacity:1" />
</g>
</g>
<g
id="g858"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g860"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g862"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g864"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g866"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g868"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g870"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g872"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g874"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g876"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g878"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g880"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g882"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g884"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g886"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g12">
<path
id="path6"
d="m 6,28 h 20 c 1.1,0 2,-0.9 2,-2 V 9 6 C 28,4.9 27.1,4 26,4 H 6 C 4.9,4 4,4.9 4,6 v 3 17 c 0,1.1 0.9,2 2,2 z"
fill="none" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -10,6 +10,7 @@
<file>scalable/copy.svg</file> <file>scalable/copy.svg</file>
<file>scalable/coremods.svg</file> <file>scalable/coremods.svg</file>
<file>scalable/custom-commands.svg</file> <file>scalable/custom-commands.svg</file>
<file>scalable/environment-variables.svg</file>
<file>scalable/externaltools.svg</file> <file>scalable/externaltools.svg</file>
<file>scalable/help.svg</file> <file>scalable/help.svg</file>
<file>scalable/instance-settings.svg</file> <file>scalable/instance-settings.svg</file>

View File

@ -0,0 +1,347 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="svg2"
height="32"
width="32"
version="1.1"
sodipodi:docname="environment-variables.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:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
pagecolor="#505050"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1011"
id="namedview52"
showgrid="false"
inkscape:zoom="10.429825"
inkscape:cx="3.4995793"
inkscape:cy="11.793103"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:bbox-nodes="true"
inkscape:pagecheckerboard="true"
inkscape:showpageshadow="2"
inkscape:deskcolor="#505050">
<inkscape:grid
type="xygrid"
id="grid858" />
</sodipodi:namedview>
<defs
id="defs4">
<linearGradient
id="linearGradient3931">
<stop
offset="0"
style="stop-color:#ffffff;stop-opacity:0"
id="stop3933" />
<stop
offset="0.69999987"
style="stop-color:#ffffff;stop-opacity:0.10396039"
id="stop3939" />
<stop
offset="1"
style="stop-color:#ffffff;stop-opacity:0.14356436"
id="stop3935" />
</linearGradient>
<linearGradient
id="linearGradient3900">
<stop
offset="0"
style="stop-color:#f6f6f6;stop-opacity:1"
id="stop3902" />
<stop
offset="0.75714284"
style="stop-color:#494949;stop-opacity:1"
id="stop3904" />
<stop
offset="1"
style="stop-color:#2c2c2c;stop-opacity:1"
id="stop3906" />
</linearGradient>
<linearGradient
id="linearGradient3808">
<stop
offset="0"
style="stop-color:#333333;stop-opacity:1"
id="stop3810" />
<stop
offset="1"
style="stop-color:#c8c8c8;stop-opacity:1"
id="stop3812" />
</linearGradient>
<linearGradient
id="linearGradient3030">
<stop
offset="0"
style="stop-color:#000000;stop-opacity:1"
id="stop3032" />
<stop
offset="0.75714284"
style="stop-color:#333333;stop-opacity:1"
id="stop3038" />
<stop
offset="1"
style="stop-color:#4d4d4d;stop-opacity:1"
id="stop3034" />
</linearGradient>
<radialGradient
gradientTransform="matrix(0.67596238,0.94191445,-0.76796117,0.55112488,7.7178628,-19.890271)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3030"
id="radialGradient3036"
fy="14.242621"
fx="29.381905"
r="16.375"
cy="14.242621"
cx="29.381905" />
<linearGradient
gradientTransform="matrix(1.5,0,0,1,-16,4)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3808"
id="linearGradient3824"
y2="1033.8622"
x2="34"
y1="1033.8622"
x1="30" />
<linearGradient
gradientTransform="matrix(0.82142857,0,0,1.500001,6.7142857,-522.68214)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3808"
id="linearGradient3834"
y2="1039.3622"
x2="32"
y1="1043.3622"
x1="32" />
<radialGradient
gradientTransform="matrix(6.479993,1.9525666,-10.415476,2.1794781,10657.845,-1282.8793)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3900"
id="radialGradient3844"
fy="1039.813"
fx="30.724609"
r="3"
cy="1039.813"
cx="30.724609" />
<radialGradient
gradientTransform="matrix(2.5191507,2.9862959,-4.0491019,3.333339,4186.8847,-2518.44)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3900"
id="radialGradient3852"
fy="1039.813"
fx="30.724609"
r="3"
cy="1039.813"
cx="30.724609" />
<radialGradient
gradientTransform="matrix(-2.5191507,2.9863064,4.0491022,3.3333507,-4122.8849,-2518.4524)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3900"
id="radialGradient3857"
fy="1039.813"
fx="30.724609"
r="3"
cy="1039.813"
cx="30.724609" />
<radialGradient
gradientTransform="matrix(-0.69414478,2.3073251,-1.6952184,-0.67174747,96.941544,960.82172)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3900"
id="radialGradient3937"
fy="21.976955"
fx="31.946348"
r="19.25"
cy="21.976955"
cx="31.946348" />
</defs>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<path
id="path2"
d="M 28,6 C 28,4.9 27.1,4 26,4 H 6 C 4.9,4 4,4.9 4,6 v 20 c 0,1.1 0.9,2 2,2 h 20 c 1.1,0 2,-0.9 2,-2 z"
fill="#f2f2f2"
clip-rule="evenodd"
fill-rule="evenodd" />
<g
style="fill:#008000"
id="g869"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g871"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g873"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g875"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g877"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g879"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g881"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g883"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g885"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g887"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g889"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g891"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g893"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g895"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g897"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
id="g856"
style="fill:#c1272d;fill-opacity:1"
transform="matrix(0.0361121,0,0,0.0361121,6.5218379,6.0218363)">
<g
id="g854"
style="fill:#c1272d;fill-opacity:1">
<path
inkscape:connector-curvature="0"
id="path850"
d="m -14.4505,276.30781 c 0,-6.995 2.705,-13.403 7.846,-18.556 l 156.788,-156.782 c 5.128,-5.140997 11.554,-7.851997 18.568,-7.851997 7.026,0 13.452,2.717 18.556,7.845997 l 16.83,16.83 c 5.129,5.135 7.84,11.549 7.84,18.538 0,7.026 -2.717,13.452 -7.846,18.556 l -121.415001,121.42 121.427001,121.433 c 5.129,5.135 7.84,11.555 7.84,18.55 0,7.02 -2.717,13.439 -7.846,18.544 l -16.775,16.774 c -5.116,5.165 -11.555,7.895 -18.611,7.895 -7.044,0 -13.47,-2.723 -18.556,-7.846 l -156.813,-156.8 c -5.128,-5.14 -7.833,-11.549 -7.833,-18.55 z"
style="fill:#c1272d;fill-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path322"
d="m 539.38069,276.30781 c 0,-6.995 -2.705,-13.403 -7.846,-18.556 l -156.788,-156.782 c -5.128,-5.140997 -11.554,-7.851997 -18.568,-7.851997 -7.026,0 -13.452,2.717 -18.556,7.845997 l -16.83,16.83 c -5.129,5.135 -7.84,11.549 -7.84,18.538 0,7.026 2.717,13.452 7.846,18.556 l 121.415,121.42 -121.427,121.433 c -5.129,5.135 -7.84,11.555 -7.84,18.55 0,7.02 2.717,13.439 7.846,18.544 l 16.775,16.774 c 5.116,5.165 11.555,7.895 18.611,7.895 7.044,0 13.47,-2.723 18.556,-7.846 l 156.813,-156.8 c 5.128,-5.14 7.833,-11.549 7.833,-18.55 z"
style="fill:#c1272d;fill-opacity:1" />
</g>
</g>
<g
id="g858"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g860"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g862"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g864"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g866"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g868"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g870"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g872"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g874"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g876"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g878"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g880"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g882"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g884"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g886"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g12">
<path
id="path6"
d="m 6,28 h 20 c 1.1,0 2,-0.9 2,-2 V 9 6 C 28,4.9 27.1,4 26,4 H 6 C 4.9,4 4,4.9 4,6 v 3 17 c 0,1.1 0.9,2 2,2 z"
fill="none" />
<path
id="path8"
d="M 26,0 H 6 C 2.7,0 0,2.7 0,6 V 9 H 4 V 6 C 4,4.9 4.9,4 6,4 h 20 c 1.1,0 2,0.9 2,2 v 3 h 4 V 6 C 32,2.7 29.3,0 26,0 Z"
fill="#39b54a" />
<path
id="path10"
d="m 28,26 c 0,1.1 -0.9,2 -2,2 H 6 C 4.9,28 4,27.1 4,26 V 9 H 0 v 17 c 0,3.3 2.7,6 6,6 h 20 c 3.3,0 6,-2.7 6,-6 V 9 h -4 z"
fill="#8c6239" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -10,6 +10,7 @@
<file>scalable/copy.svg</file> <file>scalable/copy.svg</file>
<file>scalable/coremods.svg</file> <file>scalable/coremods.svg</file>
<file>scalable/custom-commands.svg</file> <file>scalable/custom-commands.svg</file>
<file>scalable/environment-variables.svg</file>
<file>scalable/externaltools.svg</file> <file>scalable/externaltools.svg</file>
<file>scalable/help.svg</file> <file>scalable/help.svg</file>
<file>scalable/instance-settings.svg</file> <file>scalable/instance-settings.svg</file>

View File

@ -0,0 +1,345 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="svg2"
height="32"
width="32"
version="1.1"
sodipodi:docname="environment-variables.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:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
pagecolor="#505050"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1720"
inkscape:window-height="749"
id="namedview52"
showgrid="false"
inkscape:zoom="14.75"
inkscape:cx="20.305085"
inkscape:cy="19.79661"
inkscape:window-x="153"
inkscape:window-y="287"
inkscape:window-maximized="0"
inkscape:current-layer="svg2"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:bbox-nodes="true"
inkscape:pagecheckerboard="true"
inkscape:showpageshadow="2"
inkscape:deskcolor="#505050">
<inkscape:grid
type="xygrid"
id="grid858" />
</sodipodi:namedview>
<defs
id="defs4">
<linearGradient
id="linearGradient3931">
<stop
offset="0"
style="stop-color:#ffffff;stop-opacity:0"
id="stop3933" />
<stop
offset="0.69999987"
style="stop-color:#ffffff;stop-opacity:0.10396039"
id="stop3939" />
<stop
offset="1"
style="stop-color:#ffffff;stop-opacity:0.14356436"
id="stop3935" />
</linearGradient>
<linearGradient
id="linearGradient3900">
<stop
offset="0"
style="stop-color:#f6f6f6;stop-opacity:1"
id="stop3902" />
<stop
offset="0.75714284"
style="stop-color:#494949;stop-opacity:1"
id="stop3904" />
<stop
offset="1"
style="stop-color:#2c2c2c;stop-opacity:1"
id="stop3906" />
</linearGradient>
<linearGradient
id="linearGradient3808">
<stop
offset="0"
style="stop-color:#333333;stop-opacity:1"
id="stop3810" />
<stop
offset="1"
style="stop-color:#c8c8c8;stop-opacity:1"
id="stop3812" />
</linearGradient>
<linearGradient
id="linearGradient3030">
<stop
offset="0"
style="stop-color:#000000;stop-opacity:1"
id="stop3032" />
<stop
offset="0.75714284"
style="stop-color:#333333;stop-opacity:1"
id="stop3038" />
<stop
offset="1"
style="stop-color:#4d4d4d;stop-opacity:1"
id="stop3034" />
</linearGradient>
<radialGradient
gradientTransform="matrix(0.67596238,0.94191445,-0.76796117,0.55112488,7.7178628,-19.890271)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3030"
id="radialGradient3036"
fy="14.242621"
fx="29.381905"
r="16.375"
cy="14.242621"
cx="29.381905" />
<linearGradient
gradientTransform="matrix(1.5,0,0,1,-16,4)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3808"
id="linearGradient3824"
y2="1033.8622"
x2="34"
y1="1033.8622"
x1="30" />
<linearGradient
gradientTransform="matrix(0.82142857,0,0,1.500001,6.7142857,-522.68214)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3808"
id="linearGradient3834"
y2="1039.3622"
x2="32"
y1="1043.3622"
x1="32" />
<radialGradient
gradientTransform="matrix(6.479993,1.9525666,-10.415476,2.1794781,10657.845,-1282.8793)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3900"
id="radialGradient3844"
fy="1039.813"
fx="30.724609"
r="3"
cy="1039.813"
cx="30.724609" />
<radialGradient
gradientTransform="matrix(2.5191507,2.9862959,-4.0491019,3.333339,4186.8847,-2518.44)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3900"
id="radialGradient3852"
fy="1039.813"
fx="30.724609"
r="3"
cy="1039.813"
cx="30.724609" />
<radialGradient
gradientTransform="matrix(-2.5191507,2.9863064,4.0491022,3.3333507,-4122.8849,-2518.4524)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3900"
id="radialGradient3857"
fy="1039.813"
fx="30.724609"
r="3"
cy="1039.813"
cx="30.724609" />
<radialGradient
gradientTransform="matrix(-0.69414478,2.3073251,-1.6952184,-0.67174747,96.941544,960.82172)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3900"
id="radialGradient3937"
fy="21.976955"
fx="31.946348"
r="19.25"
cy="21.976955"
cx="31.946348" />
</defs>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M 26,32 H 6 C 2.7,32 0,29.3 0,26 V 6 C 0,2.7 2.7,0 6,0 h 20 c 3.3,0 6,2.7 6,6 v 20 c 0,3.3 -2.7,6 -6,6 z"
id="path2"
style="fill-opacity:1;fill:#010101" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#f2f2f2"
d="M 28,6 C 28,4.9 27.1,4 26,4 H 6 C 4.9,4 4,4.9 4,6 v 20 c 0,1.1 0.9,2 2,2 h 20 c 1.1,0 2,-0.9 2,-2 z"
id="path4" />
<g
style="fill:#008000"
id="g869"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g871"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g873"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g875"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g877"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g879"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g881"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g883"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g885"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g887"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g889"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g891"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g893"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g895"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g897"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
id="g856"
style="fill:#666666;fill-opacity:1"
transform="matrix(0.0361121,0,0,0.0361121,6.5218379,6.0218363)">
<g
id="g854"
style="fill:#666666;fill-opacity:1">
<path
inkscape:connector-curvature="0"
id="path850"
d="m -14.4505,276.30781 c 0,-6.995 2.705,-13.403 7.846,-18.556 l 156.788,-156.782 c 5.128,-5.140997 11.554,-7.851997 18.568,-7.851997 7.026,0 13.452,2.717 18.556,7.845997 l 16.83,16.83 c 5.129,5.135 7.84,11.549 7.84,18.538 0,7.026 -2.717,13.452 -7.846,18.556 l -121.415001,121.42 121.427001,121.433 c 5.129,5.135 7.84,11.555 7.84,18.55 0,7.02 -2.717,13.439 -7.846,18.544 l -16.775,16.774 c -5.116,5.165 -11.555,7.895 -18.611,7.895 -7.044,0 -13.47,-2.723 -18.556,-7.846 l -156.813,-156.8 c -5.128,-5.14 -7.833,-11.549 -7.833,-18.55 z"
style="fill:#666666;fill-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path322"
d="m 539.38069,276.30781 c 0,-6.995 -2.705,-13.403 -7.846,-18.556 l -156.788,-156.782 c -5.128,-5.140997 -11.554,-7.851997 -18.568,-7.851997 -7.026,0 -13.452,2.717 -18.556,7.845997 l -16.83,16.83 c -5.129,5.135 -7.84,11.549 -7.84,18.538 0,7.026 2.717,13.452 7.846,18.556 l 121.415,121.42 -121.427,121.433 c -5.129,5.135 -7.84,11.555 -7.84,18.55 0,7.02 2.717,13.439 7.846,18.544 l 16.775,16.774 c 5.116,5.165 11.555,7.895 18.611,7.895 7.044,0 13.47,-2.723 18.556,-7.846 l 156.813,-156.8 c 5.128,-5.14 7.833,-11.549 7.833,-18.55 z"
style="fill:#666666;fill-opacity:1" />
</g>
</g>
<g
id="g858"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g860"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g862"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g864"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g866"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g868"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g870"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g872"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g874"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g876"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g878"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g880"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g882"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g884"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g886"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g12">
<path
id="path6"
d="m 6,28 h 20 c 1.1,0 2,-0.9 2,-2 V 9 6 C 28,4.9 27.1,4 26,4 H 6 C 4.9,4 4,4.9 4,6 v 3 17 c 0,1.1 0.9,2 2,2 z"
fill="none" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -10,6 +10,7 @@
<file>scalable/copy.svg</file> <file>scalable/copy.svg</file>
<file>scalable/coremods.svg</file> <file>scalable/coremods.svg</file>
<file>scalable/custom-commands.svg</file> <file>scalable/custom-commands.svg</file>
<file>scalable/environment-variables.svg</file>
<file>scalable/externaltools.svg</file> <file>scalable/externaltools.svg</file>
<file>scalable/help.svg</file> <file>scalable/help.svg</file>
<file>scalable/instance-settings.svg</file> <file>scalable/instance-settings.svg</file>

View File

@ -0,0 +1,345 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="svg2"
height="32"
width="32"
version="1.1"
sodipodi:docname="environment-variables.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:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
pagecolor="#505050"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1011"
id="namedview52"
showgrid="false"
inkscape:zoom="11.313709"
inkscape:cx="5.5242717"
inkscape:cy="12.28598"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:bbox-nodes="true"
inkscape:pagecheckerboard="true"
inkscape:showpageshadow="2"
inkscape:deskcolor="#505050">
<inkscape:grid
type="xygrid"
id="grid858" />
</sodipodi:namedview>
<defs
id="defs4">
<linearGradient
id="linearGradient3931">
<stop
offset="0"
style="stop-color:#ffffff;stop-opacity:0"
id="stop3933" />
<stop
offset="0.69999987"
style="stop-color:#ffffff;stop-opacity:0.10396039"
id="stop3939" />
<stop
offset="1"
style="stop-color:#ffffff;stop-opacity:0.14356436"
id="stop3935" />
</linearGradient>
<linearGradient
id="linearGradient3900">
<stop
offset="0"
style="stop-color:#f6f6f6;stop-opacity:1"
id="stop3902" />
<stop
offset="0.75714284"
style="stop-color:#494949;stop-opacity:1"
id="stop3904" />
<stop
offset="1"
style="stop-color:#2c2c2c;stop-opacity:1"
id="stop3906" />
</linearGradient>
<linearGradient
id="linearGradient3808">
<stop
offset="0"
style="stop-color:#333333;stop-opacity:1"
id="stop3810" />
<stop
offset="1"
style="stop-color:#c8c8c8;stop-opacity:1"
id="stop3812" />
</linearGradient>
<linearGradient
id="linearGradient3030">
<stop
offset="0"
style="stop-color:#000000;stop-opacity:1"
id="stop3032" />
<stop
offset="0.75714284"
style="stop-color:#333333;stop-opacity:1"
id="stop3038" />
<stop
offset="1"
style="stop-color:#4d4d4d;stop-opacity:1"
id="stop3034" />
</linearGradient>
<radialGradient
gradientTransform="matrix(0.67596238,0.94191445,-0.76796117,0.55112488,7.7178628,-19.890271)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3030"
id="radialGradient3036"
fy="14.242621"
fx="29.381905"
r="16.375"
cy="14.242621"
cx="29.381905" />
<linearGradient
gradientTransform="matrix(1.5,0,0,1,-16,4)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3808"
id="linearGradient3824"
y2="1033.8622"
x2="34"
y1="1033.8622"
x1="30" />
<linearGradient
gradientTransform="matrix(0.82142857,0,0,1.500001,6.7142857,-522.68214)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3808"
id="linearGradient3834"
y2="1039.3622"
x2="32"
y1="1043.3622"
x1="32" />
<radialGradient
gradientTransform="matrix(6.479993,1.9525666,-10.415476,2.1794781,10657.845,-1282.8793)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3900"
id="radialGradient3844"
fy="1039.813"
fx="30.724609"
r="3"
cy="1039.813"
cx="30.724609" />
<radialGradient
gradientTransform="matrix(2.5191507,2.9862959,-4.0491019,3.333339,4186.8847,-2518.44)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3900"
id="radialGradient3852"
fy="1039.813"
fx="30.724609"
r="3"
cy="1039.813"
cx="30.724609" />
<radialGradient
gradientTransform="matrix(-2.5191507,2.9863064,4.0491022,3.3333507,-4122.8849,-2518.4524)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3900"
id="radialGradient3857"
fy="1039.813"
fx="30.724609"
r="3"
cy="1039.813"
cx="30.724609" />
<radialGradient
gradientTransform="matrix(-0.69414478,2.3073251,-1.6952184,-0.67174747,96.941544,960.82172)"
gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3900"
id="radialGradient3937"
fy="21.976955"
fx="31.946348"
r="19.25"
cy="21.976955"
cx="31.946348" />
</defs>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M 26,32 H 6 C 2.7,32 0,29.3 0,26 V 6 C 0,2.7 2.7,0 6,0 h 20 c 3.3,0 6,2.7 6,6 v 20 c 0,3.3 -2.7,6 -6,6 z"
id="path2"
style="fill-opacity:1;fill:#f2f2f2" />
<g
style="fill:#008000"
id="g869"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g871"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g873"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g875"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g877"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g879"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g881"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g883"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g885"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g887"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g889"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g891"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g893"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g895"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<g
style="fill:#008000"
id="g897"
transform="matrix(0.04286288,0,0,0.04286288,4.7499946,5.2499965)" />
<path
id="path2-3"
d="M 28,6 C 28,4.9 27.1,4 26,4 H 6 C 4.9,4 4,4.9 4,6 v 20.000001 c 0,1.1 0.9,2 2,2 h 20 c 1.1,0 2,-0.9 2,-2 z"
fill="#4d4d4d"
clip-rule="evenodd"
fill-rule="evenodd" />
<g
id="g856"
style="fill:#ffffff;fill-opacity:1"
transform="matrix(0.0361121,0,0,0.0361121,6.5218379,6.0218363)">
<g
id="g854"
style="fill:#ffffff;fill-opacity:1">
<path
inkscape:connector-curvature="0"
id="path850"
d="m -14.4505,276.30781 c 0,-6.995 2.705,-13.403 7.846,-18.556 l 156.788,-156.782 c 5.128,-5.140997 11.554,-7.851997 18.568,-7.851997 7.026,0 13.452,2.717 18.556,7.845997 l 16.83,16.83 c 5.129,5.135 7.84,11.549 7.84,18.538 0,7.026 -2.717,13.452 -7.846,18.556 l -121.415001,121.42 121.427001,121.433 c 5.129,5.135 7.84,11.555 7.84,18.55 0,7.02 -2.717,13.439 -7.846,18.544 l -16.775,16.774 c -5.116,5.165 -11.555,7.895 -18.611,7.895 -7.044,0 -13.47,-2.723 -18.556,-7.846 l -156.813,-156.8 c -5.128,-5.14 -7.833,-11.549 -7.833,-18.55 z"
style="fill:#ffffff;fill-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path322"
d="m 539.38069,276.30781 c 0,-6.995 -2.705,-13.403 -7.846,-18.556 l -156.788,-156.782 c -5.128,-5.140997 -11.554,-7.851997 -18.568,-7.851997 -7.026,0 -13.452,2.717 -18.556,7.845997 l -16.83,16.83 c -5.129,5.135 -7.84,11.549 -7.84,18.538 0,7.026 2.717,13.452 7.846,18.556 l 121.415,121.42 -121.427,121.433 c -5.129,5.135 -7.84,11.555 -7.84,18.55 0,7.02 2.717,13.439 7.846,18.544 l 16.775,16.774 c 5.116,5.165 11.555,7.895 18.611,7.895 7.044,0 13.47,-2.723 18.556,-7.846 l 156.813,-156.8 c 5.128,-5.14 7.833,-11.549 7.833,-18.55 z"
style="fill:#ffffff;fill-opacity:1" />
</g>
</g>
<g
id="g858"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g860"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g862"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g864"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g866"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g868"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g870"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g872"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g874"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g876"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g878"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g880"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g882"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g884"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g886"
style="fill:#00ff00"
transform="matrix(0.04286288,0,0,0.04286288,4.7499948,4.2499932)" />
<g
id="g12">
<path
id="path6"
d="m 6,28 h 20 c 1.1,0 2,-0.9 2,-2 V 9 6 C 28,4.9 27.1,4 26,4 H 6 C 4.9,4 4,4.9 4,6 v 3 17 c 0,1.1 0.9,2 2,2 z"
fill="none" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -39,87 +39,76 @@
#include <QDebug> #include <QDebug>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QList>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QStringList> #include <QStringList>
#include <QUrl> #include <QUrl>
#include <memory>
#include "Application.h"
#include "BuildConfig.h" #include "BuildConfig.h"
#include "net/StaticHeaderProxy.h"
ImgurAlbumCreation::ImgurAlbumCreation(QList<ScreenShot::Ptr> screenshots) : NetAction(), m_screenshots(screenshots) Net::NetRequest::Ptr ImgurAlbumCreation::make(std::shared_ptr<ImgurAlbumCreation::Result> output, QList<ScreenShot::Ptr> screenshots)
{ {
m_url = BuildConfig.IMGUR_BASE_URL + "album.json"; auto up = makeShared<ImgurAlbumCreation>();
m_state = State::Inactive; up->m_url = BuildConfig.IMGUR_BASE_URL + "album.json";
up->m_sink.reset(new Sink(output));
up->m_screenshots = screenshots;
return up;
} }
void ImgurAlbumCreation::executeTask() QNetworkReply* ImgurAlbumCreation::getReply(QNetworkRequest& request)
{ {
m_state = State::Running;
QNetworkRequest request(m_url);
request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgentUncached().toUtf8());
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
request.setRawHeader("Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str());
request.setRawHeader("Accept", "application/json");
QStringList hashes; QStringList hashes;
for (auto shot : m_screenshots) { for (auto shot : m_screenshots) {
hashes.append(shot->m_imgurDeleteHash); hashes.append(shot->m_imgurDeleteHash);
} }
const QByteArray data = "deletehashes=" + hashes.join(',').toUtf8() + "&title=Minecraft%20Screenshots&privacy=hidden"; const QByteArray data = "deletehashes=" + hashes.join(',').toUtf8() + "&title=Minecraft%20Screenshots&privacy=hidden";
return m_network->post(request, data);
};
QNetworkReply* rep = APPLICATION->network()->post(request, data); void ImgurAlbumCreation::init()
{
m_reply.reset(rep); qDebug() << "Setting up imgur upload";
connect(rep, &QNetworkReply::uploadProgress, this, &ImgurAlbumCreation::downloadProgress); auto api_headers = new Net::StaticHeaderProxy(
connect(rep, &QNetworkReply::finished, this, &ImgurAlbumCreation::downloadFinished); QList<Net::HeaderPair>{ { "Content-Type", "application/x-www-form-urlencoded" },
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 { "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str() },
connect(rep, &QNetworkReply::errorOccurred, this, &ImgurAlbumCreation::downloadError); { "Accept", "application/json" } });
#else addHeaderProxy(api_headers);
connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &ImgurAlbumCreation::downloadError);
#endif
connect(rep, &QNetworkReply::sslErrors, this, &ImgurAlbumCreation::sslErrors);
} }
void ImgurAlbumCreation::downloadError([[maybe_unused]] QNetworkReply::NetworkError error) auto ImgurAlbumCreation::Sink::init(QNetworkRequest& request) -> Task::State
{ {
qDebug() << m_reply->errorString(); m_output.clear();
m_state = State::Failed; return Task::State::Running;
};
auto ImgurAlbumCreation::Sink::write(QByteArray& data) -> Task::State
{
m_output.append(data);
return Task::State::Running;
} }
void ImgurAlbumCreation::downloadFinished() auto ImgurAlbumCreation::Sink::abort() -> Task::State
{ {
if (m_state != State::Failed) { m_output.clear();
QByteArray data = m_reply->readAll(); return Task::State::Failed;
m_reply.reset(); }
QJsonParseError jsonError;
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); auto ImgurAlbumCreation::Sink::finalize(QNetworkReply&) -> Task::State
if (jsonError.error != QJsonParseError::NoError) { {
qDebug() << jsonError.errorString(); QJsonParseError jsonError;
emitFailed(); QJsonDocument doc = QJsonDocument::fromJson(m_output, &jsonError);
return; if (jsonError.error != QJsonParseError::NoError) {
} qDebug() << jsonError.errorString();
auto object = doc.object(); return Task::State::Failed;
if (!object.value("success").toBool()) {
qDebug() << doc.toJson();
emitFailed();
return;
}
m_deleteHash = object.value("data").toObject().value("deletehash").toString();
m_id = object.value("data").toObject().value("id").toString();
m_state = State::Succeeded;
emit succeeded();
return;
} else {
qDebug() << m_reply->readAll();
m_reply.reset();
emitFailed();
return;
} }
} auto object = doc.object();
if (!object.value("success").toBool()) {
void ImgurAlbumCreation::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) qDebug() << doc.toJson();
{ return Task::State::Failed;
setProgress(bytesReceived, bytesTotal); }
emit progress(bytesReceived, bytesTotal); m_result->deleteHash = object.value("data").toObject().value("deletehash").toString();
m_result->id = object.value("data").toObject().value("id").toString();
return Task::State::Succeeded;
} }

View File

@ -36,34 +36,39 @@
#pragma once #pragma once
#include "Screenshot.h" #include "Screenshot.h"
#include "net/NetAction.h" #include "net/NetRequest.h"
typedef shared_qobject_ptr<class ImgurAlbumCreation> ImgurAlbumCreationPtr; class ImgurAlbumCreation : public Net::NetRequest {
class ImgurAlbumCreation : public NetAction {
public: public:
explicit ImgurAlbumCreation(QList<ScreenShot::Ptr> screenshots); virtual ~ImgurAlbumCreation() = default;
static ImgurAlbumCreationPtr make(QList<ScreenShot::Ptr> screenshots)
{
return ImgurAlbumCreationPtr(new ImgurAlbumCreation(screenshots));
}
QString deleteHash() const { return m_deleteHash; } struct Result {
QString id() const { return m_id; } QString deleteHash;
QString id;
};
void init() override{}; class Sink : public Net::Sink {
public:
Sink(std::shared_ptr<Result> res) : m_result(res){};
virtual ~Sink() = default;
protected slots: public:
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; auto init(QNetworkRequest& request) -> Task::State override;
void downloadError(QNetworkReply::NetworkError error) override; auto write(QByteArray& data) -> Task::State override;
void downloadFinished() override; auto abort() -> Task::State override;
void downloadReadyRead() override {} auto finalize(QNetworkReply& reply) -> Task::State override;
auto hasLocalData() -> bool override { return false; }
public slots: private:
void executeTask() override; std::shared_ptr<Result> m_result;
QByteArray m_output;
};
static NetRequest::Ptr make(std::shared_ptr<Result> output, QList<ScreenShot::Ptr> screenshots);
QNetworkReply* getReply(QNetworkRequest& request) override;
void init() override;
private: private:
QList<ScreenShot::Ptr> m_screenshots; QList<ScreenShot::Ptr> m_screenshots;
QString m_deleteHash;
QString m_id;
}; };

View File

@ -35,8 +35,8 @@
*/ */
#include "ImgurUpload.h" #include "ImgurUpload.h"
#include "Application.h"
#include "BuildConfig.h" #include "BuildConfig.h"
#include "net/StaticHeaderProxy.h"
#include <QDebug> #include <QDebug>
#include <QFile> #include <QFile>
@ -47,104 +47,84 @@
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QUrl> #include <QUrl>
ImgurUpload::ImgurUpload(ScreenShot::Ptr shot) : NetAction(), m_shot(shot) void ImgurUpload::init()
{ {
m_url = BuildConfig.IMGUR_BASE_URL + "upload.json"; qDebug() << "Setting up imgur upload";
m_state = State::Inactive; auto api_headers = new Net::StaticHeaderProxy(
QList<Net::HeaderPair>{ { "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str() },
{ "Accept", "application/json" } });
addHeaderProxy(api_headers);
} }
void ImgurUpload::executeTask() QNetworkReply* ImgurUpload::getReply(QNetworkRequest& request)
{ {
finished = false; auto file = new QFile(m_fileInfo.absoluteFilePath());
m_state = Task::State::Running;
QNetworkRequest request(m_url);
request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgentUncached().toUtf8());
request.setRawHeader("Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str());
request.setRawHeader("Accept", "application/json");
QFile f(m_shot->m_file.absoluteFilePath()); if (!file->open(QFile::ReadOnly)) {
if (!f.open(QFile::ReadOnly)) {
emitFailed(); emitFailed();
return; return nullptr;
} }
QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
file->setParent(multipart);
QHttpPart filePart; QHttpPart filePart;
filePart.setBody(f.readAll().toBase64()); filePart.setBodyDevice(file);
filePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png"); filePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png");
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"image\""); filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"image\"");
multipart->append(filePart); multipart->append(filePart);
QHttpPart typePart; QHttpPart typePart;
typePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"type\""); typePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"type\"");
typePart.setBody("base64"); typePart.setBody("file");
multipart->append(typePart); multipart->append(typePart);
QHttpPart namePart; QHttpPart namePart;
namePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"name\""); namePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"name\"");
namePart.setBody(m_shot->m_file.baseName().toUtf8()); namePart.setBody(m_fileInfo.baseName().toUtf8());
multipart->append(namePart); multipart->append(namePart);
QNetworkReply* rep = m_network->post(request, multipart); return m_network->post(request, multipart);
};
m_reply.reset(rep); auto ImgurUpload::Sink::init(QNetworkRequest& request) -> Task::State
connect(rep, &QNetworkReply::uploadProgress, this, &ImgurUpload::downloadProgress); {
connect(rep, &QNetworkReply::finished, this, &ImgurUpload::downloadFinished); m_output.clear();
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 return Task::State::Running;
connect(rep, &QNetworkReply::errorOccurred, this, &ImgurUpload::downloadError); };
#else
connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &ImgurUpload::downloadError); auto ImgurUpload::Sink::write(QByteArray& data) -> Task::State
#endif {
connect(rep, &QNetworkReply::sslErrors, this, &ImgurUpload::sslErrors); m_output.append(data);
return Task::State::Running;
} }
void ImgurUpload::downloadError([[maybe_unused]] QNetworkReply::NetworkError error) auto ImgurUpload::Sink::abort() -> Task::State
{ {
qCritical() << "ImgurUpload failed with error" << m_reply->errorString() << "Server reply:\n" << m_reply->readAll(); m_output.clear();
if (finished) { return Task::State::Failed;
qCritical() << "Double finished ImgurUpload!";
return;
}
m_state = Task::State::Failed;
finished = true;
m_reply.reset();
emitFailed();
} }
void ImgurUpload::downloadFinished() auto ImgurUpload::Sink::finalize(QNetworkReply&) -> Task::State
{ {
if (finished) {
qCritical() << "Double finished ImgurUpload!";
return;
}
QByteArray data = m_reply->readAll();
m_reply.reset();
QJsonParseError jsonError; QJsonParseError jsonError;
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); QJsonDocument doc = QJsonDocument::fromJson(m_output, &jsonError);
if (jsonError.error != QJsonParseError::NoError) { if (jsonError.error != QJsonParseError::NoError) {
qDebug() << "imgur server did not reply with JSON" << jsonError.errorString(); qDebug() << "imgur server did not reply with JSON" << jsonError.errorString();
finished = true; return Task::State::Failed;
m_reply.reset();
emitFailed();
return;
} }
auto object = doc.object(); auto object = doc.object();
if (!object.value("success").toBool()) { if (!object.value("success").toBool()) {
qDebug() << "Screenshot upload not successful:" << doc.toJson(); qDebug() << "Screenshot upload not successful:" << doc.toJson();
finished = true; return Task::State::Failed;
m_reply.reset();
emitFailed();
return;
} }
m_shot->m_imgurId = object.value("data").toObject().value("id").toString(); m_shot->m_imgurId = object.value("data").toObject().value("id").toString();
m_shot->m_url = object.value("data").toObject().value("link").toString(); m_shot->m_url = object.value("data").toObject().value("link").toString();
m_shot->m_imgurDeleteHash = object.value("data").toObject().value("deletehash").toString(); m_shot->m_imgurDeleteHash = object.value("data").toObject().value("deletehash").toString();
m_state = Task::State::Succeeded; return Task::State::Succeeded;
finished = true;
emit succeeded();
return;
} }
void ImgurUpload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) Net::NetRequest::Ptr ImgurUpload::make(ScreenShot::Ptr m_shot)
{ {
setProgress(bytesReceived, bytesTotal); auto up = makeShared<ImgurUpload>(m_shot->m_file);
emit progress(bytesReceived, bytesTotal); up->m_url = std::move(BuildConfig.IMGUR_BASE_URL + "upload.json");
up->m_sink.reset(new Sink(m_shot));
return up;
} }

View File

@ -35,27 +35,36 @@
#pragma once #pragma once
#include <QFileInfo>
#include "Screenshot.h" #include "Screenshot.h"
#include "net/NetAction.h" #include "net/NetRequest.h"
class ImgurUpload : public NetAction { class ImgurUpload : public Net::NetRequest {
public: public:
using Ptr = shared_qobject_ptr<ImgurUpload>; class Sink : public Net::Sink {
public:
Sink(ScreenShot::Ptr shot) : m_shot(shot){};
virtual ~Sink() = default;
explicit ImgurUpload(ScreenShot::Ptr shot); public:
static Ptr make(ScreenShot::Ptr shot) { return Ptr(new ImgurUpload(shot)); } auto init(QNetworkRequest& request) -> Task::State override;
void init() override{}; auto write(QByteArray& data) -> Task::State override;
auto abort() -> Task::State override;
auto finalize(QNetworkReply& reply) -> Task::State override;
auto hasLocalData() -> bool override { return false; }
protected slots: private:
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; ScreenShot::Ptr m_shot;
void downloadError(QNetworkReply::NetworkError error) override; QByteArray m_output;
void downloadFinished() override; };
void downloadReadyRead() override {} ImgurUpload(QFileInfo info) : m_fileInfo(info) {}
virtual ~ImgurUpload() = default;
public slots: static NetRequest::Ptr make(ScreenShot::Ptr m_shot);
void executeTask() override;
void init() override;
private: private:
ScreenShot::Ptr m_shot; virtual QNetworkReply* getReply(QNetworkRequest&) override;
bool finished = true; const QFileInfo m_fileInfo;
}; };

View File

@ -26,8 +26,8 @@
class Setting; class Setting;
class SettingsObject; class SettingsObject;
typedef std::shared_ptr<SettingsObject> SettingsObjectPtr; using SettingsObjectPtr = std::shared_ptr<SettingsObject>;
typedef std::weak_ptr<SettingsObject> SettingsObjectWeakPtr; using SettingsObjectWeakPtr = std::weak_ptr<SettingsObject>;
/*! /*!
* \brief The SettingsObject handles communicating settings between the application and a * \brief The SettingsObject handles communicating settings between the application and a

View File

@ -77,7 +77,7 @@ struct TaskStepProgress {
Q_DECLARE_METATYPE(TaskStepProgress) Q_DECLARE_METATYPE(TaskStepProgress)
typedef QList<std::shared_ptr<TaskStepProgress>> TaskStepProgressList; using TaskStepProgressList = QList<std::shared_ptr<TaskStepProgress>>;
class Task : public QObject, public QRunnable { class Task : public QObject, public QRunnable {
Q_OBJECT Q_OBJECT

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