Merge branch 'develop' into feature/sparkle-mac

# Conflicts:
#	.github/workflows/build.yml
This commit is contained in:
Sefa Eyeoglu 2022-07-10 19:38:30 +02:00
commit b3b76d5d56
No known key found for this signature in database
GPG Key ID: C10411294912A422
374 changed files with 12376 additions and 6287 deletions

16
.clang-format Normal file
View File

@ -0,0 +1,16 @@
---
Language: Cpp
BasedOnStyle: Chromium
IndentWidth: 4
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AllowShortIfStatementsOnASingleLine: false
BraceWrapping:
AfterFunction: true
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBraces: Custom
BreakConstructorInitializers: BeforeComma
ColumnLimit: 140
Cpp11BracedListStyle: false

View File

@ -27,7 +27,7 @@ body:
attributes: attributes:
label: Version of PolyMC label: Version of PolyMC
description: The version of PolyMC used in the bug report. description: The version of PolyMC used in the bug report.
placeholder: PolyMC 1.2.2 placeholder: PolyMC 1.3.2
validations: validations:
required: true required: true
- type: textarea - type: textarea

2
.github/dco.yml vendored Normal file
View File

@ -0,0 +1,2 @@
allowRemediationCommits:
individual: true

View File

@ -1,19 +0,0 @@
name: Backport PR to stable
on:
pull_request:
branches: [ develop ]
types: [ closed ]
jobs:
release_pull_request:
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'backport')
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Backport PR by cherry-pick-ing
uses: Nathanmalnoury/gh-backport-action@master
with:
pr_branch: 'stable'
github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -20,20 +20,31 @@ jobs:
include: include:
- os: ubuntu-20.04 - os: ubuntu-20.04
qt_ver: 5
- os: ubuntu-20.04 - os: ubuntu-20.04
appimage: true appimage: true
qt_ver: 6
qt_host: linux
qt_version: '6.3.1'
qt_modules: 'qt5compat qtimageformats'
- os: windows-2022 - os: windows-2022
name: "Windows-i686" name: "Windows-Legacy"
msystem: mingw32 msystem: mingw32
qt_ver: 5
- os: windows-2022 - os: windows-2022
name: "Windows-x86_64" name: "Windows"
msystem: mingw64 msystem: mingw32
qt_ver: 6
- os: macos-11 - os: macos-12
macosx_deployment_target: 10.13 macosx_deployment_target: 10.14
qt_ver: 6
qt_host: mac
qt_version: '6.3.1'
qt_modules: 'qt5compat qtimageformats'
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -43,6 +54,7 @@ jobs:
INSTALL_PORTABLE_DIR: "install-portable" INSTALL_PORTABLE_DIR: "install-portable"
INSTALL_APPIMAGE_DIR: "install-appdir" INSTALL_APPIMAGE_DIR: "install-appdir"
BUILD_DIR: "build" BUILD_DIR: "build"
CCACHE_VAR: ""
steps: steps:
## ##
@ -64,9 +76,15 @@ jobs:
pacboy: >- pacboy: >-
toolchain:p toolchain:p
cmake:p cmake:p
extra-cmake-modules:p
ninja:p ninja:p
qt5:p qt${{ matrix.qt_ver }}-base:p
qt${{ matrix.qt_ver }}-svg:p
qt${{ matrix.qt_ver }}-imageformats:p
quazip-qt${{ matrix.qt_ver }}:p
ccache:p ccache:p
nsis:p
${{ matrix.qt_ver == 6 && 'qt6-5compat:p' || '' }}
- name: Setup ccache - name: Setup ccache
if: runner.os != 'Windows' && inputs.build_type == 'Debug' if: runner.os != 'Windows' && inputs.build_type == 'Debug'
@ -84,6 +102,12 @@ jobs:
ccache -p # Show config ccache -p # Show config
ccache -z # Zero stats ccache -z # Zero stats
- name: Use ccache on Debug builds only
if: inputs.build_type == 'Debug'
shell: bash
run: |
echo "CCACHE_VAR=ccache" >> $GITHUB_ENV
- name: Retrieve ccache cache (Windows) - name: Retrieve ccache cache (Windows)
if: runner.os == 'Windows' && inputs.build_type == 'Debug' if: runner.os == 'Windows' && inputs.build_type == 'Debug'
uses: actions/cache@v3.0.2 uses: actions/cache@v3.0.2
@ -99,22 +123,32 @@ jobs:
ver_short=`git rev-parse --short HEAD` ver_short=`git rev-parse --short HEAD`
echo "VERSION=$ver_short" >> $GITHUB_ENV echo "VERSION=$ver_short" >> $GITHUB_ENV
- name: Install Qt (macOS) - name: Install Dependencies (Linux)
if: runner.os == 'macOS'
run: |
brew update
brew install qt@5 ninja
- name: Update Qt (AppImage)
if: runner.os == 'Linux' && matrix.appimage == true
run: |
sudo add-apt-repository ppa:savoury1/qt-5-15
- name: Install Qt (Linux)
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: | run: |
sudo apt-get -y update sudo apt-get -y update
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 ninja-build sudo apt-get -y install ninja-build extra-cmake-modules
- name: Install Dependencies (macOS)
if: runner.os == 'macOS'
run: |
brew update
brew install ninja extra-cmake-modules
- name: Install Qt (Linux)
if: runner.os == 'Linux' && matrix.appimage != true
run: |
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 qt5-image-formats-plugins
- name: Install Qt (macOS and AppImage)
if: matrix.qt_ver == 6 && runner.os != 'Windows'
uses: jurplel/install-qt-action@v2
with:
version: ${{ matrix.qt_version }}
host: ${{ matrix.qt_host }}
target: 'desktop'
modules: ${{ matrix.qt_modules }}
aqtversion: ==2.1.*
- name: Prepare AppImage (Linux) - name: Prepare AppImage (Linux)
if: runner.os == 'Linux' && matrix.appimage == true if: runner.os == 'Linux' && matrix.appimage == true
@ -132,18 +166,18 @@ jobs:
- name: Configure CMake (macOS) - name: Configure CMake (macOS)
if: runner.os == 'macOS' if: runner.os == 'macOS'
run: | run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DQt5_DIR=/usr/local/opt/qt@5 -DCMAKE_PREFIX_PATH=/usr/local/opt/qt@5 -DLauncher_BUILD_PLATFORM=macOS -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -G Ninja cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DLauncher_BUILD_PLATFORM=macOS -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
- name: Configure CMake (Windows) - name: Configure CMake (Windows)
if: runner.os == 'Windows' if: runner.os == 'Windows'
shell: msys2 {0} shell: msys2 {0}
run: | run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -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=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
- name: Configure CMake (Linux) - name: Configure CMake (Linux)
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: | run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -G Ninja cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
## ##
# BUILD # BUILD
@ -160,6 +194,21 @@ jobs:
run: | run: |
cmake --build ${{ env.BUILD_DIR }} cmake --build ${{ env.BUILD_DIR }}
##
# TEST
##
- name: Test
if: runner.os != 'Windows'
run: |
ctest --test-dir build --output-on-failure
- name: Test (Windows)
if: runner.os == 'Windows'
shell: msys2 {0}
run: |
ctest --test-dir build --output-on-failure
## ##
# PACKAGE BUILDS # PACKAGE BUILDS
## ##
@ -213,6 +262,13 @@ jobs:
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
- name: Package (Windows, installer)
if: runner.os == 'Windows'
shell: msys2 {0}
run: |
cd ${{ env.INSTALL_DIR }}
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
- name: Package (Linux) - name: Package (Linux)
if: runner.os == 'Linux' && matrix.appimage != true if: runner.os == 'Linux' && matrix.appimage != true
run: | run: |
@ -241,11 +297,14 @@ jobs:
chmod +x linuxdeploy-*.AppImage chmod +x linuxdeploy-*.AppImage
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-{8,17}-openjdk mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-{8,17}-openjdk
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk
cp -r /home/runner/work/PolyMC/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib" LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server" LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64" LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64"
@ -280,6 +339,13 @@ jobs:
name: PolyMC-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }} name: PolyMC-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: ${{ env.INSTALL_PORTABLE_DIR }}/** path: ${{ env.INSTALL_PORTABLE_DIR }}/**
- name: Upload installer (Windows)
if: runner.os == 'Windows'
uses: actions/upload-artifact@v3
with:
name: PolyMC-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
path: PolyMC-Setup.exe
- name: Upload binary tarball (Linux) - name: Upload binary tarball (Linux)
if: runner.os == 'Linux' && matrix.appimage != true if: runner.os == 'Linux' && matrix.appimage != true
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3

View File

@ -1,61 +0,0 @@
name: Comment on pull request
on:
workflow_run:
workflows: ['Build Application']
types: [completed]
jobs:
pr_comment:
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v5
with:
# This snippet is public-domain, taken from
# https://github.com/oprypin/nightly.link/blob/master/.github/workflows/pr-comment.yml
script: |
async function upsertComment(owner, repo, issue_number, purpose, body) {
const {data: comments} = await github.rest.issues.listComments(
{owner, repo, issue_number});
const marker = `<!-- bot: ${purpose} -->`;
body = marker + "\n" + body;
const existing = comments.filter((c) => c.body.includes(marker));
if (existing.length > 0) {
const last = existing[existing.length - 1];
core.info(`Updating comment ${last.id}`);
await github.rest.issues.updateComment({
owner, repo,
body,
comment_id: last.id,
});
} else {
core.info(`Creating a comment in issue / PR #${issue_number}`);
await github.rest.issues.createComment({issue_number, body, owner, repo});
}
}
const {owner, repo} = context.repo;
const run_id = ${{github.event.workflow_run.id}};
const pull_requests = ${{ toJSON(github.event.workflow_run.pull_requests) }};
if (!pull_requests.length) {
return core.error("This workflow doesn't match any pull requests!");
}
const artifacts = await github.paginate(
github.rest.actions.listWorkflowRunArtifacts, {owner, repo, run_id});
if (!artifacts.length) {
return core.error(`No artifacts found`);
}
let body = `Download the artifacts for this pull request:\n`;
for (const art of artifacts) {
body += `\n* [${art.name}.zip](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
}
core.info("Review thread message body:", body);
for (const pr of pull_requests) {
await upsertComment(owner, repo, pr.number,
"nightly-link", body);
}

View File

@ -42,11 +42,14 @@ jobs:
for d in PolyMC-Windows-*; do for d in PolyMC-Windows-*; do
cd "${d}" || continue cd "${d}" || continue
ARCH="$(echo -n ${d} | cut -d '-' -f 3)" LEGACY="$(echo -n ${d} | grep -o Legacy || true)"
INST="$(echo -n ${d} | grep -o Setup || true)"
PORT="$(echo -n ${d} | grep -o Portable || true)" PORT="$(echo -n ${d} | grep -o Portable || true)"
NAME="PolyMC-Windows-${ARCH}" NAME="PolyMC-Windows"
test -z "${LEGACY}" || NAME="${NAME}-Legacy"
test -z "${PORT}" || NAME="${NAME}-Portable" test -z "${PORT}" || NAME="${NAME}-Portable"
zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" * test -z "${INST}" || mv PolyMC-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
cd .. cd ..
done done
@ -66,7 +69,9 @@ jobs:
PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
PolyMC-Windows-i686-${{ env.VERSION }}.zip PolyMC-Windows-i686-${{ env.VERSION }}.zip
PolyMC-Windows-i686-Portable-${{ env.VERSION }}.zip PolyMC-Windows-i686-Portable-${{ env.VERSION }}.zip
PolyMC-Windows-i686-Setup-${{ env.VERSION }}.exe
PolyMC-Windows-x86_64-${{ env.VERSION }}.zip PolyMC-Windows-x86_64-${{ env.VERSION }}.zip
PolyMC-Windows-x86_64-Portable-${{ env.VERSION }}.zip PolyMC-Windows-x86_64-Portable-${{ env.VERSION }}.zip
PolyMC-Windows-x86_64-Setup-${{ env.VERSION }}.exe
PolyMC-macOS-${{ env.VERSION }}.tar.gz PolyMC-macOS-${{ env.VERSION }}.tar.gz
PolyMC-${{ env.VERSION }}.tar.gz PolyMC-${{ env.VERSION }}.tar.gz

14
.github/workflows/winget.yml vendored Normal file
View File

@ -0,0 +1,14 @@
name: Publish to WinGet
on:
release:
types: [released]
jobs:
publish:
runs-on: windows-latest
steps:
- uses: vedantmgoyal2009/winget-releaser@latest
with:
identifier: PolyMC.PolyMC
installers-regex: '\.exe$'
token: ${{ secrets.WINGET_TOKEN }}

1
.gitignore vendored
View File

@ -15,7 +15,6 @@ CMakeLists.txt.user.*
/.settings /.settings
/.idea /.idea
/.vscode /.vscode
.clang-format
cmake-build-*/ cmake-build-*/
Debug Debug

View File

@ -6,14 +6,12 @@ if(WIN32)
endif() endif()
project(Launcher) project(Launcher)
include(CTest)
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD) string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
if(IS_IN_SOURCE_BUILD) if(IS_IN_SOURCE_BUILD)
message(FATAL_ERROR "You are building the Launcher in-source. Please separate the build tree from the source tree.") message(FATAL_ERROR "You are building the Launcher in-source. Please separate the build tree from the source tree.")
endif() endif()
##################################### Set CMake options ##################################### ##################################### Set CMake options #####################################
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON) set(CMAKE_AUTORCC ON)
@ -34,17 +32,19 @@ set(CMAKE_C_STANDARD_REQUIRED true)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
include(GenerateExportHeader) include(GenerateExportHeader)
set(CMAKE_CXX_FLAGS " -Wall -pedantic -Werror -Wno-deprecated-declarations -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 -O3 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS "-Wall -pedantic -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}")
if(UNIX AND APPLE) if(UNIX AND APPLE)
set(CMAKE_CXX_FLAGS " -stdlib=libc++ ${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}")
endif() endif()
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type")
# Fix build with Qt 5.13 # Fix build with Qt 5.13
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_BEFORE=0x050C00") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_BEFORE=0x050C00")
# set CXXFLAGS for build targets
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
option(ENABLE_LTO "Enable Link Time Optimization" off) option(ENABLE_LTO "Enable Link Time Optimization" off)
if(ENABLE_LTO) if(ENABLE_LTO)
@ -61,6 +61,16 @@ if(ENABLE_LTO)
endif() endif()
endif() endif()
option(BUILD_TESTING "Build the testing tree." ON)
find_package(ECM REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH "${ECM_MODULE_PATH};${CMAKE_MODULE_PATH}")
include(CTest)
include(ECMAddTests)
if (BUILD_TESTING)
enable_testing()
endif()
##################################### Set Application options ##################################### ##################################### Set Application options #####################################
######## Set URLs ######## ######## Set URLs ########
@ -70,8 +80,8 @@ set(Launcher_HELP_URL "https://polymc.org/wiki/help-pages/%1" CACHE STRING "URL
######## Set version numbers ######## ######## Set version numbers ########
set(Launcher_VERSION_MAJOR 1) set(Launcher_VERSION_MAJOR 1)
set(Launcher_VERSION_MINOR 2) set(Launcher_VERSION_MINOR 4)
set(Launcher_VERSION_HOTFIX 2) set(Launcher_VERSION_HOTFIX 0)
# Build number # Build number
set(Launcher_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.") set(Launcher_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
@ -88,13 +98,6 @@ set(Launcher_META_URL "https://meta.polymc.org/v1/" CACHE STRING "URL to fetch L
# Imgur API Client ID # Imgur API Client ID
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application") set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
# MSA Client ID
set(Launcher_MSA_CLIENT_ID "549033b2-1532-4d4e-ae77-1bbaa46f9d74" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
# CurseForge API Key
# CHANGE THIS IF YOU FORK THIS PROJECT!
set(Launcher_CURSEFORGE_API_KEY "$2a$10$1Oqr2MX3O4n/ilhFGc597u8tfI3L2Hyr9/rtWDAMRjghSQV2QUuxq" CACHE STRING "CurseForge API Key")
# Bug tracker URL # Bug tracker URL
set(Launcher_BUG_TRACKER_URL "https://github.com/PolyMC/PolyMC/issues" CACHE STRING "URL for the bug tracker.") set(Launcher_BUG_TRACKER_URL "https://github.com/PolyMC/PolyMC/issues" CACHE STRING "URL for the bug tracker.")
@ -107,8 +110,6 @@ set(Launcher_MATRIX_URL "https://matrix.to/#/#polymc:matrix.org" CACHE STRING "U
# Discord URL # Discord URL
set(Launcher_DISCORD_URL "https://discord.gg/Z52pwxWCHP" CACHE STRING "URL for the Discord guild.") set(Launcher_DISCORD_URL "https://discord.gg/Z52pwxWCHP" CACHE STRING "URL for the Discord guild.")
# Subreddit URL # Subreddit URL
set(Launcher_SUBREDDIT_URL "https://www.reddit.com/r/PolyMCLauncher/" CACHE STRING "URL for the subreddit.") set(Launcher_SUBREDDIT_URL "https://www.reddit.com/r/PolyMCLauncher/" CACHE STRING "URL for the subreddit.")
@ -116,6 +117,22 @@ set(Launcher_SUBREDDIT_URL "https://www.reddit.com/r/PolyMCLauncher/" CACHE STRI
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules") set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
set(Launcher_QT_VERSION_MAJOR "5" CACHE STRING "Major Qt version to build against") set(Launcher_QT_VERSION_MAJOR "5" CACHE STRING "Major Qt version to build against")
# API Keys
# NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service
# of these platforms, please change these API keys beforehand.
# Be aware that if you were to use these API keys for malicious purposes they might get revoked, which might cause
# breakage to thousands of users.
# If you don't plan to use these features of this software, you can just remove these values.
# By using this key in your builds you accept the terms of use laid down in
# https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use
set(Launcher_MSA_CLIENT_ID "549033b2-1532-4d4e-ae77-1bbaa46f9d74" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
# By using this key in your builds you accept the terms and conditions laid down in
# https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions
# NOTE: CurseForge requires you to change this if you make any kind of derivative work.
set(Launcher_CURSEFORGE_API_KEY "$2a$10$1Oqr2MX3O4n/ilhFGc597u8tfI3L2Hyr9/rtWDAMRjghSQV2QUuxq" CACHE STRING "CurseForge API Key")
#### Check the current Git commit and branch #### Check the current Git commit and branch
include(GetGitRevisionDescription) include(GetGitRevisionDescription)
@ -125,6 +142,8 @@ message(STATUS "Git commit: ${Launcher_GIT_COMMIT}")
message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}") message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}")
set(Launcher_RELEASE_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}") set(Launcher_RELEASE_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
set(Launcher_RELEASE_VERSION_NAME4 "${Launcher_RELEASE_VERSION_NAME}.0")
set(Launcher_RELEASE_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},${Launcher_VERSION_HOTFIX},0")
string(TIMESTAMP TODAY "%Y-%m-%d") string(TIMESTAMP TODAY "%Y-%m-%d")
set(Launcher_RELEASE_TIMESTAMP "${TODAY}") set(Launcher_RELEASE_TIMESTAMP "${TODAY}")
@ -135,30 +154,45 @@ add_custom_target(tcversion echo "\\#\\#teamcity[setParameter name=\\'env.LAUNCH
################################ 3rd Party Libs ################################ ################################ 3rd Party Libs ################################
# Find the required Qt parts # Find the required Qt parts
include(QtVersionlessBackport)
if(Launcher_QT_VERSION_MAJOR EQUAL 5) if(Launcher_QT_VERSION_MAJOR EQUAL 5)
set(QT_VERSION_MAJOR 5) set(QT_VERSION_MAJOR 5)
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml) find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml)
if(NOT Launcher_FORCE_BUNDLED_LIBS) if(NOT Launcher_FORCE_BUNDLED_LIBS)
find_package(QuaZip-Qt5 1.3) find_package(QuaZip-Qt5 1.3 QUIET)
endif() endif()
if (NOT QuaZip-Qt5_FOUND) if (NOT QuaZip-Qt5_FOUND)
set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE) set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE)
set(FORCE_BUNDLED_QUAZIP 1) set(FORCE_BUNDLED_QUAZIP 1)
endif() endif()
# Qt 6 sets these by default. Notably causes Windows APIs to use UNICODE strings.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNICODE -D_UNICODE")
elseif(Launcher_QT_VERSION_MAJOR EQUAL 6)
set(QT_VERSION_MAJOR 6)
find_package(Qt6 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml Core5Compat)
list(APPEND Launcher_QT_LIBS Qt6::Core5Compat)
if(NOT Launcher_FORCE_BUNDLED_LIBS)
find_package(QuaZip-Qt6 1.3 QUIET)
endif()
if (NOT QuaZip-Qt6_FOUND)
set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE)
set(FORCE_BUNDLED_QUAZIP 1)
endif()
else() else()
message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported") message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported")
endif() endif()
# The Qt5 cmake files don't provide its install paths, so ask qmake. include(ECMQueryQt)
include(QMakeQuery) ecm_query_qt(QT_PLUGINS_DIR QT_INSTALL_PLUGINS)
query_qmake(QT_INSTALL_PLUGINS QT_PLUGINS_DIR) ecm_query_qt(QT_LIBS_DIR QT_INSTALL_LIBS)
query_qmake(QT_INSTALL_IMPORTS QT_IMPORTS_DIR) ecm_query_qt(QT_LIBEXECS_DIR QT_INSTALL_LIBEXECS)
query_qmake(QT_INSTALL_LIBS QT_LIBS_DIR) ecm_query_qt(QT_DATA_DIR QT_HOST_DATA)
query_qmake(QT_INSTALL_LIBEXECS QT_LIBEXECS_DIR)
query_qmake(QT_HOST_DATA QT_DATA_DIR)
set(QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs) set(QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs)
# NOTE: Qt 6 already sets this by default
if (Qt5_POSITION_INDEPENDENT_CODE) if (Qt5_POSITION_INDEPENDENT_CODE)
SET(CMAKE_POSITION_INDEPENDENT_CODE ON) SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif() endif()
@ -226,9 +260,6 @@ elseif(UNIX)
# Set RPATH # Set RPATH
SET(Launcher_BINARY_RPATH "$ORIGIN/") SET(Launcher_BINARY_RPATH "$ORIGIN/")
# jars path is determined on runtime, relative to "Application root path", generally /usr or the root of the portable bundle
set(Launcher_APP_BINARY_DEFS "-DLAUNCHER_JARS_LOCATION=${JARS_DEST_DIR}")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${LAUNCHER_DESKTOP_DEST_DIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${LAUNCHER_DESKTOP_DEST_DIR})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${LAUNCHER_METAINFO_DEST_DIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${LAUNCHER_METAINFO_DEST_DIR})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION ${LAUNCHER_ICON_DEST_DIR}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION ${LAUNCHER_ICON_DEST_DIR})
@ -281,12 +312,12 @@ else()
message(STATUS "Using system QuaZip") message(STATUS "Using system QuaZip")
endif() endif()
add_subdirectory(libraries/rainbow) # Qt extension for colors add_subdirectory(libraries/rainbow) # Qt extension for colors
add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
add_subdirectory(libraries/classparser) # class parser library add_subdirectory(libraries/classparser) # class parser library
add_subdirectory(libraries/optional-bare) add_subdirectory(libraries/optional-bare)
add_subdirectory(libraries/tomlc99) # toml parser add_subdirectory(libraries/tomlc99) # toml parser
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
add_subdirectory(libraries/gamemode)
############################### Built Artifacts ############################### ############################### Built Artifacts ###############################

