Compare commits

...

84 commits

Author SHA1 Message Date
Alexandru Ionut Tripon
9ceee0a6e7
[Backport release-10.x] feat: use Qt 6.10.1 outside mac and use sharun for portable builds too (#4614) 2025-12-31 14:26:22 +02:00
DioEgizio
d33874f24e chore: bump to ubuntu 24.04
no reason to keep using 22.04 with sharun

Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit 36ffd6b4ee)
2025-12-31 12:25:38 +00:00
DioEgizio
66f7fb909d chore: simplify Launcher.in quite a bit
not necessary anymore with sharun

Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit 015ee05311)
2025-12-31 12:25:38 +00:00
DioEgizio
928adcdb4e feat: Qt 6.10.1 outside macOS
Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit 20f9784881)
2025-12-31 12:25:38 +00:00
DioEgizio
a4db3bfb88 feat: use sharun for portable builds too
Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit 16635ca9fc)
2025-12-31 12:25:38 +00:00
Alexandru Ionut Tripon
73a68659f2
[Backport release-10.x] Include ARM Linux portable builds in releases (#4613) 2025-12-31 08:54:36 +02:00
Alexandru Ionut Tripon
35a99bb5e6
[Backport release-10.x] Remove some CI jobs (#4612) 2025-12-31 08:54:25 +02:00
Alexandru Ionut Tripon
5e628d9258
[Backport release-10.x] ci: use Release env for releases (#4611) 2025-12-31 08:54:10 +02:00
Alexandru Ionut Tripon
29f68ba1cf
[Backport release-10.x] Add qtimageformats to Nix wrapper (#4610) 2025-12-31 08:53:56 +02:00
Seth Flynn
6762a1f448 ci: fail releases on unmatched files
This previously let bugs slip in, like not uploading the Linux ARM
tarball

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit c059e812a0)
2025-12-31 06:46:56 +00:00
Seth Flynn
c18128dd9f ci: upload portable linux arm tarball to releases
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit d4230349e3)
2025-12-31 06:46:56 +00:00
Seth Flynn
52a42d63ba ci(flatpak): only build for x86_64
The flatpaks from CI aren't very usable in the first place, but also
take longer to complete than regular builds, as well as contribute to
our concurrent job limit. Dropping ARM builds shouldn't have much
impact, but this can obviously be reversed if people want it

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit abe0c8e687)
2025-12-31 06:43:21 +00:00
Seth Flynn
a4e86f213f ci(nix): don't build for intel macs
These are being dropped by Nixpkgs itself soon in the near future, with
few users (on top of reduced usage of our flake/cache). We also already
have coverage for macOS builds through the aarch64-darwin target, so
this doesn't have a big impact on our end either

Obviously can be reverted if enough people want it

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 698e1dd7cf)
2025-12-31 06:43:21 +00:00
Seth Flynn
13427d77db ci: only sign windows artifacts in Release environment
`CI_HAS_ACCESS_TO_AZURE` is only set in our Release env

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 44e927a69e)
2025-12-31 06:36:51 +00:00
Seth Flynn
a26954dafa ci: use Release env for releases
This ensures we have access to Azure on CI runs for tags

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit d1313cbd2d)
2025-12-31 06:36:50 +00:00
Seth Flynn
94d18c44f3 fix(nix): add qtimageformats to wrapper
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 99d00957b7)
2025-12-31 06:35:25 +00:00
Seth Flynn
66452b16f8
[Backport release-10.x] chore: use go-appimage soft fork until the pgp pr is merged (#4607) 2025-12-30 19:57:11 -05:00
DioEgizio
9c80e019cb chore: use go-appimage soft fork until the pgp pr is merged
revert this once it's merged!

Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit a4b142681d)
2025-12-31 00:56:10 +00:00
DioEgizio
317b8eea9b fix: improve a bit the FTB packs are also on cf notice
Co-authored-by: TheKodeToad <TheKodeToad@proton.me>
Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit 57c4b71c61)
2025-12-27 15:11:13 +01:00
DioEgizio
c968bafb0a chore: add new ftb packs are also on cf notice
Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit 31dd8cd9f8)
2025-12-27 15:11:13 +01:00
Alexandru Ionut Tripon
9b7c83ef23
[Backport release-10.x] feat: Use precompiled headers for 130-180% speedup (#4594) 2025-12-26 23:42:17 +02:00
Alexandru Ionut Tripon
5ee0286635
[Backport release-10.x] CMakeLists: fix ASan compile options (#4593) 2025-12-26 23:42:02 +02:00
Alexandru Ionut Tripon
10bee70c42
[Backport release-10.x] fix(APIPage.ui): resolve duplicate name (#4592) 2025-12-26 23:41:51 +02:00
Rachel Powers
1e07305803 feat: Use pre-compiled headers
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
(cherry picked from commit e53093c4b4)
2025-12-26 21:41:46 +00:00
Octol1ttle
8d8e9d0390 CMakeLists: fix ASan compile options
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 1fdc33669b)
2025-12-26 21:40:40 +00:00
Octol1ttle
2128e87d92 fix(APIPage.ui): resolve duplicate name
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit de092922d7)
2025-12-26 21:40:23 +00:00
Alexandru Ionut Tripon
dbfae0599f
[Backport release-10.x] Force disable xrandr if it is unavailable (#4589) 2025-12-26 12:11:08 +02:00
Blake Batson
b2d7211254 Force disable xrandr if it is unavailable
lwjgl2 optionally requires the xrandr command line utility on linux, but
does not check if the executable actually exists before trying to use
it. We can force it to fall back to the xf86videomode implementation
by checking for the executable ourselves, and force disabling xrandr
with this boolean [1] if it does not exist.

Link: 2df01dd762/src/java/org/lwjgl/opengl/LinuxDisplay.java (L214) [1]
Signed-off-by: Blake Batson <bbatson101@gmail.com>
(cherry picked from commit e0c2fbbcde)
2025-12-26 10:10:51 +00:00
Alexandru Ionut Tripon
0631359ec4
build(nix): use nixos-25.11 channel for nixpkgs (#4582) 2025-12-26 10:11:10 +02:00
Seth Flynn
e81ec9c39c
build(nix): use nixos-25.11 channel for nixpkgs
This makes `clangd` work again, thanks to
https://github.com/NixOS/nixpkgs/pull/462747

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/c6245e83d836d0433170a16eb185cefe0572f8b8' (2025-12-18)
  → 'https://releases.nixos.org/nixos/25.11/nixos-25.11.2222.b3aad468604d/nixexprs.tar.xz?lastModified=1766201043&narHash=sha256-v9nbQe0BgwBx%2BKcxRf6i2kbS8EwKjBFRjAawA91B/OE%3D&rev=b3aad468604d3e488d627c0b43984eb60e75e782' (2025-12-20)

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 5ee33814b6)
2025-12-25 14:49:42 +02:00
Alexandru Ionut Tripon
b30677ef10
[Backport release-10.x] fix mod update remaining disabled on second open (#4575) 2025-12-24 00:11:26 +02:00
Trial97
75215b0d31 fix mod update remaining disabled on second open
fixes
https://discord.com/channels/1031648380885147709/1450161125172707390/1453013386144124929
basically when opening the edit window a second time the model doesn't
get any updates as the data is already loaded into the memory.

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 7f68f2fe3b)
2025-12-23 22:10:44 +00:00
Alexandru Ionut Tripon
bf0aa5f980
[Backport release-10.x] preserve original file extension when importing modpack icon (#4574) 2025-12-23 22:45:42 +02:00
Trial97
5520dc6aaf preserve original file extension when importing modpack icon
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 8e96beeda0)
2025-12-23 20:45:06 +00:00
Alexandru Ionut Tripon
6d59334777
[Backport release-10.x] match disabled mods on uninstall (#4567) 2025-12-22 23:56:08 +02:00
Trial97
e90965adc1 match disabled mods on uninstall
fixes #4537
This ensures that when looking to uninstall a resource prism will
consider the disabled ones to.
Right now we have a guard in place to prevent resources using the same
name so this check will allways match with the correct resouce.

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 08c45684de)
2025-12-22 21:55:54 +00:00
Alexandru Ionut Tripon
a524d93ada
[Backport release-10.x] build(mingw): use dwarf debug info (#4564) 2025-12-22 23:48:01 +02:00
Alexandru Ionut Tripon
7101f15a2c
[Backport release-10.x] fix Pre-release filter (#4566) 2025-12-22 23:46:04 +02:00
Trial97
131dc0acf3 fix Pre-release filter
introduced here https://github.com/PrismLauncher/PrismLauncher/pull/3260
fixes #4415
reason: some snapshot have Pre-Release in our meta but when searching in
Modrinth this needs to be translated to -pre and the reverse needed to
be done for filtering after we fetched the version.
Now there are snapshots with -pre in name and that works with Modrinth
but when we translate it back we replace it with Pre-Release so the
easeiest patch is just to double the version(one with -pre one with
Pre-Release)

The correct one would be to complicate the code and identify the
versions that need the transition and only apply this for those.

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit b1408775b3)
2025-12-22 21:45:48 +00:00
Seth Flynn
fd4436880a build(mingw): use dwarf debug info
As it turns out, LLDB can load Windows crash dumps!

This allows us go back to the regular (better supported) DWARF debuginfo
format used by MinGW, as now we have a tool that can both parse those
symbols *and* Windows' crash dumps. The biggest advantage here is that
once again, MinGW crash dumps can be easily inspected on Linux

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit d85ff94f03)
2025-12-22 18:12:28 +00:00
Alexandru Ionut Tripon
2582a90b90
[Backport release-10.x] fix legacy skin model (#4561) 2025-12-22 19:38:11 +02:00
Alexandru Ionut Tripon
31cf378171 Update launcher/minecraft/skins/SkinModel.cpp
Co-authored-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
Signed-off-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
(cherry picked from commit c723b3abe8)
2025-12-22 17:32:39 +00:00
Trial97
4173faba7a add copyright for modrinth code
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 4b96c5736c)
2025-12-22 17:32:39 +00:00
Trial97
0b3fb6c4ce fix legacy skin model
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit f7111b6ec1)
2025-12-22 17:32:39 +00:00
Seth Flynn
302e10a7d9
[Backport release-10.x] fix(build): handle CMAKE_BUILD_TYPE with generator expressions (#4558) 2025-12-22 09:54:19 -05:00
Seth Flynn
947a8faa0d fix(build): handle CMAKE_BUILD_TYPE with generator expressions
Since we've started using ninja's multi-config generator, evaluating
CMAKE_BUILD_TYPE at configure-time is no longer reliable. Thankfully,
CMake offers "generator expressions" that are evaluated during build
system generation, which allows us to continue using these conditional
flags without much headache

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 85849fea41)
2025-12-22 14:50:30 +00:00
Alexandru Ionut Tripon
ad83592834
[Backport release-10.x] fix modrinth modpack allways download latest (#4555) 2025-12-22 10:13:20 +02:00
Trial97
3f9b6ae452 fix modrinth modpack allways download latest
fixes #4547
introduced by #3828
made a mistake to use addonId instead ov fileId
this only applies to modrinth
on curseforge I could not reproduce this bug

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit d7745d97f2)
2025-12-22 08:09:56 +00:00
timoreo
f1e382b035
[Backport release-10.x] fix accounts crash (#4546) 2025-12-21 05:34:09 +01:00
Trial97
a0797d00e3 fix accounts crash
fixes #4541
introduced in
3c46d8a412
The original commit introduced m_name but never used it.
When the endActivity would be called with a count of 0 this would crash
the laucnher.
How to reproduce: try to switch your skin in quick succession until you
get 429 too many requests as response to the login.
I will also presume this is not the only crash caused by this(hopefully
it is as it was not catched for four years)

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit f4b22dae90)
2025-12-21 04:33:25 +00:00
Alexandru Ionut Tripon
3d805dff29
[Backport release-10.x] fix(skin-preview): smoother chessboard background contrast (#4542) 2025-12-20 18:53:03 +02:00
Rachel Powers
8f314c982a fix(skin-preview): smoother chessboard background contrast
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
(cherry picked from commit 42b72d676c)
2025-12-20 16:51:15 +00:00
Seth Flynn
9389b9d582 ci(linux): verify appstream info for appimages
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 5d5f22e672)
2025-12-20 17:48:36 +01:00
Seth Flynn
df172c0923 revert: "fix(launcher): set correct bin path for self-contained appimages"
Refs: b1b4b5d
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit e39e59acb6)
2025-12-20 17:48:36 +01:00
Seth Flynn
47be7ae502 revert: "fix(appimage): launch external processes with bundled linker"
Refs: c305ed4
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 1d8bf82ef8)
2025-12-20 17:48:36 +01:00
Seth Flynn
e1941a5794 build(linux): use sharun for appimage bundling
This should fix issues with OpenGL, as well as help as avoid using some
annoying (and fragile) hacks to locate our actual binary/other resources

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 06e99e2990)
2025-12-20 17:48:36 +01:00
Tayou
f85d399928
[Backport release-10.x] fix skin preview leg rendering (#4540) 2025-12-20 13:00:38 +01:00
Tayou
3fbbebe93b fix skin preview leg rendering
Signed-off-by: Tayou <git@tayou.org>
(cherry picked from commit fcf201755c)
2025-12-20 12:00:17 +00:00
Alexandru Ionut Tripon
1274eb7e48
[Backport release-10.x] fix skin depth (#4530) 2025-12-19 20:28:34 +02:00
Alexandru Ionut Tripon
68010c6c49
[Backport release-10.x] fix elytra preview (#4529) 2025-12-19 20:28:20 +02:00
Trial97
49e9671c96 fix skin depth
The skin overlay was drawn together with the original skin making it
blend weirdly. By drawing the overlay after the skin this blends with
the body corectly.

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 624d506fac)
2025-12-19 18:27:02 +00:00
Trial97
819b4e49c8 fix elytra preview
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit e360a95c23)
2025-12-19 18:26:45 +00:00
Alexandru Ionut Tripon
a74b1dd79d
[Backport release-10.x] Updated App icon for macOS 26 (#4526) 2025-12-19 10:38:18 +02:00
Richard Voigtmann
add5966c52 Add suggestion from code review
Co-authored-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
Signed-off-by: Richard Voigtmann <71901885+ShadowPaint-SP@users.noreply.github.com>
(cherry picked from commit e7f801c4c4)
2025-12-19 08:37:35 +00:00
Richard Voigtmann
c89150a26e xCode version check
Signed-off-by: Richard Voigtmann <richard.v.voigtmann@gmail.com>
(cherry picked from commit e46b0335ab)
2025-12-19 08:37:35 +00:00
Richard Voigtmann
c8c6304a15 removed unnecessary Option from actool command and failsafe attempt for older versions
Signed-off-by: Richard Voigtmann <richard.v.voigtmann@gmail.com>
(cherry picked from commit e125f4767f)
2025-12-19 08:37:35 +00:00
Richard Voigtmann
8bfb9b90c1 added mono to brew because macos 26 runner doesnt include it
Signed-off-by: Richard Voigtmann <richard.v.voigtmann@gmail.com>
(cherry picked from commit 7f740e4ad6)
2025-12-19 08:37:35 +00:00
Richard Voigtmann
da62b63f52 bumped macos runner version to 26
Signed-off-by: Richard Voigtmann <richard.v.voigtmann@gmail.com>
(cherry picked from commit 7cb623e800)
2025-12-19 08:37:35 +00:00
Richard Voigtmann
881bb22d45 implemented recommended changes -reverted .icns file instellation
Signed-off-by: Richard Voigtmann <richard.v.voigtmann@gmail.com>
(cherry picked from commit d53b785a1c)
2025-12-19 08:37:35 +00:00
Richard Voigtmann
0776291e55 replaced binary with in ci building of Icons
Signed-off-by: Richard Voigtmann <richard.v.voigtmann@gmail.com>
(cherry picked from commit a7dd3aeaab)
2025-12-19 08:37:35 +00:00
Richard Voigtmann
c2d324aff3 updated Assets.car to the icon suggestion from hw2007
Signed-off-by: Richard Voigtmann <richard.v.voigtmann@gmail.com>
(cherry picked from commit b0594dbb06)
2025-12-19 08:37:35 +00:00
Richard Voigtmann
af83cd92c0 Added macOS 26 Liquid Glass Icon Support. See: #4149
Signed-off-by: Richard Voigtmann <richard.v.voigtmann@gmail.com>
(cherry picked from commit d0737eecc5)
2025-12-19 08:37:35 +00:00
Alexandru Ionut Tripon
a639091a39
[Backport release-10.x] Trim unexpected info from pack.mcmeta (#4523) 2025-12-18 23:52:03 +02:00
Dylan Schooner
ab71c44ed6 Check specifically for GarbageAtEnd error
- Out parameter for garbage data\n- Rename parseUntilMalformed to parseUntilGarbage

Signed-off-by: Dylan Schooner <dschooner05@gmail.com>
(cherry picked from commit 51b47050f9)
2025-12-18 21:51:35 +00:00
Dylan Schooner
9fef9c7bd8 Use Json::parseUntilMalformed in McClient::parseResponse
Signed-off-by: Dylan Schooner <dschooner05@gmail.com>
(cherry picked from commit 9c7fe72f9c)
2025-12-18 21:51:35 +00:00
Dylan Schooner
811e3de29b Add Json::parseUntilMalformed helper
Signed-off-by: Dylan Schooner <dschooner05@gmail.com>
(cherry picked from commit e1eee6e3ca)
2025-12-18 21:51:35 +00:00
Dylan Schooner
1ebe081e03 Trim unexpected info from pack.mcmeta
Signed-off-by: Dylan Schooner <dschooner05@gmail.com>
(cherry picked from commit 43fce3ae46)
2025-12-18 21:51:35 +00:00
Alexandru Ionut Tripon
79be92ca74
[Backport release-10.x] Use static image when opengl context is not available (#4519) 2025-12-18 13:21:26 +02:00
Alexandru Ionut Tripon
8fcfebb321 Update launcher/ui/dialogs/skins/SkinManageDialog.cpp
Signed-off-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
(cherry picked from commit 3c570f9e9c)
2025-12-18 11:21:10 +00:00
Trial97
01bb8c81cf Use static image when opengl context is not available
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 177e7b272b)
2025-12-18 11:21:10 +00:00
Alexandru Ionut Tripon
708222bb80
[Backport release-10.x] server player req: parse malformed json (#4513) 2025-12-16 16:35:45 +02:00
Tayou
474be07724 server player req: parse malformed json
ATM10 server seems to send a extra json object after the default response, that we need to cut off.
Signed-off-by: Tayou <git@tayou.org>
Co-authored-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 5ebd386797)
2025-12-16 14:35:33 +00:00
Alexandru Ionut Tripon
6b952403c9
[Backport release-10.x] fix: actually fix toml++ on fedora (#4507) 2025-12-16 10:29:03 +02:00
DioEgizio
7711a9aa81 fix: actually fix toml++ on fedora
oops

Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit 9aa1a4d11b)
2025-12-16 08:27:37 +00:00
54 changed files with 1025 additions and 384 deletions

View file

@ -58,9 +58,7 @@ runs:
GPG_PRIVATE_KEY: ${{ inputs.gpg-private-key }}
run: |
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
cp ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.{metainfo,appdata}.xml
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}
if [ '${{ inputs.gpg-private-key-id }}' != '' ]; then
echo "$GPG_PRIVATE_KEY" > privkey.asc
@ -70,11 +68,23 @@ runs:
echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY
fi
appimagetool -s deploy "$INSTALL_APPIMAGE_DIR"/usr/share/applications/*.desktop
cp ~/bin/AppImageUpdate.AppImage "$INSTALL_APPIMAGE_DIR"/usr/bin/
# FIXME(@getchoo): Validate AppStream information when https://github.com/probonopd/go-appimage/pull/379 is merged
sharun lib4bin \
--hard-links \
--with-hooks \
--dst-dir "$INSTALL_APPIMAGE_DIR" \
"$INSTALL_APPIMAGE_DIR"/bin/* "$QT_PLUGIN_PATH"/*/*.so
cp ~/bin/AppImageUpdate.AppImage "$INSTALL_APPIMAGE_DIR"/bin/
# FIXME(@getchoo): gamemode doesn't seem to be very portable with DBus. Find a way to make it work!
find "$INSTALL_APPIMAGE_DIR" -name '*gamemode*' -exec rm {} +
ln -s org.prismlauncher.PrismLauncher.metainfo.xml "$INSTALL_APPIMAGE_DIR"/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml
ln -s share/applications/org.prismlauncher.PrismLauncher.desktop "$INSTALL_APPIMAGE_DIR"
ln -s share/icons/hicolor/256x256/apps/org.prismlauncher.PrismLauncher.png "$INSTALL_APPIMAGE_DIR"
mv "$INSTALL_APPIMAGE_DIR"/{sharun,AppRun}
ls -la "$INSTALL_APPIMAGE_DIR"
mkappimage \
--no-appstream \
--updateinformation "gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-$APPIMAGE_ARCH.AppImage.zsync" \
"$INSTALL_APPIMAGE_DIR" \
"PrismLauncher-Linux-$VERSION-${{ inputs.build-type }}-$APPIMAGE_ARCH.AppImage"
@ -89,9 +99,14 @@ runs:
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
#the linked cmark .so is of the version that ubuntu uses, so without this it breaks on most updated distros
mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib
cp /lib/$APPIMAGE_ARCH-linux-gnu/libcmark.so.0.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
sharun lib4bin \
--with-hooks \
--hard-links \
--dst-dir "$INSTALL_PORTABLE_DIR" \
"$INSTALL_PORTABLE_DIR"/bin/* "$QT_PLUGIN_PATH"/*/*.so
# FIXME(@getchoo): gamemode doesn't seem to be very portable with DBus. Find a way to make it work!
find "$INSTALL_PORTABLE_DIR" -name '*gamemode*' -exec rm {} +
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
cd ${{ env.INSTALL_PORTABLE_DIR }}

View file

@ -140,7 +140,7 @@ runs:
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
- name: Sign installer
if: ${{ github.ref_name == 'develop' && inputs.azure-client-id != '' }}
if: ${{ env.CI_HAS_ACCESS_TO_AZURE != '' && inputs.azure-client-id != '' }}
uses: azure/trusted-signing-action@v0
with:
endpoint: https://eus.codesigning.azure.net/

View file

@ -21,7 +21,6 @@ inputs:
qt-version:
description: Version of Qt to use
required: true
default: 6.9.3
outputs:
build-type:
@ -78,6 +77,5 @@ runs:
with:
aqtversion: "==3.1.*"
version: ${{ inputs.qt-version }}
arch: ${{ inputs.qt-architecture }}
modules: qtimageformats qtnetworkauth
cache: ${{ inputs.build-type == 'Debug' }}

View file

@ -12,29 +12,7 @@ runs:
dpkg-dev \
ninja-build extra-cmake-modules pkg-config scdoc \
cmark gamemode-dev libarchive-dev libcmark-dev libqrencode-dev zlib1g-dev \
libxcb-cursor-dev
# TODO(@getchoo): Install with the above when all targets use Ubuntu 24.04
- name: Install tomlplusplus
if: ${{ runner.arch == 'ARM64' }}
shell: bash
run: |
sudo apt-get -y install libtomlplusplus-dev
# FIXME(@getchoo): THIS IS HORRIBLE TO DO!
# Install tomlplusplus from Ubuntu 24.04, since it never got backported to 22.04
# I've done too much to continue keeping this as a submodule....
- name: Install tomlplusplus from 24.04
if: ${{ runner.arch != 'ARM64' }}
shell: bash
run: |
deb_arch="$(dpkg-architecture -q DEB_HOST_ARCH)"
curl -Lo libtomlplusplus-dev.deb http://mirrors.kernel.org/ubuntu/pool/universe/t/tomlplusplus/libtomlplusplus-dev_3.4.0+ds-0.2build1_"$deb_arch".deb
curl -Lo libtomlplusplus3t64.deb http://mirrors.kernel.org/ubuntu/pool/universe/t/tomlplusplus/libtomlplusplus3t64_3.4.0+ds-0.2build1_"$deb_arch".deb
sudo dpkg -i libtomlplusplus3t64.deb
sudo dpkg -i libtomlplusplus-dev.deb
rm *.deb
sudo apt-get install -f
libxcb-cursor-dev libtomlplusplus-dev
- name: Setup AppImage tooling
shell: bash
@ -56,19 +34,20 @@ runs:
;;
esac
gh release download \
--repo VHSgunzo/sharun \
--pattern "sharun-$APPIMAGE_ARCH-aio" \
--output ~/bin/sharun
# FIXME!: revert this to probonopd/go-appimage once https://github.com/probonopd/go-appimage/pull/377 is merged!
gh release download continuous \
--repo probonopd/go-appimage \
--pattern "appimagetool-*-$APPIMAGE_ARCH.AppImage" \
--output ~/bin/appimagetool
gh release download continuous \
--repo probonopd/go-appimage \
--repo DioEgizio/go-appimage \
--pattern "mkappimage-*-$APPIMAGE_ARCH.AppImage" \
--output ~/bin/mkappimage
chmod +x ~/bin/appimagetool ~/bin/mkappimage
echo "$HOME/bin" >> "$GITHUB_PATH"
gh release download \
--repo AppImageCommunity/AppImageUpdate \
--pattern "AppImageUpdate-$APPIMAGE_ARCH.AppImage" \
--output ~/bin/AppImageUpdate.AppImage
chmod +x ~/bin/AppImageUpdate.AppImage
chmod +x ~/bin/*
echo "$HOME/bin" >> "$GITHUB_PATH"

View file

@ -14,7 +14,7 @@ runs:
shell: bash
run: |
brew update
brew install ninja extra-cmake-modules temurin@17
brew install ninja extra-cmake-modules temurin@17 mono
- name: Set JAVA_HOME
shell: bash

View file

@ -62,6 +62,9 @@ on:
description: Type of build (Debug or Release)
type: string
default: Debug
environment:
description: Deployment environment to run under
type: string
workflow_dispatch:
inputs:
build-type:
@ -73,6 +76,8 @@ jobs:
build:
name: Build (${{ matrix.artifact-name }})
environment: ${{ inputs.environment || '' }}
permissions:
# Required for Azure Trusted Signing
id-token: write
@ -83,17 +88,15 @@ jobs:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
- os: ubuntu-24.04
artifact-name: Linux
cmake-preset: linux
qt-version: 6.10.1
# NOTE(@getchoo): Yes, we're intentionally using 24.04 here!!!
#
# It's not really documented anywhere AFAICT, but upstream Qt binaries
# *for the same version* are compiled against 24.04 on ARM, and *not* 22.04 like x64
- os: ubuntu-24.04-arm
artifact-name: Linux-aarch64
cmake-preset: linux
qt-version: 6.10.1
- os: windows-2022
artifact-name: Windows-MinGW-w64
@ -112,16 +115,19 @@ jobs:
cmake-preset: windows_msvc
# TODO(@getchoo): This is the default in setup-dependencies/windows. Why isn't it working?!?!
vcvars-arch: amd64
qt-version: 6.10.1
- os: windows-11-arm
artifact-name: Windows-MSVC-arm64
cmake-preset: windows_msvc
vcvars-arch: arm64
qt-version: 6.10.1
- os: macos-14
- os: macos-26
artifact-name: macOS
cmake-preset: macos_universal
macosx-deployment-target: 12.0
qt-version: 6.9.3
runs-on: ${{ matrix.os }}
@ -155,7 +161,7 @@ jobs:
artifact-name: ${{ matrix.artifact-name }}
msystem: ${{ matrix.msystem }}
vcvars-arch: ${{ matrix.vcvars-arch }}
qt-architecture: ${{ matrix.qt-architecture }}
qt-version: ${{ matrix.qt-version }}
##
# BUILD

View file

@ -79,6 +79,7 @@ jobs:
uses: ./.github/actions/setup-dependencies
with:
build-type: Debug
qt-version: 6.10.1
- name: Configure and Build
run: |

View file

@ -77,9 +77,6 @@ jobs:
- os: ubuntu-22.04
arch: x86_64
- os: ubuntu-22.04-arm
arch: aarch64
runs-on: ${{ matrix.os }}
container:

View file

@ -86,9 +86,6 @@ jobs:
- os: ubuntu-22.04-arm
system: aarch64-linux
- os: macos-15-intel
system: x86_64-darwin
- os: macos-14
system: aarch64-darwin

View file

@ -11,6 +11,7 @@ jobs:
uses: ./.github/workflows/build.yml
with:
build-type: Release
environment: Release
secrets: inherit
create_release:
@ -34,6 +35,7 @@ jobs:
run: |
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
mv PrismLauncher-Linux-aarch64-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-aarch64-Qt6-Portable-${{ env.VERSION }}.tar.gz
mv PrismLauncher-*.AppImage/PrismLauncher-*-x86_64.AppImage PrismLauncher-Linux-x86_64.AppImage
mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*-x86_64.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
mv PrismLauncher-*.AppImage/PrismLauncher-*-aarch64.AppImage PrismLauncher-Linux-aarch64.AppImage
@ -88,6 +90,7 @@ jobs:
name: Prism Launcher ${{ env.VERSION }}
draft: true
prerelease: false
fail_on_unmatched_files: true
files: |
PrismLauncher-Linux-x86_64.AppImage
PrismLauncher-Linux-x86_64.AppImage.zsync

View file

@ -1,6 +1,9 @@
cmake_minimum_required(VERSION 3.22) # minimum version required by Qt
project(Launcher)
project(Launcher LANGUAGES C CXX)
if(APPLE)
enable_language(OBJC OBJCXX)
endif()
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
if(IS_IN_SOURCE_BUILD)
@ -79,19 +82,6 @@ else()
if(WIN32)
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}")
# Emit PDBs for WinDbg, etc.
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--pdb= ${CMAKE_EXE_LINKER_FLAGS}")
foreach(lang C CXX)
set("CMAKE_${lang}_FLAGS" "-gcodeview ${CMAKE_${lang}_FLAGS}")
# Force-enabling this to use generator expressions like TARGET_PDB_FILE
# (and because we can actually emit PDBs)
set("CMAKE_${lang}_LINKER_SUPPORTS_PDB" ON)
endforeach()
endif()
# -ffunction-sections and -fdata-sections help reduce binary size
# -mguard=cf enables Control Flow Guard
# TODO: Look into -gc-sections to further reduce binary size
@ -116,30 +106,24 @@ endif()
option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF)
# If this is a Debug build turn on address sanitiser
if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") AND DEBUG_ADDRESS_SANITIZER)
if (DEBUG_ADDRESS_SANITIZER)
message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
# using clang with clang-cl front end
message(STATUS "Address Sanitizer available on Clang MSVC frontend")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
else()
# AppleClang and Clang
message(STATUS "Address Sanitizer available on Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null")
endif()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# GCC
message(STATUS "Address Sanitizer available on GCC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover")
link_libraries("asan")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
message(STATUS "Address Sanitizer available on MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
set(USE_ASAN_COMPILE_OPTIONS $<AND:$<CONFIG:Debug,RelWithDebInfo>,$<COMPILE_LANGUAGE:C,CXX>>)
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
message(STATUS "Using Address Sanitizer compile options for MSVC frontend")
add_compile_options(
$<${USE_ASAN_COMPILE_OPTIONS}:/fsanitize=address>
$<${USE_ASAN_COMPILE_OPTIONS}:/Oy->
)
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
message(STATUS "Using Address Sanitizer compile options for GCC/Clang")
add_compile_options(
$<${USE_ASAN_COMPILE_OPTIONS}:-fsanitize=address,undefined>
$<${USE_ASAN_COMPILE_OPTIONS}:-fno-omit-frame-pointer>
$<${USE_ASAN_COMPILE_OPTIONS}:-fno-sanitize-recover=null>
)
link_libraries("asan" "ubsan")
else()
message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
endif()
@ -337,7 +321,7 @@ if(NOT LibArchive_FOUND)
pkg_check_modules(libarchive REQUIRED IMPORTED_TARGET libarchive)
endif()
find_package(tomlplusplus 3.2.0 REQUIRED)
find_package(tomlplusplus 3.2.0)
# fallback to pkgconfig, important especially as many distros package toml++ built with meson
if(NOT tomlplusplus_FOUND)
find_package(PkgConfig REQUIRED)
@ -399,6 +383,52 @@ if(UNIX AND APPLE)
# Add the icon
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
find_program(ACTOOL_EXE actool DOC "Path to the apple asset catalog compiler")
if(ACTOOL_EXE)
execute_process(
COMMAND xcodebuild -version
OUTPUT_VARIABLE XCODE_VERSION_OUTPUT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
string(REGEX MATCH "Xcode ([0-9]+\.[0-9]+)" XCODE_VERSION_MATCH "${XCODE_VERSION_OUTPUT}")
if(XCODE_VERSION_MATCH)
set(XCODE_VERSION ${CMAKE_MATCH_1})
else()
set(XCODE_VERSION 0.0)
endif()
if(XCODE_VERSION VERSION_GREATER_EQUAL 26.0)
set(ASSETS_OUT_DIR "${CMAKE_BINARY_DIR}/program_info")
set(GENERATED_ASSETS_CAR "${ASSETS_OUT_DIR}/Assets.car")
set(ICON_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Branding_MAC_ICON}")
add_custom_command(
OUTPUT "${GENERATED_ASSETS_CAR}"
COMMAND ${ACTOOL_EXE} "${ICON_SOURCE}"
--compile "${ASSETS_OUT_DIR}"
--output-partial-info-plist /dev/null
--app-icon PrismLauncher
--enable-on-demand-resources NO
--target-device mac
--minimum-deployment-target ${CMAKE_OSX_DEPLOYMENT_TARGET}
--platform macosx
DEPENDS "${ICON_SOURCE}"
COMMENT "Compiling asset catalog (${ICON_SOURCE})"
VERBATIM
)
add_custom_target(compile_assets ALL DEPENDS "${GENERATED_ASSETS_CAR}")
install(FILES "${GENERATED_ASSETS_CAR}" DESTINATION "${RESOURCES_DEST_DIR}")
else()
message(WARNING "Xcode ${XCODE_VERSION} is too old. Minimum required version is 26.0. Not compiling liquid glass icons.")
endif()
else()
message(WARNING "actool not found. Cannot compile macOS app icons.\n"
"Install Xcode command line tools: 'xcode-select --install'")
endif()
elseif(UNIX)
include(KDEInstallDirs)

View file

@ -21,7 +21,9 @@
<key>CFBundleGetInfoString</key>
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<string>${Launcher_Name}</string>
<key>CFBundleIconName</key>
<string>${Launcher_Name}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>

17
flake.lock generated
View file

@ -18,18 +18,15 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1765472234,
"narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b",
"type": "github"
"lastModified": 1766473571,
"narHash": "sha256-QvjEJNgMVuOootbR+DEfbiW+zSK57U32CE0jmVdcNjQ=",
"rev": "76701a179d3a98b07653e2b0409847499b2a07d3",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/25.11/nixos-25.11.2403.76701a179d3a/nixexprs.tar.xz"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
"type": "tarball",
"url": "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz"
}
},
"root": {

View file

@ -9,7 +9,7 @@
};
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs.url = "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz";
libnbtplusplus = {
url = "github:PrismLauncher/libnbtplusplus";

View file

@ -371,25 +371,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
}
QString origcwdPath = QDir::currentPath();
#if defined(Q_OS_LINUX)
const QString binFilePath = applicationFilePath();
const bool isAppImage = binFilePath.startsWith("/tmp/.mount_");
// Yes, this can technically trigger the logic below if someone makes an AppImage with an actual launcher exe named "ld-linux"
// Please don't :)
const bool executedFromLinker = QFileInfo(binFilePath).fileName().startsWith("ld-linux");
// NOTE(@getchoo): In order for `go-appimage` to generate self-contained AppImages, it executes apps from a bundled linker at
// <root>/lib64
// This is not the path to our actual binary, which we want
QString binPath;
if (isAppImage && executedFromLinker) {
binPath = FS::PathCombine(applicationDirPath(), "../usr/bin");
} else {
binPath = applicationDirPath();
}
#else
QString binPath = applicationDirPath();
#endif
{
// Root path is used for updates and portable data

View file

@ -26,13 +26,13 @@ set(CORE_SOURCES
NullInstance.h
MMCZip.h
MMCZip.cpp
archive/ArchiveReader.cpp
archive/ArchiveReader.h
archive/ArchiveWriter.cpp
archive/ArchiveWriter.h
archive/ExportToZipTask.cpp
archive/ArchiveReader.cpp
archive/ArchiveReader.h
archive/ArchiveWriter.cpp
archive/ArchiveWriter.h
archive/ExportToZipTask.cpp
archive/ExportToZipTask.h
archive/ExtractZipTask.cpp
archive/ExtractZipTask.cpp
archive/ExtractZipTask.h
StringUtils.h
StringUtils.cpp
@ -621,10 +621,10 @@ set(PRISMUPDATER_SOURCES
# Zip
MMCZip.h
MMCZip.cpp
archive/ArchiveReader.cpp
archive/ArchiveReader.h
archive/ArchiveWriter.cpp
archive/ArchiveWriter.h
archive/ArchiveReader.cpp
archive/ArchiveReader.h
archive/ArchiveWriter.cpp
archive/ArchiveWriter.h
# Time
MMCTime.h
@ -1300,6 +1300,16 @@ endif()
include(CompilerWarnings)
######## Precompiled Headers ###########
set(PRECOMPILED_HEADERS
include/base.pch.hpp
include/qtcore.pch.hpp
include/qtgui.pch.hpp
)
####### Targets ########
# Add executable
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
set_project_warnings(Launcher_logic
@ -1308,6 +1318,7 @@ set_project_warnings(Launcher_logic
"${Launcher_GCC_WARNINGS}")
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
target_precompile_headers(Launcher_logic PRIVATE ${PRECOMPILED_HEADERS})
target_link_libraries(Launcher_logic
systeminfo
Launcher_murmur2
@ -1389,6 +1400,7 @@ endif()
target_link_libraries(Launcher_logic)
add_executable(${Launcher_Name} MACOSX_BUNDLE WIN32 main.cpp ${LAUNCHER_RCS})
target_precompile_headers(${Launcher_Name} REUSE_FROM Launcher_logic)
target_link_libraries(${Launcher_Name} Launcher_logic)
if(DEFINED Launcher_APP_BINARY_NAME)
@ -1412,14 +1424,15 @@ install(TARGETS ${Launcher_Name}
)
# Deploy PDBs
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}> DESTINATION ${BINARY_DEST_DIR})
if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}> DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
endif()
if(Launcher_BUILD_UPDATER)
# Updater
add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI})
target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_precompile_headers(prism_updater_logic PRIVATE ${PRECOMPILED_HEADERS})
target_link_libraries(prism_updater_logic
${ZLIB_LIBRARIES}
systeminfo
@ -1439,6 +1452,7 @@ if(Launcher_BUILD_UPDATER)
add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp)
target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest)
target_link_libraries("${Launcher_Name}_updater" prism_updater_logic)
target_precompile_headers("${Launcher_Name}_updater" REUSE_FROM prism_updater_logic)
if(DEFINED Launcher_APP_BINARY_NAME)
set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater")
@ -1455,8 +1469,8 @@ if(Launcher_BUILD_UPDATER)
)
# Deploy PDBs
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_updater> DESTINATION ${BINARY_DEST_DIR})
if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_updater> DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
endif()
endif()
@ -1469,6 +1483,8 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
"${Launcher_GCC_WARNINGS}")
target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_precompile_headers(filelink_logic PRIVATE ${PRECOMPILED_HEADERS})
target_link_libraries(filelink_logic
systeminfo
BuildConfig
@ -1482,6 +1498,8 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
add_executable("${Launcher_Name}_filelink" WIN32 filelink/filelink_main.cpp)
target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest)
target_precompile_headers("${Launcher_Name}_filelink" REUSE_FROM filelink_logic)
# HACK: Fix manifest issues with Ninja in release mode (and only release mode) and MSVC
# I have no idea why this works or why it's needed. UPDATE THIS IF YOU EDIT THE MANIFEST!!! -@getchoo
# Thank you 2018 CMake mailing list thread https://cmake.cmake.narkive.com/LnotZXus/conflicting-msvc-manifests
@ -1506,8 +1524,8 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
)
# Deploy PDBs
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_filelink> DESTINATION ${BINARY_DEST_DIR})
if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_filelink> DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
endif()
endif()

View file

@ -2,7 +2,6 @@
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 dada513 <dada513@protonmail.com>
* Copyright (C) 2025 Seth Flynn <getchoo@tuta.io>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -76,15 +75,6 @@ bool isFlatpak()
#endif
}
bool isSelfContained()
{
#ifdef Q_OS_LINUX
return QFileInfo(QCoreApplication::applicationFilePath()).fileName().startsWith("ld-linux");
#else
return false;
#endif
}
bool isSnap()
{
#ifdef Q_OS_LINUX

View file

@ -37,11 +37,6 @@ bool openUrl(const QUrl& url);
*/
bool isFlatpak();
/**
* Determine whether the launcher is running in a self-contained Linux bundle
*/
bool isSelfContained();
/**
* Determine whether the launcher is running in a Snap environment
*/

View file

@ -4,7 +4,6 @@
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
* Copyright (C) 2025 Seth Flynn <getchoo@tuta.io>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -773,34 +772,6 @@ QString ResolveExecutable(QString path)
return pathInfo.absoluteFilePath();
}
std::unique_ptr<QProcess> createProcess(const QString& program, const QStringList& arguments)
{
qDebug() << "Creating process for" << program;
auto proc = std::unique_ptr<QProcess>(new QProcess());
#if defined(Q_OS_LINUX)
if (DesktopServices::isSelfContained()) {
const auto linkerPath = QCoreApplication::applicationFilePath();
qDebug() << "Wrapping" << program << "with self-contained linker at" << linkerPath;
QStringList wrappedArguments;
wrappedArguments << "--inhibit-cache" << program;
wrappedArguments += arguments;
proc->setProgram(linkerPath);
proc->setArguments(wrappedArguments);
} else {
proc->setProgram(program);
proc->setArguments(arguments);
}
#else
proc->setProgram(program);
proc->setArguments(arguments);
#endif
return proc;
}
/**
* Normalize path
*

View file

@ -4,7 +4,6 @@
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
* Copyright (C) 2025 Seth Flynn <getchoo@tuta.io>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -43,13 +42,11 @@
#include <system_error>
#include <QCoreApplication>
#include <QDir>
#include <QFlags>
#include <QLocalServer>
#include <QObject>
#include <QPair>
#include <QProcess>
#include <QThread>
namespace FS {
@ -336,14 +333,6 @@ QString pathTruncate(const QString& path, int depth);
*/
QString ResolveExecutable(QString path);
/**
* Create a QProcess instance
*
* This wrapper is currently only required for wrapping binaries called in
* self-contained AppImages (like those created by `go-appimage`)
*/
std::unique_ptr<QProcess> createProcess(const QString& program, const QStringList& arguments);
/**
* Normalize path
*

View file

@ -271,7 +271,7 @@ bool installIcon(QString root, QString instIconKey)
if (iconList->iconFileExists(instIconKey)) {
iconList->deleteIcon(instIconKey);
}
iconList->installIcon(importIconPath, instIconKey + ".png");
iconList->installIcon(importIconPath, instIconKey + "." + QFileInfo(importIconPath).suffix());
return true;
}
return false;

View file

@ -101,6 +101,21 @@ QJsonArray requireArray(const QJsonDocument& doc, const QString& what)
return doc.array();
}
QJsonDocument parseUntilGarbage(const QByteArray& json, QJsonParseError* error, QString* garbage)
{
auto doc = QJsonDocument::fromJson(json, error);
if (error->error == QJsonParseError::GarbageAtEnd) {
qsizetype offset = error->offset;
QByteArray validJson = json.left(offset);
doc = QJsonDocument::fromJson(validJson, error);
if (garbage)
*garbage = json.right(json.size() - offset);
}
return doc;
}
void writeString(QJsonObject& to, const QString& key, const QString& value)
{
if (!value.isEmpty()) {

View file

@ -107,6 +107,9 @@ QJsonArray toJsonArray(const QList<T>& container)
////////////////// READING ////////////////////
// Attempt to parse JSON up until garbage is encountered
QJsonDocument parseUntilGarbage(const QByteArray& json, QJsonParseError* error = nullptr, QString* garbage = nullptr);
/// @throw JsonException
template <typename T>
T requireIsType(const QJsonValue& value, const QString& what = "Value");

View file

@ -18,89 +18,19 @@ LAUNCHER_NAME=@Launcher_APP_BINARY_NAME@
LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")"
echo "Launcher Dir: ${LAUNCHER_DIR}"
# Set up env.
# Pass our custom variables separately so that the launcher can remove them for child processes
export LAUNCHER_LD_LIBRARY_PATH="${LAUNCHER_DIR}/lib@LIB_SUFFIX@"
export LAUNCHER_LD_PRELOAD=""
export LAUNCHER_QT_PLUGIN_PATH="${LAUNCHER_DIR}/plugins"
export LAUNCHER_QT_FONTPATH="${LAUNCHER_DIR}/fonts"
# Makes the launcher use portals for file picking
export QT_QPA_PLATFORMTHEME=xdgdesktopportal
export LD_LIBRARY_PATH="$LAUNCHER_LD_LIBRARY_PATH:$LD_LIBRARY_PATH"
export LD_PRELOAD="$LAUNCHER_LD_PRELOAD:$LD_PRELOAD"
export QT_PLUGIN_PATH="$LAUNCHER_QT_PLUGIN_PATH:$QT_PLUGIN_PATH"
export QT_FONTPATH="$LAUNCHER_QT_FONTPATH:$QT_FONTPATH"
# Just to be sure...
chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}"
# Detect missing dependencies...
DEPS_LIST=`ldd "${LAUNCHER_DIR}"/plugins/*/*.so 2>/dev/null | grep "not found" | sort -u | awk -vORS=", " '{ print $1 }'`
if [ "x$DEPS_LIST" = "x" ]; then
# We have all our dependencies. Run the launcher.
echo "No missing dependencies found."
ARGS=("${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}")
# Just to be sure...
chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}"
ARGS=("${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}")
if [ -f portable.txt ]; then
ARGS+=("-d" "${LAUNCHER_DIR}")
fi
ARGS+=("$@")
# Run the launcher
exec -a "${ARGS[@]}"
# Run the launcher in valgrind
# valgrind --log-file="valgrind.log" --leak-check=full --track-origins=yes "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" -d "${LAUNCHER_DIR}" "$@"
# Run the launcher with callgrind, delay instrumentation
# valgrind --log-file="valgrind.log" --tool=callgrind --instr-atstart=no "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" -d "${LAUNCHER_DIR}" "$@"
# use callgrind_control -i on/off to profile actions
# Exit with launcher's exit code.
# exit $?
else
# apt
if which apt-file &>/dev/null; then
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do apt-file -l search $LIBRARY; done`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
INSTALL_CMD="sudo apt-get install $COMMAND_LIBS"
# pacman
elif which pkgfile &>/dev/null; then
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do pkgfile $LIBRARY; done`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
INSTALL_CMD="sudo pacman -S $COMMAND_LIBS"
# dnf
elif which dnf &>/dev/null; then
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do dnf whatprovides -q $LIBRARY; done`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | grep -v 'Repo' | sort -u | awk -vORS=" " '{ print $1 }'`
INSTALL_CMD="sudo dnf install $COMMAND_LIBS"
# yum
elif which yum &>/dev/null; then
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do yum whatprovides $LIBRARY; done`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
INSTALL_CMD="sudo yum install $COMMAND_LIBS"
# zypper
elif which zypper &>/dev/null; then
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do zypper wp $LIBRARY; done`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
INSTALL_CMD="sudo zypper install $COMMAND_LIBS"
# emerge
elif which pfl &>/dev/null; then
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do pfl $LIBRARY; done`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
INSTALL_CMD="sudo emerge $COMMAND_LIBS"
fi
MESSAGE="Error: The launcher is missing the following libraries that it needs to work correctly:\n\t${DEPS_LIST}\nPlease install them from your distribution's package manager."
MESSAGE="$MESSAGE\n\nHint (please apply common sense): $INSTALL_CMD\n"
printerror "$MESSAGE"
exit 1
if [ -f portable.txt ]; then
ARGS+=("-d" "${LAUNCHER_DIR}")
fi
ARGS+=("$@")
# Run the launcher
exec -a "${ARGS[@]}"

View file

@ -0,0 +1,17 @@
#pragma once
#ifndef PRISM_PRECOMPILED_BASE_HEADERS_H
#define PRISM_PRECOMPILED_BASE_HEADERS_H
#include <algorithm>
#include <functional>
#include <memory>
#include <optional>
#include <utility>
#include <BuildConfig.h>
#include <FileSystem.h>
#include <Json.h>
#include <Version.h>
#include <sys.h>
#endif // PRISM_PRECOMPILED_BASE_HEADERS_H

View file

@ -0,0 +1,59 @@
#pragma once
#ifndef PRISM_PRECOMPILED_QTCORE_HEADERS_H
#define PRISM_PRECOMPILED_QTCORE_HEADERS_H
#include <QEvent>
#include <QMetaType>
#include <QObject>
#include <QVariant>
#include <QDebug>
#include <QCoreApplication>
// collections
#include <QByteArray>
#include <QHash>
#include <QList>
#include <QMap>
#include <QPair>
#include <QSet>
#include <QString>
#include <QStringList>
#include <QDateTime>
#include <QAbstractListModel>
#include <QCryptographicHash>
#include <QFuture>
#include <QFutureWatcher>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QFileInfo>
#include <QFileSystemWatcher>
#include <QMimeData>
#include <QSaveFile>
#include <QStandardPaths>
#include <QMutex>
#include <QProcess>
#include <QRegularExpression>
#include <QSortFilterProxyModel>
#include <QTimer>
#include <QUrl>
#include <QUuid>
#include <QtMath>
#endif // PRISM_PRECOMPILED_QTCORE_HEADERS_H

View file

@ -0,0 +1,47 @@
#pragma once
#ifndef PRISM_PRECOMPILED_QTGUI_HEADERS_H
#define PRISM_PRECOMPILED_QTGUI_HEADERS_H
#include <QApplication>
#include <QMainWindow>
#include <QClipboard>
#include <QWidget>
#include <QAction>
#include <QKeyEvent>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QMessageBox>
#include <QCheckBox>
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
#include <QPushButton>
#include <QStyle>
#include <QStyledItemDelegate>
#include <QScrollBar>
#include <QTabBar>
#include <QToolButton>
#include <QHeaderView>
#include <QListView>
#include <QTreeView>
#include <QLayout>
#include <QVBoxLayout>
#include <QIcon>
#include <QPainter>
#include <QPixmap>
#include <QPixmapCache>
#endif // PRISM_PRECOMPILED_GUI_HEADERS_H

View file

@ -91,6 +91,7 @@
#include <QActionGroup>
#include <QMainWindow>
#include <QScreen>
#include <QStandardPaths>
#include <QWindow>
#ifdef Q_OS_LINUX
@ -588,6 +589,16 @@ QStringList MinecraftInstance::javaArguments()
"minecraft.exe.heapdump");
#endif
// LWJGL2 reads `LWJGL_DISABLE_XRANDR` to force disable xrandr usage and fall back to xf86videomode.
// It *SHOULD* check for the executable to exist before trying to use it for queries but it doesnt,
// so WE can and force disable xrandr if it is not available.
#ifdef Q_OS_LINUX
// LWJGL2 is "org.lwjgl" LWJGL3 is "org.lwjgl3"
if (m_components->getComponent("org.lwjgl") != nullptr && QStandardPaths::findExecutable("xrandr").isEmpty()) {
args << QString("-DLWJGL_DISABLE_XRANDR=true");
}
#endif
int min = settings()->get("MinMemAlloc").toInt();
int max = settings()->get("MaxMemAlloc").toInt();
if (min < max) {

View file

@ -669,7 +669,7 @@ void AccountList::beginActivity()
void AccountList::endActivity()
{
if (m_activityCount == 0) {
qWarning() << m_name << " - Activity count would become below zero";
qWarning() << "Activity count would become below zero";
return;
}
bool deactivating = m_activityCount == 1;

View file

@ -111,7 +111,6 @@ class AccountList : public QAbstractListModel {
void endActivity();
private:
const char* m_name;
uint32_t m_activityCount = 0;
signals:
void listChanged();

View file

@ -207,7 +207,13 @@ void ResourceFolderModel::installResourceWithFlameMetadata(QString path, ModPlat
bool ResourceFolderModel::uninstallResource(const QString& file_name, bool preserve_metadata)
{
for (auto& resource : m_resources) {
if (resource->fileinfo().fileName() == file_name) {
auto resourceFileInfo = resource->fileinfo();
auto resourceFileName = resource->fileinfo().fileName();
if (!resource->enabled() && resourceFileName.endsWith(".disabled")) {
resourceFileName.chop(9);
}
if (resourceFileName == file_name) {
auto res = resource->destroy(indexDir(), preserve_metadata, false);
update();

View file

@ -168,10 +168,15 @@ bool processZIP(DataPack* pack, ProcessingLevel level)
// https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
bool processMCMeta(DataPack* pack, QByteArray&& raw_data)
{
try {
auto json_doc = QJsonDocument::fromJson(raw_data);
auto pack_obj = Json::requireObject(json_doc.object(), "pack", {});
QJsonParseError parse_error;
auto json_doc = Json::parseUntilGarbage(raw_data, &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
qWarning() << "Failed to parse JSON:" << parse_error.errorString();
return false;
}
try {
auto pack_obj = Json::requireObject(json_doc.object(), "pack", {});
pack->setPackFormat(pack_obj["pack_format"].toInt());
pack->setDescription(DataPackUtils::processComponent(pack_obj.value("description")));
} catch (Json::JsonException& e) {

View file

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
* Copyright (c) 2023-2025 Trial97 <alexandru.tripon97@gmail.com>
* Copyright (c) 2025 Rinth, Inc.
*
* 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
@ -22,29 +23,89 @@
#include "FileSystem.h"
static void setAlpha(QImage& image, const QRect& region, const int alpha)
{
for (int y = region.top(); y < region.bottom(); ++y) {
QRgb* line = reinterpret_cast<QRgb*>(image.scanLine(y));
for (int x = region.left(); x < region.right(); ++x) {
QRgb pixel = line[x];
line[x] = qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), alpha);
}
}
}
static void doNotchTransparencyHack(QImage& image)
{
for (int y = 0; y < 32; y++) {
QRgb* line = reinterpret_cast<QRgb*>(image.scanLine(y));
for (int x = 32; x < 64; x++) {
if (qAlpha(line[x]) < 128) {
return;
}
}
}
setAlpha(image, { 32, 0, 32, 32 }, 0);
}
static QImage improveSkin(QImage skin)
{
int height = skin.height();
int width = skin.width();
if (width != 64 || (height != 32 && height != 64)) { // this is no minecraft skin
return skin;
}
// It seems some older skins may use this format, which can't be drawn onto
// https://github.com/PrismLauncher/PrismLauncher/issues/4032
// https://doc.qt.io/qt-6/qpainter.html#begin
if (skin.format() == QImage::Format_Indexed8) {
skin = skin.convertToFormat(QImage::Format_RGB32);
skin = skin.convertToFormat(QImage::Format_ARGB32);
}
if (skin.size() == QSize(64, 32)) { // old format
auto isLegacy = height == 32; // old format
if (isLegacy) {
auto newSkin = QImage(QSize(64, 64), skin.format());
newSkin.fill(Qt::transparent);
QPainter p(&newSkin);
p.drawImage(QPoint(0, 0), skin.copy(QRect(0, 0, 64, 32))); // copy head
p.drawImage(0, 0, skin);
auto leg = skin.copy(QRect(0, 16, 16, 16));
p.drawImage(QPoint(16, 48), leg); // copy leg
auto copyRect = [&p, &newSkin](int startX, int startY, int offsetX, int offsetY, int sizeX, int sizeY) {
QImage region = newSkin.copy(startX, startY, sizeX, sizeY);
region = region.mirrored(true, false);
auto arm = skin.copy(QRect(40, 16, 16, 16));
p.drawImage(QPoint(32, 48), arm); // copy arm
return newSkin;
p.drawImage(startX + offsetX, startY + offsetY, region);
};
static const struct {
int x;
int y;
int offsetX;
int offsetY;
int width;
int height;
} faces[] = {
{ 4, 16, 16, 32, 4, 4 }, { 8, 16, 16, 32, 4, 4 }, { 0, 20, 24, 32, 4, 12 }, { 4, 20, 16, 32, 4, 12 },
{ 8, 20, 8, 32, 4, 12 }, { 12, 20, 16, 32, 4, 12 }, { 44, 16, -8, 32, 4, 4 }, { 48, 16, -8, 32, 4, 4 },
{ 40, 20, 0, 32, 4, 12 }, { 44, 20, -8, 32, 4, 12 }, { 48, 20, -16, 32, 4, 12 }, { 52, 20, -8, 32, 4, 12 },
};
for (const auto& face : faces) {
copyRect(face.x, face.y, face.offsetX, face.offsetY, face.width, face.height);
}
doNotchTransparencyHack(newSkin);
skin = newSkin;
}
static const QRect opaqueParts[] = {
{ 0, 0, 32, 16 },
{ 0, 16, 64, 16 },
{ 16, 48, 32, 16 },
};
for (const auto& p : opaqueParts) {
setAlpha(skin, p, 255);
}
return skin;
}
static QImage getSkin(const QString path)
{
return improveSkin(QImage(path));
@ -66,8 +127,8 @@ static QImage generatePreviews(QImage texture, bool slim)
paint.drawImage(4, 22, texture.copy(4, 20, 4, 12));
paint.drawImage(4, 22, texture.copy(4, 36, 4, 12));
// left leg
paint.drawImage(8, 22, texture.copy(4, 52, 4, 12));
paint.drawImage(8, 22, texture.copy(20, 52, 4, 12));
paint.drawImage(8, 22, texture.copy(4, 52, 4, 12));
auto armWidth = slim ? 3 : 4;
auto armPosX = slim ? 1 : 0;
@ -89,8 +150,8 @@ static QImage generatePreviews(QImage texture, bool slim)
paint.drawImage(24, 22, texture.copy(12, 20, 4, 12));
paint.drawImage(24, 22, texture.copy(12, 36, 4, 12));
// left leg
paint.drawImage(28, 22, texture.copy(12, 52, 4, 12));
paint.drawImage(28, 22, texture.copy(28, 52, 4, 12));
paint.drawImage(28, 22, texture.copy(12, 52, 4, 12));
// right arm
paint.drawImage(armPosX + 20, 10, texture.copy(48 + armWidth, 20, armWidth, 12));

View file

@ -126,7 +126,8 @@ ModPlatform::IndexedVersion Modrinth::loadIndexedPackVersion(QJsonObject& obj, Q
return {};
}
for (auto mcVer : versionArray) {
file.mcVersion.append(ModrinthAPI::mapMCVersionFromModrinth(mcVer.toString()));
file.mcVersion.append({ ModrinthAPI::mapMCVersionFromModrinth(mcVer.toString()),
mcVer.toString() }); // double this so we can check both strings when filtering
}
auto loaders = Json::requireArray(obj, "loaders");
for (auto loader : loaders) {

View file

@ -57,7 +57,12 @@ SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct)
{
m_ui->setupUi(this);
m_skinPreview = new SkinOpenGLWindow(this, palette().color(QPalette::Normal, QPalette::Base));
if (SkinOpenGLWindow::hasOpenGL()) {
m_skinPreview = new SkinOpenGLWindow(this, palette().color(QPalette::Normal, QPalette::Base));
} else {
m_skinPreviewLabel = new QLabel(this);
m_skinPreviewLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}
setWindowModality(Qt::WindowModal);
@ -92,7 +97,9 @@ SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct)
connect(contentsWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &SkinManageDialog::selectionChanged);
connect(m_ui->listView, &QListView::customContextMenuRequested, this, &SkinManageDialog::show_context_menu);
connect(m_ui->elytraCB, &QCheckBox::stateChanged, this, [this]() {
m_skinPreview->setElytraVisible(m_ui->elytraCB->isChecked());
if (m_skinPreview) {
m_skinPreview->setElytraVisible(m_ui->elytraCB->isChecked());
}
on_capeCombo_currentIndexChanged(0);
});
@ -103,13 +110,19 @@ SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct)
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
m_ui->skinLayout->insertWidget(0, QWidget::createWindowContainer(m_skinPreview, this));
if (m_skinPreview) {
m_ui->skinLayout->insertWidget(0, QWidget::createWindowContainer(m_skinPreview, this));
} else {
m_ui->skinLayout->insertWidget(0, m_skinPreviewLabel);
}
}
SkinManageDialog::~SkinManageDialog()
{
delete m_ui;
delete m_skinPreview;
if (m_skinPreview) {
delete m_skinPreview;
}
}
void SkinManageDialog::activated(QModelIndex index)
@ -131,7 +144,12 @@ void SkinManageDialog::selectionChanged(QItemSelection selected, [[maybe_unused]
if (!skin)
return;
m_skinPreview->updateScene(skin);
if (m_skinPreview) {
m_skinPreview->updateScene(skin);
} else {
m_skinPreviewLabel->setPixmap(
QPixmap::fromImage(skin->getPreview()).scaled(m_skinPreviewLabel->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
}
m_ui->capeCombo->setCurrentIndex(m_capesIdx.value(skin->getCapeId()));
m_ui->steveBtn->setChecked(skin->getModel() == SkinModel::CLASSIC);
m_ui->alexBtn->setChecked(skin->getModel() == SkinModel::SLIM);
@ -165,17 +183,17 @@ void SkinManageDialog::on_fileBtn_clicked()
QPixmap previewCape(QImage capeImage, bool elytra = false)
{
if (elytra) {
auto wing = capeImage.copy(34, 0, 12, 22);
auto wing = capeImage.copy(34, 2, 12, 20);
QImage mirrored = wing.mirrored(true, false);
QImage combined(wing.width() * 2 - 2, wing.height(), capeImage.format());
QImage combined(wing.width() * 2 + 1, wing.height() + 14, capeImage.format());
combined.fill(Qt::transparent);
QPainter painter(&combined);
painter.drawImage(0, 0, wing);
painter.drawImage(wing.width() - 2, 0, mirrored);
painter.drawImage(0, 7, wing);
painter.drawImage(wing.width() + 1, 7, mirrored);
painter.end();
return QPixmap::fromImage(combined.scaled(96, 176, Qt::IgnoreAspectRatio, Qt::FastTransformation));
return QPixmap::fromImage(combined.scaled(84, 128, Qt::KeepAspectRatio, Qt::FastTransformation));
}
return QPixmap::fromImage(capeImage.copy(1, 1, 10, 16).scaled(80, 128, Qt::IgnoreAspectRatio, Qt::FastTransformation));
}
@ -244,10 +262,17 @@ void SkinManageDialog::on_capeCombo_currentIndexChanged(int index)
} else {
m_ui->capeImage->clear();
}
m_skinPreview->updateCape(cape);
if (m_skinPreview) {
m_skinPreview->updateCape(cape);
}
if (auto skin = getSelectedSkin(); skin) {
skin->setCapeId(id.toString());
m_skinPreview->updateScene(skin);
if (m_skinPreview) {
m_skinPreview->updateScene(skin);
} else {
m_skinPreviewLabel->setPixmap(
QPixmap::fromImage(skin->getPreview()).scaled(m_skinPreviewLabel->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
}
}
}
@ -255,7 +280,12 @@ void SkinManageDialog::on_steveBtn_toggled(bool checked)
{
if (auto skin = getSelectedSkin(); skin) {
skin->setModel(checked ? SkinModel::CLASSIC : SkinModel::SLIM);
m_skinPreview->updateScene(skin);
if (m_skinPreview) {
m_skinPreview->updateScene(skin);
} else {
m_skinPreviewLabel->setPixmap(
QPixmap::fromImage(skin->getPreview()).scaled(m_skinPreviewLabel->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
}
}
}
@ -545,6 +575,10 @@ void SkinManageDialog::resizeEvent(QResizeEvent* event)
} else {
m_ui->capeImage->clear();
}
if (auto skin = getSelectedSkin(); skin && !m_skinPreview) {
m_skinPreviewLabel->setPixmap(
QPixmap::fromImage(skin->getPreview()).scaled(m_skinPreviewLabel->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
}
}
SkinModel* SkinManageDialog::getSelectedSkin()

View file

@ -20,6 +20,7 @@
#include <QDialog>
#include <QItemSelection>
#include <QLabel>
#include <QPixmap>
#include "minecraft/auth/MinecraftAccount.h"
@ -68,4 +69,5 @@ class SkinManageDialog : public QDialog, public SkinProvider {
QHash<QString, QImage> m_capes;
QHash<QString, int> m_capesIdx;
SkinOpenGLWindow* m_skinPreview = nullptr;
QLabel* m_skinPreviewLabel = nullptr;
};

View file

@ -31,32 +31,50 @@ Scene::Scene(const QImage& skin, bool slim, const QImage& cape) : QOpenGLFunctio
m_staticComponents = {
// head
new opengl::BoxGeometry(QVector3D(8, 8, 8), QVector3D(0, 4, 0), QPoint(0, 0), QVector3D(8, 8, 8)),
new opengl::BoxGeometry(QVector3D(9, 9, 9), QVector3D(0, 4, 0), QPoint(32, 0), QVector3D(8, 8, 8)),
// body
new opengl::BoxGeometry(QVector3D(8, 12, 4), QVector3D(0, -6, 0), QPoint(16, 16), QVector3D(8, 12, 4)),
new opengl::BoxGeometry(QVector3D(8.5, 12.5, 4.5), QVector3D(0, -6, 0), QPoint(16, 32), QVector3D(8, 12, 4)),
// right leg
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(-1.9, -18, -0.1), QPoint(0, 16), QVector3D(4, 12, 4)),
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(-1.9, -18, -0.1), QPoint(0, 32), QVector3D(4, 12, 4)),
// left leg
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(1.9, -18, -0.1), QPoint(16, 48), QVector3D(4, 12, 4)),
};
m_staticComponentsOverlay = {
// head
new opengl::BoxGeometry(QVector3D(9, 9, 9), QVector3D(0, 4, 0), QPoint(32, 0), QVector3D(8, 8, 8)),
// body
new opengl::BoxGeometry(QVector3D(8.5, 12.5, 4.5), QVector3D(0, -6, 0), QPoint(16, 32), QVector3D(8, 12, 4)),
// right leg
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(-1.9, -18, -0.1), QPoint(0, 32), QVector3D(4, 12, 4)),
// left leg
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(1.9, -18, -0.1), QPoint(0, 48), QVector3D(4, 12, 4)),
};
m_normalArms = {
// Right Arm
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(-6, -6, 0), QPoint(40, 16), QVector3D(4, 12, 4)),
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(-6, -6, 0), QPoint(40, 32), QVector3D(4, 12, 4)),
// Left Arm
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(6, -6, 0), QPoint(32, 48), QVector3D(4, 12, 4)),
};
m_normalArmsOverlay = {
// Right Arm
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(-6, -6, 0), QPoint(40, 32), QVector3D(4, 12, 4)),
// Left Arm
new opengl::BoxGeometry(QVector3D(4.5, 12.5, 4.5), QVector3D(6, -6, 0), QPoint(48, 48), QVector3D(4, 12, 4)),
};
m_slimArms = {
// Right Arm
new opengl::BoxGeometry(QVector3D(3, 12, 4), QVector3D(-5.5, -6, 0), QPoint(40, 16), QVector3D(3, 12, 4)),
new opengl::BoxGeometry(QVector3D(3.5, 12.5, 4.5), QVector3D(-5.5, -6, 0), QPoint(40, 32), QVector3D(3, 12, 4)),
// Left Arm
new opengl::BoxGeometry(QVector3D(3, 12, 4), QVector3D(5.5, -6, 0), QPoint(32, 48), QVector3D(3, 12, 4)),
};
m_slimArmsOverlay = {
// Right Arm
new opengl::BoxGeometry(QVector3D(3.5, 12.5, 4.5), QVector3D(-5.5, -6, 0), QPoint(40, 32), QVector3D(3, 12, 4)),
// Left Arm
new opengl::BoxGeometry(QVector3D(3.5, 12.5, 4.5), QVector3D(5.5, -6, 0), QPoint(48, 48), QVector3D(3, 12, 4)),
};
@ -88,7 +106,8 @@ Scene::Scene(const QImage& skin, bool slim, const QImage& cape) : QOpenGLFunctio
}
Scene::~Scene()
{
for (auto array : { m_staticComponents, m_normalArms, m_slimArms, m_elytra }) {
for (auto array :
{ m_staticComponents, m_normalArms, m_slimArms, m_elytra, m_staticComponentsOverlay, m_normalArmsOverlay, m_slimArmsOverlay }) {
for (auto g : array) {
delete g;
}
@ -106,7 +125,8 @@ void Scene::draw(QOpenGLShaderProgram* program)
{
m_skinTexture->bind();
program->setUniformValue("texture", 0);
for (auto toDraw : { m_staticComponents, m_slim ? m_slimArms : m_normalArms }) {
for (auto toDraw : { m_staticComponents, m_slim ? m_slimArms : m_normalArms, m_staticComponentsOverlay,
m_slim ? m_slimArmsOverlay : m_normalArmsOverlay }) {
for (auto g : toDraw) {
g->draw(program);
}

View file

@ -38,6 +38,9 @@ class Scene : protected QOpenGLFunctions {
QList<BoxGeometry*> m_staticComponents;
QList<BoxGeometry*> m_normalArms;
QList<BoxGeometry*> m_slimArms;
QList<BoxGeometry*> m_staticComponentsOverlay;
QList<BoxGeometry*> m_normalArmsOverlay;
QList<BoxGeometry*> m_slimArmsOverlay;
BoxGeometry* m_cape = nullptr;
QList<BoxGeometry*> m_elytra;
QOpenGLTexture* m_skinTexture = nullptr;

View file

@ -23,6 +23,7 @@
#include <QVector2D>
#include <QVector3D>
#include <QtMath>
#include <functional>
#include "minecraft/skins/SkinModel.h"
#include "rainbow.h"
@ -270,11 +271,12 @@ void SkinOpenGLWindow::updateCape(const QImage& cape)
QColor calculateContrastingColor(const QColor& color)
{
constexpr float contrast = 0.2;
auto luma = Rainbow::luma(color);
if (luma < 0.5) {
constexpr float contrast = 0.05;
return Rainbow::lighten(color, contrast);
} else {
constexpr float contrast = 0.2;
return Rainbow::darken(color, contrast);
}
}
@ -282,11 +284,14 @@ QColor calculateContrastingColor(const QColor& color)
QImage generateChessboardImage(int width, int height, int tileSize, QColor baseColor)
{
QImage image(width, height, QImage::Format_RGB888);
auto white = baseColor;
auto black = calculateContrastingColor(baseColor);
bool isDarkBase = Rainbow::luma(baseColor) < 0.5;
float contrast = isDarkBase ? 0.05 : 0.45;
auto contrastFunc = std::bind(isDarkBase ? Rainbow::lighten : Rainbow::darken, std::placeholders::_1, contrast, 1.0);
auto white = contrastFunc(baseColor);
auto black = contrastFunc(calculateContrastingColor(baseColor));
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
bool isWhite = ((x / tileSize) % 2) == ((y / tileSize) % 2);
bool isWhite = ((x / tileSize) + (y / tileSize)) % 2 == 0;
image.setPixelColor(x, y, isWhite ? white : black);
}
}
@ -325,3 +330,9 @@ void SkinOpenGLWindow::setElytraVisible(bool visible)
if (m_scene)
m_scene->setElytraVisible(visible);
}
bool SkinOpenGLWindow::hasOpenGL()
{
QOpenGLContext ctx;
return ctx.create();
}

View file

@ -45,6 +45,8 @@ class SkinOpenGLWindow : public QOpenGLWindow, protected QOpenGLFunctions {
void updateCape(const QImage& cape);
void setElytraVisible(bool visible);
static bool hasOpenGL();
protected:
void mousePressEvent(QMouseEvent* e) override;
void mouseReleaseEvent(QMouseEvent* e) override;

View file

@ -10,7 +10,7 @@
<height>620</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>

View file

@ -113,6 +113,7 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared
m_model->loadColumns(ui->treeView);
connect(ui->treeView->header(), &QHeaderView::sectionResized, this, [this] { m_model->saveColumns(ui->treeView); });
connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged);
updateActions();
}
ExternalResourcesPage::~ExternalResourcesPage()

View file

@ -80,7 +80,13 @@ void McClient::parseResponse()
Q_UNUSED(readVarInt(m_resp)); // json length
// 'resp' should now be the JSON string
QJsonDocument doc = QJsonDocument::fromJson(m_resp);
QJsonParseError parseError;
QJsonDocument doc = Json::parseUntilGarbage(m_resp, &parseError);
if (parseError.error != QJsonParseError::NoError) {
qDebug() << "Failed to parse JSON:" << parseError.errorString();
emitFail(parseError.errorString());
return;
}
emitSucceed(doc.object());
}

View file

@ -1,13 +1,19 @@
#include <QFutureWatcher>
#include <Json.h>
#include "Exception.h"
#include "McClient.h"
#include "McResolver.h"
#include "ServerPingTask.h"
unsigned getOnlinePlayers(QJsonObject data)
{
return Json::requireInteger(Json::requireObject(data, "players"), "online");
try {
return Json::requireInteger(Json::requireObject(data, "players"), "online");
} catch (Exception& e) {
qWarning() << "server ping failed to parse response" << e.what();
return 0;
}
}
void ServerPingTask::executeTask()

View file

@ -19,10 +19,10 @@
</font>
</property>
<property name="text">
<string>Note: If your FTB instances are not in the default location, select it using the button next to search.</string>
<string>Note: Many recent FTB modpacks are also available from CurseForge! Also, if your FTB instances are not in the default location, select it using the button next to search.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
@ -47,8 +47,7 @@
<string/>
</property>
<property name="icon">
<iconset theme="viewfolder">
<normaloff>.</normaloff>.</iconset>
<iconset theme="viewfolder"/>
</property>
<property name="flat">
<bool>true</bool>
@ -82,7 +81,7 @@
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>

View file

@ -203,7 +203,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
++it;
#endif
for (const auto& version : m_current->versions) {
m_ui->versionSelectionBox->addItem(version.getVersionDisplayString(), QVariant(version.addonId));
m_ui->versionSelectionBox->addItem(version.getVersionDisplayString(), QVariant(version.fileId));
}
QVariant current_updated;
@ -227,9 +227,9 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
for (auto version : m_current->versions) {
if (!version.version.contains(version.version))
m_ui->versionSelectionBox->addItem(QString("%1 - %2").arg(version.version, version.version_number),
QVariant(version.addonId));
QVariant(version.fileId));
else
m_ui->versionSelectionBox->addItem(version.version, QVariant(version.addonId));
m_ui->versionSelectionBox->addItem(version.version, QVariant(version.fileId));
}
suggestCurrent();
@ -312,7 +312,7 @@ void ModrinthPage::suggestCurrent()
}
for (auto& ver : m_current->versions) {
if (ver.addonId == m_selectedVersion) {
if (ver.fileId == m_selectedVersion) {
QMap<QString, QString> extra_info;
extra_info.insert("pack_id", m_current->addonId.toString());
extra_info.insert("pack_version_id", ver.fileId.toString());

View file

@ -26,6 +26,7 @@
#include <QDebug>
#include <QDir>
#include <QMessageBox>
#include <QProcess>
#include <QProgressDialog>
#include <QSettings>
#include <QTimer>
@ -34,7 +35,6 @@
#include "StringUtils.h"
#include "BuildConfig.h"
#include "FileSystem.h"
#include "ui/dialogs/UpdateAvailableDialog.h"
@ -97,9 +97,14 @@ void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
progress.show();
QCoreApplication::processEvents();
QProcess proc;
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
#if defined Q_OS_WIN32
exe_name.append(".exe");
auto env = QProcessEnvironment::systemEnvironment();
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
proc.setProcessEnvironment(env);
#else
exe_name = QString("bin/%1").arg(exe_name);
#endif
@ -108,21 +113,15 @@ void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
if (priv->allowBeta)
args.append("--pre-release");
auto proc = FS::createProcess(priv->appDir.absoluteFilePath(exe_name), args);
#if defined Q_OS_WIN32
auto env = QProcessEnvironment::systemEnvironment();
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
proc->setProcessEnvironment(env);
#endif
proc->start(proc->program(), proc->arguments());
auto result_start = proc->waitForStarted(5000);
proc.start(priv->appDir.absoluteFilePath(exe_name), args);
auto result_start = proc.waitForStarted(5000);
if (!result_start) {
auto err = proc->error();
auto err = proc.error();
qDebug() << "Failed to start updater after 5 seconds."
<< "reason:" << err << proc->errorString();
<< "reason:" << err << proc.errorString();
auto msgBox =
QMessageBox(QMessageBox::Information, tr("Update Check Failed"),
tr("Failed to start after 5 seconds\nReason: %1.").arg(proc->errorString()), QMessageBox::Ok, priv->parent);
tr("Failed to start after 5 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent);
msgBox.setMinimumWidth(460);
msgBox.adjustSize();
msgBox.exec();
@ -134,16 +133,16 @@ void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
}
QCoreApplication::processEvents();
auto result_finished = proc->waitForFinished(60000);
auto result_finished = proc.waitForFinished(60000);
if (!result_finished) {
proc->kill();
auto err = proc->error();
auto output = proc->readAll();
proc.kill();
auto err = proc.error();
auto output = proc.readAll();
qDebug() << "Updater failed to close after 60 seconds."
<< "reason:" << err << proc->errorString();
<< "reason:" << err << proc.errorString();
auto msgBox =
QMessageBox(QMessageBox::Information, tr("Update Check Failed"),
tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc->errorString()), QMessageBox::Ok, priv->parent);
tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent);
msgBox.setDetailedText(output);
msgBox.setMinimumWidth(460);
msgBox.adjustSize();
@ -155,10 +154,10 @@ void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
return;
}
auto exit_code = proc->exitCode();
auto exit_code = proc.exitCode();
auto std_output = proc->readAllStandardOutput();
auto std_error = proc->readAllStandardError();
auto std_output = proc.readAllStandardOutput();
auto std_error = proc.readAllStandardError();
progress.hide();
QCoreApplication::processEvents();
@ -336,9 +335,14 @@ void PrismExternalUpdater::offerUpdate(const QString& version_name, const QStrin
void PrismExternalUpdater::performUpdate(const QString& version_tag)
{
QProcess proc;
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
#if defined Q_OS_WIN32
exe_name.append(".exe");
auto env = QProcessEnvironment::systemEnvironment();
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
proc.setProcessEnvironment(env);
#else
exe_name = QString("bin/%1").arg(exe_name);
#endif
@ -347,16 +351,9 @@ void PrismExternalUpdater::performUpdate(const QString& version_tag)
if (priv->allowBeta)
args.append("--pre-release");
auto proc = FS::createProcess(exe_name, args);
#if defined Q_OS_WIN32
auto env = QProcessEnvironment::systemEnvironment();
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
proc->setProcessEnvironment(env);
#endif
auto result = proc->startDetached(priv->appDir.absoluteFilePath(exe_name), args);
auto result = proc.startDetached(priv->appDir.absoluteFilePath(exe_name), args);
if (!result) {
qDebug() << "Failed to start updater:" << proc->error() << proc->errorString();
qDebug() << "Failed to start updater:" << proc.error() << proc.errorString();
}
QCoreApplication::exit();
}

View file

@ -124,19 +124,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
logToConsole = parser.isSet("debug");
QString origCwdPath = QDir::currentPath();
#if defined(Q_OS_LINUX)
// NOTE(@getchoo): In order for `go-appimage` to generate self-contained AppImages, it executes apps from a bundled linker at
// <root>/lib64
// This is not the path to our actual binary, which we want
QString binPath;
if (DesktopServices::isSelfContained()) {
binPath = FS::PathCombine(applicationDirPath(), "../usr/bin");
} else {
binPath = applicationDirPath();
}
#else
QString binPath = applicationDirPath();
#endif
{ // find data director
// Root path is used for updates and portable data
@ -819,16 +807,13 @@ QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset)
bool PrismUpdaterApp::callAppImageUpdate()
{
auto appimage_path = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE"));
QProcess proc = QProcess();
qDebug() << "Calling: AppImageUpdate" << appimage_path;
const auto program = FS::PathCombine(m_rootPath, "bin", "AppImageUpdate.AppImage");
auto proc = FS::createProcess(program, { appimage_path });
if (!proc) {
qCritical() << "Unable to create process:" << program;
return false;
}
auto result = proc->startDetached();
proc.setProgram(FS::PathCombine(m_rootPath, "bin", "AppImageUpdate.AppImage"));
proc.setArguments({ appimage_path });
auto result = proc.startDetached();
if (!result)
qDebug() << "Failed to start AppImageUpdate reason:" << proc->errorString();
qDebug() << "Failed to start AppImageUpdate reason:" << proc.errorString();
return result;
}

View file

@ -63,6 +63,7 @@ symlinkJoin {
buildInputs = [
kdePackages.qtbase
kdePackages.qtimageformats
kdePackages.qtsvg
]
++ lib.optional (

View file

@ -32,6 +32,7 @@ set(Launcher_MetaInfo "program_info/${Launcher_AppID}.metainfo.xml" PARENT_SCOPE
set(Launcher_PNG_256 "program_info/${Launcher_AppID}_256.png" PARENT_SCOPE)
set(Launcher_SVG "program_info/${Launcher_SVGFileName}" PARENT_SCOPE)
set(Launcher_Branding_ICNS "program_info/prismlauncher.icns" PARENT_SCOPE)
set(Launcher_Branding_MAC_ICON "program_info/PrismLauncher.icon" PARENT_SCOPE)
set(Launcher_Branding_ICO "program_info/prismlauncher.ico")
set(Launcher_Branding_ICO "${Launcher_Branding_ICO}" PARENT_SCOPE)
set(Launcher_Branding_WindowsRC "program_info/prismlauncher.rc" PARENT_SCOPE)

View file

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="48"
height="48"
version="1.1"
viewBox="0 0 12.7 12.7"
id="svg_combined"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<title>Prism Launcher Logo</title>
<g
transform="matrix(0.88,0,0,0.88,-10.906,-1.2421)"
id="g32_top"
style="display:inline">
<g
transform="translate(13.26,2.2776)"
id="g28"
style="display:inline">
<path
transform="matrix(0.96975,0,0,0.96975,0.1921,0.1921)"
d="m 6.3498,2.9393 c -0.34105,0 -2.7827,1.4099 -2.9532,1.7052 L 6.3498,9.7602 9.3036,4.6445 C 9.13308,4.34915 6.6909,2.9393 6.3498,2.9393 Z"
fill="#ffffff"
stroke-width="0.26458"
id="path26"
style="display:inline" />
</g>
</g>
<g
transform="matrix(0.88,0,0,0.88,-10.906,-1.2421)"
id="g32"
style="display:inline">
<path
d="m 16.746,6.9737 2.8639,4.9609 c 0.33073,0 2.6991,-1.3672 2.8644,-1.6536 0.16536,-0.28642 0.16536,-3.0209 0,-3.3073 l -2.8644,1.6536 z"
fill="#dfdfdf"
stroke-width="0.26458"
id="path30"
style="display:inline" />
</g>
<path
d="m 3.8299,4.8948 c -0.14551,0.25205 -0.14553,2.6584 0,2.9104 0.14553,0.25204 2.2292,1.4552 2.5203,1.4552 V 6.35 Z"
fill="#d6d2d2"
stroke-width="0.26458"
id="path34"
style="display:inline" />
<metadata
id="metadata64">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>Prism Launcher Logo</dc:title>
<dc:date>19/10/2022</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Prism Launcher</dc:title>
</cc:Agent>
</dc:creator>
<dc:contributor>
<cc:Agent>
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
</cc:Agent>
</dc:contributor>
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
<dc:publisher>
<cc:Agent>
<dc:title>Prism Launcher</dc:title>
</cc:Agent>
</dc:publisher>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 3 KiB

View file

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="48"
height="48"
version="1.1"
viewBox="0 0 12.7 12.7"
id="svg_rainbow"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<title>Prism Launcher Logo</title>
<g stroke-width="0.26458">
<path
d="M 6.35,0.52917 3.8292,4.89477 6.35,6.34997 8.8703,4.89477 8.97985,1.79517 C 7.82875,1.13058 6.64105,0.52907 6.35005,0.52907 Z"
fill="#df6277"
id="path6" />
<path
d="M 8.9798,1.7952 6.35,6.35 8.8703,7.8052 11.3911,3.4396 C 11.24558,3.18755 10.131,2.45985 8.9799,1.7953 Z"
fill="#fb9168"
id="path8" />
<path
d="M 11.391,3.4396 6.35,6.35 8.8703,7.8052 11.6092,6.35 c 0,-1.3292 -0.07255,-2.6584 -0.21808,-2.9104 z"
fill="#f3db6c"
id="path10" />
<path
d="m 6.35,6.35 v 2.9104 h 5.041 C 11.53652,9.00835 11.60907,7.6792 11.60908,6.35 Z"
fill="#7ab392"
id="path12" />
<path
d="m 6.35,6.35 v 2.9104 l 2.6298,1.6443 c 1.1511,-0.66459 2.2657,-1.3923 2.4112,-1.6443 z"
fill="#4b7cbc"
id="path14" />
<path
d="M 6.35,6.35 3.8292,7.8052 6.35,12.1708 c 0.29104,0 1.4787,-0.60148 2.6298,-1.2661 z"
fill="#6f488c"
id="path16" />
<path
d="M 3.8292,4.8948 1.3089,9.2604 c 0.29104,0.5041 4.459,2.9104 5.041,2.9104 V 6.35 Z"
fill="#4d3f33"
id="path18" />
<path
d="m 1.309,3.4396 c -0.29104,0.5041 -0.29104,5.3167 0,5.8208 L 6.35,6.35 V 3.4396 Z"
fill="#7a573b"
id="path20" />
<path
d="m 6.35,0.52917 c -0.58208,-2e-8 -4.75,2.4063 -5.041,2.9104 l 5.041,2.9104 z"
fill="#99cd61"
id="path22" />
</g>
<metadata
id="metadata64">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>Prism Launcher Logo</dc:title>
<dc:date>19/10/2022</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Prism Launcher</dc:title>
</cc:Agent>
</dc:creator>
<dc:contributor>
<cc:Agent>
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
</cc:Agent>
</dc:contributor>
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
<dc:publisher>
<cc:Agent>
<dc:title>Prism Launcher</dc:title>
</cc:Agent>
</dc:publisher>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -0,0 +1,72 @@
{
"color-space-for-untagged-svg-colors" : "display-p3",
"fill" : {
"solid" : "extended-gray:1.00000,1.00000"
},
"groups" : [
{
"layers" : [
{
"image-name" : "block.svg",
"name" : "block",
"position" : {
"scale" : 19.28,
"translation-in-points" : [
0,
0
]
}
}
],
"shadow" : {
"kind" : "neutral",
"opacity" : 0.5
},
"translucency" : {
"enabled" : true,
"value" : 0.5
}
},
{
"blur-material-specializations" : [
{
"value" : 0.5
},
{
"appearance" : "dark",
"value" : null
}
],
"layers" : [
{
"blend-mode" : "normal",
"fill" : "automatic",
"glass" : true,
"hidden" : false,
"image-name" : "rainbow.svg",
"name" : "rainbow",
"position" : {
"scale" : 19.28,
"translation-in-points" : [
0,
0
]
}
}
],
"shadow" : {
"kind" : "neutral",
"opacity" : 0.5
},
"translucency" : {
"enabled" : false,
"value" : 0.5
}
}
],
"supported-platforms" : {
"squares" : [
"macOS"
]
}
}

191
tools/ninjatracing.py Normal file
View file

@ -0,0 +1,191 @@
#!/usr/bin/env python3
# Copyright 2018 Nico Weber
# Patched to work with v7 logs
#
# 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.
"""Converts one (or several) .ninja_log files into chrome's about:tracing format.
If clang -ftime-trace .json files are found adjacent to generated files they
are embedded.
Usage:
ninja -C $BUILDDIR
ninjatracing $BUILDDIR/.ninja_log > trace.json
"""
import json
import os
import optparse
import re
import sys
class Target:
"""Represents a single line read for a .ninja_log file. Start and end times
are milliseconds."""
def __init__(self, start, end):
self.start = int(start)
self.end = int(end)
self.targets = []
def read_targets(log, show_all):
"""Reads all targets from .ninja_log file |log_file|, sorted by start
time"""
header = log.readline()
m = re.search(r'^# ninja log v(\d+)\n$', header)
assert m, "unrecognized ninja log version %r" % header
version = int(m.group(1))
assert 5 <= version <= 7, "unsupported ninja log version %d" % version
if version >= 6:
# Skip header line
next(log)
targets = {}
last_end_seen = 0
for line in log:
if line.startswith('#'):
continue
start, end, _, name, cmdhash = line.strip().split('\t') # Ignore restat.
if not show_all and int(end) < last_end_seen:
# An earlier time stamp means that this step is the first in a new
# build, possibly an incremental build. Throw away the previous data
# so that this new build will be displayed independently.
targets = {}
last_end_seen = int(end)
targets.setdefault(cmdhash, Target(start, end)).targets.append(name)
return sorted(targets.values(), key=lambda job: job.end, reverse=True)
class Threads:
"""Tries to reconstruct the parallelism from a .ninja_log"""
def __init__(self):
self.workers = [] # Maps thread id to time that thread is occupied for.
def alloc(self, target):
"""Places target in an available thread, or adds a new thread."""
for worker in range(len(self.workers)):
if self.workers[worker] >= target.end:
self.workers[worker] = target.start
return worker
self.workers.append(target.start)
return len(self.workers) - 1
def read_events(trace, options):
"""Reads all events from time-trace json file |trace|."""
trace_data = json.load(trace)
def include_event(event, options):
"""Only include events if they are complete events, are longer than
granularity, and are not totals."""
return ((event['ph'] == 'X') and
(event['dur'] >= options['granularity']) and
(not event['name'].startswith('Total')))
return [x for x in trace_data['traceEvents'] if include_event(x, options)]
def trace_to_dicts(target, trace, options, pid, tid):
"""Read a file-like object |trace| containing -ftime-trace data and yields
about:tracing dict per eligible event in that log."""
ninja_time = (target.end - target.start) * 1000
for event in read_events(trace, options):
# Check if any event duration is greater than the duration from ninja.
if event['dur'] > ninja_time:
print("Inconsistent timing found (clang time > ninja time). Please"
" ensure that timings are from consistent builds.")
sys.exit(1)
# Set tid and pid from ninja log.
event['pid'] = pid
event['tid'] = tid
# Offset trace time stamp by ninja start time.
event['ts'] += (target.start * 1000)
yield event
def embed_time_trace(ninja_log_dir, target, pid, tid, options):
"""Produce time trace output for the specified ninja target. Expects
time-trace file to be in .json file named based on .o file."""
for t in target.targets:
o_path = os.path.join(ninja_log_dir, t)
json_trace_path = os.path.splitext(o_path)[0] + '.json'
try:
with open(json_trace_path, 'r') as trace:
for time_trace_event in trace_to_dicts(target, trace, options,
pid, tid):
yield time_trace_event
except IOError:
pass
def log_to_dicts(log, pid, options):
"""Reads a file-like object |log| containing a .ninja_log, and yields one
about:tracing dict per command found in the log."""
threads = Threads()
for target in read_targets(log, options['showall']):
tid = threads.alloc(target)
yield {
'name': '%0s' % ', '.join(target.targets), 'cat': 'targets',
'ph': 'X', 'ts': (target.start * 1000),
'dur': ((target.end - target.start) * 1000),
'pid': pid, 'tid': tid, 'args': {},
}
if options.get('embed_time_trace', False):
# Add time-trace information into the ninja trace.
try:
ninja_log_dir = os.path.dirname(log.name)
except AttributeError:
continue
for time_trace in embed_time_trace(ninja_log_dir, target, pid,
tid, options):
yield time_trace
def main(argv):
usage = __doc__
parser = optparse.OptionParser(usage)
parser.add_option('-a', '--showall', action='store_true', dest='showall',
default=False,
help='report on last build step for all outputs. Default '
'is to report just on the last (possibly incremental) '
'build')
parser.add_option('-g', '--granularity', type='int', default=50000,
dest='granularity',
help='minimum length time-trace event to embed in '
'microseconds. Default: %default')
parser.add_option('-e', '--embed-time-trace', action='store_true',
default=False, dest='embed_time_trace',
help='embed clang -ftime-trace json file found adjacent '
'to a target file')
(options, args) = parser.parse_args()
if len(args) == 0:
print('Must specify at least one .ninja_log file')
parser.print_help()
return 1
entries = []
for pid, log_file in enumerate(args):
with open(log_file, 'r') as log:
entries += list(log_to_dicts(log, pid, vars(options)))
json.dump(entries, sys.stdout)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))