Compare commits

...

240 commits

Author SHA1 Message Date
Rachel Powers
d7793caa90 feat: Auto handle Http 429 Too Many Requests with retry
- Must be explicitly enabled for a request
- Uses Retry-After Header if present, falls back to exponential back off
  starting with 10 seconds
- if retry delay is greater than 1 minute or it retries more than 3
  times then fail with a "Rate Limited" reason
- Sets task status to inform user of retry.

Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
(cherry picked from commit e8da9ee4fb)
2026-02-06 18:42:57 +01:00
Octol1ttle
930cf828c4 change(Actions): unhardcode AppImage signing key ID
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 27172dd1bd)
2026-02-04 20:06:28 +01:00
Alexandru Ionut Tripon
301bce3c72
[Backport release-10.x] start core update task only if not running already (#4961) 2026-02-04 11:38:36 +02:00
Trial97
8e6979258a start core update task only if not running already
This line crashes develop builds because the task is running already.

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit bc6a6e959b)
2026-02-04 09:18:36 +00:00
Rachel Powers
6e9e79a435 fix: turn off debug assert in release builds
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
(cherry picked from commit d888ecaf11)
2026-02-04 07:34:10 +01:00
Alexandru Ionut Tripon
6ee608e678
[Backport release-10.x] Use Merge Groups for primary CI (#4951) 2026-02-03 23:40:00 +02:00
Alexandru Ionut Tripon
1ca94829d8
[Backport release-10.x] Fix symlink instance copying on Windows (#4953) 2026-02-03 23:39:25 +02:00
Rachel Powers
757136f5b3 fix: windows filelink results needs to capture by refrence
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
(cherry picked from commit da019c8425)
2026-02-03 03:03:04 +00:00
Seth Flynn
f31ff5742f ci(build+codeql): run for merge groups
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit a770cc1128)
2026-02-02 22:49:31 +00:00
Octol1ttle
cb69f999f7 change(Actions): run tests with more output
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 2338455076)
2026-02-01 09:59:51 +01:00
Octol1ttle
bc092d688f fix(ModrinthCheckUpdate): always increment loader index
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit e7382fd43d)
2026-02-01 07:58:34 +01:00
Alexandru Ionut Tripon
9f103c1f36
[Backport release-10.x] bump qt warning levels (#4932) 2026-02-01 08:20:40 +02:00
Trial97
55bf58c6c2 bump qt warninig levels
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 5232ebe989)
2026-02-01 03:20:46 +00:00
Seth Flynn
27b7a1f9a7
bump to 10.0.5 (#4925) 2026-01-31 22:11:38 -05:00
Trial97
2e0ddc4ad5
bump to 10.0.5
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2026-01-31 23:21:14 +02:00
Alexandru Ionut Tripon
cd8b6f6b67
bump to 10.0.4 (#4919) 2026-01-31 21:05:07 +02:00
Trial97
c8e120be85
bump to 10.0.4
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2026-01-31 21:02:20 +02:00
Alexandru Ionut Tripon
9e0bd4f3b9
[Backport release-10.x] update to qt 6.10.2, build codeql on the qt version we still want to support, sign DLLs on windows (#4917) 2026-01-31 19:37:16 +02:00
DioEgizio
d01db5de7f
chore: trusted signing is now artifact signing
see https://github.com/Azure/artifact-signing-action/issues/107

Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit 8781ac5f64)
2026-01-31 19:34:46 +02:00
DioEgizio
0e5605b4f1
fix: sign DLLs too on windows
should fix issues with Smart App Control

Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit db7685259e)
2026-01-31 19:33:17 +02:00
DioEgizio
8d2327a28a
chore: update to Qt 6.10.2 on windows and linux appimage/portable
also switches codeql to build on Qt 6.4.3, to make sure prism still builds on the oldest version we still wanna support.
for this reason, codeql also now runs tests (to see if they don't fail on 6.4.3). While doing this I also noticed our qt requirement is 6.4, as we use Qt::Literals::StringLiterals

Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit b1857508f7)
2026-01-31 19:33:17 +02:00
Alexandru Ionut Tripon
3d6f15f635
[Backport release-10.x] Mention nightly Flatpak in docs (#4912) 2026-01-31 10:39:26 +02:00
Seth Flynn
a656bcff49 docs(README): mention nightly flatpak
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 81335aa1aa)
2026-01-31 08:39:07 +00:00
Seth Flynn
ef696fdc87 docs(README): remove mentions of community in-development packages
Most of these no longer exist or are prone to breakage

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 82b7c9786c)
2026-01-31 08:39:07 +00:00
Alexandru Ionut Tripon
a16925d75c
[Backport release-10.x] Abort launch when there are libraries missing (#4907) 2026-01-31 10:38:27 +02:00
Octol1ttle
a33b4297ad improve wording
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit ffd1e7bc33)
2026-01-30 17:49:35 +02:00
Octol1ttle
af6e4445ad style: reorder includes
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 193840b237)
2026-01-30 13:17:17 +02:00
Octol1ttle
19797c0f58 feat: abort launch when there are libraries missing
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit d4817a5669)
2026-01-30 13:16:59 +02:00
Alexandru Ionut Tripon
9cee254a88
[Backport release-10.x] remove followSymlinks calls (#4906) 2026-01-30 13:11:15 +02:00
Alexandru Ionut Tripon
9e9a768ba7
[Backport release-10.x] Remove double spaces in logging (#4905) 2026-01-30 13:10:53 +02:00
Trial97
c3a0e26923 remove followSymlinks calls
most probably they were used incorectly anyway

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit e5aa5e298f)
2026-01-30 11:10:38 +00:00
Octol1ttle
11e2c77eff got clang-format'd 💀
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 158a7bbc34)
2026-01-30 11:10:01 +00:00
Octol1ttle
8cfeb30c56 chore: remove double spaces from logs
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 584dc47da5)
2026-01-30 11:10:01 +00:00
Alexandru Ionut Tripon
3e03d47fbf
[Backport release-10.x] add more loaders to override setting (#4904) 2026-01-30 11:56:00 +02:00
Trial97
a942fd9db0 add more loaders to ovveride setting
fixes #4624

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 6a41932e65)
2026-01-30 09:31:48 +00:00
Octol1ttle
7e3d27743b change(MSAStep): log server errors
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 665aa4c546)
2026-01-29 13:52:43 +01:00
Alexandru Ionut Tripon
f5e56ea3e4
[Backport release-10.x] Trim whitespace from path values in settings (#4888) 2026-01-28 08:49:53 +02:00
Seth Flynn
9a74635011 fix(MinecraftSettingsWidget): trim whitespace from path values
Should help in mitigating common mistakes in copy/pasting

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit cf63bccfd7)
2026-01-28 06:49:05 +00:00
Alexandru Ionut Tripon
7b214d0048
[Backport release-10.x] fix: remove flatpak submodule (#4880) 2026-01-27 18:32:44 +02:00
Octol1ttle
9c7f692c09 fix: remove flatpak submodule
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 5de150b8dd)
2026-01-27 16:32:26 +00:00
Alexandru Ionut Tripon
eb8743e75b
[Backport release-10.x] Remove in-tree flatpak (#4876) 2026-01-27 14:05:16 +02:00
Seth Flynn
62d57878b8 build(flatpak): remove
See https://github.com/PrismLauncher/flatpak

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 3833b1e292)
2026-01-27 12:04:56 +00:00
Alexandru Ionut Tripon
2550753e1f
[Backport release-10.x] PackProfile: don't reset dirty if component list saving failed (#4875) 2026-01-27 13:59:03 +02:00
Octol1ttle
070c800ce9 fix(PackProfile): don't reset dirty if component list saving failed
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit c6072ff434)
2026-01-27 11:58:21 +00:00
Alexandru Ionut Tripon
f56aab1c58
[Backport release-10.x] properly redraw viewport when scroll happens (#4874) 2026-01-27 13:56:32 +02:00
Trial97
20f8f7ec0e properly redraw viewport when scroll happens
fixes #1504

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 7baaa05683)
2026-01-27 11:56:17 +00:00
Alexandru Ionut Tripon
ca4a36531c
[Backport release-10.x] MinecraftSettingsWidget: Swap window width/height spinboxes (#4859) 2026-01-25 10:30:07 +02:00
Alexandru Ionut Tripon
f85271e5ef
[Backport release-10.x] MSAStep: Tighten isSchemeHandlerRegistered check (#4858) 2026-01-25 10:29:57 +02:00
Octol1ttle
884e3e49a6 "British roots ending with '-our' usually have '-or' in American English" thanks clion
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit dd220e61be)
2026-01-25 08:28:36 +00:00
Octol1ttle
1b9d0b95a8 fix(MinecraftSettingsWidget): swap width and height spinboxes to be in their expected positions
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit b4f892cf42)
2026-01-25 08:28:36 +00:00
Octol1ttle
f7539c5869 fix(MSAStep): tighten isSchemeHandlerRegistered check
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 87b3a2ef99)
2026-01-25 08:28:24 +00:00
Alexandru Ionut Tripon
43eeb14a7f
[Backport release-10.x] ModrinthCheckUpdate: Don't send a request that is doomed to fail (#4844) 2026-01-22 22:45:36 +02:00
Alexandru Ionut Tripon
3a3bbb66a6
[Backport release-10.x] Add asserts to invalid Task states (#4843) 2026-01-22 22:45:23 +02:00
Octol1ttle
168816d796
ModrinthCheckUpdate: don't send a request that is doomed to fail
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 29afecdbde)
2026-01-22 22:43:07 +02:00
Octol1ttle
60a1a3ce88 rename Assert.h because it causes conflicts???
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit fa040fc959)
2026-01-22 20:41:17 +00:00
Octol1ttle
03de3bbe91 fix(ResourceFolderModel): don't read state from off-thread task
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 6cb07e203b)
2026-01-22 20:41:17 +00:00
Octol1ttle
d531abb684 code review
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 1cd78bf94a)
2026-01-22 20:41:17 +00:00
Octol1ttle
20cf5d3a65 Introduce macro to assert and return the assertion condition
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 490df18fd5)
2026-01-22 20:41:17 +00:00
Octol1ttle
f686d9b598 Add asserts to invalid Task states
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 9ac0314d7a)
2026-01-22 20:41:17 +00:00
Alexandru Ionut Tripon
3e567fef3b
[Backport release-10.x] Add missing returns after task signal activation (#4842) 2026-01-22 22:41:00 +02:00
Octol1ttle
1ff7d25352 fix: add missing returns after emitSucceeded
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit e27246c3f9)
2026-01-22 20:40:11 +00:00
Octol1ttle
eacebcc153 fix: add missing returns after emitFailed/Aborted
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 507de0fcbd)
2026-01-22 20:40:11 +00:00
Alexandru Ionut Tripon
e7864d6e2b
[Backport release-10.x] initilize world size (#4834) 2026-01-21 12:47:38 +02:00
Trial97
fb8193041a initilize world size
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 09f548f688)
2026-01-21 10:45:16 +00:00
Alexandru Ionut Tripon
fa773da508
[Backport release-10.x] Reformat with editorconfig, add clang-format target (#4814) 2026-01-19 09:07:42 +02:00
Seth Flynn
da4bf6e8c2
style: re-format tree with editorconfig
```
find $PWD \
  -type f \
  ! -path '*/.git/*' ! -path '*/flatpak/shared-modules/*' \
  ! -path '*/libraries/*' ! -path '*/testdata/*' ! -name '*.patch' \
  ! -name '*.svg' ! -name '*.scd' ! -path '*/program_info/LICENSE' \
  ! -path '*/COPYING.md' ! -path '*/cmake/*' ! -name '.gitmodules' \
  -exec eclint -fix {} \;
```

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit fd91f87c21)
2026-01-18 23:01:42 +02:00
Seth Flynn
3b23752f73
build: add clang-format target
Through the power of CMake itself, we can format our files

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 686ad72e03)
2026-01-18 22:59:44 +02:00
DioEgizio
c806f61a02 hack: resolve dependencies for imageformats on mingw
Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit a98ec194be)
2026-01-18 21:56:41 +01:00
Tayou
24fab616a4
[Backport release-10.x] fix: improve we couldn't launch after 3 tries message (#4812) 2026-01-18 20:26:29 +01:00
DioEgizio
1aff2e64d5 fix: improve we couldn't launch after 3 tries message
Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit d5da7c9bde)
2026-01-18 19:25:46 +00:00
Alexandru Ionut Tripon
b27a559010
[Backport release-10.x] Take $GRADLE_USER_HOME into account when looking for JDKs (#4808) 2026-01-18 01:18:14 +02:00
SandaruKasa
40796e9f41 Take $GRADLE_USER_HOME into account when looking for JDKs
Signed-off-by: SandaruKasa <sandarukasa@ya.ru>
(cherry picked from commit 267e8f1d33)
2026-01-17 23:17:56 +00:00
Alexandru Ionut Tripon
6296ec7fce
[Backport release-10.x] fix ftb import for old packs (#4798) 2026-01-17 17:05:02 +02:00
Trial97
d1605ec02a fix ftb import for old packs
fixes #4786

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 3496b7b3a0)
2026-01-17 15:04:08 +00:00
Alexandru Ionut Tripon
6bd74bedac
[Backport release-10.x] Fix instance and settings window minimum size (#4774) 2026-01-14 23:25:05 +02:00
TheKodeToad
3006e68849 Fix new instance window being too large
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit d283726494)
2026-01-14 21:24:42 +00:00
TheKodeToad
84a2d7d10a Fix settings window being too large
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 40c6f79b5f)
2026-01-14 21:24:42 +00:00
Alexandru Ionut Tripon
96269d408c
[Backport release-10.x] Allow opting out of pre-compiled headers (#4773) 2026-01-14 23:22:00 +02:00
TheKodeToad
22c6e1a331 Fix JavaWizardWidget compilation without PCH
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 90e3c59bd8)
2026-01-14 21:21:36 +00:00
TheKodeToad
e2842bb5cd Allow toggling pre-compiled headers
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 3a16cf3081)
2026-01-14 21:21:36 +00:00
Alexandru Ionut Tripon
5983919c2f
[Backport release-10.x] Fix for Prism failing to recognize some shader pack ZIP archives added manually by the user (#4766) 2026-01-14 13:52:40 +02:00
Alexandru Ionut Tripon
70f274ca9b
[Backport release-10.x] IconList: Make sure we always set new path (#4765) 2026-01-14 13:52:30 +02:00
Alexandru Ionut Tripon
3bf9b881fa
[Backport release-10.x] chore: make appimage use portals for file picking (#4764) 2026-01-14 13:52:21 +02:00
Alexandru Ionut Tripon
326f818d0a
[Backport release-10.x] accept libarchive warning result (#4763) 2026-01-14 13:52:13 +02:00
SwitchAxe
aadd88cbd8 Fixed formatting
Signed-off-by: SwitchAxe <sofiacerasuoli@gmail.com>
(cherry picked from commit 809e766aec)
2026-01-14 11:51:57 +00:00
SwitchAxe
0b7646740b Improved the check for the assets dir
Signed-off-by: SwitchAxe <sofiacerasuoli@gmail.com>
(cherry picked from commit 7f0f90fcce)
2026-01-14 11:51:57 +00:00
SwitchAxe
19ead6adbd Made the loop more efficient
Signed-off-by: SwitchAxe <sofiacerasuoli@gmail.com>
(cherry picked from commit 8aba994312)
2026-01-14 11:51:57 +00:00
Sofia
fb77027d84 Update launcher/minecraft/mod/tasks/LocalShaderPackParseTask.cpp
Co-authored-by: Octol1ttle <l1ttleofficial@outlook.com>
Signed-off-by: Sofia <75943257+SwitchAxe@users.noreply.github.com>
(cherry picked from commit 6321db5942)
2026-01-14 11:51:57 +00:00
SwitchAxe
2692cbcdc8 Fixed Indentation
Signed-off-by: SwitchAxe <sofiacerasuoli@gmail.com>
(cherry picked from commit 6ac9de7a11)
2026-01-14 11:51:57 +00:00
SwitchAxe
17941872f8 Added support for shader packs with a top-level parent directory
Signed-off-by: SwitchAxe <sofiacerasuoli@gmail.com>
(cherry picked from commit 9a93696915)
2026-01-14 11:51:57 +00:00
Octol1ttle
6e82d0a4eb fix(IconList): make sure we always set new path
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 6cbedbe184)
2026-01-14 11:50:57 +00:00
DioEgizio
c9098f7556 chore: make appimage use portals for file picking
much better than Qt's ugly default one

Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit 1b15643fcd)
2026-01-14 11:50:39 +00:00
Trial97
0d21749568 accept libarchive warning result
this is the intended behavior to treat warnings as ok, because
teoretically the file was extracted, even if the time of the file can't
be set

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit a6d6ff9926)
2026-01-14 11:50:12 +00:00
Alexandru Ionut Tripon
fe3c3e7449
[Backport release-10.x] Free up disk space on Flatpak builds so they don't crash (#4757) 2026-01-13 08:40:13 +02:00
Octol1ttle
c0fae914d8 hack: try to free up disk space on Flatpak builds so they don't crash
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit eafce5d5f6)
2026-01-13 06:15:08 +00:00
Alexandru Ionut Tripon
df9bed30af
[Backport release-10.x] auth: improve status messages (#4754) 2026-01-12 20:20:49 +02:00
Octol1ttle
1d1dd6262f auth: improve status messages
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 6cddc28e9b)
2026-01-12 18:20:05 +00:00
Alexandru Ionut Tripon
aabd0a6d53
[Backport release-10.x] Update copyright year (#4743) 2026-01-12 00:23:39 +02:00
Alexandru Ionut Tripon
6486c3d1a8
[Backport release-10.x] Set current auth step description in correct method (#4742) 2026-01-12 00:23:28 +02:00
Alexandru Ionut Tripon
8936103378
[Backport release-10.x] Fix auto-join getting stuck disabled (#4741) 2026-01-12 00:23:05 +02:00
Trial97
a3b9281c09 Update copyright year
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 508a66aedd)
2026-01-11 22:22:25 +00:00
Octol1ttle
c55f4b373e fix(AuthFlow): set current step description in correct method
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit f0813b578e)
2026-01-11 22:22:13 +00:00
TheKodeToad
5ed7a5b56f Fix auto-join getting stuck disabled
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit e856f60804)
2026-01-11 22:22:02 +00:00
Alexandru Ionut Tripon
702d4eb75c
[Backport release-10.x] Network: log server response on error (#4732) 2026-01-11 15:20:24 +02:00
Alexandru Ionut Tripon
7cc148ed60
[Backport release-10.x] Fix environment variables always being discarded and custom commands always being overriden (#4731) 2026-01-11 15:20:16 +02:00
Alexandru Ionut Tripon
4b91ae019e
[Backport release-10.x] Fix shaderpacks folder being hidden (#4730) 2026-01-11 15:19:57 +02:00
Octol1ttle
323a50efa2 change(NetRequest): log server response on error
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 7f3790bf73)
2026-01-11 13:19:37 +00:00
TheKodeToad
5923504aff Fix broken EnvironmentVariables and CustomCommands when tab is inactive
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 01228cc0b7)
2026-01-11 13:19:27 +00:00
TheKodeToad
60f598a0e7 Fix shaderpacks folder being hidden on Windows
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 3e7ef5ee17)
2026-01-11 13:19:10 +00:00
Alexandru Ionut Tripon
810558a6f4
[Backport release-10.x] fix modrinth environments filter (#4725) 2026-01-10 18:37:31 +02:00
Trial97
6d60fc1cbe fix modrinth environments filter
fixes #4630
reversed the conditions to check for side because somewhere the mod side
is no initilized(easier to check one line than search where it is not
initialized)

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit a97c15d0fb)
2026-01-10 16:36:50 +00:00
github-actions[bot]
a80c215113
[Backport release-10.x] Bump to 10.0.2 (#4720)
Bump to 10.0.2 (#4713)


(cherry picked from commit cbe77872fa)

Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
Co-authored-by: TheKodeToad <TheKodeToad@proton.me>
2026-01-09 16:03:57 -07:00
Alexandru Ionut Tripon
1109ddce2a
[Backport release-10.x] format the code (#4719) 2026-01-09 23:38:05 +02:00
Trial97
b2e195a651 format the code
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 53acc60983)
2026-01-09 21:36:46 +00:00
Alexandru Ionut Tripon
0008542228
[Backport release-10.x] Disable tooltips if using gamescope / Steam Deck. (#4716) 2026-01-09 22:15:09 +02:00
Alexandru Ionut Tripon
e6bfaf4717
[Backport release-10.x] remove curseforge api key validation (#4715) 2026-01-09 22:14:45 +02:00
github-actions[bot]
185d5cb4bb
[Backport release-10.x] fix jpg icons (#4714)
fix jpg icons

fixes #4686 and fixes #4666
Forces jpg and jpeg to go through QPixmap first then to Icon.
The original behaivior used the QIcon internal engine to build the
QPixmap causing some inconsitencies.


(cherry picked from commit 3f53670cc2)

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
Co-authored-by: Trial97 <alexandru.tripon97@gmail.com>
2026-01-09 22:14:27 +02:00
Mark Deneen
bbc94272be Disable tooltips if using gamescope / Steam Deck. (#4096)
* Disable tooltips if using gamescope / Steam Deck.

On a Steam Deck, Prism Launcher's window is scaled to fit the screen.
Whenever a tool tip is shown, it is often display outside of the window,
causing the compositor to scale the view to fit the new bounding box.

This effect is quite jarring, and I don't like it.  This patch adds a
small global event filter which swallows up the tool tip events.  It is
currently not configurable, although I suppose that could be an option.

Signed-off-by: Mark Deneen <mdeneen@gmail.com>

* Move tooltip filter addition from the Main Window to the Application.

Signed-off-by: Mark Deneen <mdeneen@gmail.com>

* Add license information to new files

Signed-off-by: Mark Deneen <mdeneen@gmail.com>

* Remove other authors, they should not have been added in the first place

Signed-off-by: Mark Deneen <mdeneen@gmail.com>

* Correct the years as well

Signed-off-by: Mark Deneen <mdeneen@gmail.com>

* Update launcher/ui/ToolTipFilter.cpp

Co-authored-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
Signed-off-by: Mark Deneen <mdeneen@gmail.com>

* Update launcher/ui/ToolTipFilter.h

Co-authored-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
Signed-off-by: Mark Deneen <mdeneen@gmail.com>

---------

Signed-off-by: Mark Deneen <mdeneen@gmail.com>
Co-authored-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
(cherry picked from commit 133842d6a8)
2026-01-09 20:14:02 +00:00
Trial97
05f3280405 remove curseforge api key validation
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 7f952d9fdf)
2026-01-09 20:13:20 +00:00
Alexandru Ionut Tripon
0d32167303
[Backport release-10.x] fix curseforge import (#4712) 2026-01-09 21:26:02 +02:00
Trial97
295ed87c1d fix curseforge import
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 398689637d)
2026-01-09 19:24:31 +00:00
Alexandru Ionut Tripon
6aad22b75a
[Backport release-10.x] Remove prismlauncher-qt5-git badge from README (#4697) 2026-01-08 18:42:34 +02:00
Elliott Tallis
9652a43fe2 Remove prismlauncher-qt5-git badge from README
AUR has been deleted since Qt 5 builds are no longer possible

Signed-off-by: Elliott Tallis <tallis.elliott@gmail.com>
(cherry picked from commit ada0401137)
2026-01-08 16:24:27 +00:00
Alexandru Ionut Tripon
f2f9cfb103
[Backport release-10.x] Update build instructions links in README (#4689) 2026-01-07 21:42:30 +02:00
Dylan Renwick
96634879bb Update build instructions links in README
Signed-off-by: Dylan Renwick <skidsirongenesisdev@gmail.com>
(cherry picked from commit e91d05922a)
2026-01-07 19:34:39 +00:00
Alexandru Ionut Tripon
8a432e9e5e
[Backport release-10.x] Check that mouse press is inside view rectangle of CheckComboBox (#4683) 2026-01-07 12:27:24 +02:00
Alexandru Ionut Tripon
2c00eb53a5
[Backport release-10.x] Skip asking for offline name when using offline account (#4682) 2026-01-07 12:27:05 +02:00
Octol1ttle
fd1d7f3c28 fix(CheckComboBox): check that mouse press is inside view rectangle
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 39e283169e)
2026-01-07 10:26:32 +00:00
Octol1ttle
2c01416040 fix: skip asking for offline name when using offline account
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 2e6efab913)
2026-01-07 10:26:04 +00:00
Seth Flynn
96ad89845c
[Backport release-10.x] Fix AppImage zsync information (#4674) 2026-01-06 18:53:03 -05:00
Seth Flynn
a9c065174c build(appimage): use tag for version when available
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 4037446051)
2026-01-06 23:52:42 +00:00
Seth Flynn
4dec044883
[Backport release-10.x] Automatically use vcpkg to build on macOS and Windows (#4672) 2026-01-06 17:40:00 -05:00
Seth Flynn
c3a1fd7e49 build(macos/windows): automatically use vcpkg
Previously `CMAKE_TOOLCHAIN_FILE` needed to be set manually, which was
kinda pointless when vcpkg is already installed and meant to be used

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 44bf3aad29)
2026-01-06 22:38:18 +00:00
Alexandru Ionut Tripon
03d6818570
[Backport release-10.x] Don't use .index for shaderpacks (#4669) 2026-01-06 23:53:59 +02:00
TheKodeToad
f1a30bf251 Fix oversights and make requested changes
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 3a7366a998)
2026-01-06 21:53:30 +00:00
TheKodeToad
7e4ea5a47e Avoid some errors (less noisy log)
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit e4991d81d7)
2026-01-06 21:53:30 +00:00
TheKodeToad
e3b3ef65ee Less destructive delete
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 398305eb66)
2026-01-06 21:53:30 +00:00
TheKodeToad
6ef89db08e Properly show shaderpacks in export
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit d625a28112)
2026-01-06 21:53:30 +00:00
TheKodeToad
993243b6a3 Don't use .index for shaderpacks
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 1cf48dfd85)
2026-01-06 21:53:30 +00:00
Alexandru Ionut Tripon
1f22a88935
[Backport release-10.x] Bump to 10.0.1 (#4668) 2026-01-06 23:38:19 +02:00
TheKodeToad
29a4ae516a Bump to 10.0.1
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit ea7122b641)
2026-01-06 21:37:09 +00:00
Alexandru Ionut Tripon
8315113f26
[Backport release-10.x] fix manifest generation for linux (#4657) 2026-01-06 17:02:14 +02:00
Trial97
5e4163d91c fix manifest generation for linux
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 681c635f0e)
2026-01-06 15:01:56 +00:00
Alexandru Ionut Tripon
406b56060c
[Backport release-10.x] Modified the plist to categorize it as a game [Mac] (#4654) 2026-01-06 14:22:02 +02:00
David Kaluta
33b515d0ee Modified the plist to categorize it as a game
Signed-off-by: David Kaluta <mail@dkaluta.com>
(cherry picked from commit 094425552b)
2026-01-06 12:21:27 +00:00
Alexandru Ionut Tripon
3a836e8f6d
[Backport release-10.x] change(program_info): more brand unhardcoding (#4653) 2026-01-06 14:16:56 +02:00
Octol1ttle
e2d633e2c4 change(program_info): unhardcode brand from macOS bundle GUI identifier
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit abd482db4a)
2026-01-06 12:16:34 +00:00
Octol1ttle
e40dd81c38 change(program_info): unhardcode brand from Windows files
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit c46eaec046)
2026-01-06 12:16:34 +00:00
Alexandru Ionut Tripon
7981c6c64e
[Backport release-10.x] use libarchive to extract release files (#4652) 2026-01-06 14:16:26 +02:00
Trial97
d32568fd5e use libarchive to extract release files
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 7b0896a0b9)
2026-01-06 12:15:48 +00:00
Alexandru Ionut Tripon
5368452c9f
[Backport release-10.x] Change enum style guideline to PascalCase (#4646) 2026-01-06 13:28:30 +02:00
Alexandru Ionut Tripon
52ddb88d34
[Backport release-10.x] refactor: remove broken Qt includes (#4645) 2026-01-06 13:28:09 +02:00
TheKodeToad
0bd8f05f82 Change enum style guideline to PascalCase
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 55102d4113)
2026-01-06 11:06:27 +00:00
Octol1ttle
6ee81b527d refactor: remove broken Qt includes
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 56392a20ed)
2026-01-06 11:06:15 +00:00
Alexandru Ionut Tripon
f0725e9edf
[Backport release-10.x] ci: actually sign windows builds in Release env (#4633) 2026-01-05 20:35:12 +02:00
Seth Flynn
dcb65e8a64 ci: actually sign windows builds in Release env
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 939093d648)
2026-01-05 18:34:58 +00:00
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
253 changed files with 2488 additions and 1725 deletions

View file

@ -7,17 +7,17 @@ Checks:
# ^ Without unused-parameters the readability-identifier-naming check doesn't cause any warnings. # ^ Without unused-parameters the readability-identifier-naming check doesn't cause any warnings.
CheckOptions: CheckOptions:
- { key: readability-identifier-naming.ClassCase, value: PascalCase } - { key: readability-identifier-naming.ClassCase, value: PascalCase }
- { key: readability-identifier-naming.EnumCase, value: PascalCase } - { key: readability-identifier-naming.EnumCase, value: PascalCase }
- { key: readability-identifier-naming.FunctionCase, value: camelCase } - { key: readability-identifier-naming.FunctionCase, value: camelCase }
- { key: readability-identifier-naming.GlobalVariableCase, value: camelCase } - { key: readability-identifier-naming.GlobalVariableCase, value: camelCase }
- { key: readability-identifier-naming.GlobalFunctionCase, value: camelCase } - { key: readability-identifier-naming.GlobalFunctionCase, value: camelCase }
- { key: readability-identifier-naming.GlobalConstantCase, value: SCREAMING_SNAKE_CASE } - { key: readability-identifier-naming.GlobalConstantCase, value: SCREAMING_SNAKE_CASE }
- { key: readability-identifier-naming.MacroDefinitionCase, value: SCREAMING_SNAKE_CASE } - { key: readability-identifier-naming.MacroDefinitionCase, value: SCREAMING_SNAKE_CASE }
- { key: readability-identifier-naming.ClassMemberCase, value: camelCase } - { key: readability-identifier-naming.ClassMemberCase, value: camelCase }
- { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ } - { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ }
- { key: readability-identifier-naming.ProtectedMemberPrefix, value: m_ } - { key: readability-identifier-naming.ProtectedMemberPrefix, value: m_ }
- { key: readability-identifier-naming.PrivateStaticMemberPrefix, value: s_ } - { key: readability-identifier-naming.PrivateStaticMemberPrefix, value: s_ }
- { key: readability-identifier-naming.ProtectedStaticMemberPrefix, value: s_ } - { key: readability-identifier-naming.ProtectedStaticMemberPrefix, value: s_ }
- { key: readability-identifier-naming.PublicStaticConstantCase, value: SCREAMING_SNAKE_CASE } - { key: readability-identifier-naming.PublicStaticConstantCase, value: SCREAMING_SNAKE_CASE }
- { key: readability-identifier-naming.EnumConstantCase, value: SCREAMING_SNAKE_CASE } - { key: readability-identifier-naming.EnumConstantCase, value: PascalCase }

View file

@ -52,32 +52,50 @@ runs:
- name: Package AppImage - name: Package AppImage
shell: bash shell: bash
env: env:
VERSION: ${{ inputs.version }} VERSION: ${{ github.ref_type == 'tag' && github.ref_name || inputs.version }}
BUILD_DIR: build BUILD_DIR: build
INSTALL_APPIMAGE_DIR: install-appdir INSTALL_APPIMAGE_DIR: install-appdir
GPG_PRIVATE_KEY: ${{ inputs.gpg-private-key }} GPG_PRIVATE_KEY: ${{ inputs.gpg-private-key }}
run: | run: |
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}
cp ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.{metainfo,appdata}.xml
if [ '${{ inputs.gpg-private-key-id }}' != '' ]; then if [ '${{ inputs.gpg-private-key-id }}' != '' ]; then
echo "$GPG_PRIVATE_KEY" > privkey.asc echo "$GPG_PRIVATE_KEY" > privkey.asc
gpg --import privkey.asc gpg --import privkey.asc
gpg --export --armor 9C7A2C9B62603299 > pubkey.asc gpg --export --armor ${{ inputs.gpg-private-key-id }} > pubkey.asc
else else
echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY
fi fi
appimagetool -s deploy "$INSTALL_APPIMAGE_DIR"/usr/share/applications/*.desktop sharun lib4bin \
cp ~/bin/AppImageUpdate.AppImage "$INSTALL_APPIMAGE_DIR"/usr/bin/ --hard-links \
# FIXME(@getchoo): Validate AppStream information when https://github.com/probonopd/go-appimage/pull/379 is merged --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 {} +
#makes the launcher use portals for file picking
echo "QT_QPA_PLATFORMTHEME=xdgdesktopportal" > "$INSTALL_APPIMAGE_DIR"/.env
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"
if [[ "${{ github.ref_type }}" == "tag" ]]; then
APPIMAGE_DEST="PrismLauncher-Linux-$APPIMAGE_ARCH.AppImage"
else
APPIMAGE_DEST="PrismLauncher-Linux-$VERSION-${{ inputs.build-type }}-$APPIMAGE_ARCH.AppImage"
fi
mkappimage \ mkappimage \
--no-appstream \
--updateinformation "gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-$APPIMAGE_ARCH.AppImage.zsync" \ --updateinformation "gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-$APPIMAGE_ARCH.AppImage.zsync" \
"$INSTALL_APPIMAGE_DIR" \ "$INSTALL_APPIMAGE_DIR" \
"PrismLauncher-Linux-$VERSION-${{ inputs.build-type }}-$APPIMAGE_ARCH.AppImage" "$APPIMAGE_DEST"
- name: Package portable tarball - name: Package portable tarball
shell: bash shell: bash
@ -89,11 +107,16 @@ 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 }}
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable 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 sharun lib4bin \
mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib --with-hooks \
cp /lib/$APPIMAGE_ARCH-linux-gnu/libcmark.so.0.* ${{ env.INSTALL_PORTABLE_DIR }}/lib --hard-links \
--dst-dir "$INSTALL_PORTABLE_DIR" \
"$INSTALL_PORTABLE_DIR"/bin/* "$QT_PLUGIN_PATH"/*/*.so
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 # 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 -o -type l); 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 }} cd ${{ env.INSTALL_PORTABLE_DIR }}
tar -czf ../PrismLauncher-portable.tar.gz * tar -czf ../PrismLauncher-portable.tar.gz *
@ -107,10 +130,10 @@ runs:
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v6
with: with:
name: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage name: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage
path: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage path: PrismLauncher-${{ runner.os }}-*${{ env.APPIMAGE_ARCH }}.AppImage
- name: Upload AppImage Zsync - name: Upload AppImage Zsync
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v6
with: with:
name: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage.zsync name: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage.zsync
path: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage.zsync path: PrismLauncher-${{ runner.os }}-*${{ env.APPIMAGE_ARCH }}.AppImage.zsync

View file

@ -54,13 +54,13 @@ runs:
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
- name: Emit warning for unsigned builds - name: Emit warning for unsigned builds
if: ${{ github.ref_name != 'develop' || inputs.azure-client-id == '' }} if: ${{ env.CI_HAS_ACCESS_TO_AZURE == '' || inputs.azure-client-id == '' }}
shell: pwsh shell: pwsh
run: | run: |
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY ":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
- name: Login to Azure - name: Login to Azure
if: ${{ github.ref_name == 'develop' && inputs.azure-client-id != '' }} if: ${{ env.CI_HAS_ACCESS_TO_AZURE != '' && inputs.azure-client-id != '' }}
uses: azure/login@v2 uses: azure/login@v2
with: with:
client-id: ${{ inputs.azure-client-id }} client-id: ${{ inputs.azure-client-id }}
@ -68,18 +68,19 @@ runs:
subscription-id: ${{ inputs.azure-subscription-id }} subscription-id: ${{ inputs.azure-subscription-id }}
- name: Sign executables - name: Sign executables
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 uses: azure/artifact-signing-action@v1
with: with:
endpoint: https://eus.codesigning.azure.net/ endpoint: https://eus.codesigning.azure.net/
trusted-signing-account-name: PrismLauncher trusted-signing-account-name: PrismLauncher
certificate-profile-name: PrismLauncher certificate-profile-name: PrismLauncher
files-folder: ${{ github.workspace }}\install\
files: | files-folder-filter: dll,exe
${{ github.workspace }}\install\prismlauncher.exe files-folder-recurse: true
${{ github.workspace }}\install\prismlauncher_filelink.exe files-folder-depth: 2
${{ github.workspace }}\install\prismlauncher_updater.exe # recommended in https://github.com/Azure/artifact-signing-action#timestamping-1
timestamp-rfc3161: "http://timestamp.acs.microsoft.com"
timestamp-digest: "SHA256"
# TODO(@getchoo): Is this all really needed??? # TODO(@getchoo): Is this all really needed???
# https://github.com/Azure/trusted-signing-action/blob/fc390cf8ed0f14e248a542af1d838388a47c7a7c/docs/OIDC.md # https://github.com/Azure/trusted-signing-action/blob/fc390cf8ed0f14e248a542af1d838388a47c7a7c/docs/OIDC.md
exclude-environment-credential: true exclude-environment-credential: true
@ -140,8 +141,8 @@ runs:
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi" makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
- name: Sign installer - 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 uses: azure/artifact-signing-action@v1
with: with:
endpoint: https://eus.codesigning.azure.net/ endpoint: https://eus.codesigning.azure.net/
trusted-signing-account-name: PrismLauncher trusted-signing-account-name: PrismLauncher
@ -150,6 +151,9 @@ runs:
files: | files: |
${{ github.workspace }}\PrismLauncher-Setup.exe ${{ github.workspace }}\PrismLauncher-Setup.exe
# recommended in https://github.com/Azure/artifact-signing-action#timestamping-1
timestamp-rfc3161: "http://timestamp.acs.microsoft.com"
timestamp-digest: "SHA256"
# TODO(@getchoo): Is this all really needed??? # TODO(@getchoo): Is this all really needed???
# https://github.com/Azure/trusted-signing-action/blob/fc390cf8ed0f14e248a542af1d838388a47c7a7c/docs/OIDC.md # https://github.com/Azure/trusted-signing-action/blob/fc390cf8ed0f14e248a542af1d838388a47c7a7c/docs/OIDC.md
exclude-environment-credential: true exclude-environment-credential: true

View file

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

View file

@ -12,29 +12,7 @@ runs:
dpkg-dev \ dpkg-dev \
ninja-build extra-cmake-modules pkg-config scdoc \ ninja-build extra-cmake-modules pkg-config scdoc \
cmark gamemode-dev libarchive-dev libcmark-dev libqrencode-dev zlib1g-dev \ cmark gamemode-dev libarchive-dev libcmark-dev libqrencode-dev zlib1g-dev \
libxcb-cursor-dev libxcb-cursor-dev libtomlplusplus-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
- name: Setup AppImage tooling - name: Setup AppImage tooling
shell: bash shell: bash
@ -56,19 +34,20 @@ runs:
;; ;;
esac 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 \ gh release download continuous \
--repo probonopd/go-appimage \ --repo DioEgizio/go-appimage \
--pattern "appimagetool-*-$APPIMAGE_ARCH.AppImage" \
--output ~/bin/appimagetool
gh release download continuous \
--repo probonopd/go-appimage \
--pattern "mkappimage-*-$APPIMAGE_ARCH.AppImage" \ --pattern "mkappimage-*-$APPIMAGE_ARCH.AppImage" \
--output ~/bin/mkappimage --output ~/bin/mkappimage
chmod +x ~/bin/appimagetool ~/bin/mkappimage
echo "$HOME/bin" >> "$GITHUB_PATH"
gh release download \ gh release download \
--repo AppImageCommunity/AppImageUpdate \ --repo AppImageCommunity/AppImageUpdate \
--pattern "AppImageUpdate-$APPIMAGE_ARCH.AppImage" \ --pattern "AppImageUpdate-$APPIMAGE_ARCH.AppImage" \
--output ~/bin/AppImageUpdate.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 shell: bash
run: | run: |
brew update brew update
brew install ninja extra-cmake-modules temurin@17 brew install ninja extra-cmake-modules temurin@17 mono
- name: Set JAVA_HOME - name: Set JAVA_HOME
shell: bash shell: bash
@ -44,4 +44,4 @@ runs:
- name: Setup vcpkg environment - name: Setup vcpkg environment
shell: bash shell: bash
run: | run: |
echo "CMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" >> "$GITHUB_ENV" echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> "$GITHUB_ENV"

View file

@ -57,7 +57,7 @@ runs:
if: ${{ inputs.msystem == '' }} if: ${{ inputs.msystem == '' }}
shell: bash shell: bash
run: | run: |
echo "CMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" >> "$GITHUB_ENV" echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> "$GITHUB_ENV"
- name: Setup MSYS2 (MinGW) - name: Setup MSYS2 (MinGW)
if: ${{ inputs.msystem != '' }} if: ${{ inputs.msystem != '' }}

View file

@ -70,7 +70,6 @@ jobs:
' <<< "$PR_JSON")" ' <<< "$PR_JSON")"
} >> "$GITHUB_ENV" } >> "$GITHUB_ENV"
- name: Find Blocked/Stacked PRs in body - name: Find Blocked/Stacked PRs in body
id: pr_ids id: pr_ids
run: | run: |
@ -80,7 +79,7 @@ jobs:
| ( | (
$body | $body |
reduce ( reduce (
. | scan("blocked (?:by|on):? #([0-9]+)") . | scan("[Bb]locked (?:[Bb]y|[Oo]n):? #([0-9]+)")
| map({ | map({
"type": "Blocked on", "type": "Blocked on",
"number": ( . | tonumber ) "number": ( . | tonumber )
@ -90,7 +89,7 @@ jobs:
| ( | (
$body | $body |
reduce ( reduce (
. | scan("stacked on:? #([0-9]+)") . | scan("[Ss]tacked [Oo]n:? #([0-9]+)")
| map({ | map({
"type": "Stacked on", "type": "Stacked on",
"number": ( . | tonumber ) "number": ( . | tonumber )
@ -252,4 +251,3 @@ jobs:
--body "$COMMENT_BODY" \ --body "$COMMENT_BODY" \
--create-if-none \ --create-if-none \
--edit-last --edit-last

View file

@ -5,33 +5,8 @@ concurrency:
cancel-in-progress: true cancel-in-progress: true
on: on:
push: merge_group:
branches: types: [checks_requested]
- "develop"
- "release-*"
paths:
# File types
- "**.cpp"
- "**.h"
- "**.java"
- "**.ui"
# Directories
- "buildconfig/**"
- "cmake/**"
- "launcher/**"
- "libraries/**"
- "program_info/**"
- "tests/**"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/build.yml"
- ".github/actions/package/**"
- ".github/actions/setup-dependencies/**"
pull_request: pull_request:
paths: paths:
# File types # File types
@ -62,6 +37,9 @@ on:
description: Type of build (Debug or Release) description: Type of build (Debug or Release)
type: string type: string
default: Debug default: Debug
environment:
description: Deployment environment to run under
type: string
workflow_dispatch: workflow_dispatch:
inputs: inputs:
build-type: build-type:
@ -73,6 +51,8 @@ jobs:
build: build:
name: Build (${{ matrix.artifact-name }}) name: Build (${{ matrix.artifact-name }})
environment: ${{ inputs.environment || '' }}
permissions: permissions:
# Required for Azure Trusted Signing # Required for Azure Trusted Signing
id-token: write id-token: write
@ -83,17 +63,15 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- os: ubuntu-22.04 - os: ubuntu-24.04
artifact-name: Linux artifact-name: Linux
cmake-preset: linux cmake-preset: linux
qt-version: 6.10.2
# 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 - os: ubuntu-24.04-arm
artifact-name: Linux-aarch64 artifact-name: Linux-aarch64
cmake-preset: linux cmake-preset: linux
qt-version: 6.10.2
- os: windows-2022 - os: windows-2022
artifact-name: Windows-MinGW-w64 artifact-name: Windows-MinGW-w64
@ -112,16 +90,19 @@ jobs:
cmake-preset: windows_msvc cmake-preset: windows_msvc
# TODO(@getchoo): This is the default in setup-dependencies/windows. Why isn't it working?!?! # TODO(@getchoo): This is the default in setup-dependencies/windows. Why isn't it working?!?!
vcvars-arch: amd64 vcvars-arch: amd64
qt-version: 6.10.2
- os: windows-11-arm - os: windows-11-arm
artifact-name: Windows-MSVC-arm64 artifact-name: Windows-MSVC-arm64
cmake-preset: windows_msvc cmake-preset: windows_msvc
vcvars-arch: arm64 vcvars-arch: arm64
qt-version: 6.10.2
- os: macos-14 - os: macos-26
artifact-name: macOS artifact-name: macOS
cmake-preset: macos_universal cmake-preset: macos_universal
macosx-deployment-target: 12.0 macosx-deployment-target: 12.0
qt-version: 6.9.3
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -155,7 +136,7 @@ jobs:
artifact-name: ${{ matrix.artifact-name }} artifact-name: ${{ matrix.artifact-name }}
msystem: ${{ matrix.msystem }} msystem: ${{ matrix.msystem }}
vcvars-arch: ${{ matrix.vcvars-arch }} vcvars-arch: ${{ matrix.vcvars-arch }}
qt-architecture: ${{ matrix.qt-architecture }} qt-version: ${{ matrix.qt-version }}
## ##
# BUILD # BUILD
@ -171,7 +152,7 @@ jobs:
- name: Run tests - name: Run tests
run: | run: |
ctest --preset "$CMAKE_PRESET" --build-config "$BUILD_TYPE" ctest --preset "$CMAKE_PRESET" --build-config "$BUILD_TYPE" --extra-verbose --output-on-failure
## ##
# PACKAGE # PACKAGE
@ -214,6 +195,8 @@ jobs:
- name: Package (Windows) - name: Package (Windows)
if: ${{ runner.os == 'Windows' }} if: ${{ runner.os == 'Windows' }}
uses: ./.github/actions/package/windows uses: ./.github/actions/package/windows
env:
CI_HAS_ACCESS_TO_AZURE: ${{ vars.CI_HAS_ACCESS_TO_AZURE || '' }}
with: with:
version: ${{ steps.short-version.outputs.version }} version: ${{ steps.short-version.outputs.version }}
build-type: ${{ steps.setup-dependencies.outputs.build-type }} build-type: ${{ steps.setup-dependencies.outputs.build-type }}

View file

@ -5,33 +5,8 @@ concurrency:
cancel-in-progress: true cancel-in-progress: true
on: on:
push: merge_group:
branches: types: [checks_requested]
- "develop"
- "release-*"
paths:
# File types
- "**.cpp"
- "**.h"
- "**.java"
- "**.ui"
# Directories
- "buildconfig/**"
- "cmake/**"
- "launcher/**"
- "libraries/**"
- "program_info/**"
- "tests/**"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/codeql/**"
- ".github/workflows/codeql.yml"
- ".github/actions/setup-dependencies/**"
pull_request: pull_request:
paths: paths:
# File types # File types
@ -79,11 +54,16 @@ jobs:
uses: ./.github/actions/setup-dependencies uses: ./.github/actions/setup-dependencies
with: with:
build-type: Debug build-type: Debug
qt-version: 6.4.3
- name: Configure and Build - name: Configure and Build
run: | run: |
cmake --preset linux cmake --preset linux
cmake --build --preset linux --config Debug cmake --build --preset linux --config Debug
- name: Run tests
run: |
ctest --preset linux --build-config Debug --extra-verbose --output-on-failure
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4 uses: github/codeql-action/analyze@v4

View file

@ -1,105 +0,0 @@
name: Flatpak
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches:
- "develop"
- "release-*"
# We don't do anything with these artifacts on releases. They go to Flathub
tags-ignore:
- "*"
paths:
# File types
- "**.cpp"
- "**.h"
- "**.java"
- "**.ui"
# Build files
- "flatpak/**"
# Directories
- "buildconfig/**"
- "cmake/**"
- "launcher/**"
- "libraries/**"
- "program_info/**"
- "tests/**"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/flatpak.yml"
pull_request:
paths:
# File types
- "**.cpp"
- "**.h"
- "**.java"
- "**.ui"
# Build files
- "flatpak/**"
# Directories
- "buildconfig/**"
- "cmake/**"
- "launcher/**"
- "libraries/**"
- "program_info/**"
- "tests/**"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/flatpak.yml"
workflow_dispatch:
permissions:
contents: read
jobs:
build:
name: Build (${{ matrix.arch }})
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
arch: x86_64
- os: ubuntu-22.04-arm
arch: aarch64
runs-on: ${{ matrix.os }}
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.10
options: --privileged
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
submodules: true
- name: Set short version
shell: bash
run: |
echo "VERSION=${GITHUB_SHA::7}" >> "$GITHUB_ENV"
- name: Build Flatpak
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
with:
bundle: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-Flatpak.flatpak
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
arch: ${{ matrix.arch }}

View file

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

View file

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

3
.gitmodules vendored
View file

@ -1,6 +1,3 @@
[submodule "libraries/libnbtplusplus"] [submodule "libraries/libnbtplusplus"]
path = libraries/libnbtplusplus path = libraries/libnbtplusplus
url = https://github.com/PrismLauncher/libnbtplusplus.git url = https://github.com/PrismLauncher/libnbtplusplus.git
[submodule "flatpak/shared-modules"]
path = flatpak/shared-modules
url = https://github.com/flathub/shared-modules.git

View file

@ -1,6 +1,9 @@
cmake_minimum_required(VERSION 3.22) # minimum version required by Qt 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) string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
if(IS_IN_SOURCE_BUILD) if(IS_IN_SOURCE_BUILD)
@ -27,6 +30,9 @@ set(CMAKE_C_STANDARD_REQUIRED true)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
include(GenerateExportHeader) include(GenerateExportHeader)
add_compile_definitions($<$<CONFIG:Release>:QT_NO_DEBUG>)
if(MSVC) if(MSVC)
# /GS Adds buffer security checks, default on but incuded anyway to mirror gcc's fstack-protector flag # /GS Adds buffer security checks, default on but incuded anyway to mirror gcc's fstack-protector flag
# /permissive- specify standards-conforming compiler behavior, also enabled by Qt6, default on with std:c++20 # /permissive- specify standards-conforming compiler behavior, also enabled by Qt6, default on with std:c++20
@ -79,19 +85,6 @@ else()
if(WIN32) if(WIN32)
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}") 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 # -ffunction-sections and -fdata-sections help reduce binary size
# -mguard=cf enables Control Flow Guard # -mguard=cf enables Control Flow Guard
# TODO: Look into -gc-sections to further reduce binary size # TODO: Look into -gc-sections to further reduce binary size
@ -101,8 +94,8 @@ else()
endif() endif()
endif() endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_WARN_DEPRECATED_UP_TO=0x060200") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_WARN_DEPRECATED_UP_TO=0x060400")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_UP_TO=0x060000") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_UP_TO=0x060400")
# set CXXFLAGS for build targets # set CXXFLAGS for build targets
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}") set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
@ -116,30 +109,24 @@ endif()
option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF) option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF)
# If this is a Debug build turn on address sanitiser # 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") 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") set(USE_ASAN_COMPILE_OPTIONS $<AND:$<CONFIG:Debug,RelWithDebInfo>,$<COMPILE_LANGUAGE:C,CXX>>)
# using clang with clang-cl front end if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
message(STATUS "Address Sanitizer available on Clang MSVC frontend") message(STATUS "Using Address Sanitizer compile options for MSVC frontend")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-") add_compile_options(
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-") $<${USE_ASAN_COMPILE_OPTIONS}:/fsanitize=address>
else() $<${USE_ASAN_COMPILE_OPTIONS}:/Oy->
# AppleClang and Clang )
message(STATUS "Address Sanitizer available on Clang") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null") message(STATUS "Using Address Sanitizer compile options for GCC/Clang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=null") add_compile_options(
endif() $<${USE_ASAN_COMPILE_OPTIONS}:-fsanitize=address,undefined>
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") $<${USE_ASAN_COMPILE_OPTIONS}:-fno-omit-frame-pointer>
# GCC $<${USE_ASAN_COMPILE_OPTIONS}:-fno-sanitize-recover=null>
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") link_libraries("asan" "ubsan")
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-")
else() else()
message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}") message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
endif() endif()
@ -192,7 +179,7 @@ set(Launcher_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE S
######## Set version numbers ######## ######## Set version numbers ########
set(Launcher_VERSION_MAJOR 10) set(Launcher_VERSION_MAJOR 10)
set(Launcher_VERSION_MINOR 0) set(Launcher_VERSION_MINOR 0)
set(Launcher_VERSION_PATCH 0) set(Launcher_VERSION_PATCH 5)
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}") set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}")
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}.0") set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}.0")
@ -232,6 +219,8 @@ set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL
# Builds # Builds
set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against") set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against")
option(Launcher_USE_PCH "Use precompiled headers where possible" ON)
# Java downloader # Java downloader
set(Launcher_ENABLE_JAVA_DOWNLOADER_DEFAULT ON) set(Launcher_ENABLE_JAVA_DOWNLOADER_DEFAULT ON)
@ -297,7 +286,7 @@ set(Launcher_BUILD_TIMESTAMP "${TODAY}")
# Find the required Qt parts # Find the required Qt parts
if(Launcher_QT_VERSION_MAJOR EQUAL 6) if(Launcher_QT_VERSION_MAJOR EQUAL 6)
set(QT_VERSION_MAJOR 6) set(QT_VERSION_MAJOR 6)
find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml NetworkAuth OpenGL) find_package(Qt6 6.4 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml NetworkAuth OpenGL)
find_package(Qt6 COMPONENTS DBus) find_package(Qt6 COMPONENTS DBus)
list(APPEND Launcher_QT_DBUS Qt6::DBus) list(APPEND Launcher_QT_DBUS Qt6::DBus)
else() else()
@ -337,7 +326,7 @@ if(NOT LibArchive_FOUND)
pkg_check_modules(libarchive REQUIRED IMPORTED_TARGET libarchive) pkg_check_modules(libarchive REQUIRED IMPORTED_TARGET libarchive)
endif() 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 # fallback to pkgconfig, important especially as many distros package toml++ built with meson
if(NOT tomlplusplus_FOUND) if(NOT tomlplusplus_FOUND)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
@ -379,7 +368,7 @@ if(UNIX AND APPLE)
# Mac bundle settings # Mac bundle settings
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_DisplayName}") set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_DisplayName}")
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_DisplayName}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.") set(MACOSX_BUNDLE_INFO_STRING "${Launcher_DisplayName}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.prismlauncher.${Launcher_Name}") set(MACOSX_BUNDLE_GUI_IDENTIFIER "${Launcher_AppID}")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_NAME}")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}")
@ -399,6 +388,52 @@ if(UNIX AND APPLE)
# Add the icon # Add the icon
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns) 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) elseif(UNIX)
include(KDEInstallDirs) include(KDEInstallDirs)

View file

@ -1,214 +1,221 @@
{ {
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json", "$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
"version": 8, "version": 8,
"cmakeMinimumRequired": { "cmakeMinimumRequired": {
"major": 3, "major": 3,
"minor": 28 "minor": 28
}, },
"configurePresets": [ "configurePresets": [
{ {
"name": "base", "name": "base",
"hidden": true, "hidden": true,
"binaryDir": "build", "binaryDir": "build",
"installDir": "install", "installDir": "install",
"generator": "Ninja Multi-Config", "generator": "Ninja Multi-Config",
"cacheVariables": { "cacheVariables": {
"Launcher_BUILD_ARTIFACT": "$penv{ARTIFACT_NAME}", "Launcher_BUILD_ARTIFACT": "$penv{ARTIFACT_NAME}",
"Launcher_BUILD_PLATFORM": "$penv{BUILD_PLATFORM}", "Launcher_BUILD_PLATFORM": "$penv{BUILD_PLATFORM}",
"Launcher_ENABLE_JAVA_DOWNLOADER": "ON", "Launcher_ENABLE_JAVA_DOWNLOADER": "ON",
"ENABLE_LTO": "ON" "ENABLE_LTO": "ON"
} }
}, },
{ {
"name": "linux", "name": "linux",
"displayName": "Linux", "displayName": "Linux",
"inherits": [ "inherits": [
"base" "base"
], ],
"condition": { "condition": {
"type": "equals", "type": "equals",
"lhs": "${hostSystemName}", "lhs": "${hostSystemName}",
"rhs": "Linux" "rhs": "Linux"
} }
}, },
{ {
"name": "macos", "name": "macos",
"displayName": "macOS", "displayName": "macOS",
"inherits": [ "inherits": [
"base" "base"
], ],
"condition": { "condition": {
"type": "equals", "type": "equals",
"lhs": "${hostSystemName}", "lhs": "${hostSystemName}",
"rhs": "Darwin" "rhs": "Darwin"
} },
}, "cacheVariables": {
{ "CMAKE_TOOLCHAIN_FILE": "$penv{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
"name": "macos_universal", }
"displayName": "macOS (Universal Binary)", },
"inherits": [ {
"macos" "name": "macos_universal",
], "displayName": "macOS (Universal Binary)",
"cacheVariables": { "inherits": [
"CMAKE_OSX_ARCHITECTURES": "x86_64;arm64", "macos"
"VCPKG_TARGET_TRIPLET": "universal-osx" ],
} "cacheVariables": {
}, "CMAKE_TOOLCHAIN_FILE": "$penv{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
{ "CMAKE_OSX_ARCHITECTURES": "x86_64;arm64",
"name": "windows_mingw", "VCPKG_TARGET_TRIPLET": "universal-osx"
"displayName": "Windows (MinGW)", }
"inherits": [ },
"base" {
], "name": "windows_mingw",
"condition": { "displayName": "Windows (MinGW)",
"type": "equals", "inherits": [
"lhs": "${hostSystemName}", "base"
"rhs": "Windows" ],
} "condition": {
}, "type": "equals",
{ "lhs": "${hostSystemName}",
"name": "windows_msvc", "rhs": "Windows"
"displayName": "Windows (MSVC)", }
"inherits": [ },
"base" {
], "name": "windows_msvc",
"condition": { "displayName": "Windows (MSVC)",
"type": "equals", "inherits": [
"lhs": "${hostSystemName}", "base"
"rhs": "Windows" ],
} "condition": {
} "type": "equals",
], "lhs": "${hostSystemName}",
"buildPresets": [ "rhs": "Windows"
{ },
"name": "linux", "cacheVariables": {
"displayName": "Linux", "CMAKE_TOOLCHAIN_FILE": "$penv{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
"condition": { }
"type": "equals", }
"lhs": "${hostSystemName}", ],
"rhs": "Linux" "buildPresets": [
}, {
"configurePreset": "linux" "name": "linux",
}, "displayName": "Linux",
{ "condition": {
"name": "macos", "type": "equals",
"displayName": "macOS", "lhs": "${hostSystemName}",
"condition": { "rhs": "Linux"
"type": "equals", },
"lhs": "${hostSystemName}", "configurePreset": "linux"
"rhs": "Darwin" },
}, {
"name": "macos",
"displayName": "macOS",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Darwin"
},
"configurePreset": "macos" "configurePreset": "macos"
}, },
{ {
"name": "macos_universal", "name": "macos_universal",
"displayName": "macOS (Universal Binary)", "displayName": "macOS (Universal Binary)",
"inherits": [ "inherits": [
"macos" "macos"
], ],
"configurePreset": "macos_universal" "configurePreset": "macos_universal"
}, },
{ {
"name": "windows_mingw", "name": "windows_mingw",
"displayName": "Windows (MinGW)", "displayName": "Windows (MinGW)",
"condition": { "condition": {
"type": "equals", "type": "equals",
"lhs": "${hostSystemName}", "lhs": "${hostSystemName}",
"rhs": "Windows" "rhs": "Windows"
}, },
"configurePreset": "windows_mingw" "configurePreset": "windows_mingw"
}, },
{ {
"name": "windows_msvc", "name": "windows_msvc",
"displayName": "Windows (MSVC)", "displayName": "Windows (MSVC)",
"condition": { "condition": {
"type": "equals", "type": "equals",
"lhs": "${hostSystemName}", "lhs": "${hostSystemName}",
"rhs": "Windows" "rhs": "Windows"
}, },
"configurePreset": "windows_msvc" "configurePreset": "windows_msvc"
} }
], ],
"testPresets": [ "testPresets": [
{ {
"name": "base", "name": "base",
"hidden": true, "hidden": true,
"output": { "output": {
"outputOnFailure": true "outputOnFailure": true
}, },
"execution": { "execution": {
"noTestsAction": "error" "noTestsAction": "error"
}, },
"filter": { "filter": {
"exclude": { "exclude": {
"name": "^example64|example$" "name": "^example64|example$"
} }
} }
}, },
{ {
"name": "linux", "name": "linux",
"displayName": "Linux", "displayName": "Linux",
"inherits": [ "inherits": [
"base" "base"
], ],
"condition": { "condition": {
"type": "equals", "type": "equals",
"lhs": "${hostSystemName}", "lhs": "${hostSystemName}",
"rhs": "Linux" "rhs": "Linux"
}, },
"configurePreset": "linux" "configurePreset": "linux"
}, },
{ {
"name": "macos", "name": "macos",
"displayName": "macOS", "displayName": "macOS",
"inherits": [ "inherits": [
"base" "base"
], ],
"condition": { "condition": {
"type": "equals", "type": "equals",
"lhs": "${hostSystemName}", "lhs": "${hostSystemName}",
"rhs": "Darwin" "rhs": "Darwin"
}, },
"configurePreset": "macos" "configurePreset": "macos"
}, },
{ {
"name": "macos_universal", "name": "macos_universal",
"displayName": "macOS (Universal Binary)", "displayName": "macOS (Universal Binary)",
"inherits": [ "inherits": [
"base" "base"
], ],
"condition": { "condition": {
"type": "equals", "type": "equals",
"lhs": "${hostSystemName}", "lhs": "${hostSystemName}",
"rhs": "Darwin" "rhs": "Darwin"
}, },
"configurePreset": "macos_universal" "configurePreset": "macos_universal"
}, },
{ {
"name": "windows_mingw", "name": "windows_mingw",
"displayName": "Windows (MinGW)", "displayName": "Windows (MinGW)",
"inherits": [ "inherits": [
"base" "base"
], ],
"condition": { "condition": {
"type": "equals", "type": "equals",
"lhs": "${hostSystemName}", "lhs": "${hostSystemName}",
"rhs": "Windows" "rhs": "Windows"
}, },
"configurePreset": "windows_mingw" "configurePreset": "windows_mingw"
}, },
{ {
"name": "windows_msvc", "name": "windows_msvc",
"displayName": "Windows (MSVC)", "displayName": "Windows (MSVC)",
"inherits": [ "inherits": [
"base" "base"
], ],
"condition": { "condition": {
"type": "equals", "type": "equals",
"lhs": "${hostSystemName}", "lhs": "${hostSystemName}",
"rhs": "Windows" "rhs": "Windows"
}, },
"configurePreset": "windows_msvc" "configurePreset": "windows_msvc"
} }
] ]
} }

View file

@ -13,7 +13,8 @@ Please also follow the project's conventions for C++:
- Public, private or protected `static const` class data members should be formatted as `SCREAMING_SNAKE_CASE`: `MAX_VALUE`. - Public, private or protected `static const` class data members should be formatted as `SCREAMING_SNAKE_CASE`: `MAX_VALUE`.
- Class function members should be formatted as `camelCase` without a prefix: `incrementCounter`. - Class function members should be formatted as `camelCase` without a prefix: `incrementCounter`.
- Global functions and non-`const` global variables should be formatted as `camelCase` without a prefix: `globalData`. - Global functions and non-`const` global variables should be formatted as `camelCase` without a prefix: `globalData`.
- `const` global variables, macros, and enum constants should be formatted as `SCREAMING_SNAKE_CASE`: `LIGHT_GRAY`. - `const` global variables and macros should be formatted as `SCREAMING_SNAKE_CASE`: `LIGHT_GRAY`.
- enum constants should be formatted as `PascalCase`: `CamelusBactrianus`
- Avoid inventing acronyms or abbreviations especially for a name of multiple words - like `tp` for `texturePack`. - Avoid inventing acronyms or abbreviations especially for a name of multiple words - like `tp` for `texturePack`.
- Avoid using `[[nodiscard]]` unless ignoring the return value is likely to cause a bug in cases such as: - Avoid using `[[nodiscard]]` unless ignoring the return value is likely to cause a bug in cases such as:
- A function allocates memory or another resource and the caller needs to clean it up. - A function allocates memory or another resource and the caller needs to clean it up.
@ -30,7 +31,7 @@ Here is what these conventions with the formatting configuration look like:
constexpr double PI = 3.14159; constexpr double PI = 3.14159;
enum class PizzaToppings { HAM_AND_PINEAPPLE, OREO_AND_KETCHUP }; enum class PizzaToppings { HamAndPineapple, OreoAndKetchup };
struct Person { struct Person {
QString name; QString name;

View file

@ -1,7 +1,7 @@
## Prism Launcher ## Prism Launcher
Prism Launcher - Minecraft Launcher Prism Launcher - Minecraft Launcher
Copyright (C) 2022-2025 Prism Launcher Contributors Copyright (C) 2022-2026 Prism Launcher Contributors
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View file

@ -33,11 +33,7 @@ These have debug information in the binaries, so their file sizes are relatively
Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**. Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**.
For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**, respectively, you can use these packages for the latest development versions: On Linux, we also offer our own [Flatpak nightly repository](https://github.com/PrismLauncher/flatpak). Most software centers are able to install it by opening [this link](https://flatpak.prismlauncher.org/prismlauncher-nightly.flatpakref).
[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=COPR&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher)
These packages are also available to all the distributions based on the ones mentioned above.
## Community & Support ## Community & Support
@ -61,12 +57,7 @@ The translation effort for Prism Launcher is hosted on [Weblate](https://hosted.
## Building ## Building
If you want to build Prism Launcher yourself, check the build instructions: If you want to build Prism Launcher yourself, check the [build instructions](https://prismlauncher.org/wiki/development/build-instructions).
- [Windows](https://prismlauncher.org/wiki/development/build-instructions/windows/)
- [Linux](https://prismlauncher.org/wiki/development/build-instructions/linux/)
- [MacOS](https://prismlauncher.org/wiki/development/build-instructions/macos/)
- [OpenBSD](https://prismlauncher.org/wiki/development/build-instructions/openbsd/)
## Sponsors & Partners ## Sponsors & Partners

View file

@ -33,7 +33,6 @@
* limitations under the License. * limitations under the License.
*/ */
#include <qstringliteral.h>
#include <QObject> #include <QObject>
#include "BuildConfig.h" #include "BuildConfig.h"

View file

@ -21,7 +21,9 @@
<key>CFBundleGetInfoString</key> <key>CFBundleGetInfoString</key>
<string>${MACOSX_BUNDLE_INFO_STRING}</string> <string>${MACOSX_BUNDLE_INFO_STRING}</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string> <string>${Launcher_Name}</string>
<key>CFBundleIconName</key>
<string>${Launcher_Name}</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
@ -42,6 +44,8 @@
<true/> <true/>
<key>LSRequiresCarbon</key> <key>LSRequiresCarbon</key>
<true/> <true/>
<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string> <string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>SUPublicEDKey</key> <key>SUPublicEDKey</key>

17
flake.lock generated
View file

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

View file

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

View file

@ -1,14 +0,0 @@
name: cmark
buildsystem: cmake-ninja
builddir: true
config-opts:
- -DCMAKE_TESTS=OFF
sources:
- type: archive
url: https://github.com/commonmark/cmark/archive/0.31.1.tar.gz
sha256: 3da93db5469c30588cfeb283d9d62edfc6ded9eb0edc10a4f5bbfb7d722ea802
x-checker-data:
type: anitya
project-id: 9159
stable-only: true
url-template: https://github.com/commonmark/cmark/archive/$version.tar.gz

View file

@ -1,20 +0,0 @@
{
"name": "flite",
"config-opts": [
"--enable-shared",
"--with-audio=pulseaudio"
],
"no-parallel-make": true,
"sources": [
{
"type": "git",
"url": "https://github.com/festvox/flite.git",
"tag": "v2.2",
"commit": "e9e2e37c329dbe98bfeb27a1828ef9a71fa84f88",
"x-checker-data": {
"type": "git",
"tag-pattern": "^v([\\d.]+)$"
}
}
]
}

View file

@ -1,18 +0,0 @@
{
"name": "libdecor",
"buildsystem": "meson",
"config-opts": [
"-Ddemo=false"
],
"sources": [
{
"type": "git",
"url": "https://gitlab.freedesktop.org/libdecor/libdecor.git",
"commit": "c2bd8ad6fa42c0cb17553ce77ad8a87d1f543b1f"
}
],
"cleanup": [
"/include",
"/lib/pkgconfig"
]
}

View file

@ -1,154 +0,0 @@
id: org.prismlauncher.PrismLauncher
runtime: org.kde.Platform
runtime-version: '6.10'
sdk: org.kde.Sdk
sdk-extensions:
- org.freedesktop.Sdk.Extension.openjdk17
command: prismlauncher
finish-args:
- --share=ipc
- --socket=x11
- --socket=wayland
- --device=all
- --share=network
- --socket=pulseaudio
# for Discord RPC mods
- --filesystem=xdg-run/app/com.discordapp.Discord:create
# Mod drag&drop
- --filesystem=xdg-download:ro
# FTBApp import
- --filesystem=~/.ftba:ro
# Userspace visibility for manual hugepages configuration
# Required for -XX:+UseLargePages
- --filesystem=/sys/kernel/mm/hugepages:ro
# Userspace visibility for transparent hugepages configuration
# Required for -XX:+UseTransparentHugePages
- --filesystem=/sys/kernel/mm/transparent_hugepage:ro
modules:
- cmark.yml
- tomlplusplus.yml
# Might be needed by some Controller mods (see https://github.com/isXander/Controlify/issues/31)
- shared-modules/libusb/libusb.json
# Needed for proper Wayland support
- libdecor.json
# Text to Speech in the game
- flite.json
- name: prismlauncher
buildsystem: cmake-ninja
builddir: true
config-opts:
- -DLauncher_BUILD_PLATFORM=flatpak
# This allows us to manage and update Java independently of this Flatpak
- -DLauncher_ENABLE_JAVA_DOWNLOADER=ON
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
build-options:
env:
JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17
JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac
run-tests: true
sources:
- type: dir
path: ../
- name: glfw
buildsystem: cmake-ninja
config-opts:
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
- -DBUILD_SHARED_LIBS:BOOL=ON
- -DGLFW_BUILD_WAYLAND:BOOL=ON
- -DGLFW_BUILD_DOCS:BOOL=OFF
sources:
- type: git
url: https://github.com/glfw/glfw.git
commit: 7b6aead9fb88b3623e3b3725ebb42670cbe4c579 # 3.4
- type: patch
path: patches/0009-Defer-setting-cursor-position-until-the-cursor-is-lo.patch
cleanup:
- /include
- /lib/cmake
- /lib/pkgconfig
- name: xrandr
buildsystem: autotools
sources:
- type: archive
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.3.tar.xz
sha256: f8dd7566adb74147fab9964680b6bbadee87cf406a7fcff51718a5e6949b841c
x-checker-data:
type: anitya
project-id: 14957
stable-only: true
url-template: https://xorg.freedesktop.org/archive/individual/app/xrandr-$version.tar.xz
cleanup:
- /share/man
- /bin/xkeystone
- name: gamemode
buildsystem: meson
config-opts:
- -Dwith-sd-bus-provider=no-daemon
- -Dwith-examples=false
post-install:
# gamemoderun is installed for users who want to use wrapper commands
# post-install is running inside the build dir, we need it from the source though
- install -Dm755 ../data/gamemoderun -t /app/bin
sources:
- type: archive
dest-filename: gamemode.tar.gz
url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.8.2
sha256: 2886d4ce543c78bd2a364316d5e7fd59ef06b71de63f896b37c6d3dc97658f60
x-checker-data:
type: json
url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest
version-query: .tag_name
url-query: .tarball_url
timestamp-query: .published_at
cleanup:
- /include
- /lib/pkgconfig
- /lib/libgamemodeauto.a
- name: glxinfo
buildsystem: meson
config-opts:
- --bindir=/app/mesa-demos
- -Degl=disabled
- -Dglut=disabled
- -Dosmesa=disabled
- -Dvulkan=disabled
- -Dwayland=disabled
post-install:
- mv -v /app/mesa-demos/glxinfo /app/bin
sources:
- type: archive
url: https://archive.mesa3d.org/demos/mesa-demos-9.0.0.tar.xz
sha256: 3046a3d26a7b051af7ebdd257a5f23bfeb160cad6ed952329cdff1e9f1ed496b
x-checker-data:
type: anitya
project-id: 16781
stable-only: true
url-template: https://archive.mesa3d.org/demos/mesa-demos-$version.tar.xz
cleanup:
- /include
- /mesa-demos
- /share
modules:
- shared-modules/glu/glu-9.json
- name: enhance
buildsystem: simple
build-commands:
- install -Dm755 prime-run /app/bin/prime-run
- mv /app/bin/prismlauncher /app/bin/prismrun
- install -Dm755 prismlauncher /app/bin/prismlauncher
sources:
- type: file
path: prime-run
- type: file
path: prismlauncher

View file

@ -1,59 +0,0 @@
From 9997ae55a47de469ea26f8437c30b51483abda5f Mon Sep 17 00:00:00 2001
From: Dan Klishch <danilklishch@gmail.com>
Date: Sat, 30 Sep 2023 23:38:05 -0400
Subject: Defer setting cursor position until the cursor is locked
---
src/wl_platform.h | 3 +++
src/wl_window.c | 14 ++++++++++++--
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/src/wl_platform.h b/src/wl_platform.h
index ca34f66e..cd1f227f 100644
--- a/src/wl_platform.h
+++ b/src/wl_platform.h
@@ -403,6 +403,9 @@ typedef struct _GLFWwindowWayland
int scaleSize;
int compositorPreferredScale;
+ double askedCursorPosX, askedCursorPosY;
+ GLFWbool didAskForSetCursorPos;
+
struct zwp_relative_pointer_v1* relativePointer;
struct zwp_locked_pointer_v1* lockedPointer;
struct zwp_confined_pointer_v1* confinedPointer;
diff --git a/src/wl_window.c b/src/wl_window.c
index 1de26558..0df16747 100644
--- a/src/wl_window.c
+++ b/src/wl_window.c
@@ -2586,8 +2586,9 @@ void _glfwGetCursorPosWayland(_GLFWwindow* window, double* xpos, double* ypos)
void _glfwSetCursorPosWayland(_GLFWwindow* window, double x, double y)
{
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
- "Wayland: The platform does not support setting the cursor position");
+ window->wl.didAskForSetCursorPos = true;
+ window->wl.askedCursorPosX = x;
+ window->wl.askedCursorPosY = y;
}
void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode)
@@ -2819,6 +2820,15 @@ static const struct zwp_relative_pointer_v1_listener relativePointerListener =
static void lockedPointerHandleLocked(void* userData,
struct zwp_locked_pointer_v1* lockedPointer)
{
+ _GLFWwindow* window = userData;
+
+ if (window->wl.didAskForSetCursorPos)
+ {
+ window->wl.didAskForSetCursorPos = false;
+ zwp_locked_pointer_v1_set_cursor_position_hint(window->wl.lockedPointer,
+ wl_fixed_from_double(window->wl.askedCursorPosX),
+ wl_fixed_from_double(window->wl.askedCursorPosY));
+ }
}
static void lockedPointerHandleUnlocked(void* userData,
--
2.42.0

View file

@ -1,4 +0,0 @@
#!/bin/sh
export __NV_PRIME_RENDER_OFFLOAD=1 __VK_LAYER_NV_optimus=NVIDIA_only __GLX_VENDOR_LIBRARY_NAME=nvidia
exec "$@"

View file

@ -1,11 +0,0 @@
#!/bin/bash
# discord RPC
for i in {0..9}; do
test -S "$XDG_RUNTIME_DIR"/discord-ipc-"$i" || ln -sf {app/com.discordapp.Discord,"$XDG_RUNTIME_DIR"}/discord-ipc-"$i";
done
export PATH="${PATH}${PATH:+:}/usr/lib/extensions/vulkan/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin"
export VK_LAYER_PATH="/usr/lib/extensions/vulkan/share/vulkan/implicit_layer.d/"
exec /app/bin/prismrun "$@"

@ -1 +0,0 @@
Subproject commit 73f08ed2c3187f6648ca04ebef030930a6c9f0be

View file

@ -1,6 +0,0 @@
name: tomlplusplus
buildsystem: cmake-ninja
sources:
- type: archive
url: https://github.com/marzer/tomlplusplus/archive/v3.4.0.tar.gz
sha256: 8517f65938a4faae9ccf8ebb36631a38c1cadfb5efa85d9a72e15b9e97d25155

View file

@ -50,6 +50,7 @@
#include "tools/GenericProfiler.h" #include "tools/GenericProfiler.h"
#include "ui/InstanceWindow.h" #include "ui/InstanceWindow.h"
#include "ui/MainWindow.h" #include "ui/MainWindow.h"
#include "ui/ToolTipFilter.h"
#include "ui/ViewLogWindow.h" #include "ui/ViewLogWindow.h"
#include "ui/dialogs/ProgressDialog.h" #include "ui/dialogs/ProgressDialog.h"
@ -371,25 +372,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
} }
QString origcwdPath = QDir::currentPath(); 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(); QString binPath = applicationDirPath();
#endif
{ {
// Root path is used for updates and portable data // Root path is used for updates and portable data
@ -620,25 +603,25 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
{ {
qInfo() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME + ", " + QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); qInfo() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME + ", " + QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
qInfo() << "Version : " << BuildConfig.printableVersionString(); qInfo() << "Version :" << BuildConfig.printableVersionString();
qInfo() << "Platform : " << BuildConfig.BUILD_PLATFORM; qInfo() << "Platform :" << BuildConfig.BUILD_PLATFORM;
qInfo() << "Git commit : " << BuildConfig.GIT_COMMIT; qInfo() << "Git commit :" << BuildConfig.GIT_COMMIT;
qInfo() << "Git refspec : " << BuildConfig.GIT_REFSPEC; qInfo() << "Git refspec :" << BuildConfig.GIT_REFSPEC;
qInfo() << "Compiled for : " << BuildConfig.systemID(); qInfo() << "Compiled for :" << BuildConfig.systemID();
qInfo() << "Compiled by : " << BuildConfig.compilerID(); qInfo() << "Compiled by :" << BuildConfig.compilerID();
qInfo() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT; qInfo() << "Build Artifact :" << BuildConfig.BUILD_ARTIFACT;
qInfo() << "Updates Enabled : " << (updaterEnabled() ? "Yes" : "No"); qInfo() << "Updates Enabled :" << (updaterEnabled() ? "Yes" : "No");
if (adjustedBy.size()) { if (adjustedBy.size()) {
qInfo() << "Work dir before adjustment : " << origcwdPath; qInfo() << "Work dir before adjustment :" << origcwdPath;
qInfo() << "Work dir after adjustment : " << QDir::currentPath(); qInfo() << "Work dir after adjustment :" << QDir::currentPath();
qInfo() << "Adjusted by : " << adjustedBy; qInfo() << "Adjusted by :" << adjustedBy;
} else { } else {
qInfo() << "Work dir : " << QDir::currentPath(); qInfo() << "Work dir :" << QDir::currentPath();
} }
qInfo() << "Binary path : " << binPath; qInfo() << "Binary path :" << binPath;
qInfo() << "Application root path : " << m_rootPath; qInfo() << "Application root path :" << m_rootPath;
if (!m_instanceIdToLaunch.isEmpty()) { if (!m_instanceIdToLaunch.isEmpty()) {
qInfo() << "ID of instance to launch : " << m_instanceIdToLaunch; qInfo() << "ID of instance to launch :" << m_instanceIdToLaunch;
} }
if (!m_serverToJoin.isEmpty()) { if (!m_serverToJoin.isEmpty()) {
qInfo() << "Address of server to join :" << m_serverToJoin; qInfo() << "Address of server to join :" << m_serverToJoin;
@ -702,8 +685,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
QFontInfo consoleFontInfo(consoleFont); QFontInfo consoleFontInfo(consoleFont);
QString resolvedDefaultMonospace = consoleFontInfo.family(); QString resolvedDefaultMonospace = consoleFontInfo.family();
QFont resolvedFont(resolvedDefaultMonospace); QFont resolvedFont(resolvedDefaultMonospace);
qDebug() << "Detected default console font:" << resolvedDefaultMonospace qDebug().nospace() << "Detected default console font: " << resolvedDefaultMonospace
<< ", substitutions:" << resolvedFont.substitutions().join(','); << ", substitutions: " << resolvedFont.substitutions().join(',');
m_settings->registerSetting("ConsoleFont", resolvedDefaultMonospace); m_settings->registerSetting("ConsoleFont", resolvedDefaultMonospace);
m_settings->registerSetting("ConsoleFontSize", defaultSize); m_settings->registerSetting("ConsoleFontSize", defaultSize);
@ -1005,7 +988,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// instance path: check for problems with '!' in instance path and warn the user in the log // instance path: check for problems with '!' in instance path and warn the user in the log
// and remember that we have to show him a dialog when the gui starts (if it does so) // and remember that we have to show him a dialog when the gui starts (if it does so)
QString instDir = m_settings->get("InstanceDir").toString(); QString instDir = m_settings->get("InstanceDir").toString();
qInfo() << "Instance path : " << instDir; qInfo() << "Instance path :" << instDir;
if (FS::checkProblemticPathJava(QDir(instDir))) { if (FS::checkProblemticPathJava(QDir(instDir))) {
qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!"; qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!";
} }
@ -1217,6 +1200,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
} }
} }
if (qgetenv("XDG_CURRENT_DESKTOP") == "gamescope") {
installEventFilter(new ToolTipFilter);
}
if (createSetupWizard()) { if (createSetupWizard()) {
return; return;
} }

25
launcher/AssertHelpers.h Normal file
View file

@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2025 Octol1ttle <l1ttleofficial@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#if defined(ASSERT_NEVER)
#error ASSERT_NEVER already defined
#else
#define ASSERT_NEVER(cond) (Q_ASSERT((cond) == false), (cond))
#endif

View file

@ -102,6 +102,9 @@ set(CORE_SOURCES
MMCTime.cpp MMCTime.cpp
MTPixmapCache.h MTPixmapCache.h
# Assertion helper
AssertHelpers.h
) )
if (UNIX AND NOT CYGWIN AND NOT APPLE) if (UNIX AND NOT CYGWIN AND NOT APPLE)
set(CORE_SOURCES set(CORE_SOURCES
@ -261,6 +264,8 @@ set(MINECRAFT_SOURCES
minecraft/launch/ClaimAccount.h minecraft/launch/ClaimAccount.h
minecraft/launch/CreateGameFolders.cpp minecraft/launch/CreateGameFolders.cpp
minecraft/launch/CreateGameFolders.h minecraft/launch/CreateGameFolders.h
minecraft/launch/EnsureOfflineLibraries.cpp
minecraft/launch/EnsureOfflineLibraries.h
minecraft/launch/ModMinecraftJar.cpp minecraft/launch/ModMinecraftJar.cpp
minecraft/launch/ModMinecraftJar.h minecraft/launch/ModMinecraftJar.h
minecraft/launch/ExtractNatives.cpp minecraft/launch/ExtractNatives.cpp
@ -346,6 +351,7 @@ set(MINECRAFT_SOURCES
minecraft/mod/TexturePackFolderModel.h minecraft/mod/TexturePackFolderModel.h
minecraft/mod/TexturePackFolderModel.cpp minecraft/mod/TexturePackFolderModel.cpp
minecraft/mod/ShaderPackFolderModel.h minecraft/mod/ShaderPackFolderModel.h
minecraft/mod/ShaderPackFolderModel.cpp
minecraft/mod/tasks/ResourceFolderLoadTask.h minecraft/mod/tasks/ResourceFolderLoadTask.h
minecraft/mod/tasks/ResourceFolderLoadTask.cpp minecraft/mod/tasks/ResourceFolderLoadTask.cpp
minecraft/mod/tasks/LocalModParseTask.h minecraft/mod/tasks/LocalModParseTask.h
@ -842,6 +848,8 @@ SET(LAUNCHER_SOURCES
ui/InstanceWindow.cpp ui/InstanceWindow.cpp
ui/ViewLogWindow.h ui/ViewLogWindow.h
ui/ViewLogWindow.cpp ui/ViewLogWindow.cpp
ui/ToolTipFilter.h
ui/ToolTipFilter.cpp
# FIXME: maybe find a better home for this. # FIXME: maybe find a better home for this.
FileIgnoreProxy.cpp FileIgnoreProxy.cpp
@ -1300,6 +1308,19 @@ endif()
include(CompilerWarnings) include(CompilerWarnings)
######## Precompiled Headers ###########
if(${Launcher_USE_PCH})
message(STATUS "Using precompiled headers for applicable launcher targets")
set(PRECOMPILED_HEADERS
include/base.pch.hpp
include/qtcore.pch.hpp
include/qtgui.pch.hpp
)
endif()
####### Targets ########
# Add executable # Add executable
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES}) add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
set_project_warnings(Launcher_logic set_project_warnings(Launcher_logic
@ -1308,6 +1329,11 @@ set_project_warnings(Launcher_logic
"${Launcher_GCC_WARNINGS}") "${Launcher_GCC_WARNINGS}")
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION) target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
if(${Launcher_USE_PCH})
target_precompile_headers(Launcher_logic PRIVATE ${PRECOMPILED_HEADERS})
endif()
target_link_libraries(Launcher_logic target_link_libraries(Launcher_logic
systeminfo systeminfo
Launcher_murmur2 Launcher_murmur2
@ -1389,6 +1415,11 @@ endif()
target_link_libraries(Launcher_logic) target_link_libraries(Launcher_logic)
add_executable(${Launcher_Name} MACOSX_BUNDLE WIN32 main.cpp ${LAUNCHER_RCS}) add_executable(${Launcher_Name} MACOSX_BUNDLE WIN32 main.cpp ${LAUNCHER_RCS})
if(${Launcher_USE_PCH})
target_precompile_headers(${Launcher_Name} REUSE_FROM Launcher_logic)
endif()
target_link_libraries(${Launcher_Name} Launcher_logic) target_link_libraries(${Launcher_Name} Launcher_logic)
if(DEFINED Launcher_APP_BINARY_NAME) if(DEFINED Launcher_APP_BINARY_NAME)
@ -1412,14 +1443,19 @@ install(TARGETS ${Launcher_Name}
) )
# Deploy PDBs # Deploy PDBs
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}> DESTINATION ${BINARY_DEST_DIR}) install(FILES $<TARGET_PDB_FILE:${Launcher_Name}> DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
endif() endif()
if(Launcher_BUILD_UPDATER) if(Launcher_BUILD_UPDATER)
# Updater # Updater
add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI}) add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI})
target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
if(${Launcher_USE_PCH})
target_precompile_headers(prism_updater_logic PRIVATE ${PRECOMPILED_HEADERS})
endif()
target_link_libraries(prism_updater_logic target_link_libraries(prism_updater_logic
${ZLIB_LIBRARIES} ${ZLIB_LIBRARIES}
systeminfo systeminfo
@ -1440,6 +1476,10 @@ if(Launcher_BUILD_UPDATER)
target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest) target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest)
target_link_libraries("${Launcher_Name}_updater" prism_updater_logic) target_link_libraries("${Launcher_Name}_updater" prism_updater_logic)
if(${Launcher_USE_PCH})
target_precompile_headers("${Launcher_Name}_updater" REUSE_FROM prism_updater_logic)
endif()
if(DEFINED Launcher_APP_BINARY_NAME) if(DEFINED Launcher_APP_BINARY_NAME)
set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater") set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater")
endif() endif()
@ -1455,8 +1495,8 @@ if(Launcher_BUILD_UPDATER)
) )
# Deploy PDBs # Deploy PDBs
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_updater> DESTINATION ${BINARY_DEST_DIR}) install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_updater> DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
endif() endif()
endif() endif()
@ -1469,6 +1509,11 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
"${Launcher_GCC_WARNINGS}") "${Launcher_GCC_WARNINGS}")
target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
if(${Launcher_USE_PCH})
target_precompile_headers(filelink_logic PRIVATE ${PRECOMPILED_HEADERS})
endif()
target_link_libraries(filelink_logic target_link_libraries(filelink_logic
systeminfo systeminfo
BuildConfig BuildConfig
@ -1480,8 +1525,12 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
) )
add_executable("${Launcher_Name}_filelink" WIN32 filelink/filelink_main.cpp) add_executable("${Launcher_Name}_filelink" WIN32 filelink/filelink_main.cpp)
target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest) target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest)
if(${Launcher_USE_PCH})
target_precompile_headers("${Launcher_Name}_filelink" REUSE_FROM filelink_logic)
endif()
# HACK: Fix manifest issues with Ninja in release mode (and only release mode) and MSVC # 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 # 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 # Thank you 2018 CMake mailing list thread https://cmake.cmake.narkive.com/LnotZXus/conflicting-msvc-manifests
@ -1506,8 +1555,8 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
) )
# Deploy PDBs # Deploy PDBs
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) if(CMAKE_CXX_LINKER_SUPPORTS_PDB)
install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_filelink> DESTINATION ${BINARY_DEST_DIR}) install(FILES $<TARGET_PDB_FILE:${Launcher_Name}_filelink> DESTINATION ${BINARY_DEST_DIR} OPTIONAL)
endif() endif()
endif() endif()
@ -1567,6 +1616,49 @@ if(WIN32 OR (UNIX AND APPLE))
SCRIPT ${QT_DEPLOY_SCRIPT} SCRIPT ${QT_DEPLOY_SCRIPT}
COMPONENT bundle COMPONENT bundle
) )
# FIXME: remove this crap once we stop using msys2
if(MINGW)
# i've not found a solution better than injecting the config vars like this...
# with install(CODE" for everything everything just breaks
install(CODE "
set(QT_PLUGINS_DIR \"${QT_PLUGINS_DIR}\")
set(QT_LIBS_DIR \"${QT_LIBS_DIR}\")
set(QT_LIBEXECS_DIR \"${QT_LIBEXECS_DIR}\")
set(CMAKE_SYSTEM_LIBRARY_PATH \"${CMAKE_SYSTEM_LIBRARY_PATH}\")
set(CMAKE_INSTALL_PREFIX \"${CMAKE_INSTALL_PREFIX}\")
"
COMPONENT bundle)
install(CODE [[
file(GLOB QT_IMAGEFORMAT_DLLS "${QT_PLUGINS_DIR}/imageformats/*.dll")
set(CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL objdump)
file(GET_RUNTIME_DEPENDENCIES
RESOLVED_DEPENDENCIES_VAR imageformatdeps
LIBRARIES ${QT_IMAGEFORMAT_DLLS}
DIRECTORIES
${CMAKE_SYSTEM_LIBRARY_PATH}
${QT_PLUGINS_DIR}
${QT_LIBS_DIR}
${QT_LIBEXECS_DIR}
PRE_EXCLUDE_REGEXES
"^(api-ms-win|ext-ms)-.*\\.dll$"
# FIXME: Why aren't these caught by the below regex???
"^azure.*\\.dll$"
"^vcruntime.*\\.dll$"
POST_EXCLUDE_REGEXES
"system32"
)
foreach(_lib ${imageformatdeps})
file(INSTALL
DESTINATION ${CMAKE_INSTALL_PREFIX}
TYPE SHARED_LIBRARY
FOLLOW_SYMLINK_CHAIN
FILES ${_lib}
)
endforeach()
]]
COMPONENT bundle)
endif()
# Add qt.conf - this makes Qt stop looking for things outside the bundle # Add qt.conf - this makes Qt stop looking for things outside the bundle
install( install(
@ -1580,3 +1672,15 @@ if(WIN32 OR (UNIX AND APPLE))
COMPONENT bundle COMPONENT bundle
) )
endif() endif()
find_program(CLANG_FORMAT clang-format OPTIONAL)
if(CLANG_FORMAT)
message(STATUS "Creating clang-format target")
add_custom_target(
clang-format
COMMAND ${CLANG_FORMAT} -i --style=file:${CMAKE_SOURCE_DIR}/.clang-format ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${PRISMUPDATER_SOURCES} ${LINKEXE_SOURCES} ${PRECOMPILED_HEADERS}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
)
else()
message(WARNING "Unable to find `clang-format`. Not creating custom target")
endif()

View file

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

View file

@ -37,11 +37,6 @@ bool openUrl(const QUrl& url);
*/ */
bool isFlatpak(); 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 * Determine whether the launcher is running in a Snap environment
*/ */

View file

@ -266,7 +266,21 @@ bool FileIgnoreProxy::filterAcceptsRow(int sourceRow, const QModelIndex& sourceP
bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const
{ {
return m_ignoreFiles.contains(fileInfo.fileName()) || m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath())); if (m_ignoreFiles.contains(fileInfo.fileName())) {
return true;
}
for (const auto& suffix : m_ignoreFilesSuffixes) {
if (fileInfo.fileName().endsWith(suffix)) {
return true;
}
}
if (m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath()))) {
return true;
}
return false;
} }
bool FileIgnoreProxy::filterFile(const QFileInfo& file) const bool FileIgnoreProxy::filterFile(const QFileInfo& file) const

View file

@ -66,6 +66,7 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
// list of file names that need to be removed completely from model // list of file names that need to be removed completely from model
inline QStringList& ignoreFilesWithName() { return m_ignoreFiles; } inline QStringList& ignoreFilesWithName() { return m_ignoreFiles; }
inline QStringList& ignoreFilesWithSuffix() { return m_ignoreFilesSuffixes; }
// list of relative paths that need to be removed completely from model // list of relative paths that need to be removed completely from model
inline SeparatorPrefixTree<'/'>& ignoreFilesWithPath() { return m_ignoreFilePaths; } inline SeparatorPrefixTree<'/'>& ignoreFilesWithPath() { return m_ignoreFilePaths; }
@ -85,5 +86,6 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
const QString m_root; const QString m_root;
SeparatorPrefixTree<'/'> m_blocked; SeparatorPrefixTree<'/'> m_blocked;
QStringList m_ignoreFiles; QStringList m_ignoreFiles;
QStringList m_ignoreFilesSuffixes;
SeparatorPrefixTree<'/'> m_ignoreFilePaths; SeparatorPrefixTree<'/'> m_ignoreFilePaths;
}; };