62
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,62 @@
# Contributions Guidelines
## Code formatting
Try to follow the existing formatting.
If there is no existing formatting, you may use `clang-format` with our included `.clang-format` configuration.
In general, in order of importance:
- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
- Prefer readability over dogma.
- Keep to the existing formatting.
- Indent with 4 space unless it's in a submodule.
- Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
## Signing your work
In an effort to ensure that the code you contribute is actually compatible with the licenses in this codebase, we require you to sign-off all your contributions.
This can be done by appending `-s` to your `git commit` call, or by manually appending the following text to your commit message:
```
<commit message>
Signed-off-by: Author name <Author email>
```
By signing off your work, you agree to the terms below:
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
These terms will be enforced once you create a pull request, and you will be informed automatically if any of your commits aren't signed-off by you.
As a bonus, you can also [cryptographically sign your commits][gh-signing-commits] and enable [vigilant mode][gh-vigilant-mode] on GitHub.
[gh-signing-commits]: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits
[gh-vigilant-mode]: https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits

View File

@ -1,6 +1,6 @@
# PolyMC # PolyMC
Copyright (C) 2012-2021 MultiMC Contributors PolyMC - Minecraft Launcher
Copyright (C) 2021-2022 PolyMC Contributors Copyright (C) 2021-2022 PolyMC Contributors
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
@ -15,8 +15,11 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
# Launcher (https://github.com/MultiMC/Launcher) This file incorporates work covered by the following copyright and
Copyright 2012-2021 MultiMC Contributors permission notice:
Copyright 2013-2021 MultiMC Contributors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
@ -213,6 +216,57 @@
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# launcher (`libraries/launcher`)
PolyMC - Minecraft Launcher
Copyright (C) 2021-2022 PolyMC Contributors
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.
Linking this library statically or dynamically with other modules is
making a combined work based on this library. Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.
As a special exception, the copyright holders of this library give
you permission to link this library with independent modules to
produce an executable, regardless of the license terms of these
independent modules, and to copy and distribute the resulting
executable under terms of your choice, provided that you also meet,
for each linked independent module, the terms and conditions of the
license of that module. An independent module is a module which is
not derived from or based on this library. If you modify this
library, you may extend this exception to your version of the
library, but you are not obliged to do so. If you do not wish to do
so, delete this exception statement from your version.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
This file incorporates work covered by the following copyright and
permission notice:
Copyright 2013-2021 MultiMC Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
# lionshead # lionshead
Code has been taken from https://github.com/natefoo/lionshead and loosely Code has been taken from https://github.com/natefoo/lionshead and loosely

View File

