Merge pull request #1268 from Ryex/feat/launcher-updater
Feat: Windows (And portable linux / appimage) Auto Updater
This commit is contained in:
commit
436db23ffc
98
.github/workflows/build.yml
vendored
98
.github/workflows/build.yml
vendored
@ -37,65 +37,64 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
|
||||
- os: ubuntu-20.04
|
||||
qt_ver: 5
|
||||
|
||||
- os: ubuntu-20.04
|
||||
qt_ver: 6
|
||||
qt_host: linux
|
||||
qt_arch: ''
|
||||
qt_version: '6.2.4'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
qt_arch: ""
|
||||
qt_version: "6.2.4"
|
||||
qt_modules: "qt5compat qtimageformats"
|
||||
qt_tools: ""
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-MinGW-w64"
|
||||
msystem: clang64
|
||||
vcvars_arch: 'amd64_x86'
|
||||
vcvars_arch: "amd64_x86"
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-MSVC"
|
||||
msystem: ''
|
||||
architecture: 'x64'
|
||||
vcvars_arch: 'amd64'
|
||||
msystem: ""
|
||||
architecture: "x64"
|
||||
vcvars_arch: "amd64"
|
||||
qt_ver: 6
|
||||
qt_host: windows
|
||||
qt_arch: ''
|
||||
qt_version: '6.5.2'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
qt_arch: ""
|
||||
qt_version: "6.5.2"
|
||||
qt_modules: "qt5compat qtimageformats"
|
||||
qt_tools: ""
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-MSVC-arm64"
|
||||
msystem: ''
|
||||
architecture: 'arm64'
|
||||
vcvars_arch: 'amd64_arm64'
|
||||
msystem: ""
|
||||
architecture: "arm64"
|
||||
vcvars_arch: "amd64_arm64"
|
||||
qt_ver: 6
|
||||
qt_host: windows
|
||||
qt_arch: 'win64_msvc2019_arm64'
|
||||
qt_version: '6.5.2'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
qt_arch: "win64_msvc2019_arm64"
|
||||
qt_version: "6.5.2"
|
||||
qt_modules: "qt5compat qtimageformats"
|
||||
qt_tools: ""
|
||||
|
||||
- os: macos-12
|
||||
name: macOS
|
||||
macosx_deployment_target: 11.0
|
||||
qt_ver: 6
|
||||
qt_host: mac
|
||||
qt_arch: ''
|
||||
qt_version: '6.5.2'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
qt_arch: ""
|
||||
qt_version: "6.5.2"
|
||||
qt_modules: "qt5compat qtimageformats"
|
||||
qt_tools: ""
|
||||
|
||||
- os: macos-12
|
||||
name: macOS-Legacy
|
||||
macosx_deployment_target: 10.13
|
||||
qt_ver: 5
|
||||
qt_host: mac
|
||||
qt_version: '5.15.2'
|
||||
qt_modules: ''
|
||||
qt_tools: ''
|
||||
qt_version: "5.15.2"
|
||||
qt_modules: ""
|
||||
qt_tools: ""
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
@ -115,9 +114,9 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
submodules: "true"
|
||||
|
||||
- name: 'Setup MSYS2'
|
||||
- name: "Setup MSYS2"
|
||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
@ -202,12 +201,12 @@ jobs:
|
||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
aqtversion: '==3.1.*'
|
||||
py7zrversion: '>=0.20.2'
|
||||
aqtversion: "==3.1.*"
|
||||
py7zrversion: ">=0.20.2"
|
||||
version: ${{ matrix.qt_version }}
|
||||
host: 'windows'
|
||||
target: 'desktop'
|
||||
arch: ''
|
||||
host: "windows"
|
||||
target: "desktop"
|
||||
arch: ""
|
||||
modules: ${{ matrix.qt_modules }}
|
||||
tools: ${{ matrix.qt_tools }}
|
||||
cache: ${{ inputs.is_qt_cached }}
|
||||
@ -219,11 +218,11 @@ jobs:
|
||||
if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '')
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
aqtversion: '==3.1.*'
|
||||
py7zrversion: '>=0.20.2'
|
||||
aqtversion: "==3.1.*"
|
||||
py7zrversion: ">=0.20.2"
|
||||
version: ${{ matrix.qt_version }}
|
||||
host: ${{ matrix.qt_host }}
|
||||
target: 'desktop'
|
||||
target: "desktop"
|
||||
arch: ${{ matrix.qt_arch }}
|
||||
modules: ${{ matrix.qt_modules }}
|
||||
tools: ${{ matrix.qt_tools }}
|
||||
@ -271,12 +270,12 @@ jobs:
|
||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||
shell: msys2 {0}
|
||||
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)
|
||||
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||
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)
|
||||
if ("${{ env.CCACHE_VAR }}")
|
||||
{
|
||||
@ -291,7 +290,7 @@ jobs:
|
||||
- name: Configure CMake (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
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
|
||||
@ -390,7 +389,6 @@ 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
|
||||
|
||||
|
||||
- name: Fetch codesign certificate (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash # yes, we are not using MSYS2 or PowerShell here
|
||||
@ -403,7 +401,7 @@ jobs:
|
||||
if (Get-Content ./codesign.pfx){
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
# 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 {
|
||||
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||
}
|
||||
@ -495,15 +493,7 @@ jobs:
|
||||
export LD_LIBRARY_PATH
|
||||
|
||||
chmod +x AppImageUpdate-x86_64.AppImage
|
||||
./AppImageUpdate-x86_64.AppImage --appimage-extract
|
||||
|
||||
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional
|
||||
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins
|
||||
|
||||
cp -r squashfs-root/usr/bin/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin
|
||||
cp -r squashfs-root/usr/lib/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib
|
||||
cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional
|
||||
cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins
|
||||
cp AppImageUpdate-x86_64.AppImage ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin
|
||||
|
||||
export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-x86_64.AppImage.zsync"
|
||||
|
||||
@ -557,14 +547,14 @@ jobs:
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
name: PrismLauncher-${{ runner.os }}-Qt5-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PrismLauncher.tar.gz
|
||||
|
||||
- name: Upload binary tarball (Linux, portable, Qt 5)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||
uses: actions/upload-artifact@v3
|
||||
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
|
||||
|
||||
- name: Upload binary tarball (Linux, Qt 6)
|
||||
@ -611,7 +601,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
if: inputs.build_type == 'Debug'
|
||||
with:
|
||||
submodules: 'true'
|
||||
submodules: "true"
|
||||
- name: Build Flatpak (Linux)
|
||||
if: inputs.build_type == 'Debug'
|
||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||
|
29
.github/workflows/trigger_builds.yml
vendored
29
.github/workflows/trigger_builds.yml
vendored
@ -3,26 +3,25 @@ name: Build Application
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'renovate/**'
|
||||
- "renovate/**"
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**/LICENSE'
|
||||
- 'flake.lock'
|
||||
- 'packages/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
- '.markdownlint**'
|
||||
- "**.md"
|
||||
- "**/LICENSE"
|
||||
- "flake.lock"
|
||||
- "packages/**"
|
||||
- ".github/ISSUE_TEMPLATE/**"
|
||||
- ".markdownlint**"
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**/LICENSE'
|
||||
- 'flake.lock'
|
||||
- 'packages/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
- '.markdownlint**'
|
||||
- "**.md"
|
||||
- "**/LICENSE"
|
||||
- "flake.lock"
|
||||
- "packages/**"
|
||||
- ".github/ISSUE_TEMPLATE/**"
|
||||
- ".markdownlint**"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
build_debug:
|
||||
name: Build Debug
|
||||
uses: ./.github/workflows/build.yml
|
||||
@ -34,3 +33,5 @@ jobs:
|
||||
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||
|
10
.github/workflows/trigger_release.yml
vendored
10
.github/workflows/trigger_release.yml
vendored
@ -17,6 +17,8 @@ jobs:
|
||||
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||
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:
|
||||
needs: build_release
|
||||
@ -40,8 +42,8 @@ jobs:
|
||||
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*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ 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.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||
mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
||||
@ -85,8 +87,8 @@ jobs:
|
||||
draft: true
|
||||
prerelease: false
|
||||
files: |
|
||||
PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-x86_64.AppImage
|
||||
PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||
|
@ -188,8 +188,11 @@ set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_M
|
||||
# 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.")
|
||||
|
||||
# Channel list URL
|
||||
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
||||
# Github repo URL with releases for 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
|
||||
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
|
||||
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
|
||||
include(GetGitRevisionDescription)
|
||||
|
@ -33,6 +33,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <qstringliteral.h>
|
||||
#include "BuildConfig.h"
|
||||
#include <QObject>
|
||||
|
||||
@ -59,8 +60,16 @@ Config::Config()
|
||||
VERSION_MINOR = @Launcher_VERSION_MINOR@;
|
||||
|
||||
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
||||
BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@";
|
||||
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_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
|
||||
@ -68,6 +77,8 @@ Config::Config()
|
||||
if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
|
||||
{
|
||||
UPDATER_ENABLED = true;
|
||||
} else if(!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_ARTIFACT.isEmpty()) {
|
||||
UPDATER_ENABLED = true;
|
||||
}
|
||||
|
||||
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
||||
@ -89,9 +100,6 @@ Config::Config()
|
||||
{
|
||||
VERSION_CHANNEL = GIT_REFSPEC;
|
||||
VERSION_CHANNEL.remove("refs/heads/");
|
||||
if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty()) {
|
||||
UPDATER_ENABLED = true;
|
||||
}
|
||||
}
|
||||
else if (!GIT_COMMIT.isEmpty())
|
||||
{
|
||||
@ -136,3 +144,16 @@ QString Config::printableVersionString() const
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -71,11 +71,29 @@ class Config {
|
||||
/// A short string identifying this build's platform or distribution.
|
||||
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
|
||||
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
|
||||
QString UPDATER_BASE;
|
||||
QString UPDATER_GITHUB_REPO;
|
||||
|
||||
/// The public key used to sign releases for the Sparkle updater appcast
|
||||
QString MAC_SPARKLE_PUB_KEY;
|
||||
@ -175,6 +193,18 @@ class Config {
|
||||
* \return The version number in string format (major.minor.revision.build).
|
||||
*/
|
||||
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;
|
||||
|
@ -122,6 +122,7 @@
|
||||
#include <FileSystem.h>
|
||||
#include <LocalPeer.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys.h>
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
@ -130,9 +131,13 @@
|
||||
#include "gamemode_client.h"
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_MAC) && defined(SPARKLE_ENABLED)
|
||||
#if defined(Q_OS_MAC)
|
||||
#if defined(SPARKLE_ENABLED)
|
||||
#include "updater/MacSparkleUpdater.h"
|
||||
#endif
|
||||
#else
|
||||
#include "updater/PrismExternalUpdater.h"
|
||||
#endif
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
#include "WindowsConsole.h"
|
||||
@ -164,6 +169,34 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt
|
||||
|
||||
} // 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)
|
||||
{
|
||||
#if defined Q_OS_WIN32
|
||||
@ -296,6 +329,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
.arg(dataPath));
|
||||
return;
|
||||
}
|
||||
m_dataPath = dataPath;
|
||||
|
||||
/*
|
||||
* Establish the mechanism for communication with an already running PrismLauncher that uses the same data path.
|
||||
@ -450,11 +484,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() << "Platform : " << BuildConfig.BUILD_PLATFORM;
|
||||
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
|
||||
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()) {
|
||||
qDebug() << "Work dir before adjustment : " << origcwdPath;
|
||||
qDebug() << "Work dir after adjustment : " << QDir::currentPath();
|
||||
@ -741,15 +780,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
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
|
||||
{
|
||||
auto setting = APPLICATION->settings()->getSetting("IconsDir");
|
||||
@ -852,6 +882,107 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
|
||||
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()) {
|
||||
return;
|
||||
}
|
||||
@ -920,6 +1051,26 @@ bool Application::createSetupWizard()
|
||||
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)
|
||||
{
|
||||
#ifdef Q_OS_MACOS
|
||||
@ -988,6 +1139,20 @@ void Application::performMainStartupAction()
|
||||
showMainWindow(false);
|
||||
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()) {
|
||||
qDebug() << "<> Importing from url:" << m_urlsToImport;
|
||||
m_mainWindow->processURLs(m_urlsToImport);
|
||||
|
@ -159,6 +159,9 @@ class Application : public QApplication {
|
||||
/// this is the root of the 'installation'. Used for automatic updates
|
||||
const QString& root() { return m_rootPath; }
|
||||
|
||||
/// the data path the application is using
|
||||
const QString& dataRoot() { return m_dataPath; }
|
||||
|
||||
bool isPortable() { return m_portable; }
|
||||
|
||||
const Capabilities capabilities() { return m_capabilities; }
|
||||
@ -179,6 +182,9 @@ class Application : public QApplication {
|
||||
|
||||
int suitableMaxMem();
|
||||
|
||||
bool updaterEnabled();
|
||||
QString updaterBinaryName();
|
||||
|
||||
QUrl normalizeImportUrl(QString const& url);
|
||||
|
||||
signals:
|
||||
@ -244,6 +250,7 @@ class Application : public QApplication {
|
||||
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
||||
|
||||
QString m_rootPath;
|
||||
QString m_dataPath;
|
||||
Status m_status = Application::StartingUp;
|
||||
Capabilities m_capabilities;
|
||||
bool m_portable = false;
|
||||
|
@ -181,6 +181,11 @@ set(MAC_UPDATE_SOURCES
|
||||
updater/MacSparkleUpdater.mm
|
||||
)
|
||||
|
||||
set(PRISM_UPDATE_SOURCES
|
||||
updater/PrismExternalUpdater.h
|
||||
updater/PrismExternalUpdater.cpp
|
||||
)
|
||||
|
||||
# Backend for the news bar... there's usually no news.
|
||||
set(NEWS_SOURCES
|
||||
# News System
|
||||
@ -579,6 +584,63 @@ set(LINKEXE_SOURCES
|
||||
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 ########
|
||||
|
||||
ecm_qt_declare_logging_category(CORE_SOURCES
|
||||
@ -675,6 +737,8 @@ set(LOGIC_SOURCES
|
||||
|
||||
if(APPLE AND Launcher_ENABLE_UPDATER)
|
||||
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES})
|
||||
else()
|
||||
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${PRISM_UPDATE_SOURCES})
|
||||
endif()
|
||||
|
||||
SET(LAUNCHER_SOURCES
|
||||
@ -1033,6 +1097,15 @@ SET(LAUNCHER_SOURCES
|
||||
ui/instanceview/VisualGroup.h
|
||||
)
|
||||
|
||||
if (NOT Apple)
|
||||
set(LAUNCHER_SOURCES
|
||||
${LAUNCHER_SOURCES}
|
||||
|
||||
ui/dialogs/UpdateAvailableDialog.h
|
||||
ui/dialogs/UpdateAvailableDialog.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(LAUNCHER_SOURCES
|
||||
WindowsConsole.cpp
|
||||
@ -1103,6 +1176,14 @@ qt_wrap_ui(LAUNCHER_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
|
||||
resources/backgrounds/backgrounds.qrc
|
||||
resources/multimc/multimc.qrc
|
||||
@ -1119,6 +1200,12 @@ qt_add_resources(LAUNCHER_RESOURCES
|
||||
../${Launcher_Branding_LogoQRC}
|
||||
)
|
||||
|
||||
qt_wrap_ui(PRISMUPDATER_UI
|
||||
updater/prismupdater/SelectReleaseDialog.ui
|
||||
ui/widgets/SubTaskProgressBar.ui
|
||||
ui/dialogs/ProgressDialog.ui
|
||||
)
|
||||
|
||||
######## Windows resource files ########
|
||||
if(WIN32)
|
||||
set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC})
|
||||
@ -1137,6 +1224,7 @@ set_project_warnings(Launcher_logic
|
||||
"${Launcher_GCC_WARNINGS}")
|
||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
||||
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
||||
target_link_libraries(Launcher_logic
|
||||
systeminfo
|
||||
Launcher_murmur2
|
||||
@ -1218,7 +1306,45 @@ install(TARGETS ${Launcher_Name}
|
||||
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
if(NOT APPLE OR (DEFINED Launcher_BUILD_UPDATER AND 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})
|
||||
set_project_warnings(filelink_logic
|
||||
"${Launcher_MSVC_WARNINGS}"
|
||||
@ -1237,7 +1363,7 @@ if(WIN32)
|
||||
${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)
|
||||
|
||||
|
@ -194,6 +194,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)
|
||||
{
|
||||
QFile file(filename);
|
||||
@ -287,6 +321,9 @@ bool copy::operator()(const QString& offset, bool dryRun)
|
||||
if (!m_followSymlinks)
|
||||
opt |= copy_opts::copy_symlinks;
|
||||
|
||||
if (m_overwrite)
|
||||
opt |= copy_opts::overwrite_existing;
|
||||
|
||||
// Function that'll do the actual copying
|
||||
auto copy_file = [&](QString src_path, QString relative_dst_path) {
|
||||
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
|
||||
|
@ -61,6 +61,16 @@ class FileSystemException : public ::Exception {
|
||||
*/
|
||||
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\
|
||||
*/
|
||||
@ -109,6 +119,11 @@ class copy : public QObject {
|
||||
m_whitelist = whitelist;
|
||||
return *this;
|
||||
}
|
||||
copy& overwrite(const bool overwrite)
|
||||
{
|
||||
m_overwrite = overwrite;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||
|
||||
@ -128,6 +143,7 @@ class copy : public QObject {
|
||||
bool m_followSymlinks = true;
|
||||
const IPathMatcher* m_matcher = nullptr;
|
||||
bool m_whitelist = false;
|
||||
bool m_overwrite = false;
|
||||
QDir m_src;
|
||||
QDir m_dst;
|
||||
qsizetype m_copied;
|
||||
|
@ -42,7 +42,11 @@
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
#include <QtConcurrentRun>
|
||||
#endif
|
||||
|
||||
namespace MMCZip {
|
||||
// ours
|
||||
@ -132,6 +136,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files,
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
// ours
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ours
|
||||
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;
|
||||
}
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
void ExportToZipTask::executeTask()
|
||||
{
|
||||
setStatus("Adding files...");
|
||||
@ -500,5 +507,6 @@ bool ExportToZipTask::abort()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace MMCZip
|
@ -48,7 +48,10 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
#include "minecraft/mod/Mod.h"
|
||||
#endif
|
||||
#include "tasks/Task.h"
|
||||
|
||||
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);
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
/**
|
||||
* take a source jar, add mods to it, resulting in target jar
|
||||
*/
|
||||
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods);
|
||||
|
||||
#endif
|
||||
/**
|
||||
* 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);
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
class ExportToZipTask : public Task {
|
||||
public:
|
||||
ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false)
|
||||
@ -189,4 +194,5 @@ class ExportToZipTask : public Task {
|
||||
QFuture<ZipResult> m_build_zip_future;
|
||||
QFutureWatcher<ZipResult> m_build_zip_watcher;
|
||||
};
|
||||
#endif
|
||||
} // namespace MMCZip
|
||||
|
@ -35,6 +35,7 @@
|
||||
*/
|
||||
|
||||
#include "StringUtils.h"
|
||||
#include <qpair.h>
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QUuid>
|
||||
@ -149,7 +150,7 @@ QString StringUtils::truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_
|
||||
}
|
||||
|
||||
if ((url_compact.length() >= max_len) && hard_limit) {
|
||||
// still too long, truncate normaly
|
||||
// still too long, truncate normally
|
||||
url_compact = QString(str_url);
|
||||
auto to_remove = url_compact.length() - max_len + 3;
|
||||
url_compact.remove(url_compact.length() - to_remove - 1, to_remove);
|
||||
@ -182,3 +183,32 @@ QString StringUtils::getRandomAlphaNumeric()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
@ -36,8 +36,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QPair>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <utility>
|
||||
|
||||
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
|
||||
* @param url Url to truncate
|
||||
* @param max_len max lenght of url in charaters
|
||||
* @param hard_limit if truncating the path can't get the url short enough, truncate it normaly.
|
||||
* @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 normally.
|
||||
*/
|
||||
QString truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_limit = false);
|
||||
|
||||
QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1);
|
||||
|
||||
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
|
||||
|
@ -56,6 +56,7 @@ class Version {
|
||||
bool operator!=(const Version& other) const;
|
||||
|
||||
QString toString() const { return m_string; }
|
||||
bool isEmpty() const { return m_string.isEmpty(); }
|
||||
|
||||
friend QDebug operator<<(QDebug debug, const Version& v);
|
||||
|
||||
|
@ -93,6 +93,7 @@ FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv),
|
||||
joinServer(serverToJoin);
|
||||
} else {
|
||||
qDebug() << "no server to join";
|
||||
m_status = Failed;
|
||||
exit();
|
||||
}
|
||||
}
|
||||
@ -108,6 +109,7 @@ void FileLinkApp::joinServer(QString server)
|
||||
connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs);
|
||||
|
||||
connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError) {
|
||||
m_status = Failed;
|
||||
switch (socketError) {
|
||||
case QLocalSocket::ServerNotFoundError:
|
||||
qDebug()
|
||||
@ -132,6 +134,7 @@ void FileLinkApp::joinServer(QString server)
|
||||
|
||||
connect(&socket, &QLocalSocket::disconnected, this, [&]() {
|
||||
qDebug() << "disconnected from server, should exit";
|
||||
m_status = Succeeded;
|
||||
exit();
|
||||
});
|
||||
|
||||
|
@ -41,8 +41,10 @@ class FileLinkApp : public QCoreApplication {
|
||||
// friends for the purpose of limiting access to deprecated stuff
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Status { Starting, Failed, Succeeded, Initialized };
|
||||
FileLinkApp(int& argc, char** argv);
|
||||
virtual ~FileLinkApp();
|
||||
Status status() const { return m_status; }
|
||||
|
||||
private:
|
||||
void joinServer(QString server);
|
||||
@ -50,6 +52,8 @@ class FileLinkApp : public QCoreApplication {
|
||||
void runLink();
|
||||
void sendResults();
|
||||
|
||||
Status m_status = Status::Starting;
|
||||
|
||||
bool m_useHardLinks = false;
|
||||
|
||||
QDateTime m_startTime;
|
||||
|
@ -26,5 +26,16 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
FileLinkApp ldh(argc, argv);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -568,7 +568,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
||||
m_mod_id_resolver.reset();
|
||||
connect(m_files_job.get(), &NetJob::succeeded, this, [&]() {
|
||||
m_files_job.reset();
|
||||
validateZIPResouces();
|
||||
validateZIPResources();
|
||||
});
|
||||
connect(m_files_job.get(), &NetJob::failed, [&](QString reason) {
|
||||
m_files_job.reset();
|
||||
@ -617,7 +617,7 @@ void FlameCreationTask::copyBlockedMods(QList<BlockedMod> const& blocked_mods)
|
||||
setAbortable(true);
|
||||
}
|
||||
|
||||
void FlameCreationTask::validateZIPResouces()
|
||||
void FlameCreationTask::validateZIPResources()
|
||||
{
|
||||
qDebug() << "Validating whether resources stored as .zip are in the right place";
|
||||
for (auto [fileName, targetFolder] : m_ZIP_resources) {
|
||||
@ -670,8 +670,8 @@ void FlameCreationTask::validateZIPResouces()
|
||||
validatePath(fileName, targetFolder, "datapacks");
|
||||
break;
|
||||
case PackedResourceType::ShaderPack:
|
||||
// in theroy flame API can't do this but who knows, that *may* change ?
|
||||
// better to handle it if it *does* occure in the future
|
||||
// in theory flame API can't do this but who knows, that *may* change ?
|
||||
// better to handle it if it *does* occur in the future
|
||||
validatePath(fileName, targetFolder, "shaderpacks");
|
||||
break;
|
||||
case PackedResourceType::WorldSave:
|
||||
|
@ -74,7 +74,7 @@ class FlameCreationTask final : public InstanceCreationTask {
|
||||
void idResolverSucceeded(QEventLoop&);
|
||||
void setupDownloadJob(QEventLoop&);
|
||||
void copyBlockedMods(QList<BlockedMod> const& blocked_mods);
|
||||
void validateZIPResouces();
|
||||
void validateZIPResources();
|
||||
QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion);
|
||||
|
||||
private:
|
||||
|
@ -36,11 +36,16 @@
|
||||
*/
|
||||
|
||||
#include "NetJob.h"
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
#include "Application.h"
|
||||
#endif
|
||||
|
||||
NetJob::NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network)
|
||||
: ConcurrentTask(nullptr, job_name, APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()), m_network(network)
|
||||
{}
|
||||
NetJob::NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network) : ConcurrentTask(nullptr, job_name), m_network(network)
|
||||
{
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
setMaxConcurrent(APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
|
||||
#endif
|
||||
}
|
||||
|
||||
auto NetJob::addNetAction(NetAction::Ptr action) -> bool
|
||||
{
|
||||
|
@ -46,6 +46,7 @@
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
#include "Application.h"
|
||||
#endif
|
||||
#include "BuildConfig.h"
|
||||
|
||||
#include "net/NetAction.h"
|
||||
|
||||
|
@ -219,7 +219,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
ui->actionDISCORD->setVisible(!BuildConfig.DISCORD_URL.isEmpty());
|
||||
ui->actionREDDIT->setVisible(!BuildConfig.SUBREDDIT_URL.isEmpty());
|
||||
|
||||
ui->actionCheckUpdate->setVisible(BuildConfig.UPDATER_ENABLED);
|
||||
ui->actionCheckUpdate->setVisible(APPLICATION->updaterEnabled());
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
ui->actionAddToPATH->setVisible(false);
|
||||
@ -377,7 +377,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
updateNewsLabel();
|
||||
}
|
||||
|
||||
if (BuildConfig.UPDATER_ENABLED) {
|
||||
if (APPLICATION->updaterEnabled()) {
|
||||
bool updatesAllowed = APPLICATION->updatesAreAllowed();
|
||||
updatesAllowedChanged(updatesAllowed);
|
||||
|
||||
@ -677,7 +677,7 @@ void MainWindow::repopulateAccountsMenu()
|
||||
|
||||
void MainWindow::updatesAllowedChanged(bool allowed)
|
||||
{
|
||||
if (!BuildConfig.UPDATER_ENABLED) {
|
||||
if (!APPLICATION->updaterEnabled()) {
|
||||
return;
|
||||
}
|
||||
ui->actionCheckUpdate->setEnabled(allowed);
|
||||
@ -1218,7 +1218,7 @@ void MainWindow::refreshInstances()
|
||||
|
||||
void MainWindow::checkForUpdates()
|
||||
{
|
||||
if (BuildConfig.UPDATER_ENABLED) {
|
||||
if (APPLICATION->updaterEnabled()) {
|
||||
APPLICATION->triggerUpdateCheck();
|
||||
} else {
|
||||
qWarning() << "Updater not set up. Cannot check for updates.";
|
||||
|
@ -48,6 +48,9 @@
|
||||
<property name="text">
|
||||
<string>Global Task Status...</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@ -109,8 +112,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>464</width>
|
||||
<height>96</height>
|
||||
<width>460</width>
|
||||
<height>108</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="taskProgressLayout">
|
||||
|
63
launcher/ui/dialogs/UpdateAvailableDialog.cpp
Normal file
63
launcher/ui/dialogs/UpdateAvailableDialog.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "UpdateAvailableDialog.h"
|
||||
#include <QPushButton>
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "Markdown.h"
|
||||
#include "ui_UpdateAvailableDialog.h"
|
||||
|
||||
UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion,
|
||||
const QString& availableVersion,
|
||||
const QString& releaseNotes,
|
||||
QWidget* parent)
|
||||
: QDialog(parent), ui(new Ui::UpdateAvailableDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
QString launcherName = BuildConfig.LAUNCHER_DISPLAYNAME;
|
||||
|
||||
ui->headerLabel->setText(tr("A new version of %1 is available!").arg(launcherName));
|
||||
ui->versionAvailableLabel->setText(
|
||||
tr("Version %1 is now available - you have %2 . Would you like to download it now?").arg(availableVersion).arg(currentVersion));
|
||||
ui->icon->setPixmap(APPLICATION->getThemedIcon("checkupdate").pixmap(64));
|
||||
|
||||
auto releaseNotesHtml = markdownToHTML(releaseNotes);
|
||||
ui->releaseNotes->setHtml(releaseNotesHtml);
|
||||
ui->releaseNotes->setOpenExternalLinks(true);
|
||||
|
||||
connect(ui->skipButton, &QPushButton::clicked, this, [this]() {
|
||||
setResult(ResultCode::Skip);
|
||||
done(ResultCode::Skip);
|
||||
});
|
||||
|
||||
connect(ui->delayButton, &QPushButton::clicked, this, [this]() {
|
||||
setResult(ResultCode::DontInstall);
|
||||
done(ResultCode::DontInstall);
|
||||
});
|
||||
|
||||
connect(ui->installButton, &QPushButton::clicked, this, [this]() {
|
||||
setResult(ResultCode::Install);
|
||||
done(ResultCode::Install);
|
||||
});
|
||||
}
|
48
launcher/ui/dialogs/UpdateAvailableDialog.h
Normal file
48
launcher/ui/dialogs/UpdateAvailableDialog.h
Normal file
@ -0,0 +1,48 @@
|
||||
// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
//
|
||||
// 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 <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class UpdateAvailableDialog;
|
||||
}
|
||||
|
||||
class UpdateAvailableDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum ResultCode {
|
||||
Install = 10,
|
||||
DontInstall = 11,
|
||||
Skip = 12,
|
||||
};
|
||||
|
||||
explicit UpdateAvailableDialog(const QString& currentVersion,
|
||||
const QString& availableVersion,
|
||||
const QString& releaseNotes,
|
||||
QWidget* parent = 0);
|
||||
~UpdateAvailableDialog() = default;
|
||||
|
||||
private:
|
||||
Ui::UpdateAvailableDialog* ui;
|
||||
};
|
155
launcher/ui/dialogs/UpdateAvailableDialog.ui
Normal file
155
launcher/ui/dialogs/UpdateAvailableDialog.ui
Normal file
@ -0,0 +1,155 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>UpdateAvailableDialog</class>
|
||||
<widget class="QDialog" name="UpdateAvailableDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>636</width>
|
||||
<height>352</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Update Available</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="leftsideLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="icon">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="mainLayout">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="headerLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>A new version is available!</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="versionAvailableLabel">
|
||||
<property name="text">
|
||||
<string>Version %1 is now available - you have %2 . Would you like to download it now?</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="releaseNotesLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Release Notes:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="releaseNotes"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="skipButton">
|
||||
<property name="text">
|
||||
<string>Skip This Version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="delayButton">
|
||||
<property name="text">
|
||||
<string>Remind Me Later</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="installButton">
|
||||
<property name="text">
|
||||
<string>Install Update</string>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
354
launcher/updater/PrismExternalUpdater.cpp
Normal file
354
launcher/updater/PrismExternalUpdater.cpp
Normal file
@ -0,0 +1,354 @@
|
||||
// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "PrismExternalUpdater.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
#include <QProcess>
|
||||
#include <QProgressDialog>
|
||||
#include <QSettings>
|
||||
#include <QTimer>
|
||||
#include <memory>
|
||||
|
||||
#include "StringUtils.h"
|
||||
|
||||
#include "BuildConfig.h"
|
||||
|
||||
#include "ui/dialogs/UpdateAvailableDialog.h"
|
||||
|
||||
class PrismExternalUpdater::Private {
|
||||
public:
|
||||
QDir appDir;
|
||||
QDir dataDir;
|
||||
QTimer updateTimer;
|
||||
bool allowBeta;
|
||||
bool autoCheck;
|
||||
double updateInterval;
|
||||
QDateTime lastCheck;
|
||||
std::unique_ptr<QSettings> settings;
|
||||
|
||||
QWidget* parent;
|
||||
};
|
||||
|
||||
PrismExternalUpdater::PrismExternalUpdater(QWidget* parent, const QString& appDir, const QString& dataDir)
|
||||
{
|
||||
priv = new PrismExternalUpdater::Private();
|
||||
priv->appDir = QDir(appDir);
|
||||
priv->dataDir = QDir(dataDir);
|
||||
auto settings_file = priv->dataDir.absoluteFilePath("prismlauncher_update.cfg");
|
||||
priv->settings = std::make_unique<QSettings>(settings_file, QSettings::Format::IniFormat);
|
||||
priv->allowBeta = priv->settings->value("allow_beta", false).toBool();
|
||||
priv->autoCheck = priv->settings->value("auto_check", false).toBool();
|
||||
bool interval_ok;
|
||||
// default once per day
|
||||
priv->updateInterval = priv->settings->value("update_interval", 86400).toInt(&interval_ok);
|
||||
if (!interval_ok)
|
||||
priv->updateInterval = 86400;
|
||||
auto last_check = priv->settings->value("last_check");
|
||||
if (!last_check.isNull() && last_check.isValid()) {
|
||||
priv->lastCheck = QDateTime::fromString(last_check.toString(), Qt::ISODate);
|
||||
}
|
||||
priv->parent = parent;
|
||||
connectTimer();
|
||||
resetAutoCheckTimer();
|
||||
}
|
||||
|
||||
PrismExternalUpdater::~PrismExternalUpdater()
|
||||
{
|
||||
if (priv->updateTimer.isActive())
|
||||
priv->updateTimer.stop();
|
||||
disconnectTimer();
|
||||
priv->settings->sync();
|
||||
delete priv;
|
||||
}
|
||||
|
||||
void PrismExternalUpdater::checkForUpdates()
|
||||
{
|
||||
QProgressDialog progress(tr("Checking for updates..."), "", 0, 0, priv->parent);
|
||||
progress.setCancelButton(nullptr);
|
||||
progress.adjustSize();
|
||||
progress.show();
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QProcess proc;
|
||||
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
|
||||
#if defined Q_OS_WIN32
|
||||
exe_name.append(".exe");
|
||||
|
||||
auto env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
|
||||
proc.setProcessEnvironment(env);
|
||||
#else
|
||||
exe_name = QString("bin/%1").arg(exe_name);
|
||||
#endif
|
||||
|
||||
QStringList args = { "--check-only", "--dir", priv->dataDir.absolutePath(), "--debug" };
|
||||
if (priv->allowBeta)
|
||||
args.append("--pre-release");
|
||||
|
||||
proc.start(priv->appDir.absoluteFilePath(exe_name), args);
|
||||
auto result_start = proc.waitForStarted(5000);
|
||||
if (!result_start) {
|
||||
auto err = proc.error();
|
||||
qDebug() << "Failed to start updater after 5 seconds."
|
||||
<< "reason:" << err << proc.errorString();
|
||||
auto msgBox =
|
||||
QMessageBox(QMessageBox::Information, tr("Update Check Failed"),
|
||||
tr("Failed to start after 5 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent);
|
||||
msgBox.setMinimumWidth(460);
|
||||
msgBox.adjustSize();
|
||||
msgBox.exec();
|
||||
priv->lastCheck = QDateTime::currentDateTime();
|
||||
priv->settings->setValue("last_check", priv->lastCheck.toString(Qt::ISODate));
|
||||
priv->settings->sync();
|
||||
resetAutoCheckTimer();
|
||||
return;
|
||||
}
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
auto result_finished = proc.waitForFinished(60000);
|
||||
if (!result_finished) {
|
||||
proc.kill();
|
||||
auto err = proc.error();
|
||||
auto output = proc.readAll();
|
||||
qDebug() << "Updater failed to close after 60 seconds."
|
||||
<< "reason:" << err << proc.errorString();
|
||||
auto msgBox =
|
||||
QMessageBox(QMessageBox::Information, tr("Update Check Failed"),
|
||||
tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent);
|
||||
msgBox.setDetailedText(output);
|
||||
msgBox.setMinimumWidth(460);
|
||||
msgBox.adjustSize();
|
||||
msgBox.exec();
|
||||
priv->lastCheck = QDateTime::currentDateTime();
|
||||
priv->settings->setValue("last_check", priv->lastCheck.toString(Qt::ISODate));
|
||||
priv->settings->sync();
|
||||
resetAutoCheckTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
auto exit_code = proc.exitCode();
|
||||
|
||||
auto std_output = proc.readAllStandardOutput();
|
||||
auto std_error = proc.readAllStandardError();
|
||||
|
||||
progress.hide();
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
switch (exit_code) {
|
||||
case 0:
|
||||
// no update available
|
||||
{
|
||||
qDebug() << "No update available";
|
||||
auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("You are running the latest version."),
|
||||
QMessageBox::Ok, priv->parent);
|
||||
msgBox.setMinimumWidth(460);
|
||||
msgBox.adjustSize();
|
||||
msgBox.exec();
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
// there was an error
|
||||
{
|
||||
qDebug() << "Updater subprocess error" << qPrintable(std_error);
|
||||
auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Check Error"),
|
||||
tr("There was an error running the update check."), QMessageBox::Ok, priv->parent);
|
||||
msgBox.setDetailedText(QString(std_error));
|
||||
msgBox.setMinimumWidth(460);
|
||||
msgBox.adjustSize();
|
||||
msgBox.exec();
|
||||
}
|
||||
break;
|
||||
case 100:
|
||||
// update available
|
||||
{
|
||||
auto [first_line, remainder1] = StringUtils::splitFirst(std_output, '\n');
|
||||
auto [second_line, remainder2] = StringUtils::splitFirst(remainder1, '\n');
|
||||
auto [third_line, release_notes] = StringUtils::splitFirst(remainder2, '\n');
|
||||
auto version_name = StringUtils::splitFirst(first_line, ": ").second.trimmed();
|
||||
auto version_tag = StringUtils::splitFirst(second_line, ": ").second.trimmed();
|
||||
auto release_timestamp = QDateTime::fromString(StringUtils::splitFirst(third_line, ": ").second.trimmed(), Qt::ISODate);
|
||||
qDebug() << "Update available:" << version_name << version_tag << release_timestamp;
|
||||
qDebug() << "Update release notes:" << release_notes;
|
||||
|
||||
offerUpdate(version_name, version_tag, release_notes);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// unknown error code
|
||||
{
|
||||
qDebug() << "Updater exited with unknown code" << exit_code;
|
||||
auto msgBox =
|
||||
QMessageBox(QMessageBox::Information, tr("Unknown Update Error"),
|
||||
tr("The updater exited with an unknown condition.\nExit Code: %1").arg(QString::number(exit_code)),
|
||||
QMessageBox::Ok, priv->parent);
|
||||
auto detail_txt = tr("StdOut: %1\nStdErr: %2").arg(QString(std_output)).arg(QString(std_error));
|
||||
msgBox.setDetailedText(detail_txt);
|
||||
msgBox.setMinimumWidth(460);
|
||||
msgBox.adjustSize();
|
||||
msgBox.exec();
|
||||
}
|
||||
}
|
||||
priv->lastCheck = QDateTime::currentDateTime();
|
||||
priv->settings->setValue("last_check", priv->lastCheck.toString(Qt::ISODate));
|
||||
priv->settings->sync();
|
||||
resetAutoCheckTimer();
|
||||
}
|
||||
|
||||
bool PrismExternalUpdater::getAutomaticallyChecksForUpdates()
|
||||
{
|
||||
return priv->autoCheck;
|
||||
}
|
||||
|
||||
double PrismExternalUpdater::getUpdateCheckInterval()
|
||||
{
|
||||
return priv->updateInterval;
|
||||
}
|
||||
|
||||
bool PrismExternalUpdater::getBetaAllowed()
|
||||
{
|
||||
return priv->allowBeta;
|
||||
}
|
||||
|
||||
void PrismExternalUpdater::setAutomaticallyChecksForUpdates(bool check)
|
||||
{
|
||||
priv->autoCheck = check;
|
||||
priv->settings->setValue("auto_check", check);
|
||||
priv->settings->sync();
|
||||
resetAutoCheckTimer();
|
||||
}
|
||||
|
||||
void PrismExternalUpdater::setUpdateCheckInterval(double seconds)
|
||||
{
|
||||
priv->updateInterval = seconds;
|
||||
priv->settings->setValue("update_interval", seconds);
|
||||
priv->settings->sync();
|
||||
resetAutoCheckTimer();
|
||||
}
|
||||
|
||||
void PrismExternalUpdater::setBetaAllowed(bool allowed)
|
||||
{
|
||||
priv->allowBeta = allowed;
|
||||
priv->settings->setValue("auto_beta", allowed);
|
||||
priv->settings->sync();
|
||||
}
|
||||
|
||||
void PrismExternalUpdater::resetAutoCheckTimer()
|
||||
{
|
||||
if (priv->autoCheck) {
|
||||
int timeoutDuration = 0;
|
||||
auto now = QDateTime::currentDateTime();
|
||||
if (priv->lastCheck.isValid()) {
|
||||
auto diff = priv->lastCheck.secsTo(now);
|
||||
auto secs_left = priv->updateInterval - diff;
|
||||
if (secs_left < 0)
|
||||
secs_left = 0;
|
||||
timeoutDuration = secs_left * 1000; // to msec
|
||||
}
|
||||
qDebug() << "Auto update timer starting," << timeoutDuration / 1000 << "seconds left";
|
||||
priv->updateTimer.start(timeoutDuration);
|
||||
} else {
|
||||
if (priv->updateTimer.isActive())
|
||||
priv->updateTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void PrismExternalUpdater::connectTimer()
|
||||
{
|
||||
connect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired);
|
||||
}
|
||||
|
||||
void PrismExternalUpdater::disconnectTimer()
|
||||
{
|
||||
disconnect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired);
|
||||
}
|
||||
|
||||
void PrismExternalUpdater::autoCheckTimerFired()
|
||||
{
|
||||
qDebug() << "Auto update Timer fired";
|
||||
checkForUpdates();
|
||||
}
|
||||
|
||||
void PrismExternalUpdater::offerUpdate(const QString& version_name, const QString& version_tag, const QString& release_notes)
|
||||
{
|
||||
priv->settings->beginGroup("skip");
|
||||
auto should_skip = priv->settings->value(version_tag, false).toBool();
|
||||
priv->settings->endGroup();
|
||||
|
||||
if (should_skip) {
|
||||
auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("There are no new updates available."),
|
||||
QMessageBox::Ok, priv->parent);
|
||||
msgBox.setMinimumWidth(460);
|
||||
msgBox.adjustSize();
|
||||
msgBox.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateAvailableDialog dlg(BuildConfig.printableVersionString(), version_name, release_notes);
|
||||
|
||||
auto result = dlg.exec();
|
||||
qDebug() << "offer dlg result" << result;
|
||||
switch (result) {
|
||||
case UpdateAvailableDialog::Install: {
|
||||
performUpdate(version_tag);
|
||||
return;
|
||||
}
|
||||
case UpdateAvailableDialog::Skip: {
|
||||
priv->settings->beginGroup("skip");
|
||||
priv->settings->setValue(version_tag, true);
|
||||
priv->settings->endGroup();
|
||||
priv->settings->sync();
|
||||
return;
|
||||
}
|
||||
case UpdateAvailableDialog::DontInstall: {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrismExternalUpdater::performUpdate(const QString& version_tag)
|
||||
{
|
||||
QProcess proc;
|
||||
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
|
||||
#if defined Q_OS_WIN32
|
||||
exe_name.append(".exe");
|
||||
|
||||
auto env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
|
||||
proc.setProcessEnvironment(env);
|
||||
#else
|
||||
exe_name = QString("bin/%1").arg(exe_name);
|
||||
#endif
|
||||
|
||||
QStringList args = { "--dir", priv->dataDir.absolutePath(), "--install-version", version_tag };
|
||||
if (priv->allowBeta)
|
||||
args.append("--pre-release");
|
||||
|
||||
auto result = proc.startDetached(priv->appDir.absoluteFilePath(exe_name), args);
|
||||
if (!result) {
|
||||
qDebug() << "Failed to start updater:" << proc.error() << proc.errorString();
|
||||
}
|
||||
QCoreApplication::exit();
|
||||
}
|
95
launcher/updater/PrismExternalUpdater.h
Normal file
95
launcher/updater/PrismExternalUpdater.h
Normal file
@ -0,0 +1,95 @@
|
||||
// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
//
|
||||
// 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 <QObject>
|
||||
|
||||
#include "ExternalUpdater.h"
|
||||
|
||||
/*!
|
||||
* An implementation for the updater on windows and linux that uses out external updater.
|
||||
*/
|
||||
|
||||
class PrismExternalUpdater : public ExternalUpdater {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PrismExternalUpdater(QWidget* parent, const QString& appDir, const QString& dataDir);
|
||||
~PrismExternalUpdater() override;
|
||||
|
||||
/*!
|
||||
* Check for updates manually, showing the user a progress bar and an alert if no updates are found.
|
||||
*/
|
||||
void checkForUpdates() override;
|
||||
|
||||
/*!
|
||||
* Indicates whether or not to check for updates automatically.
|
||||
*/
|
||||
bool getAutomaticallyChecksForUpdates() override;
|
||||
|
||||
/*!
|
||||
* Indicates the current automatic update check interval in seconds.
|
||||
*/
|
||||
double getUpdateCheckInterval() override;
|
||||
|
||||
/*!
|
||||
* Indicates whether or not beta updates should be checked for in addition to regular releases.
|
||||
*/
|
||||
bool getBetaAllowed() override;
|
||||
|
||||
/*!
|
||||
* Set whether or not to check for updates automatically.
|
||||
*
|
||||
* The update schedule cycle will be reset in a short delay after the property’s new value is set. This is to allow
|
||||
* reverting this property without kicking off a schedule change immediately."
|
||||
*/
|
||||
void setAutomaticallyChecksForUpdates(bool check) override;
|
||||
|
||||
/*!
|
||||
* Set the current automatic update check interval in seconds.
|
||||
*
|
||||
* The update schedule cycle will be reset in a short delay after the property’s new value is set. This is to allow
|
||||
* reverting this property without kicking off a schedule change immediately."
|
||||
*/
|
||||
void setUpdateCheckInterval(double seconds) override;
|
||||
|
||||
/*!
|
||||
* Set whether or not beta updates should be checked for in addition to regular releases.
|
||||
*/
|
||||
void setBetaAllowed(bool allowed) override;
|
||||
|
||||
void resetAutoCheckTimer();
|
||||
void disconnectTimer();
|
||||
void connectTimer();
|
||||
|
||||
void offerUpdate(const QString& version_name, const QString& version_tag, const QString& release_notes);
|
||||
void performUpdate(const QString& version_tag);
|
||||
|
||||
public slots:
|
||||
void autoCheckTimerFired();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
|
||||
Private* priv;
|
||||
};
|
93
launcher/updater/prismupdater/GitHubRelease.cpp
Normal file
93
launcher/updater/prismupdater/GitHubRelease.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "GitHubRelease.h"
|
||||
|
||||
QDebug operator<<(QDebug debug, const GitHubReleaseAsset& asset)
|
||||
{
|
||||
QDebugStateSaver saver(debug);
|
||||
debug.nospace() << "GitHubReleaseAsset( "
|
||||
"id: "
|
||||
<< asset.id
|
||||
<< ", "
|
||||
"name "
|
||||
<< asset.name
|
||||
<< ", "
|
||||
"label: "
|
||||
<< asset.label
|
||||
<< ", "
|
||||
"content_type: "
|
||||
<< asset.content_type
|
||||
<< ", "
|
||||
"size: "
|
||||
<< asset.size
|
||||
<< ", "
|
||||
"created_at: "
|
||||
<< asset.created_at
|
||||
<< ", "
|
||||
"updated_at: "
|
||||
<< asset.updated_at
|
||||
<< ", "
|
||||
"browser_download_url: "
|
||||
<< asset.browser_download_url
|
||||
<< " "
|
||||
")";
|
||||
return debug;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const GitHubRelease& rls)
|
||||
{
|
||||
QDebugStateSaver saver(debug);
|
||||
debug.nospace() << "GitHubRelease( "
|
||||
"id: "
|
||||
<< rls.id
|
||||
<< ", "
|
||||
"name "
|
||||
<< rls.name
|
||||
<< ", "
|
||||
"tag_name: "
|
||||
<< rls.tag_name
|
||||
<< ", "
|
||||
"created_at: "
|
||||
<< rls.created_at
|
||||
<< ", "
|
||||
"published_at: "
|
||||
<< rls.published_at
|
||||
<< ", "
|
||||
"prerelease: "
|
||||
<< rls.prerelease
|
||||
<< ", "
|
||||
"draft: "
|
||||
<< rls.draft
|
||||
<< ", "
|
||||
"version"
|
||||
<< rls.version
|
||||
<< ", "
|
||||
"body: "
|
||||
<< rls.body
|
||||
<< ", "
|
||||
"assets: "
|
||||
<< rls.assets
|
||||
<< " "
|
||||
")";
|
||||
return debug;
|
||||
}
|
61
launcher/updater/prismupdater/GitHubRelease.h
Normal file
61
launcher/updater/prismupdater/GitHubRelease.h
Normal file
@ -0,0 +1,61 @@
|
||||
// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
//
|
||||
// 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 <QDateTime>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "Version.h"
|
||||
|
||||
struct GitHubReleaseAsset {
|
||||
int id = -1;
|
||||
QString name;
|
||||
QString label;
|
||||
QString content_type;
|
||||
int size;
|
||||
QDateTime created_at;
|
||||
QDateTime updated_at;
|
||||
QString browser_download_url;
|
||||
|
||||
bool isValid() { return id > 0; }
|
||||
};
|
||||
|
||||
struct GitHubRelease {
|
||||
int id = -1;
|
||||
QString name;
|
||||
QString tag_name;
|
||||
QDateTime created_at;
|
||||
QDateTime published_at;
|
||||
bool prerelease;
|
||||
bool draft;
|
||||
QString body;
|
||||
QList<GitHubReleaseAsset> assets;
|
||||
Version version;
|
||||
|
||||
bool isValid() const { return id > 0; }
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug debug, const GitHubReleaseAsset& rls);
|
||||
QDebug operator<<(QDebug debug, const GitHubRelease& rls);
|
1401
launcher/updater/prismupdater/PrismUpdater.cpp
Normal file
1401
launcher/updater/prismupdater/PrismUpdater.cpp
Normal file
File diff suppressed because it is too large
Load Diff
143
launcher/updater/prismupdater/PrismUpdater.h
Normal file
143
launcher/updater/prismupdater/PrismUpdater.h
Normal file
@ -0,0 +1,143 @@
|
||||
// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
//
|
||||
// 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 <QtCore>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDataStream>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QFlag>
|
||||
#include <QIcon>
|
||||
#include <QLocalSocket>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QUrl>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
#include "net/Download.h"
|
||||
|
||||
#define PRISM_EXTERNAL_EXE
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include "GitHubRelease.h"
|
||||
|
||||
class PrismUpdaterApp : public QApplication {
|
||||
// friends for the purpose of limiting access to deprecated stuff
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Status { Starting, Failed, Succeeded, Initialized, Aborted };
|
||||
PrismUpdaterApp(int& argc, char** argv);
|
||||
virtual ~PrismUpdaterApp();
|
||||
void loadReleaseList();
|
||||
void run();
|
||||
Status status() const { return m_status; }
|
||||
|
||||
private:
|
||||
void fail(const QString& reason);
|
||||
void abort(const QString& reason);
|
||||
void showFatalErrorMessage(const QString& title, const QString& content);
|
||||
|
||||
bool loadPrismVersionFromExe(const QString& exe_path);
|
||||
|
||||
void downloadReleasePage(const QString& api_url, int page);
|
||||
int parseReleasePage(const QByteArray* response);
|
||||
|
||||
bool needUpdate(const GitHubRelease& release);
|
||||
|
||||
GitHubRelease getLatestRelease();
|
||||
GitHubRelease selectRelease();
|
||||
QList<GitHubRelease> newerReleases();
|
||||
QList<GitHubRelease> nonDraftReleases();
|
||||
|
||||
void printReleases();
|
||||
|
||||
QList<GitHubReleaseAsset> validReleaseArtifacts(const GitHubRelease& release);
|
||||
GitHubReleaseAsset selectAsset(const QList<GitHubReleaseAsset>& assets);
|
||||
void performUpdate(const GitHubRelease& release);
|
||||
void performInstall(QFileInfo file);
|
||||
void unpackAndInstall(QFileInfo file);
|
||||
void backupAppDir();
|
||||
std::optional<QDir> unpackArchive(QFileInfo file);
|
||||
|
||||
QFileInfo downloadAsset(const GitHubReleaseAsset& asset);
|
||||
bool callAppImageUpdate();
|
||||
|
||||
void moveAndFinishUpdate(QDir target);
|
||||
|
||||
public slots:
|
||||
void downloadError(QString reason);
|
||||
|
||||
private:
|
||||
const QString& root() { return m_rootPath; }
|
||||
|
||||
bool isPortable() { return m_isPortable; }
|
||||
|
||||
void clearUpdateLog();
|
||||
void logUpdate(const QString& msg);
|
||||
|
||||
QString m_rootPath;
|
||||
QString m_dataPath;
|
||||
bool m_isPortable = false;
|
||||
bool m_isAppimage = false;
|
||||
bool m_isFlatpak = false;
|
||||
QString m_appimagePath;
|
||||
QString m_prismExecutable;
|
||||
QUrl m_prismRepoUrl;
|
||||
Version m_userSelectedVersion;
|
||||
bool m_checkOnly;
|
||||
bool m_forceUpdate;
|
||||
bool m_printOnly;
|
||||
bool m_selectUI;
|
||||
bool m_allowDowngrade;
|
||||
bool m_allowPreRelease;
|
||||
|
||||
QString m_updateLogPath;
|
||||
|
||||
QString m_prismBinaryName;
|
||||
QString m_prismVersion;
|
||||
int m_prismVersionMajor = -1;
|
||||
int m_prismVersionMinor = -1;
|
||||
QString m_prsimVersionChannel;
|
||||
QString m_prismGitCommit;
|
||||
|
||||
GitHubRelease m_install_release;
|
||||
|
||||
Status m_status = Status::Starting;
|
||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||
QString m_current_url;
|
||||
Task::Ptr m_current_task;
|
||||
QList<GitHubRelease> m_releases;
|
||||
|
||||
public:
|
||||
std::unique_ptr<QFile> logFile;
|
||||
bool logToConsole = false;
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
// used on Windows to attach the standard IO streams
|
||||
bool consoleAttached = false;
|
||||
#endif
|
||||
};
|
89
launcher/updater/prismupdater/SelectReleaseDialog.ui
Normal file
89
launcher/updater/prismupdater/SelectReleaseDialog.ui
Normal file
@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SelectReleaseDialog</class>
|
||||
<widget class="QDialog" name="SelectReleaseDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>468</width>
|
||||
<height>385</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Select Release to Install</string>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1,0">
|
||||
<item>
|
||||
<widget class="QLabel" name="eplainLabel">
|
||||
<property name="text">
|
||||
<string>Please select the release you wish to update to.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="versionsTree">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">1</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="changelogTextBrowser"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>SelectReleaseDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>SelectReleaseDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
168
launcher/updater/prismupdater/UpdaterDialogs.cpp
Normal file
168
launcher/updater/prismupdater/UpdaterDialogs.cpp
Normal file
@ -0,0 +1,168 @@
|
||||
// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "UpdaterDialogs.h"
|
||||
|
||||
#include "ui_SelectReleaseDialog.h"
|
||||
|
||||
#include <QTextBrowser>
|
||||
#include "Markdown.h"
|
||||
|
||||
SelectReleaseDialog::SelectReleaseDialog(const Version& current_version, const QList<GitHubRelease>& releases, QWidget* parent)
|
||||
: QDialog(parent), m_releases(releases), m_currentVersion(current_version), ui(new Ui::SelectReleaseDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->changelogTextBrowser->setOpenExternalLinks(true);
|
||||
ui->changelogTextBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth);
|
||||
ui->changelogTextBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded);
|
||||
|
||||
ui->versionsTree->setColumnCount(2);
|
||||
|
||||
ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
|
||||
ui->versionsTree->setHeaderLabels({ tr("Version"), tr("Published Date") });
|
||||
ui->versionsTree->header()->setStretchLastSection(false);
|
||||
|
||||
ui->eplainLabel->setText(tr("Select a version to install.\n"
|
||||
"\n"
|
||||
"Currently installed version: %1")
|
||||
.arg(m_currentVersion.toString()));
|
||||
|
||||
loadReleases();
|
||||
|
||||
connect(ui->versionsTree, &QTreeWidget::currentItemChanged, this, &SelectReleaseDialog::selectionChanged);
|
||||
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SelectReleaseDialog::accept);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &SelectReleaseDialog::reject);
|
||||
}
|
||||
|
||||
SelectReleaseDialog::~SelectReleaseDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void SelectReleaseDialog::loadReleases()
|
||||
{
|
||||
for (auto rls : m_releases) {
|
||||
appendRelease(rls);
|
||||
}
|
||||
}
|
||||
|
||||
void SelectReleaseDialog::appendRelease(GitHubRelease const& release)
|
||||
{
|
||||
auto rls_item = new QTreeWidgetItem(ui->versionsTree);
|
||||
rls_item->setText(0, release.tag_name);
|
||||
rls_item->setExpanded(true);
|
||||
rls_item->setText(1, release.published_at.toString());
|
||||
rls_item->setData(0, Qt::UserRole, QVariant(release.id));
|
||||
|
||||
ui->versionsTree->addTopLevelItem(rls_item);
|
||||
}
|
||||
|
||||
GitHubRelease SelectReleaseDialog::getRelease(QTreeWidgetItem* item)
|
||||
{
|
||||
int id = item->data(0, Qt::UserRole).toInt();
|
||||
GitHubRelease release;
|
||||
for (auto rls : m_releases) {
|
||||
if (rls.id == id)
|
||||
release = rls;
|
||||
}
|
||||
return release;
|
||||
}
|
||||
|
||||
void SelectReleaseDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
|
||||
{
|
||||
GitHubRelease release = getRelease(current);
|
||||
QString body = markdownToHTML(release.body.toUtf8());
|
||||
m_selectedRelease = release;
|
||||
|
||||
ui->changelogTextBrowser->setHtml(body);
|
||||
}
|
||||
|
||||
SelectReleaseAssetDialog::SelectReleaseAssetDialog(const QList<GitHubReleaseAsset>& assets, QWidget* parent)
|
||||
: QDialog(parent), m_assets(assets), ui(new Ui::SelectReleaseDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->changelogTextBrowser->setOpenExternalLinks(true);
|
||||
ui->changelogTextBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth);
|
||||
ui->changelogTextBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded);
|
||||
|
||||
ui->versionsTree->setColumnCount(2);
|
||||
|
||||
ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
|
||||
ui->versionsTree->setHeaderLabels({ tr("Version"), tr("Published Date") });
|
||||
ui->versionsTree->header()->setStretchLastSection(false);
|
||||
|
||||
ui->eplainLabel->setText(tr("Select a version to install."));
|
||||
|
||||
ui->changelogTextBrowser->setHidden(true);
|
||||
|
||||
loadAssets();
|
||||
|
||||
connect(ui->versionsTree, &QTreeWidget::currentItemChanged, this, &SelectReleaseAssetDialog::selectionChanged);
|
||||
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SelectReleaseAssetDialog::accept);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &SelectReleaseAssetDialog::reject);
|
||||
}
|
||||
|
||||
SelectReleaseAssetDialog::~SelectReleaseAssetDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void SelectReleaseAssetDialog::loadAssets()
|
||||
{
|
||||
for (auto rls : m_assets) {
|
||||
appendAsset(rls);
|
||||
}
|
||||
}
|
||||
|
||||
void SelectReleaseAssetDialog::appendAsset(GitHubReleaseAsset const& asset)
|
||||
{
|
||||
auto rls_item = new QTreeWidgetItem(ui->versionsTree);
|
||||
rls_item->setText(0, asset.name);
|
||||
rls_item->setExpanded(true);
|
||||
rls_item->setText(1, asset.updated_at.toString());
|
||||
rls_item->setData(0, Qt::UserRole, QVariant(asset.id));
|
||||
|
||||
ui->versionsTree->addTopLevelItem(rls_item);
|
||||
}
|
||||
|
||||
GitHubReleaseAsset SelectReleaseAssetDialog::getAsset(QTreeWidgetItem* item)
|
||||
{
|
||||
int id = item->data(0, Qt::UserRole).toInt();
|
||||
GitHubReleaseAsset selected_asset;
|
||||
for (auto asset : m_assets) {
|
||||
if (asset.id == id)
|
||||
selected_asset = asset;
|
||||
}
|
||||
return selected_asset;
|
||||
}
|
||||
|
||||
void SelectReleaseAssetDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
|
||||
{
|
||||
GitHubReleaseAsset asset = getAsset(current);
|
||||
m_selectedAsset = asset;
|
||||
}
|
75
launcher/updater/prismupdater/UpdaterDialogs.h
Normal file
75
launcher/updater/prismupdater/UpdaterDialogs.h
Normal file
@ -0,0 +1,75 @@
|
||||
// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
//
|
||||
// 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 <QDialog>
|
||||
#include <QTreeWidgetItem>
|
||||
|
||||
#include "GitHubRelease.h"
|
||||
#include "Version.h"
|
||||
|
||||
namespace Ui {
|
||||
class SelectReleaseDialog;
|
||||
}
|
||||
|
||||
class SelectReleaseDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SelectReleaseDialog(const Version& cur_version, const QList<GitHubRelease>& releases, QWidget* parent = 0);
|
||||
~SelectReleaseDialog();
|
||||
|
||||
void loadReleases();
|
||||
void appendRelease(GitHubRelease const& release);
|
||||
GitHubRelease selectedRelease() { return m_selectedRelease; }
|
||||
private slots:
|
||||
GitHubRelease getRelease(QTreeWidgetItem* item);
|
||||
void selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous);
|
||||
|
||||
protected:
|
||||
QList<GitHubRelease> m_releases;
|
||||
GitHubRelease m_selectedRelease;
|
||||
Version m_currentVersion;
|
||||
|
||||
Ui::SelectReleaseDialog* ui;
|
||||
};
|
||||
|
||||
class SelectReleaseAssetDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SelectReleaseAssetDialog(const QList<GitHubReleaseAsset>& assets, QWidget* parent = 0);
|
||||
~SelectReleaseAssetDialog();
|
||||
|
||||
void loadAssets();
|
||||
void appendAsset(GitHubReleaseAsset const& asset);
|
||||
GitHubReleaseAsset selectedAsset() { return m_selectedAsset; }
|
||||
private slots:
|
||||
GitHubReleaseAsset getAsset(QTreeWidgetItem* item);
|
||||
void selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous);
|
||||
|
||||
protected:
|
||||
QList<GitHubReleaseAsset> m_assets;
|
||||
GitHubReleaseAsset m_selectedAsset;
|
||||
|
||||
Ui::SelectReleaseDialog* ui;
|
||||
};
|
26
launcher/updater/prismupdater/updater.exe.manifest
Normal file
26
launcher/updater/prismupdater/updater.exe.manifest
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 10, Windows 11 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
</assembly>
|
40
launcher/updater/prismupdater/updater_main.cpp
Normal file
40
launcher/updater/prismupdater/updater_main.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "PrismUpdater.h"
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
PrismUpdaterApp wUpApp(argc, argv);
|
||||
|
||||
switch (wUpApp.status()) {
|
||||
case PrismUpdaterApp::Starting:
|
||||
case PrismUpdaterApp::Initialized: {
|
||||
return wUpApp.exec();
|
||||
}
|
||||
case PrismUpdaterApp::Failed:
|
||||
return 1;
|
||||
case PrismUpdaterApp::Succeeded:
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
@ -350,6 +350,7 @@ Section "@Launcher_DisplayName@"
|
||||
|
||||
File "@Launcher_APP_BINARY_NAME@.exe"
|
||||
File "@Launcher_APP_BINARY_NAME@_filelink.exe"
|
||||
File "@Launcher_APP_BINARY_NAME@_updater.exe"
|
||||
File "qt.conf"
|
||||
File "qtlogging.ini"
|
||||
File *.dll
|
||||
@ -435,6 +436,7 @@ Section "Uninstall"
|
||||
|
||||
Delete $INSTDIR\@Launcher_APP_BINARY_NAME@.exe
|
||||
Delete $INSTDIR\@Launcher_APP_BINARY_NAME@_filelink.exe
|
||||
Delete $INSTDIR\@Launcher_APP_BINARY_NAME@_updater.exe
|
||||
Delete $INSTDIR\qt.conf
|
||||
Delete $INSTDIR\*.dll
|
||||
|
||||
@ -472,7 +474,6 @@ Function .onInit
|
||||
${GetParameters} $R0
|
||||
${GetOptions} $R0 "/NoShortcuts" $R1
|
||||
${IfNot} ${Errors}
|
||||
${OrIf} ${FileExists} "$InstDir\@Launcher_APP_BINARY_NAME@.exe"
|
||||
!insertmacro UnselectSection ${SM_SHORTCUTS}
|
||||
!insertmacro UnselectSection ${DESKTOP_SHORTCUTS}
|
||||
${EndIf}
|
||||
|
Loading…
Reference in New Issue
Block a user