View file

@ -4,7 +4,6 @@
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me> * Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * 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 * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -433,7 +432,7 @@ void create_link::make_link_list(const QString& offset)
link_file(src, ""); link_file(src, "");
} else { } else {
if (m_debug) if (m_debug)
qDebug() << "linking recursively:" << src << "to" << dst << ", max_depth:" << m_max_depth; qDebug().nospace() << "linking recursively: " << src << " to " << dst << ", max_depth: " << m_max_depth;
QDir src_dir(src); QDir src_dir(src);
QDirIterator source_it(src, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::Subdirectories); QDirIterator source_it(src, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::Subdirectories);
@ -593,7 +592,7 @@ void create_link::runPrivileged(const QString& offset)
} }
ExternalLinkFileProcess* linkFileProcess = new ExternalLinkFileProcess(serverName, m_useHardLinks, this); ExternalLinkFileProcess* linkFileProcess = new ExternalLinkFileProcess(serverName, m_useHardLinks, this);
connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [this, gotResults]() { emit finishedPrivileged(gotResults); }); connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [this, &gotResults]() { emit finishedPrivileged(gotResults); });
connect(linkFileProcess, &ExternalLinkFileProcess::finished, linkFileProcess, &QObject::deleteLater); connect(linkFileProcess, &ExternalLinkFileProcess::finished, linkFileProcess, &QObject::deleteLater);
linkFileProcess->start(); linkFileProcess->start();
@ -773,34 +772,6 @@ QString ResolveExecutable(QString path)
return pathInfo.absoluteFilePath(); 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 * Normalize path
* *
@ -1129,17 +1100,17 @@ QString createShortcut(QString destination, QString target, QStringList args, QS
hres = ppf->Save(wsz, TRUE); hres = ppf->Save(wsz, TRUE);
if (FAILED(hres)) { if (FAILED(hres)) {
qWarning() << "IPresistFile->Save() failed"; qWarning() << "IPresistFile->Save() failed";
qWarning() << "hres = " << hres; qWarning() << "hres =" << hres;
} }
ppf->Release(); ppf->Release();
} else { } else {
qWarning() << "Failed to query IPersistFile interface from IShellLink instance"; qWarning() << "Failed to query IPersistFile interface from IShellLink instance";
qWarning() << "hres = " << hres; qWarning() << "hres =" << hres;
} }
psl->Release(); psl->Release();
} else { } else {
qWarning() << "Failed to create IShellLink instance"; qWarning() << "Failed to create IShellLink instance";
qWarning() << "hres = " << hres; qWarning() << "hres =" << hres;
} }
// go away COM, nobody likes you // go away COM, nobody likes you
@ -1428,14 +1399,14 @@ bool win_ioctl_clone(const std::wstring& src_path, const std::wstring& dst_path,
ULONG fs_flags; ULONG fs_flags;
if (!GetVolumeInformationByHandleW(hSourceFile, nullptr, 0, nullptr, nullptr, &fs_flags, nullptr, 0)) { if (!GetVolumeInformationByHandleW(hSourceFile, nullptr, 0, nullptr, nullptr, &fs_flags, nullptr, 0)) {
ec = std::error_code(GetLastError(), std::system_category()); ec = std::error_code(GetLastError(), std::system_category());
qDebug() << "Failed to get Filesystem information for " << src_path.c_str(); qDebug() << "Failed to get Filesystem information for" << src_path.c_str();
CloseHandle(hSourceFile); CloseHandle(hSourceFile);
return false; return false;
} }
if (!(fs_flags & FILE_SUPPORTS_BLOCK_REFCOUNTING)) { if (!(fs_flags & FILE_SUPPORTS_BLOCK_REFCOUNTING)) {
SetLastError(ERROR_NOT_CAPABLE); SetLastError(ERROR_NOT_CAPABLE);
ec = std::error_code(GetLastError(), std::system_category()); ec = std::error_code(GetLastError(), std::system_category());
qWarning() << "Filesystem at " << src_path.c_str() << " does not support reflink"; qWarning() << "Filesystem at" << src_path.c_str() << "does not support reflink";
CloseHandle(hSourceFile); CloseHandle(hSourceFile);
return false; return false;
} }

View file

@ -4,7 +4,6 @@
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me> * Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * 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 * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -43,13 +42,11 @@
#include <system_error> #include <system_error>
#include <QCoreApplication>
#include <QDir> #include <QDir>
#include <QFlags> #include <QFlags>
#include <QLocalServer> #include <QLocalServer>
#include <QObject> #include <QObject>
#include <QPair> #include <QPair>
#include <QProcess>
#include <QThread> #include <QThread>
namespace FS { namespace FS {
@ -336,14 +333,6 @@ QString pathTruncate(const QString& path, int depth);
*/ */
QString ResolveExecutable(QString path); 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 * Normalize path
* *

View file

@ -64,7 +64,6 @@ void InstanceCopyTask::executeTask()
savesCopy = std::make_unique<FS::copy>(FS::PathCombine(m_origInstance->gameRoot(), "saves"), savesCopy = std::make_unique<FS::copy>(FS::PathCombine(m_origInstance->gameRoot(), "saves"),
FS::PathCombine(staging_mc_dir, "saves")); FS::PathCombine(staging_mc_dir, "saves"));
savesCopy->followSymlinks(true);
(*savesCopy)(true); (*savesCopy)(true);
setProgress(0, savesCopy->totalCopied()); setProgress(0, savesCopy->totalCopied());
connect(savesCopy.get(), &FS::copy::fileCopied, [this](QString src) { setProgress(m_progress + 1, m_progressTotal); }); connect(savesCopy.get(), &FS::copy::fileCopied, [this](QString src) { setProgress(m_progress + 1, m_progressTotal); });
@ -126,11 +125,11 @@ void InstanceCopyTask::executeTask()
return !there_were_errors; return !there_were_errors;
} }
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath); FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
folderCopy.followSymlinks(false).matcher(m_matcher); folderCopy.matcher(m_matcher);
folderCopy(true); folderCopy(true);
setProgress(0, folderCopy.totalCopied()); setProgress(0, folderCopy.totalCopied());
connect(&folderCopy, &FS::copy::fileCopied, [this](QString src) { setProgress(m_progress + 1, m_progressTotal); }); connect(&folderCopy, &FS::copy::fileCopied, [this]() { setProgress(m_progress + 1, m_progressTotal); });
return folderCopy(); return folderCopy();
}); });
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished); connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished);