@ -1,8 +1,7 @@
<p align="center"> <p align="center">
<img src="./program_info/polymc-header-black.svg#gh-light-mode-only" alt="PolyMC logo"/> <img src="./program_info/polymc-header-black.svg#gh-light-mode-only" alt="PolyMC logo" width="50%"/>
<img src="./program_info/polymc-header.svg#gh-dark-mode-only" alt="PolyMC logo"/> <img src="./program_info/polymc-header.svg#gh-dark-mode-only" alt="PolyMC logo" width="50%"/>
</p> </p>
<br>
PolyMC is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity. PolyMC is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.
@ -47,7 +46,7 @@ If there are any issues with the space or you are using a client that does not s
[![Support](https://img.shields.io/matrix/polymc-support:matrix.org?label=PolyMC%20Support)](https://matrix.to/#/#polymc-support:matrix.org) [![Support](https://img.shields.io/matrix/polymc-support:matrix.org?label=PolyMC%20Support)](https://matrix.to/#/#polymc-support:matrix.org)
[![Voice](https://img.shields.io/matrix/polymc-voice:matrix.org?label=PolyMC%20Voice)](https://matrix.to/#/#polymc-voice:matrix.org) [![Voice](https://img.shields.io/matrix/polymc-voice:matrix.org?label=PolyMC%20Voice)](https://matrix.to/#/#polymc-voice:matrix.org)
we also have a subreddit you can post your issues and suggestions on: We also have a subreddit you can post your issues and suggestions on:
[r/PolyMCLauncher](https://www.reddit.com/r/PolyMCLauncher/) [r/PolyMCLauncher](https://www.reddit.com/r/PolyMCLauncher/)
@ -59,31 +58,41 @@ If you want to contribute to PolyMC you might find it useful to join our Discord
If you want to build PolyMC yourself, check [Build Instructions](https://polymc.org/wiki/development/build-instructions/) for build instructions. If you want to build PolyMC yourself, check [Build Instructions](https://polymc.org/wiki/development/build-instructions/) for build instructions.
## Code formatting
Just follow the existing formatting.
In general, in order of importance:
- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
- Prefer readability over dogma.
- Keep to the existing formatting.
- Indent with 4 space unless it's in a submodule.
- Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
## Translations ## Translations
The translation effort for PolyMC is hosted on [Weblate](https://hosted.weblate.org/projects/polymc/polymc/) and information about translating PolyMC is available at https://github.com/PolyMC/Translations The translation effort for PolyMC is hosted on [Weblate](https://hosted.weblate.org/projects/polymc/polymc/) and information about translating PolyMC is available at https://github.com/PolyMC/Translations
## Download information ## Download information
To modify download information or change packaging information send a pull request or issue to the website [Here](https://github.com/PolyMC/polymc.github.io/blob/master/src/download.md) To modify download information or change packaging information send a pull request or issue to the website [Here](https://github.com/PolyMC/polymc.github.io/blob/master/src/download.md)
## Forking/Redistributing/Custom builds policy ## Forking/Redistributing/Custom builds policy
Do whatever you want, we don't care. Just follow the license. If you have any questions about this feel free to ask in an issue. We don't care what you do with your fork/custom build as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy:
- Make it clear that your fork is not PolyMC and is not endorsed by or affiliated with the PolyMC project (https://polymc.org).
- Go through [CMakeLists.txt](CMakeLists.txt) and change PolyMC's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled).
If you have any questions or want any clarification on the above conditions please make an issue and ask us.
Be aware that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions:
- [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
- [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions)
If you do not agree with these terms and conditions, then remove the associated API keys from the [CMakeLists.txt](CMakeLists.txt) file by setting them to an empty string (`""`).
All launcher code is available under the GPL-3.0-only license. All launcher code is available under the GPL-3.0-only license.
[Source for the website](https://github.com/PolyMC/polymc.github.io) is hosted under the AGPL-3.0-or-later License.
The logo and related assets are under the CC BY-SA 4.0 license. The logo and related assets are under the CC BY-SA 4.0 license.
## Sponsors
Thank you to all our generous backers over at Open Collective! Support PolyMC by [becoming a backer](https://opencollective.com/polymc).
[![OpenCollective Backers](https://opencollective.com/polymc/backers.svg?width=890&limit=1000)](https://opencollective.com/polymc#backers)
Also, thanks to JetBrains for providing us a few licenses for all their products, as part of their [Open Source program](https://www.jetbrains.com/opensource/).
[![JetBrains](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/opensource/)
Additionally, thanks to the awesome people over at [MacStadium](https://www.macstadium.com/), for providing M1-Macs for development purposes!
<a href="https://www.macstadium.com"><img src="https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png" alt="Powered by MacStadium" width="300"></a>

View File

@ -7,5 +7,5 @@ add_library(BuildConfig STATIC
${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp ${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp
) )
target_link_libraries(BuildConfig Qt5::Core) target_link_libraries(BuildConfig Qt${QT_VERSION_MAJOR}::Core)
target_include_directories(BuildConfig PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") target_include_directories(BuildConfig PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")

100
cmake/ECMQueryQt.cmake Normal file
View File

@ -0,0 +1,100 @@
# SPDX-FileCopyrightText: 2014 Rohan Garg <rohan16garg@gmail.com>
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
# SPDX-FileCopyrightText: 2014-2016 Aleix Pol <aleixpol@kde.org>
# SPDX-FileCopyrightText: 2017 Friedrich W. H. Kossebau <kossebau@kde.org>
# SPDX-FileCopyrightText: 2022 Ahmad Samir <a.samir78@gmail.com>
#
# SPDX-License-Identifier: BSD-3-Clause
#[=======================================================================[.rst:
ECMQueryQt
---------------
This module can be used to query the installation paths used by Qt.
For Qt5 this uses ``qmake``, and for Qt6 this used ``qtpaths`` (the latter has built-in
support to query the paths of a target platform when cross-compiling).
This module defines the following function:
::
ecm_query_qt(<result_variable> <qt_variable> [TRY])
Passing ``TRY`` will result in the method not making the build fail if the executable
used for querying has not been found, but instead simply print a warning message and
return an empty string.
Example usage:
.. code-block:: cmake
include(ECMQueryQt)
ecm_query_qt(bin_dir QT_INSTALL_BINS)
If the call succeeds ``${bin_dir}`` will be set to ``<prefix>/path/to/bin/dir`` (e.g.
``/usr/lib64/qt/bin/``).
Since: 5.93
#]=======================================================================]
include(${CMAKE_CURRENT_LIST_DIR}/QtVersionOption.cmake)
include(CheckLanguage)
check_language(CXX)
if (CMAKE_CXX_COMPILER)
# Enable the CXX language to let CMake look for config files in library dirs.
# See: https://gitlab.kitware.com/cmake/cmake/-/issues/23266
enable_language(CXX)
endif()
if (QT_MAJOR_VERSION STREQUAL "5")
# QUIET to accommodate the TRY option
find_package(Qt${QT_MAJOR_VERSION}Core QUIET)
if(TARGET Qt5::qmake)
get_target_property(_qmake_executable_default Qt5::qmake LOCATION)
set(QUERY_EXECUTABLE ${_qmake_executable_default}
CACHE FILEPATH "Location of the Qt5 qmake executable")
set(_exec_name_text "Qt5 qmake")
set(_cli_option "-query")
endif()
elseif(QT_MAJOR_VERSION STREQUAL "6")
# QUIET to accommodate the TRY option
find_package(Qt6 COMPONENTS CoreTools QUIET CONFIG)
if (TARGET Qt6::qtpaths)
get_target_property(_qtpaths_executable Qt6::qtpaths LOCATION)
set(QUERY_EXECUTABLE ${_qtpaths_executable}
CACHE FILEPATH "Location of the Qt6 qtpaths executable")
set(_exec_name_text "Qt6 qtpaths")
set(_cli_option "--query")
endif()
endif()
function(ecm_query_qt result_variable qt_variable)
set(options TRY)
set(oneValueArgs)
set(multiValueArgs)
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT QUERY_EXECUTABLE)
if(ARGS_TRY)
set(${result_variable} "" PARENT_SCOPE)
message(STATUS "No ${_exec_name_text} executable found. Can't check ${qt_variable}")
return()
else()
message(FATAL_ERROR "No ${_exec_name_text} executable found. Can't check ${qt_variable} as required")
endif()
endif()
execute_process(
COMMAND ${QUERY_EXECUTABLE} ${_cli_option} "${qt_variable}"
RESULT_VARIABLE return_code
OUTPUT_VARIABLE output
)
if(return_code EQUAL 0)
string(STRIP "${output}" output)
file(TO_CMAKE_PATH "${output}" output_path)
set(${result_variable} "${output_path}" PARENT_SCOPE)
else()
message(WARNING "Failed call: ${_command} \"${qt_variable}\"")
message(FATAL_ERROR "${_exec_name_text} call failed: ${return_code}")
endif()
endfunction()

View File

@ -1,14 +0,0 @@
if(__QMAKEQUERY_CMAKE__)
return()
endif()
set(__QMAKEQUERY_CMAKE__ TRUE)
get_target_property(QMAKE_EXECUTABLE Qt5::qmake LOCATION)
function(QUERY_QMAKE VAR RESULT)
exec_program(${QMAKE_EXECUTABLE} ARGS "-query ${VAR}" RETURN_VALUE return_code OUTPUT_VARIABLE output )
if(NOT return_code)
file(TO_CMAKE_PATH "${output}" output)
set(${RESULT} ${output} PARENT_SCOPE)
endif(NOT return_code)
endfunction(QUERY_QMAKE)

View File

@ -0,0 +1,38 @@
#.rst:
# QtVersionOption
# ---------------
#
# Adds a build option to select the major Qt version if necessary,
# that is, if the major Qt version has not yet been determined otherwise
# (e.g. by a corresponding find_package() call).
#
# This module is typically included by other modules requiring knowledge
# about the major Qt version.
#
# ``QT_MAJOR_VERSION`` is defined to either be "5" or "6".
#
#
# Since 5.82.0.
#=============================================================================
# SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
#
# SPDX-License-Identifier: BSD-3-Clause
if (DEFINED QT_MAJOR_VERSION)
return()
endif()
if (TARGET Qt5::Core)
set(QT_MAJOR_VERSION 5)
elseif (TARGET Qt6::Core)
set(QT_MAJOR_VERSION 6)
else()
option(BUILD_WITH_QT6 "Build against Qt 6" OFF)
if (BUILD_WITH_QT6)
set(QT_MAJOR_VERSION 6)
else()
set(QT_MAJOR_VERSION 5)
endif()
endif()

View File

@ -0,0 +1,97 @@
#=============================================================================
# Copyright 2005-2011 Kitware, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# * Neither the name of Kitware, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#=============================================================================
# From Qt5CoreMacros.cmake
function(qt_generate_moc)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_generate_moc(${ARGV})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_generate_moc(${ARGV})
endif()
endfunction()
function(qt_wrap_cpp outfiles)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_wrap_cpp("${outfiles}" ${ARGN})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_wrap_cpp("${outfiles}" ${ARGN})
endif()
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
endfunction()
function(qt_add_binary_resources)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_add_binary_resources(${ARGV})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_add_binary_resources(${ARGV})
endif()
endfunction()
function(qt_add_resources outfiles)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_add_resources("${outfiles}" ${ARGN})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_add_resources("${outfiles}" ${ARGN})
endif()
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
endfunction()
function(qt_add_big_resources outfiles)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_add_big_resources(${outfiles} ${ARGN})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_add_big_resources(${outfiles} ${ARGN})
endif()
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
endfunction()
function(qt_import_plugins)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_import_plugins(${ARGV})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_import_plugins(${ARGV})
endif()
endfunction()
# From Qt5WidgetsMacros.cmake
function(qt_wrap_ui outfiles)
if(QT_VERSION_MAJOR EQUAL 5)
qt5_wrap_ui("${outfiles}" ${ARGN})
elseif(QT_VERSION_MAJOR EQUAL 6)
qt6_wrap_ui("${outfiles}" ${ARGN})
endif()
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
endfunction()

View File

@ -1,50 +0,0 @@
find_package(Qt5Test REQUIRED)
set(TEST_RESOURCE_PATH ${CMAKE_CURRENT_LIST_DIR})
message(${TEST_RESOURCE_PATH})
function(add_unit_test name)
if(BUILD_TESTING)
set(options "")
set(oneValueArgs DATA)
set(multiValueArgs SOURCES LIBS)
cmake_parse_arguments(OPT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
if(WIN32)
add_executable(${name}_test ${OPT_SOURCES} ${TEST_RESOURCE_PATH}/UnitTest/test.rc)
else()
add_executable(${name}_test ${OPT_SOURCES})
endif()
if(NOT "${OPT_DATA}" STREQUAL "")
set(TEST_DATA_PATH "${CMAKE_CURRENT_BINARY_DIR}/data")
set(TEST_DATA_PATH_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${OPT_DATA}")
message("From ${TEST_DATA_PATH_SRC} to ${TEST_DATA_PATH}")
string(REGEX REPLACE "[/\\:]" "_" DATA_TARGET_NAME "${TEST_DATA_PATH_SRC}")
if(UNIX)
# on unix we get the third / from the filename
set(TEST_DATA_URL "file://${TEST_DATA_PATH}")
else()
# we don't on windows, so we have to add it ourselves
set(TEST_DATA_URL "file:///${TEST_DATA_PATH}")
endif()
if(NOT TARGET "${DATA_TARGET_NAME}")
add_custom_target(${DATA_TARGET_NAME})
add_dependencies(${name}_test ${DATA_TARGET_NAME})
add_custom_command(
TARGET ${DATA_TARGET_NAME}
COMMAND ${CMAKE_COMMAND} "-DTEST_DATA_URL=${TEST_DATA_URL}" -DSOURCE=${TEST_DATA_PATH_SRC} -DDESTINATION=${TEST_DATA_PATH} -P ${TEST_RESOURCE_PATH}/UnitTest/generate_test_data.cmake
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endif()
endif()
target_link_libraries(${name}_test Qt5::Test ${OPT_LIBS})
target_include_directories(${name}_test PRIVATE "${TEST_RESOURCE_PATH}/UnitTest/")
add_test(NAME ${name} COMMAND ${name}_test WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
endif()
endfunction()

View File

@ -1,28 +0,0 @@
#pragma once
#include <QFile>
#include <QCoreApplication>
#include <QTest>
#include <QDir>
#define expandstr(s) expandstr2(s)
#define expandstr2(s) #s
class TestsInternal
{
public:
static QByteArray readFile(const QString &fileName)
{
QFile f(fileName);
f.open(QFile::ReadOnly);
return f.readAll();
}
static QString readFileUtf8(const QString &fileName)
{
return QString::fromUtf8(readFile(fileName));
}
};
#define GET_TEST_FILE(file) TestsInternal::readFile(QFINDTESTDATA(file))
#define GET_TEST_FILE_UTF8(file) TestsInternal::readFileUtf8(QFINDTESTDATA(file))

View File

@ -1,23 +0,0 @@
# Copy files from source directory to destination directory, substituting any
# variables. Create destination directory if it does not exist.
function(configure_files srcDir destDir)
make_directory(${destDir})
file(GLOB templateFiles RELATIVE ${srcDir} ${srcDir}/*)
foreach(templateFile ${templateFiles})
set(srcTemplatePath ${srcDir}/${templateFile})
if(NOT IS_DIRECTORY ${srcTemplatePath})
configure_file(
${srcTemplatePath}
${destDir}/${templateFile}
@ONLY
NEWLINE_STYLE LF
)
else()
configure_files("${srcTemplatePath}" "${destDir}/${templateFile}")
endif()
endforeach()
endfunction()
configure_files(${SOURCE} ${DESTINATION})

View File

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity name="Launcher.Test.0" type="win32" version="5.0.0.0" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"/>
</dependentAssembly>
</dependency>
<description>Custom Minecraft launcher for managing multiple installs.</description>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!--The ID below indicates app support for Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!--The ID below indicates app support for Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!--The ID below indicates app support for Windows Developer Preview / Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
</application>
</compatibility>
</assembly>

View File

@ -1,28 +0,0 @@
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
1 RT_MANIFEST "test.manifest"
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "000004b0"
BEGIN
VALUE "CompanyName", "MultiMC & PolyMC Contributors"
VALUE "FileDescription", "Testcase"
VALUE "FileVersion", "1.0.0.0"
VALUE "ProductName", "Launcher Testcase"
VALUE "ProductVersion", "5"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0000, 0x04b0 // Unicode
END
END

View File

@ -1 +1 @@
(import packages/nix/flake-compat.nix).defaultNix (import nix/flake-compat.nix).defaultNix

View File

@ -3,11 +3,11 @@
"flake-compat": { "flake-compat": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1648199409, "lastModified": 1650374568,
"narHash": "sha256-JwPKdC2PoVBkG6E+eWw3j6BMR6sL3COpYWfif7RVb8Y=", "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=",
"owner": "edolstra", "owner": "edolstra",
"repo": "flake-compat", "repo": "flake-compat",
"rev": "64a525ee38886ab9028e6f61790de0832aa3ef03", "rev": "b4a34015c698c7793d592d66adbab377907a2be8",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -34,11 +34,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1648219316, "lastModified": 1654665288,
"narHash": "sha256-Ctij+dOi0ZZIfX5eMhgwugfvB+WZSrvVNAyAuANOsnQ=", "narHash": "sha256-7blJpfoZEu7GKb84uh3io/5eSJNdaagXD9d15P9iQMs=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "30d3d79b7d3607d56546dd2a6b49e156ba0ec634", "rev": "43ecbe7840d155fa933ee8a500fb00dbbc651fc8",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -48,28 +48,11 @@
"type": "github" "type": "github"
} }
}, },
"quazip": {
"flake": false,
"locked": {
"lastModified": 1643049383,
"narHash": "sha256-LcJY6yd6GyeL7X5MP4L94diceM1TYespWByliBsjK98=",
"owner": "stachenov",
"repo": "quazip",
"rev": "09ec1d10c6d627f895109b21728dda000cbfa7d1",
"type": "github"
},
"original": {
"owner": "stachenov",
"repo": "quazip",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
"libnbtplusplus": "libnbtplusplus", "libnbtplusplus": "libnbtplusplus",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs"
"quazip": "quazip"
} }
} }
}, },

View File

@ -5,30 +5,33 @@
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
libnbtplusplus = { url = "github:multimc/libnbtplusplus"; flake = false; }; libnbtplusplus = { url = "github:multimc/libnbtplusplus"; flake = false; };
quazip = { url = "github:stachenov/quazip"; flake = false; };
}; };
outputs = { self, nixpkgs, libnbtplusplus, quazip, ... }: outputs = { self, nixpkgs, libnbtplusplus, ... }:
let let
# Generate a user-friendly version number. # User-friendly version number.
version = builtins.substring 0 8 self.lastModifiedDate; version = builtins.substring 0 8 self.lastModifiedDate;
# System types to support (qtbase is currently broken for "aarch64-darwin") # Supported systems (qtbase is currently broken for "aarch64-darwin")
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" ]; supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" ];
# Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'. # Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
forAllSystems = nixpkgs.lib.genAttrs supportedSystems; forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
# Nixpkgs instantiated for supported system types. # Nixpkgs instantiated for supported systems.
pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system}); pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
packagesFn = pkgs: rec {
polymc = pkgs.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus; };
polymc-qt6 = pkgs.qt6Packages.callPackage ./nix { inherit version self libnbtplusplus; };
};
in in
{ {
packages = forAllSystems (system: { polymc = pkgs.${system}.libsForQt5.callPackage ./packages/nix/polymc { inherit version self quazip libnbtplusplus; }; }); packages = forAllSystems (system:
defaultPackage = forAllSystems (system: self.packages.${system}.polymc); let packages = packagesFn pkgs.${system}; in
packages // { default = packages.polymc; }
);
apps = forAllSystems (system: { polymc = { type = "app"; program = "${self.defaultPackage.${system}}/bin/polymc"; }; }); overlay = final: packagesFn;
defaultApp = forAllSystems (system: self.apps.${system}.polymc);
overlay = final: prev: { polymc = self.defaultPackage.${final.system}; };
}; };
} }

View File

@ -2,6 +2,7 @@
/* /*
* PolyMC - Minecraft Launcher * PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 Lenny McLennington <lenny@sneed.church>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -36,6 +37,7 @@
#include "Application.h" #include "Application.h"
#include "BuildConfig.h" #include "BuildConfig.h"
#include "net/PasteUpload.h"
#include "ui/MainWindow.h" #include "ui/MainWindow.h"
#include "ui/InstanceWindow.h" #include "ui/InstanceWindow.h"
@ -61,6 +63,7 @@
#include "ui/setupwizard/SetupWizard.h" #include "ui/setupwizard/SetupWizard.h"
#include "ui/setupwizard/LanguageWizardPage.h" #include "ui/setupwizard/LanguageWizardPage.h"
#include "ui/setupwizard/JavaWizardPage.h" #include "ui/setupwizard/JavaWizardPage.h"
#include "ui/setupwizard/PasteWizardPage.h"
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
@ -81,6 +84,7 @@
#include <QDebug> #include <QDebug>
#include <QStyleFactory> #include <QStyleFactory>
#include <QWindow> #include <QWindow>
#include <QIcon>
#include "InstanceList.h" #include "InstanceList.h"
@ -96,7 +100,6 @@
#include "tools/JVisualVM.h" #include "tools/JVisualVM.h"
#include "tools/MCEditTool.h" #include "tools/MCEditTool.h"
#include <xdgicon.h>
#include "settings/INISettingsObject.h" #include "settings/INISettingsObject.h"
#include "settings/Setting.h" #include "settings/Setting.h"
@ -221,7 +224,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
setOrganizationName(BuildConfig.LAUNCHER_NAME); setOrganizationName(BuildConfig.LAUNCHER_NAME);
setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN); setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN);
setApplicationName(BuildConfig.LAUNCHER_NAME); setApplicationName(BuildConfig.LAUNCHER_NAME);
setApplicationDisplayName(BuildConfig.LAUNCHER_DISPLAYNAME); setApplicationDisplayName(QString("%1 %2").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString()));
setApplicationVersion(BuildConfig.printableVersionString()); setApplicationVersion(BuildConfig.printableVersionString());
setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME); setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME);
startTime = QDateTime::currentDateTime(); startTime = QDateTime::currentDateTime();
@ -329,10 +332,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
// on macOS, touch the root to force Finder to reload the .app metadata (and fix any icon change issues) // on macOS, touch the root to force Finder to reload the .app metadata (and fix any icon change issues)
FS::updateTimestamp(m_rootPath); FS::updateTimestamp(m_rootPath);
#endif #endif
#ifdef LAUNCHER_JARS_LOCATION
m_jarsPath = TOSTRING(LAUNCHER_JARS_LOCATION);
#endif
} }
QString adjustedBy; QString adjustedBy;
@ -622,6 +621,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->registerSetting("JavaPath", ""); m_settings->registerSetting("JavaPath", "");
m_settings->registerSetting("JavaTimestamp", 0); m_settings->registerSetting("JavaTimestamp", 0);
m_settings->registerSetting("JavaArchitecture", ""); m_settings->registerSetting("JavaArchitecture", "");
m_settings->registerSetting("JavaRealArchitecture", "");
m_settings->registerSetting("JavaVersion", ""); m_settings->registerSetting("JavaVersion", "");
m_settings->registerSetting("JavaVendor", ""); m_settings->registerSetting("JavaVendor", "");
m_settings->registerSetting("LastHostname", ""); m_settings->registerSetting("LastHostname", "");
@ -633,6 +633,11 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->registerSetting("UseNativeOpenAL", false); m_settings->registerSetting("UseNativeOpenAL", false);
m_settings->registerSetting("UseNativeGLFW", false); m_settings->registerSetting("UseNativeGLFW", false);
// Peformance related options
m_settings->registerSetting("EnableFeralGamemode", false);
m_settings->registerSetting("EnableMangoHud", false);
m_settings->registerSetting("UseDiscreteGpu", false);
// Game time // Game time
m_settings->registerSetting("ShowGameTime", true); m_settings->registerSetting("ShowGameTime", true);
m_settings->registerSetting("ShowGlobalGameTime", true); m_settings->registerSetting("ShowGlobalGameTime", true);
@ -641,6 +646,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
// Minecraft launch method // Minecraft launch method
m_settings->registerSetting("MCLaunchMethod", "LauncherPart"); m_settings->registerSetting("MCLaunchMethod", "LauncherPart");
// Minecraft mods
m_settings->registerSetting("ModMetadataDisabled", false);
// Minecraft offline player name // Minecraft offline player name
m_settings->registerSetting("LastOfflinePlayerName", ""); m_settings->registerSetting("LastOfflinePlayerName", "");
@ -672,14 +680,41 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->registerSetting("UpdateDialogGeometry", ""); m_settings->registerSetting("UpdateDialogGeometry", "");
// pastebin URL // HACK: This code feels so stupid is there a less stupid way of doing this?
m_settings->registerSetting("PastebinURL", "https://0x0.st"); {
m_settings->registerSetting("PastebinURL", "");
m_settings->registerSetting("PastebinType", PasteUpload::PasteType::Mclogs);
m_settings->registerSetting("PastebinCustomAPIBase", "");
QString pastebinURL = m_settings->get("PastebinURL").toString();
bool userHadDefaultPastebin = pastebinURL == "https://0x0.st";
if (!pastebinURL.isEmpty() && !userHadDefaultPastebin)
{
m_settings->set("PastebinType", PasteUpload::PasteType::NullPointer);
m_settings->set("PastebinCustomAPIBase", pastebinURL);
m_settings->reset("PastebinURL");
}
bool ok;
int pasteType = m_settings->get("PastebinType").toInt(&ok);
// If PastebinType is invalid then reset the related settings.
if (!ok || !(PasteUpload::PasteType::First <= pasteType && pasteType <= PasteUpload::PasteType::Last))
{
m_settings->reset("PastebinType");
m_settings->reset("PastebinCustomAPIBase");
}
}
// meta URL
m_settings->registerSetting("MetaURLOverride", "");
m_settings->registerSetting("CloseAfterLaunch", false); m_settings->registerSetting("CloseAfterLaunch", false);
m_settings->registerSetting("QuitAfterGameStop", false); m_settings->registerSetting("QuitAfterGameStop", false);
// Custom MSA credentials // Custom MSA credentials
m_settings->registerSetting("MSAClientIDOverride", ""); m_settings->registerSetting("MSAClientIDOverride", "");
m_settings->registerSetting("CFKeyOverride", "");
m_settings->registerSetting("UserAgentOverride", "");
// Init page provider // Init page provider
{ {
@ -843,6 +878,12 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_mcedit.reset(new MCEditTool(m_settings)); m_mcedit.reset(new MCEditTool(m_settings));
} }
#ifdef Q_OS_MACOS
connect(this, &Application::clickedOnDock, [this]() {
this->showMainWindow();
});
#endif
connect(this, &Application::aboutToQuit, [this](){ connect(this, &Application::aboutToQuit, [this](){
if(m_instances) if(m_instances)
{ {
@ -899,7 +940,8 @@ bool Application::createSetupWizard()
return true; return true;
return false; return false;
}(); }();
bool wizardRequired = javaRequired || languageRequired; bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired;
if(wizardRequired) if(wizardRequired)
{ {
@ -913,6 +955,11 @@ bool Application::createSetupWizard()
{ {
m_setupWizard->addPage(new JavaWizardPage(m_setupWizard)); m_setupWizard->addPage(new JavaWizardPage(m_setupWizard));
} }
if (pasteInterventionRequired)
{
m_setupWizard->addPage(new PasteWizardPage(m_setupWizard));
}
connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished); connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished);
m_setupWizard->show(); m_setupWizard->show();
return true; return true;
@ -920,6 +967,21 @@ bool Application::createSetupWizard()
return false; return false;
} }
bool Application::event(QEvent* event) {
#ifdef Q_OS_MACOS
if (event->type() == QEvent::ApplicationStateChange) {
auto ev = static_cast<QApplicationStateChangeEvent*>(event);
if (m_prevAppState == Qt::ApplicationActive
&& ev->applicationState() == Qt::ApplicationActive) {
emit clickedOnDock();
}
m_prevAppState = ev->applicationState();
}
#endif
return QApplication::event(event);
}
void Application::setupWizardFinished(int status) void Application::setupWizardFinished(int status)
{ {
qDebug() << "Wizard result =" << status; qDebug() << "Wizard result =" << status;
@ -1121,7 +1183,7 @@ void Application::setApplicationTheme(const QString& name, bool initial)
void Application::setIconTheme(const QString& name) void Application::setIconTheme(const QString& name)
{ {
XdgIcon::setThemeName(name); QIcon::setThemeName(name);
} }
QIcon Application::getThemedIcon(const QString& name) QIcon Application::getThemedIcon(const QString& name)
@ -1129,7 +1191,7 @@ QIcon Application::getThemedIcon(const QString& name)
if(name == "logo") { if(name == "logo") {
return QIcon(":/org.polymc.PolyMC.svg"); return QIcon(":/org.polymc.PolyMC.svg");
} }
return XdgIcon::fromTheme(name); return QIcon::fromTheme(name);
} }
bool Application::openJsonEditor(const QString &filename) bool Application::openJsonEditor(const QString &filename)
@ -1491,13 +1553,22 @@ shared_qobject_ptr<Meta::Index> Application::metadataIndex()
return m_metadataIndex; return m_metadataIndex;
} }
QString Application::getJarsPath() QString Application::getJarPath(QString jarFile)
{ {
if(m_jarsPath.isEmpty()) QStringList potentialPaths = {
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
FS::PathCombine(m_rootPath, "share/jars"),
#endif
FS::PathCombine(m_rootPath, "jars"),
FS::PathCombine(applicationDirPath(), "jars")
};
for(QString p : potentialPaths)
{ {
return FS::PathCombine(QCoreApplication::applicationDirPath(), "jars"); QString jarPath = FS::PathCombine(p, jarFile);
if (QFileInfo(jarPath).isFile())
return jarPath;
} }
return FS::PathCombine(m_rootPath, m_jarsPath); return {};
} }
QString Application::getMSAClientID() QString Application::getMSAClientID()
@ -1509,3 +1580,34 @@ QString Application::getMSAClientID()
return BuildConfig.MSA_CLIENT_ID; return BuildConfig.MSA_CLIENT_ID;
} }
QString Application::getCurseKey()
{
QString keyOverride = m_settings->get("CFKeyOverride").toString();
if (!keyOverride.isEmpty()) {
return keyOverride;
}
return BuildConfig.CURSEFORGE_API_KEY;
}
QString Application::getUserAgent()
{
QString uaOverride = m_settings->get("UserAgentOverride").toString();
if (!uaOverride.isEmpty()) {
return uaOverride.replace("$LAUNCHER_VER", BuildConfig.printableVersionString());
}
return BuildConfig.USER_AGENT;
}
QString Application::getUserAgentUncached()
{
QString uaOverride = m_settings->get("UserAgentOverride").toString();
if (!uaOverride.isEmpty()) {
uaOverride += " (Uncached)";
return uaOverride.replace("$LAUNCHER_VER", BuildConfig.printableVersionString());
}
return BuildConfig.USER_AGENT_UNCACHED;
}

View File

@ -94,6 +94,8 @@ public:
Application(int &argc, char **argv); Application(int &argc, char **argv);
virtual ~Application(); virtual ~Application();
bool event(QEvent* event) override;
std::shared_ptr<SettingsObject> settings() const { std::shared_ptr<SettingsObject> settings() const {
return m_settings; return m_settings;
} }
@ -152,9 +154,16 @@ public:
shared_qobject_ptr<Meta::Index> metadataIndex(); shared_qobject_ptr<Meta::Index> metadataIndex();
QString getJarsPath(); /*!
* Finds and returns the full path to a jar file.
* Returns a null-string if it could not be found.
*/
QString getJarPath(QString jarFile);
QString getMSAClientID(); QString getMSAClientID();
QString getCurseKey();
QString getUserAgent();
QString getUserAgentUncached();
/// this is the root of the 'installation'. Used for automatic updates /// this is the root of the 'installation'. Used for automatic updates
const QString &root() { const QString &root() {
@ -180,6 +189,10 @@ signals:
void globalSettingsAboutToOpen(); void globalSettingsAboutToOpen();
void globalSettingsClosed(); void globalSettingsClosed();
#ifdef Q_OS_MACOS
void clickedOnDock();
#endif
public slots: public slots:
bool launch( bool launch(
InstancePtr instance, InstancePtr instance,
@ -229,7 +242,6 @@ private:
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider; std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;
std::map<QString, std::unique_ptr<ITheme>> m_themes; std::map<QString, std::unique_ptr<ITheme>> m_themes;
std::unique_ptr<MCEditTool> m_mcedit; std::unique_ptr<MCEditTool> m_mcedit;
QString m_jarsPath;
QSet<QString> m_features; QSet<QString> m_features;
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers; QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
@ -237,6 +249,10 @@ private:
QString m_rootPath; QString m_rootPath;
Status m_status = Application::StartingUp; Status m_status = Application::StartingUp;
#ifdef Q_OS_MACOS
Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive;
#endif
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
// used on Windows to attach the standard IO streams // used on Windows to attach the standard IO streams
bool consoleAttached = false; bool consoleAttached = false;

View File

@ -1,11 +1,47 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ApplicationMessage.h" #include "ApplicationMessage.h"
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include "Json.h"
void ApplicationMessage::parse(const QByteArray & input) { void ApplicationMessage::parse(const QByteArray & input) {
auto doc = QJsonDocument::fromBinaryData(input); auto doc = Json::requireDocument(input, "ApplicationMessage");
auto root = doc.object(); auto root = Json::requireObject(doc, "ApplicationMessage");
command = root.value("command").toString(); command = root.value("command").toString();
args.clear(); args.clear();
@ -25,7 +61,5 @@ QByteArray ApplicationMessage::serialize() {
} }
root.insert("args", outArgs); root.insert("args", outArgs);
QJsonDocument out; return Json::toText(root);
out.setObject(root);
return out.toBinaryData();
} }

View File

@ -2,6 +2,7 @@
/* /*
* PolyMC - Minecraft Launcher * PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -38,6 +39,7 @@
#include <QFileInfo> #include <QFileInfo>
#include <QDir> #include <QDir>
#include <QDebug> #include <QDebug>
#include <QRegularExpression>
#include "settings/INISettingsObject.h" #include "settings/INISettingsObject.h"
#include "settings/Setting.h" #include "settings/Setting.h"
@ -59,7 +61,11 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
m_settings->registerSetting("lastLaunchTime", 0); m_settings->registerSetting("lastLaunchTime", 0);
m_settings->registerSetting("totalTimePlayed", 0); m_settings->registerSetting("totalTimePlayed", 0);
m_settings->registerSetting("lastTimePlayed", 0); m_settings->registerSetting("lastTimePlayed", 0);
m_settings->registerSetting("InstanceType", "OneSix");
// NOTE: Sometimees InstanceType is already registered, as it was used to identify the type of
// a locally stored instance
if (!m_settings->getSetting("InstanceType"))
m_settings->registerSetting("InstanceType", "");
// Custom Commands // Custom Commands
auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false); auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
@ -76,6 +82,14 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleMaxLines"), nullptr); m_settings->registerPassthrough(globalSettings->getSetting("ConsoleMaxLines"), nullptr);
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleOverflowStop"), nullptr); m_settings->registerPassthrough(globalSettings->getSetting("ConsoleOverflowStop"), nullptr);
// Managed Packs
m_settings->registerSetting("ManagedPack", false);
m_settings->registerSetting("ManagedPackType", "");
m_settings->registerSetting("ManagedPackID", "");
m_settings->registerSetting("ManagedPackName", "");
m_settings->registerSetting("ManagedPackVersionID", "");
m_settings->registerSetting("ManagedPackVersionName", "");
} }
QString BaseInstance::getPreLaunchCommand() QString BaseInstance::getPreLaunchCommand()
@ -93,6 +107,46 @@ QString BaseInstance::getPostExitCommand()
return settings()->get("PostExitCommand").toString(); return settings()->get("PostExitCommand").toString();
} }
bool BaseInstance::isManagedPack()
{
return settings()->get("ManagedPack").toBool();
}
QString BaseInstance::getManagedPackType()
{
return settings()->get("ManagedPackType").toString();
}
QString BaseInstance::getManagedPackID()
{
return settings()->get("ManagedPackID").toString();
}
QString BaseInstance::getManagedPackName()
{
return settings()->get("ManagedPackName").toString();
}
QString BaseInstance::getManagedPackVersionID()
{
return settings()->get("ManagedPackVersionID").toString();
}
QString BaseInstance::getManagedPackVersionName()
{
return settings()->get("ManagedPackVersionName").toString();
}
void BaseInstance::setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version)
{
settings()->set("ManagedPack", true);
settings()->set("ManagedPackType", type);
settings()->set("ManagedPackID", id);
settings()->set("ManagedPackName", name);
settings()->set("ManagedPackVersionID", versionId);
settings()->set("ManagedPackVersionName", version);
}
int BaseInstance::getConsoleMaxLines() const int BaseInstance::getConsoleMaxLines() const
{ {
auto lineSetting = settings()->getSetting("ConsoleMaxLines"); auto lineSetting = settings()->getSetting("ConsoleMaxLines");
@ -282,7 +336,7 @@ QString BaseInstance::name() const
QString BaseInstance::windowTitle() const QString BaseInstance::windowTitle() const
{ {
return BuildConfig.LAUNCHER_NAME + ": " + name().replace(QRegExp("[ \n\r\t]+"), " "); return BuildConfig.LAUNCHER_NAME + ": " + name().replace(QRegularExpression("\\s+"), " ");
} }
// FIXME: why is this here? move it to MinecraftInstance!!! // FIXME: why is this here? move it to MinecraftInstance!!!

View File

@ -2,6 +2,7 @@
/* /*
* PolyMC - Minecraft Launcher * PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -139,6 +140,14 @@ public:
QString getPostExitCommand(); QString getPostExitCommand();
QString getWrapperCommand(); QString getWrapperCommand();
bool isManagedPack();
QString getManagedPackType();
QString getManagedPackID();
QString getManagedPackName();
QString getManagedPackVersionID();
QString getManagedPackVersionName();
void setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version);
/// guess log level from a line of game log /// guess log level from a line of game log
virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level) virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level)
{ {
@ -179,6 +188,7 @@ public:
* Create envrironment variables for running the instance * Create envrironment variables for running the instance
*/ */
virtual QProcessEnvironment createEnvironment() = 0; virtual QProcessEnvironment createEnvironment() = 0;
virtual QProcessEnvironment createLaunchEnvironment() = 0;
/*! /*!
* Returns a matcher that can maps relative paths within the instance to whether they are 'log files' * Returns a matcher that can maps relative paths within the instance to whether they are 'log files'

View File

@ -1,4 +1,24 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -51,7 +71,7 @@ QVariant BaseVersionList::data(const QModelIndex &index, int role) const
switch (role) switch (role)
{ {
case VersionPointerRole: case VersionPointerRole:
return qVariantFromValue(version); return QVariant::fromValue(version);
case VersionRole: case VersionRole:
return version->name(); return version->name();

View File

@ -4,8 +4,6 @@ project(application)
######## Sources and headers ######## ######## Sources and headers ########
include (UnitTest)
set(CORE_SOURCES set(CORE_SOURCES
# LOGIC - Base classes and infrastructure # LOGIC - Base classes and infrastructure
BaseInstaller.h BaseInstaller.h
@ -90,16 +88,11 @@ set(CORE_SOURCES
MMCTime.cpp MMCTime.cpp
) )
add_unit_test(FileSystem ecm_add_test(FileSystem_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
SOURCES FileSystem_test.cpp TEST_NAME FileSystem) # TODO: needs testdata
LIBS Launcher_logic
DATA testdata
)
add_unit_test(GZip ecm_add_test(GZip_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
SOURCES GZip_test.cpp TEST_NAME GZip)
LIBS Launcher_logic
)
set(PATHMATCHER_SOURCES set(PATHMATCHER_SOURCES
# Path matchers # Path matchers
@ -128,6 +121,8 @@ set(NET_SOURCES
net/PasteUpload.h net/PasteUpload.h
net/Sink.h net/Sink.h
net/Validator.h net/Validator.h
net/Upload.cpp
net/Upload.h
) )
# Game launch logic # Game launch logic
@ -170,18 +165,6 @@ set(MAC_UPDATE_SOURCES
updater/MacSparkleUpdater.mm updater/MacSparkleUpdater.mm
) )
add_unit_test(UpdateChecker
SOURCES updater/UpdateChecker_test.cpp
LIBS Launcher_logic
DATA updater/testdata
)
add_unit_test(DownloadTask
SOURCES updater/DownloadTask_test.cpp
LIBS Launcher_logic
DATA updater/testdata
)
# Backend for the news bar... there's usually no news. # Backend for the news bar... there's usually no news.
set(NEWS_SOURCES set(NEWS_SOURCES
# News System # News System
@ -328,19 +311,22 @@ set(MINECRAFT_SOURCES
minecraft/WorldList.h minecraft/WorldList.h
minecraft/WorldList.cpp minecraft/WorldList.cpp
minecraft/mod/MetadataHandler.h
minecraft/mod/Mod.h minecraft/mod/Mod.h
minecraft/mod/Mod.cpp minecraft/mod/Mod.cpp
minecraft/mod/ModDetails.h minecraft/mod/ModDetails.h
minecraft/mod/ModFolderModel.h minecraft/mod/ModFolderModel.h
minecraft/mod/ModFolderModel.cpp minecraft/mod/ModFolderModel.cpp
minecraft/mod/ModFolderLoadTask.h
minecraft/mod/ModFolderLoadTask.cpp
minecraft/mod/LocalModParseTask.h
minecraft/mod/LocalModParseTask.cpp
minecraft/mod/ResourcePackFolderModel.h minecraft/mod/ResourcePackFolderModel.h
minecraft/mod/ResourcePackFolderModel.cpp minecraft/mod/ResourcePackFolderModel.cpp
minecraft/mod/TexturePackFolderModel.h minecraft/mod/TexturePackFolderModel.h
minecraft/mod/TexturePackFolderModel.cpp minecraft/mod/TexturePackFolderModel.cpp
minecraft/mod/tasks/ModFolderLoadTask.h
minecraft/mod/tasks/ModFolderLoadTask.cpp
minecraft/mod/tasks/LocalModParseTask.h
minecraft/mod/tasks/LocalModParseTask.cpp
minecraft/mod/tasks/LocalModUpdateTask.h
minecraft/mod/tasks/LocalModUpdateTask.cpp
# Assets # Assets
minecraft/AssetsUtils.h minecraft/AssetsUtils.h
@ -358,10 +344,8 @@ set(MINECRAFT_SOURCES
mojang/PackageManifest.cpp mojang/PackageManifest.cpp
minecraft/Agent.h) minecraft/Agent.h)
add_unit_test(GradleSpecifier ecm_add_test(minecraft/GradleSpecifier_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
SOURCES minecraft/GradleSpecifier_test.cpp TEST_NAME GradleSpecifier)
LIBS Launcher_logic
)
if(BUILD_TESTING) if(BUILD_TESTING)
add_executable(PackageManifest add_executable(PackageManifest
@ -369,7 +353,7 @@ if(BUILD_TESTING)
) )
target_link_libraries(PackageManifest target_link_libraries(PackageManifest
Launcher_logic Launcher_logic
Qt5::Test Qt${QT_VERSION_MAJOR}::Test
) )
target_include_directories(PackageManifest target_include_directories(PackageManifest
PRIVATE ../cmake/UnitTest/ PRIVATE ../cmake/UnitTest/
@ -381,28 +365,20 @@ if(BUILD_TESTING)
) )
endif() endif()
add_unit_test(MojangVersionFormat # TODO: needs minecraft/testdata
SOURCES minecraft/MojangVersionFormat_test.cpp ecm_add_test(minecraft/MojangVersionFormat_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
LIBS Launcher_logic TEST_NAME MojangVersionFormat)
DATA minecraft/testdata
)
add_unit_test(Library ecm_add_test(minecraft/Library_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
SOURCES minecraft/Library_test.cpp TEST_NAME Library)
LIBS Launcher_logic
)
# FIXME: shares data with FileSystem test # FIXME: shares data with FileSystem test
add_unit_test(ModFolderModel # TODO: needs testdata
SOURCES minecraft/mod/ModFolderModel_test.cpp ecm_add_test(minecraft/mod/ModFolderModel_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
DATA testdata TEST_NAME ModFolderModel)
LIBS Launcher_logic
)
add_unit_test(ParseUtils ecm_add_test(minecraft/ParseUtils_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
SOURCES minecraft/ParseUtils_test.cpp TEST_NAME ParseUtils)
LIBS Launcher_logic
)
# the screenshots feature # the screenshots feature
set(SCREENSHOTS_SOURCES set(SCREENSHOTS_SOURCES
@ -417,14 +393,14 @@ set(TASKS_SOURCES
# Tasks # Tasks
tasks/Task.h tasks/Task.h
tasks/Task.cpp tasks/Task.cpp
tasks/ConcurrentTask.h
tasks/ConcurrentTask.cpp
tasks/SequentialTask.h tasks/SequentialTask.h
tasks/SequentialTask.cpp tasks/SequentialTask.cpp
) )
add_unit_test(Task ecm_add_test(tasks/Task_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
SOURCES tasks/Task_test.cpp TEST_NAME Task)
LIBS Launcher_logic
)
set(SETTINGS_SOURCES set(SETTINGS_SOURCES
# Settings # Settings
@ -442,10 +418,8 @@ set(SETTINGS_SOURCES
settings/SettingsObject.h settings/SettingsObject.h
) )
add_unit_test(INIFile ecm_add_test(settings/INIFile_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
SOURCES settings/INIFile_test.cpp TEST_NAME INIFile)
LIBS Launcher_logic
)
set(JAVA_SOURCES set(JAVA_SOURCES
java/JavaChecker.h java/JavaChecker.h
@ -462,10 +436,8 @@ set(JAVA_SOURCES
java/JavaVersion.cpp java/JavaVersion.cpp
) )
add_unit_test(JavaVersion ecm_add_test(java/JavaVersion_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
SOURCES java/JavaVersion_test.cpp TEST_NAME JavaVersion)
LIBS Launcher_logic
)
set(TRANSLATIONS_SOURCES set(TRANSLATIONS_SOURCES
translations/TranslationsModel.h translations/TranslationsModel.h
@ -503,6 +475,9 @@ set(META_SOURCES
) )
set(API_SOURCES set(API_SOURCES
modplatform/ModIndex.h
modplatform/ModIndex.cpp
modplatform/ModAPI.h modplatform/ModAPI.h
modplatform/flame/FlameAPI.h modplatform/flame/FlameAPI.h
@ -549,6 +524,15 @@ set(MODPACKSCH_SOURCES
modplatform/modpacksch/FTBPackManifest.cpp modplatform/modpacksch/FTBPackManifest.cpp
) )
set(PACKWIZ_SOURCES
modplatform/packwiz/Packwiz.h
modplatform/packwiz/Packwiz.cpp
)
# TODO: needs modplatform/packwiz/testdata
ecm_add_test(modplatform/packwiz/Packwiz_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
TEST_NAME Packwiz)
set(TECHNIC_SOURCES set(TECHNIC_SOURCES
modplatform/technic/SingleZipPackInstallTask.h modplatform/technic/SingleZipPackInstallTask.h
modplatform/technic/SingleZipPackInstallTask.cpp modplatform/technic/SingleZipPackInstallTask.cpp
@ -571,10 +555,8 @@ set(ATLAUNCHER_SOURCES
modplatform/atlauncher/ATLShareCode.h modplatform/atlauncher/ATLShareCode.h
) )
add_unit_test(Index ecm_add_test(meta/Index_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
SOURCES meta/Index_test.cpp TEST_NAME Index)
LIBS Launcher_logic
)
################################ COMPILE ################################ ################################ COMPILE ################################
@ -602,6 +584,7 @@ set(LOGIC_SOURCES
${FLAME_SOURCES} ${FLAME_SOURCES}
${MODRINTH_SOURCES} ${MODRINTH_SOURCES}
${MODPACKSCH_SOURCES} ${MODPACKSCH_SOURCES}
${PACKWIZ_SOURCES}
${TECHNIC_SOURCES} ${TECHNIC_SOURCES}
${ATLAUNCHER_SOURCES} ${ATLAUNCHER_SOURCES}
) )
@ -671,6 +654,8 @@ SET(LAUNCHER_SOURCES
ui/setupwizard/JavaWizardPage.h ui/setupwizard/JavaWizardPage.h
ui/setupwizard/LanguageWizardPage.cpp ui/setupwizard/LanguageWizardPage.cpp
ui/setupwizard/LanguageWizardPage.h ui/setupwizard/LanguageWizardPage.h
ui/setupwizard/PasteWizardPage.cpp
ui/setupwizard/PasteWizardPage.h
# GUI - themes # GUI - themes
ui/themes/FusionTheme.cpp ui/themes/FusionTheme.cpp
@ -703,6 +688,8 @@ SET(LAUNCHER_SOURCES
ui/pages/BasePageProvider.h ui/pages/BasePageProvider.h
# GUI - instance pages # GUI - instance pages
ui/pages/instance/ExternalResourcesPage.cpp
ui/pages/instance/ExternalResourcesPage.h
ui/pages/instance/GameOptionsPage.cpp ui/pages/instance/GameOptionsPage.cpp
ui/pages/instance/GameOptionsPage.h ui/pages/instance/GameOptionsPage.h
ui/pages/instance/VersionPage.cpp ui/pages/instance/VersionPage.cpp
@ -831,6 +818,8 @@ SET(LAUNCHER_SOURCES
ui/dialogs/NewComponentDialog.h ui/dialogs/NewComponentDialog.h
ui/dialogs/NewInstanceDialog.cpp ui/dialogs/NewInstanceDialog.cpp
ui/dialogs/NewInstanceDialog.h ui/dialogs/NewInstanceDialog.h
ui/dialogs/NewsDialog.cpp
ui/dialogs/NewsDialog.h
ui/pagedialog/PageDialog.cpp ui/pagedialog/PageDialog.cpp
ui/pagedialog/PageDialog.h ui/pagedialog/PageDialog.h
ui/dialogs/ProgressDialog.cpp ui/dialogs/ProgressDialog.cpp
@ -845,6 +834,8 @@ SET(LAUNCHER_SOURCES
ui/dialogs/SkinUploadDialog.h ui/dialogs/SkinUploadDialog.h
ui/dialogs/ModDownloadDialog.cpp ui/dialogs/ModDownloadDialog.cpp
ui/dialogs/ModDownloadDialog.h ui/dialogs/ModDownloadDialog.h
ui/dialogs/ScrollMessageBox.cpp
ui/dialogs/ScrollMessageBox.h
# GUI - widgets # GUI - widgets
ui/widgets/Common.cpp ui/widgets/Common.cpp
@ -899,7 +890,8 @@ SET(LAUNCHER_SOURCES
ui/instanceview/VisualGroup.h ui/instanceview/VisualGroup.h
) )
qt5_wrap_ui(LAUNCHER_UI qt_wrap_ui(LAUNCHER_UI
ui/setupwizard/PasteWizardPage.ui
ui/pages/global/AccountListPage.ui ui/pages/global/AccountListPage.ui
ui/pages/global/JavaPage.ui ui/pages/global/JavaPage.ui
ui/pages/global/LauncherPage.ui ui/pages/global/LauncherPage.ui
@ -907,7 +899,7 @@ qt5_wrap_ui(LAUNCHER_UI
ui/pages/global/ProxyPage.ui ui/pages/global/ProxyPage.ui
ui/pages/global/MinecraftPage.ui ui/pages/global/MinecraftPage.ui
ui/pages/global/ExternalToolsPage.ui ui/pages/global/ExternalToolsPage.ui
ui/pages/instance/ModFolderPage.ui ui/pages/instance/ExternalResourcesPage.ui
ui/pages/instance/NotesPage.ui ui/pages/instance/NotesPage.ui
ui/pages/instance/LogPage.ui ui/pages/instance/LogPage.ui
ui/pages/instance/ServersPage.ui ui/pages/instance/ServersPage.ui
@ -937,6 +929,7 @@ qt5_wrap_ui(LAUNCHER_UI
ui/dialogs/NewInstanceDialog.ui ui/dialogs/NewInstanceDialog.ui
ui/dialogs/UpdateDialog.ui ui/dialogs/UpdateDialog.ui
ui/dialogs/NewComponentDialog.ui ui/dialogs/NewComponentDialog.ui
ui/dialogs/NewsDialog.ui
ui/dialogs/ProfileSelectDialog.ui ui/dialogs/ProfileSelectDialog.ui
ui/dialogs/SkinUploadDialog.ui ui/dialogs/SkinUploadDialog.ui
ui/dialogs/ExportInstanceDialog.ui ui/dialogs/ExportInstanceDialog.ui
@ -947,9 +940,10 @@ qt5_wrap_ui(LAUNCHER_UI
ui/dialogs/LoginDialog.ui ui/dialogs/LoginDialog.ui
ui/dialogs/EditAccountDialog.ui ui/dialogs/EditAccountDialog.ui
ui/dialogs/ReviewMessageBox.ui ui/dialogs/ReviewMessageBox.ui
ui/dialogs/ScrollMessageBox.ui
) )
qt5_add_resources(LAUNCHER_RESOURCES qt_add_resources(LAUNCHER_RESOURCES
resources/backgrounds/backgrounds.qrc resources/backgrounds/backgrounds.qrc
resources/multimc/multimc.qrc resources/multimc/multimc.qrc
resources/pe_dark/pe_dark.qrc resources/pe_dark/pe_dark.qrc
@ -965,7 +959,7 @@ qt5_add_resources(LAUNCHER_RESOURCES
######## Windows resource files ######## ######## Windows resource files ########
if(WIN32) if(WIN32)
set(LAUNCHER_RCS ../${Launcher_Branding_WindowsRC}) set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC})
endif() endif()
# Add executable # Add executable
@ -979,16 +973,25 @@ target_link_libraries(Launcher_logic
tomlc99 tomlc99
BuildConfig BuildConfig
Katabasis Katabasis
Qt${QT_VERSION_MAJOR}::Widgets
)
if (UNIX AND NOT CYGWIN AND NOT APPLE)
target_link_libraries(Launcher_logic
gamemode
)
endif()
target_link_libraries(Launcher_logic
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Xml
Qt${QT_VERSION_MAJOR}::Network
Qt${QT_VERSION_MAJOR}::Concurrent
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Widgets
${Launcher_QT_LIBS}
) )
target_link_libraries(Launcher_logic target_link_libraries(Launcher_logic
Qt5::Core
Qt5::Xml
Qt5::Network
Qt5::Concurrent
Qt5::Gui
)
target_link_libraries(Launcher_logic
Launcher_iconfix
QuaZip::QuaZip QuaZip::QuaZip
hoedown hoedown
LocalPeer LocalPeer
@ -1082,6 +1085,14 @@ if(INSTALL_BUNDLE STREQUAL "full")
COMPONENT Runtime COMPONENT Runtime
) )
endif() endif()
# TLS plugins (Qt 6 only)
if(EXISTS "${QT_PLUGINS_DIR}/tls")
install(
DIRECTORY "${QT_PLUGINS_DIR}/tls"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
)
endif()
else() else()
# Image formats # Image formats
install( install(
@ -1124,6 +1135,16 @@ if(INSTALL_BUNDLE STREQUAL "full")
REGEX "\\.dSYM" EXCLUDE REGEX "\\.dSYM" EXCLUDE
) )
endif() endif()
# TLS plugins (Qt 6 only)
if(EXISTS "${QT_PLUGINS_DIR}/tls")
install(
DIRECTORY "${QT_PLUGINS_DIR}/tls"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "_debug\\." EXCLUDE
REGEX "\\.dSYM" EXCLUDE
)
endif()
endif() endif()
configure_file( configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in" "${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"

View File

@ -1,4 +1,24 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Authors: Orochimarufan <orochimarufan.x3@gmail.com> * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
* *
@ -47,7 +67,7 @@ QStringList splitArgs(QString args)
if (cchar == '\\') if (cchar == '\\')
escape = true; escape = true;
else if (cchar == inquotes) else if (cchar == inquotes)
inquotes = 0; inquotes = QChar::Null;
else else
current += cchar; current += cchar;
// otherwise // otherwise

View File

@ -1,4 +1,37 @@
// Licensed under the Apache-2.0 license. See README.md for details. // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "FileSystem.h" #include "FileSystem.h"
@ -346,7 +379,7 @@ bool checkProblemticPathJava(QDir folder)
} }
// Win32 crap // Win32 crap
#if defined Q_OS_WIN #ifdef Q_OS_WIN
bool called_coinit = false; bool called_coinit = false;
@ -366,7 +399,7 @@ HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args)
} }
} }
IShellLink *link; IShellLinkA *link;
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink,
(LPVOID *)&link); (LPVOID *)&link);
@ -454,4 +487,47 @@ bool createShortCut(QString location, QString dest, QStringList args, QString na
return false; return false;
#endif #endif
} }
QStringList listFolderPaths(QDir root)
{
auto createAbsPath = [](QFileInfo const& entry) { return FS::PathCombine(entry.path(), entry.fileName()); };
QStringList entries;
root.refresh();
for (auto entry : root.entryInfoList(QDir::Filter::Files)) {
entries.append(createAbsPath(entry));
}
for (auto entry : root.entryInfoList(QDir::Filter::AllDirs | QDir::Filter::NoDotAndDotDot)) {
entries.append(listFolderPaths(createAbsPath(entry)));
}
return entries;
}
bool overrideFolder(QString overwritten_path, QString override_path)
{
if (!FS::ensureFolderPathExists(overwritten_path))
return false;
QStringList paths_to_override;
QDir root_override (override_path);
for (auto file : listFolderPaths(root_override)) {
QString destination = file;
destination.replace(override_path, overwritten_path);
qDebug() << QString("Applying override %1 in %2").arg(file, destination);
if (QFile::exists(destination))
QFile::remove(destination);
if (!QFile::rename(file, destination)) {
qCritical() << QString("Failed to apply override from %1 to %2").arg(file, destination);
return false;
}
}
return true;
}
} }

View File

@ -1,4 +1,37 @@
// Licensed under the Apache-2.0 license. See README.md for details. // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once #pragma once
@ -49,8 +82,8 @@ class copy
public: public:
copy(const QString & src, const QString & dst) copy(const QString & src, const QString & dst)
{ {
m_src = src; m_src.setPath(src);
m_dst = dst; m_dst.setPath(dst);
} }
copy & followSymlinks(const bool follow) copy & followSymlinks(const bool follow)
{ {
@ -120,8 +153,7 @@ bool checkProblemticPathJava(QDir folder);
// Get the Directory representing the User's Desktop // Get the Directory representing the User's Desktop
QString getDesktopDir(); QString getDesktopDir();
// Create a shortcut at *location*, pointing to *dest* called with the arguments *args* // Overrides one folder with the contents of another, preserving items exclusive to the first folder
// call it *name* and assign it the icon *icon* // Equivalent to doing QDir::rename, but allowing for overrides
// return true if operation succeeded bool overrideFolder(QString overwritten_path, QString override_path);
bool createShortCut(QString location, QString dest, QStringList args, QString name, QString iconLocation);
} }

View File

@ -1,7 +1,6 @@
#include <QTest> #include <QTest>
#include <QTemporaryDir> #include <QTemporaryDir>
#include <QStandardPaths> #include <QStandardPaths>
#include "TestUtil.h"
#include "FileSystem.h" #include "FileSystem.h"
@ -81,7 +80,7 @@ slots:
void test_copy() void test_copy()
{ {
QString folder = QFINDTESTDATA("data/test_folder"); QString folder = QFINDTESTDATA("testdata/test_folder");
auto f = [&folder]() auto f = [&folder]()
{ {
QTemporaryDir tempDir; QTemporaryDir tempDir;
@ -116,47 +115,6 @@ slots:
{ {
QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
} }
// this is only valid on linux
// FIXME: implement on windows, OSX, then test.
#if defined(Q_OS_LINUX)
void test_createShortcut_data()
{
QTest::addColumn<QString>("location");
QTest::addColumn<QString>("dest");
QTest::addColumn<QStringList>("args");
QTest::addColumn<QString>("name");
QTest::addColumn<QString>("iconLocation");
QTest::addColumn<QByteArray>("result");
QTest::newRow("unix") << QDir::currentPath()
<< "asdfDest"
<< (QStringList() << "arg1" << "arg2")
<< "asdf"
<< QString()
#if defined(Q_OS_LINUX)
<< GET_TEST_FILE("data/FileSystem-test_createShortcut-unix")
#elif defined(Q_OS_WIN)
<< QByteArray()
#endif
;
}
void test_createShortcut()
{
QFETCH(QString, location);
QFETCH(QString, dest);
QFETCH(QStringList, args);
QFETCH(QString, name);
QFETCH(QString, iconLocation);
QFETCH(QByteArray, result);
QVERIFY(FS::createShortCut(location, dest, args, name, iconLocation));
QCOMPARE(QString::fromLocal8Bit(TestsInternal::readFile(location + QDir::separator() + name + ".desktop")), QString::fromLocal8Bit(result));
//QDir().remove(location);
}
#endif
}; };
QTEST_GUILESS_MAIN(FileSystemTest) QTEST_GUILESS_MAIN(FileSystemTest)

View File

@ -1,3 +1,38 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "GZip.h" #include "GZip.h"
#include <zlib.h> #include <zlib.h>
#include <QByteArray> #include <QByteArray>
@ -67,7 +102,7 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
return true; return true;
} }
unsigned compLength = std::min(uncompressedBytes.size(), 16); unsigned compLength = qMin(uncompressedBytes.size(), 16);
compressedBytes.clear(); compressedBytes.clear();
compressedBytes.resize(compLength); compressedBytes.resize(compLength);

View File

@ -1,5 +1,4 @@
#include <QTest> #include <QTest>
#include "TestUtil.h"
#include "GZip.h" #include "GZip.h"
#include <random> #include <random>

View File

@ -41,6 +41,7 @@
#include "FileSystem.h" #include "FileSystem.h"
#include "MMCZip.h" #include "MMCZip.h"
#include "NullInstance.h" #include "NullInstance.h"
#include "icons/IconList.h"
#include "icons/IconUtils.h" #include "icons/IconUtils.h"
#include "settings/INISettingsObject.h" #include "settings/INISettingsObject.h"
@ -59,9 +60,9 @@
#include "net/ChecksumValidator.h" #include "net/ChecksumValidator.h"
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/ScrollMessageBox.h"
#include <algorithm> #include <algorithm>
#include <iterator>
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent) InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent)
{ {
@ -71,6 +72,7 @@ InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent)
bool InstanceImportTask::abort() bool InstanceImportTask::abort()
{ {
if (m_filesNetJob)
m_filesNetJob->abort(); m_filesNetJob->abort();
m_extractFuture.cancel(); m_extractFuture.cancel();
@ -134,18 +136,20 @@ void InstanceImportTask::processZipPack()
return; return;
} }
QStringList blacklist = {"instance.cfg", "manifest.json"}; QuaZipDir packZipDir(m_packZip.get());
QString mmcFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
bool technicFound = QuaZipDir(m_packZip.get()).exists("/bin/modpack.jar") || QuaZipDir(m_packZip.get()).exists("/bin/version.json"); // https://docs.modrinth.com/docs/modpacks/format_definition/#storage
QString flameFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json"); bool modrinthFound = packZipDir.exists("/modrinth.index.json");
QString modrinthFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "modrinth.index.json"); bool technicFound = packZipDir.exists("/bin/modpack.jar") || packZipDir.exists("/bin/version.json");
QString root; QString root;
if(!mmcFound.isNull())
// NOTE: Prioritize modpack platforms that aren't searched for recursively.
// Especially Flame has a very common filename for its manifest, which may appear inside overrides for example
if(modrinthFound)
{ {
// process as MultiMC instance/pack // process as Modrinth pack
qDebug() << "MultiMC:" << mmcFound; qDebug() << "Modrinth:" << modrinthFound;
root = mmcFound; m_modpackType = ModpackType::Modrinth;
m_modpackType = ModpackType::MultiMC;
} }
else if (technicFound) else if (technicFound)
{ {
@ -155,19 +159,25 @@ void InstanceImportTask::processZipPack()
extractDir.cd(".minecraft"); extractDir.cd(".minecraft");
m_modpackType = ModpackType::Technic; m_modpackType = ModpackType::Technic;
} }
else if(!flameFound.isNull()) else
{
QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
if (!mmcRoot.isNull())
{
// process as MultiMC instance/pack
qDebug() << "MultiMC:" << mmcRoot;
root = mmcRoot;
m_modpackType = ModpackType::MultiMC;
}
else if(!flameRoot.isNull())
{ {
// process as Flame pack // process as Flame pack
qDebug() << "Flame:" << flameFound; qDebug() << "Flame:" << flameRoot;
root = flameFound; root = flameRoot;
m_modpackType = ModpackType::Flame; m_modpackType = ModpackType::Flame;
} }
else if(!modrinthFound.isNull())
{
// process as Modrinth pack
qDebug() << "Modrinth:" << modrinthFound;
root = modrinthFound;
m_modpackType = ModpackType::Modrinth;
} }
if(m_modpackType == ModpackType::Unknown) if(m_modpackType == ModpackType::Unknown)
{ {
@ -315,7 +325,7 @@ void InstanceImportTask::processFlame()
// Hack to correct some 'special sauce'... // Hack to correct some 'special sauce'...
if(mcVersion.endsWith('.')) if(mcVersion.endsWith('.'))
{ {
mcVersion.remove(QRegExp("[.]+$")); mcVersion.remove(QRegularExpression("[.]+$"));
logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack.")); logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
} }
auto components = instance.getPackProfile(); auto components = instance.getPackProfile();
@ -384,35 +394,54 @@ void InstanceImportTask::processFlame()
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]() connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]()
{ {
auto results = m_modIdResolver->getResults(); auto results = m_modIdResolver->getResults();
//first check for blocked mods
QString text;
auto anyBlocked = false;
for(const auto& result: results.files.values()) {
if (!result.resolved || result.url.isEmpty()) {
text += QString("%1: <a href='%2'>%2</a><br/>").arg(result.fileName, result.websiteUrl);
anyBlocked = true;
}
}
if(anyBlocked) {
qWarning() << "Blocked mods found, displaying mod list";
auto message_dialog = new ScrollMessageBox(m_parent,
tr("Blocked mods found"),
tr("The following mods were blocked on third party launchers.<br/>"
"You will need to manually download them and add them to the modpack"),
text);
message_dialog->setModal(true);
if (message_dialog->exec()) {
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network()); m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
for(auto result: results.files) for (const auto &result: m_modIdResolver->getResults().files) {
{
QString filename = result.fileName; QString filename = result.fileName;
if(!result.required) if (!result.required) {
{
filename += ".disabled"; filename += ".disabled";
} }
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename); auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
auto path = FS::PathCombine(m_stagingPath , relpath); auto path = FS::PathCombine(m_stagingPath, relpath);
switch(result.type) switch (result.type) {
{ case Flame::File::Type::Folder: {
case Flame::File::Type::Folder:
{
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath)); logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
// fall-through intentional, we treat these as plain old mods and dump them wherever. // fall-through intentional, we treat these as plain old mods and dump them wherever.
} }
case Flame::File::Type::SingleFile: case Flame::File::Type::SingleFile:
case Flame::File::Type::Mod: case Flame::File::Type::Mod: {
{ if (!result.url.isEmpty()) {
qDebug() << "Will download" << result.url << "to" << path; qDebug() << "Will download" << result.url << "to" << path;
auto dl = Net::Download::makeFile(result.url, path); auto dl = Net::Download::makeFile(result.url, path);
m_filesNetJob->addNetAction(dl); m_filesNetJob->addNetAction(dl);
}
break; break;
} }
case Flame::File::Type::Modpack: case Flame::File::Type::Modpack:
logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath)); logWarning(
tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(
relpath));
break; break;
case Flame::File::Type::Cmod2: case Flame::File::Type::Cmod2:
case Flame::File::Type::Ctoc: case Flame::File::Type::Ctoc:
@ -422,23 +451,78 @@ void InstanceImportTask::processFlame()
} }
} }
m_modIdResolver.reset(); m_modIdResolver.reset();
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() {
{
m_filesNetJob.reset(); m_filesNetJob.reset();
emitSucceeded(); emitSucceeded();
} }
); );
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) {
{
m_filesNetJob.reset(); m_filesNetJob.reset();
emitFailed(reason); emitFailed(reason);
}); });
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
{
setProgress(current, total); setProgress(current, total);
}); });
setStatus(tr("Downloading mods...")); setStatus(tr("Downloading mods..."));
m_filesNetJob->start(); m_filesNetJob->start();
} else {
m_modIdResolver.reset();
emitFailed("Canceled");
}
} else {
//TODO extract to function ?
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
for (const auto &result: m_modIdResolver->getResults().files) {
QString filename = result.fileName;
if (!result.required) {
filename += ".disabled";
}
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
auto path = FS::PathCombine(m_stagingPath, relpath);
switch (result.type) {
case Flame::File::Type::Folder: {
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
// fall-through intentional, we treat these as plain old mods and dump them wherever.
}
case Flame::File::Type::SingleFile:
case Flame::File::Type::Mod: {
if (!result.url.isEmpty()) {
qDebug() << "Will download" << result.url << "to" << path;
auto dl = Net::Download::makeFile(result.url, path);
m_filesNetJob->addNetAction(dl);
}
break;
}
case Flame::File::Type::Modpack:
logWarning(
tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(
relpath));
break;
case Flame::File::Type::Cmod2:
case Flame::File::Type::Ctoc:
case Flame::File::Type::Unknown:
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
break;
}
}
m_modIdResolver.reset();
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() {
m_filesNetJob.reset();
emitSucceeded();
}
);
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) {
m_filesNetJob.reset();
emitFailed(reason);
});
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
setProgress(current, total);
});
setStatus(tr("Downloading mods..."));
m_filesNetJob->start();
}
} }
); );
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason) connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
@ -497,6 +581,7 @@ void InstanceImportTask::processMultiMC()
emitSucceeded(); emitSucceeded();
} }
// https://docs.modrinth.com/docs/modpacks/format_definition/
void InstanceImportTask::processModrinth() void InstanceImportTask::processModrinth()
{ {
std::vector<Modrinth::File> files; std::vector<Modrinth::File> files;
@ -514,11 +599,13 @@ void InstanceImportTask::processModrinth()
auto jsonFiles = Json::requireIsArrayOf<QJsonObject>(obj, "files", "modrinth.index.json"); auto jsonFiles = Json::requireIsArrayOf<QJsonObject>(obj, "files", "modrinth.index.json");
bool had_optional = false; bool had_optional = false;
for (auto& obj : jsonFiles) { for (auto modInfo : jsonFiles) {
Modrinth::File file; Modrinth::File file;
file.path = Json::requireString(obj, "path"); file.path = Json::requireString(modInfo, "path");
auto env = Json::ensureObject(obj, "env"); auto env = Json::ensureObject(modInfo, "env");
// 'env' field is optional
if (!env.isEmpty()) {
QString support = Json::ensureString(env, "client", "unsupported"); QString support = Json::ensureString(env, "client", "unsupported");
if (support == "unsupported") { if (support == "unsupported") {
continue; continue;
@ -528,15 +615,17 @@ void InstanceImportTask::processModrinth()
had_optional = true; had_optional = true;
auto info = CustomMessageBox::selectable( auto info = CustomMessageBox::selectable(
m_parent, tr("Optional mod detected!"), m_parent, tr("Optional mod detected!"),
tr("One or more mods from this modpack are optional. They will be downloaded, but disabled by default!"), QMessageBox::Information); tr("One or more mods from this modpack are optional. They will be downloaded, but disabled by default!"),
QMessageBox::Information);
info->exec(); info->exec();
} }
if (file.path.endsWith(".jar")) if (file.path.endsWith(".jar"))
file.path += ".disabled"; file.path += ".disabled";
} }
}
QJsonObject hashes = Json::requireObject(obj, "hashes"); QJsonObject hashes = Json::requireObject(modInfo, "hashes");
QString hash; QString hash;
QCryptographicHash::Algorithm hashAlgorithm; QCryptographicHash::Algorithm hashAlgorithm;
hash = Json::ensureString(hashes, "sha1"); hash = Json::ensureString(hashes, "sha1");
@ -554,12 +643,28 @@ void InstanceImportTask::processModrinth()
} }
file.hash = QByteArray::fromHex(hash.toLatin1()); file.hash = QByteArray::fromHex(hash.toLatin1());
file.hashAlgorithm = hashAlgorithm; file.hashAlgorithm = hashAlgorithm;
// Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode // Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode
// (as Modrinth seems to incorrectly handle spaces) // (as Modrinth seems to incorrectly handle spaces)
file.download = Json::requireString(Json::ensureArray(obj, "downloads").first(), "Download URL for " + file.path);
if (!file.download.isValid() || !Modrinth::validateDownloadUrl(file.download)) { auto download_arr = Json::ensureArray(modInfo, "downloads");
throw JSONValidationError("Download URL for " + file.path + " is not a correctly formatted URL"); for(auto download : download_arr) {
qWarning() << download.toString();
bool is_last = download.toString() == download_arr.last().toString();
auto download_url = QUrl(download.toString());
if (!download_url.isValid()) {
qDebug() << QString("Download URL (%1) for %2 is not a correctly formatted URL")
.arg(download_url.toString(), file.path);
if(is_last && file.downloads.isEmpty())
throw JSONValidationError(tr("Download URL for %1 is not a correctly formatted URL").arg(file.path));
} }
else {
file.downloads.push_back(download_url);
}
}
files.push_back(file); files.push_back(file);
} }
@ -591,15 +696,25 @@ void InstanceImportTask::processModrinth()
return; return;
} }
QString overridePath = FS::PathCombine(m_stagingPath, "overrides"); auto mcPath = FS::PathCombine(m_stagingPath, ".minecraft");
if (QFile::exists(overridePath)) {
QString mcPath = FS::PathCombine(m_stagingPath, ".minecraft"); auto override_path = FS::PathCombine(m_stagingPath, "overrides");
if (!QFile::rename(overridePath, mcPath)) { if (QFile::exists(override_path)) {
if (!QFile::rename(override_path, mcPath)) {
emitFailed(tr("Could not rename the overrides folder:\n") + "overrides"); emitFailed(tr("Could not rename the overrides folder:\n") + "overrides");
return; return;
} }
} }
// Do client overrides
auto client_override_path = FS::PathCombine(m_stagingPath, "client-overrides");
if (QFile::exists(client_override_path)) {
if (!FS::overrideFolder(mcPath, client_override_path)) {
emitFailed(tr("Could not rename the client overrides folder:\n") + "client overrides");
return;
}
}
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
auto instanceSettings = std::make_shared<INISettingsObject>(configPath); auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath); MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
@ -607,11 +722,11 @@ void InstanceImportTask::processModrinth()
components->buildingFromScratch(); components->buildingFromScratch();
components->setComponentVersion("net.minecraft", minecraftVersion, true); components->setComponentVersion("net.minecraft", minecraftVersion, true);
if (!fabricVersion.isEmpty()) if (!fabricVersion.isEmpty())
components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion, true); components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion);
if (!quiltVersion.isEmpty()) if (!quiltVersion.isEmpty())
components->setComponentVersion("org.quiltmc.quilt-loader", quiltVersion, true); components->setComponentVersion("org.quiltmc.quilt-loader", quiltVersion);
if (!forgeVersion.isEmpty()) if (!forgeVersion.isEmpty())
components->setComponentVersion("net.minecraftforge", forgeVersion, true); components->setComponentVersion("net.minecraftforge", forgeVersion);
if (m_instIcon != "default") if (m_instIcon != "default")
{ {
instance.setIconKey(m_instIcon); instance.setIconKey(m_instIcon);
@ -624,13 +739,24 @@ void InstanceImportTask::processModrinth()
instance.saveNow(); instance.saveNow();
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network()); m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
for (auto &file : files) for (auto file : files)
{ {
auto path = FS::PathCombine(m_stagingPath, ".minecraft", file.path); auto path = FS::PathCombine(m_stagingPath, ".minecraft", file.path);
qDebug() << "Will download" << file.download << "to" << path; qDebug() << "Will try to download" << file.downloads.front() << "to" << path;
auto dl = Net::Download::makeFile(file.download, path); auto dl = Net::Download::makeFile(file.downloads.dequeue(), path);
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash)); dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
m_filesNetJob->addNetAction(dl); m_filesNetJob->addNetAction(dl);
if (file.downloads.size() > 0) {
// FIXME: This really needs to be put into a ConcurrentTask of
// MultipleOptionsTask's , once those exist :)
connect(dl.get(), &NetAction::failed, [this, &file, path, dl]{
auto dl = Net::Download::makeFile(file.downloads.dequeue(), path);
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
m_filesNetJob->addNetAction(dl);
dl->succeeded();
});
}
} }
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
{ {

View File

@ -42,6 +42,7 @@
#include <QFutureWatcher> #include <QFutureWatcher>
#include "settings/SettingsObject.h" #include "settings/SettingsObject.h"
#include "QObjectPtr.h" #include "QObjectPtr.h"
#include "modplatform/flame/PackManifest.h"
#include <nonstd/optional> #include <nonstd/optional>
@ -59,6 +60,10 @@ public:
bool canAbort() const override { return true; } bool canAbort() const override { return true; }
bool abort() override; bool abort() override;
const QVector<Flame::File> &getBlockedFiles() const
{
return m_blockedMods;
}
protected: protected:
//! Entry point for tasks. //! Entry point for tasks.
@ -87,6 +92,7 @@ private: /* data */
std::unique_ptr<QuaZip> m_packZip; std::unique_ptr<QuaZip> m_packZip;
QFuture<nonstd::optional<QStringList>> m_extractFuture; QFuture<nonstd::optional<QStringList>> m_extractFuture;
QFutureWatcher<nonstd::optional<QStringList>> m_extractFutureWatcher; QFutureWatcher<nonstd::optional<QStringList>> m_extractFutureWatcher;
QVector<Flame::File> m_blockedMods;
enum class ModpackType{ enum class ModpackType{
Unknown, Unknown,
MultiMC, MultiMC,

View File

@ -1,4 +1,24 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -136,7 +156,7 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const
{ {
case InstancePointerRole: case InstancePointerRole:
{ {
QVariant v = qVariantFromValue((void *)pdata); QVariant v = QVariant::fromValue((void *)pdata);
return v; return v;
} }
case InstanceIDRole: case InstanceIDRole:
@ -252,7 +272,7 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
QStringList InstanceList::getGroups() QStringList InstanceList::getGroups()
{ {
return m_groupNameCache.toList(); return m_groupNameCache.values();
} }
void InstanceList::deleteGroup(const QString& name) void InstanceList::deleteGroup(const QString& name)
@ -353,7 +373,11 @@ QList< InstanceId > InstanceList::discoverInstances()
out.append(id); out.append(id);
qDebug() << "Found instance ID" << id; qDebug() << "Found instance ID" << id;
} }
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
instanceSet = QSet<QString>(out.begin(), out.end());
#else
instanceSet = out.toSet(); instanceSet = out.toSet();
#endif
m_instancesProbed = true; m_instancesProbed = true;
return out; return out;
} }
@ -547,8 +571,20 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id)
auto instanceRoot = FS::PathCombine(m_instDir, id); auto instanceRoot = FS::PathCombine(m_instDir, id);
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg")); auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg"));
InstancePtr inst; InstancePtr inst;
// TODO: Handle incompatible instances
instanceSettings->registerSetting("InstanceType", "");
QString inst_type = instanceSettings->get("InstanceType").toString();
// NOTE: Some PolyMC versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a OneSix instance
if (inst_type == "OneSix" || inst_type.isEmpty())
{
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot)); inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
}
else
{
inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot));
}
qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot(); qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot();
return inst; return inst;
} }

View File

@ -33,10 +33,10 @@ public:
values.append(new LogPage(inst)); values.append(new LogPage(inst));
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst); std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
values.append(new VersionPage(onesix.get())); values.append(new VersionPage(onesix.get()));
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList(), "mods", "loadermods", tr("Mods"), "Loader-mods"); auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList());
modsPage->setFilter("%1 (*.zip *.jar *.litemod)"); modsPage->setFilter("%1 (*.zip *.jar *.litemod)");
values.append(modsPage); values.append(modsPage);
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList(), "coremods", "coremods", tr("Core mods"), "Core-mods")); values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList()));
values.append(new ResourcePackPage(onesix.get())); values.append(new ResourcePackPage(onesix.get()));
values.append(new TexturePackPage(onesix.get())); values.append(new TexturePackPage(onesix.get()));
values.append(new ShaderPackPage(onesix.get())); values.append(new ShaderPackPage(onesix.get()));

View File

@ -1,10 +1,47 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "JavaCommon.h" #include "JavaCommon.h"
#include "java/JavaUtils.h"
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
#include <MMCStrings.h> #include <MMCStrings.h>
#include <QRegularExpression>
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent) bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
{ {
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegExp("-Xm[sx]")) if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegularExpression("-Xm[sx]"))
|| jvmargs.contains("-XX-MaxHeapSize") || jvmargs.contains("-XX:InitialHeapSize")) || jvmargs.contains("-XX-MaxHeapSize") || jvmargs.contains("-XX:InitialHeapSize"))
{ {
auto warnStr = QObject::tr( auto warnStr = QObject::tr(
@ -18,7 +55,7 @@ bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
return false; return false;
} }
// block lunacy with passing required version to the JVM // block lunacy with passing required version to the JVM
if (jvmargs.contains(QRegExp("-version:.*"))) { if (jvmargs.contains(QRegularExpression("-version:.*"))) {
auto warnStr = QObject::tr( auto warnStr = QObject::tr(
"You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be allowed.\n" "You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be allowed.\n"
"This message will be displayed until you remove this from the JVM arguments."); "This message will be displayed until you remove this from the JVM arguments.");
@ -65,6 +102,13 @@ void JavaCommon::javaBinaryWasBad(QWidget *parent, JavaCheckResult result)
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show(); CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
} }
void JavaCommon::javaCheckNotFound(QWidget *parent)
{
QString text;
text += QObject::tr("Java checker library could not be found. Please check your installation");
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
}
void JavaCommon::TestCheck::run() void JavaCommon::TestCheck::run()
{ {
if (!JavaCommon::checkJVMArgs(m_args, m_parent)) if (!JavaCommon::checkJVMArgs(m_args, m_parent))
@ -72,6 +116,11 @@ void JavaCommon::TestCheck::run()
emit finished(); emit finished();
return; return;
} }
if (JavaUtils::getJavaCheckPath().isEmpty()) {
javaCheckNotFound(m_parent);
emit finished();
return;
}
checker.reset(new JavaChecker()); checker.reset(new JavaChecker());
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
SLOT(checkFinished(JavaCheckResult))); SLOT(checkFinished(JavaCheckResult)));

View File

@ -10,12 +10,14 @@ namespace JavaCommon
{ {
bool checkJVMArgs(QString args, QWidget *parent); bool checkJVMArgs(QString args, QWidget *parent);
// Show a dialog saying that the Java binary was not usable
void javaBinaryWasBad(QWidget *parent, JavaCheckResult result);
// Show a dialog saying that the Java binary was not usable because of bad options
void javaArgsWereBad(QWidget *parent, JavaCheckResult result);
// Show a dialog saying that the Java binary was usable // Show a dialog saying that the Java binary was usable
void javaWasOk(QWidget *parent, JavaCheckResult result); void javaWasOk(QWidget *parent, JavaCheckResult result);
// Show a dialog saying that the Java binary was not usable because of bad options
void javaArgsWereBad(QWidget *parent, JavaCheckResult result);
// Show a dialog saying that the Java binary was not usable
void javaBinaryWasBad(QWidget *parent, JavaCheckResult result);
// Show a dialog if we couldn't find Java Checker
void javaCheckNotFound(QWidget *parent);
class TestCheck : public QObject class TestCheck : public QObject
{ {

View File

@ -1,4 +1,37 @@
// Licensed under the Apache-2.0 license. See README.md for details. // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Json.h" #include "Json.h"
@ -22,14 +55,6 @@ void write(const QJsonArray &array, const QString &filename)
write(QJsonDocument(array), filename); write(QJsonDocument(array), filename);
} }
QByteArray toBinary(const QJsonObject &obj)
{
return QJsonDocument(obj).toBinaryData();
}
QByteArray toBinary(const QJsonArray &array)
{
return QJsonDocument(array).toBinaryData();
}
QByteArray toText(const QJsonObject &obj) QByteArray toText(const QJsonObject &obj)
{ {
return QJsonDocument(obj).toJson(QJsonDocument::Compact); return QJsonDocument(obj).toJson(QJsonDocument::Compact);
@ -48,12 +73,8 @@ QJsonDocument requireDocument(const QByteArray &data, const QString &what)
{ {
if (isBinaryJson(data)) if (isBinaryJson(data))
{ {
QJsonDocument doc = QJsonDocument::fromBinaryData(data); // FIXME: Is this needed?
if (doc.isNull()) throw JsonException(what + ": Invalid JSON. Binary JSON unsupported");
{
throw JsonException(what + ": Invalid JSON (binary JSON detected)");
}
return doc;
} }
else else
{ {

View File

@ -1,4 +1,37 @@
// Licensed under the Apache-2.0 license. See README.md for details. // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once #pragma once
@ -29,8 +62,6 @@ void write(const QJsonObject &object, const QString &filename);
/// @throw FileSystemException /// @throw FileSystemException
void write(const QJsonArray &array, const QString &filename); void write(const QJsonArray &array, const QString &filename);
QByteArray toBinary(const QJsonObject &obj);
QByteArray toBinary(const QJsonArray &array);
QByteArray toText(const QJsonObject &obj); QByteArray toText(const QJsonObject &obj);
QByteArray toText(const QJsonArray &array); QByteArray toText(const QJsonArray &array);

View File

@ -105,6 +105,11 @@ void LaunchController::decideAccount()
// Open the account manager. // Open the account manager.
APPLICATION->ShowGlobalSettings(m_parentWidget, "accounts"); APPLICATION->ShowGlobalSettings(m_parentWidget, "accounts");
} }
else if (reply == QMessageBox::No)
{
// Do not open "profile select" dialog.
return;
}
} }
m_accountToUse = accounts->defaultAccount(); m_accountToUse = accounts->defaultAccount();

View File

@ -1,3 +1,38 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "LoggedProcess.h" #include "LoggedProcess.h"
#include "MessageLevel.h" #include "MessageLevel.h"
#include <QDebug> #include <QDebug>
@ -8,7 +43,11 @@ LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent)
connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut); connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut);
connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr); connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr);
connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(on_exit(int,QProcess::ExitStatus))); connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(on_exit(int,QProcess::ExitStatus)));
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
connect(this, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError)));
#else
connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError))); connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError)));
#endif
connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange); connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange);
} }
@ -157,19 +196,6 @@ void LoggedProcess::on_stateChange(QProcess::ProcessState state)
} }
} }
#if defined Q_OS_WIN32
#include <windows.h>
#endif
qint64 LoggedProcess::processId() const
{
#ifdef Q_OS_WIN
return pid() ? pid()->dwProcessId : 0;
#else
return pid();
#endif
}
void LoggedProcess::setDetachable(bool detachable) void LoggedProcess::setDetachable(bool detachable)
{ {
m_is_detachable = detachable; m_is_detachable = detachable;

View File

@ -1,4 +1,24 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -43,7 +63,6 @@ public:
State state() const; State state() const;
int exitCode() const; int exitCode() const;
qint64 processId() const;
void setDetachable(bool detachable); void setDetachable(bool detachable);

View File

@ -151,23 +151,23 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
continue; continue;
if (mod.type() == Mod::MOD_ZIPFILE) if (mod.type() == Mod::MOD_ZIPFILE)
{ {
if (!mergeZipFiles(&zipOut, mod.filename(), addedFiles)) if (!mergeZipFiles(&zipOut, mod.fileinfo(), addedFiles))
{ {
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar."; qCritical() << "Failed to add" << mod.fileinfo().fileName() << "to the jar.";
return false; return false;
} }
} }
else if (mod.type() == Mod::MOD_SINGLEFILE) else if (mod.type() == Mod::MOD_SINGLEFILE)
{ {
// FIXME: buggy - does not work with addedFiles // FIXME: buggy - does not work with addedFiles
auto filename = mod.filename(); auto filename = mod.fileinfo();
if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName()))
{ {
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar."; qCritical() << "Failed to add" << mod.fileinfo().fileName() << "to the jar.";
return false; return false;
} }
addedFiles.insert(filename.fileName()); addedFiles.insert(filename.fileName());
@ -176,7 +176,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
{ {
// untested, but seems to be unused / not possible to reach // untested, but seems to be unused / not possible to reach
// FIXME: buggy - does not work with addedFiles // FIXME: buggy - does not work with addedFiles
auto filename = mod.filename(); auto filename = mod.fileinfo();
QString what_to_zip = filename.absoluteFilePath(); QString what_to_zip = filename.absoluteFilePath();
QDir dir(what_to_zip); QDir dir(what_to_zip);
dir.cdUp(); dir.cdUp();
@ -193,7 +193,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
{ {
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar."; qCritical() << "Failed to add" << mod.fileinfo().fileName() << "to the jar.";
return false; return false;
} }
qDebug() << "Adding folder " << filename.fileName() << " from " qDebug() << "Adding folder " << filename.fileName() << " from "
@ -204,7 +204,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
// Make sure we do not continue launching when something is missing or undefined... // Make sure we do not continue launching when something is missing or undefined...
zipOut.close(); zipOut.close();
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to add unknown mod type" << mod.filename().fileName() << "to the jar."; qCritical() << "Failed to add unknown mod type" << mod.fileinfo().fileName() << "to the jar.";
return false; return false;
} }
} }
@ -305,7 +305,7 @@ nonstd::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString &
QString path; QString path;
if(name.contains('/') && !name.endsWith('/')){ if(name.contains('/') && !name.endsWith('/')){
path = name.section('/', 0, -2) + "/"; path = name.section('/', 0, -2) + "/";
FS::ensureFolderPathExists(path); FS::ensureFolderPathExists(FS::PathCombine(target, path));
name = name.split('/').last(); name = name.split('/').last();
} }
@ -421,7 +421,7 @@ bool MMCZip::collectFileListRecursively(const QString& rootDir, const QString& s
continue; continue;
} }
files->append(e.filePath()); // we want the original paths for MMCZip::compressDirFiles files->append(e); // we want the original paths for MMCZip::compressDirFiles
} }
return true; return true;
} }

View File

@ -1,24 +1,50 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* 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 "ModDownloadTask.h" #include "ModDownloadTask.h"
#include "Application.h" #include "Application.h"
#include "minecraft/mod/ModFolderModel.h"
ModDownloadTask::ModDownloadTask(const QUrl sourceUrl,const QString filename, const std::shared_ptr<ModFolderModel> mods) ModDownloadTask::ModDownloadTask(ModPlatform::IndexedPack mod, ModPlatform::IndexedVersion version, const std::shared_ptr<ModFolderModel> mods, bool is_indexed)
: m_sourceUrl(sourceUrl), mods(mods), filename(filename) { : m_mod(mod), m_mod_version(version), mods(mods)
} {
if (is_indexed) {
m_update_task.reset(new LocalModUpdateTask(mods->indexDir(), m_mod, m_mod_version));
void ModDownloadTask::executeTask() { addTask(m_update_task);
setStatus(tr("Downloading mod:\n%1").arg(m_sourceUrl.toString())); }
m_filesNetJob.reset(new NetJob(tr("Mod download"), APPLICATION->network())); m_filesNetJob.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
m_filesNetJob->addNetAction(Net::Download::makeFile(m_sourceUrl, mods->dir().absoluteFilePath(filename))); m_filesNetJob->setStatus(tr("Downloading mod:\n%1").arg(m_mod_version.downloadUrl));
m_filesNetJob->addNetAction(Net::Download::makeFile(m_mod_version.downloadUrl, mods->dir().absoluteFilePath(getFilename())));
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ModDownloadTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ModDownloadTask::downloadSucceeded);
connect(m_filesNetJob.get(), &NetJob::progress, this, &ModDownloadTask::downloadProgressChanged); connect(m_filesNetJob.get(), &NetJob::progress, this, &ModDownloadTask::downloadProgressChanged);
connect(m_filesNetJob.get(), &NetJob::failed, this, &ModDownloadTask::downloadFailed); connect(m_filesNetJob.get(), &NetJob::failed, this, &ModDownloadTask::downloadFailed);
m_filesNetJob->start();
addTask(m_filesNetJob);
} }
void ModDownloadTask::downloadSucceeded() void ModDownloadTask::downloadSucceeded()
{ {
emitSucceeded();
m_filesNetJob.reset(); m_filesNetJob.reset();
} }
@ -32,8 +58,3 @@ void ModDownloadTask::downloadProgressChanged(qint64 current, qint64 total)
{ {
emit progress(current, total); emit progress(current, total);
} }
bool ModDownloadTask::abort() {
return m_filesNetJob->abort();
}