View file

@ -25,7 +25,7 @@ void InstanceCreationTask::executeTask()
qWarning() << "Instance creation failed!"; qWarning() << "Instance creation failed!";
if (!m_error_message.isEmpty()) { if (!m_error_message.isEmpty()) {
qWarning() << "Reason: " << m_error_message; qWarning() << "Reason:" << m_error_message;
emitFailed(tr("Error while creating new instance:\n%1").arg(m_error_message)); emitFailed(tr("Error while creating new instance:\n%1").arg(m_error_message));
} else { } else {
emitFailed(tr("Error while creating new instance.")); emitFailed(tr("Error while creating new instance."));

View file

@ -150,22 +150,15 @@ void InstanceImportTask::processZipPack()
extractDir.cd("minecraft"); extractDir.cd("minecraft");
m_modpackType = ModpackType::Technic; m_modpackType = ModpackType::Technic;
stop = true; stop = true;
} else { } else if (fileName == "manifest.json") {
QFileInfo fileInfo(fileName); qDebug() << "Flame:" << true;
if (fileInfo.fileName() == "instance.cfg") { m_modpackType = ModpackType::Flame;
qDebug() << "MultiMC:" << true; stop = true;
m_modpackType = ModpackType::MultiMC; } else if (QFileInfo fileInfo(fileName); fileInfo.fileName() == "instance.cfg") {
root = cleanPath(fileInfo.path()); qDebug() << "MultiMC:" << true;
stop = true; m_modpackType = ModpackType::MultiMC;
return true; root = cleanPath(fileInfo.path());
} stop = true;
if (fileInfo.fileName() == "manifest.json") {
qDebug() << "Flame:" << true;
m_modpackType = ModpackType::Flame;
root = cleanPath(fileInfo.path());
stop = true;
return true;
}
} }
QCoreApplication::processEvents(); QCoreApplication::processEvents();
return true; return true;
@ -271,7 +264,7 @@ bool installIcon(QString root, QString instIconKey)
if (iconList->iconFileExists(instIconKey)) { if (iconList->iconFileExists(instIconKey)) {
iconList->deleteIcon(instIconKey); iconList->deleteIcon(instIconKey);
} }
iconList->installIcon(importIconPath, instIconKey + ".png"); iconList->installIcon(importIconPath, instIconKey + "." + QFileInfo(importIconPath).suffix());
return true; return true;
} }
return false; return false;