View File

@ -1,28 +1,45 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once #pragma once
#include "QObjectPtr.h"
#include "tasks/Task.h"
#include "minecraft/mod/ModFolderModel.h"
#include "net/NetJob.h" #include "net/NetJob.h"
#include <QUrl> #include "tasks/SequentialTask.h"
#include "modplatform/ModIndex.h"
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
class ModDownloadTask : public Task { class ModFolderModel;
class ModDownloadTask : public SequentialTask {
Q_OBJECT Q_OBJECT
public: public:
explicit ModDownloadTask(const QUrl sourceUrl, const QString filename, const std::shared_ptr<ModFolderModel> mods); explicit ModDownloadTask(ModPlatform::IndexedPack mod, ModPlatform::IndexedVersion version, const std::shared_ptr<ModFolderModel> mods, bool is_indexed);
const QString& getFilename() const { return filename; } const QString& getFilename() const { return m_mod_version.fileName; }
public slots:
bool abort() override;
protected:
//! Entry point for tasks.
void executeTask() override;
private: private:
QUrl m_sourceUrl; ModPlatform::IndexedPack m_mod;
NetJob::Ptr m_filesNetJob; ModPlatform::IndexedVersion m_mod_version;
const std::shared_ptr<ModFolderModel> mods; const std::shared_ptr<ModFolderModel> mods;
const QString filename;
NetJob::Ptr m_filesNetJob;
LocalModUpdateTask::Ptr m_update_task;
void downloadProgressChanged(qint64 current, qint64 total); void downloadProgressChanged(qint64 current, qint64 total);

View File

@ -39,6 +39,10 @@ public:
{ {
return QProcessEnvironment(); return QProcessEnvironment();
} }
QProcessEnvironment createLaunchEnvironment() override
{
return QProcessEnvironment();
}
QMap<QString, QString> getVariables() const override QMap<QString, QString> getVariables() const override
{ {
return QMap<QString, QString>(); return QMap<QString, QString>();

View File

@ -1,6 +1,42 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once #pragma once
#include <QString> #include <QString>
#include <QStringView>
#include <QList> #include <QList>
class QUrl; class QUrl;
@ -39,13 +75,21 @@ private:
break; break;
} }
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
auto numPart = QStringView{m_fullString}.left(cutoff);
#else
auto numPart = m_fullString.leftRef(cutoff); auto numPart = m_fullString.leftRef(cutoff);
#endif
if(numPart.size()) if(numPart.size())
{ {
numValid = true; numValid = true;
m_numPart = numPart.toInt(); m_numPart = numPart.toInt();
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
auto stringPart = QStringView{m_fullString}.mid(cutoff);
#else
auto stringPart = m_fullString.midRef(cutoff); auto stringPart = m_fullString.midRef(cutoff);
#endif
if(stringPart.size()) if(stringPart.size())
{ {
m_stringPart = stringPart.toString(); m_stringPart = stringPart.toString();

View File

@ -1,3 +1,38 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VersionProxyModel.h" #include "VersionProxyModel.h"
#include "Application.h" #include "Application.h"
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
@ -208,7 +243,8 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
{ {
return APPLICATION->getThemedIcon("bug"); return APPLICATION->getThemedIcon("bug");
} }
auto pixmap = QPixmapCache::find("placeholder"); QPixmap pixmap;
QPixmapCache::find("placeholder", &pixmap);
if(!pixmap) if(!pixmap)
{ {
QPixmap px(16,16); QPixmap px(16,16);
@ -216,7 +252,7 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
QPixmapCache::insert("placeholder", px); QPixmapCache::insert("placeholder", px);
return px; return px;
} }
return *pixmap; return pixmap;
} }
} }
default: default:

View File

@ -1,4 +1,24 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -56,6 +76,15 @@ IconList::IconList(const QStringList &builtinPaths, QString path, QObject *paren
emit iconUpdated({}); emit iconUpdated({});
} }
void IconList::sortIconList()
{
qDebug() << "Sorting icon list...";
std::sort(icons.begin(), icons.end(), [](const MMCIcon& a, const MMCIcon& b) {
return a.m_key.localeAwareCompare(b.m_key) < 0;
});
reindex();
}
void IconList::directoryChanged(const QString &path) void IconList::directoryChanged(const QString &path)
{ {
QDir new_dir (path); QDir new_dir (path);
@ -77,7 +106,11 @@ void IconList::directoryChanged(const QString &path)
QString &foo = (*it); QString &foo = (*it);
foo = m_dir.filePath(foo); foo = m_dir.filePath(foo);
} }
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QSet<QString> new_set(new_list.begin(), new_list.end());
#else
auto new_set = new_list.toSet(); auto new_set = new_list.toSet();
#endif
QList<QString> current_list; QList<QString> current_list;
for (auto &it : icons) for (auto &it : icons)
{ {
@ -85,7 +118,11 @@ void IconList::directoryChanged(const QString &path)
continue; continue;
current_list.push_back(it.m_images[IconType::FileBased].filename); current_list.push_back(it.m_images[IconType::FileBased].filename);
} }
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QSet<QString> current_set(current_list.begin(), current_list.end());
#else
QSet<QString> current_set = current_list.toSet(); QSet<QString> current_set = current_list.toSet();
#endif
QSet<QString> to_remove = current_set; QSet<QString> to_remove = current_set;
to_remove -= new_set; to_remove -= new_set;
@ -141,6 +178,8 @@ void IconList::directoryChanged(const QString &path)
emit iconUpdated(key); emit iconUpdated(key);
} }
} }
sortIconList();
} }
void IconList::fileChanged(const QString &path) void IconList::fileChanged(const QString &path)
@ -273,7 +312,7 @@ void IconList::installIcons(const QStringList &iconFiles)
QFileInfo fileinfo(file); QFileInfo fileinfo(file);
if (!fileinfo.isReadable() || !fileinfo.isFile()) if (!fileinfo.isReadable() || !fileinfo.isFile())
continue; continue;
QString target = FS::PathCombine(m_dir.dirName(), fileinfo.fileName()); QString target = FS::PathCombine(getDirectory(), fileinfo.fileName());
QString suffix = fileinfo.suffix(); QString suffix = fileinfo.suffix();
if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif") if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif")
@ -290,7 +329,7 @@ void IconList::installIcon(const QString &file, const QString &name)
if(!fileinfo.isReadable() || !fileinfo.isFile()) if(!fileinfo.isReadable() || !fileinfo.isFile())
return; return;
QString target = FS::PathCombine(m_dir.dirName(), name); QString target = FS::PathCombine(getDirectory(), name);
QFile::copy(file, target); QFile::copy(file, target);
} }