View file

@ -101,6 +101,21 @@ QJsonArray requireArray(const QJsonDocument& doc, const QString& what)
return doc.array(); 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) void writeString(QJsonObject& to, const QString& key, const QString& value)
{ {
if (!value.isEmpty()) { if (!value.isEmpty()) {

View file

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

View file

@ -220,7 +220,10 @@ void LaunchController::login()
if (tries > 0 && tries % 3 == 0) { if (tries > 0 && tries % 3 == 0) {
auto result = auto result =
QMessageBox::question(m_parentWidget, tr("Continue launch?"), QMessageBox::question(m_parentWidget, tr("Continue launch?"),
tr("It looks like we couldn't launch after %1 tries. Do you want to continue trying?").arg(tries)); tr("It looks like we couldn't launch after %1 tries. Usually this can be fixed by logging out and "
"logging back in your Microsoft account. If that doesn't work, Minecraft authentication servers "
"may be having an outage or you may need a VPN in your region. Do you want to continue trying?")
.arg(tries));
if (result == QMessageBox::No) { if (result == QMessageBox::No) {
emitAborted(); emitAborted();
@ -266,7 +269,7 @@ void LaunchController::login()
} }
/* fallthrough */ /* fallthrough */
case AccountState::Online: { case AccountState::Online: {
if (!m_session->wants_online) { if (!m_session->wants_online && m_accountToUse->accountType() != AccountType::Offline) {
// we ask the user for a player name // we ask the user for a player name
bool ok = false; bool ok = false;
QString name; QString name;

View file

@ -18,89 +18,19 @@ LAUNCHER_NAME=@Launcher_APP_BINARY_NAME@
LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")" LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")"
echo "Launcher Dir: ${LAUNCHER_DIR}" echo "Launcher Dir: ${LAUNCHER_DIR}"
# Set up env. # Makes the launcher use portals for file picking
# Pass our custom variables separately so that the launcher can remove them for child processes export QT_QPA_PLATFORMTHEME=xdgdesktopportal
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"
export LD_LIBRARY_PATH="$LAUNCHER_LD_LIBRARY_PATH:$LD_LIBRARY_PATH" # Just to be sure...
export LD_PRELOAD="$LAUNCHER_LD_PRELOAD:$LD_PRELOAD" chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}"
export QT_PLUGIN_PATH="$LAUNCHER_QT_PLUGIN_PATH:$QT_PLUGIN_PATH"
export QT_FONTPATH="$LAUNCHER_QT_FONTPATH:$QT_FONTPATH"
# Detect missing dependencies... ARGS=("${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}")
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."
# Just to be sure... if [ -f portable.txt ]; then
chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" ARGS+=("-d" "${LAUNCHER_DIR}")
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
fi fi
ARGS+=("$@")
# Run the launcher
exec -a "${ARGS[@]}"

View file

@ -16,7 +16,6 @@
*/ */
#include <MMCTime.h> #include <MMCTime.h>
#include <qobject.h>
#include <QDateTime> #include <QDateTime>
#include <QObject> #include <QObject>

View file

@ -56,18 +56,18 @@ bool mergeZipFiles(ArchiveWriter& into, QFileInfo from, QSet<QString>& contained
return r.parse([&into, &contained, &filter, from](ArchiveReader::File* f) { return r.parse([&into, &contained, &filter, from](ArchiveReader::File* f) {
auto filename = f->filename(); auto filename = f->filename();
if (filter && !filter(filename)) { if (filter && !filter(filename)) {
qDebug() << "Skipping file " << filename << " from " << from.fileName() << " - filtered"; qDebug() << "Skipping file" << filename << "from" << from.fileName() << "- filtered";
f->skip(); f->skip();
return true; return true;
} }
if (contained.contains(filename)) { if (contained.contains(filename)) {
qDebug() << "Skipping already contained file " << filename << " from " << from.fileName(); qDebug() << "Skipping already contained file" << filename << "from" << from.fileName();
f->skip(); f->skip();
return true; return true;
} }
contained.insert(filename); contained.insert(filename);
if (!into.addFile(f)) { if (!into.addFile(f)) {
qCritical() << "Failed to copy data of " << filename << " into the jar"; qCritical() << "Failed to copy data of" << filename << "into the jar";
return false; return false;
} }
return true; return true;
@ -149,7 +149,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
return false; return false;
} }
qDebug() << "Adding folder " << filename.fileName() << " from " << filename.absoluteFilePath(); qDebug() << "Adding folder" << filename.fileName() << "from" << filename.absoluteFilePath();
} else { } else {
// Make sure we do not continue launching when something is missing or undefined... // Make sure we do not continue launching when something is missing or undefined...
zipOut.close(); zipOut.close();
@ -321,7 +321,7 @@ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, Q
for (const auto& e : entries) { for (const auto& e : entries) {
if (excludeFilter && excludeFilter(e)) { if (excludeFilter && excludeFilter(e)) {
QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath()); QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath());
qDebug() << "Skipping file " << relativeFilePath; qDebug() << "Skipping file" << relativeFilePath;
continue; continue;
} }

View file

@ -1,6 +1,5 @@
#pragma once #pragma once
#include <qlogging.h>
#include <QString> #include <QString>
#include <compare> #include <compare>

View file

@ -103,8 +103,8 @@ void ResourceDownloadTask::downloadSucceeded()
void ResourceDownloadTask::downloadFailed(QString reason) void ResourceDownloadTask::downloadFailed(QString reason)
{ {
emitFailed(reason);
m_filesNetJob.reset(); m_filesNetJob.reset();
emitFailed(reason);
} }
void ResourceDownloadTask::downloadProgressChanged(qint64 current, qint64 total) void ResourceDownloadTask::downloadProgressChanged(qint64 current, qint64 total)

View file

@ -35,7 +35,6 @@
*/ */
#include "StringUtils.h" #include "StringUtils.h"
#include <qpair.h>
#include <QRegularExpression> #include <QRegularExpression>
#include <QUuid> #include <QUuid>

View file

@ -57,7 +57,7 @@ QByteArray ArchiveReader::File::readAll(int* outStatus)
data.append(static_cast<const char*>(buff), static_cast<qsizetype>(size)); data.append(static_cast<const char*>(buff), static_cast<qsizetype>(size));
} }
if (status != ARCHIVE_EOF && status != ARCHIVE_OK) { if (status != ARCHIVE_EOF && status != ARCHIVE_OK) {
qWarning() << "libarchive read error: " << archive_error_string(m_archive.get()); qWarning() << "libarchive read error:" << archive_error_string(m_archive.get());
} }
if (outStatus) { if (outStatus) {
*outStatus = status; *outStatus = status;
@ -151,7 +151,7 @@ bool ArchiveReader::File::writeFile(archive* out, QString targetFileName, bool n
auto r = archive_write_finish_entry(out); auto r = archive_write_finish_entry(out);
if (r < ARCHIVE_OK) if (r < ARCHIVE_OK)
qCritical() << "Failed to finish writing entry:" << archive_error_string(out); qCritical() << "Failed to finish writing entry:" << archive_error_string(out);
return (r > ARCHIVE_WARN); return (r >= ARCHIVE_WARN);
} }
bool ArchiveReader::parse(std::function<bool(File*, bool&)> doStuff) bool ArchiveReader::parse(std::function<bool(File*, bool&)> doStuff)
@ -180,6 +180,7 @@ bool ArchiveReader::parse(std::function<bool(File*, bool&)> doStuff)
archive_read_close(a); archive_read_close(a);
return true; return true;
} }
bool ArchiveReader::parse(std::function<bool(File*)> doStuff) bool ArchiveReader::parse(std::function<bool(File*)> doStuff)
{ {
return parse([doStuff](File* f, bool&) { return doStuff(f); }); return parse([doStuff](File* f, bool&) { return doStuff(f); });

View file

@ -167,14 +167,14 @@ bool ArchiveWriter::addFile(const QString& fileName, const QString& fileDest)
} }
if (archive_write_header(m_archive, entry) != ARCHIVE_OK) { if (archive_write_header(m_archive, entry) != ARCHIVE_OK) {
qCritical() << "Failed to write header for: " << fileDest << "-" << archive_error_string(m_archive); qCritical() << "Failed to write header for:" << fileDest << "-" << archive_error_string(m_archive);
return false; return false;
} }
if (fileInfo.isFile() && !fileInfo.isSymLink()) { if (fileInfo.isFile() && !fileInfo.isSymLink()) {
QFile file(fileInfo.absoluteFilePath()); QFile file(fileInfo.absoluteFilePath());
if (!file.open(QIODevice::ReadOnly)) { if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Failed to open file: " << fileInfo.filePath(); qCritical() << "Failed to open file:" << fileInfo.filePath();
return false; return false;
} }
@ -185,12 +185,12 @@ bool ArchiveWriter::addFile(const QString& fileName, const QString& fileDest)
while (!file.atEnd()) { while (!file.atEnd()) {
auto bytesRead = file.read(buffer.data(), chunkSize); auto bytesRead = file.read(buffer.data(), chunkSize);
if (bytesRead < 0) { if (bytesRead < 0) {
qCritical() << "Read error in file: " << fileInfo.filePath(); qCritical() << "Read error in file:" << fileInfo.filePath();
return false; return false;
} }
if (archive_write_data(m_archive, buffer.constData(), bytesRead) < 0) { if (archive_write_data(m_archive, buffer.constData(), bytesRead) < 0) {
qCritical() << "Write error in archive for: " << fileDest; qCritical() << "Write error in archive for:" << fileDest;
return false; return false;
} }
} }
@ -216,12 +216,12 @@ bool ArchiveWriter::addFile(const QString& fileDest, const QByteArray& data)
archive_entry_set_size(entry, data.size()); archive_entry_set_size(entry, data.size());
if (archive_write_header(m_archive, entry) != ARCHIVE_OK) { if (archive_write_header(m_archive, entry) != ARCHIVE_OK) {
qCritical() << "Failed to write header for: " << fileDest << "-" << archive_error_string(m_archive); qCritical() << "Failed to write header for:" << fileDest << "-" << archive_error_string(m_archive);
return false; return false;
} }
if (archive_write_data(m_archive, data.constData(), data.size()) < 0) { if (archive_write_data(m_archive, data.constData(), data.size()) < 0) {
qCritical() << "Write error in archive for: " << fileDest << "-" << archive_error_string(m_archive); qCritical() << "Write error in archive for:" << fileDest << "-" << archive_error_string(m_archive);
return false; return false;
} }
return true; return true;

View file

@ -115,7 +115,7 @@ void FileLinkApp::joinServer(QString server)
qDebug() << ("The connection was closed by the peer. "); qDebug() << ("The connection was closed by the peer. ");
break; break;
default: default:
qDebug() << "The following error occurred: " << socket.errorString(); qDebug() << "The following error occurred:" << socket.errorString();
} }
}); });

View file

@ -40,6 +40,7 @@
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
#include <QMap> #include <QMap>
#include <QMimeData> #include <QMimeData>
#include <QPixmap>
#include <QSet> #include <QSet>
#include <QUrl> #include <QUrl>
#include "icons/IconUtils.h" #include "icons/IconUtils.h"
@ -146,8 +147,7 @@ void IconList::directoryChanged(const QString& path)
{ {
QDir newDir(path); QDir newDir(path);
if (m_dir.absolutePath() != newDir.absolutePath()) { if (m_dir.absolutePath() != newDir.absolutePath()) {
if (!path.startsWith(m_dir.absolutePath())) m_dir.setPath(path);
m_dir.setPath(path);
m_dir.refresh(); m_dir.refresh();
if (m_isWatching) if (m_isWatching)
stopWatching(); stopWatching();
@ -169,7 +169,7 @@ void IconList::directoryChanged(const QString& path)
QSet<QString> toAdd = newSet - currentSet; QSet<QString> toAdd = newSet - currentSet;
for (const QString& removedPath : toRemove) { for (const QString& removedPath : toRemove) {
qDebug() << "Removing icon " << removedPath; qDebug() << "Removing icon" << removedPath;
QFileInfo removedFile(removedPath); QFileInfo removedFile(removedPath);
QString relativePath = m_dir.relativeFilePath(removedFile.absoluteFilePath()); QString relativePath = m_dir.relativeFilePath(removedFile.absoluteFilePath());
QString key = QFileInfo(relativePath).completeBaseName(); QString key = QFileInfo(relativePath).completeBaseName();
@ -191,7 +191,7 @@ void IconList::directoryChanged(const QString& path)
} }
for (const QString& addedPath : toAdd) { for (const QString& addedPath : toAdd) {
qDebug() << "Adding icon " << addedPath; qDebug() << "Adding icon" << addedPath;
QFileInfo addfile(addedPath); QFileInfo addfile(addedPath);
QString relativePath = m_dir.relativeFilePath(addfile.absoluteFilePath()); QString relativePath = m_dir.relativeFilePath(addfile.absoluteFilePath());
@ -209,7 +209,7 @@ void IconList::directoryChanged(const QString& path)
void IconList::fileChanged(const QString& path) void IconList::fileChanged(const QString& path)
{ {
qDebug() << "Checking icon " << path; qDebug() << "Checking icon" << path;
QFileInfo checkfile(path); QFileInfo checkfile(path);
if (!checkfile.exists()) if (!checkfile.exists())
return; return;
@ -217,7 +217,13 @@ void IconList::fileChanged(const QString& path)
int idx = getIconIndex(key); int idx = getIconIndex(key);
if (idx == -1) if (idx == -1)
return; return;
QIcon icon(path); QIcon icon;
// special handling for jpg and jpeg to go through pixmap to keep the size constant
if (path.endsWith(".jpg") || path.endsWith(".jpeg")) {
icon.addPixmap(QPixmap(path));
} else {
icon.addFile(path);
}
if (icon.availableSizes().empty()) if (icon.availableSizes().empty())
return; return;
@ -240,9 +246,9 @@ void IconList::startWatching()
FS::ensureFolderPathExists(abs_path); FS::ensureFolderPathExists(abs_path);
m_isWatching = addPathRecursively(abs_path); m_isWatching = addPathRecursively(abs_path);
if (m_isWatching) { if (m_isWatching) {
qDebug() << "Started watching " << abs_path; qDebug() << "Started watching" << abs_path;
} else { } else {
qDebug() << "Failed to start watching " << abs_path; qDebug() << "Failed to start watching" << abs_path;
} }
} }
@ -395,7 +401,14 @@ bool IconList::addThemeIcon(const QString& key)
bool IconList::addIcon(const QString& key, const QString& name, const QString& path, const IconType type) bool IconList::addIcon(const QString& key, const QString& name, const QString& path, const IconType type)
{ {
// replace the icon even? is the input valid? // replace the icon even? is the input valid?
QIcon icon(path); QIcon icon;
// special handling for jpg and jpeg to go through pixmap to keep the size constant
if (path.endsWith(".jpg") || path.endsWith(".jpeg")) {
icon.addPixmap(QPixmap(path));
} else {
icon.addFile(path);
}
if (icon.isNull()) if (icon.isNull())
return false; return false;
auto iter = m_nameIndex.find(key); auto iter = m_nameIndex.find(key);

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

@ -483,7 +483,8 @@ QList<QString> JavaUtils::FindJavaPaths()
QString asdfDataDir = qEnvironmentVariable("ASDF_DATA_DIR", FS::PathCombine(home, ".asdf")); QString asdfDataDir = qEnvironmentVariable("ASDF_DATA_DIR", FS::PathCombine(home, ".asdf"));
scanJavaDirs(FS::PathCombine(asdfDataDir, "installs/java")); scanJavaDirs(FS::PathCombine(asdfDataDir, "installs/java"));
// javas downloaded by gradle (toolchains) // javas downloaded by gradle (toolchains)
scanJavaDirs(FS::PathCombine(home, ".gradle/jdks")); QString gradleUserHome = qEnvironmentVariable("GRADLE_USER_HOME", FS::PathCombine(home, ".gradle"));
scanJavaDirs(FS::PathCombine(gradleUserHome, "jdks"));
javas.append(getMinecraftJavaBundle()); javas.append(getMinecraftJavaBundle());
javas.append(getPrismJavaBundle()); javas.append(getPrismJavaBundle());

View file

@ -61,7 +61,7 @@ void ManifestDownloadTask::executeTask()
QJsonParseError parse_error{}; QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(*files, &parse_error); QJsonDocument doc = QJsonDocument::fromJson(*files, &parse_error);
if (parse_error.error != QJsonParseError::NoError) { if (parse_error.error != QJsonParseError::NoError) {
qWarning() << "Error while parsing JSON response at " << parse_error.offset << ". Reason: " << parse_error.errorString(); qWarning() << "Error while parsing JSON response at" << parse_error.offset << "reason:" << parse_error.errorString();
qWarning() << *files; qWarning() << *files;
emitFailed(parse_error.errorString()); emitFailed(parse_error.errorString());
return; return;

View file

@ -76,6 +76,7 @@ void LaunchTask::executeTask()
if (!m_steps.size()) { if (!m_steps.size()) {
state = LaunchTask::Finished; state = LaunchTask::Finished;
emitSucceeded(); emitSucceeded();
return;
} }
state = LaunchTask::Running; state = LaunchTask::Running;
onStepFinished(); onStepFinished();

View file

@ -87,6 +87,6 @@ void LookupServerAddress::resolve(const QString& address, quint16 port)
m_output->address = address; m_output->address = address;
m_output->port = port; m_output->port = port;
emitSucceeded();
m_dnsLookup->deleteLater(); m_dnsLookup->deleteLater();
emitSucceeded();
} }

View file

@ -42,7 +42,8 @@ class SecurityBookmarkFileAccess {
bool m_readOnly; bool m_readOnly;
NSURL* securityScopedBookmarkToNSURL(QByteArray& bookmark, bool& isStale); NSURL* securityScopedBookmarkToNSURL(QByteArray& bookmark, bool& isStale);
public:
public:
/// \param readOnly A boolean indicating whether the bookmark should be read-only. /// \param readOnly A boolean indicating whether the bookmark should be read-only.
SecurityBookmarkFileAccess(bool readOnly = false); SecurityBookmarkFileAccess(bool readOnly = false);
~SecurityBookmarkFileAccess(); ~SecurityBookmarkFileAccess();
@ -86,4 +87,4 @@ public:
bool isAccessingPath(const QString& path); bool isAccessingPath(const QString& path);
}; };
#endif //FILEACCESS_H #endif // FILEACCESS_H

View file

@ -479,6 +479,7 @@ void Component::clearUpdateAction()
QDebug operator<<(QDebug d, const Component& comp) QDebug operator<<(QDebug d, const Component& comp)
{ {
d << "Component(" << comp.m_uid << " : " << comp.m_cachedVersion << ")"; QDebugStateSaver saver(d);
d.nospace() << "Component(" << comp.m_uid << " : " << comp.m_cachedVersion << ")";
return d; return d;
} }

View file

@ -521,7 +521,7 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly)
// change a version of something that exists // change a version of something that exists
for (auto& change : toChange) { for (auto& change : toChange) {
// FIXME: this should not work directly with the component list // FIXME: this should not work directly with the component list
qCDebug(instanceProfileResolveC) << "Setting version of " << change.uid << "to" << change.equalsVersion; qCDebug(instanceProfileResolveC) << "Setting version of" << change.uid << "to" << change.equalsVersion;
auto component = componentIndex[change.uid]; auto component = componentIndex[change.uid];
component->setVersion(change.equalsVersion); component->setVersion(change.equalsVersion);
} }
@ -744,7 +744,7 @@ void ComponentUpdateTask::remoteLoadFailed(size_t taskIndex, const QString& msg)
qCWarning(instanceProfileResolveC) << "Got multiple results from remote load task" << taskIndex; qCWarning(instanceProfileResolveC) << "Got multiple results from remote load task" << taskIndex;
return; return;
} }
qCDebug(instanceProfileResolveC) << "Remote task" << taskIndex << "failed: " << msg; qCDebug(instanceProfileResolveC) << "Remote task" << taskIndex << "failed:" << msg;
d->remoteLoadSuccessful = false; d->remoteLoadSuccessful = false;
taskSlot.succeeded = false; taskSlot.succeeded = false;
taskSlot.finished = true; taskSlot.finished = true;
@ -773,8 +773,9 @@ void ComponentUpdateTask::checkIfAllFinished()
.arg(component->getName(), component->m_version)); .arg(component->getName(), component->m_version));
} }
} }
d->remoteLoadStatusList.clear();
auto allErrors = allErrorsList.join("\n"); auto allErrors = allErrorsList.join("\n");
emitFailed(tr("Component metadata update task failed while downloading from remote server:\n%1").arg(allErrors)); emitFailed(tr("Component metadata update task failed while downloading from remote server:\n%1").arg(allErrors));
d->remoteLoadStatusList.clear();
} }
} }