View File

@ -71,6 +71,7 @@ private:
// hide assign op // hide assign op
IconList &operator=(const IconList &) = delete; IconList &operator=(const IconList &) = delete;
void reindex(); void reindex();
void sortIconList();
public slots: public slots:
void directoryChanged(const QString &path); void directoryChanged(const QString &path);

View File

@ -1,4 +1,24 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,7 +35,7 @@
#include "MMCIcon.h" #include "MMCIcon.h"
#include <QFileInfo> #include <QFileInfo>
#include <xdgicon.h> #include <QIcon>
IconType operator--(IconType &t, int) IconType operator--(IconType &t, int)
{ {
@ -63,7 +83,7 @@ QIcon MMCIcon::icon() const
if(!icon.isNull()) if(!icon.isNull())
return icon; return icon;
// FIXME: inject this. // FIXME: inject this.
return XdgIcon::fromTheme(m_images[m_current_type].key); return QIcon::fromTheme(m_images[m_current_type].key);
} }
void MMCIcon::remove(IconType rm_type) void MMCIcon::remove(IconType rm_type)

View File

@ -1,3 +1,38 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "JavaChecker.h" #include "JavaChecker.h"
#include <QFile> #include <QFile>
@ -16,7 +51,13 @@ JavaChecker::JavaChecker(QObject *parent) : QObject(parent)
void JavaChecker::performCheck() void JavaChecker::performCheck()
{ {
QString checkerJar = FS::PathCombine(APPLICATION->getJarsPath(), "JavaCheck.jar"); QString checkerJar = JavaUtils::getJavaCheckPath();
if (checkerJar.isEmpty())
{
qDebug() << "Java checker library could not be found. Please check your installation.";
return;
}
QStringList args; QStringList args;
@ -47,7 +88,11 @@ void JavaChecker::performCheck()
qDebug() << "Running java checker: " + m_path + args.join(" ");; qDebug() << "Running java checker: " + m_path + args.join(" ");;
connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus))); connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus)));
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
connect(process.get(), SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError)));
#else
connect(process.get(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError))); connect(process.get(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError)));
#endif
connect(process.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(stdoutReady())); connect(process.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(stdoutReady()));
connect(process.get(), SIGNAL(readyReadStandardError()), this, SLOT(stderrReady())); connect(process.get(), SIGNAL(readyReadStandardError()), this, SLOT(stderrReady()));
connect(&killTimer, SIGNAL(timeout()), SLOT(timeout())); connect(&killTimer, SIGNAL(timeout()), SLOT(timeout()));
@ -99,7 +144,12 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
bool success = true; bool success = true;
QMap<QString, QString> results; QMap<QString, QString> results;
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QStringList lines = m_stdout.split("\n", Qt::SkipEmptyParts);
#else
QStringList lines = m_stdout.split("\n", QString::SkipEmptyParts); QStringList lines = m_stdout.split("\n", QString::SkipEmptyParts);
#endif
for(QString line : lines) for(QString line : lines)
{ {
line = line.trimmed(); line = line.trimmed();
@ -108,7 +158,11 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
continue; continue;
} }
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
auto parts = line.split('=', Qt::SkipEmptyParts);
#else
auto parts = line.split('=', QString::SkipEmptyParts); auto parts = line.split('=', QString::SkipEmptyParts);
#endif
if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty()) if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty())
{ {
continue; continue;

View File

@ -1,4 +1,24 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,7 +35,6 @@
#include <QtNetwork> #include <QtNetwork>
#include <QtXml> #include <QtXml>
#include <QRegExp>
#include <QDebug> #include <QDebug>
@ -81,7 +100,7 @@ QVariant JavaInstallList::data(const QModelIndex &index, int role) const
switch (role) switch (role)
{ {
case VersionPointerRole: case VersionPointerRole:
return qVariantFromValue(m_vlist[index.row()]); return QVariant::fromValue(m_vlist[index.row()]);
case VersionIdRole: case VersionIdRole:
return version->descriptor(); return version->descriptor();
case VersionRole: case VersionRole:

View File

@ -1,4 +1,24 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -24,6 +44,7 @@
#include "java/JavaUtils.h" #include "java/JavaUtils.h"
#include "java/JavaInstallList.h" #include "java/JavaInstallList.h"
#include "FileSystem.h" #include "FileSystem.h"
#include "Application.h"
#define IBUS "@im=ibus" #define IBUS "@im=ibus"
@ -176,25 +197,25 @@ QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
archType = "32"; archType = "32";
HKEY jreKey; HKEY jreKey;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName.toStdString().c_str(), 0, if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyName.toStdWString().c_str(), 0,
KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == ERROR_SUCCESS) KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == ERROR_SUCCESS)
{ {
// Read the current type version from the registry. // Read the current type version from the registry.
// This will be used to find any key that contains the JavaHome value. // This will be used to find any key that contains the JavaHome value.
char *value = new char[0]; char *value = new char[0];
DWORD valueSz = 0; DWORD valueSz = 0;
if (RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz) == if (RegQueryValueExW(jreKey, L"CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz) ==
ERROR_MORE_DATA) ERROR_MORE_DATA)
{ {
value = new char[valueSz]; value = new char[valueSz];
RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz); RegQueryValueExW(jreKey, L"CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz);
} }
TCHAR subKeyName[255]; TCHAR subKeyName[255];
DWORD subKeyNameSize, numSubKeys, retCode; DWORD subKeyNameSize, numSubKeys, retCode;
// Get the number of subkeys // Get the number of subkeys
RegQueryInfoKey(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, RegQueryInfoKeyW(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL,
NULL, NULL); NULL, NULL);
// Iterate until RegEnumKeyEx fails // Iterate until RegEnumKeyEx fails
@ -203,31 +224,32 @@ QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
for (DWORD i = 0; i < numSubKeys; i++) for (DWORD i = 0; i < numSubKeys; i++)
{ {
subKeyNameSize = 255; subKeyNameSize = 255;
retCode = RegEnumKeyEx(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL, retCode = RegEnumKeyExW(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL,
NULL); NULL);
QString newSubkeyName = QString::fromWCharArray(subKeyName);
if (retCode == ERROR_SUCCESS) if (retCode == ERROR_SUCCESS)
{ {
// Now open the registry key for the version that we just got. // Now open the registry key for the version that we just got.
QString newKeyName = keyName + "\\" + subKeyName + subkeySuffix; QString newKeyName = keyName + "\\" + newSubkeyName + subkeySuffix;
HKEY newKey; HKEY newKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, newKeyName.toStdString().c_str(), 0, if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, newKeyName.toStdWString().c_str(), 0,
KEY_READ | KEY_WOW64_64KEY, &newKey) == ERROR_SUCCESS) KEY_READ | KEY_WOW64_64KEY, &newKey) == ERROR_SUCCESS)
{ {
// Read the JavaHome value to find where Java is installed. // Read the JavaHome value to find where Java is installed.
value = new char[0]; value = new char[0];
valueSz = 0; valueSz = 0;
if (RegQueryValueEx(newKey, keyJavaDir.toStdString().c_str(), NULL, NULL, (BYTE *)value, if (RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, (BYTE *)value,
&valueSz) == ERROR_MORE_DATA) &valueSz) == ERROR_MORE_DATA)
{ {
value = new char[valueSz]; value = new char[valueSz];
RegQueryValueEx(newKey, keyJavaDir.toStdString().c_str(), NULL, NULL, (BYTE *)value, RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, (BYTE *)value,
&valueSz); &valueSz);
// Now, we construct the version object and add it to the list. // Now, we construct the version object and add it to the list.
JavaInstallPtr javaVersion(new JavaInstall()); JavaInstallPtr javaVersion(new JavaInstall());
javaVersion->id = subKeyName; javaVersion->id = newSubkeyName;
javaVersion->arch = archType; javaVersion->arch = archType;
javaVersion->path = javaVersion->path =
QDir(FS::PathCombine(value, "bin")).absoluteFilePath("javaw.exe"); QDir(FS::PathCombine(value, "bin")).absoluteFilePath("javaw.exe");
@ -437,3 +459,8 @@ QList<QString> JavaUtils::FindJavaPaths()
return addJavasFromEnv(javas); return addJavasFromEnv(javas);
} }
#endif #endif
QString JavaUtils::getJavaCheckPath()
{
return APPLICATION->getJarPath("JavaCheck.jar");
}

View File

@ -39,4 +39,6 @@ public:
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
QList<JavaInstallPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName, QString keyJavaDir, QString subkeySuffix = ""); QList<JavaInstallPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName, QString keyJavaDir, QString subkeySuffix = "");
#endif #endif
static QString getJavaCheckPath();
}; };

View File

@ -1,5 +1,4 @@
#include <QTest> #include <QTest>
#include "TestUtil.h"
#include "java/JavaVersion.h" #include "java/JavaVersion.h"

View File

@ -282,6 +282,23 @@ void LaunchTask::emitFailed(QString reason)
Task::emitFailed(reason); Task::emitFailed(reason);
} }
void LaunchTask::substituteVariables(const QStringList &args) const
{
auto variables = m_instance->getVariables();
auto envVariables = QProcessEnvironment::systemEnvironment();
for (auto arg : args) {
for (auto key : variables)
{
arg.replace("$" + key, variables.value(key));
}
for (auto env : envVariables.keys())
{
arg.replace("$" + env, envVariables.value(env));
}
}
}
QString LaunchTask::substituteVariables(const QString &cmd) const QString LaunchTask::substituteVariables(const QString &cmd) const
{ {
QString out = cmd; QString out = cmd;

View File

@ -1,4 +1,24 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Authors: Orochimarufan <orochimarufan.x3@gmail.com> * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
* *
@ -85,6 +105,7 @@ public: /* methods */
shared_qobject_ptr<LogModel> getLogModel(); shared_qobject_ptr<LogModel> getLogModel();
public: public:
void substituteVariables(const QStringList &args) const;
QString substituteVariables(const QString &cmd) const; QString substituteVariables(const QString &cmd) const;
QString censorPrivateInfo(QString in); QString censorPrivateInfo(QString in);

View File

@ -34,6 +34,7 @@
*/ */
#include "CheckJava.h" #include "CheckJava.h"
#include "java/JavaUtils.h"
#include <launch/LaunchTask.h> #include <launch/LaunchTask.h>
#include <FileSystem.h> #include <FileSystem.h>
#include <QStandardPaths> #include <QStandardPaths>
@ -71,15 +72,26 @@ void CheckJava::executeTask()
emit logLine("Java path is:\n" + m_javaPath + "\n\n", MessageLevel::Launcher); emit logLine("Java path is:\n" + m_javaPath + "\n\n", MessageLevel::Launcher);
} }
if (JavaUtils::getJavaCheckPath().isEmpty())
{
const char *reason = QT_TR_NOOP("Java checker library could not be found. Please check your installation.");
emit logLine(tr(reason), MessageLevel::Fatal);
emitFailed(tr(reason));
return;
}
QFileInfo javaInfo(realJavaPath); QFileInfo javaInfo(realJavaPath);
qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch(); qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch();
auto storedUnixTime = settings->get("JavaTimestamp").toLongLong(); auto storedUnixTime = settings->get("JavaTimestamp").toLongLong();
auto storedArchitecture = settings->get("JavaArchitecture").toString(); auto storedArchitecture = settings->get("JavaArchitecture").toString();
auto storedRealArchitecture = settings->get("JavaRealArchitecture").toString();
auto storedVersion = settings->get("JavaVersion").toString(); auto storedVersion = settings->get("JavaVersion").toString();
auto storedVendor = settings->get("JavaVendor").toString(); auto storedVendor = settings->get("JavaVendor").toString();
m_javaUnixTime = javaUnixTime; m_javaUnixTime = javaUnixTime;
// if timestamps are not the same, or something is missing, check! // if timestamps are not the same, or something is missing, check!
if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0 || storedVendor.size() == 0) if (javaUnixTime != storedUnixTime || storedVersion.size() == 0
|| storedArchitecture.size() == 0 || storedRealArchitecture.size() == 0
|| storedVendor.size() == 0)
{ {
m_JavaChecker = new JavaChecker(); m_JavaChecker = new JavaChecker();
emit logLine(QString("Checking Java version..."), MessageLevel::Launcher); emit logLine(QString("Checking Java version..."), MessageLevel::Launcher);
@ -92,8 +104,9 @@ void CheckJava::executeTask()
{ {
auto verString = instance->settings()->get("JavaVersion").toString(); auto verString = instance->settings()->get("JavaVersion").toString();
auto archString = instance->settings()->get("JavaArchitecture").toString(); auto archString = instance->settings()->get("JavaArchitecture").toString();
auto realArchString = settings->get("JavaRealArchitecture").toString();
auto vendorString = instance->settings()->get("JavaVendor").toString(); auto vendorString = instance->settings()->get("JavaVendor").toString();
printJavaInfo(verString, archString, vendorString); printJavaInfo(verString, archString, realArchString, vendorString);
} }
emitSucceeded(); emitSucceeded();
} }
@ -124,10 +137,11 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
case JavaCheckResult::Validity::Valid: case JavaCheckResult::Validity::Valid:
{ {
auto instance = m_parent->instance(); auto instance = m_parent->instance();
printJavaInfo(result.javaVersion.toString(), result.realPlatform, result.javaVendor); printJavaInfo(result.javaVersion.toString(), result.mojangPlatform, result.realPlatform, result.javaVendor);
printSystemInfo(true, result.is_64bit); printSystemInfo(true, result.is_64bit);
instance->settings()->set("JavaVersion", result.javaVersion.toString()); instance->settings()->set("JavaVersion", result.javaVersion.toString());
instance->settings()->set("JavaArchitecture", result.mojangPlatform); instance->settings()->set("JavaArchitecture", result.mojangPlatform);
instance->settings()->set("JavaRealArchitecture", result.realPlatform);
instance->settings()->set("JavaVendor", result.javaVendor); instance->settings()->set("JavaVendor", result.javaVendor);
instance->settings()->set("JavaTimestamp", m_javaUnixTime); instance->settings()->set("JavaTimestamp", m_javaUnixTime);
emitSucceeded(); emitSucceeded();
@ -136,9 +150,10 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
} }
} }
void CheckJava::printJavaInfo(const QString& version, const QString& architecture, const QString & vendor) void CheckJava::printJavaInfo(const QString& version, const QString& architecture, const QString& realArchitecture, const QString & vendor)
{ {
emit logLine(QString("Java is version %1, using %2 architecture, from %3.\n\n").arg(version, architecture, vendor), MessageLevel::Launcher); emit logLine(QString("Java is version %1, using %2 (%3) architecture, from %4.\n\n")
.arg(version, architecture, realArchitecture, vendor), MessageLevel::Launcher);
} }
void CheckJava::printSystemInfo(bool javaIsKnown, bool javaIs64bit) void CheckJava::printSystemInfo(bool javaIsKnown, bool javaIs64bit)

View File

@ -35,7 +35,7 @@ private slots:
void checkJavaFinished(JavaCheckResult result); void checkJavaFinished(JavaCheckResult result);
private: private:
void printJavaInfo(const QString & version, const QString & architecture, const QString & vendor); void printJavaInfo(const QString & version, const QString & architecture, const QString & realArchitecture, const QString & vendor);
void printSystemInfo(bool javaIsKnown, bool javaIs64bit); void printSystemInfo(bool javaIsKnown, bool javaIs64bit);
private: private:

View File

@ -1,4 +1,24 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -27,9 +47,19 @@ PostLaunchCommand::PostLaunchCommand(LaunchTask *parent) : LaunchStep(parent)
void PostLaunchCommand::executeTask() void PostLaunchCommand::executeTask()
{ {
//FIXME: where to put this?
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
auto args = QProcess::splitCommand(m_command);
m_parent->substituteVariables(args);
emit logLine(tr("Running Post-Launch command: %1").arg(args.join(' ')), MessageLevel::Launcher);
const QString program = args.takeFirst();
m_process.start(program, args);
#else
QString postlaunch_cmd = m_parent->substituteVariables(m_command); QString postlaunch_cmd = m_parent->substituteVariables(m_command);
emit logLine(tr("Running Post-Launch command: %1").arg(postlaunch_cmd), MessageLevel::Launcher); emit logLine(tr("Running Post-Launch command: %1").arg(postlaunch_cmd), MessageLevel::Launcher);
m_process.start(postlaunch_cmd); m_process.start(postlaunch_cmd);
#endif
} }
void PostLaunchCommand::on_state(LoggedProcess::State state) void PostLaunchCommand::on_state(LoggedProcess::State state)