View file

@ -19,7 +19,6 @@
*/ */
#include "minecraft/Logging.h" #include "minecraft/Logging.h"
#include <qloggingcategory.h>
Q_LOGGING_CATEGORY(instanceProfileC, "launcher.instance.profile") Q_LOGGING_CATEGORY(instanceProfileC, "launcher.instance.profile")
Q_LOGGING_CATEGORY(instanceProfileResolveC, "launcher.instance.profile.resolve") Q_LOGGING_CATEGORY(instanceProfileResolveC, "launcher.instance.profile.resolve")

View file

@ -40,13 +40,6 @@
#include "BuildConfig.h" #include "BuildConfig.h"
#include "Json.h" #include "Json.h"
#include "QObjectPtr.h" #include "QObjectPtr.h"
#include "minecraft/launch/AutoInstallJava.h"
#include "minecraft/launch/CreateGameFolders.h"
#include "minecraft/launch/ExtractNatives.h"
#include "minecraft/launch/PrintInstanceInfo.h"
#include "minecraft/update/AssetUpdateTask.h"
#include "minecraft/update/FMLLibrariesTask.h"
#include "minecraft/update/LibrariesTask.h"
#include "settings/Setting.h" #include "settings/Setting.h"
#include "settings/SettingsObject.h" #include "settings/SettingsObject.h"
@ -63,13 +56,23 @@
#include "launch/steps/QuitAfterGameStop.h" #include "launch/steps/QuitAfterGameStop.h"
#include "launch/steps/TextPrint.h" #include "launch/steps/TextPrint.h"
#include "minecraft/launch/AutoInstallJava.h"
#include "minecraft/launch/ClaimAccount.h" #include "minecraft/launch/ClaimAccount.h"
#include "minecraft/launch/CreateGameFolders.h"
#include "minecraft/launch/EnsureOfflineLibraries.h"
#include "minecraft/launch/ExtractNatives.h"
#include "minecraft/launch/LauncherPartLaunch.h" #include "minecraft/launch/LauncherPartLaunch.h"
#include "minecraft/launch/ModMinecraftJar.h" #include "minecraft/launch/ModMinecraftJar.h"
#include "minecraft/launch/PrintInstanceInfo.h"
#include "minecraft/launch/ReconstructAssets.h" #include "minecraft/launch/ReconstructAssets.h"
#include "minecraft/launch/ScanModFolders.h" #include "minecraft/launch/ScanModFolders.h"
#include "minecraft/launch/VerifyJavaInstall.h" #include "minecraft/launch/VerifyJavaInstall.h"
#include "minecraft/update/AssetUpdateTask.h"
#include "minecraft/update/FMLLibrariesTask.h"
#include "minecraft/update/FoldersTask.h"
#include "minecraft/update/LibrariesTask.h"
#include "java/JavaUtils.h" #include "java/JavaUtils.h"
#include "icons/IconList.h" #include "icons/IconList.h"
@ -84,13 +87,13 @@
#include "AssetsUtils.h" #include "AssetsUtils.h"
#include "MinecraftLoadAndCheck.h" #include "MinecraftLoadAndCheck.h"
#include "PackProfile.h" #include "PackProfile.h"
#include "minecraft/update/FoldersTask.h"
#include "tools/BaseProfiler.h" #include "tools/BaseProfiler.h"
#include <QActionGroup> #include <QActionGroup>
#include <QMainWindow> #include <QMainWindow>
#include <QScreen> #include <QScreen>
#include <QStandardPaths>
#include <QWindow> #include <QWindow>
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
@ -483,7 +486,7 @@ QStringList MinecraftInstance::getNativeJars()
return nativeJars; return nativeJars;
} }
static QString replaceTokensIn(const QString &text, const QMap<QString, QString> &with) static QString replaceTokensIn(const QString& text, const QMap<QString, QString>& with)
{ {
// TODO: does this still work?? // TODO: does this still work??
QString result; QString result;
@ -505,7 +508,6 @@ static QString replaceTokensIn(const QString &text, const QMap<QString, QString>
return result; return result;
} }
QStringList MinecraftInstance::extraArguments() QStringList MinecraftInstance::extraArguments()
{ {
auto list = BaseInstance::extraArguments(); auto list = BaseInstance::extraArguments();
@ -520,7 +522,7 @@ QStringList MinecraftInstance::extraArguments()
if (!addn.isEmpty()) { if (!addn.isEmpty()) {
QMap<QString, QString> tokenMapping = makeProfileVarMapping(m_components->getProfile()); QMap<QString, QString> tokenMapping = makeProfileVarMapping(m_components->getProfile());
for (const QString &item : addn) { for (const QString& item : addn) {
list.append(replaceTokensIn(item, tokenMapping)); list.append(replaceTokensIn(item, tokenMapping));
} }
} }
@ -588,6 +590,16 @@ QStringList MinecraftInstance::javaArguments()
"minecraft.exe.heapdump"); "minecraft.exe.heapdump");
#endif #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 min = settings()->get("MinMemAlloc").toInt();
int max = settings()->get("MaxMemAlloc").toInt(); int max = settings()->get("MaxMemAlloc").toInt();
if (min < max) { if (min < max) {
@ -753,7 +765,6 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, Mine
} }
} }
QMap<QString, QString> tokenMapping = makeProfileVarMapping(profile); QMap<QString, QString> tokenMapping = makeProfileVarMapping(profile);
// yggdrasil! // yggdrasil!
@ -910,21 +921,13 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr
out << "Libraries:"; out << "Libraries:";
QStringList jars, nativeJars; QStringList jars, nativeJars;
profile->getLibraryFiles(runtimeContext(), jars, nativeJars, getLocalLibraryPath(), binRoot()); profile->getLibraryFiles(runtimeContext(), jars, nativeJars, getLocalLibraryPath(), binRoot());
auto printLibFile = [&out](const QString& path) {
QFileInfo info(path);
if (info.exists()) {
out << " " + path;
} else {
out << " " + path + " (missing)";
}
};
for (auto file : jars) { for (auto file : jars) {
printLibFile(file); out << " " + file;
} }
out << ""; out << "";
out << "Native libraries:"; out << "Native libraries:";
for (auto file : nativeJars) { for (auto file : nativeJars) {
printLibFile(file); out << " " + file;
} }
out << ""; out << "";
} }
@ -1163,6 +1166,8 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
for (auto t : createUpdateTask()) { for (auto t : createUpdateTask()) {
process->appendStep(makeShared<TaskStepWrapper>(pptr, t)); process->appendStep(makeShared<TaskStepWrapper>(pptr, t));
} }
} else {
process->appendStep(makeShared<EnsureOfflineLibraries>(pptr, this));
} }
// if there are any jar mods // if there are any jar mods