View File

@ -1,4 +1,24 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -28,9 +48,18 @@ PreLaunchCommand::PreLaunchCommand(LaunchTask *parent) : LaunchStep(parent)
void PreLaunchCommand::executeTask() void PreLaunchCommand::executeTask()
{ {
//FIXME: where to put this? //FIXME: where to put this?
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
auto args = QProcess::splitCommand(m_command);
m_parent->substituteVariables(args);
emit logLine(tr("Running Pre-Launch command: %1").arg(args.join(' ')), MessageLevel::Launcher);
const QString program = args.takeFirst();
m_process.start(program, args);
#else
QString prelaunch_cmd = m_parent->substituteVariables(m_command); QString prelaunch_cmd = m_parent->substituteVariables(m_command);
emit logLine(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd), MessageLevel::Launcher); emit logLine(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd), MessageLevel::Launcher);
m_process.start(prelaunch_cmd); m_process.start(prelaunch_cmd);
#endif
} }
void PreLaunchCommand::on_state(LoggedProcess::State state) void PreLaunchCommand::on_state(LoggedProcess::State state)

View File

@ -1,3 +1,38 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Application.h" #include "Application.h"
// #define BREAK_INFINITE_LOOP // #define BREAK_INFINITE_LOOP
@ -24,11 +59,9 @@ int main(int argc, char *argv[])
return 42; return 42;
#endif #endif
#if QT_VERSION <= QT_VERSION_CHECK(6, 0, 0)
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif #endif
// initialize Qt // initialize Qt

View File

@ -75,7 +75,16 @@ Meta::BaseEntity::~BaseEntity()
QUrl Meta::BaseEntity::url() const QUrl Meta::BaseEntity::url() const
{ {
auto s = APPLICATION->settings();
QString metaOverride = s->get("MetaURLOverride").toString();
if(metaOverride.isEmpty())
{
return QUrl(BuildConfig.META_URL).resolved(localFilename()); return QUrl(BuildConfig.META_URL).resolved(localFilename());
}
else
{
return QUrl(metaOverride).resolved(localFilename());
}
} }
bool Meta::BaseEntity::loadLocalFile() bool Meta::BaseEntity::loadLocalFile()

View File

@ -1,5 +1,4 @@
#include <QTest> #include <QTest>
#include "TestUtil.h"
#include "meta/Index.h" #include "meta/Index.h"
#include "meta/VersionList.h" #include "meta/VersionList.h"

View File

@ -1,4 +1,24 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -297,7 +317,7 @@ NetAction::Ptr AssetObject::getDownloadAction()
auto rawHash = QByteArray::fromHex(hash.toLatin1()); auto rawHash = QByteArray::fromHex(hash.toLatin1());
objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash)); objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash));
} }
objectDL->m_total_progress = size; objectDL->setProgress(objectDL->getProgress(), size);
return objectDL; return objectDL;
} }
return nullptr; return nullptr;

View File

@ -197,6 +197,10 @@ void ComponentUpdateTask::loadComponents()
{ {
remoteLoadFailed(taskIndex, error); remoteLoadFailed(taskIndex, error);
}); });
connect(indexLoadTask.get(), &Task::aborted, [=]()
{
remoteLoadFailed(taskIndex, tr("Aborted"));
});
taskIndex++; taskIndex++;
} }
} }
@ -243,6 +247,10 @@ void ComponentUpdateTask::loadComponents()
{ {
remoteLoadFailed(taskIndex, error); remoteLoadFailed(taskIndex, error);
}); });
connect(loadTask.get(), &Task::aborted, [=]()
{
remoteLoadFailed(taskIndex, tr("Aborted"));
});
RemoteLoadStatus status; RemoteLoadStatus status;
status.type = loadType; status.type = loadType;
status.PackProfileIndex = componentIndex; status.PackProfileIndex = componentIndex;

View File

@ -1,7 +1,43 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once #pragma once
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QRegularExpression>
#include "DefaultVariable.h" #include "DefaultVariable.h"
struct GradleSpecifier struct GradleSpecifier
@ -25,20 +61,21 @@ struct GradleSpecifier
4 "jdk15" 4 "jdk15"
5 "jar" 5 "jar"
*/ */
QRegExp matcher("([^:@]+):([^:@]+):([^:@]+)" "(?::([^:@]+))?" "(?:@([^:@]+))?"); QRegularExpression matcher(QRegularExpression::anchoredPattern("([^:@]+):([^:@]+):([^:@]+)" "(?::([^:@]+))?" "(?:@([^:@]+))?"));
m_valid = matcher.exactMatch(value); QRegularExpressionMatch match = matcher.match(value);
m_valid = match.hasMatch();
if(!m_valid) { if(!m_valid) {
m_invalidValue = value; m_invalidValue = value;
return *this; return *this;
} }
auto elements = matcher.capturedTexts(); auto elements = match.captured();
m_groupId = elements[1]; m_groupId = match.captured(1);
m_artifactId = elements[2]; m_artifactId = match.captured(2);
m_version = elements[3]; m_version = match.captured(3);
m_classifier = elements[4]; m_classifier = match.captured(4);
if(!elements[5].isEmpty()) if(match.lastCapturedIndex() >= 5)
{ {
m_extension = elements[5]; m_extension = match.captured(5);
} }
return *this; return *this;
} }

View File

@ -1,5 +1,4 @@
#include <QTest> #include <QTest>
#include "TestUtil.h"
#include "minecraft/GradleSpecifier.h" #include "minecraft/GradleSpecifier.h"

View File

@ -1,5 +1,4 @@
#include <QTest> #include <QTest>
#include "TestUtil.h"
#include "minecraft/MojangVersionFormat.h" #include "minecraft/MojangVersionFormat.h"
#include "minecraft/OneSixVersionFormat.h" #include "minecraft/OneSixVersionFormat.h"
@ -11,15 +10,14 @@ class LibraryTest : public QObject
{ {
Q_OBJECT Q_OBJECT
private: private:
LibraryPtr readMojangJson(const char *file) LibraryPtr readMojangJson(const QString path)
{ {
auto path = QFINDTESTDATA(file);
QFile jsonFile(path); QFile jsonFile(path);
jsonFile.open(QIODevice::ReadOnly); jsonFile.open(QIODevice::ReadOnly);
auto data = jsonFile.readAll(); auto data = jsonFile.readAll();
jsonFile.close(); jsonFile.close();
ProblemContainer problems; ProblemContainer problems;
return MojangVersionFormat::libraryFromJson(problems, QJsonDocument::fromJson(data).object(), file); return MojangVersionFormat::libraryFromJson(problems, QJsonDocument::fromJson(data).object(), path);
} }
// get absolute path to expected storage, assuming default cache prefix // get absolute path to expected storage, assuming default cache prefix
QStringList getStorage(QString relative) QStringList getStorage(QString relative)
@ -32,7 +30,7 @@ slots:
{ {
cache.reset(new HttpMetaCache()); cache.reset(new HttpMetaCache());
cache->addBase("libraries", QDir("libraries").absolutePath()); cache->addBase("libraries", QDir("libraries").absolutePath());
dataDir = QDir("data").absolutePath(); dataDir = QDir(QFINDTESTDATA("testdata")).absolutePath();
} }
void test_legacy() void test_legacy()
{ {
@ -74,14 +72,14 @@ slots:
QCOMPARE(test.isNative(), false); QCOMPARE(test.isNative(), false);
QStringList failedFiles; QStringList failedFiles;
test.setHint("local"); test.setHint("local");
auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString("data")); auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QFINDTESTDATA("testdata"));
QCOMPARE(downloads.size(), 0); QCOMPARE(downloads.size(), 0);
qDebug() << failedFiles; qDebug() << failedFiles;
QCOMPARE(failedFiles.size(), 0); QCOMPARE(failedFiles.size(), 0);
QStringList jar, native, native32, native64; QStringList jar, native, native32, native64;
test.getApplicableFiles(currentSystem, jar, native, native32, native64, QString("data")); test.getApplicableFiles(currentSystem, jar, native, native32, native64, QFINDTESTDATA("testdata"));
QCOMPARE(jar, {QFileInfo("data/codecwav-20101023.jar").absoluteFilePath()}); QCOMPARE(jar, {QFileInfo(QFINDTESTDATA("testdata/codecwav-20101023.jar")).absoluteFilePath()});
QCOMPARE(native, {}); QCOMPARE(native, {});
QCOMPARE(native32, {}); QCOMPARE(native32, {});
QCOMPARE(native64, {}); QCOMPARE(native64, {});
@ -167,20 +165,20 @@ slots:
test.setRepositoryURL("file://foo/bar"); test.setRepositoryURL("file://foo/bar");
{ {
QStringList jar, native, native32, native64; QStringList jar, native, native32, native64;
test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QString("data")); test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QFINDTESTDATA("testdata"));
QCOMPARE(jar, {}); QCOMPARE(jar, {});
QCOMPARE(native, {}); QCOMPARE(native, {});
QCOMPARE(native32, {QFileInfo("data/testname-testversion-linux-32.jar").absoluteFilePath()}); QCOMPARE(native32, {QFileInfo(QFINDTESTDATA("testdata/testname-testversion-linux-32.jar")).absoluteFilePath()});
QCOMPARE(native64, {QFileInfo("data/testname-testversion-linux-64.jar").absoluteFilePath()}); QCOMPARE(native64, {QFileInfo(QFINDTESTDATA("testdata") + "/testname-testversion-linux-64.jar").absoluteFilePath()});
QStringList failedFiles; QStringList failedFiles;
auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString("data")); auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QFINDTESTDATA("testdata"));
QCOMPARE(dls.size(), 0); QCOMPARE(dls.size(), 0);
QCOMPARE(failedFiles, {"data/testname-testversion-linux-64.jar"}); QCOMPARE(failedFiles, {QFileInfo(QFINDTESTDATA("testdata") + "/testname-testversion-linux-64.jar").absoluteFilePath()});
} }
} }
void test_onenine() void test_onenine()
{ {
auto test = readMojangJson("data/lib-simple.json"); auto test = readMojangJson(QFINDTESTDATA("testdata/lib-simple.json"));
{ {
QStringList jar, native, native32, native64; QStringList jar, native, native32, native64;
test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString()); test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString());
@ -199,41 +197,41 @@ slots:
test->setHint("local"); test->setHint("local");
{ {
QStringList jar, native, native32, native64; QStringList jar, native, native32, native64;
test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString("data")); test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QFINDTESTDATA("testdata"));
QCOMPARE(jar, {QFileInfo("data/codecwav-20101023.jar").absoluteFilePath()}); QCOMPARE(jar, {QFileInfo(QFINDTESTDATA("testdata/codecwav-20101023.jar")).absoluteFilePath()});
QCOMPARE(native, {}); QCOMPARE(native, {});
QCOMPARE(native32, {}); QCOMPARE(native32, {});
QCOMPARE(native64, {}); QCOMPARE(native64, {});
} }
{ {
QStringList failedFiles; QStringList failedFiles;
auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString("data")); auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QFINDTESTDATA("testdata"));
QCOMPARE(dls.size(), 0); QCOMPARE(dls.size(), 0);
QCOMPARE(failedFiles, {}); QCOMPARE(failedFiles, {});
} }
} }
void test_onenine_local_override() void test_onenine_local_override()
{ {
auto test = readMojangJson("data/lib-simple.json"); auto test = readMojangJson(QFINDTESTDATA("testdata/lib-simple.json"));
test->setHint("local"); test->setHint("local");
{ {
QStringList jar, native, native32, native64; QStringList jar, native, native32, native64;
test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString("data")); test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QFINDTESTDATA("testdata"));
QCOMPARE(jar, {QFileInfo("data/codecwav-20101023.jar").absoluteFilePath()}); QCOMPARE(jar, {QFileInfo(QFINDTESTDATA("testdata/codecwav-20101023.jar")).absoluteFilePath()});
QCOMPARE(native, {}); QCOMPARE(native, {});
QCOMPARE(native32, {}); QCOMPARE(native32, {});
QCOMPARE(native64, {}); QCOMPARE(native64, {});
} }
{ {
QStringList failedFiles; QStringList failedFiles;
auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString("data")); auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QFINDTESTDATA("testdata"));
QCOMPARE(dls.size(), 0); QCOMPARE(dls.size(), 0);
QCOMPARE(failedFiles, {}); QCOMPARE(failedFiles, {});
} }
} }
void test_onenine_native() void test_onenine_native()
{ {
auto test = readMojangJson("data/lib-native.json"); auto test = readMojangJson(QFINDTESTDATA("testdata/lib-native.json"));
QStringList jar, native, native32, native64; QStringList jar, native, native32, native64;
test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString()); test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString());
QCOMPARE(jar, QStringList()); QCOMPARE(jar, QStringList());
@ -248,7 +246,7 @@ slots:
} }
void test_onenine_native_arch() void test_onenine_native_arch()
{ {
auto test = readMojangJson("data/lib-native-arch.json"); auto test = readMojangJson(QFINDTESTDATA("testdata/lib-native-arch.json"));
QStringList jar, native, native32, native64; QStringList jar, native, native32, native64;
test->getApplicableFiles(Os_Windows, jar, native, native32, native64, QString()); test->getApplicableFiles(Os_Windows, jar, native, native32, native64, QString());
QCOMPARE(jar, {}); QCOMPARE(jar, {});

View File

@ -2,6 +2,7 @@
/* /*
* PolyMC - Minecraft Launcher * PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -153,6 +154,12 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO
m_settings->registerOverride(globalSettings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride); m_settings->registerOverride(globalSettings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride);
m_settings->registerOverride(globalSettings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride); m_settings->registerOverride(globalSettings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride);
// Peformance related options
auto performanceOverride = m_settings->registerSetting("OverridePerformance", false);
m_settings->registerOverride(globalSettings->getSetting("EnableFeralGamemode"), performanceOverride);
m_settings->registerOverride(globalSettings->getSetting("EnableMangoHud"), performanceOverride);
m_settings->registerOverride(globalSettings->getSetting("UseDiscreteGpu"), performanceOverride);
// Game time // Game time
auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false); auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false);
m_settings->registerOverride(globalSettings->getSetting("ShowGameTime"), gameTimeOverride); m_settings->registerOverride(globalSettings->getSetting("ShowGameTime"), gameTimeOverride);
@ -167,6 +174,8 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO
m_settings->registerOverride(globalSettings->getSetting("CloseAfterLaunch"), miscellaneousOverride); m_settings->registerOverride(globalSettings->getSetting("CloseAfterLaunch"), miscellaneousOverride);
m_settings->registerOverride(globalSettings->getSetting("QuitAfterGameStop"), miscellaneousOverride); m_settings->registerOverride(globalSettings->getSetting("QuitAfterGameStop"), miscellaneousOverride);
m_settings->set("InstanceType", "OneSix");
m_components.reset(new PackProfile(this)); m_components.reset(new PackProfile(this));
} }
@ -432,27 +441,57 @@ QProcessEnvironment MinecraftInstance::createEnvironment()
return env; return env;
} }
QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
{
// prepare the process environment
QProcessEnvironment env = createEnvironment();
#ifdef Q_OS_LINUX
if (settings()->get("EnableMangoHud").toBool())
{
auto preload = env.value("LD_PRELOAD", "") + ":libMangoHud_dlsym.so:libMangoHud.so";
auto lib_path = env.value("LD_LIBRARY_PATH", "") + ":/usr/local/$LIB/mangohud/:/usr/$LIB/mangohud/";
env.insert("LD_PRELOAD", preload);
env.insert("LD_LIBRARY_PATH", lib_path);
env.insert("MANGOHUD", "1");
}
if (settings()->get("UseDiscreteGpu").toBool())
{
// Open Source Drivers
env.insert("DRI_PRIME", "1");
// Proprietary Nvidia Drivers
env.insert("__NV_PRIME_RENDER_OFFLOAD", "1");
env.insert("__VK_LAYER_NV_optimus", "NVIDIA_only");
env.insert("__GLX_VENDOR_LIBRARY_NAME", "nvidia");
}
#endif
return env;
}
static QString replaceTokensIn(QString text, QMap<QString, QString> with) static QString replaceTokensIn(QString text, QMap<QString, QString> with)
{ {
// TODO: does this still work??
QString result; QString result;
QRegExp token_regexp("\\$\\{(.+)\\}"); QRegularExpression token_regexp("\\$\\{(.+)\\}", QRegularExpression::InvertedGreedinessOption);
token_regexp.setMinimal(true);
QStringList list; QStringList list;
int tail = 0; QRegularExpressionMatchIterator i = token_regexp.globalMatch(text);
int head = 0; int lastCapturedEnd = 0;
while ((head = token_regexp.indexIn(text, head)) != -1) while (i.hasNext())
{ {
result.append(text.mid(tail, head - tail)); QRegularExpressionMatch match = i.next();
QString key = token_regexp.cap(1); result.append(text.mid(lastCapturedEnd, match.capturedStart()));
QString key = match.captured(1);
auto iter = with.find(key); auto iter = with.find(key);
if (iter != with.end()) if (iter != with.end())
{ {
result.append(*iter); result.append(*iter);
} }
head += token_regexp.matchedLength(); lastCapturedEnd = match.capturedEnd();
tail = head;
} }
result.append(text.mid(tail)); result.append(text.mid(lastCapturedEnd));
return result; return result;
} }
@ -487,9 +526,8 @@ QStringList MinecraftInstance::processMinecraftArgs(
} }
} }
// blatant self-promotion. token_mapping["profile_name"] = name();
token_mapping["profile_name"] = token_mapping["version_name"] = BuildConfig.LAUNCHER_NAME; token_mapping["version_name"] = profile->getMinecraftVersion();
token_mapping["version_type"] = profile->getMinecraftVersionType(); token_mapping["version_type"] = profile->getMinecraftVersionType();
QString absRootDir = QDir(gameRoot()).absolutePath(); QString absRootDir = QDir(gameRoot()).absolutePath();
@ -502,7 +540,11 @@ QStringList MinecraftInstance::processMinecraftArgs(
token_mapping["assets_root"] = absAssetsDir; token_mapping["assets_root"] = absAssetsDir;
token_mapping["assets_index_name"] = assets->id; token_mapping["assets_index_name"] = assets->id;
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QStringList parts = args_pattern.split(' ', Qt::SkipEmptyParts);
#else
QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts); QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts);
#endif
for (int i = 0; i < parts.length(); i++) for (int i = 0; i < parts.length(); i++)
{ {
parts[i] = replaceTokensIn(parts[i], token_mapping); parts[i] = replaceTokensIn(parts[i], token_mapping);
@ -659,23 +701,23 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr
out << QString("%1:").arg(label); out << QString("%1:").arg(label);
auto modList = model.allMods(); auto modList = model.allMods();
std::sort(modList.begin(), modList.end(), [](Mod &a, Mod &b) { std::sort(modList.begin(), modList.end(), [](Mod &a, Mod &b) {
auto aName = a.filename().completeBaseName(); auto aName = a.fileinfo().completeBaseName();
auto bName = b.filename().completeBaseName(); auto bName = b.fileinfo().completeBaseName();
return aName.localeAwareCompare(bName) < 0; return aName.localeAwareCompare(bName) < 0;
}); });
for(auto & mod: modList) for(auto & mod: modList)
{ {
if(mod.type() == Mod::MOD_FOLDER) if(mod.type() == Mod::MOD_FOLDER)
{ {
out << u8" [📁] " + mod.filename().completeBaseName() + " (folder)"; out << u8" [📁] " + mod.fileinfo().completeBaseName() + " (folder)";
continue; continue;
} }
if(mod.enabled()) { if(mod.enabled()) {
out << u8" [✔️] " + mod.filename().completeBaseName(); out << u8" [✔️] " + mod.fileinfo().completeBaseName();
} }
else { else {
out << u8" [❌] " + mod.filename().completeBaseName() + " (disabled)"; out << u8" [❌] " + mod.fileinfo().completeBaseName() + " (disabled)";
} }
} }
@ -745,7 +787,9 @@ QMap<QString, QString> MinecraftInstance::createCensorFilterFromSession(AuthSess
{ {
addToFilter(sessionRef.session, tr("<SESSION ID>")); addToFilter(sessionRef.session, tr("<SESSION ID>"));
} }
if (sessionRef.access_token != "offline") {
addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>")); addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>"));
}
if(sessionRef.client_token.size()) { if(sessionRef.client_token.size()) {
addToFilter(sessionRef.client_token, tr("<CLIENT TOKEN>")); addToFilter(sessionRef.client_token, tr("<CLIENT TOKEN>"));
} }
@ -824,8 +868,16 @@ QString MinecraftInstance::getStatusbarDescription()
traits.append(tr("broken")); traits.append(tr("broken"));
} }
QString mcVersion = m_components->getComponentVersion("net.minecraft");
if (mcVersion.isEmpty())
{
// Load component info if needed
m_components->reload(Net::Mode::Offline);
mcVersion = m_components->getComponentVersion("net.minecraft");
}
QString description; QString description;
description.append(tr("Minecraft %1 (%2)").arg(m_components->getComponentVersion("net.minecraft")).arg(typeName())); description.append(tr("Minecraft %1").arg(mcVersion));
if(m_settings->get("ShowGameTime").toBool()) if(m_settings->get("ShowGameTime").toBool())
{ {
if (lastTimePlayed() > 0) { if (lastTimePlayed() > 0) {
@ -1013,7 +1065,8 @@ std::shared_ptr<ModFolderModel> MinecraftInstance::loaderModList() const
{ {
if (!m_loader_mod_list) if (!m_loader_mod_list)
{ {
m_loader_mod_list.reset(new ModFolderModel(modsRoot())); bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
m_loader_mod_list.reset(new ModFolderModel(modsRoot(), is_indexed));
m_loader_mod_list->disableInteraction(isRunning()); m_loader_mod_list->disableInteraction(isRunning());
connect(this, &BaseInstance::runningStatusChanged, m_loader_mod_list.get(), &ModFolderModel::disableInteraction); connect(this, &BaseInstance::runningStatusChanged, m_loader_mod_list.get(), &ModFolderModel::disableInteraction);
} }
@ -1024,7 +1077,8 @@ std::shared_ptr<ModFolderModel> MinecraftInstance::coreModList() const
{ {
if (!m_core_mod_list) if (!m_core_mod_list)
{ {
m_core_mod_list.reset(new ModFolderModel(coreModsDir())); bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
m_core_mod_list.reset(new ModFolderModel(coreModsDir(), is_indexed));
m_core_mod_list->disableInteraction(isRunning()); m_core_mod_list->disableInteraction(isRunning());
connect(this, &BaseInstance::runningStatusChanged, m_core_mod_list.get(), &ModFolderModel::disableInteraction); connect(this, &BaseInstance::runningStatusChanged, m_core_mod_list.get(), &ModFolderModel::disableInteraction);
} }

View File

@ -91,6 +91,7 @@ public:
/// create an environment for launching processes /// create an environment for launching processes
QProcessEnvironment createEnvironment() override; QProcessEnvironment createEnvironment() override;
QProcessEnvironment createLaunchEnvironment() override;
/// guess log level from a line of minecraft log /// guess log level from a line of minecraft log
MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level) override; MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level) override;

View File

@ -20,6 +20,7 @@ void MinecraftLoadAndCheck::executeTask()
} }
connect(m_task.get(), &Task::succeeded, this, &MinecraftLoadAndCheck::subtaskSucceeded); connect(m_task.get(), &Task::succeeded, this, &MinecraftLoadAndCheck::subtaskSucceeded);
connect(m_task.get(), &Task::failed, this, &MinecraftLoadAndCheck::subtaskFailed); connect(m_task.get(), &Task::failed, this, &MinecraftLoadAndCheck::subtaskFailed);
connect(m_task.get(), &Task::aborted, this, [this]{ subtaskFailed(tr("Aborted")); });
connect(m_task.get(), &Task::progress, this, &MinecraftLoadAndCheck::progress); connect(m_task.get(), &Task::progress, this, &MinecraftLoadAndCheck::progress);
connect(m_task.get(), &Task::status, this, &MinecraftLoadAndCheck::setStatus); connect(m_task.get(), &Task::status, this, &MinecraftLoadAndCheck::setStatus);
} }

View File

@ -98,6 +98,7 @@ void MinecraftUpdate::next()
auto task = m_tasks[m_currentTask - 1]; auto task = m_tasks[m_currentTask - 1];
disconnect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded); disconnect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded);
disconnect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed); disconnect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
disconnect(task.get(), &Task::aborted, this, &Task::abort);
disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress); disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
disconnect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus); disconnect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
} }
@ -115,6 +116,7 @@ void MinecraftUpdate::next()
} }
connect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded); connect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded);
connect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed); connect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
connect(task.get(), &Task::aborted, this, &Task::abort);
connect(task.get(), &Task::progress, this, &MinecraftUpdate::progress); connect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
connect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus); connect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
// if the task is already running, do not start it again // if the task is already running, do not start it again

View File

@ -27,6 +27,8 @@
class MinecraftVersion; class MinecraftVersion;
class MinecraftInstance; class MinecraftInstance;
// FIXME: This looks very similar to a SequentialTask. Maybe we can reduce code duplications? :^)
class MinecraftUpdate : public Task class MinecraftUpdate : public Task
{ {
Q_OBJECT Q_OBJECT

View File

@ -65,7 +65,7 @@ struct MojangAssetIndexInfo : public MojangDownloadInfo
// https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/ // https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/
if(id == "legacy") if(id == "legacy")
{ {
url = "https://launchermeta.mojang.com/mc/assets/legacy/c0fd82e8ce9fbc93119e40d96d5a4e62cfa3f729/legacy.json"; url = "https://piston-meta.mojang.com/mc/assets/legacy/c0fd82e8ce9fbc93119e40d96d5a4e62cfa3f729/legacy.json";
} }
// HACK // HACK
else else

View File

@ -1,6 +1,5 @@
#include <QTest> #include <QTest>
#include <QDebug> #include <QDebug>
#include "TestUtil.h"
#include "minecraft/MojangVersionFormat.h" #include "minecraft/MojangVersionFormat.h"
@ -8,9 +7,8 @@ class MojangVersionFormatTest : public QObject
{ {
Q_OBJECT Q_OBJECT
static QJsonDocument readJson(const char *file) static QJsonDocument readJson(const QString path)
{ {
auto path = QFINDTESTDATA(file);
QFile jsonFile(path); QFile jsonFile(path);
jsonFile.open(QIODevice::ReadOnly); jsonFile.open(QIODevice::ReadOnly);
auto data = jsonFile.readAll(); auto data = jsonFile.readAll();
@ -31,7 +29,7 @@ private
slots: slots:
void test_Through_Simple() void test_Through_Simple()
{ {
QJsonDocument doc = readJson("data/1.9-simple.json"); QJsonDocument doc = readJson(QFINDTESTDATA("testdata/1.9-simple.json"));
auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9-simple.json"); auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9-simple.json");
auto doc2 = MojangVersionFormat::versionFileToJson(vfile); auto doc2 = MojangVersionFormat::versionFileToJson(vfile);
writeJson("1.9-simple-passthorugh.json", doc2); writeJson("1.9-simple-passthorugh.json", doc2);
@ -41,7 +39,7 @@ slots:
void test_Through() void test_Through()
{ {
QJsonDocument doc = readJson("data/1.9.json"); QJsonDocument doc = readJson(QFINDTESTDATA("testdata/1.9.json"));
auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9.json"); auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9.json");
auto doc2 = MojangVersionFormat::versionFileToJson(vfile); auto doc2 = MojangVersionFormat::versionFileToJson(vfile);
writeJson("1.9-passthorugh.json", doc2); writeJson("1.9-passthorugh.json", doc2);

View File

@ -1,3 +1,38 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "OneSixVersionFormat.h" #include "OneSixVersionFormat.h"
#include <Json.h> #include <Json.h>
#include "minecraft/Agent.h" #include "minecraft/Agent.h"
@ -296,7 +331,7 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch
} }
writeString(root, "appletClass", patch->appletClass); writeString(root, "appletClass", patch->appletClass);
writeStringList(root, "+tweakers", patch->addTweakers); writeStringList(root, "+tweakers", patch->addTweakers);
writeStringList(root, "+traits", patch->traits.toList()); writeStringList(root, "+traits", patch->traits.values());
if (!patch->libraries.isEmpty()) if (!patch->libraries.isEmpty())
{ {
QJsonArray array; QJsonArray array;

View File

@ -1,4 +1,24 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -36,6 +56,13 @@
#include "ComponentUpdateTask.h" #include "ComponentUpdateTask.h"
#include "Application.h" #include "Application.h"
#include "modplatform/ModAPI.h"
static const QMap<QString, ModAPI::ModLoaderType> modloaderMapping{
{"net.minecraftforge", ModAPI::Forge},
{"net.fabricmc.fabric-loader", ModAPI::Fabric},
{"org.quiltmc.quilt-loader", ModAPI::Quilt}
};
PackProfile::PackProfile(MinecraftInstance * instance) PackProfile::PackProfile(MinecraftInstance * instance)
: QAbstractListModel() : QAbstractListModel()
@ -339,6 +366,7 @@ void PackProfile::resolve(Net::Mode netmode)
d->m_updateTask.reset(updateTask); d->m_updateTask.reset(updateTask);
connect(updateTask, &ComponentUpdateTask::succeeded, this, &PackProfile::updateSucceeded); connect(updateTask, &ComponentUpdateTask::succeeded, this, &PackProfile::updateSucceeded);
connect(updateTask, &ComponentUpdateTask::failed, this, &PackProfile::updateFailed); connect(updateTask, &ComponentUpdateTask::failed, this, &PackProfile::updateFailed);
connect(updateTask, &ComponentUpdateTask::aborted, this, [this]{ updateFailed(tr("Aborted")); });
d->m_updateTask->start(); d->m_updateTask->start();
} }
@ -680,7 +708,11 @@ void PackProfile::move(const int index, const MoveDirection direction)
return; return;
} }
beginMoveRows(QModelIndex(), index, index, QModelIndex(), togap); beginMoveRows(QModelIndex(), index, index, QModelIndex(), togap);
#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
d->components.swapItemsAt(index, theirIndex);
#else
d->components.swap(index, theirIndex); d->components.swap(index, theirIndex);
#endif
endMoveRows(); endMoveRows();
invalidateLaunchProfile(); invalidateLaunchProfile();
scheduleSave(); scheduleSave();
@ -971,19 +1003,19 @@ void PackProfile::disableInteraction(bool disable)
} }
} }
ModAPI::ModLoaderType PackProfile::getModLoader() ModAPI::ModLoaderTypes PackProfile::getModLoaders()
{ {
if (!getComponentVersion("net.minecraftforge").isEmpty()) ModAPI::ModLoaderTypes result = ModAPI::Unspecified;
QMapIterator<QString, ModAPI::ModLoaderType> i(modloaderMapping);
while (i.hasNext())
{ {
return ModAPI::Forge; i.next();
Component* c = getComponent(i.key());
if (c != nullptr && c->isEnabled()) {
result |= i.value();
} }
else if (!getComponentVersion("net.fabricmc.fabric-loader").isEmpty())
{
return ModAPI::Fabric;
} }
else if (!getComponentVersion("org.quiltmc.quilt-loader").isEmpty()) return result;
{
return ModAPI::Quilt;
}
return ModAPI::Unspecified;
} }

View File

@ -118,7 +118,7 @@ public:
// todo(merged): is this the best approach // todo(merged): is this the best approach
void appendComponent(ComponentPtr component); void appendComponent(ComponentPtr component);
ModAPI::ModLoaderType getModLoader(); ModAPI::ModLoaderTypes getModLoaders();
private: private:
void scheduleSave(); void scheduleSave();

View File

@ -1,5 +1,4 @@
#include <QTest> #include <QTest>
#include "TestUtil.h"
#include "minecraft/ParseUtils.h" #include "minecraft/ParseUtils.h"
@ -42,4 +41,3 @@ slots:
QTEST_GUILESS_MAIN(ParseUtilsTest) QTEST_GUILESS_MAIN(ParseUtilsTest)
#include "ParseUtils_test.moc" #include "ParseUtils_test.moc"

View File

@ -1,3 +1,38 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ProfileUtils.h" #include "ProfileUtils.h"
#include "minecraft/VersionFilterData.h" #include "minecraft/VersionFilterData.h"
#include "minecraft/OneSixVersionFormat.h" #include "minecraft/OneSixVersionFormat.h"
@ -141,24 +176,6 @@ bool saveJsonFile(const QJsonDocument doc, const QString & filename)
return true; return true;
} }
VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo)
{
QFile file(fileInfo.absoluteFilePath());
if (!file.open(QFile::ReadOnly))
{
auto errorStr = QObject::tr("Unable to open the version file %1: %2.").arg(fileInfo.fileName(), file.errorString());
return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr);
}
QJsonDocument doc = QJsonDocument::fromBinaryData(file.readAll());
file.close();
if (doc.isNull())
{
file.remove();
throw JSONValidationError(QObject::tr("Unable to process the version file %1.").arg(fileInfo.fileName()));
}
return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), false);
}
void removeLwjglFromPatch(VersionFilePtr patch) void removeLwjglFromPatch(VersionFilePtr patch)
{ {
auto filter = [](QList<LibraryPtr>& libs) auto filter = [](QList<LibraryPtr>& libs)

View File

@ -1,3 +1,38 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once #pragma once
#include "Library.h" #include "Library.h"
#include "VersionFile.h" #include "VersionFile.h"
@ -19,9 +54,6 @@ VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder)
/// Save a JSON file (in any format) /// Save a JSON file (in any format)
bool saveJsonFile(const QJsonDocument doc, const QString & filename); bool saveJsonFile(const QJsonDocument doc, const QString & filename);
/// Parse a version file in binary JSON format
VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo);
/// Remove LWJGL from a patch file. This is applied to all Mojang-like profile files. /// Remove LWJGL from a patch file. This is applied to all Mojang-like profile files.
void removeLwjglFromPatch(VersionFilePtr patch); void removeLwjglFromPatch(VersionFilePtr patch);

View File

@ -2,6 +2,7 @@
/* /*
* PolyMC - Minecraft Launcher * PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -55,7 +56,7 @@ void VersionFile::applyTo(LaunchProfile *profile)
// Only real Minecraft can set those. Don't let anything override them. // Only real Minecraft can set those. Don't let anything override them.
if (isMinecraftVersion(uid)) if (isMinecraftVersion(uid))
{ {
profile->applyMinecraftVersion(minecraftVersion); profile->applyMinecraftVersion(version);
profile->applyMinecraftVersionType(type); profile->applyMinecraftVersionType(type);
// HACK: ignore assets from other version files than Minecraft // HACK: ignore assets from other version files than Minecraft
// workaround for stupid assets issue caused by amazon: // workaround for stupid assets issue caused by amazon:
@ -88,14 +89,3 @@ void VersionFile::applyTo(LaunchProfile *profile)
} }
profile->applyProblemSeverity(getProblemSeverity()); profile->applyProblemSeverity(getProblemSeverity());
} }
/*
auto theirVersion = profile->getMinecraftVersion();
if (!theirVersion.isNull() && !dependsOnMinecraftVersion.isNull())
{
if (QRegExp(dependsOnMinecraftVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(theirVersion) == -1)
{
throw MinecraftVersionMismatch(uid, dependsOnMinecraftVersion, theirVersion);
}
}
*/

View File

@ -1,4 +1,24 @@
/* Copyright 2015-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -321,7 +341,8 @@ bool World::install(const QString &to, const QString &name)
if(ok && !name.isEmpty() && m_actualName != name) if(ok && !name.isEmpty() && m_actualName != name)
{ {
World newWorld(finalPath); QFileInfo finalPathInfo(finalPath);
World newWorld(finalPathInfo);
if(newWorld.isValid()) if(newWorld.isValid())
{ {
newWorld.rename(name); newWorld.rename(name);

View File

@ -1,4 +1,24 @@
/* Copyright 2015-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -195,7 +215,7 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
switch (column) switch (column)
{ {
case SizeColumn: case SizeColumn:
return qVariantFromValue<qlonglong>(world.bytes()); return QVariant::fromValue<qlonglong>(world.bytes());
default: default:
return data(index, Qt::DisplayRole); return data(index, Qt::DisplayRole);
@ -215,7 +235,7 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
} }
case SeedRole: case SeedRole:
{ {
return qVariantFromValue<qlonglong>(world.seed()); return QVariant::fromValue<qlonglong>(world.seed());
} }
case NameRole: case NameRole:
{ {
@ -227,7 +247,7 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
} }
case SizeRole: case SizeRole:
{ {
return qVariantFromValue<qlonglong>(world.bytes()); return QVariant::fromValue<qlonglong>(world.bytes());
} }
case IconFileRole: case IconFileRole:
{ {
@ -301,7 +321,11 @@ public:
} }
protected: protected:
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QVariant retrieveData(const QString &mimetype, QMetaType type) const
#else
QVariant retrieveData(const QString &mimetype, QVariant::Type type) const QVariant retrieveData(const QString &mimetype, QVariant::Type type) const
#endif
{ {
QList<QUrl> urls; QList<QUrl> urls;
for(auto &world: m_worlds) for(auto &world: m_worlds)

View File

@ -39,6 +39,7 @@
#include <QJsonArray> #include <QJsonArray>
#include <QDebug> #include <QDebug>
#include <QUuid> #include <QUuid>
#include <QRegularExpression>
namespace { namespace {
void tokenToJSONV3(QJsonObject &parent, Katabasis::Token t, const char * tokenName) { void tokenToJSONV3(QJsonObject &parent, Katabasis::Token t, const char * tokenName) {
@ -451,7 +452,7 @@ void AccountData::invalidateClientToken() {
if(type != AccountType::Mojang) { if(type != AccountType::Mojang) {
return; return;
} }
yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegExp("[{-}]")); yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{-}]"));
} }
QString AccountData::profileId() const { QString AccountData::profileId() const {
@ -473,7 +474,7 @@ QString AccountData::accountDisplayString() const {
return userName(); return userName();
} }
case AccountType::Offline: { case AccountType::Offline: {
return userName(); return QObject::tr("<Offline>");
} }
case AccountType::MSA: { case AccountType::MSA: {
if(xboxApiToken.extra.contains("gtg")) { if(xboxApiToken.extra.contains("gtg")) {

View File

@ -282,6 +282,10 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
case Qt::DisplayRole: case Qt::DisplayRole:
switch (index.column()) switch (index.column())
{ {
case ProfileNameColumn: {
return account->profileName();
}
case NameColumn: case NameColumn:
return account->accountDisplayString(); return account->accountDisplayString();
@ -300,7 +304,7 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
return tr("Offline", "Account status"); return tr("Offline", "Account status");
} }
case AccountState::Online: { case AccountState::Online: {
return tr("Online", "Account status"); return tr("Ready", "Account status");
} }
case AccountState::Working: { case AccountState::Working: {
return tr("Working", "Account status"); return tr("Working", "Account status");
@ -320,10 +324,6 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
} }
} }
case ProfileNameColumn: {
return account->profileName();
}
case MigrationColumn: { case MigrationColumn: {
if(account->isMSA() || account->isOffline()) { if(account->isMSA() || account->isOffline()) {
return tr("N/A", "Can Migrate?"); return tr("N/A", "Can Migrate?");
@ -349,7 +349,7 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
case Qt::CheckStateRole: case Qt::CheckStateRole:
switch (index.column()) switch (index.column())
{ {
case NameColumn: case ProfileNameColumn:
return account == m_defaultAccount ? Qt::Checked : Qt::Unchecked; return account == m_defaultAccount ? Qt::Checked : Qt::Unchecked;
} }
@ -365,6 +365,8 @@ QVariant AccountList::headerData(int section, Qt::Orientation orientation, int r
case Qt::DisplayRole: case Qt::DisplayRole:
switch (section) switch (section)
{ {
case ProfileNameColumn:
return tr("Username");
case NameColumn: case NameColumn:
return tr("Account"); return tr("Account");
case TypeColumn: case TypeColumn:
@ -373,8 +375,6 @@ QVariant AccountList::headerData(int section, Qt::Orientation orientation, int r
return tr("Status"); return tr("Status");
case MigrationColumn: case MigrationColumn:
return tr("Can Migrate?"); return tr("Can Migrate?");
case ProfileNameColumn:
return tr("Profile");
default: default:
return QVariant(); return QVariant();
} }
@ -382,6 +382,8 @@ QVariant AccountList::headerData(int section, Qt::Orientation orientation, int r
case Qt::ToolTipRole: case Qt::ToolTipRole:
switch (section) switch (section)
{ {
case ProfileNameColumn:
return tr("Minecraft username associated with the account.");
case NameColumn: case NameColumn:
return tr("User name of the account."); return tr("User name of the account.");
case TypeColumn: case TypeColumn:
@ -389,9 +391,7 @@ QVariant AccountList::headerData(int section, Qt::Orientation orientation, int r
case StatusColumn: case StatusColumn:
return tr("Current status of the account."); return tr("Current status of the account.");
case MigrationColumn: case MigrationColumn:
return tr("Can this account migrate to Microsoft account?"); return tr("Can this account migrate to a Microsoft account?");
case ProfileNameColumn:
return tr("Name of the Minecraft profile associated with the account.");
default: default:
return QVariant(); return QVariant();
} }

View File

@ -58,8 +58,8 @@ public:
enum VListColumns enum VListColumns
{ {
// TODO: Add icon column. // TODO: Add icon column.
NameColumn = 0, ProfileNameColumn = 0,
ProfileNameColumn, NameColumn,
MigrationColumn, MigrationColumn,
TypeColumn, TypeColumn,
StatusColumn, StatusColumn,

View File

@ -79,6 +79,8 @@ QString AccountTask::getStateMessage() const
bool AccountTask::changeState(AccountTaskState newState, QString reason) bool AccountTask::changeState(AccountTaskState newState, QString reason)
{ {
m_taskState = newState; m_taskState = newState;
// FIXME: virtual method invoked in constructor.
// We want that behavior, but maybe make it less weird?
setStatus(getStateMessage()); setStatus(getStateMessage());
switch(newState) { switch(newState) {
case AccountTaskState::STATE_CREATED: { case AccountTaskState::STATE_CREATED: {

View File

@ -1,3 +1,38 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cassert> #include <cassert>
#include <QDebug> #include <QDebug>
@ -20,7 +55,11 @@ void AuthRequest::get(const QNetworkRequest &req, int timeout/* = 60*1000*/) {
reply_ = APPLICATION->network()->get(request_); reply_ = APPLICATION->network()->get(request_);
status_ = Requesting; status_ = Requesting;
timedReplies_.add(new Katabasis::Reply(reply_, timeout)); timedReplies_.add(new Katabasis::Reply(reply_, timeout));
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
connect(reply_, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)));
#else
connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError))); connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)));
#endif
connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished())); connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()));
connect(reply_, &QNetworkReply::sslErrors, this, &AuthRequest::onSslErrors); connect(reply_, &QNetworkReply::sslErrors, this, &AuthRequest::onSslErrors);
} }
@ -31,7 +70,11 @@ void AuthRequest::post(const QNetworkRequest &req, const QByteArray &data, int t
status_ = Requesting; status_ = Requesting;
reply_ = APPLICATION->network()->post(request_, data_); reply_ = APPLICATION->network()->post(request_, data_);
timedReplies_.add(new Katabasis::Reply(reply_, timeout)); timedReplies_.add(new Katabasis::Reply(reply_, timeout));
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
connect(reply_, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)));
#else
connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError))); connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)));
#endif
connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished())); connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()));
connect(reply_, &QNetworkReply::sslErrors, this, &AuthRequest::onSslErrors); connect(reply_, &QNetworkReply::sslErrors, this, &AuthRequest::onSslErrors);
connect(reply_, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(onUploadProgress(qint64,qint64))); connect(reply_, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(onUploadProgress(qint64,qint64)));

View File

@ -40,7 +40,7 @@
#include <QUuid> #include <QUuid>
#include <QJsonObject> #include <QJsonObject>
#include <QJsonArray> #include <QJsonArray>
#include <QRegExp> #include <QRegularExpression>
#include <QStringList> #include <QStringList>
#include <QJsonDocument> #include <QJsonDocument>
@ -53,7 +53,7 @@
#include "flows/Offline.h" #include "flows/Offline.h"
MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) { MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) {
data.internalId = QUuid::createUuid().toString().remove(QRegExp("[{}-]")); data.internalId = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
} }
@ -78,7 +78,7 @@ MinecraftAccountPtr MinecraftAccount::createFromUsername(const QString &username
MinecraftAccountPtr account = new MinecraftAccount(); MinecraftAccountPtr account = new MinecraftAccount();
account->data.type = AccountType::Mojang; account->data.type = AccountType::Mojang;
account->data.yggdrasilToken.extra["userName"] = username; account->data.yggdrasilToken.extra["userName"] = username;
account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegExp("[{}-]")); account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
return account; return account;
} }
@ -97,10 +97,10 @@ MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username)
account->data.yggdrasilToken.validity = Katabasis::Validity::Certain; account->data.yggdrasilToken.validity = Katabasis::Validity::Certain;
account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc(); account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc();
account->data.yggdrasilToken.extra["userName"] = username; account->data.yggdrasilToken.extra["userName"] = username;
account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegExp("[{}-]")); account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
account->data.minecraftEntitlement.ownsMinecraft = true; account->data.minecraftEntitlement.ownsMinecraft = true;
account->data.minecraftEntitlement.canPlayMinecraft = true; account->data.minecraftEntitlement.canPlayMinecraft = true;
account->data.minecraftProfile.id = QUuid::createUuid().toString().remove(QRegExp("[{}-]")); account->data.minecraftProfile.id = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
account->data.minecraftProfile.name = username; account->data.minecraftProfile.name = username;
account->data.minecraftProfile.validity = Katabasis::Validity::Certain; account->data.minecraftProfile.validity = Katabasis::Validity::Certain;
return account; return account;
@ -135,6 +135,7 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::login(QString password) {
m_currentTask.reset(new MojangLogin(&data, password)); m_currentTask.reset(new MojangLogin(&data, password));
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); });
emit activityChanged(true); emit activityChanged(true);
return m_currentTask; return m_currentTask;
} }
@ -145,6 +146,7 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::loginMSA() {
m_currentTask.reset(new MSAInteractive(&data)); m_currentTask.reset(new MSAInteractive(&data));
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); });
emit activityChanged(true); emit activityChanged(true);
return m_currentTask; return m_currentTask;
} }
@ -155,6 +157,7 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::loginOffline() {
m_currentTask.reset(new OfflineLogin(&data)); m_currentTask.reset(new OfflineLogin(&data));
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); });
emit activityChanged(true); emit activityChanged(true);
return m_currentTask; return m_currentTask;
} }
@ -176,6 +179,7 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::refresh() {
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); });
emit activityChanged(true); emit activityChanged(true);
return m_currentTask; return m_currentTask;
} }

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