View file

@ -38,7 +38,6 @@
*/ */
#include <Version.h> #include <Version.h>
#include <qlogging.h>
#include <QCryptographicHash> #include <QCryptographicHash>
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
@ -168,6 +167,7 @@ static bool savePackProfile(const QString& filename, const ComponentContainer& c
} }
if (!outFile.commit()) { if (!outFile.commit()) {
qCCritical(instanceProfileC) << "Couldn't save" << outFile.fileName() << "because:" << outFile.errorString(); qCCritical(instanceProfileC) << "Couldn't save" << outFile.fileName() << "because:" << outFile.errorString();
return false;
} }
return true; return true;
} }
@ -230,9 +230,8 @@ static PackProfile::Result loadPackProfile(PackProfile* parent,
void PackProfile::saveNow() void PackProfile::saveNow()
{ {
if (saveIsScheduled()) { if (saveIsScheduled() && save_internal()) {
d->m_saveTimer.stop(); d->m_saveTimer.stop();
save_internal();
} }
} }
@ -280,12 +279,15 @@ QString PackProfile::patchFilePathForUid(const QString& uid) const
return patchesPattern().arg(uid); return patchesPattern().arg(uid);
} }
void PackProfile::save_internal() bool PackProfile::save_internal()
{ {
qDebug() << d->m_instance->name() << "|" << "Component list save performed now"; qDebug() << d->m_instance->name() << "|" << "Component list save performed now";
auto filename = componentsFilePath(); auto filename = componentsFilePath();
savePackProfile(filename, d->components); if (savePackProfile(filename, d->components)) {
d->dirty = false; d->dirty = false;
return true;
}
return false;
} }
PackProfile::Result PackProfile::load() PackProfile::Result PackProfile::load()
@ -364,7 +366,7 @@ void PackProfile::updateSucceeded()
void PackProfile::updateFailed(const QString& error) void PackProfile::updateFailed(const QString& error)
{ {
qCDebug(instanceProfileC) << d->m_instance->name() << "|" << "Component list update/resolve task failed " << "Reason:" << error; qCDebug(instanceProfileC) << d->m_instance->name() << "|" << "Component list update/resolve task failed. Reason:" << error;
d->m_updateTask.reset(); d->m_updateTask.reset();
invalidateLaunchProfile(); invalidateLaunchProfile();
} }
@ -952,7 +954,7 @@ std::shared_ptr<LaunchProfile> PackProfile::getProfile() const
} }
d->m_profile = profile; d->m_profile = profile;
} catch (const Exception& error) { } catch (const Exception& error) {
qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "Couldn't apply profile patches because: " << error.cause(); qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "Couldn't apply profile patches because:" << error.cause();
} }
} }
return d->m_profile; return d->m_profile;

View file

@ -175,7 +175,7 @@ class PackProfile : public QAbstractListModel {
QString patchesPattern() const; QString patchesPattern() const;
private slots: private slots:
void save_internal(); bool save_internal();
void updateSucceeded(); void updateSucceeded();
void updateFailed(const QString& error); void updateFailed(const QString& error);
void componentDataChanged(); void componentDataChanged();

View file

@ -55,7 +55,7 @@ bool readOverrideOrders(QString path, PatchOrder& order)
return false; return false;
} }
if (!orderFile.open(QFile::ReadOnly)) { if (!orderFile.open(QFile::ReadOnly)) {
qCritical() << "Couldn't open" << orderFile.fileName() << " for reading:" << orderFile.errorString(); qCritical() << "Couldn't open" << orderFile.fileName() << "for reading:" << orderFile.errorString();
qWarning() << "Ignoring overridden order"; qWarning() << "Ignoring overridden order";
return false; return false;
} }

View file

@ -405,7 +405,7 @@ void World::loadFromLevelDat(QByteArray data)
try { try {
valPtr = &levelData->at("Data"); valPtr = &levelData->at("Data");
} catch (const std::out_of_range& e) { } catch (const std::out_of_range& e) {
qWarning() << "Unable to read NBT tags from " << m_folderName << ":" << e.what(); qWarning().nospace() << "Unable to read NBT tags from " << m_folderName << ": " << e.what();
m_isValid = false; m_isValid = false;
return; return;
} }

View file

@ -87,7 +87,7 @@ class World {
QString m_iconFile; QString m_iconFile;
QDateTime m_levelDatTime; QDateTime m_levelDatTime;
QDateTime m_lastPlayed; QDateTime m_lastPlayed;
int64_t m_size; int64_t m_size = 0;
int64_t m_randomSeed = 0; int64_t m_randomSeed = 0;
GameType m_gameType; GameType m_gameType;
bool m_isValid = false; bool m_isValid = false;

View file

@ -36,7 +36,6 @@
#include "WorldList.h" #include "WorldList.h"
#include <FileSystem.h> #include <FileSystem.h>
#include <qmimedata.h>
#include <QDebug> #include <QDebug>
#include <QDirIterator> #include <QDirIterator>
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
@ -65,9 +64,9 @@ void WorldList::startWatching()
update(); update();
m_isWatching = m_watcher->addPath(m_dir.absolutePath()); m_isWatching = m_watcher->addPath(m_dir.absolutePath());
if (m_isWatching) { if (m_isWatching) {
qDebug() << "Started watching " << m_dir.absolutePath(); qDebug() << "Started watching" << m_dir.absolutePath();
} else { } else {
qDebug() << "Failed to start watching " << m_dir.absolutePath(); qDebug() << "Failed to start watching" << m_dir.absolutePath();
} }
} }
@ -78,9 +77,9 @@ void WorldList::stopWatching()
} }
m_isWatching = !m_watcher->removePath(m_dir.absolutePath()); m_isWatching = !m_watcher->removePath(m_dir.absolutePath());
if (!m_isWatching) { if (!m_isWatching) {
qDebug() << "Stopped watching " << m_dir.absolutePath(); qDebug() << "Stopped watching" << m_dir.absolutePath();
} else { } else {
qDebug() << "Failed to stop watching " << m_dir.absolutePath(); qDebug() << "Failed to stop watching" << m_dir.absolutePath();
} }
} }
@ -352,7 +351,7 @@ Qt::DropActions WorldList::supportedDropActions() const
void WorldList::installWorld(QFileInfo filename) void WorldList::installWorld(QFileInfo filename)
{ {
qDebug() << "installing: " << filename.absoluteFilePath(); qDebug() << "installing:" << filename.absoluteFilePath();
World w(filename); World w(filename);
if (!w.isValid()) { if (!w.isValid()) {
return; return;

View file

@ -574,7 +574,7 @@ void AccountList::fillQueue()
if (m_defaultAccount && m_defaultAccount->shouldRefresh()) { if (m_defaultAccount && m_defaultAccount->shouldRefresh()) {
auto idToRefresh = m_defaultAccount->internalId(); auto idToRefresh = m_defaultAccount->internalId();
m_refreshQueue.push_back(idToRefresh); m_refreshQueue.push_back(idToRefresh);
qDebug() << "AccountList: Queued default account with internal ID " << idToRefresh << " to refresh first"; qDebug() << "AccountList: Queued default account with internal ID" << idToRefresh << "to refresh first";
} }
for (int i = 0; i < count(); i++) { for (int i = 0; i < count(); i++) {
@ -598,7 +598,7 @@ void AccountList::requestRefresh(QString accountId)
m_refreshQueue.removeAt(index); m_refreshQueue.removeAt(index);
} }
m_refreshQueue.push_front(accountId); m_refreshQueue.push_front(accountId);
qDebug() << "AccountList: Pushed account with internal ID " << accountId << " to the front of the queue"; qDebug() << "AccountList: Pushed account with internal ID" << accountId << "to the front of the queue";
if (!isActive()) { if (!isActive()) {
tryNext(); tryNext();
} }
@ -610,7 +610,7 @@ void AccountList::queueRefresh(QString accountId)
return; return;
} }
m_refreshQueue.push_back(accountId); m_refreshQueue.push_back(accountId);
qDebug() << "AccountList: Queued account with internal ID " << accountId << " to refresh"; qDebug() << "AccountList: Queued account with internal ID" << accountId << "to refresh";
} }
void AccountList::tryNext() void AccountList::tryNext()
@ -626,13 +626,13 @@ void AccountList::tryNext()
connect(m_currentTask.get(), &Task::succeeded, this, &AccountList::authSucceeded); connect(m_currentTask.get(), &Task::succeeded, this, &AccountList::authSucceeded);
connect(m_currentTask.get(), &Task::failed, this, &AccountList::authFailed); connect(m_currentTask.get(), &Task::failed, this, &AccountList::authFailed);
m_currentTask->start(); m_currentTask->start();
qDebug() << "RefreshSchedule: Processing account " << account->accountDisplayString() << " with internal ID " qDebug() << "RefreshSchedule: Processing account" << account->accountDisplayString() << "with internal ID"
<< accountId; << accountId;
return; return;
} }
} }
} }
qDebug() << "RefreshSchedule: Account with with internal ID " << accountId << " not found."; qDebug() << "RefreshSchedule: Account with internal ID" << accountId << "not found.";
} }
// if we get here, no account needed refreshing. Schedule refresh in an hour. // if we get here, no account needed refreshing. Schedule refresh in an hour.
m_refreshTimer->start(1000 * 3600); m_refreshTimer->start(1000 * 3600);
@ -647,7 +647,7 @@ void AccountList::authSucceeded()
void AccountList::authFailed(QString reason) void AccountList::authFailed(QString reason)
{ {
qDebug() << "RefreshSchedule: Background account refresh failed: " << reason; qDebug() << "RefreshSchedule: Background account refresh failed:" << reason;
m_currentTask.reset(); m_currentTask.reset();
m_nextTimer->start(1000 * 20); m_nextTimer->start(1000 * 20);
} }
@ -669,7 +669,7 @@ void AccountList::beginActivity()
void AccountList::endActivity() void AccountList::endActivity()
{ {
if (m_activityCount == 0) { if (m_activityCount == 0) {
qWarning() << m_name << " - Activity count would become below zero"; qWarning() << "Activity count would become below zero";
return; return;
} }
bool deactivating = m_activityCount == 1; bool deactivating = m_activityCount == 1;

View file

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

View file

@ -69,6 +69,7 @@ void AuthFlow::nextStep()
} }
m_currentStep = m_steps.front(); m_currentStep = m_steps.front();
qDebug() << "AuthFlow:" << m_currentStep->describe(); qDebug() << "AuthFlow:" << m_currentStep->describe();
setStatus(m_currentStep->describe());
m_steps.pop_front(); m_steps.pop_front();
connect(m_currentStep.get(), &AuthStep::finished, this, &AuthFlow::stepFinished); connect(m_currentStep.get(), &AuthStep::finished, this, &AuthFlow::stepFinished);
@ -92,7 +93,9 @@ bool AuthFlow::changeState(AccountTaskState newState, QString reason)
return true; return true;
} }
case AccountTaskState::STATE_WORKING: { case AccountTaskState::STATE_WORKING: {
setStatus(m_currentStep ? m_currentStep->describe() : tr("Working...")); if (!m_currentStep) {
setStatus(tr("Preparing to log in..."));
}
m_data->accountState = AccountState::Working; m_data->accountState = AccountState::Working;
return true; return true;
} }
@ -148,8 +151,8 @@ bool AuthFlow::changeState(AccountTaskState newState, QString reason)
} }
bool AuthFlow::abort() bool AuthFlow::abort()
{ {
emitAborted();
if (m_currentStep) if (m_currentStep)
m_currentStep->abort(); m_currentStep->abort();
emitAborted();
return true; return true;
} }

View file

@ -75,7 +75,7 @@ bool getBool(QJsonValue value, bool& out)
"Message":"", "Message":"",
"Redirect":"https://start.ui.xboxlive.com/AddChildToFamily" "Redirect":"https://start.ui.xboxlive.com/AddChildToFamily"
} }
// 2148916233 = missing XBox account // 2148916233 = missing Xbox account
// 2148916238 = child account not linked to a family // 2148916238 = child account not linked to a family
*/ */
@ -86,7 +86,7 @@ bool parseXTokenResponse(QByteArray& data, Token& output, QString name)
QJsonParseError jsonError; QJsonParseError jsonError;
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
if (jsonError.error) { if (jsonError.error) {
qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString(); qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON:" << jsonError.errorString();
return false; return false;
} }
@ -123,7 +123,7 @@ bool parseXTokenResponse(QByteArray& data, Token& output, QString name)
for (auto iter = obj_.begin(); iter != obj_.end(); iter++) { for (auto iter = obj_.begin(); iter != obj_.end(); iter++) {
QString claim; QString claim;
if (!getString(obj_.value(iter.key()), claim)) { if (!getString(obj_.value(iter.key()), claim)) {
qWarning() << "display claim " << iter.key() << " is not a string..."; qWarning() << "display claim" << iter.key() << "is not a string...";
return false; return false;
} }
output.extra[iter.key()] = claim; output.extra[iter.key()] = claim;
@ -148,7 +148,7 @@ bool parseMinecraftProfile(QByteArray& data, MinecraftProfile& output)
QJsonParseError jsonError; QJsonParseError jsonError;
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
if (jsonError.error) { if (jsonError.error) {
qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString(); qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON:" << jsonError.errorString();
return false; return false;
} }
@ -290,7 +290,7 @@ bool parseMinecraftProfileMojang(QByteArray& data, MinecraftProfile& output)
QJsonParseError jsonError; QJsonParseError jsonError;
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
if (jsonError.error) { if (jsonError.error) {
qWarning() << "Failed to parse response as JSON: " << jsonError.errorString(); qWarning() << "Failed to parse response as JSON:" << jsonError.errorString();
return false; return false;
} }
@ -331,7 +331,7 @@ bool parseMinecraftProfileMojang(QByteArray& data, MinecraftProfile& output)
doc = QJsonDocument::fromJson(texturePayload, &jsonError); doc = QJsonDocument::fromJson(texturePayload, &jsonError);
if (jsonError.error) { if (jsonError.error) {
qWarning() << "Failed to parse response as JSON: " << jsonError.errorString(); qWarning() << "Failed to parse response as JSON:" << jsonError.errorString();
return false; return false;
} }
@ -400,7 +400,7 @@ bool parseMinecraftEntitlements(QByteArray& data, MinecraftEntitlement& output)
QJsonParseError jsonError; QJsonParseError jsonError;
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
if (jsonError.error) { if (jsonError.error) {
qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString(); qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON:" << jsonError.errorString();
return false; return false;
} }
@ -463,7 +463,7 @@ bool parseMojangResponse(QByteArray& data, Token& output)
qCDebug(authCredentials()) << data; qCDebug(authCredentials()) << data;
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
if (jsonError.error) { if (jsonError.error) {
qWarning() << "Failed to parse response from api.minecraftservices.com/launcher/login as JSON: " << jsonError.errorString(); qWarning() << "Failed to parse response from api.minecraftservices.com/launcher/login as JSON:" << jsonError.errorString();
return false; return false;
} }

View file

@ -34,6 +34,7 @@ void EntitlementsStep::perform()
m_response.reset(new QByteArray()); m_response.reset(new QByteArray());
m_request = Net::Download::makeByteArray(url, m_response); m_request = Net::Download::makeByteArray(url, m_response);
m_request->addHeaderProxy(new Net::RawHeaderProxy(headers)); m_request->addHeaderProxy(new Net::RawHeaderProxy(headers));
m_request->enableAutoRetry(true);
m_task.reset(new NetJob("EntitlementsStep", APPLICATION->network())); m_task.reset(new NetJob("EntitlementsStep", APPLICATION->network()));
m_task->setAskRetry(false); m_task->setAskRetry(false);

View file

@ -18,6 +18,7 @@ void GetSkinStep::perform()
m_response.reset(new QByteArray()); m_response.reset(new QByteArray());
m_request = Net::Download::makeByteArray(url, m_response); m_request = Net::Download::makeByteArray(url, m_response);
m_request->enableAutoRetry(true);
m_task.reset(new NetJob("GetSkinStep", APPLICATION->network())); m_task.reset(new NetJob("GetSkinStep", APPLICATION->network()));
m_task->setAskRetry(false); m_task->setAskRetry(false);

View file

@ -14,7 +14,7 @@ LauncherLoginStep::LauncherLoginStep(AccountData* data) : AuthStep(data) {}
QString LauncherLoginStep::describe() QString LauncherLoginStep::describe()
{ {
return tr("Accessing Mojang services."); return tr("Fetching Minecraft access token");
} }
void LauncherLoginStep::perform() void LauncherLoginStep::perform()
@ -39,6 +39,7 @@ void LauncherLoginStep::perform()
m_response.reset(new QByteArray()); m_response.reset(new QByteArray());
m_request = Net::Upload::makeByteArray(url, m_response, requestBody.toUtf8()); m_request = Net::Upload::makeByteArray(url, m_response, requestBody.toUtf8());
m_request->addHeaderProxy(new Net::RawHeaderProxy(headers)); m_request->addHeaderProxy(new Net::RawHeaderProxy(headers));
m_request->enableAutoRetry(true);
m_task.reset(new NetJob("LauncherLoginStep", APPLICATION->network())); m_task.reset(new NetJob("LauncherLoginStep", APPLICATION->network()));
m_task->setAskRetry(false); m_task->setAskRetry(false);
@ -69,5 +70,5 @@ void LauncherLoginStep::onRequestDone()
emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to parse the Minecraft access token response.")); emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to parse the Minecraft access token response."));
return; return;
} }
emit finished(AccountTaskState::STATE_WORKING, tr("")); emit finished(AccountTaskState::STATE_WORKING, tr("Got Minecraft access token"));
} }

View file

@ -69,6 +69,7 @@ void MSADeviceCodeStep::perform()
m_response.reset(new QByteArray()); m_response.reset(new QByteArray());
m_request = Net::Upload::makeByteArray(url, m_response, payload); m_request = Net::Upload::makeByteArray(url, m_response, payload);
m_request->addHeaderProxy(new Net::RawHeaderProxy(headers)); m_request->addHeaderProxy(new Net::RawHeaderProxy(headers));
m_request->enableAutoRetry(true);
m_task.reset(new NetJob("MSADeviceCodeStep", APPLICATION->network())); m_task.reset(new NetJob("MSADeviceCodeStep", APPLICATION->network()));
m_task->setAskRetry(false); m_task->setAskRetry(false);
@ -120,12 +121,13 @@ void MSADeviceCodeStep::deviceAuthorizationFinished()
return; return;
} }
if (!m_request->wasSuccessful() || m_request->error() != QNetworkReply::NoError) { if (!m_request->wasSuccessful() || m_request->error() != QNetworkReply::NoError) {
qWarning() << "Device authorization failed:" << *m_response;
emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Failed to retrieve device authorization")); emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Failed to retrieve device authorization"));
qDebug() << *m_response;
return; return;
} }
if (rsp.device_code.isEmpty() || rsp.user_code.isEmpty() || rsp.verification_uri.isEmpty() || rsp.expires_in == 0) { if (rsp.device_code.isEmpty() || rsp.user_code.isEmpty() || rsp.verification_uri.isEmpty() || rsp.expires_in == 0) {
qWarning() << "Device authorization failed: required fields missing";
emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Device authorization failed: required fields missing")); emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Device authorization failed: required fields missing"));
return; return;
} }
@ -272,5 +274,5 @@ void MSADeviceCodeStep::authenticationFinished()
m_data->msaToken.extra = rsp.extra; m_data->msaToken.extra = rsp.extra;
m_data->msaToken.refresh_token = rsp.refresh_token; m_data->msaToken.refresh_token = rsp.refresh_token;
m_data->msaToken.token = rsp.access_token; m_data->msaToken.token = rsp.access_token;
emit finished(AccountTaskState::STATE_WORKING, tr("Got")); emit finished(AccountTaskState::STATE_WORKING, tr("Got MSA token"));
} }

View file

@ -56,13 +56,14 @@ bool isSchemeHandlerRegistered()
process.waitForFinished(); process.waitForFinished();
QString output = process.readAllStandardOutput().trimmed(); QString output = process.readAllStandardOutput().trimmed();
return output.contains(BuildConfig.LAUNCHER_APP_BINARY_NAME); return output.contains(APPLICATION->desktopFileName());
#elif defined(Q_OS_WIN) #elif defined(Q_OS_WIN)
QString regPath = QString("HKEY_CURRENT_USER\\Software\\Classes\\%1").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); QString regPath = QString("HKEY_CURRENT_USER\\Software\\Classes\\%1").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
QSettings settings(regPath, QSettings::NativeFormat); QSettings settings(regPath, QSettings::NativeFormat);
return settings.contains("shell/open/command/."); const QString registeredRunCommand = settings.value("shell/open/command/.").toString().replace("\\", "/");
return registeredRunCommand.contains(QCoreApplication::applicationFilePath());
#endif #endif
return true; return true;
} }
@ -80,6 +81,33 @@ class CustomOAuthOobReplyHandler : public QOAuthOobReplyHandler {
disconnect(APPLICATION, &Application::oauthReplyRecieved, this, &QOAuthOobReplyHandler::callbackReceived); disconnect(APPLICATION, &Application::oauthReplyRecieved, this, &QOAuthOobReplyHandler::callbackReceived);
} }
QString callback() const override { return BuildConfig.LAUNCHER_APP_BINARY_NAME + "://oauth/microsoft"; } QString callback() const override { return BuildConfig.LAUNCHER_APP_BINARY_NAME + "://oauth/microsoft"; }
protected:
void networkReplyFinished(QNetworkReply* reply) override
{
if (reply->error() != QNetworkReply::NoError) {
qWarning() << "OAuth2 request failed:" << reply->readAll();
}
QOAuthOobReplyHandler::networkReplyFinished(reply);
}
};
class LoggingOAuthHttpServerReplyHandler final : public QOAuthHttpServerReplyHandler {
Q_OBJECT
public:
explicit LoggingOAuthHttpServerReplyHandler(QObject* parent = nullptr) : QOAuthHttpServerReplyHandler(parent) {}
protected:
void networkReplyFinished(QNetworkReply* reply) override
{
if (reply->error() != QNetworkReply::NoError) {
qWarning() << "OAuth2 request failed:" << reply->readAll();
}
QOAuthHttpServerReplyHandler::networkReplyFinished(reply);
}
}; };
MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(silent) MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(silent)
@ -88,7 +116,7 @@ MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(sile
if (QCoreApplication::applicationFilePath().startsWith("/tmp/.mount_") || APPLICATION->isPortable() || !isSchemeHandlerRegistered()) if (QCoreApplication::applicationFilePath().startsWith("/tmp/.mount_") || APPLICATION->isPortable() || !isSchemeHandlerRegistered())
{ {
auto replyHandler = new QOAuthHttpServerReplyHandler(this); auto replyHandler = new LoggingOAuthHttpServerReplyHandler(this);
replyHandler->setCallbackText(QString(R"XXX( replyHandler->setCallbackText(QString(R"XXX(
<noscript> <noscript>
<meta http-equiv="Refresh" content="0; URL=%1" /> <meta http-equiv="Refresh" content="0; URL=%1" />
@ -116,7 +144,7 @@ MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(sile
m_data->msaToken.extra = m_oauth2.extraTokens(); m_data->msaToken.extra = m_oauth2.extraTokens();
m_data->msaToken.refresh_token = m_oauth2.refreshToken(); m_data->msaToken.refresh_token = m_oauth2.refreshToken();
m_data->msaToken.token = m_oauth2.token(); m_data->msaToken.token = m_oauth2.token();
emit finished(AccountTaskState::STATE_WORKING, tr("Got ")); emit finished(AccountTaskState::STATE_WORKING, tr("Got MSA token"));
}); });
connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, this, &MSAStep::authorizeWithBrowser); connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, this, &MSAStep::authorizeWithBrowser);
connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::requestFailed, this, [this, silent](const QAbstractOAuth2::Error err) { connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::requestFailed, this, [this, silent](const QAbstractOAuth2::Error err) {

View file

@ -24,6 +24,7 @@ void MinecraftProfileStep::perform()
m_response.reset(new QByteArray()); m_response.reset(new QByteArray());
m_request = Net::Download::makeByteArray(url, m_response); m_request = Net::Download::makeByteArray(url, m_response);
m_request->addHeaderProxy(new Net::RawHeaderProxy(headers)); m_request->addHeaderProxy(new Net::RawHeaderProxy(headers));
m_request->enableAutoRetry(true);
m_task.reset(new NetJob("MinecraftProfileStep", APPLICATION->network())); m_task.reset(new NetJob("MinecraftProfileStep", APPLICATION->network()));
m_task->setAskRetry(false); m_task->setAskRetry(false);
@ -44,9 +45,9 @@ void MinecraftProfileStep::onRequestDone()
} }
if (m_request->error() != QNetworkReply::NoError) { if (m_request->error() != QNetworkReply::NoError) {
qWarning() << "Error getting profile:"; qWarning() << "Error getting profile:";
qWarning() << " HTTP Status: " << m_request->replyStatusCode(); qWarning() << " HTTP Status :" << m_request->replyStatusCode();
qWarning() << " Internal error no.: " << m_request->error(); qWarning() << " Internal error no.:" << m_request->error();
qWarning() << " Error string: " << m_request->errorString(); qWarning() << " Error string :" << m_request->errorString();
qWarning() << " Response:"; qWarning() << " Response:";
qWarning() << QString::fromUtf8(*m_response); qWarning() << QString::fromUtf8(*m_response);
@ -66,5 +67,5 @@ void MinecraftProfileStep::onRequestDone()
return; return;
} }
emit finished(AccountTaskState::STATE_WORKING, tr("Minecraft Java profile acquisition succeeded.")); emit finished(AccountTaskState::STATE_WORKING, tr("Got Minecraft profile"));
} }

View file

@ -44,6 +44,7 @@ void XboxAuthorizationStep::perform()
m_response.reset(new QByteArray()); m_response.reset(new QByteArray());
m_request = Net::Upload::makeByteArray(url, m_response, xbox_auth_data.toUtf8()); m_request = Net::Upload::makeByteArray(url, m_response, xbox_auth_data.toUtf8());
m_request->addHeaderProxy(new Net::RawHeaderProxy(headers)); m_request->addHeaderProxy(new Net::RawHeaderProxy(headers));
m_request->enableAutoRetry(true);
m_task.reset(new NetJob("XboxAuthorizationStep", APPLICATION->network())); m_task.reset(new NetJob("XboxAuthorizationStep", APPLICATION->network()));
m_task->setAskRetry(false); m_task->setAskRetry(false);
@ -52,7 +53,7 @@ void XboxAuthorizationStep::perform()
connect(m_task.get(), &Task::finished, this, &XboxAuthorizationStep::onRequestDone); connect(m_task.get(), &Task::finished, this, &XboxAuthorizationStep::onRequestDone);
m_task->start(); m_task->start();
qDebug() << "Getting authorization token for " << m_relyingParty; qDebug() << "Getting authorization token for" << m_relyingParty;
} }
void XboxAuthorizationStep::onRequestDone() void XboxAuthorizationStep::onRequestDone()
@ -99,7 +100,7 @@ bool XboxAuthorizationStep::processSTSError()
QJsonParseError jsonError; QJsonParseError jsonError;
QJsonDocument doc = QJsonDocument::fromJson(*m_response, &jsonError); QJsonDocument doc = QJsonDocument::fromJson(*m_response, &jsonError);
if (jsonError.error) { if (jsonError.error) {
qWarning() << "Cannot parse error XSTS response as JSON: " << jsonError.errorString(); qWarning() << "Cannot parse error XSTS response as JSON:" << jsonError.errorString();
emit finished(AccountTaskState::STATE_FAILED_SOFT, emit finished(AccountTaskState::STATE_FAILED_SOFT,
tr("Cannot parse %1 authorization error response as JSON: %2").arg(m_authorizationKind, jsonError.errorString())); tr("Cannot parse %1 authorization error response as JSON: %2").arg(m_authorizationKind, jsonError.errorString()));
return true; return true;
@ -115,13 +116,13 @@ bool XboxAuthorizationStep::processSTSError()
switch (errorCode) { switch (errorCode) {
case 2148916233: { case 2148916233: {
emit finished(AccountTaskState::STATE_FAILED_SOFT, emit finished(AccountTaskState::STATE_FAILED_SOFT,
tr("This Microsoft account does not have an XBox Live profile. Buy the game on %1 first.") tr("This Microsoft account does not have an Xbox Live profile. Buy the game on %1 first.")
.arg("<a href=\"https://www.minecraft.net/en-us/store/minecraft-java-edition\">minecraft.net</a>")); .arg("<a href=\"https://www.minecraft.net/en-us/store/minecraft-java-edition\">minecraft.net</a>"));
return true; return true;
} }
case 2148916235: { case 2148916235: {
// NOTE: this is the Grulovia error // NOTE: this is the Grulovia error
emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XBox Live is not available in your country. You've been blocked.")); emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Xbox Live is not available in your country. You've been blocked."));
return true; return true;
} }
case 2148916238: { case 2148916238: {

View file

@ -60,7 +60,7 @@ void XboxProfileStep::onRequestDone()
return; return;
} }
qCDebug(authCredentials()) << "XBox profile: " << *m_response; qCDebug(authCredentials()) << "Xbox profile:" << *m_response;
emit finished(AccountTaskState::STATE_WORKING, tr("Got Xbox profile")); emit finished(AccountTaskState::STATE_WORKING, tr("Got Xbox profile"));
} }

View file

@ -40,6 +40,7 @@ void XboxUserStep::perform()
m_response.reset(new QByteArray()); m_response.reset(new QByteArray());
m_request = Net::Upload::makeByteArray(url, m_response, xbox_auth_data.toUtf8()); m_request = Net::Upload::makeByteArray(url, m_response, xbox_auth_data.toUtf8());
m_request->addHeaderProxy(new Net::RawHeaderProxy(headers)); m_request->addHeaderProxy(new Net::RawHeaderProxy(headers));
m_request->enableAutoRetry(true);
m_task.reset(new NetJob("XboxUserStep", APPLICATION->network())); m_task.reset(new NetJob("XboxUserStep", APPLICATION->network()));
m_task->setAskRetry(false); m_task->setAskRetry(false);
@ -48,7 +49,7 @@ void XboxUserStep::perform()
connect(m_task.get(), &Task::finished, this, &XboxUserStep::onRequestDone); connect(m_task.get(), &Task::finished, this, &XboxUserStep::onRequestDone);
m_task->start(); m_task->start();
qDebug() << "First layer of XBox auth ... commencing."; qDebug() << "First layer of Xbox auth ... commencing.";
} }
void XboxUserStep::onRequestDone() void XboxUserStep::onRequestDone()
@ -56,9 +57,9 @@ void XboxUserStep::onRequestDone()
if (m_request->error() != QNetworkReply::NoError) { if (m_request->error() != QNetworkReply::NoError) {
qWarning() << "Reply error:" << m_request->error(); qWarning() << "Reply error:" << m_request->error();
if (Net::isApplicationError(m_request->error())) { if (Net::isApplicationError(m_request->error())) {
emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XBox user authentication failed: %1").arg(m_request->errorString())); emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Xbox user authentication failed: %1").arg(m_request->errorString()));
} else { } else {
emit finished(AccountTaskState::STATE_OFFLINE, tr("XBox user authentication failed: %1").arg(m_request->errorString())); emit finished(AccountTaskState::STATE_OFFLINE, tr("Xbox user authentication failed: %1").arg(m_request->errorString()));
} }
return; return;
} }
@ -66,7 +67,7 @@ void XboxUserStep::onRequestDone()
Token temp; Token temp;
if (!Parsers::parseXTokenResponse(*m_response, temp, "UToken")) { if (!Parsers::parseXTokenResponse(*m_response, temp, "UToken")) {
qWarning() << "Could not parse user authentication response..."; qWarning() << "Could not parse user authentication response...";
emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XBox user authentication response could not be understood.")); emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Xbox user authentication response could not be understood."));
return; return;
} }
m_data->userToken = temp; m_data->userToken = temp;

View file

@ -175,8 +175,8 @@ void AutoInstallJava::downloadJava(Meta::Version::Ptr version, QString javaName)
m_current_task = makeShared<Java::ArchiveDownloadTask>(java->url, final_path, java->checksumType, java->checksumHash); m_current_task = makeShared<Java::ArchiveDownloadTask>(java->url, final_path, java->checksumType, java->checksumHash);
break; break;
case Java::DownloadType::Unknown: case Java::DownloadType::Unknown:
emitFailed(tr("Could not determine Java download type!"));
deletePath(); deletePath();
emitFailed(tr("Could not determine Java download type!"));
return; return;
} }
#if defined(Q_OS_MACOS) #if defined(Q_OS_MACOS)

View file

@ -0,0 +1,42 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2026 Octol1ttle <l1ttleofficial@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "EnsureOfflineLibraries.h"
#include "minecraft/PackProfile.h"
EnsureOfflineLibraries::EnsureOfflineLibraries(LaunchTask* parent, MinecraftInstance* instance) : LaunchStep(parent), m_instance(instance)
{}
void EnsureOfflineLibraries::executeTask()
{
const auto profile = m_instance->getPackProfile()->getProfile();
QStringList allJars;
profile->getLibraryFiles(m_instance->runtimeContext(), allJars, allJars, m_instance->getLocalLibraryPath(), m_instance->binRoot());
for (const auto& jar : allJars) {
if (!QFileInfo::exists(jar)) {
emit logLine(tr("This instance cannot be launched because some libraries are missing or have not been downloaded yet. Please "
"try again in online mode with a working Internet connection"),
MessageLevel::Fatal);
emitFailed("Required libraries are missing");
return;
}
}
emitSucceeded();
}

View file

@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2026 Octol1ttle <l1ttleofficial@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "launch/LaunchStep.h"
#include "minecraft/MinecraftInstance.h"
class EnsureOfflineLibraries : public LaunchStep {
Q_OBJECT
public:
explicit EnsureOfflineLibraries(LaunchTask* parent, MinecraftInstance* instance);
~EnsureOfflineLibraries() override = default;
void executeTask() override;
bool canAbort() const override { return false; }
private:
MinecraftInstance* m_instance;
};

View file

@ -76,6 +76,7 @@ void ExtractNatives::executeTask()
const char* reason = QT_TR_NOOP("Couldn't extract native jar '%1' to destination '%2'"); const char* reason = QT_TR_NOOP("Couldn't extract native jar '%1' to destination '%2'");
emit logLine(QString(reason).arg(source, outputPath), MessageLevel::Fatal); emit logLine(QString(reason).arg(source, outputPath), MessageLevel::Fatal);
emitFailed(tr(reason).arg(source, outputPath)); emitFailed(tr(reason).arg(source, outputPath));
return;
} }
} }
emitSucceeded(); emitSucceeded();

View file

@ -55,14 +55,14 @@ LauncherPartLaunch::LauncherPartLaunch(LaunchTask* parent)
if (parent->instance()->settings()->get("CloseAfterLaunch").toBool()) { if (parent->instance()->settings()->get("CloseAfterLaunch").toBool()) {
static const QRegularExpression s_settingUser(".*Setting user.+", QRegularExpression::CaseInsensitiveOption); static const QRegularExpression s_settingUser(".*Setting user.+", QRegularExpression::CaseInsensitiveOption);
std::shared_ptr<QMetaObject::Connection> connection{ new QMetaObject::Connection }; std::shared_ptr<QMetaObject::Connection> connection{ new QMetaObject::Connection };
*connection = connect(&m_process, &LoggedProcess::log, this, *connection =
[connection](const QStringList& lines, [[maybe_unused]] MessageLevel level) { connect(&m_process, &LoggedProcess::log, this, [connection](const QStringList& lines, [[maybe_unused]] MessageLevel level) {
qDebug() << lines; qDebug() << lines;
if (lines.filter(s_settingUser).length() != 0) { if (lines.filter(s_settingUser).length() != 0) {
APPLICATION->closeAllWindows(); APPLICATION->closeAllWindows();
disconnect(*connection); disconnect(*connection);
} }
}); });
} }
connect(&m_process, &LoggedProcess::log, this, &LauncherPartLaunch::logLines); connect(&m_process, &LoggedProcess::log, this, &LauncherPartLaunch::logLines);

View file

@ -51,11 +51,13 @@ void ModMinecraftJar::executeTask()
// nuke obsolete stripped jar(s) if needed // nuke obsolete stripped jar(s) if needed
if (!FS::ensureFolderPathExists(m_inst->binRoot())) { if (!FS::ensureFolderPathExists(m_inst->binRoot())) {
emitFailed(tr("Couldn't create the bin folder for Minecraft.jar")); emitFailed(tr("Couldn't create the bin folder for Minecraft.jar"));
return;
} }
auto finalJarPath = QDir(m_inst->binRoot()).absoluteFilePath("minecraft.jar"); auto finalJarPath = QDir(m_inst->binRoot()).absoluteFilePath("minecraft.jar");
if (!removeJar()) { if (!removeJar()) {
emitFailed(tr("Couldn't remove stale jar file: %1").arg(finalJarPath)); emitFailed(tr("Couldn't remove stale jar file: %1").arg(finalJarPath));
return;
} }
// create temporary modded jar, if needed // create temporary modded jar, if needed

View file

@ -36,8 +36,6 @@
*/ */
#include "DataPackFolderModel.h" #include "DataPackFolderModel.h"
#include <qnamespace.h>
#include <qsize.h>
#include <QIcon> #include <QIcon>
#include <QStyle> #include <QStyle>

View file

@ -36,7 +36,6 @@
*/ */
#include "Mod.h" #include "Mod.h"
#include <qpixmap.h>
#include <QDir> #include <QDir>
#include <QRegularExpression> #include <QRegularExpression>

View file

@ -23,6 +23,7 @@
#include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameAPI.h"
#include "modplatform/flame/FlameModIndex.h" #include "modplatform/flame/FlameModIndex.h"
#include "settings/Setting.h" #include "settings/Setting.h"
#include "tasks/SequentialTask.h"
#include "tasks/Task.h" #include "tasks/Task.h"
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
@ -37,9 +38,12 @@ ResourceFolderModel::ResourceFolderModel(const QDir& dir, BaseInstance* instance
m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &ResourceFolderModel::directoryChanged); connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &ResourceFolderModel::directoryChanged);
connect(&m_helper_thread_task, &ConcurrentTask::finished, this, [this] { m_helper_thread_task.clear(); }); connect(&m_resourceResolver, &ConcurrentTask::finished, this, [this] {
m_resourceResolver.clear();
m_resourceResolverRunning = false;
});
if (APPLICATION_DYN) { // in tests the application macro doesn't work if (APPLICATION_DYN) { // in tests the application macro doesn't work
m_helper_thread_task.setMaxConcurrent(APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()); m_resourceResolver.setMaxConcurrent(APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt());
} }
} }
@ -60,9 +64,9 @@ bool ResourceFolderModel::startWatching(const QStringList& paths)
auto couldnt_be_watched = m_watcher.addPaths(paths); auto couldnt_be_watched = m_watcher.addPaths(paths);
for (auto path : paths) { for (auto path : paths) {
if (couldnt_be_watched.contains(path)) if (couldnt_be_watched.contains(path))
qDebug() << "Failed to start watching " << path; qDebug() << "Failed to start watching" << path;
else else
qDebug() << "Started watching " << path; qDebug() << "Started watching" << path;
} }
update(); update();
@ -79,9 +83,9 @@ bool ResourceFolderModel::stopWatching(const QStringList& paths)
auto couldnt_be_stopped = m_watcher.removePaths(paths); auto couldnt_be_stopped = m_watcher.removePaths(paths);
for (auto path : paths) { for (auto path : paths) {
if (couldnt_be_stopped.contains(path)) if (couldnt_be_stopped.contains(path))
qDebug() << "Failed to stop watching " << path; qDebug() << "Failed to stop watching" << path;
else else
qDebug() << "Stopped watching " << path; qDebug() << "Stopped watching" << path;
} }
m_is_watching = !m_is_watching; m_is_watching = !m_is_watching;
@ -98,7 +102,7 @@ bool ResourceFolderModel::installResource(QString original_path)
qWarning() << "Caught attempt to install non-existing file or file-like object:" << original_path; qWarning() << "Caught attempt to install non-existing file or file-like object:" << original_path;
return false; return false;
} }
qDebug() << "Installing: " << file_info.absoluteFilePath(); qDebug() << "Installing:" << file_info.absoluteFilePath();
Resource resource(file_info); Resource resource(file_info);
if (!resource.valid()) { if (!resource.valid()) {
@ -181,8 +185,8 @@ void ResourceFolderModel::installResourceWithFlameMetadata(QString path, ModPlat
QJsonParseError parse_error{}; QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) { if (parse_error.error != QJsonParseError::NoError) {
qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset qWarning() << "Error while parsing JSON response for mod info at" << parse_error.offset
<< " reason: " << parse_error.errorString(); << "reason:" << parse_error.errorString();
qDebug() << *response; qDebug() << *response;
return; return;
} }
@ -191,7 +195,7 @@ void ResourceFolderModel::installResourceWithFlameMetadata(QString path, ModPlat
FlameMod::loadIndexedPack(pack, obj); FlameMod::loadIndexedPack(pack, obj);
} catch (const JSONValidationError& e) { } catch (const JSONValidationError& e) {
qDebug() << doc; qDebug() << doc;
qWarning() << "Error while reading mod info: " << e.cause(); qWarning() << "Error while reading mod info:" << e.cause();
} }
LocalResourceUpdateTask update_metadata(indexDir(), pack, vers); LocalResourceUpdateTask update_metadata(indexDir(), pack, vers);
connect(&update_metadata, &Task::finished, this, install); connect(&update_metadata, &Task::finished, this, install);
@ -207,7 +211,13 @@ void ResourceFolderModel::installResourceWithFlameMetadata(QString path, ModPlat
bool ResourceFolderModel::uninstallResource(const QString& file_name, bool preserve_metadata) bool ResourceFolderModel::uninstallResource(const QString& file_name, bool preserve_metadata)
{ {
for (auto& resource : m_resources) { 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); auto res = resource->destroy(indexDir(), preserve_metadata, false);
update(); update();
@ -328,7 +338,20 @@ bool ResourceFolderModel::update()
}, },
Qt::ConnectionType::QueuedConnection); Qt::ConnectionType::QueuedConnection);
QThreadPool::globalInstance()->start(m_current_update_task.get()); Task::Ptr preUpdate{ createPreUpdateTask() };
if (preUpdate != nullptr) {
auto task = new SequentialTask("ResourceFolderModel::update");
task->addTask(preUpdate);
task->addTask(m_current_update_task);
connect(task, &Task::finished, [task] { task->deleteLater(); });
QThreadPool::globalInstance()->start(task);
} else {
QThreadPool::globalInstance()->start(m_current_update_task.get());
}
return true; return true;
} }
@ -362,10 +385,11 @@ void ResourceFolderModel::resolveResource(Resource::Ptr res)
}, },
Qt::ConnectionType::QueuedConnection); Qt::ConnectionType::QueuedConnection);
m_helper_thread_task.addTask(task); m_resourceResolver.addTask(task);
if (!m_helper_thread_task.isRunning()) { if (!m_resourceResolverRunning) {
QThreadPool::globalInstance()->start(&m_helper_thread_task); QThreadPool::globalInstance()->start(&m_resourceResolver);
m_resourceResolverRunning = true;
} }
} }

View file

@ -84,7 +84,7 @@ class ResourceFolderModel : public QAbstractListModel {
virtual bool startWatching() { return startWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); } virtual bool startWatching() { return startWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); }
virtual bool stopWatching() { return stopWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); } virtual bool stopWatching() { return stopWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); }
QDir indexDir() { return { QString("%1/.index").arg(dir().absolutePath()) }; } virtual QDir indexDir() const { return { QString("%1/.index").arg(dir().absolutePath()) }; }
/** Given a path in the system, install that resource, moving it to its place in the /** Given a path in the system, install that resource, moving it to its place in the
* instance file hierarchy. * instance file hierarchy.
@ -188,6 +188,7 @@ class ResourceFolderModel : public QAbstractListModel {
void parseFinished(); void parseFinished();
protected: protected:
[[nodiscard]] virtual Task* createPreUpdateTask() { return nullptr; }
/** This creates a new update task to be executed by update(). /** This creates a new update task to be executed by update().
* *
* The task should load and parse all resources necessary, and provide a way of accessing such results. * The task should load and parse all resources necessary, and provide a way of accessing such results.
@ -260,7 +261,10 @@ class ResourceFolderModel : public QAbstractListModel {
// Represents the relationship between a resource's internal ID and it's row position on the model. // Represents the relationship between a resource's internal ID and it's row position on the model.
QMap<QString, int> m_resources_index; QMap<QString, int> m_resources_index;
ConcurrentTask m_helper_thread_task; // Runs off-thread
ConcurrentTask m_resourceResolver;
bool m_resourceResolverRunning = false;
QMap<int, Task::Ptr> m_active_parse_tasks; QMap<int, Task::Ptr> m_active_parse_tasks;
std::atomic<int> m_next_resolution_ticket = 0; std::atomic<int> m_next_resolution_ticket = 0;
}; };

View file

@ -35,8 +35,6 @@
*/ */
#include "ResourcePackFolderModel.h" #include "ResourcePackFolderModel.h"
#include <qnamespace.h>
#include <qsize.h>
#include <QIcon> #include <QIcon>
#include <QStyle> #include <QStyle>

View file

@ -0,0 +1,56 @@
#include "ShaderPackFolderModel.h"
#include "FileSystem.h"
namespace {
class ShaderPackIndexMigrateTask : public Task {
Q_OBJECT
public:
ShaderPackIndexMigrateTask(QDir resourceDir, QDir indexDir) : m_resourceDir(std::move(resourceDir)), m_indexDir(std::move(indexDir)) {}
void executeTask() override
{
if (!m_indexDir.exists()) {
qDebug() << m_indexDir.absolutePath() << "does not exist; nothing to migrate";
emitSucceeded();
return;
}
QStringList pwFiles = m_indexDir.entryList({ "*.pw.toml" }, QDir::Files);
bool movedAll = true;
for (const auto& file : pwFiles) {
QString src = m_indexDir.filePath(file);
QString dest = m_resourceDir.filePath(file);
if (FS::move(src, dest)) {
qDebug() << "Moved" << src << "to" << dest;
} else {
movedAll = false;
}
}
if (!movedAll) {
// FIXME: not shown in the UI
emitFailed(tr("Failed to migrate shaderpack metadata from .index"));
return;
}
if (!FS::deletePath(m_indexDir.absolutePath())) {
emitFailed(tr("Failed to remove old .index dir"));
return;
}
emitSucceeded();
}
private:
QDir m_resourceDir, m_indexDir;
};
} // namespace
Task* ShaderPackFolderModel::createPreUpdateTask()
{
return new ShaderPackIndexMigrateTask(m_dir, ResourceFolderModel::indexDir());
}
#include "ShaderPackFolderModel.moc"

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