Compare commits

..

225 commits

Author SHA1 Message Date
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
521 changed files with 5290 additions and 14836 deletions

View file

@ -16,4 +16,3 @@ BraceWrapping:
BreakBeforeBraces: Custom
BreakConstructorInitializers: BeforeComma
Cpp11BracedListStyle: false
QualifierAlignment: Left

View file

@ -1,32 +1,23 @@
FormatStyle: file
Checks:
"bugprone-*,clang-analyzer-*,cppcoreguidelines-*,hicpp-*,misc-*,modernize-*,performance-*,portability-*,readability-*,
-*-magic-numbers,
-*-non-private-member-variables-in-classes,
-*-special-member-functions,
-bugprone-easily-swappable-parameters,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-pro-type-static-cast-downcast,
-modernize-use-nodiscard,
-modernize-use-trailing-return-type,
-portability-avoid-pragma-once,
-readability-avoid-unconditional-preprocessor-if,
-readability-function-cognitive-complexity,
-readability-identifier-length,
-readability-redundant-access-specifiers"
- modernize-use-using
- readability-avoid-const-params-in-decls
- misc-unused-parameters,
- readability-identifier-naming
# ^ Without unused-parameters the readability-identifier-naming check doesn't cause any warnings.
CheckOptions:
misc-include-cleaner.MissingIncludes: false
readability-identifier-naming.DefaultCase: "camelBack"
readability-identifier-naming.NamespaceCase: "CamelCase"
readability-identifier-naming.ClassCase: "CamelCase"
readability-identifier-naming.ClassConstantCase: "CamelCase"
readability-identifier-naming.EnumCase: "CamelCase"
readability-identifier-naming.EnumConstantCase: "CamelCase"
readability-identifier-naming.MacroDefinitionCase: "UPPER_CASE"
readability-identifier-naming.ClassMemberPrefix: "m_"
readability-identifier-naming.StaticConstantPrefix: "s_"
readability-identifier-naming.StaticVariablePrefix: "s_"
readability-identifier-naming.GlobalConstantPrefix: "g_"
readability-implicit-bool-conversion.AllowPointerConditions: true
- { key: readability-identifier-naming.ClassCase, value: PascalCase }
- { key: readability-identifier-naming.EnumCase, value: PascalCase }
- { key: readability-identifier-naming.FunctionCase, value: camelCase }
- { key: readability-identifier-naming.GlobalVariableCase, value: camelCase }
- { key: readability-identifier-naming.GlobalFunctionCase, value: camelCase }
- { key: readability-identifier-naming.GlobalConstantCase, value: SCREAMING_SNAKE_CASE }
- { key: readability-identifier-naming.MacroDefinitionCase, value: SCREAMING_SNAKE_CASE }
- { key: readability-identifier-naming.ClassMemberCase, value: camelCase }
- { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ }
- { key: readability-identifier-naming.ProtectedMemberPrefix, value: m_ }
- { key: readability-identifier-naming.PrivateStaticMemberPrefix, value: s_ }
- { key: readability-identifier-naming.ProtectedStaticMemberPrefix, value: s_ }
- { key: readability-identifier-naming.PublicStaticConstantCase, value: SCREAMING_SNAKE_CASE }
- { key: readability-identifier-naming.EnumConstantCase, value: PascalCase }

View file

@ -1,6 +1,6 @@
name: Bug Report
description: File a bug report
labels: ["bug: unconfirmed", "status: needs triage"]
labels: [bug]
body:
- type: markdown
attributes:

View file

@ -1,7 +1,7 @@
# Template based on https://gitlab.archlinux.org/archlinux/rfcs/-/blob/0ba3b61e987e197f8d1901709409b8564958f78a/rfcs/0000-template.rst
name: Request for Comment (RFC)
description: Propose a larger change and start a discussion.
labels: ["type: enhancement", "status: needs discussion", "status: needs triage"]
labels: [rfc]
body:
- type: markdown
attributes:

View file

@ -1,6 +1,6 @@
name: Suggestion
description: Make a suggestion
labels: ["type: enhancement", "status: needs triage"]
labels: [enhancement]
body:
- type: markdown
attributes:

View file

@ -27,18 +27,6 @@ runs:
using: composite
steps:
- name: Cleanup Qt installation on Linux
shell: bash
run: |
rm -rf "$QT_PLUGIN_PATH"/printsupport
rm -rf "$QT_PLUGIN_PATH"/sqldrivers
rm -rf "$QT_PLUGIN_PATH"/help
rm -rf "$QT_PLUGIN_PATH"/designer
rm -rf "$QT_PLUGIN_PATH"/qmltooling
rm -rf "$QT_PLUGIN_PATH"/qmlls
rm -rf "$QT_PLUGIN_PATH"/qmllint
rm -rf "$QT_PLUGIN_PATH"/platformthemes/libqgtk3.so
- name: Setup build variables
shell: bash
run: |
@ -75,7 +63,7 @@ runs:
if [ '${{ inputs.gpg-private-key-id }}' != '' ]; then
echo "$GPG_PRIVATE_KEY" > privkey.asc
gpg --import privkey.asc
gpg --export --armor ${{ inputs.gpg-private-key-id }} > pubkey.asc
gpg --export --armor 9C7A2C9B62603299 > pubkey.asc
else
echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY
fi
@ -90,10 +78,8 @@ runs:
# 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 {} +
#disable OpenGL and Vulkan launcher features until https://github.com/VHSgunzo/sharun/issues/35
echo "PRISMLAUNCHER_DISABLE_GLVULKAN=1" >> "$INSTALL_APPIMAGE_DIR"/.env
#makes the launcher use portals for file picking
echo "QT_QPA_PLATFORMTHEME=xdgdesktopportal" >> "$INSTALL_APPIMAGE_DIR"/.env
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"
@ -135,19 +121,19 @@ runs:
tar -czf ../PrismLauncher-portable.tar.gz *
- name: Upload binary tarball
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: PrismLauncher-${{ inputs.artifact-name }}-Qt6-Portable-${{ inputs.version }}-${{ inputs.build-type }}
path: PrismLauncher-portable.tar.gz
- name: Upload AppImage
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage
path: PrismLauncher-${{ runner.os }}-*${{ env.APPIMAGE_ARCH }}.AppImage
- name: Upload AppImage Zsync
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-${{ env.APPIMAGE_ARCH }}.AppImage.zsync
path: PrismLauncher-${{ runner.os }}-*${{ env.APPIMAGE_ARCH }}.AppImage.zsync

View file

@ -96,36 +96,16 @@ runs:
fi
ditto -c -k --sequesterRsrc --keepParent "Prism Launcher.app" ../PrismLauncher.zip
- name: Create DMG
shell: bash
env:
INSTALL_DIR: install
run: |
cd ${{ env.INSTALL_DIR }}
mkdir -p src
cp -R "Prism Launcher.app" src/
ln -s /Applications src/
hdiutil create \
-volname "Prism Launcher ${{ inputs.version }}" \
-srcfolder src \
-ov -format ULMO \
"../PrismLauncher.dmg"
- name: Make Sparkle signature
shell: bash
run: |
if [ '${{ inputs.sparkle-ed25519-key }}' != '' ]; then
echo '${{ inputs.sparkle-ed25519-key }}' > ed25519-priv.pem
signature_zip=$(/opt/homebrew/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.zip -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
signature_dmg=$(/opt/homebrew/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.dmg -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
signature=$(/opt/homebrew/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.zip -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
rm ed25519-priv.pem
cat >> $GITHUB_STEP_SUMMARY << EOF
### Artifact Information :information_source:
- :memo: Sparkle Signature (ed25519): \`$signature_zip\` (ZIP)
- :memo: Sparkle Signature (ed25519): \`$signature_dmg\` (DMG)
- :memo: Sparkle Signature (ed25519): \`$signature\`
EOF
else
cat >> $GITHUB_STEP_SUMMARY << EOF
@ -135,13 +115,7 @@ runs:
fi
- name: Upload binary tarball
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: PrismLauncher-${{ inputs.artifact-name }}-${{ inputs.version }}-${{ inputs.build-type }}
path: PrismLauncher.zip
- name: Upload disk image
uses: actions/upload-artifact@v7
with:
name: PrismLauncher-${{ inputs.artifact-name }}-${{ inputs.version }}-${{ inputs.build-type }}.dmg
path: PrismLauncher.dmg

View file

@ -61,7 +61,7 @@ runs:
- name: Login to Azure
if: ${{ env.CI_HAS_ACCESS_TO_AZURE != '' && inputs.azure-client-id != '' }}
uses: azure/login@v3
uses: azure/login@v2
with:
client-id: ${{ inputs.azure-client-id }}
tenant-id: ${{ inputs.azure-tenant-id }}
@ -79,8 +79,8 @@ runs:
files-folder-recurse: true
files-folder-depth: 2
# recommended in https://github.com/Azure/artifact-signing-action#timestamping-1
timestamp-rfc3161: 'http://timestamp.acs.microsoft.com'
timestamp-digest: 'SHA256'
timestamp-rfc3161: "http://timestamp.acs.microsoft.com"
timestamp-digest: "SHA256"
# TODO(@getchoo): Is this all really needed???
# https://github.com/Azure/trusted-signing-action/blob/fc390cf8ed0f14e248a542af1d838388a47c7a7c/docs/OIDC.md
exclude-environment-credential: true
@ -152,8 +152,8 @@ runs:
${{ 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'
timestamp-rfc3161: "http://timestamp.acs.microsoft.com"
timestamp-digest: "SHA256"
# TODO(@getchoo): Is this all really needed???
# https://github.com/Azure/trusted-signing-action/blob/fc390cf8ed0f14e248a542af1d838388a47c7a7c/docs/OIDC.md
exclude-environment-credential: true
@ -168,19 +168,19 @@ runs:
exclude-interactive-browser-credential: true
- name: Upload binary zip
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: PrismLauncher-${{ inputs.artifact-name }}-${{ inputs.version }}-${{ inputs.build-type }}
path: install/**
- name: Upload portable zip
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: PrismLauncher-${{ inputs.artifact-name }}-Portable-${{ inputs.version }}-${{ inputs.build-type }}
path: install-portable/**
- name: Upload installer
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
with:
name: PrismLauncher-${{ inputs.artifact-name }}-Setup-${{ inputs.version }}-${{ inputs.build-type }}
path: PrismLauncher-Setup.exe

View file

@ -55,7 +55,7 @@ runs:
# TODO(@getchoo): Get this working on MSYS2!
- name: Setup ccache
if: ${{ (runner.os != 'Windows' || inputs.msystem == '') && inputs.build-type == 'Debug' }}
uses: hendrikmuhs/ccache-action@v1.2.23
uses: hendrikmuhs/ccache-action@v1.2.20
with:
variant: sccache
create-symlink: ${{ runner.os != 'Windows' }}

View file

@ -1,5 +1,4 @@
name: Setup Linux dependencies
description: Install and setup dependencies for building Prism Launcher
runs:
using: composite
@ -13,7 +12,7 @@ runs:
dpkg-dev \
ninja-build extra-cmake-modules pkg-config scdoc \
cmark gamemode-dev libarchive-dev libcmark-dev libqrencode-dev zlib1g-dev \
libxcb-cursor-dev libtomlplusplus-dev libvulkan-dev
libxcb-cursor-dev libtomlplusplus-dev
- name: Setup AppImage tooling
shell: bash

View file

@ -1,5 +1,4 @@
name: Setup Windows Dependencies
description: Install and setup dependencies for building Prism Launcher
inputs:
build-type:
@ -91,7 +90,7 @@ runs:
- name: Retrieve ccache cache (MinGW)
if: ${{ inputs.msystem != '' && inputs.build-type == 'Debug' }}
uses: actions/cache@v5.0.5
uses: actions/cache@v5.0.1
with:
path: '${{ github.workspace }}\.ccache'
key: ${{ runner.os }}-mingw-w64-ccache-${{ github.run_id }}

View file

@ -8,7 +8,8 @@ on:
# the GitHub repository. This means that it should not evaluate user input in a
# way that allows code injection.
permissions: {}
permissions:
contents: read
jobs:
backport:
@ -18,13 +19,13 @@ jobs:
actions: write # for korthout/backport-action to create PR with workflow changes
name: Backport Pull Request
if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
runs-on: ubuntu-slim
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Create backport PRs
uses: korthout/backport-action@v4.4
uses: korthout/backport-action@v4.0.0
with:
# Config README: https://github.com/korthout/backport-action#backport-action
pull_description: |-

View file

@ -14,17 +14,15 @@ on:
required: true
type: number
permissions: {}
jobs:
blocked_status:
name: Check Blocked Status
runs-on: ubuntu-slim
runs-on: ubuntu-latest
steps:
- name: Generate token
id: generate-token
uses: actions/create-github-app-token@v3
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.PULL_REQUEST_APP_ID }}
private-key: ${{ secrets.PULL_REQUEST_APP_PRIVATE_KEY }}
@ -72,7 +70,6 @@ jobs:
' <<< "$PR_JSON")"
} >> "$GITHUB_ENV"
- name: Find Blocked/Stacked PRs in body
id: pr_ids
run: |
@ -152,12 +149,12 @@ jobs:
- name: Add 'blocked' Label if Missing
id: label_blocked
if: "(fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0) && !contains(fromJSON(env.JOB_DATA).prLabels, 'status: blocked') && !fromJSON(steps.blocking_data.outputs.all_merged)"
if: (fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0) && !contains(fromJSON(env.JOB_DATA).prLabels, 'blocked') && !fromJSON(steps.blocking_data.outputs.all_merged)
continue-on-error: true
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |
gh -R ${{ github.repository }} issue edit --add-label 'status: blocked' "$PR_NUMBER"
gh -R ${{ github.repository }} issue edit --add-label 'blocked' "$PR_NUMBER"
- name: Remove 'blocked' Label if All Dependencies Are Merged
id: unlabel_blocked
@ -166,7 +163,7 @@ jobs:
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |
gh -R ${{ github.repository }} issue edit --remove-label 'status: blocked' "$PR_NUMBER"
gh -R ${{ github.repository }} issue edit --remove-label 'blocked' "$PR_NUMBER"
- name: Apply 'blocking' Label to Unmerged Dependencies
id: label_blocking
@ -177,7 +174,7 @@ jobs:
BLOCKING_ISSUES: ${{ steps.blocking_data.outputs.current_blocking }}
run: |
while read -r pr ; do
gh -R ${{ github.repository }} issue edit --add-label 'status: blocking' "$pr" || true
gh -R ${{ github.repository }} issue edit --add-label 'blocking' "$pr" || true
done < <(jq -c '.[]' <<< "$BLOCKING_ISSUES")
- name: Apply Blocking PR Status Check
@ -254,4 +251,3 @@ jobs:
--body "$COMMENT_BODY" \
--create-if-none \
--edit-last

View file

@ -5,9 +5,57 @@ concurrency:
cancel-in-progress: true
on:
merge_group:
types: [checks_requested]
push:
branches:
- "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:
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/**"
workflow_call:
inputs:
build-type:
@ -24,8 +72,6 @@ on:
type: string
default: Debug
permissions: {}
jobs:
build:
name: Build (${{ matrix.artifact-name }})
@ -33,7 +79,6 @@ jobs:
environment: ${{ inputs.environment || '' }}
permissions:
contents: read
# Required for Azure Trusted Signing
id-token: write
# Required for vcpkg binary cache

View file

@ -1,53 +0,0 @@
name: Clang-Tidy Code Scanning
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
merge_group:
types: [checks_requested]
pull_request:
permissions: {}
jobs:
clang-tidy:
name: Run Clang-Tidy
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0 # Required for diffing later on
submodules: "true"
- name: Setup sccache
uses: hendrikmuhs/ccache-action@v1.2.23
with:
variant: sccache
- name: Install Nix
uses: cachix/install-nix-action@v31
- name: Run build
# TODO(@getchoo): Figure out how to make this work with PCH
run: |
nix develop --command bash -c '
cmake -B build -D Launcher_USE_PCH=OFF -D CMAKE_CXX_COMPILER_LAUNCHER=sccache && cmake --build build
'
# TODO: Use SARIF after https://github.com/psastras/sarif-rs/issues/638 is fixed
- name: Run clang-tidy-diff
env:
BASE_REF: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
run: |
nix develop --command bash -c '
clang-tidy -verify-config && git diff -U0 --no-color "$BASE_REF" | clang-tidy-diff.py -p1 -quiet -only-check-in-db
'

View file

@ -5,21 +5,63 @@ concurrency:
cancel-in-progress: true
on:
merge_group:
types: [checks_requested]
pull_request:
workflow_dispatch:
push:
branches:
- "develop"
- "release-*"
paths:
# File types
- "**.cpp"
- "**.h"
- "**.java"
- "**.ui"
permissions: {}
# 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:
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/**"
workflow_dispatch:
jobs:
CodeQL:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
@ -41,12 +83,12 @@ jobs:
- name: Configure and Build
run: |
cmake --preset linux -DLauncher_USE_PCH=OFF
cmake --preset linux
cmake --build --preset linux --config Debug
- name: Run tests
run: |
ctest --preset linux --build-config Debug --extra-verbose --output-on-failure
ctest --preset linux --build-config Debug
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4

View file

@ -1,174 +0,0 @@
name: Development Container
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
merge_group:
types: [checks_requested]
pull_request:
workflow_dispatch:
permissions: {}
env:
REGISTRY: ghcr.io
jobs:
build:
name: Build (${{ matrix.arch }})
permissions:
contents: read
packages: write
outputs:
image-name: ${{ steps.image-name.outputs.image-name }}
strategy:
fail-fast: false
matrix:
include:
- arch: arm64
os: ubuntu-24.04-arm
- arch: amd64
os: ubuntu-24.04
runs-on: ${{ matrix.os }}
steps:
- name: Set image name
id: image-name
run: |
echo "image-name=${REGISTRY}/${GITHUB_REPOSITORY_OWNER,,}/devcontainer" >> "$GITHUB_OUTPUT"
- name: Install Podman
uses: redhat-actions/podman-install@main
# TODO(@getchoo): Always use this when the action properly supports ARM
if: ${{ runner.arch == 'X64' || runner.arch == 'X86' }}
with:
github-token: ${{ github.token }}
- name: Checkout repository
uses: actions/checkout@v6
- name: Determine metadata for image
id: image-metadata
uses: docker/metadata-action@v6
with:
images: |
${{ steps.image-name.outputs.image-name }}
flavor: |
latest=false
tags: |
type=raw,value=latest,enable=${{ github.event.merge_group.base_ref == 'refs/heads/develop' }}
type=sha
type=sha,format=long
type=ref,event=branch
type=ref,event=tag
- name: Build image
id: build-image
uses: redhat-actions/buildah-build@v2
with:
containerfiles: |
./Containerfile
tags: ${{ steps.image-metadata.outputs.tags }}
labels: ${{ steps.image-metadata.outputs.labels }}
- name: Push image
id: push-image
if: ${{ github.event_name != 'pull_request' }}
uses: redhat-actions/push-to-registry@v2
with:
tags: ${{ steps.build-image.outputs.tags }}
username: ${{ github.repository_owner }}
password: ${{ github.token }}
tls-verify: true
- name: Export image digest
if: ${{ github.event_name != 'pull_request' }}
env:
DIGEST: ${{ steps.push-image.outputs.digest }}
run: |
mkdir -p "$RUNNER_TEMP"/digests
touch "$RUNNER_TEMP"/digests/"${DIGEST#sha256:}"
- name: Upload digest artifact
if: ${{ github.event_name != 'pull_request' }}
uses: actions/upload-artifact@v7
with:
name: digests-${{ matrix.arch }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
manifest:
name: Create manifest
needs: [ build ]
if: ${{ github.event_name != 'pull_request' }}
permissions:
contents: read
packages: write
runs-on: ubuntu-24.04
steps:
- name: Download digests
uses: actions/download-artifact@v8
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true
- name: Install Podman
# TODO(@getchoo): Always use this when the action properly supports ARM
if: ${{ runner.arch == 'X64' || runner.arch == 'X86' }}
uses: redhat-actions/podman-install@main
with:
github-token: ${{ github.token }}
- name: Login to registry
uses: redhat-actions/podman-login@v1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.repository_owner }}
password: ${{ github.token }}
- name: Determine metadata for manifest
id: manifest-metadata
uses: docker/metadata-action@v6
with:
images: |
${{ needs.build.outputs.image-name }}
flavor: |
latest=false
tags: |
type=raw,value=latest,enable=${{ github.event.merge_group.base_ref == 'refs/heads/develop' }}
type=sha
type=sha,format=long
type=ref,event=branch
type=ref,event=tag
- name: Create manifest list
working-directory: ${{ runner.temp }}/digests
env:
IMAGE_NAME: ${{ needs.build.outputs.image-name }}
run: |
while read -r tag; do
podman manifest create "$tag" \
$(printf "$IMAGE_NAME@sha256:%s " *)
done <<< "$DOCKER_METADATA_OUTPUT_TAGS"
- name: Push manifest
uses: redhat-actions/push-to-registry@v2
with:
tags: ${{ steps.manifest-metadata.outputs.tags }}
username: ${{ github.repository_owner }}
password: ${{ github.token }}
tls-verify: true

View file

@ -11,21 +11,19 @@ on:
required: true
type: number
permissions: {}
jobs:
update-blocked-status:
name: Update Blocked Status
runs-on: ubuntu-slim
runs-on: ubuntu-latest
# a pr that was a `blocking:<id>` label was merged.
# find the open pr's it was blocked by and trigger a refresh of their state
if: "${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'status: blocking') }}"
if: ${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'blocking') }}
steps:
- name: Generate token
id: generate-token
uses: actions/create-github-app-token@v3
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.PULL_REQUEST_APP_ID }}
private-key: ${{ secrets.PULL_REQUEST_APP_PRIVATE_KEY }}
@ -37,7 +35,7 @@ jobs:
PR_NUMBER: ${{ inputs.pr_id || github.event.pull_request.number }}
run: |
blocked_prs=$(
gh -R ${{ github.repository }} pr list --label 'status: blocked' --json 'number,body' \
gh -R ${{ github.repository }} pr list --label 'blocked' --json 'number,body' \
| jq -c --argjson pr "$PR_NUMBER" '
reduce ( .[] | select(
.body |

View file

@ -17,7 +17,6 @@ on:
- "**.h"
- "**.java"
- "**.ui"
- "**.md"
# Build files
- "**.nix"
@ -34,6 +33,7 @@ on:
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/nix.yml"
@ -44,7 +44,6 @@ on:
- "**.h"
- "**.java"
- "**.ui"
- "**.md"
# Build files
- "**.nix"
@ -61,12 +60,14 @@ on:
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/nix.yml"
workflow_dispatch:
permissions: {}
permissions:
contents: read
env:
DEBUG: ${{ github.ref_type != 'tag' }}
@ -75,9 +76,6 @@ jobs:
build:
name: Build (${{ matrix.system }})
permissions:
contents: read
strategy:
fail-fast: false
matrix:
@ -88,7 +86,7 @@ jobs:
- os: ubuntu-22.04-arm
system: aarch64-linux
- os: macos-26
- os: macos-14
system: aarch64-darwin
runs-on: ${{ matrix.os }}
@ -111,7 +109,7 @@ jobs:
# For in-tree builds
- name: Setup Cachix
if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
uses: cachix/cachix-action@v17
uses: cachix/cachix-action@v16
with:
name: prismlauncher
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}

View file

@ -4,16 +4,14 @@ on:
release:
types: [ released ]
permissions: {}
permissions:
contents: read
jobs:
winget:
name: Winget
permissions:
contents: read
runs-on: ubuntu-slim
runs-on: windows-latest
steps:
- name: Publish on Winget

View file

@ -5,18 +5,10 @@ on:
tags:
- "*"
permissions: {}
jobs:
build_release:
name: Build Release
uses: ./.github/workflows/build.yml
permissions:
contents: read
# Required for Azure Trusted Signing
id-token: write
# Required for vcpkg binary cache
packages: write
with:
build-type: Release
environment: Release
@ -24,9 +16,7 @@ jobs:
create_release:
needs: build_release
permissions:
contents: write
runs-on: ubuntu-slim
runs-on: ubuntu-latest
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
@ -36,7 +26,7 @@ jobs:
submodules: "true"
path: "PrismLauncher-source"
- name: Download artifacts
uses: actions/download-artifact@v8
uses: actions/download-artifact@v7
- name: Grab and store version
run: |
tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$")
@ -51,7 +41,6 @@ jobs:
mv PrismLauncher-*.AppImage/PrismLauncher-*-aarch64.AppImage PrismLauncher-Linux-aarch64.AppImage
mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*-aarch64.AppImage.zsync PrismLauncher-Linux-aarch64.AppImage.zsync
mv PrismLauncher-macOS*/PrismLauncher.zip PrismLauncher-macOS-${{ env.VERSION }}.zip
mv PrismLauncher-macOS*/PrismLauncher.dmg PrismLauncher-macOS-${{ env.VERSION }}.dmg
tar --exclude='.git' -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }}
@ -94,7 +83,7 @@ jobs:
- name: Create release
id: create_release
uses: softprops/action-gh-release@v3
uses: softprops/action-gh-release@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag_name: ${{ github.ref }}
@ -122,5 +111,4 @@ jobs:
PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-Setup-${{ env.VERSION }}.exe
PrismLauncher-macOS-${{ env.VERSION }}.zip
PrismLauncher-macOS-${{ env.VERSION }}.dmg
PrismLauncher-${{ env.VERSION }}.tar.gz

29
.github/workflows/stale.yml vendored Normal file
View file

@ -0,0 +1,29 @@
name: Stale
on:
schedule:
# run weekly on sunday
- cron: "0 0 * * 0"
workflow_dispatch:
jobs:
label:
name: Label issues and PRs
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v10
with:
days-before-stale: 60
days-before-close: -1 # Don't close anything
exempt-issue-labels: rfc,nostale,help wanted
exempt-all-milestones: true
exempt-all-assignees: true
operations-per-run: 1000
stale-issue-label: inactive
stale-pr-label: inactive

View file

@ -6,30 +6,25 @@ on:
- cron: "0 0 * * 0"
workflow_dispatch:
permissions: {}
permissions:
contents: write
pull-requests: write
jobs:
update-flake:
if: github.repository == 'PrismLauncher/PrismLauncher'
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-slim
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: cachix/install-nix-action@616559265b40713947b9c190a8ff4b507b5df49b # v31
- uses: cachix/install-nix-action@4e002c8ec80594ecd40e759629461e26c8abed15 # v31
- uses: DeterminateSystems/update-flake-lock@v28
with:
commit-msg: "chore(nix): update lockfile"
pr-title: "chore(nix): update lockfile"
pr-labels: |
platform: Linux
area: packaging
complexity: low
priority: low
type: robot
Linux
packaging
simple change
changelog:omit

View file

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.25) # Required for features like `CMAKE_MSVC_DEBUG_INFORMATION_FORMAT`
cmake_minimum_required(VERSION 3.22) # minimum version required by Qt
project(Launcher LANGUAGES C CXX)
if(APPLE)
@ -30,82 +30,79 @@ set(CMAKE_C_STANDARD_REQUIRED true)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_C_STANDARD 11)
include(GenerateExportHeader)
if(MSVC)
# /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
# Use /W4 as /Wall includes unnesserey warnings such as added padding to structs
set(CMAKE_CXX_FLAGS "/GS /permissive- /W4 ${CMAKE_CXX_FLAGS}")
add_compile_definitions($<$<NOT:$<CONFIG:Debug>>:QT_NO_DEBUG>)
add_compile_definitions(QT_WARN_DEPRECATED_UP_TO=0x060400)
add_compile_definitions(QT_DISABLE_DEPRECATED_UP_TO=0x060400)
# /EHs Enables stack unwind semantics for standard C++ exceptions to ensure stackframes are unwound
# and object deconstructors are called when an exception is caught.
# without it memory leaks and a warning is printed
# /EHc tells the compiler to assume that functions declared as extern "C" never throw a C++ exception
# This appears to not always be a defualt compiler option in CMAKE
set(CMAKE_CXX_FLAGS "/EHsc ${CMAKE_CXX_FLAGS}")
if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
add_compile_options(
# /GS Adds buffer security checks, default on but included anyway to mirror gcc's fstack-protector flag
"$<$<COMPILE_LANGUAGE:C,CXX>:/GS>"
# /Gw helps reduce binary size
# /Gy allows the compiler to package individual functions
# /guard:cf enables control flow guard
"$<$<AND:$<CONFIG:Release,RelWithDebInfo>,$<COMPILE_LANGUAGE:C,CXX>>:/Gw;/Gy;/guard:cf>"
)
add_link_options(
# /LTCG allows for linking wholy optimizated programs
# /MANIFEST:NO disables generating a manifest file, we instead provide our own
# /STACK sets the stack reserve size, ATL's pack list needs 3-4 MiB as of November 2022, provide 8 MiB
"$<$<COMPILE_LANGUAGE:C,CXX>:/LTCG;/MANIFEST:NO;/STACK:8388608>"
)
# LINK accepts /SUBSYSTEM whics sets if we are a WINDOWS (gui) or a CONSOLE programs
# This implicitly selects an entrypoint specific to the subsystem selected
# qtmain/QtEntryPointLib provides the correct entrypoint (wWinMain) for gui programs
# Additinaly LINK autodetects we use a GUI so we can omit /SUBSYSTEM
# This allows tests to still use have console without using seperate linker flags
# /LTCG allows for linking wholy optimizated programs
# /MANIFEST:NO disables generating a manifest file, we instead provide our own
# /STACK sets the stack reserve size, ATL's pack list needs 3-4 MiB as of November 2022, provide 8 MiB
set(CMAKE_EXE_LINKER_FLAGS "/LTCG /MANIFEST:NO /STACK:8388608 ${CMAKE_EXE_LINKER_FLAGS}")
# /GL enables whole program optimizations
# NOTE: With Clang, this is implemented as regular LTO and only used during linking
if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_compile_options("$<$<AND:$<CONFIG:Release,RelWithDebInfo>,$<COMPILE_LANGUAGE:C,CXX>>:/GL>")
endif()
# /Gw helps reduce binary size
# /Gy allows the compiler to package individual functions
# /guard:cf enables control flow guard
foreach(lang C CXX)
set("CMAKE_${lang}_FLAGS_RELEASE" "/GL /Gw /Gy /guard:cf")
endforeach()
# See https://github.com/ccache/ccache/issues/1040
# TODO(@getchoo): Is sccache affected by this? Would be nice to use `ProgramDatabase`....
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>")
# Note, CMake 3.25 replaces this with CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
# See https://cmake.org/cmake/help/v3.25/variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT.html
foreach(config DEBUG RELWITHDEBINFO)
foreach(lang C CXX)
set(flags_var "CMAKE_${lang}_FLAGS_${config}")
string(REGEX REPLACE "/Z[Ii]" "/Z7" ${flags_var} "${${flags_var}}")
endforeach()
endforeach()
if(CMAKE_MSVC_RUNTIME_LIBRARY STREQUAL "MultiThreadedDLL")
set(CMAKE_MAP_IMPORTED_CONFIG_DEBUG Release "")
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release "")
endif()
else()
add_compile_options("$<$<COMPILE_LANGUAGE:C,CXX>:-fstack-protector-strong;--param=ssp-buffer-size=4>")
# Avoid re-defining _FORTIFY_SOURCE, as it can cause redefinition errors in setups that use it by default (i.e., package builds)
if(NOT (CMAKE_C_FLAGS MATCHES "-D_FORTIFY_SOURCE" OR CMAKE_CXX_FLAGS MATCHES "-D_FORTIFY_SOURCE"))
# NOTE: _FORTIFY_SOURCE requires optimizations in most newer versions of glibc
add_compile_options("$<$<AND:$<COMPILE_LANGUAGE:C,CXX>,$<CONFIG:Release,RelWithDebInfo>>:-D_FORTIFY_SOURCE=2>")
endif()
set(CMAKE_CXX_FLAGS "-Wall -pedantic -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}")
# ATL's pack list needs more than the default 1 Mib stack on windows
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_link_options("$<$<COMPILE_LANGUAGE:C,CXX>:-Wl,--stack,8388608>")
if(WIN32)
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}")
# -ffunction-sections and -fdata-sections help reduce binary size
# -mguard=cf enables Control Flow Guard
# TODO: Look into -gc-sections to further reduce binary size
add_compile_options("$<$<AND:$<CONFIG:Release,RelWithDebInfo>,$<COMPILE_LANGUAGE:C,CXX>>:-ffunction-sections;-fdata-sections;-mguard=cf>")
foreach(lang C CXX)
set("CMAKE_${lang}_FLAGS_RELEASE" "-ffunction-sections -fdata-sections -mguard=cf")
endforeach()
endif()
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_WARN_DEPRECATED_UP_TO=0x060200")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_UP_TO=0x060000")
# set CXXFLAGS for build targets
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
# Export compile commands for debug builds if we can (useful in LSPs like clangd)
# https://cmake.org/cmake/help/v3.31/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html
if(CMAKE_GENERATOR STREQUAL "Unix Makefiles" OR CMAKE_GENERATOR MATCHES "^Ninja")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
endif()
option(USE_CLANG_TIDY "Enable the use of clang-tidy during compilation" OFF)
if(USE_CLANG_TIDY)
find_program(CLANG_TIDY clang-tidy OPTIONAL)
if(CLANG_TIDY)
message(STATUS "Using clang-tidy during compilation")
set(CLANG_TIDY_COMMAND "${CLANG_TIDY}" "--config-file=${CMAKE_SOURCE_DIR}/.clang-tidy")
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_COMMAND})
else()
message(WARNING "Unable to find `clang-tidy`. Not using during compilation")
endif()
endif()
option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF)
# If this is a Debug build turn on address sanitiser
@ -142,9 +139,8 @@ if(ENABLE_LTO)
if(ipo_supported)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL TRUE)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
if(CMAKE_BUILD_TYPE)
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
message(STATUS "IPO / LTO enabled")
else()
message(STATUS "Not enabling IPO / LTO on debug builds")
@ -173,15 +169,14 @@ endif()
######## Set URLs ########
set(Launcher_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch Prism Launcher's news RSS feed from.")
set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'")
set(Launcher_WIKI_URL "https://prismlauncher.org/wiki/" CACHE STRING "URL that gets opened when the user clicks 'Launcher Help'")
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help in a dialog window")
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
set(Launcher_LOGIN_CALLBACK_URL "https://prismlauncher.org/successful-login" CACHE STRING "URL that gets opened when the user successfully logins.")
set(Launcher_LEGACY_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE STRING "URL for legacy (<=1.5.2) FML Libraries.")
set(Launcher_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE STRING "URL for FML Libraries.")
######## Set version numbers ########
set(Launcher_VERSION_MAJOR 12)
set(Launcher_VERSION_MAJOR 10)
set(Launcher_VERSION_MINOR 0)
set(Launcher_VERSION_PATCH 0)
set(Launcher_VERSION_PATCH 4)
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")
@ -415,7 +410,7 @@ if(UNIX AND APPLE)
COMMAND ${ACTOOL_EXE} "${ICON_SOURCE}"
--compile "${ASSETS_OUT_DIR}"
--output-partial-info-plist /dev/null
--app-icon ${Launcher_Name}
--app-icon PrismLauncher
--enable-on-demand-resources NO
--target-device mac
--minimum-deployment-target ${CMAKE_OSX_DEPLOYMENT_TARGET}
@ -450,7 +445,7 @@ elseif(UNIX)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${KDE_INSTALL_METAINFODIR})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps")
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_PNG_256} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/256x256/apps" RENAME "${Launcher_AppID}.png")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}")
@ -488,6 +483,7 @@ option(NBT_USE_ZLIB "Build NBT library with zlib support" OFF)
option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests.
add_subdirectory(libraries/libnbtplusplus)
add_subdirectory(libraries/systeminfo) # system information library
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
add_subdirectory(libraries/javacheck) # java compatibility checker

View file

@ -105,7 +105,7 @@
"lhs": "${hostSystemName}",
"rhs": "Darwin"
},
"configurePreset": "macos"
"configurePreset": "macos"
},
{
"name": "macos_universal",
@ -141,8 +141,7 @@
"name": "base",
"hidden": true,
"output": {
"outputOnFailure": true,
"verbosity": "extra"
"outputOnFailure": true
},
"execution": {
"noTestsAction": "error"
@ -153,11 +152,11 @@
}
}
},
{
"name": "linux",
{
"name": "linux",
"displayName": "Linux",
"inherits": [
"base"
"base"
],
"condition": {
"type": "equals",
@ -166,11 +165,11 @@
},
"configurePreset": "linux"
},
{
"name": "macos",
{
"name": "macos",
"displayName": "macOS",
"inherits": [
"base"
"base"
],
"condition": {
"type": "equals",
@ -179,11 +178,11 @@
},
"configurePreset": "macos"
},
{
"name": "macos_universal",
{
"name": "macos_universal",
"displayName": "macOS (Universal Binary)",
"inherits": [
"base"
"base"
],
"condition": {
"type": "equals",
@ -192,11 +191,11 @@
},
"configurePreset": "macos_universal"
},
{
"name": "windows_mingw",
{
"name": "windows_mingw",
"displayName": "Windows (MinGW)",
"inherits": [
"base"
"base"
],
"condition": {
"type": "equals",
@ -205,11 +204,11 @@
},
"configurePreset": "windows_mingw"
},
{
"name": "windows_msvc",
{
"name": "windows_msvc",
"displayName": "Windows (MSVC)",
"inherits": [
"base"
"base"
],
"condition": {
"type": "equals",

View file

@ -1,52 +1,5 @@
# Contributions Guidelines
## Restrictions on Generative AI Usage (AI Policy)
> [!NOTE]
> The following is adapted from [matplotlib's contributing guide](https://matplotlib.org/devdocs/devel/contribute.html#generative-ai) and the [Linux Kernel policy guide](https://www.kernel.org/doc./html/next/process/coding-assistants.html)
We expect authentic engagement in our community.
- Do not post output from Large Language Models or similar generative AI as comments on GitHub or our discord server, as such comments tend to be formulaic and low-quality content.
- If you use generative AI tools as an aid in developing code or documentation changes, ensure that you fully understand the proposed changes and can explain why they are the correct approach.
Make sure you have added value based on your personal competency to your contributions.
Just taking some input, feeding it to an AI and posting the result is not of value to the project.
To preserve precious core developer capacity, we reserve the right to rigorously reject seemingly AI generated low-value contributions.
### Signed-off-by and Developer Certificate of Origin
AI agents MUST NOT add Signed-off-by tags. Only humans can legally certify the Developer Certificate of Origin (DCO). The human submitter is responsible for:
- Reviewing all AI-generated code
- Ensuring compliance with licensing requirements
- Adding their own Signed-off-by tag to certify the DCO
- Taking full responsibility for the contribution
See [Signing your work](#signing-your-work) for more information.
### Attribution
When AI tools contribute to development, proper attribution helps track the evolving role of AI in the development process. Contributions should include an Assisted-by tag in the commit message with the following format:
```text
Assisted-by: AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2]
```
Where:
- `AGENT_NAME` is the name of the AI tool or framework
- `MODEL_VERSION` is the specific model version used
- `[TOOL1] [TOOL2]` are optional specialized analysis tools used (e.g., coccinelle, sparse, smatch, clang-tidy)
Basic development tools (git, gcc, make, editors) should not be listed.
Example:
```text
Assisted-by: Claude:claude-3-opus coccinelle sparse
```
## Code style
All files are formatted with `clang-format` using the configuration in `.clang-format`. Ensure it is run on changed files before committing!

View file

@ -1,74 +0,0 @@
ARG DEBIAN_VERSION=stable-slim
FROM docker.io/library/debian:${DEBIAN_VERSION}
ARG QT_ABI=gcc_64
ARG QT_ARCH=
ARG QT_HOST=linux
ARG QT_TARGET=desktop
ARG QT_VERSION=6.10.2
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get --assume-yes upgrade \
&& apt-get --assume-yes autopurge
# Use Adoptium for Java 17
RUN apt-get --assume-yes --no-install-recommends install \
apt-transport-https ca-certificates curl gpg
RUN curl -L https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor | tee /etc/apt/trusted.gpg.d/adoptium.gpg
RUN echo "deb https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | tee /etc/apt/sources.list.d/adoptium.list
RUN apt-get update
# Install base dependencies
RUN apt-get --assume-yes --no-install-recommends install \
# Compilers
clang lld llvm temurin-17-jdk \
# Build system
cmake ninja-build extra-cmake-modules pkg-config \
# Dependencies
cmark gamemode-dev libarchive-dev libcmark-dev libgamemode0 libgl1-mesa-dev libqrencode-dev libtomlplusplus-dev libvulkan-dev scdoc zlib1g-dev \
# Tooling
clang-format clang-tidy git
# Use LLD by default for faster linking
ENV CMAKE_LINKER_TYPE=lld
# Prepare and install Qt
## Setup UTF-8 locale (required, apparently)
RUN apt-get --assume-yes --no-install-recommends install locales
RUN echo "C.UTF-8 UTF-8" > /etc/locale.gen
RUN locale-gen
ENV LC_ALL=C.UTF-8
## Some libraries are required for the official binaries
RUN apt-get --assume-yes --no-install-recommends install \
libglib2.0-0t64 libxkbcommon0 python3-pip
RUN pip3 install --break-system-packages aqtinstall
RUN aqt install-qt \
${QT_HOST} ${QT_TARGET} ${QT_VERSION} ${QT_ARCH} \
--outputdir /opt/qt \
--modules qtimageformats qtnetworkauth
ENV PATH=/opt/qt/${QT_VERSION}/${QT_ABI}/bin:$PATH
ENV QT_PLUGIN_PATH=/opt/qt/${QT_VERSION}/${QT_ABI}/plugins/
## We don't use these. Nuke them
RUN rm -rf \
"$QT_PLUGIN_PATH"/designer \
"$QT_PLUGIN_PATH"/help \
# "$QT_PLUGIN_PATH"/platformthemes/libqgtk3.so \
"$QT_PLUGIN_PATH"/printsupport \
"$QT_PLUGIN_PATH"/qmllint \
"$QT_PLUGIN_PATH"/qmlls \
"$QT_PLUGIN_PATH"/qmltooling \
"$QT_PLUGIN_PATH"/sqldrivers
# Setup workspace
RUN mkdir /work
WORKDIR /work
ENTRYPOINT ["bash"]
CMD ["-i"]

View file

@ -27,7 +27,7 @@ Please understand that these builds are not intended for most users. There may b
There are development builds available through:
- [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) (includes builds from pull requests opened by contributors)
- [nightly.link](https://prismlauncher.org/nightly) (this will always point only to the latest version of develop)
- [nightly.link](https://nightly.link/PrismLauncher/PrismLauncher/workflows/build/develop) (this will always point only to the latest version of develop)
These have debug information in the binaries, so their file sizes are relatively larger.

View file

@ -50,7 +50,6 @@ Config::Config()
LAUNCHER_GIT = "@Launcher_Git@";
LAUNCHER_APPID = "@Launcher_AppID@";
LAUNCHER_SVGFILENAME = "@Launcher_SVGFileName@";
LAUNCHER_ENVNAME = "@Launcher_ENVName@";
USER_AGENT = "@Launcher_UserAgent@";
@ -106,14 +105,13 @@ Config::Config()
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@";
WIKI_URL = "@Launcher_WIKI_URL@";
HELP_URL = "@Launcher_HELP_URL@";
LOGIN_CALLBACK_URL = "@Launcher_LOGIN_CALLBACK_URL@";
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
META_URL = "@Launcher_META_URL@";
LEGACY_FMLLIBS_BASE_URL = "@Launcher_LEGACY_FMLLIBS_BASE_URL@";
FMLLIBS_BASE_URL = "@Launcher_FMLLIBS_BASE_URL@";
GLFW_LIBRARY_NAME = "@Launcher_GLFW_LIBRARY_NAME@";
OPENAL_LIBRARY_NAME = "@Launcher_OPENAL_LIBRARY_NAME@";

View file

@ -54,7 +54,6 @@ class Config {
QString LAUNCHER_GIT;
QString LAUNCHER_APPID;
QString LAUNCHER_SVGFILENAME;
QString LAUNCHER_ENVNAME;
/// The major version number.
int VERSION_MAJOR;
@ -129,12 +128,7 @@ class Config {
QString NEWS_OPEN_URL;
/**
* URL that gets opened when the user clicks 'Launcher Help'
*/
QString WIKI_URL;
/**
* URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help in a dialog window
* URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help
*/
QString HELP_URL;
@ -175,10 +169,10 @@ class Config {
QString DEFAULT_RESOURCE_BASE = "https://resources.download.minecraft.net/";
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
QString LEGACY_FMLLIBS_BASE_URL;
QString FMLLIBS_BASE_URL;
QString TRANSLATION_FILES_URL;
QString FTB_API_BASE_URL = "https://api.feed-the-beast.com/v1/modpacks/public";
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
QString LEGACY_FTB_CDN_BASE_URL = "https://dist.creeper.host/FTB2/";

View file

@ -0,0 +1,163 @@
#
# Function to set compiler warnings with reasonable defaults at the project level.
# Taken from https://github.com/aminya/project_options/blob/main/src/CompilerWarnings.cmake
# under the folowing license:
#
# MIT License
#
# Copyright (c) 2022-2100 Amin Yahyaabadi
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
include_guard()
function(_set_project_warnings_add_target_link_option TARGET OPTIONS)
target_link_options(${_project_name} INTERFACE ${OPTIONS})
endfunction()
# Set the compiler warnings
#
# https://clang.llvm.org/docs/DiagnosticsReference.html
# https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md
function(
set_project_warnings
_project_name
MSVC_WARNINGS
CLANG_WARNINGS
GCC_WARNINGS
)
if("${MSVC_WARNINGS}" STREQUAL "")
set(MSVC_WARNINGS
/W4 # Baseline reasonable warnings
/w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data
/w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
/w14263 # 'function': member function does not override any base class virtual member function
/w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not
# be destructed correctly
/w14287 # 'operator': unsigned/negative constant mismatch
/we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside
# the for-loop scope
/w14296 # 'operator': expression is always 'boolean_value'
/w14311 # 'variable': pointer truncation from 'type1' to 'type2'
/w14545 # expression before comma evaluates to a function which is missing an argument list
/w14546 # function call before comma missing argument list
/w14547 # 'operator': operator before comma has no effect; expected operator with side-effect
/w14549 # 'operator': operator before comma has no effect; did you intend 'operator'?
/w14555 # expression has no effect; expected expression with side- effect
/w14619 # pragma warning: there is no warning number 'number'
/w14640 # Enable warning on thread un-safe static member initialization
/w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior.
/w14905 # wide string literal cast to 'LPSTR'
/w14906 # string literal cast to 'LPWSTR'
/w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied
/permissive- # standards conformance mode for MSVC compiler.
/we4062 # forbid omitting a possible value of an enum in a switch statement
)
endif()
if("${CLANG_WARNINGS}" STREQUAL "")
set(CLANG_WARNINGS
-Wall
-Wextra # reasonable and standard
-Wshadow # warn the user if a variable declaration shadows one from a parent context
-Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps
# catch hard to track down memory errors
-Wold-style-cast # warn for c-style casts
-Wcast-align # warn for potential performance problem casts
-Wunused # warn on anything being unused
-Woverloaded-virtual # warn if you overload (not override) a virtual function
-Wpedantic # warn if non-standard C++ is used
-Wconversion # warn on type conversions that may lose data
-Wsign-conversion # warn on sign conversions
-Wnull-dereference # warn if a null dereference is detected
-Wdouble-promotion # warn if float is implicit promoted to double
-Wformat=2 # warn on security issues around functions that format output (ie printf)
-Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation
# -Wgnu-zero-variadic-macro-arguments (part of -pedantic) is triggered by every qCDebug() call and therefore results
# in a lot of noise. This warning is only notifying us that clang is emulating the GCC behaviour
# instead of the exact standard wording so we can safely ignore it
-Wno-gnu-zero-variadic-macro-arguments
-Werror=switch # forbid omitting a possible value of an enum in a switch statement
)
endif()
if("${GCC_WARNINGS}" STREQUAL "")
set(GCC_WARNINGS
${CLANG_WARNINGS}
-Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist
-Wduplicated-cond # warn if if / else chain has duplicated conditions
-Wduplicated-branches # warn if if / else branches have duplicated code
-Wlogical-op # warn about logical operations being used where bitwise were probably wanted
-Wuseless-cast # warn if you perform a cast to the same type
-Werror=switch # forbid omitting a possible value of an enum in a switch statement
)
endif()
if(MSVC)
set(PROJECT_WARNINGS_CXX ${MSVC_WARNINGS})
elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
set(PROJECT_WARNINGS_CXX ${CLANG_WARNINGS})
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(PROJECT_WARNINGS_CXX ${GCC_WARNINGS})
else()
message(AUTHOR_WARNING "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'")
# TODO support Intel compiler
endif()
# Add C warnings
set(PROJECT_WARNINGS_C "${PROJECT_WARNINGS_CXX}")
list(
REMOVE_ITEM
PROJECT_WARNINGS_C
-Wnon-virtual-dtor
-Wold-style-cast
-Woverloaded-virtual
-Wuseless-cast
-Wextra-semi
-Werror=switch # forbid omitting a possible value of an enum in a switch statement
)
target_compile_options(
${_project_name}
INTERFACE # C++ warnings
$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_WARNINGS_CXX}>
# C warnings
$<$<COMPILE_LANGUAGE:C>:${PROJECT_WARNINGS_C}>
)
# If we are using the compiler as a linker driver pass the warnings to it
# (most useful when using LTO or warnings as errors)
if(CMAKE_CXX_LINK_EXECUTABLE MATCHES "^<CMAKE_CXX_COMPILER>")
_set_project_warnings_add_target_link_option(
${_project_name} "$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_WARNINGS_CXX}>"
)
endif()
if(CMAKE_C_LINK_EXECUTABLE MATCHES "^<CMAKE_C_COMPILER>")
_set_project_warnings_add_target_link_option(
${_project_name} "$<$<COMPILE_LANGUAGE:C>:${PROJECT_WARNINGS_C}>"
)
endif()
endfunction()

View file

@ -7,7 +7,7 @@
<key>NSMicrophoneUsageDescription</key>
<string>A Minecraft mod wants to access your microphone.</string>
<key>NSDownloadsFolderUsageDescription</key>
<string>${Launcher_DisplayName} uses access to your Downloads folder to help you more quickly add mods that can't be automatically downloaded to your instance. You can change where ${Launcher_DisplayName} scans for downloaded mods in Settings or the prompt that appears.</string>
<string>Prism uses access to your Downloads folder to help you more quickly add mods that can't be automatically downloaded to your instance. You can change where Prism scans for downloaded mods in Settings or the prompt that appears.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Minecraft uses the local network to find and connect to LAN servers.</string>
<key>NSPrincipalClass</key>
@ -61,7 +61,7 @@
<string>mrpack</string>
</array>
<key>CFBundleTypeName</key>
<string>${Launcher_DisplayName} instance</string>
<string>Prism Launcher instance</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>TEXT</string>
@ -87,11 +87,10 @@
</dict>
<dict>
<key>CFBundleURLName</key>
<string>${Launcher_Name}</string>
<string>Prismlauncher</string>
<key>CFBundleURLSchemes</key>
<array>
<string>prismlauncher</string>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
</array>
</dict>
</array>

View file

@ -3,11 +3,11 @@
"libnbtplusplus": {
"flake": false,
"locked": {
"lastModified": 1772016279,
"narHash": "sha256-7itkptyjoRcXfGLwg1/jxajetZ3a4mDc66+w4X6yW8s=",
"lastModified": 1744811532,
"narHash": "sha256-qhmjaRkt+O7A+gu6HjUkl7QzOEb4r8y8vWZMG2R/C6o=",
"owner": "PrismLauncher",
"repo": "libnbtplusplus",
"rev": "687e43031df0dc641984b4256bcca50d5b3f7de3",
"rev": "531449ba1c930c98e0bcf5d332b237a8566f9d78",
"type": "github"
},
"original": {
@ -18,15 +18,15 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1776169885,
"narHash": "sha256-Gk2T0tDDDAs319hp/ak+bAIUG5bPMvnNEjPV8CS86Fg=",
"rev": "4bd9165a9165d7b5e33ae57f3eecbcb28fb231c9",
"lastModified": 1766473571,
"narHash": "sha256-QvjEJNgMVuOootbR+DEfbiW+zSK57U32CE0jmVdcNjQ=",
"rev": "76701a179d3a98b07653e2b0409847499b2a07d3",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre980183.4bd9165a9165/nixexprs.tar.xz"
"url": "https://releases.nixos.org/nixos/25.11/nixos-25.11.2403.76701a179d3a/nixexprs.tar.xz"
},
"original": {
"type": "tarball",
"url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"
"url": "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz"
}
},
"root": {

View file

@ -9,7 +9,7 @@
};
inputs = {
nixpkgs.url = "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz";
nixpkgs.url = "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz";
libnbtplusplus = {
url = "github:PrismLauncher/libnbtplusplus";
@ -42,7 +42,7 @@
let
pkgs = nixpkgsFor.${system};
llvm = pkgs.llvmPackages_22;
llvm = pkgs.llvmPackages_19;
in
{
@ -85,9 +85,7 @@
let
pkgs = nixpkgsFor.${system};
llvm = pkgs.llvmPackages_22;
python = pkgs.python3;
mkShell = pkgs.mkShell.override { inherit (llvm) stdenv; };
llvm = pkgs.llvmPackages_19;
packages' = self.packages.${system};
@ -133,36 +131,18 @@
in
{
default = mkShell {
default = pkgs.mkShell {
name = "prism-launcher";
inputsFrom = [ packages'.prismlauncher-unwrapped ];
packages = [
pkgs.ccache
packages = with pkgs; [
ccache
llvm.clang-tools
python # NOTE(@getchoo): Required for run-clang-tidy, etc.
(pkgs.stdenvNoCC.mkDerivation {
pname = "clang-tidy-diff";
inherit (llvm.clang) version;
nativeBuildInputs = [
pkgs.installShellFiles
python.pkgs.wrapPython
];
dontUnpack = true;
dontConfigure = true;
dontBuild = true;
postInstall = "installBin ${llvm.libclang.python}/share/clang/clang-tidy-diff.py";
postFixup = "wrapPythonPrograms";
})
];
cmakeBuildType = "Debug";
cmakeFlags = [ "-GNinja" ] ++ packages'.prismlauncher-unwrapped.cmakeFlags;
cmakeFlags = [ "-GNinja" ] ++ packages'.prismlauncher.cmakeFlags;
dontFixCmake = true;
shellHook = ''
@ -185,25 +165,17 @@
formatter = forAllSystems (system: nixpkgsFor.${system}.nixfmt-rfc-style);
overlays.default =
final: prev:
let
llvm = final.llvmPackages_22 or prev.llvmPackages_22;
in
{
prismlauncher-unwrapped = prev.callPackage ./nix/unwrapped.nix {
inherit (llvm) stdenv;
inherit
libnbtplusplus
self
;
};
prismlauncher = final.callPackage ./nix/wrapper.nix { };
overlays.default = final: prev: {
prismlauncher-unwrapped = prev.callPackage ./nix/unwrapped.nix {
inherit
libnbtplusplus
self
;
};
prismlauncher = final.callPackage ./nix/wrapper.nix { };
};
packages = forAllSystems (
system:

View file

@ -126,11 +126,12 @@
#include <LocalPeer.h>
#include <stdlib.h>
#include <sys.h>
#include "SysInfo.h"
#ifdef Q_OS_LINUX
#include <dlfcn.h>
#include "LibraryUtils.h"
#include "MangoHud.h"
#include "gamemode_client.h"
#endif
@ -157,6 +158,7 @@
#endif
#include <windows.h>
#include <QStyleHints>
#include "console/WindowsConsole.h"
#endif
#include "console/Console.h"
@ -290,9 +292,21 @@ std::tuple<QDateTime, QString, QString, QString, QString> read_lock_File(const Q
Application::Application(int& argc, char** argv) : QApplication(argc, argv)
{
#if defined Q_OS_WIN32
// attach the parent console if stdout not already captured
if (AttachWindowsConsole()) {
consoleAttached = true;
if (auto err = EnableAnsiSupport(); !err) {
isANSIColorConsole = true;
} else {
std::cout << "Error setting up ansi console" << err.message() << std::endl;
}
}
#else
if (console::isConsole()) {
isANSIColorConsole = true;
}
#endif
setOrganizationName(BuildConfig.LAUNCHER_NAME);
setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN);
@ -318,7 +332,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
{ { "o", "offline" }, "Launch offline, with given player name (only valid in combination with --launch)", "offline" },
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
{ "show-window", "Show the main launcher window (useful in combination with --launch)" },
{ { "I", "import" }, "Import instance or resource from specified local path or URL", "url" },
{ "show", "Opens the window for the specified instance (by instance ID)", "show" } });
// Has to be positional for some OS to handle that properly
@ -334,13 +347,12 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_worldToJoin = parser.value("world");
m_profileToUse = parser.value("profile");
if (parser.isSet("offline")) {
m_launchOffline = true;
m_offline = true;
m_offlineName = parser.value("offline");
}
m_liveCheck = parser.isSet("alive");
m_instanceIdToShowWindowOf = parser.value("show");
m_showMainWindow = parser.isSet("show-window");
for (auto url : parser.values("import")) {
m_urlsToImport.append(normalizeImportUrl(url));
@ -352,7 +364,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
}
// error if --launch is missing with --server or --profile
if ((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty() || !m_profileToUse.isEmpty() || m_launchOffline) &&
if ((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty() || !m_profileToUse.isEmpty() || m_offline) &&
m_instanceIdToLaunch.isEmpty()) {
std::cerr << "--server, --profile and --offline can only be used in combination with --launch!" << std::endl;
m_status = Application::Failed;
@ -394,7 +406,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
} else {
QDir foo;
if (DesktopServices::isSnap()) {
foo = QDir(qEnvironmentVariable("SNAP_USER_COMMON"));
foo = QDir(getenv("SNAP_USER_COMMON"));
} else {
foo = QDir(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), ".."));
}
@ -480,7 +492,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
if (!m_profileToUse.isEmpty()) {
launch.args["profile"] = m_profileToUse;
}
if (m_launchOffline) {
if (m_offline) {
launch.args["offline_enabled"] = "true";
launch.args["offline_name"] = m_offlineName;
}
@ -514,13 +526,12 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
logFile = std::unique_ptr<QFile>(new QFile(logBase.arg(0)));
if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
showFatalErrorMessage("The launcher data folder is not writable!",
QString("The launcher couldn't create a log file - %1.\n"
QString("The launcher couldn't create a log file - the data folder is not writable.\n"
"\n"
"Make sure you have write permissions to the data folder.\n"
"(%2)\n"
"(%1)\n"
"\n"
"The launcher cannot continue until you fix this problem.")
.arg(logFile->errorString())
.arg(dataPath));
return;
}
@ -627,11 +638,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
if (check.write(payload) == payload.size()) {
check.close();
} else {
qWarning() << "Could not write into" << liveCheckFile << "error:" << check.errorString();
qWarning() << "Could not write into" << liveCheckFile << "!";
check.remove(); // also closes file!
}
} else {
qWarning() << "Could not open" << liveCheckFile << "for writing:" << check.errorString();
qWarning() << "Could not open" << liveCheckFile << "for writing!";
}
}
@ -733,9 +744,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// Memory
m_settings->registerSetting({ "MinMemAlloc", "MinMemoryAlloc" }, 512);
m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, SysInfo::defaultMaxJvmMem());
m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, SysInfo::suitableMaxMem());
m_settings->registerSetting("PermGen", 128);
m_settings->registerSetting("LowMemWarning", true);
// Java Settings
m_settings->registerSetting("JavaPath", "");
@ -778,7 +788,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("ModMetadataDisabled", false);
m_settings->registerSetting("ModDependenciesDisabled", false);
m_settings->registerSetting("SkipModpackUpdatePrompt", false);
m_settings->registerSetting("ShowModIncompat", false);
// Minecraft offline player name
m_settings->registerSetting("LastOfflinePlayerName", "");
@ -819,8 +828,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("UpdateDialogGeometry", "");
m_settings->registerSetting("NewsGeometry", "");
m_settings->registerSetting("ModDownloadGeometry", "");
m_settings->registerSetting("RPDownloadGeometry", "");
m_settings->registerSetting("TPDownloadGeometry", "");
@ -855,23 +862,25 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
}
}
{
auto resetIfInvalid = [this](const Setting* setting) {
if (const QUrl url(setting->get().toString()); !url.isValid() || (url.scheme() != "http" && url.scheme() != "https")) {
m_settings->reset(setting->id());
}
};
// Meta URL
resetIfInvalid(m_settings->registerSetting("MetaURLOverride", "").get());
m_settings->registerSetting("MetaURLOverride", "");
QUrl metaUrl(m_settings->get("MetaURLOverride").toString());
// get rid of invalid meta urls
if (!metaUrl.isValid() || (metaUrl.scheme() != "http" && metaUrl.scheme() != "https"))
m_settings->reset("MetaURLOverride");
// Resource URL
resetIfInvalid(m_settings->registerSetting({ "ResourceURLOverride", "ResourceURL" }, "").get());
m_settings->registerSetting("ResourceURL", BuildConfig.DEFAULT_RESOURCE_BASE);
// Legacy FML libs URL
resetIfInvalid(m_settings->registerSetting("LegacyFMLLibsURLOverride", "").get());
QUrl resourceUrl(m_settings->get("ResourceURL").toString());
// get rid of invalid resource urls
if (!resourceUrl.isValid() || (resourceUrl.scheme() != "http" && resourceUrl.scheme() != "https"))
m_settings->reset("ResourceURL");
}
m_settings->registerSetting("MetaRefreshOnLaunch", true);
m_settings->registerSetting("CloseAfterLaunch", false);
m_settings->registerSetting("QuitAfterGameStop", false);
@ -891,7 +900,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->set("FlameKeyOverride", flameKey);
m_settings->reset("CFKeyOverride");
}
m_settings->registerSetting("FallbackMRBlockedMods", true);
m_settings->registerSetting("ModrinthToken", "");
m_settings->registerSetting("UserAgentOverride", "");
@ -903,7 +911,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// Init page provider
{
m_globalSettingsProvider = std::make_unique<GenericPageProvider>(tr("Settings"));
m_globalSettingsProvider = std::make_shared<GenericPageProvider>(tr("Settings"));
m_globalSettingsProvider->addPage<LauncherPage>();
m_globalSettingsProvider->addPage<LanguagePage>();
m_globalSettingsProvider->addPage<AppearancePage>();
@ -984,7 +992,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
if (FS::checkProblemticPathJava(QDir(instDir))) {
qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!";
}
m_instances.reset(new InstanceList(m_settings.get(), instDir, this));
m_instances.reset(new InstanceList(m_settings, instDir, this));
connect(InstDirSetting.get(), &Setting::SettingChanged, m_instances.get(), &InstanceList::on_InstFolderChanged);
qInfo() << "Loading Instances...";
m_instances->loadList();
@ -1018,7 +1026,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_metacache->addBase("translations", QDir("translations").absolutePath());
m_metacache->addBase("meta", QDir("meta").absolutePath());
m_metacache->addBase("java", QDir("cache/java").absolutePath());
m_metacache->addBase("feed", QDir("cache/feed").absolutePath());
m_metacache->Load();
qInfo() << "<> Cache initialized.";
}
@ -1031,12 +1038,12 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_profilers.insert("jvisualvm", std::shared_ptr<BaseProfilerFactory>(new JVisualVMFactory()));
m_profilers.insert("generic", std::shared_ptr<BaseProfilerFactory>(new GenericProfilerFactory()));
for (auto profiler : m_profilers.values()) {
profiler->registerSettings(m_settings.get());
profiler->registerSettings(m_settings);
}
// Create the MCEdit thing... why is this here?
{
m_mcedit.reset(new MCEditTool(m_settings.get()));
m_mcedit.reset(new MCEditTool(m_settings));
}
#ifdef Q_OS_MACOS
@ -1357,11 +1364,8 @@ void Application::performMainStartupAction()
qDebug() << " Launching with account" << m_profileToUse;
}
launch(inst, m_launchOffline ? LaunchMode::Offline : LaunchMode::Normal, targetToJoin, accountToUse, m_offlineName);
if (!m_showMainWindow) {
return;
}
launch(inst, !m_offline, false, targetToJoin, accountToUse, m_offlineName);
return;
}
}
if (!m_instanceIdToShowWindowOf.isEmpty()) {
@ -1414,6 +1418,16 @@ Application::~Application()
{
// Shut down logger by setting the logger function to nothing
qInstallMessageHandler(nullptr);
#if defined Q_OS_WIN32
// Detach from Windows console
if (consoleAttached) {
fclose(stdout);
fclose(stdin);
fclose(stderr);
FreeConsole();
}
#endif
}
void Application::messageReceived(const QByteArray& message)
@ -1455,7 +1469,7 @@ void Application::messageReceived(const QByteArray& message)
bool offline = received.args["offline_enabled"] == "true";
QString offlineName = received.args["offline_name"];
BaseInstance* instance;
InstancePtr instance;
if (!id.isEmpty()) {
instance = instances()->getInstanceById(id);
if (!instance) {
@ -1483,23 +1497,23 @@ void Application::messageReceived(const QByteArray& message)
}
}
launch(instance, offline ? LaunchMode::Offline : LaunchMode::Normal, serverObject, accountObject, offlineName);
launch(instance, !offline, false, serverObject, accountObject, offlineName);
} else {
qWarning() << "Received invalid message" << message;
}
}
TranslationsModel* Application::translations()
std::shared_ptr<TranslationsModel> Application::translations()
{
return m_translations.get();
return m_translations;
}
JavaInstallList* Application::javalist()
std::shared_ptr<JavaInstallList> Application::javalist()
{
if (!m_javalist) {
m_javalist.reset(new JavaInstallList());
}
return m_javalist.get();
return m_javalist;
}
QIcon Application::logo()
@ -1518,8 +1532,9 @@ bool Application::openJsonEditor(const QString& filename)
}
}
bool Application::launch(BaseInstance* instance,
LaunchMode mode,
bool Application::launch(InstancePtr instance,
bool online,
bool demo,
MinecraftTarget::Ptr targetToJoin,
MinecraftAccountPtr accountToUse,
const QString& offlineName)
@ -1538,7 +1553,8 @@ bool Application::launch(BaseInstance* instance,
auto& controller = extras.controller;
controller.reset(new LaunchController());
controller->setInstance(instance);
controller->setLaunchMode(mode);
controller->setOnline(online);
controller->setDemo(demo);
controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
controller->setTargetToJoin(targetToJoin);
controller->setAccountToUse(accountToUse);
@ -1548,7 +1564,9 @@ bool Application::launch(BaseInstance* instance,
} else if (m_mainWindow) {
controller->setParentWidget(m_mainWindow);
}
connect(controller.get(), &LaunchController::finished, this, &Application::controllerFinished);
connect(controller.get(), &LaunchController::succeeded, this, &Application::controllerSucceeded);
connect(controller.get(), &LaunchController::failed, this, &Application::controllerFailed);
connect(controller.get(), &LaunchController::aborted, this, [this] { controllerFailed(tr("Aborted")); });
addRunningInstance();
QMetaObject::invokeMethod(controller.get(), &Task::start, Qt::QueuedConnection);
return true;
@ -1562,7 +1580,7 @@ bool Application::launch(BaseInstance* instance,
return false;
}
bool Application::kill(BaseInstance* instance)
bool Application::kill(InstancePtr instance)
{
if (!instance->isRunning()) {
qWarning() << "Attempted to kill instance" << instance->id() << ", which isn't running.";
@ -1571,7 +1589,7 @@ bool Application::kill(BaseInstance* instance)
QMutexLocker locker(&m_instanceExtrasMutex);
auto& extras = m_instanceExtras[instance->id()];
// NOTE: copy of the shared pointer keeps it alive
auto& controller = extras.controller;
auto controller = extras.controller;
locker.unlock();
if (controller) {
return controller->abort();
@ -1620,7 +1638,7 @@ void Application::updateIsRunning(bool running)
m_updateRunning = running;
}
void Application::controllerFinished()
void Application::controllerSucceeded()
{
auto controller = qobject_cast<LaunchController*>(sender());
if (!controller)
@ -1628,11 +1646,10 @@ void Application::controllerFinished()
auto id = controller->id();
QMutexLocker locker(&m_instanceExtrasMutex);
auto& extras = m_instanceExtras.at(id);
auto& extras = m_instanceExtras[id];
const bool wasSuccessful = controller->wasSuccessful();
// on success, do...
if (wasSuccessful && controller->instance()->settings()->get("AutoCloseConsole").toBool()) {
if (controller->instance()->settings()->get("AutoCloseConsole").toBool()) {
if (extras.window) {
QMetaObject::invokeMethod(extras.window, &QWidget::close, Qt::QueuedConnection);
}
@ -1642,8 +1659,29 @@ void Application::controllerFinished()
// quit when there are no more windows.
if (shouldExitNow()) {
m_status = wasSuccessful ? Succeeded : Failed;
exit(wasSuccessful ? 0 : 1);
m_status = Status::Succeeded;
exit(0);
}
}
void Application::controllerFailed(const QString& error)
{
Q_UNUSED(error);
auto controller = qobject_cast<LaunchController*>(sender());
if (!controller)
return;
auto id = controller->id();
QMutexLocker locker(&m_instanceExtrasMutex);
auto& extras = m_instanceExtras[id];
// on failure, do... nothing
extras.controller.reset();
subRunningInstance();
// quit when there are no more windows.
if (shouldExitNow()) {
m_status = Status::Failed;
exit(1);
}
}
@ -1700,7 +1738,7 @@ ViewLogWindow* Application::showLogWindow()
return m_viewLogWindow;
}
InstanceWindow* Application::showInstanceWindow(BaseInstance* instance, QString page)
InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString page)
{
if (!instance)
return nullptr;
@ -1812,22 +1850,22 @@ void Application::updateProxySettings(QString proxyTypeStr, QString addr, int po
qDebug() << proxyDesc;
}
HttpMetaCache* Application::metacache()
shared_qobject_ptr<HttpMetaCache> Application::metacache()
{
return m_metacache.get();
return m_metacache;
}
QNetworkAccessManager* Application::network()
shared_qobject_ptr<QNetworkAccessManager> Application::network()
{
return m_network.get();
return m_network;
}
Meta::Index* Application::metadataIndex()
shared_qobject_ptr<Meta::Index> Application::metadataIndex()
{
if (!m_metadataIndex) {
m_metadataIndex.reset(new Meta::Index());
}
return m_metadataIndex.get();
return m_metadataIndex;
}
void Application::updateCapabilities()
@ -1842,7 +1880,7 @@ void Application::updateCapabilities()
if (gamemode_query_status() >= 0)
m_capabilities |= SupportsGameMode;
if (!LibraryUtils::findMangoHud().isEmpty())
if (!MangoHud::getLibraryString().isEmpty())
m_capabilities |= SupportsMangoHud;
#endif
}
@ -1850,8 +1888,8 @@ void Application::updateCapabilities()
void Application::detectLibraries()
{
#ifdef Q_OS_LINUX
m_detectedGLFWPath = LibraryUtils::find(BuildConfig.GLFW_LIBRARY_NAME);
m_detectedOpenALPath = LibraryUtils::find(BuildConfig.OPENAL_LIBRARY_NAME);
m_detectedGLFWPath = MangoHud::findLibrary(BuildConfig.GLFW_LIBRARY_NAME);
m_detectedOpenALPath = MangoHud::findLibrary(BuildConfig.OPENAL_LIBRARY_NAME);
qDebug() << "Detected native libraries:" << m_detectedGLFWPath << m_detectedOpenALPath;
#endif
}
@ -1958,7 +1996,7 @@ bool Application::handleDataMigration(const QString& currentData,
auto setDoNotMigrate = [&nomigratePath] {
QFile file(nomigratePath);
if (!file.open(QIODevice::WriteOnly)) {
qWarning() << "setDoNotMigrate failed; Failed to open file" << file.fileName() << "for writing:" << file.errorString();
qWarning() << "setDoNotMigrate failed; Failed to open file '" << file.fileName() << "' for writing!";
}
};
@ -2012,7 +2050,7 @@ void Application::triggerUpdateCheck()
}
}
QUrl Application::normalizeImportUrl(const QString& url)
QUrl Application::normalizeImportUrl(QString const& url)
{
auto local_file = QFileInfo(url);
if (local_file.exists()) {

View file

@ -37,8 +37,6 @@
#pragma once
#include <memory>
#include <QApplication>
#include <QDateTime>
#include <QDebug>
@ -46,10 +44,12 @@
#include <QIcon>
#include <QMutex>
#include <QUrl>
#include <memory>
#include "QObjectPtr.h"
#include <BaseInstance.h>
#include "minecraft/auth/MinecraftAccount.h"
#include "launch/LogModel.h"
#include "minecraft/launch/MinecraftTarget.h"
class LaunchController;
class LocalPeer;
@ -74,12 +74,6 @@ class ITheme;
class MCEditTool;
class ThemeManager;
class IconTheme;
class BaseInstance;
class LogModel;
struct MinecraftTarget;
class MinecraftAccount;
namespace Meta {
class Index;
@ -97,6 +91,7 @@ class Index;
#define APPLICATION_DYN (dynamic_cast<Application*>(QCoreApplication::instance()))
class Application : public QApplication {
// friends for the purpose of limiting access to deprecated stuff
Q_OBJECT
public:
enum Status { StartingUp, Failed, Succeeded, Initialized };
@ -117,7 +112,7 @@ class Application : public QApplication {
bool event(QEvent* event) override;
SettingsObject* settings() const { return m_settings.get(); }
std::shared_ptr<SettingsObject> settings() const { return m_settings; }
qint64 timeSinceStart() const { return m_startTime.msecsTo(QDateTime::currentDateTime()); }
@ -125,21 +120,21 @@ class Application : public QApplication {
ThemeManager* themeManager() { return m_themeManager.get(); }
ExternalUpdater* updater() { return m_updater.get(); }
shared_qobject_ptr<ExternalUpdater> updater() { return m_updater; }
void triggerUpdateCheck();
TranslationsModel* translations();
std::shared_ptr<TranslationsModel> translations();
JavaInstallList* javalist();
std::shared_ptr<JavaInstallList> javalist();
InstanceList* instances() const { return m_instances.get(); }
std::shared_ptr<InstanceList> instances() const { return m_instances; }
IconList* icons() const { return m_icons.get(); }
std::shared_ptr<IconList> icons() const { return m_icons; }
MCEditTool* mcedit() const { return m_mcedit.get(); }
AccountList* accounts() const { return m_accounts.get(); }
shared_qobject_ptr<AccountList> accounts() const { return m_accounts; }
Status status() const { return m_status; }
@ -147,11 +142,11 @@ class Application : public QApplication {
void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
QNetworkAccessManager* network();
shared_qobject_ptr<QNetworkAccessManager> network();
HttpMetaCache* metacache();
shared_qobject_ptr<HttpMetaCache> metacache();
Meta::Index* metadataIndex();
shared_qobject_ptr<Meta::Index> metadataIndex();
void updateCapabilities();
@ -187,7 +182,7 @@ class Application : public QApplication {
*/
bool openJsonEditor(const QString& filename);
InstanceWindow* showInstanceWindow(BaseInstance* instance, QString page = QString());
InstanceWindow* showInstanceWindow(InstancePtr instance, QString page = QString());
MainWindow* showMainWindow(bool minimized = false);
ViewLogWindow* showLogWindow();
@ -199,7 +194,7 @@ class Application : public QApplication {
bool updaterEnabled();
QString updaterBinaryName();
QUrl normalizeImportUrl(const QString& url);
QUrl normalizeImportUrl(QString const& url);
signals:
void updateAllowedChanged(bool status);
@ -214,18 +209,20 @@ class Application : public QApplication {
#endif
public slots:
bool launch(BaseInstance* instance,
LaunchMode mode = LaunchMode::Normal,
std::shared_ptr<MinecraftTarget> targetToJoin = nullptr,
shared_qobject_ptr<MinecraftAccount> accountToUse = nullptr,
bool launch(InstancePtr instance,
bool online = true,
bool demo = false,
MinecraftTarget::Ptr targetToJoin = nullptr,
MinecraftAccountPtr accountToUse = nullptr,
const QString& offlineName = QString());
bool kill(BaseInstance* instance);
bool kill(InstancePtr instance);
void closeCurrentWindow();
private slots:
void on_windowClose();
void messageReceived(const QByteArray& message);
void controllerFinished();
void controllerSucceeded();
void controllerFailed(const QString& error);
void setupWizardFinished(int status);
private:
@ -241,27 +238,23 @@ class Application : public QApplication {
void subRunningInstance();
bool shouldExitNow() const;
private:
QHash<QString, int> m_qsaveResources;
mutable QMutex m_qsaveResourcesMutex;
private:
QDateTime m_startTime;
std::unique_ptr<QNetworkAccessManager> m_network;
shared_qobject_ptr<QNetworkAccessManager> m_network;
std::unique_ptr<ExternalUpdater> m_updater;
std::unique_ptr<AccountList> m_accounts;
shared_qobject_ptr<ExternalUpdater> m_updater;
shared_qobject_ptr<AccountList> m_accounts;
std::unique_ptr<HttpMetaCache> m_metacache;
std::unique_ptr<Meta::Index> m_metadataIndex;
shared_qobject_ptr<HttpMetaCache> m_metacache;
shared_qobject_ptr<Meta::Index> m_metadataIndex;
std::unique_ptr<SettingsObject> m_settings;
std::unique_ptr<InstanceList> m_instances;
std::unique_ptr<IconList> m_icons;
std::unique_ptr<JavaInstallList> m_javalist;
std::unique_ptr<TranslationsModel> m_translations;
std::unique_ptr<GenericPageProvider> m_globalSettingsProvider;
std::shared_ptr<SettingsObject> m_settings;
std::shared_ptr<InstanceList> m_instances;
std::shared_ptr<IconList> m_icons;
std::shared_ptr<JavaInstallList> m_javalist;
std::shared_ptr<TranslationsModel> m_translations;
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;
std::unique_ptr<MCEditTool> m_mcedit;
QSet<QString> m_features;
std::unique_ptr<ThemeManager> m_themeManager;
@ -278,10 +271,15 @@ class Application : public QApplication {
Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive;
#endif
#if defined Q_OS_WIN32
// used on Windows to attach the standard IO streams
bool consoleAttached = false;
#endif
// FIXME: attach to instances instead.
struct InstanceXtras {
InstanceWindow* window = nullptr;
std::unique_ptr<LaunchController> controller;
shared_qobject_ptr<LaunchController> controller;
};
std::map<QString, InstanceXtras> m_instanceExtras;
mutable QMutex m_instanceExtrasMutex;
@ -309,17 +307,20 @@ class Application : public QApplication {
QString m_serverToJoin;
QString m_worldToJoin;
QString m_profileToUse;
bool m_launchOffline = false;
bool m_offline = false;
QString m_offlineName;
bool m_liveCheck = false;
QList<QUrl> m_urlsToImport;
QString m_instanceIdToShowWindowOf;
bool m_showMainWindow = false;
std::unique_ptr<QFile> logFile;
std::unique_ptr<LogModel> logModel;
shared_qobject_ptr<LogModel> logModel;
public:
void addQSavePath(QString);
void removeQSavePath(QString);
bool checkQSavePath(QString);
private:
QHash<QString, int> m_qsaveResources;
mutable QMutex m_qsaveResourcesMutex;
};

View file

@ -45,7 +45,6 @@
#include "Application.h"
#include "Json.h"
#include "launch/LaunchTask.h"
#include "settings/INISettingsObject.h"
#include "settings/OverrideSetting.h"
#include "settings/Setting.h"
@ -54,7 +53,7 @@
#include "Commandline.h"
#include "FileSystem.h"
int getConsoleMaxLines(SettingsObject* settings)
int getConsoleMaxLines(SettingsObjectPtr settings)
{
auto lineSetting = settings->getSetting("ConsoleMaxLines");
bool conversionOk = false;
@ -66,14 +65,14 @@ int getConsoleMaxLines(SettingsObject* settings)
return maxLines;
}
bool shouldStopOnConsoleOverflow(SettingsObject* settings)
bool shouldStopOnConsoleOverflow(SettingsObjectPtr settings)
{
return settings->get("ConsoleOverflowStop").toBool();
}
BaseInstance::BaseInstance(SettingsObject* globalSettings, std::unique_ptr<SettingsObject> settings, const QString& rootDir) : QObject()
BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir) : QObject()
{
m_settings = std::move(settings);
m_settings = settings;
m_global_settings = globalSettings;
m_rootDir = rootDir;
@ -123,13 +122,10 @@ BaseInstance::BaseInstance(SettingsObject* globalSettings, std::unique_ptr<Setti
m_settings->registerSetting("ManagedPackName", "");
m_settings->registerSetting("ManagedPackVersionID", "");
m_settings->registerSetting("ManagedPackVersionName", "");
m_settings->registerSetting("ManagedPackURL", "");
m_settings->registerSetting("Profiler", "");
}
BaseInstance::~BaseInstance() {}
QString BaseInstance::getPreLaunchCommand()
{
return settings()->get("PreLaunchCommand").toString();
@ -339,11 +335,11 @@ QString BaseInstance::instanceRoot() const
return m_rootDir;
}
SettingsObject* BaseInstance::settings()
SettingsObjectPtr BaseInstance::settings()
{
loadSpecificSettings();
return m_settings.get();
return m_settings;
}
bool BaseInstance::canLaunch() const
@ -471,9 +467,9 @@ QStringList BaseInstance::extraArguments()
return Commandline::splitArgs(settings()->get("JvmArgs").toString());
}
LaunchTask* BaseInstance::getLaunchTask()
shared_qobject_ptr<LaunchTask> BaseInstance::getLaunchTask()
{
return m_launchProcess.get();
return m_launchProcess;
}
void BaseInstance::updateRuntimeContext()

View file

@ -64,6 +64,9 @@ class Task;
class LaunchTask;
class BaseInstance;
// pointer for lazy people
using InstancePtr = std::shared_ptr<BaseInstance>;
/// Shortcut saving target representations
enum class ShortcutTarget { Desktop, Applications, Other };
@ -75,8 +78,8 @@ struct ShortcutData {
};
/// Console settings
int getConsoleMaxLines(SettingsObject* settings);
bool shouldStopOnConsoleOverflow(SettingsObject* settings);
int getConsoleMaxLines(SettingsObjectPtr settings);
bool shouldStopOnConsoleOverflow(SettingsObjectPtr settings);
/*!
* \brief Base class for instances.
@ -86,11 +89,11 @@ bool shouldStopOnConsoleOverflow(SettingsObject* settings);
* To create a new instance type, create a new class inheriting from this class
* and implement the pure virtual functions.
*/
class BaseInstance : public QObject {
class BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance> {
Q_OBJECT
protected:
/// no-touchy!
BaseInstance(SettingsObject* globalSettings, std::unique_ptr<SettingsObject> settings, const QString& rootDir);
BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir);
public: /* types */
enum class Status {
@ -100,7 +103,7 @@ class BaseInstance : public QObject {
public:
/// virtual destructor to make sure the destruction is COMPLETE
virtual ~BaseInstance();
virtual ~BaseInstance() {}
virtual void saveNow() = 0;
@ -190,7 +193,7 @@ class BaseInstance : public QObject {
*
* \return A pointer to this instance's settings object.
*/
virtual SettingsObject* settings();
virtual SettingsObjectPtr settings();
/*!
* \brief Loads settings specific to an instance type if they're not already loaded.
@ -201,10 +204,10 @@ class BaseInstance : public QObject {
virtual QList<Task::Ptr> createUpdateTask() = 0;
/// returns a valid launcher (task container)
virtual LaunchTask* createLaunchTask(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) = 0;
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) = 0;
/// returns the current launch task (if any)
LaunchTask* getLaunchTask();
shared_qobject_ptr<LaunchTask> getLaunchTask();
/*!
* Create envrironment variables for running the instance
@ -283,7 +286,7 @@ class BaseInstance : public QObject {
protected:
void changeStatus(Status newStatus);
SettingsObject* globalSettings() const { return m_global_settings; }
SettingsObjectPtr globalSettings() const { return m_global_settings.lock(); }
bool isSpecificSettingsLoaded() const { return m_specific_settings_loaded; }
void setSpecificSettingsLoaded(bool loaded) { m_specific_settings_loaded = loaded; }
@ -294,7 +297,7 @@ class BaseInstance : public QObject {
*/
void propertiesChanged(BaseInstance* inst);
void launchTaskChanged(LaunchTask*);
void launchTaskChanged(shared_qobject_ptr<LaunchTask>);
void runningStatusChanged(bool running);
@ -307,10 +310,10 @@ class BaseInstance : public QObject {
protected: /* data */
QString m_rootDir;
std::unique_ptr<SettingsObject> m_settings;
SettingsObjectPtr m_settings;
// InstanceFlags m_flags;
bool m_isRunning = false;
std::unique_ptr<LaunchTask> m_launchProcess;
shared_qobject_ptr<LaunchTask> m_launchProcess;
QDateTime m_timeStarted;
RuntimeContext m_runtimeContext;
@ -320,7 +323,7 @@ class BaseInstance : public QObject {
bool m_hasUpdate = false;
bool m_hasBrokenVersion = false;
SettingsObject* m_global_settings;
SettingsObjectWeakPtr m_global_settings;
bool m_specific_settings_loaded = false;
};

View file

@ -24,7 +24,6 @@
*/
class BaseVersion {
public:
// TODO: delete
using Ptr = std::shared_ptr<BaseVersion>;
virtual ~BaseVersion() {}
/*!

View file

@ -63,7 +63,7 @@ class BaseVersionList : public QAbstractListModel {
* The task returned by this function should reset the model when it's done.
* \return A pointer to a task that reloads the version list.
*/
virtual Task::Ptr getLoadTask(bool forceReload = false) = 0;
virtual Task::Ptr getLoadTask() = 0;
//! Checks whether or not the list is loaded. If this returns false, the list should be
// loaded.

View file

@ -75,6 +75,9 @@ set(CORE_SOURCES
# RW lock protected map
RWStorage.h
# A variable that has an implicit default value and keeps track of changes
DefaultVariable.h
# a smart pointer wrapper intended for safer use with Qt signal/slot mechanisms
QObjectPtr.h
@ -107,9 +110,9 @@ if (UNIX AND NOT CYGWIN AND NOT APPLE)
set(CORE_SOURCES
${CORE_SOURCES}
# LibraryUtils
LibraryUtils.h
LibraryUtils.cpp
# MangoHud
MangoHud.h
MangoHud.cpp
)
endif()
@ -119,7 +122,6 @@ set(NET_SOURCES
net/ChecksumValidator.h
net/Download.cpp
net/Download.h
net/DummySink.h
net/FileSink.cpp
net/FileSink.h
net/HttpMetaCache.cpp
@ -244,13 +246,15 @@ set(MINECRAFT_SOURCES
minecraft/auth/steps/MSAStep.h
minecraft/auth/steps/XboxAuthorizationStep.cpp
minecraft/auth/steps/XboxAuthorizationStep.h
minecraft/auth/steps/XboxProfileStep.cpp
minecraft/auth/steps/XboxProfileStep.h
minecraft/auth/steps/XboxUserStep.cpp
minecraft/auth/steps/XboxUserStep.h
minecraft/update/AssetUpdateTask.h
minecraft/update/AssetUpdateTask.cpp
minecraft/update/LegacyFMLLibrariesTask.cpp
minecraft/update/LegacyFMLLibrariesTask.h
minecraft/update/FMLLibrariesTask.cpp
minecraft/update/FMLLibrariesTask.h
minecraft/update/FoldersTask.cpp
minecraft/update/FoldersTask.h
minecraft/update/LibrariesTask.cpp
@ -260,8 +264,6 @@ set(MINECRAFT_SOURCES
minecraft/launch/ClaimAccount.h
minecraft/launch/CreateGameFolders.cpp
minecraft/launch/CreateGameFolders.h
minecraft/launch/EnsureAvailableMemory.cpp
minecraft/launch/EnsureAvailableMemory.h
minecraft/launch/EnsureOfflineLibraries.cpp
minecraft/launch/EnsureOfflineLibraries.h
minecraft/launch/ModMinecraftJar.cpp
@ -529,11 +531,6 @@ set(FTB_SOURCES
modplatform/import_ftb/PackInstallTask.cpp
modplatform/import_ftb/PackHelpers.h
modplatform/import_ftb/PackHelpers.cpp
modplatform/ftb/FTBPackInstallTask.h
modplatform/ftb/FTBPackInstallTask.cpp
modplatform/ftb/FTBPackManifest.h
modplatform/ftb/FTBPackManifest.cpp
)
set(FLAME_SOURCES
@ -799,8 +796,6 @@ SET(LAUNCHER_SOURCES
ApplicationMessage.cpp
SysInfo.h
SysInfo.cpp
HardwareInfo.cpp
HardwareInfo.h
# console utils
console/Console.h
@ -817,6 +812,23 @@ SET(LAUNCHER_SOURCES
KonamiCode.h
KonamiCode.cpp
# Bundled resources
resources/backgrounds/backgrounds.qrc
resources/multimc/multimc.qrc
resources/pe_dark/pe_dark.qrc
resources/pe_light/pe_light.qrc
resources/pe_colored/pe_colored.qrc
resources/pe_blue/pe_blue.qrc
resources/breeze_dark/breeze_dark.qrc
resources/breeze_light/breeze_light.qrc
resources/OSX/OSX.qrc
resources/iOS/iOS.qrc
resources/flat/flat.qrc
resources/flat_white/flat_white.qrc
resources/documents/documents.qrc
resources/shaders/shaders.qrc
"${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_LogoQRC}"
# Icons
icons/MMCIcon.h
icons/MMCIcon.cpp
@ -886,7 +898,6 @@ SET(LAUNCHER_SOURCES
ui/themes/CatPainter.h
# Processes
LaunchMode.h
LaunchController.h
LaunchController.cpp
@ -997,13 +1008,6 @@ SET(LAUNCHER_SOURCES
ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp
ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h
ui/pages/modplatform/ftb/FtbFilterModel.cpp
ui/pages/modplatform/ftb/FtbFilterModel.h
ui/pages/modplatform/ftb/FtbListModel.cpp
ui/pages/modplatform/ftb/FtbListModel.h
ui/pages/modplatform/ftb/FtbPage.cpp
ui/pages/modplatform/ftb/FtbPage.h
ui/pages/modplatform/legacy_ftb/Page.cpp
ui/pages/modplatform/legacy_ftb/Page.h
ui/pages/modplatform/legacy_ftb/ListModel.h
@ -1067,8 +1071,6 @@ SET(LAUNCHER_SOURCES
ui/dialogs/ImportResourceDialog.h
ui/dialogs/MSALoginDialog.cpp
ui/dialogs/MSALoginDialog.h
ui/dialogs/NetworkJobFailedDialog.cpp
ui/dialogs/NetworkJobFailedDialog.h
ui/dialogs/NewComponentDialog.cpp
ui/dialogs/NewComponentDialog.h
ui/dialogs/NewInstanceDialog.cpp
@ -1235,7 +1237,6 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/modplatform/import_ftb/ImportFTBPage.ui
ui/pages/modplatform/ImportPage.ui
ui/pages/modplatform/OptionalModDialog.ui
ui/pages/modplatform/ftb/FtbPage.ui
ui/pages/modplatform/modrinth/ModrinthPage.ui
ui/pages/modplatform/technic/TechnicPage.ui
ui/widgets/CustomCommands.ui
@ -1251,7 +1252,6 @@ qt_wrap_ui(LAUNCHER_UI
ui/dialogs/ProfileSetupDialog.ui
ui/dialogs/ProgressDialog.ui
ui/dialogs/NewInstanceDialog.ui
ui/dialogs/NetworkJobFailedDialog.ui
ui/dialogs/NewComponentDialog.ui
ui/dialogs/NewsDialog.ui
ui/dialogs/ProfileSelectDialog.ui
@ -1290,7 +1290,6 @@ qt_add_resources(LAUNCHER_RESOURCES
resources/OSX/OSX.qrc
resources/iOS/iOS.qrc
resources/flat/flat.qrc
resources/flat_white/flat_white.qrc
resources/documents/documents.qrc
resources/shaders/shaders.qrc
"${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_LogoQRC}"
@ -1307,6 +1306,8 @@ if(WIN32)
set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC})
endif()
include(CompilerWarnings)
######## Precompiled Headers ###########
if(${Launcher_USE_PCH})
@ -1322,6 +1323,10 @@ endif()
# Add executable
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
set_project_warnings(Launcher_logic
"${Launcher_MSVC_WARNINGS}"
"${Launcher_CLANG_WARNINGS}"
"${Launcher_GCC_WARNINGS}")
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
@ -1330,6 +1335,7 @@ if(${Launcher_USE_PCH})
endif()
target_link_libraries(Launcher_logic
systeminfo
Launcher_murmur2
nbt++
${ZLIB_LIBRARIES}
@ -1406,6 +1412,8 @@ if(APPLE)
endif()
endif()
target_link_libraries(Launcher_logic)
add_executable(${Launcher_Name} MACOSX_BUNDLE WIN32 main.cpp ${LAUNCHER_RCS})
if(${Launcher_USE_PCH})
@ -1450,6 +1458,7 @@ if(Launcher_BUILD_UPDATER)
target_link_libraries(prism_updater_logic
${ZLIB_LIBRARIES}
systeminfo
BuildConfig
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::Core
@ -1494,6 +1503,10 @@ endif()
if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
# File link
add_library(filelink_logic STATIC ${LINKEXE_SOURCES})
set_project_warnings(filelink_logic
"${Launcher_MSVC_WARNINGS}"
"${Launcher_CLANG_WARNINGS}"
"${Launcher_GCC_WARNINGS}")
target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
@ -1502,6 +1515,7 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
endif()
target_link_libraries(filelink_logic
systeminfo
BuildConfig
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::Core
@ -1553,26 +1567,6 @@ if (UNIX AND APPLE AND Launcher_ENABLE_UPDATER)
install(DIRECTORY ${MACOSX_SPARKLE_DIR}/Sparkle.framework DESTINATION ${FRAMEWORK_DEST_DIR} USE_SOURCE_PERMISSIONS)
endif()
# Set basic compiler warning/error flags for all targets
get_property(Launcher_TARGETS DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY BUILDSYSTEM_TARGETS)
foreach(target ${Launcher_TARGETS})
message(STATUS "Enabling all warnings as errors for target '${target}'")
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
target_compile_options(${target} PRIVATE /W4 /WX /permissive-)
else()
target_compile_options(${target} PRIVATE -Wall -Wextra -Wpedantic -Werror)
endif()
endforeach()
# Disable some warnings in main launcher target due to being present in a lot of places. TODO: Fix them.
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
target_compile_options(Launcher_logic PRIVATE /wd4100) # C4100 - unused parameter
target_compile_options(${Launcher_Name} PRIVATE /wd4100) # C4100 - unused parameter
else()
target_compile_options(Launcher_logic PRIVATE -Wno-unused-parameter -Wno-missing-field-initializers)
target_compile_options(${Launcher_Name} PRIVATE -Wno-unused-parameter -Wno-missing-field-initializers)
endif()
#### The bundle mess! ####
# Bundle utilities are used to complete packages for different platforms - they add all the libraries that would otherwise be missing on the target system.
# NOTE: it seems that this absolutely has to be here, and nowhere else.

View file

@ -63,7 +63,7 @@ void DataMigrationTask::dryRunFinished()
void DataMigrationTask::dryRunAborted()
{
emitAborted();
emitFailed(tr("Aborted"));
}
void DataMigrationTask::copyFinished()
@ -81,5 +81,5 @@ void DataMigrationTask::copyFinished()
void DataMigrationTask::copyAborted()
{
emitAborted();
emitFailed(tr("Aborted"));
}

View file

@ -0,0 +1,23 @@
#pragma once
template <typename T>
class DefaultVariable {
public:
DefaultVariable(const T& value) { defaultValue = value; }
DefaultVariable<T>& operator=(const T& value)
{
currentValue = value;
is_default = currentValue == defaultValue;
is_explicit = true;
return *this;
}
operator const T&() const { return is_default ? defaultValue : currentValue; }
bool isDefault() const { return is_default; }
bool isExplicit() const { return is_explicit; }
private:
T currentValue;
T defaultValue;
bool is_default = true;
bool is_explicit = false;
};

View file

@ -282,9 +282,6 @@ bool copyFileAttributes(QString src, QString dst)
if (attrs == INVALID_FILE_ATTRIBUTES)
return false;
return SetFileAttributesW(dst.toStdWString().c_str(), attrs);
#else
Q_UNUSED(src);
Q_UNUSED(dst);
#endif
return true;
}
@ -595,7 +592,7 @@ void create_link::runPrivileged(const QString& offset)
}
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);
linkFileProcess->start();
@ -953,10 +950,7 @@ QString createShortcut(QString destination, QString target, QStringList args, QS
qWarning() << "Couldn't create directories within application";
return QString();
}
if (!info.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning() << "Failed to open file" << info.fileName() << "for writing:" << info.errorString();
return QString();
}
info.open(QIODevice::WriteOnly | QIODevice::Text);
QFile(icon).rename(resources.path() + "/Icon.icns");
@ -964,10 +958,7 @@ QString createShortcut(QString destination, QString target, QStringList args, QS
QString exec = binaryDir.path() + "/Run.command";
QFile f(exec);
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning() << "Failed to open file" << f.fileName() << "for writing:" << f.errorString();
return QString();
}
f.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream stream(&f);
auto argstring = quoteArgs(args, "\"", "\\\"");
@ -1010,7 +1001,7 @@ QString createShortcut(QString destination, QString target, QStringList args, QS
destination += ".desktop";
QFile f(destination);
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning() << "Failed to open file" << f.fileName() << "for writing:" << f.errorString();
qWarning() << "Failed to open file '" << f.fileName() << "' for writing!";
return QString();
}
QTextStream stream(&f);

View file

@ -172,8 +172,7 @@ int inf(QFile* source, std::function<bool(const QByteArray&)> handleBlock)
assert(ret != Z_STREAM_ERROR); /* state not clobbered */
switch (ret) {
case Z_NEED_DICT:
ret = Z_DATA_ERROR;
[[fallthrough]];
ret = Z_DATA_ERROR; /* and fall through */
case Z_DATA_ERROR:
case Z_MEM_ERROR:
(void)inflateEnd(&strm);

View file

@ -1,346 +0,0 @@
// 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 "HardwareInfo.h"
#include <QCoreApplication>
#include <QOffscreenSurface>
#include <QOpenGLFunctions>
#include <QProcessEnvironment>
#include "BuildConfig.h"
#ifndef Q_OS_MACOS
#include <QVulkanInstance>
#include <QVulkanWindow>
#endif
namespace {
bool vulkanInfo(QStringList& out)
{
if (!QProcessEnvironment::systemEnvironment()
.value(QStringLiteral("%1_DISABLE_GLVULKAN").arg(BuildConfig.LAUNCHER_ENVNAME))
.isEmpty()) {
return false;
}
#ifndef Q_OS_MACOS
QVulkanInstance inst;
if (!inst.create()) {
qWarning() << "Vulkan instance creation failed, VkResult:" << inst.errorCode();
out << "Couldn't get Vulkan device information";
return false;
}
QVulkanWindow window;
window.setVulkanInstance(&inst);
for (auto device : window.availablePhysicalDevices()) {
const auto supportedVulkanVersion = QVersionNumber(VK_API_VERSION_MAJOR(device.apiVersion), VK_API_VERSION_MINOR(device.apiVersion),
VK_API_VERSION_PATCH(device.apiVersion));
out << QString("Found Vulkan device: %1 (API version %2)").arg(device.deviceName).arg(supportedVulkanVersion.toString());
}
#endif
return true;
}
bool openGlInfo(QStringList& out)
{
if (!QProcessEnvironment::systemEnvironment()
.value(QStringLiteral("%1_DISABLE_GLVULKAN").arg(BuildConfig.LAUNCHER_ENVNAME))
.isEmpty()) {
return false;
}
QOpenGLContext ctx;
if (!ctx.create()) {
qWarning() << "OpenGL context creation failed";
out << "Couldn't get OpenGL device information";
return false;
}
QOffscreenSurface surface;
surface.create();
ctx.makeCurrent(&surface);
auto* f = ctx.functions();
f->initializeOpenGLFunctions();
auto toQString = [](const GLubyte* str) { return QString(reinterpret_cast<const char*>(str)); };
out << "OpenGL driver vendor: " + toQString(f->glGetString(GL_VENDOR));
out << "OpenGL renderer: " + toQString(f->glGetString(GL_RENDERER));
out << "OpenGL driver version: " + toQString(f->glGetString(GL_VERSION));
return true;
}
} // namespace
#ifndef Q_OS_LINUX
QStringList HardwareInfo::gpuInfo()
{
QStringList info;
vulkanInfo(info);
openGlInfo(info);
return info;
}
#endif
#ifdef Q_OS_WINDOWS
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <QSettings>
#include "windows.h"
QString HardwareInfo::cpuInfo()
{
const QSettings registry(R"(HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor\0)", QSettings::NativeFormat);
return registry.value("ProcessorNameString").toString();
}
uint64_t HardwareInfo::totalRamMiB()
{
MEMORYSTATUSEX status;
status.dwLength = sizeof status;
if (GlobalMemoryStatusEx(&status) == TRUE) {
// transforming bytes -> mib
return status.ullTotalPhys / 1024 / 1024;
}
qWarning() << "Could not get total RAM: GlobalMemoryStatusEx";
return 0;
}
uint64_t HardwareInfo::availableRamMiB()
{
MEMORYSTATUSEX status;
status.dwLength = sizeof status;
if (GlobalMemoryStatusEx(&status) == TRUE) {
// transforming bytes -> mib
return status.ullAvailPhys / 1024 / 1024;
}
qWarning() << "Could not get available RAM: GlobalMemoryStatusEx";
return 0;
}
#elif defined(Q_OS_MACOS)
#include "sys/sysctl.h"
QString HardwareInfo::cpuInfo()
{
std::array<char, 512> buffer;
size_t bufferSize = buffer.size();
if (sysctlbyname("machdep.cpu.brand_string", &buffer, &bufferSize, nullptr, 0) == 0) {
return QString(buffer.data());
}
qWarning() << "Could not get CPU model: sysctlbyname";
return "";
}
uint64_t HardwareInfo::totalRamMiB()
{
uint64_t memsize;
size_t memsizeSize = sizeof memsize;
if (sysctlbyname("hw.memsize", &memsize, &memsizeSize, nullptr, 0) == 0) {
// transforming bytes -> mib
return memsize / 1024 / 1024;
}
qWarning() << "Could not get total RAM: sysctlbyname";
return 0;
}
uint64_t HardwareInfo::availableRamMiB()
{
return 0;
}
MacOSHardwareInfo::MemoryPressureLevel MacOSHardwareInfo::memoryPressureLevel()
{
uint32_t level;
size_t levelSize = sizeof level;
if (sysctlbyname("kern.memorystatus_vm_pressure_level", &level, &levelSize, nullptr, 0) == 0) {
return static_cast<MemoryPressureLevel>(level);
}
qWarning() << "Could not get memory pressure level: sysctlbyname";
return MemoryPressureLevel::Normal;
}
QString MacOSHardwareInfo::memoryPressureLevelName()
{
// The names are internal, users refer to levels by their graph colors in Activity Monitor
switch (memoryPressureLevel()) {
case MemoryPressureLevel::Normal:
return "Green";
case MemoryPressureLevel::Warning:
return "Yellow";
case MemoryPressureLevel::Critical:
return "Red";
}
}
#elif defined(Q_OS_LINUX)
#include <fstream>
namespace {
QString afterColon(QString& str)
{
return str.remove(0, str.indexOf(':') + 2).trimmed();
}
} // namespace
QString HardwareInfo::cpuInfo()
{
std::ifstream cpuin("/proc/cpuinfo");
for (std::string line; std::getline(cpuin, line);) {
// model name : AMD Ryzen 7 5800X 8-Core Processor
if (QString str = QString::fromStdString(line); str.startsWith("model name")) {
return afterColon(str);
}
}
qWarning() << "Could not get CPU model: /proc/cpuinfo";
return "unknown";
}
uint64_t readMemInfo(QString searchTarget)
{
std::ifstream memin("/proc/meminfo");
for (std::string line; std::getline(memin, line);) {
// MemTotal: 16287480 kB
if (QString str = QString::fromStdString(line); str.startsWith(searchTarget)) {
bool ok = false;
const uint total = str.simplified().section(' ', 1, 1).toUInt(&ok);
if (!ok) {
qWarning() << "Could not read /proc/meminfo: failed to parse string:" << str;
return 0;
}
// transforming kib -> mib
return total / 1024;
}
}
qWarning() << "Could not read /proc/meminfo: search target not found:" << searchTarget;
return 0;
}
uint64_t HardwareInfo::totalRamMiB()
{
return readMemInfo("MemTotal");
}
uint64_t HardwareInfo::availableRamMiB()
{
return readMemInfo("MemAvailable");
}
QStringList HardwareInfo::gpuInfo()
{
QStringList list;
const bool vulkanSuccess = vulkanInfo(list);
const bool openGlSuccess = openGlInfo(list);
if (vulkanSuccess || openGlSuccess) {
return list;
}
std::array<char, 512> buffer;
FILE* lspci = popen("lspci -k", "r");
if (!lspci) {
return { "Could not detect GPUs: lspci is not present" };
}
bool readingGpuInfo = false;
QString currentModel = "";
while (fgets(buffer.data(), 512, lspci) != nullptr) {
QString str(buffer.data());
// clang-format off
// 04:00.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (rev e7)
// Subsystem: Sapphire Technology Limited Radeon RX 580 Pulse 4GB
// Kernel driver in use: amdgpu
// Kernel modules: amdgpu
// clang-format on
if (str.contains("VGA compatible controller")) {
readingGpuInfo = true;
} else if (!str.startsWith('\t')) {
readingGpuInfo = false;
}
if (!readingGpuInfo) {
continue;
}
if (str.contains("Subsystem")) {
currentModel = "Found GPU: " + afterColon(str);
}
if (str.contains("Kernel driver in use")) {
currentModel += " (using driver " + afterColon(str);
}
if (str.contains("Kernel modules")) {
currentModel += ", available drivers: " + afterColon(str) + ")";
list.append(currentModel);
}
}
pclose(lspci);
return list;
}
#else
QString HardwareInfo::cpuInfo()
{
return "unknown";
}
#if defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
#include <cstdio>
uint64_t HardwareInfo::totalRamMiB()
{
char buff[512];
FILE* fp = popen("sysctl hw.physmem", "r");
if (fp != nullptr) {
if (fgets(buff, 512, fp) != nullptr) {
std::string str(buff);
uint64_t mem = std::stoull(str.substr(12, std::string::npos));
// transforming kib -> mib
return mem / 1024;
}
}
return 0;
}
#else
uint64_t HardwareInfo::totalRamMiB()
{
return 0;
}
#endif
uint64_t HardwareInfo::availableRamMiB()
{
return 0;
}
#endif

View file

@ -1,42 +0,0 @@
// 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 <QString>
#include <cstdint>
namespace HardwareInfo {
QString cpuInfo();
uint64_t totalRamMiB();
uint64_t availableRamMiB();
QStringList gpuInfo();
} // namespace HardwareInfo
#ifdef Q_OS_MACOS
namespace MacOSHardwareInfo {
enum class MemoryPressureLevel : uint8_t {
Normal = 1,
Warning = 2,
Critical = 4,
};
MemoryPressureLevel memoryPressureLevel();
QString memoryPressureLevelName();
} // namespace MacOSHardwareInfo
#endif

View file

@ -8,7 +8,7 @@
#include "settings/INISettingsObject.h"
#include "tasks/Task.h"
InstanceCopyTask::InstanceCopyTask(BaseInstance* origInstance, const InstanceCopyPrefs& prefs)
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs)
{
m_origInstance = origInstance;
m_keepPlaytime = prefs.isKeepPlaytimeEnabled();
@ -146,9 +146,9 @@ void InstanceCopyTask::copyFinished()
}
// FIXME: shouldn't this be able to report errors?
auto instanceSettings = std::make_unique<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
BaseInstance* inst(new NullInstance(m_globalSettings, std::move(instanceSettings), m_stagingPath));
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
inst->setName(name());
inst->setIconKey(m_instIcon);
if (!m_keepPlaytime) {

View file

@ -15,7 +15,7 @@
class InstanceCopyTask : public InstanceTask {
Q_OBJECT
public:
explicit InstanceCopyTask(BaseInstance* origInstance, const InstanceCopyPrefs& prefs);
explicit InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs);
protected:
//! Entry point for tasks.
@ -26,7 +26,7 @@ class InstanceCopyTask : public InstanceTask {
private:
/* data */
BaseInstance* m_origInstance;
InstancePtr m_origInstance;
QFuture<bool> m_copyFuture;
QFutureWatcher<bool> m_copyFutureWatcher;
Filter m_matcher;

View file

@ -2,24 +2,7 @@
#include <QDebug>
#include <QFile>
#include "InstanceTask.h"
#include "minecraft/MinecraftLoadAndCheck.h"
#include "tasks/SequentialTask.h"
bool InstanceCreationTask::abort()
{
if (!canAbort()) {
return false;
}
m_abort = true;
if (m_gameFilesTask) {
return m_gameFilesTask->abort();
}
return InstanceTask::abort();
}
#include "FileSystem.h"
void InstanceCreationTask::executeTask()
{
@ -36,8 +19,7 @@ void InstanceCreationTask::executeTask()
return;
}
m_instance = createInstance();
if (!m_instance) {
if (!createInstance()) {
if (m_abort)
return;
@ -62,7 +44,7 @@ void InstanceCreationTask::executeTask()
setStatus(tr("Removing old conflicting files..."));
qDebug() << "Removing old files";
for (const QString& path : m_filesToRemove) {
for (const QString& path : m_files_to_remove) {
if (!QFile::exists(path))
continue;
@ -79,57 +61,6 @@ void InstanceCreationTask::executeTask()
return;
}
}
if (!m_abort) {
setAbortable(true);
setAbortButtonText(tr("Skip"));
qDebug() << "Downloading game files";
auto updateTasks = m_instance->createUpdateTask();
if (updateTasks.isEmpty()) {
emitSucceeded();
return;
}
auto task = makeShared<SequentialTask>();
task->addTask(makeShared<MinecraftLoadAndCheck>(m_instance.get(), Net::Mode::Online));
for (const auto& t : updateTasks) {
task->addTask(t);
}
connect(task.get(), &Task::finished, this, [this, task] {
if (task->wasSuccessful() || m_abort) {
emitSucceeded();
} else {
emitFailed(tr("Could not download game files: %1").arg(task->failReason()));
}
});
propagateFromOther(task.get());
setDetails(tr("Downloading game files"));
m_gameFilesTask = task;
m_gameFilesTask->start();
}
}
void InstanceCreationTask::scheduleToDelete(QWidget* parent, QDir dir, QString path, bool checkDisabled)
{
if (path.isEmpty()) {
return;
}
if (path.startsWith("saves/")) {
if (m_shouldDeleteSaves == ShouldDeleteSaves::NotAsked) {
m_shouldDeleteSaves = askIfShouldDeleteSaves(parent);
}
if (m_shouldDeleteSaves == ShouldDeleteSaves::No) {
return;
}
}
qDebug() << "Scheduling" << path << "for removal";
m_filesToRemove.append(dir.absoluteFilePath(path));
if (checkDisabled) {
if (path.endsWith(".disabled")) { // remove it if it was enabled/disabled by user
m_filesToRemove.append(dir.absoluteFilePath(path.chopped(9)));
} else {
m_filesToRemove.append(dir.absoluteFilePath(path + ".disabled"));
}
}
if (!m_abort)
emitSucceeded();
}

View file

@ -2,7 +2,6 @@
#include "BaseVersion.h"
#include "InstanceTask.h"
#include "minecraft/MinecraftInstance.h"
class InstanceCreationTask : public InstanceTask {
Q_OBJECT
@ -10,8 +9,6 @@ class InstanceCreationTask : public InstanceTask {
InstanceCreationTask() = default;
virtual ~InstanceCreationTask() = default;
bool abort() override;
protected:
void executeTask() final override;
@ -30,24 +27,20 @@ class InstanceCreationTask : public InstanceTask {
/**
* Creates a new instance.
*
* Returns the instance if it was created or nullptr otherwise.
* Returns whether the instance creation was successful (true) or not (false).
*/
virtual std::unique_ptr<MinecraftInstance> createInstance() { return nullptr; }
virtual bool createInstance() { return false; };
QString getError() const { return m_error_message; }
protected:
void setError(const QString& message) { m_error_message = message; };
void scheduleToDelete(QWidget* parent, QDir dir, QString path, bool checkDisabled = false);
protected:
bool m_abort = false;
QStringList m_filesToRemove;
ShouldDeleteSaves m_shouldDeleteSaves;
QStringList m_files_to_remove;
private:
QString m_error_message;
std::unique_ptr<MinecraftInstance> m_instance;
Task::Ptr m_gameFilesTask;
};

View file

@ -42,7 +42,7 @@
#include "InstanceList.h"
#include "ui/dialogs/CustomMessageBox.h"
QString askToUpdateInstanceDirName(BaseInstance* instance, const QString& oldName, const QString& newName, QWidget* parent)
QString askToUpdateInstanceDirName(InstancePtr instance, const QString& oldName, const QString& newName, QWidget* parent)
{
if (oldName == newName)
return QString();

View file

@ -37,7 +37,7 @@
#include "BaseInstance.h"
/// Update instanceRoot to make it sync with name/id; return newRoot if a directory rename happened
QString askToUpdateInstanceDirName(BaseInstance* instance, const QString& oldName, const QString& newName, QWidget* parent);
QString askToUpdateInstanceDirName(InstancePtr instance, const QString& oldName, const QString& newName, QWidget* parent);
/// Check if there are linked instances, and display a warning; return true if the operation should proceed
bool checkLinkedInstances(const QString& id, QWidget* parent, const QString& verb);

View file

@ -322,7 +322,6 @@ void InstanceImportTask::processFlame()
connect(inst_creation_task.get(), &Task::aborted, this, &InstanceImportTask::emitAborted);
connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
connect(inst_creation_task.get(), &Task::abortButtonTextChanged, this, &Task::setAbortButtonText);
connect(inst_creation_task.get(), &Task::warningLogged, this, [this](const QString& line) { m_Warnings.append(line); });
@ -342,9 +341,9 @@ void InstanceImportTask::processTechnic()
void InstanceImportTask::processMultiMC()
{
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
auto instanceSettings = std::make_unique<INISettingsObject>(configPath);
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
NullInstance instance(m_globalSettings, std::move(instanceSettings), m_stagingPath);
NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
// reset time played on import... because packs.
instance.resetTimePlayed();
@ -422,7 +421,6 @@ void InstanceImportTask::processModrinth()
connect(inst_creation_task.get(), &Task::aborted, this, &InstanceImportTask::emitAborted);
connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
connect(inst_creation_task.get(), &Task::abortButtonTextChanged, this, &Task::setAbortButtonText);
connect(inst_creation_task.get(), &Task::warningLogged, this, [this](const QString& line) { m_Warnings.append(line); });

View file

@ -34,37 +34,42 @@
* limitations under the License.
*/
#include "InstanceList.h"
#include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QFileInfo>
#include <QFileSystemWatcher>
#include <QJsonArray>
#include <QJsonDocument>
#include <QMimeData>
#include <QPair>
#include <QSet>
#include <QStack>
#include <QTextStream>
#include <QThread>
#include <QTimer>
#include <QUuid>
#include <QXmlStreamReader>
#include "BaseInstance.h"
#include "ExponentialSeries.h"
#include "FileSystem.h"
#include "InstanceList.h"
#include "InstanceTask.h"
#include "NullInstance.h"
#include "WatchLock.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/ShortcutUtils.h"
#include "settings/INISettingsObject.h"
#ifdef Q_OS_WIN32
#include <windows.h>
#include <Windows.h>
#endif
const static int GROUP_FILE_FORMAT_VERSION = 1;
InstanceList::InstanceList(SettingsObject* settings, const QString& instDir, QObject* parent)
InstanceList::InstanceList(SettingsObjectPtr settings, const QString& instDir, QObject* parent)
: QAbstractListModel(parent), m_globalSettings(settings)
{
resumeWatch();
@ -138,7 +143,7 @@ QMimeData* InstanceList::mimeData(const QModelIndexList& indexes) const
QStringList InstanceList::getLinkedInstancesById(const QString& id) const
{
QStringList linkedInstances;
for (auto& inst : m_instances) {
for (auto inst : m_instances) {
if (inst->isLinkedToInstanceId(id))
linkedInstances.append(inst->id());
}
@ -148,15 +153,15 @@ QStringList InstanceList::getLinkedInstancesById(const QString& id) const
int InstanceList::rowCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
return count();
return m_instances.count();
}
QModelIndex InstanceList::index(int row, int column, const QModelIndex& parent) const
{
Q_UNUSED(parent);
if (row < 0 || row >= count())
if (row < 0 || row >= m_instances.size())
return QModelIndex();
return createIndex(row, column, m_instances.at(row).get());
return createIndex(row, column, (void*)m_instances.at(row).get());
}
QVariant InstanceList::data(const QModelIndex& index, int role) const
@ -261,7 +266,7 @@ void InstanceList::setInstanceGroup(const InstanceId& id, GroupId name)
if (changed) {
increaseGroupCount(name);
auto idx = getInstIndex(inst);
auto idx = getInstIndex(inst.get());
emit dataChanged(index(idx), index(idx), { GroupRole });
saveGroupList();
}
@ -452,7 +457,7 @@ void InstanceList::deleteInstance(const InstanceId& id)
}
}
static QMap<InstanceId, InstanceLocator> getIdMapping(const std::vector<std::unique_ptr<BaseInstance>>& list)
static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr>& list)
{
QMap<InstanceId, InstanceLocator> out;
int i = 0;
@ -461,7 +466,7 @@ static QMap<InstanceId, InstanceLocator> getIdMapping(const std::vector<std::uni
if (out.contains(id)) {
qWarning() << "Duplicate ID" << id << "in instance list";
}
out[id] = std::make_pair(item.get(), i);
out[id] = std::make_pair(item, i);
i++;
}
return out;
@ -499,16 +504,17 @@ InstanceList::InstListError InstanceList::loadList()
{
auto existingIds = getIdMapping(m_instances);
std::vector<std::unique_ptr<BaseInstance>> newList;
QList<InstancePtr> newList;
for (auto& id : discoverInstances()) {
if (existingIds.contains(id)) {
auto instPair = existingIds[id];
existingIds.remove(id);
qInfo() << "Should keep and soft-reload" << id;
} else {
std::unique_ptr<BaseInstance> instPtr = loadInstance(id);
InstancePtr instPtr = loadInstance(id);
if (instPtr) {
newList.push_back(std::move(instPtr));
newList.append(instPtr);
}
}
}
@ -560,8 +566,8 @@ InstanceList::InstListError InstanceList::loadList()
void InstanceList::updateTotalPlayTime()
{
totalPlayTime = 0;
for (const auto& itr : m_instances) {
totalPlayTime += itr->totalTimePlayed();
for (auto const& itr : m_instances) {
totalPlayTime += itr.get()->totalTimePlayed();
}
}
@ -572,12 +578,12 @@ void InstanceList::saveNow()
}
}
void InstanceList::add(std::vector<std::unique_ptr<BaseInstance>>& t)
void InstanceList::add(const QList<InstancePtr>& t)
{
beginInsertRows(QModelIndex(), count(), static_cast<int>(count() + t.size() - 1));
beginInsertRows(QModelIndex(), m_instances.count(), m_instances.count() + t.size() - 1);
m_instances.append(t);
for (auto& ptr : t) {
m_instances.push_back(std::move(ptr));
connect(m_instances.back().get(), &BaseInstance::propertiesChanged, this, &InstanceList::propertiesChanged);
connect(ptr.get(), &BaseInstance::propertiesChanged, this, &InstanceList::propertiesChanged);
}
endInsertRows();
}
@ -607,26 +613,26 @@ void InstanceList::providerUpdated()
}
}
BaseInstance* InstanceList::getInstanceById(QString instId) const
InstancePtr InstanceList::getInstanceById(QString instId) const
{
if (instId.isEmpty())
return nullptr;
return InstancePtr();
for (auto& inst : m_instances) {
if (inst->id() == instId) {
return inst.get();
return inst;
}
}
return nullptr;
return InstancePtr();
}
BaseInstance* InstanceList::getInstanceByManagedName(const QString& managed_name) const
InstancePtr InstanceList::getInstanceByManagedName(const QString& managed_name) const
{
if (managed_name.isEmpty())
return {};
for (auto& instance : m_instances) {
for (auto instance : m_instances) {
if (instance->getManagedPackName() == managed_name)
return instance.get();
return instance;
}
return {};
@ -634,14 +640,14 @@ BaseInstance* InstanceList::getInstanceByManagedName(const QString& managed_name
QModelIndex InstanceList::getInstanceIndexById(const QString& id) const
{
return index(getInstIndex(getInstanceById(id)));
return index(getInstIndex(getInstanceById(id).get()));
}
int InstanceList::getInstIndex(BaseInstance* inst) const
{
int count = this->count();
int count = m_instances.count();
for (int i = 0; i < count; i++) {
if (inst == m_instances.at(i).get()) {
if (inst == m_instances[i].get()) {
return i;
}
}
@ -657,15 +663,15 @@ void InstanceList::propertiesChanged(BaseInstance* inst)
}
}
std::unique_ptr<BaseInstance> InstanceList::loadInstance(const InstanceId& id)
InstancePtr InstanceList::loadInstance(const InstanceId& id)
{
if (!m_groupsLoaded) {
loadGroupList();
}
auto instanceRoot = FS::PathCombine(m_instDir, id);
auto instanceSettings = std::make_unique<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg"));
std::unique_ptr<BaseInstance> inst;
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg"));
InstancePtr inst;
instanceSettings->registerSetting("InstanceType", "");
@ -674,9 +680,9 @@ std::unique_ptr<BaseInstance> InstanceList::loadInstance(const InstanceId& id)
// NOTE: Some launcher versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a
// OneSix instance
if (inst_type == "OneSix" || inst_type.isEmpty()) {
inst.reset(new MinecraftInstance(m_globalSettings, std::move(instanceSettings), instanceRoot));
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
} else {
inst.reset(new NullInstance(m_globalSettings, std::move(instanceSettings), instanceRoot));
inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot));
}
qDebug() << "Loaded instance" << inst->name() << "from" << inst->instanceRoot();
@ -905,20 +911,20 @@ class InstanceStaging : public Task {
const unsigned maxBackoff = 16;
public:
InstanceStaging(InstanceList* parent, InstanceTask* child, SettingsObject* settings) : m_parent(parent), backoff(minBackoff, maxBackoff)
InstanceStaging(InstanceList* parent, InstanceTask* child, SettingsObjectPtr settings)
: m_parent(parent), backoff(minBackoff, maxBackoff)
{
m_stagingPath = parent->getStagedInstancePath();
m_child.reset(child);
m_child->setStagingPath(m_stagingPath);
m_child->setParentSettings(settings);
m_child->setParentSettings(std::move(settings));
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceeded);
connect(child, &Task::failed, this, &InstanceStaging::childFailed);
connect(child, &Task::aborted, this, &InstanceStaging::childAborted);
connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable);
connect(child, &Task::abortButtonTextChanged, this, &InstanceStaging::setAbortButtonText);
connect(child, &Task::status, this, &InstanceStaging::setStatus);
connect(child, &Task::details, this, &InstanceStaging::setDetails);
connect(child, &Task::progress, this, &InstanceStaging::setProgress);
@ -926,21 +932,22 @@ class InstanceStaging : public Task {
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceeded);
}
~InstanceStaging() override = default;
virtual ~InstanceStaging() {}
// FIXME/TODO: add ability to abort during instance commit retries
bool abort() override
{
if (!canAbort()) {
if (!canAbort())
return false;
}
return m_child->abort();
m_child->abort();
return Task::abort();
}
bool canAbort() const override { return (m_child && m_child->canAbort()); }
protected:
void executeTask() override
virtual void executeTask() override
{
if (m_stagingPath.isNull()) {
emitFailed(tr("Could not create staging folder"));
@ -955,14 +962,12 @@ class InstanceStaging : public Task {
void childSucceeded()
{
unsigned sleepTime = backoff();
if (m_parent->commitStagedInstance(m_stagingPath, *m_child, m_child->group(), *m_child)) {
m_backoffTimer.stop();
if (m_parent->commitStagedInstance(m_stagingPath, *m_child.get(), m_child->group(), *m_child.get())) {
emitSucceeded();
return;
}
// we actually failed, retry?
if (sleepTime == maxBackoff) {
m_backoffTimer.stop();
emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
return;
}
@ -971,14 +976,12 @@ class InstanceStaging : public Task {
}
void childFailed(const QString& reason)
{
m_backoffTimer.stop();
m_parent->destroyStagingPath(m_stagingPath);
emitFailed(reason);
}
void childAborted()
{
m_backoffTimer.stop();
m_parent->destroyStagingPath(m_stagingPath);
emitAborted();
}
@ -992,7 +995,7 @@ class InstanceStaging : public Task {
*/
ExponentialSeries backoff;
QString m_stagingPath;
std::unique_ptr<InstanceTask> m_child;
unique_qobject_ptr<InstanceTask> m_child;
QTimer m_backoffTimer;
};
@ -1025,14 +1028,15 @@ QString InstanceList::getStagedInstancePath()
}
bool InstanceList::commitStagedInstance(const QString& path,
const InstanceName& instanceName,
InstanceName const& instanceName,
QString groupName,
const InstanceTask& commiting)
InstanceTask const& commiting)
{
if (groupName.isEmpty() && !groupName.isNull())
groupName = QString();
QString instID;
InstancePtr inst;
auto should_override = commiting.shouldOverride();

View file

@ -50,7 +50,7 @@ struct InstanceName;
using InstanceId = QString;
using GroupId = QString;
using InstanceLocator = std::pair<BaseInstance*, int>;
using InstanceLocator = std::pair<InstancePtr, int>;
enum class InstCreateError { NoCreateError = 0, NoSuchVersion, UnknownCreateError, InstExists, CantCreateDir };
@ -73,7 +73,7 @@ class InstanceList : public QAbstractListModel {
Q_OBJECT
public:
explicit InstanceList(SettingsObject* settings, const QString& instDir, QObject* parent = 0);
explicit InstanceList(SettingsObjectPtr settings, const QString& instDir, QObject* parent = 0);
virtual ~InstanceList();
public:
@ -96,17 +96,17 @@ class InstanceList : public QAbstractListModel {
*/
enum InstListError { NoError = 0, UnknownError };
BaseInstance* at(int i) const { return m_instances.at(i).get(); }
InstancePtr at(int i) const { return m_instances.at(i); }
int count() const { return static_cast<int>(m_instances.size()); }
int count() const { return m_instances.count(); }
InstListError loadList();
void saveNow();
/* O(n) */
BaseInstance* getInstanceById(QString id) const;
InstancePtr getInstanceById(QString id) const;
/* O(n) */
BaseInstance* getInstanceByManagedName(const QString& managed_name) const;
InstancePtr getInstanceByManagedName(const QString& managed_name) const;
QModelIndex getInstanceIndexById(const QString& id) const;
QStringList getGroups();
bool isGroupCollapsed(const QString& groupName);
@ -179,11 +179,11 @@ class InstanceList : public QAbstractListModel {
void updateTotalPlayTime();
void suspendWatch();
void resumeWatch();
void add(std::vector<std::unique_ptr<BaseInstance>>& list);
void add(const QList<InstancePtr>& list);
void loadGroupList();
void saveGroupList();
QList<InstanceId> discoverInstances();
std::unique_ptr<BaseInstance> loadInstance(const InstanceId& id);
InstancePtr loadInstance(const InstanceId& id);
void increaseGroupCount(const QString& group);
void decreaseGroupCount(const QString& group);
@ -192,11 +192,11 @@ class InstanceList : public QAbstractListModel {
int m_watchLevel = 0;
int totalPlayTime = 0;
bool m_dirty = false;
std::vector<std::unique_ptr<BaseInstance>> m_instances;
QList<InstancePtr> m_instances;
// id -> refs
QMap<QString, int> m_groupNameCache;
SettingsObject* m_globalSettings;
SettingsObjectPtr m_globalSettings;
QString m_instDir;
QFileSystemWatcher* m_watcher;
// FIXME: this is so inefficient that looking at it is almost painful.

View file

@ -21,26 +21,26 @@
class InstancePageProvider : protected QObject, public BasePageProvider {
Q_OBJECT
public:
explicit InstancePageProvider(BaseInstance* parent) { inst = parent; }
explicit InstancePageProvider(InstancePtr parent) { inst = parent; }
virtual ~InstancePageProvider() = default;
virtual QList<BasePage*> getPages() override
{
QList<BasePage*> values;
values.append(new LogPage(inst));
MinecraftInstance* onesix = dynamic_cast<MinecraftInstance*>(inst);
values.append(new VersionPage(onesix));
values.append(ManagedPackPage::createPage(onesix));
auto modsPage = new ModFolderPage(onesix, onesix->loaderModList());
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
values.append(new VersionPage(onesix.get()));
values.append(ManagedPackPage::createPage(onesix.get()));
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList());
modsPage->setFilter("%1 (*.zip *.jar *.litemod *.nilmod)");
values.append(modsPage);
values.append(new CoreModFolderPage(onesix, onesix->coreModList()));
values.append(new NilModFolderPage(onesix, onesix->nilModList()));
values.append(new ResourcePackPage(onesix, onesix->resourcePackList()));
values.append(new GlobalDataPackPage(onesix));
values.append(new TexturePackPage(onesix, onesix->texturePackList()));
values.append(new ShaderPackPage(onesix, onesix->shaderPackList()));
values.append(new NotesPage(onesix));
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList()));
values.append(new NilModFolderPage(onesix.get(), onesix->nilModList()));
values.append(new ResourcePackPage(onesix.get(), onesix->resourcePackList()));
values.append(new GlobalDataPackPage(onesix.get()));
values.append(new TexturePackPage(onesix.get(), onesix->texturePackList()));
values.append(new ShaderPackPage(onesix.get(), onesix->shaderPackList()));
values.append(new NotesPage(onesix.get()));
values.append(new WorldListPage(onesix, onesix->worldList()));
values.append(new ServersPage(onesix));
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
@ -52,5 +52,5 @@ class InstancePageProvider : protected QObject, public BasePageProvider {
virtual QString dialogTitle() override { return tr("Edit Instance (%1)").arg(inst->name()); }
protected:
BaseInstance* inst;
InstancePtr inst;
};

View file

@ -1,5 +1,4 @@
#include "InstanceTask.h"
#include <QDir>
#include "Application.h"
#include "settings/SettingsObject.h"
@ -83,13 +82,3 @@ void InstanceName::setName(InstanceName& other)
}
InstanceTask::InstanceTask() : Task(), InstanceName() {}
ShouldDeleteSaves askIfShouldDeleteSaves(QWidget* parent)
{
auto dialog = CustomMessageBox::selectable(parent, QObject::tr("Delete Existing Save Files"),
QObject::tr("An earlier version of this mod pack installed save files.\n"
"Would you like to remove those existing saves as part of this update?"),
QMessageBox::Question, QMessageBox::No | QMessageBox::Yes);
auto result = dialog->exec();
return result == QMessageBox::Yes ? ShouldDeleteSaves::Yes : ShouldDeleteSaves::No;
}

View file

@ -8,8 +8,6 @@ enum class InstanceNameChange { ShouldChange, ShouldKeep };
[[nodiscard]] InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name);
enum class ShouldUpdate { Update, SkipUpdating, Cancel };
[[nodiscard]] ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name);
enum class ShouldDeleteSaves { NotAsked, Yes, No };
[[nodiscard]] ShouldDeleteSaves askIfShouldDeleteSaves(QWidget* parent);
struct InstanceName {
public:
@ -37,7 +35,7 @@ class InstanceTask : public Task, public InstanceName {
InstanceTask();
~InstanceTask() override = default;
void setParentSettings(SettingsObject* settings) { m_globalSettings = settings; }
void setParentSettings(SettingsObjectPtr settings) { m_globalSettings = settings; }
void setStagingPath(const QString& stagingPath) { m_stagingPath = stagingPath; }
@ -62,7 +60,7 @@ class InstanceTask : public Task, public InstanceName {
}
protected: /* data */
SettingsObject* m_globalSettings;
SettingsObjectPtr m_globalSettings;
QString m_instIcon;
QString m_instGroup;
QString m_stagingPath;

View file

@ -303,7 +303,7 @@ QStringList toStringList(const QString& jsonString)
return {};
try {
return requireIsArrayOf<QString>(doc);
} catch (Json::JsonException&) {
} catch (Json::JsonException& e) {
return {};
}
}

View file

@ -41,16 +41,21 @@
#include "minecraft/auth/AccountList.h"
#include "ui/InstanceWindow.h"
#include "ui/MainWindow.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/MSALoginDialog.h"
#include "ui/dialogs/ProfileSelectDialog.h"
#include "ui/dialogs/ProfileSetupDialog.h"
#include "ui/dialogs/ProgressDialog.h"
#include <QHostAddress>
#include <QHostInfo>
#include <QInputDialog>
#include <QLineEdit>
#include <QList>
#include <QPushButton>
#include <utility>
#include <QRegularExpression>
#include <QStringList>
#include "BuildConfig.h"
#include "JavaCommon.h"
@ -58,7 +63,7 @@
#include "tasks/Task.h"
#include "ui/dialogs/ChooseOfflineNameDialog.h"
LaunchController::LaunchController() = default;
LaunchController::LaunchController() : Task() {}
void LaunchController::executeTask()
{
@ -81,17 +86,9 @@ void LaunchController::decideAccount()
return;
}
// Select the account to use. If the instance has a specific account set, that will be used. Otherwise, the default account will be used
auto* accounts = APPLICATION->accounts();
const auto instanceAccountId = m_instance->settings()->get("InstanceAccountId").toString();
const auto instanceAccountIndex = accounts->findAccountByProfileId(instanceAccountId);
if (instanceAccountIndex == -1 || instanceAccountId.isEmpty()) {
m_accountToUse = accounts->defaultAccount();
} else {
m_accountToUse = accounts->at(instanceAccountIndex);
}
if (!accounts->anyAccountIsValid()) {
// Find an account to use.
auto accounts = APPLICATION->accounts();
if (accounts->count() <= 0 || !accounts->anyAccountIsValid()) {
// Tell the user they need to log in at least one account in order to play.
auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"),
tr("In order to play Minecraft, you must have at least one Microsoft "
@ -109,6 +106,15 @@ void LaunchController::decideAccount()
}
}
// Select the account to use. If the instance has a specific account set, that will be used. Otherwise, the default account will be used
auto instanceAccountId = m_instance->settings()->get("InstanceAccountId").toString();
auto instanceAccountIndex = accounts->findAccountByProfileId(instanceAccountId);
if (instanceAccountIndex == -1 || instanceAccountId.isEmpty()) {
m_accountToUse = accounts->defaultAccount();
} else {
m_accountToUse = accounts->at(instanceAccountIndex);
}
if (!m_accountToUse) {
// If no default account is set, ask the user which one to use.
ProfileSelectDialog selectDialog(tr("Which account would you like to use?"), ProfileSelectDialog::GlobalDefaultCheckbox,
@ -126,134 +132,35 @@ void LaunchController::decideAccount()
}
}
LaunchDecision LaunchController::decideLaunchMode()
{
if (!m_accountToUse || m_wantedLaunchMode == LaunchMode::Demo) {
m_actualLaunchMode = LaunchMode::Demo;
return LaunchDecision::Continue;
}
if (m_wantedLaunchMode == LaunchMode::Normal) {
if (m_accountToUse->shouldRefresh() || m_accountToUse->accountState() == AccountState::Offline) {
// Force account refresh on the account used to launch the instance updating the AccountState
// only on first try and if it is not meant to be offline
m_accountToUse->refresh();
}
}
const auto* accounts = APPLICATION->accounts();
MinecraftAccountPtr accountToCheck = nullptr;
if (m_accountToUse->accountType() != AccountType::Offline) {
accountToCheck = m_accountToUse->ownsMinecraft() ? m_accountToUse : nullptr;
} else if (const auto defaultAccount = accounts->defaultAccount(); defaultAccount && defaultAccount->ownsMinecraft()) {
accountToCheck = defaultAccount;
} else {
for (int i = 0; i < accounts->count(); i++) {
if (const auto account = accounts->at(i); account->ownsMinecraft()) {
accountToCheck = account;
break;
}
}
}
if (!accountToCheck) {
m_actualLaunchMode = LaunchMode::Demo;
return LaunchDecision::Continue;
}
auto state = accountToCheck->accountState();
if (state == AccountState::Unchecked || state == AccountState::Errored) {
accountToCheck->refresh();
state = AccountState::Working;
}
if (state == AccountState::Working) {
// refresh is in progress, we need to wait for it to finish to proceed.
ProgressDialog progDialog(m_parentWidget);
progDialog.setSkipButton(true, tr("Abort"));
// TODO: this relies on tasks' synchronous signal dispatching nature
// TODO: meaning currentTask can't complete and become null while this code is running
// TODO: this code will produce a race condition when tasks become fully async
auto task = accountToCheck->currentTask();
progDialog.execWithTask(task.get());
if (task->getState() == State::AbortedByUser) {
return LaunchDecision::Abort;
}
state = accountToCheck->accountState();
}
QString reauthReason;
switch (state) {
case AccountState::Errored:
reauthReason = tr("An error occurred while refreshing '%1'").arg(accountToCheck->profileName());
break;
case AccountState::Expired:
reauthReason = tr("'%1' has expired and needs to be reauthenticated").arg(accountToCheck->profileName());
break;
case AccountState::Disabled:
reauthReason = tr("The launcher's client identification has changed");
break;
case AccountState::Gone:
reauthReason = tr("'%1' no longer exists on the servers").arg(accountToCheck->profileName());
break;
default:
m_actualLaunchMode =
state == AccountState::Online && m_wantedLaunchMode == LaunchMode::Normal ? LaunchMode::Normal : LaunchMode::Offline;
return LaunchDecision::Continue; // All good to go
}
if (reauthenticateAccount(accountToCheck, reauthReason)) {
return LaunchDecision::Undecided;
}
return LaunchDecision::Abort;
}
bool LaunchController::askPlayDemo() const
bool LaunchController::askPlayDemo()
{
QMessageBox box(m_parentWidget);
box.setWindowTitle(tr("Play demo?"));
QString text = m_accountToUse
? tr("This account does not own Minecraft.\nYou need to purchase the game first to play the full version.")
: tr("No account was selected for launch.");
text += tr("\n\nDo you want to play the demo?");
box.setText(text);
box.setText(
tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play "
"the demo?"));
box.setIcon(QMessageBox::Warning);
const auto* demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole);
auto* cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole);
auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole);
auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole);
box.setDefaultButton(cancelButton);
box.exec();
return box.clickedButton() == demoButton;
}
QString LaunchController::askOfflineName(const QString& playerName, bool* ok) const
QString LaunchController::askOfflineName(QString playerName, bool demo, bool* ok)
{
if (ok != nullptr) {
*ok = false;
}
QString message;
switch (m_actualLaunchMode) {
case LaunchMode::Normal:
Q_ASSERT(false);
return "";
case LaunchMode::Demo:
message = tr("Choose your demo mode player name");
break;
case LaunchMode::Offline:
if (m_wantedLaunchMode == LaunchMode::Normal) {
message = tr("You are not connected to the Internet, launching in offline mode\n\n");
}
message += tr("Choose your offline mode player name");
break;
// we ask the user for a player name
QString message = tr("Choose your offline mode player name.");
if (demo) {
message = tr("Choose your demo mode player name.");
}
const QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
QString usedname = lastOfflinePlayerName.isEmpty() ? playerName : lastOfflinePlayerName;
ChooseOfflineNameDialog dialog(message, m_parentWidget);
@ -263,7 +170,8 @@ QString LaunchController::askOfflineName(const QString& playerName, bool* ok) co
return {};
}
usedname = dialog.getUsername();
const QString name = dialog.getUsername();
usedname = name;
APPLICATION->settings()->set("LastOfflinePlayerName", usedname);
if (ok != nullptr) {
@ -276,69 +184,183 @@ void LaunchController::login()
{
decideAccount();
LaunchDecision decision = decideLaunchMode();
while (decision == LaunchDecision::Undecided) {
decision = decideLaunchMode();
}
if (decision == LaunchDecision::Abort) {
emitAborted();
return;
}
if (m_actualLaunchMode == LaunchMode::Demo) {
if (m_wantedLaunchMode == LaunchMode::Demo || askPlayDemo()) {
if (!m_accountToUse) {
// if no account is selected, ask about demo
if (!m_demo) {
m_demo = askPlayDemo();
}
if (m_demo) {
// we ask the user for a player name
bool ok = false;
auto name = askOfflineName("Player", &ok);
auto name = askOfflineName("Player", m_demo, &ok);
if (ok) {
m_session = std::make_shared<AuthSession>();
m_session->MakeDemo(name, MinecraftAccount::uuidFromUsername(name).toString(QUuid::Id128));
static const QRegularExpression s_removeChars("[{}-]");
m_session->MakeDemo(name, MinecraftAccount::uuidFromUsername(name).toString().remove(s_removeChars));
launchInstance();
return;
}
}
emitFailed(tr("No account selected for launch"));
// if no account is selected, we bail
emitFailed(tr("No account selected for launch."));
return;
}
m_session = std::make_shared<AuthSession>();
m_session->launchMode = m_actualLaunchMode;
m_accountToUse->fillSession(m_session);
// we loop until the user succeeds in logging in or gives up
bool tryagain = true;
unsigned int tries = 0;
if (m_accountToUse->accountType() != AccountType::Offline) {
if (m_actualLaunchMode == LaunchMode::Normal && !m_accountToUse->hasProfile()) {
// Now handle setting up a profile name here...
if (ProfileSetupDialog dialog(m_accountToUse, m_parentWidget); dialog.exec() != QDialog::Accepted) {
if ((m_accountToUse->accountType() != AccountType::Offline && m_accountToUse->accountState() == AccountState::Offline) ||
m_accountToUse->shouldRefresh()) {
// Force account refresh on the account used to launch the instance updating the AccountState
// only on first try and if it is not meant to be offline
m_accountToUse->refresh();
}
while (tryagain) {
if (tries > 0 && tries % 3 == 0) {
auto result =
QMessageBox::question(m_parentWidget, tr("Continue launch?"),
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) {
emitAborted();
return;
}
}
tries++;
m_session = std::make_shared<AuthSession>();
m_session->wants_online = m_online;
m_session->demo = m_demo;
m_accountToUse->fillSession(m_session);
if (m_actualLaunchMode == LaunchMode::Offline && m_accountToUse->accountType() != AccountType::Offline) {
bool ok = false;
QString name = m_offlineName;
if (name.isEmpty()) {
name = askOfflineName(m_session->player_name, &ok);
if (!ok) {
emitAborted();
return;
}
MinecraftAccountPtr accountToCheck;
if (m_accountToUse->ownsMinecraft())
accountToCheck = m_accountToUse;
else if (const MinecraftAccountPtr defaultAccount = APPLICATION->accounts()->defaultAccount();
defaultAccount != nullptr && defaultAccount->ownsMinecraft()) {
accountToCheck = defaultAccount;
} else {
for (int i = 0; i < APPLICATION->accounts()->count(); i++) {
MinecraftAccountPtr account = APPLICATION->accounts()->at(i);
if (account->ownsMinecraft())
accountToCheck = account;
}
}
if (accountToCheck == nullptr) {
if (!m_session->demo)
m_session->demo = askPlayDemo();
if (m_session->demo)
launchInstance();
else
emitFailed(tr("Launch cancelled - account does not own Minecraft."));
return;
}
switch (accountToCheck->accountState()) {
case AccountState::Offline: {
m_session->wants_online = false;
}
/* fallthrough */
case AccountState::Online: {
if (!m_session->wants_online && m_accountToUse->accountType() != AccountType::Offline) {
// we ask the user for a player name
bool ok = false;
QString name;
if (m_offlineName.isEmpty()) {
name = askOfflineName(m_session->player_name, m_session->demo, &ok);
if (!ok) {
tryagain = false;
break;
}
} else {
name = m_offlineName;
}
m_session->MakeOffline(name);
// offline flavored game from here :3
} else if (m_accountToUse == accountToCheck && !m_accountToUse->hasProfile()) {
// Now handle setting up a profile name here...
ProfileSetupDialog dialog(m_accountToUse, m_parentWidget);
if (dialog.exec() == QDialog::Accepted) {
tryagain = true;
continue;
} else {
emitFailed(tr("Received undetermined session status during login."));
return;
}
}
if (m_accountToUse->accountType() == AccountType::Offline)
m_session->wants_online = false;
// we own Minecraft, there is a profile, it's all ready to go!
launchInstance();
return;
}
case AccountState::Errored:
// This means some sort of soft error that we can fix with a refresh ... so let's refresh.
case AccountState::Unchecked: {
accountToCheck->refresh();
}
/* fallthrough */
case AccountState::Working: {
// refresh is in progress, we need to wait for it to finish to proceed.
ProgressDialog progDialog(m_parentWidget);
progDialog.setSkipButton(true, tr("Abort"));
auto task = accountToCheck->currentTask();
progDialog.execWithTask(task.get());
// don't retry if aborted
if (task->getState() == Task::State::AbortedByUser)
tryagain = false;
continue;
}
case AccountState::Expired: {
if (reauthenticateAccount(accountToCheck))
continue;
return;
}
case AccountState::Disabled: {
auto errorString = tr("The launcher's client identification has changed. Please remove '%1' and try again.")
.arg(accountToCheck->profileName());
QMessageBox::warning(m_parentWidget, tr("Client identification changed"), errorString, QMessageBox::StandardButton::Ok,
QMessageBox::StandardButton::Ok);
emitFailed(errorString);
return;
}
case AccountState::Gone: {
auto errorString =
tr("'%1' no longer exists on the servers. It may have been migrated, in which case please add the new account "
"you migrated this one to.")
.arg(accountToCheck->profileName());
QMessageBox::warning(m_parentWidget, tr("Account gone"), errorString, QMessageBox::StandardButton::Ok,
QMessageBox::StandardButton::Ok);
emitFailed(errorString);
return;
}
m_session->MakeOffline(name);
}
}
launchInstance();
emitFailed(tr("Failed to launch."));
}
bool LaunchController::reauthenticateAccount(const MinecraftAccountPtr& account, const QString& reason)
bool LaunchController::reauthenticateAccount(MinecraftAccountPtr account)
{
auto button = QMessageBox::warning(
m_parentWidget, tr("Account refresh failed"), tr("%1. Do you want to reauthenticate this account?").arg(reason),
m_parentWidget, tr("Account refresh failed"),
tr("'%1' has expired and needs to be reauthenticated. Do you want to reauthenticate this account?").arg(account->profileName()),
QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::Yes);
if (button == QMessageBox::StandardButton::Yes) {
auto* accounts = APPLICATION->accounts();
const bool isDefault = accounts->defaultAccount() == account;
auto accounts = APPLICATION->accounts();
bool isDefault = accounts->defaultAccount() == account;
accounts->removeAccount(accounts->index(accounts->findAccountByProfileId(account->profileId())));
if (account->accountType() == AccountType::MSA) {
auto newAccount = MSALoginDialog::newAccount(m_parentWidget);
@ -346,9 +368,8 @@ bool LaunchController::reauthenticateAccount(const MinecraftAccountPtr& account,
if (newAccount != nullptr) {
accounts->addAccount(newAccount);
if (isDefault) {
if (isDefault)
accounts->setDefaultAccount(newAccount);
}
if (m_accountToUse == account) {
m_accountToUse = nullptr;
@ -359,13 +380,14 @@ bool LaunchController::reauthenticateAccount(const MinecraftAccountPtr& account,
}
}
emitFailed(tr("The account has expired and needs to be reauthenticated"));
return false;
}
void LaunchController::launchInstance()
{
Q_ASSERT(m_instance != nullptr);
Q_ASSERT(m_session.get() != nullptr);
Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL");
Q_ASSERT_X(m_session.get() != nullptr, "launchInstance", "session is NULL");
if (!m_instance->reloadSettings()) {
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't load the instance profile."));
@ -379,36 +401,37 @@ void LaunchController::launchInstance()
return;
}
const auto* console = qobject_cast<InstanceWindow*>(m_parentWidget);
const auto showConsole = m_instance->settings()->get("ShowConsole").toBool();
auto console = qobject_cast<InstanceWindow*>(m_parentWidget);
auto showConsole = m_instance->settings()->get("ShowConsole").toBool();
if (!console && showConsole) {
APPLICATION->showInstanceWindow(m_instance);
}
connect(m_launcher, &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch);
connect(m_launcher, &LaunchTask::succeeded, this, &LaunchController::onSucceeded);
connect(m_launcher, &LaunchTask::failed, this, &LaunchController::onFailed);
connect(m_launcher, &LaunchTask::requestProgress, this, &LaunchController::onProgressRequested);
connect(m_launcher.get(), &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch);
connect(m_launcher.get(), &LaunchTask::succeeded, this, &LaunchController::onSucceeded);
connect(m_launcher.get(), &LaunchTask::failed, this, &LaunchController::onFailed);
connect(m_launcher.get(), &LaunchTask::requestProgress, this, &LaunchController::onProgressRequested);
// Prepend Online and Auth Status
QString online_mode;
if (m_actualLaunchMode == LaunchMode::Normal) {
if (m_session->wants_online) {
online_mode = "online";
// Prepend Server Status
const QStringList servers = { "login.microsoftonline.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" };
QStringList servers = { "login.microsoftonline.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" };
m_launcher->prependStep(makeShared<PrintServers>(m_launcher, servers));
m_launcher->prependStep(makeShared<PrintServers>(m_launcher.get(), servers));
} else {
online_mode = m_actualLaunchMode == LaunchMode::Demo ? "demo" : "offline";
online_mode = m_demo ? "demo" : "offline";
}
m_launcher->prependStep(makeShared<TextPrint>(m_launcher, "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
m_launcher->prependStep(
makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
// Prepend Version
{
auto versionString = QString("%1 version: %2 (%3)")
.arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString(), BuildConfig.BUILD_PLATFORM);
m_launcher->prependStep(makeShared<TextPrint>(m_launcher, versionString + "\n", MessageLevel::Launcher));
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), versionString + "\n\n", MessageLevel::Launcher));
}
m_launcher->start();
}
@ -465,10 +488,10 @@ void LaunchController::onFailed(QString reason)
if (m_instance->settings()->get("ShowConsoleOnError").toBool()) {
APPLICATION->showInstanceWindow(m_instance, "console");
}
emitFailed(std::move(reason));
emitFailed(reason);
}
void LaunchController::onProgressRequested(Task* task) const
void LaunchController::onProgressRequested(Task* task)
{
ProgressDialog progDialog(m_parentWidget);
progDialog.setSkipButton(true, tr("Abort"));

View file

@ -36,30 +36,30 @@
#pragma once
#include <BaseInstance.h>
#include <tools/BaseProfiler.h>
#include <QObject>
#include "minecraft/auth/MinecraftAccount.h"
#include "minecraft/launch/MinecraftTarget.h"
class InstanceWindow;
enum class LaunchDecision { Undecided, Continue, Abort };
class LaunchController : public Task {
Q_OBJECT
public:
void executeTask() override;
LaunchController();
~LaunchController() override = default;
virtual ~LaunchController() = default;
void setInstance(BaseInstance* instance) { m_instance = instance; }
void setInstance(InstancePtr instance) { m_instance = instance; }
BaseInstance* instance() const { return m_instance; }
InstancePtr instance() { return m_instance; }
void setLaunchMode(const LaunchMode mode) { m_wantedLaunchMode = mode; }
void setOnline(bool online) { m_online = online; }
void setOfflineName(const QString& offlineName) { m_offlineName = offlineName; }
void setDemo(bool demo) { m_demo = demo; }
void setProfiler(BaseProfilerFactory* profiler) { m_profiler = profiler; }
void setParentWidget(QWidget* widget) { m_parentWidget = widget; }
@ -68,7 +68,7 @@ class LaunchController : public Task {
void setAccountToUse(MinecraftAccountPtr accountToUse) { m_accountToUse = std::move(accountToUse); }
QString id() const { return m_instance->id(); }
QString id() { return m_instance->id(); }
bool abort() override;
@ -76,28 +76,27 @@ class LaunchController : public Task {
void login();
void launchInstance();
void decideAccount();
LaunchDecision decideLaunchMode();
bool askPlayDemo() const;
QString askOfflineName(const QString& playerName, bool* ok = nullptr) const;
bool reauthenticateAccount(const MinecraftAccountPtr& account, const QString& reason);
bool askPlayDemo();
QString askOfflineName(QString playerName, bool demo, bool* ok = nullptr);
bool reauthenticateAccount(MinecraftAccountPtr account);
private slots:
void readyForLaunch();
void onSucceeded();
void onFailed(QString reason);
void onProgressRequested(Task* task) const;
void onProgressRequested(Task* task);
private:
LaunchMode m_wantedLaunchMode = LaunchMode::Normal;
LaunchMode m_actualLaunchMode = LaunchMode::Normal;
BaseProfilerFactory* m_profiler = nullptr;
bool m_online = true;
QString m_offlineName;
BaseInstance* m_instance = nullptr;
bool m_demo = false;
InstancePtr m_instance;
QWidget* m_parentWidget = nullptr;
InstanceWindow* m_console = nullptr;
MinecraftAccountPtr m_accountToUse = nullptr;
AuthSessionPtr m_session = nullptr;
LaunchTask* m_launcher = nullptr;
MinecraftTarget::Ptr m_targetToJoin = nullptr;
AuthSessionPtr m_session;
shared_qobject_ptr<LaunchTask> m_launcher;
MinecraftTarget::Ptr m_targetToJoin;
};

View file

@ -1,25 +0,0 @@
// 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
enum class LaunchMode {
Normal,
Offline,
Demo,
};

View file

@ -15,18 +15,12 @@ fi
LAUNCHER_NAME=@Launcher_APP_BINARY_NAME@
LAUNCHER_ENVNAME=@Launcher_ENVName@
LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")"
echo "Launcher Dir: ${LAUNCHER_DIR}"
# Makes the launcher use portals for file picking
export QT_QPA_PLATFORMTHEME=xdgdesktopportal
# disable OpenGL and Vulkan launcher features on sharun until https://github.com/VHSgunzo/sharun/issues/35
if [[ -f "${LAUNCHER_DIR}/sharun" ]]; then
export ${LAUNCHER_ENVNAME}_DISABLE_GLVULKAN=1
fi
# Just to be sure...
chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}"

View file

@ -233,7 +233,7 @@ std::optional<QStringList> extractSubDir(ArchiveReader* zip, const QString& subd
<< target;
return false;
}
if (!f->writeFile(ext, target_file_path, target)) {
if (!f->writeFile(ext, target_file_path)) {
qWarning() << "Failed to extract file" << original_name << "to" << target_file_path;
return false;
}

View file

@ -25,7 +25,7 @@
#include "FileSystem.h"
#include "Json.h"
#include "LibraryUtils.h"
#include "MangoHud.h"
#ifdef __GLIBC__
#ifndef _GNU_SOURCE
@ -36,9 +36,9 @@
#include <linux/limits.h>
#endif
namespace LibraryUtils {
namespace MangoHud {
QString findMangoHud()
QString getLibraryString()
{
/**
* Guess MangoHud install location by searching for vulkan layers in this order:
@ -123,7 +123,7 @@ QString findMangoHud()
#ifdef __GLIBC__
// Check whether mangohud is usable on a glibc based system
QString libraryPath = find(libraryName);
QString libraryPath = findLibrary(libraryName);
if (!libraryPath.isEmpty()) {
return libraryPath;
}
@ -138,7 +138,7 @@ QString findMangoHud()
return {};
}
QString find(QString libName)
QString findLibrary(QString libName)
{
#ifdef __GLIBC__
const char* library = libName.toLocal8Bit().constData();
@ -161,11 +161,11 @@ QString find(QString libName)
dlclose(handle);
return fullPath;
#else
qWarning() << "LibraryUtils::find is not implemented on this platform";
qWarning() << "MangoHud::findLibrary is not implemented on this platform";
return {};
#endif
}
} // namespace LibraryUtils
} // namespace MangoHud
#ifdef UNDEF_GNU_SOURCE
#undef _GNU_SOURCE

View file

@ -21,9 +21,9 @@
#include <QString>
#include <QStringList>
namespace LibraryUtils {
namespace MangoHud {
QString findMangoHud();
QString getLibraryString();
QString find(QString libName);
} // namespace LibraryUtils
QString findLibrary(QString libName);
} // namespace MangoHud

View file

@ -41,8 +41,8 @@
class NullInstance : public BaseInstance {
Q_OBJECT
public:
NullInstance(SettingsObject* globalSettings, std::unique_ptr<SettingsObject> settings, const QString& rootDir)
: BaseInstance(globalSettings, std::move(settings), rootDir)
NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir)
: BaseInstance(globalSettings, settings, rootDir)
{
setVersionBroken(true);
}
@ -52,7 +52,7 @@ class NullInstance : public BaseInstance {
QString getStatusbarDescription() override { return tr("Unknown instance type"); };
QSet<QString> traits() const override { return {}; };
QString instanceConfigFolder() const override { return instanceRoot(); };
LaunchTask* createLaunchTask(AuthSessionPtr, MinecraftTarget::Ptr) override { return nullptr; }
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftTarget::Ptr) override { return nullptr; }
QList<Task::Ptr> createUpdateTask() override { return {}; }
QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); }
QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); }

View file

@ -67,5 +67,5 @@ class PSaveFile : public QSaveFile {
QString m_absoluteFilePath;
};
#else
using PSaveFile = QSaveFile;
#define PSaveFile QSaveFile
#endif

View file

@ -31,7 +31,7 @@
ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion version,
ResourceFolderModel* packs,
const std::shared_ptr<ResourceFolderModel> packs,
bool is_indexed)
: m_pack(std::move(pack)), m_pack_version(std::move(version)), m_pack_model(packs)
{
@ -88,7 +88,7 @@ void ResourceDownloadTask::downloadSucceeded()
m_pack_model->uninstallResource(oldFilename, true);
// also rename the shader config file
if (dynamic_cast<ShaderPackFolderModel*>(m_pack_model) != nullptr) {
if (dynamic_cast<ShaderPackFolderModel*>(m_pack_model.get()) != nullptr) {
QFileInfo oldConfig(m_pack_model->dir(), oldFilename + ".txt");
QFileInfo newConfig(m_pack_model->dir(), getFilename() + ".txt");

View file

@ -32,7 +32,7 @@ class ResourceDownloadTask : public SequentialTask {
public:
explicit ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion version,
ResourceFolderModel* packs,
std::shared_ptr<ResourceFolderModel> packs,
bool is_indexed = true);
const QString& getFilename() const { return m_pack_version.fileName; }
const QVariant& getVersionID() const { return m_pack_version.fileId; }
@ -44,7 +44,7 @@ class ResourceDownloadTask : public SequentialTask {
private:
ModPlatform::IndexedPack::Ptr m_pack;
ModPlatform::IndexedVersion m_pack_version;
ResourceFolderModel* m_pack_model;
const std::shared_ptr<ResourceFolderModel> m_pack_model;
NetJob::Ptr m_filesNetJob;
LocalResourceUpdateTask::Ptr m_update_task;

View file

@ -41,7 +41,7 @@ struct RuntimeContext {
return javaRealArchitecture;
}
void updateFromInstanceSettings(SettingsObject* instanceSettings)
void updateFromInstanceSettings(SettingsObjectPtr instanceSettings)
{
javaArchitecture = instanceSettings->get("JavaArchitecture").toString();
javaRealArchitecture = instanceSettings->get("JavaRealArchitecture").toString();

View file

@ -1,54 +1,20 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 r58Playz <r58playz@gmail.com>
* Copyright (C) 2024 timoreo <contact@timoreo.fr>
* Copyright (C) 2024 Trial97 <alexandru.tripon97@gmail.com>
* Copyright (C) 2025 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <QDebug>
#include <QString>
#include "HardwareInfo.h"
#include "sys.h"
#ifdef Q_OS_MACOS
#include <sys/sysctl.h>
#endif
#include <QFile>
#include <QMap>
#include <QProcess>
#include <QStandardPaths>
#ifdef Q_OS_MACOS
bool rosettaDetect()
{
int ret = 0;
size_t size = sizeof(ret);
if (sysctlbyname("sysctl.proc_translated", &ret, &size, nullptr, 0) == -1) {
if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
return false;
}
return ret == 1;
@ -85,13 +51,18 @@ QString useQTForArch()
return QSysInfo::currentCpuArchitecture();
}
int defaultMaxJvmMem()
int suitableMaxMem()
{
float totalRAM = (float)Sys::getSystemRam() / (float)Sys::mebibyte;
int maxMemoryAlloc;
// If totalRAM < 6GB, use (totalRAM / 1.5), else 4GB
if (const uint64_t totalRAM = HardwareInfo::totalRamMiB(); totalRAM < (4096 * 1.5))
return totalRAM / 1.5;
if (totalRAM < (4096 * 1.5))
maxMemoryAlloc = (int)(totalRAM / 1.5);
else
return 4096;
maxMemoryAlloc = 4096;
return maxMemoryAlloc;
}
QString getSupportedJavaArchitecture()

View file

@ -1,12 +1,9 @@
#pragma once
#include <cstdint>
#include <QString>
namespace SysInfo {
QString currentSystem();
QString useQTForArch();
QString getSupportedJavaArchitecture();
int defaultMaxJvmMem();
int suitableMaxMem();
} // namespace SysInfo

View file

@ -36,7 +36,7 @@ class Usable {
*/
class UseLock {
public:
UseLock(Usable* usable) : m_usable(usable)
UseLock(shared_qobject_ptr<Usable> usable) : m_usable(usable)
{
// this doesn't use shared pointer use count, because that wouldn't be correct. this count is separate.
m_usable->incrementUses();
@ -44,5 +44,5 @@ class UseLock {
~UseLock() { m_usable->decrementUses(); }
private:
Usable* m_usable;
shared_qobject_ptr<Usable> m_usable;
};

View file

@ -1,42 +1,124 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2023 flowln <flowlnlnln@gmail.com>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2026 Trial97 <alexandru.tripon97@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "Version.h"
#include <QDebug>
#include <QRegularExpressionMatch>
#include <QUrl>
#include <compare>
Version::Version(QString str) : m_string(std::move(str))
{
parse();
}
#define VERSION_OPERATOR(return_on_different) \
bool exclude_our_sections = false; \
bool exclude_their_sections = false; \
\
const auto size = qMax(m_sections.size(), other.m_sections.size()); \
for (int i = 0; i < size; ++i) { \
Section sec1 = (i >= m_sections.size()) ? Section() : m_sections.at(i); \
Section sec2 = (i >= other.m_sections.size()) ? Section() : other.m_sections.at(i); \
\
{ /* Don't include appendixes in the comparison */ \
if (sec1.isAppendix()) \
exclude_our_sections = true; \
if (sec2.isAppendix()) \
exclude_their_sections = true; \
\
if (exclude_our_sections) { \
sec1 = Section(); \
if (sec2.m_isNull) \
break; \
} \
\
if (exclude_their_sections) { \
sec2 = Section(); \
if (sec1.m_isNull) \
break; \
} \
} \
\
if (sec1 != sec2) \
return return_on_different; \
}
bool Version::operator<(const Version& other) const
{
VERSION_OPERATOR(sec1 < sec2)
return false;
}
bool Version::operator==(const Version& other) const
{
VERSION_OPERATOR(false)
return true;
}
bool Version::operator!=(const Version& other) const
{
return !operator==(other);
}
bool Version::operator<=(const Version& other) const
{
return *this < other || *this == other;
}
bool Version::operator>(const Version& other) const
{
return !(*this <= other);
}
bool Version::operator>=(const Version& other) const
{
return !(*this < other);
}
void Version::parse()
{
m_sections.clear();
QString currentSection;
if (m_string.isEmpty())
return;
auto classChange = [&currentSection](QChar lastChar, QChar currentChar) {
if (lastChar.isNull())
return false;
if (lastChar.isDigit() != currentChar.isDigit())
return true;
const QList<QChar> s_separators{ '.', '-', '+' };
if (s_separators.contains(currentChar) && currentSection.at(0) != currentChar)
return true;
return false;
};
currentSection += m_string.at(0);
for (int i = 1; i < m_string.size(); ++i) {
const auto& current_char = m_string.at(i);
if (classChange(m_string.at(i - 1), current_char)) {
if (!currentSection.isEmpty())
m_sections.append(Section(currentSection));
currentSection = "";
}
currentSection += current_char;
}
if (!currentSection.isEmpty())
m_sections.append(Section(currentSection));
}
/// qDebug print support for the Version class
QDebug operator<<(QDebug debug, const Version& v)
{
const QDebugStateSaver saver(debug);
QDebugStateSaver saver(debug);
debug.nospace() << "Version{ string: " << v.toString() << ", sections: [ ";
bool first = true;
for (const auto& s : v.m_sections) {
if (!first) {
for (auto s : v.m_sections) {
if (!first)
debug.nospace() << ", ";
}
debug.nospace() << s.value;
debug.nospace() << s.m_fullString;
first = false;
}
@ -44,114 +126,3 @@ QDebug operator<<(QDebug debug, const Version& v)
return debug;
}
std::strong_ordering Version::Section::operator<=>(const Section& other) const
{
// If both components are numeric, compare numerically (codepoint-wise)
if (this->t == Type::Numeric && other.t == Type::Numeric) {
auto aLen = this->value.size();
if (aLen != other.value.size()) {
// Lengths differ; compare by length
return aLen <=> other.value.size();
}
// Compare by digits
auto cmp = QString::compare(this->value, other.value);
if (cmp < 0) {
return std::strong_ordering::less;
}
if (cmp > 0) {
return std::strong_ordering::greater;
}
return std::strong_ordering::equal;
}
// One or both are null
if (this->t == Type::Null) {
if (other.t == Type::PreRelease) {
return std::strong_ordering::greater;
}
return std::strong_ordering::less;
}
if (other.t == Type::Null) {
if (this->t == Type::PreRelease) {
return std::strong_ordering::less;
}
return std::strong_ordering::greater;
}
// Textual comparison (differing type, or both textual/pre-release)
auto minLen = qMin(this->value.size(), other.value.size());
for (int i = 0; i < minLen; i++) {
auto a = this->value.at(i);
auto b = other.value.at(i);
if (a != b) {
// Compare by rune
return a.unicode() <=> b.unicode();
}
}
// Compare by length
return this->value.size() <=> other.value.size();
}
namespace {
void removeLeadingZeros(QString& s)
{
s.remove(0, std::distance(s.begin(), std::ranges::find_if_not(s, [](QChar c) { return c == '0'; })));
}
} // namespace
void Version::parse()
{
auto len = m_string.size();
for (int i = 0; i < len;) {
Section cur(Section::Type::Textual);
auto c = m_string.at(i);
if (c == '+') {
break; // Ignore appendices
}
// custom: the space is special to handle the strings like "1.20 Pre-Release 1"
// this is needed to support Modrinth versions
if (c == '-' || c == ' ') {
// Add dash to component
cur.value += c;
i++;
// If the next rune is non-digit, mark as pre-release (requires >= 1 non-digit after dash so the component has length > 1)
if (i < len && !m_string.at(i).isDigit()) {
cur.t = Section::Type::PreRelease;
}
} else if (c.isDigit()) {
// Mark as numeric
cur.t = Section::Type::Numeric;
}
for (; i < len; i++) {
auto r = m_string.at(i);
if ((r.isDigit() != (cur.t == Section::Type::Numeric)) // starts a new section
|| (r == ' ' && cur.t == Section::Type::Numeric) // custom: numeric section then a space is a pre-release
|| (r == '-' && cur.t != Section::Type::PreRelease) // "---" is a valid pre-release component
|| r == '+') {
// Run completed (do not consume this rune)
break;
}
// Add rune to current run
cur.value += r;
}
if (!cur.value.isEmpty()) {
if (cur.t == Section::Type::Numeric) {
removeLeadingZeros(cur.value);
}
m_sections.append(cur);
}
}
}
std::strong_ordering Version::operator<=>(const Version& other) const
{
const auto size = qMax(m_sections.size(), other.m_sections.size());
for (int i = 0; i < size; ++i) {
auto sec1 = (i >= m_sections.size()) ? Section() : m_sections.at(i);
auto sec2 = (i >= other.m_sections.size()) ? Section() : other.m_sections.at(i);
if (auto cmp = sec1 <=> sec2; cmp != std::strong_ordering::equal) {
return cmp;
}
}
return std::strong_ordering::equal;
}

View file

@ -3,7 +3,6 @@
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2023 flowln <flowlnlnln@gmail.com>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2026 Trial97 <alexandru.tripon97@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -16,6 +15,23 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
@ -25,36 +41,115 @@
#include <QString>
#include <QStringView>
// this implements the FlexVer
// https://git.sleeping.town/exa/FlexVer
class QUrl;
class Version {
public:
Version(QString str) : m_string(std::move(str)) { parse(); } // NOLINT(hicpp-explicit-conversions)
Version(QString str);
Version() = default;
private:
struct Section {
enum class Type : std::uint8_t { Null, Textual, Numeric, PreRelease };
explicit Section(Type t = Type::Null, QString value = "") : t(t), value(std::move(value)) {}
Type t;
QString value;
bool operator==(const Section& other) const = default;
std::strong_ordering operator<=>(const Section& other) const;
};
bool operator<(const Version& other) const;
bool operator<=(const Version& other) const;
bool operator>(const Version& other) const;
bool operator>=(const Version& other) const;
bool operator==(const Version& other) const;
bool operator!=(const Version& other) const;
private:
void parse();
public:
QString toString() const { return m_string; }
bool isEmpty() const { return m_string.isEmpty(); }
friend QDebug operator<<(QDebug debug, const Version& v);
bool operator==(const Version& other) const { return (*this <=> other) == std::strong_ordering::equal; }
std::strong_ordering operator<=>(const Version& other) const;
private:
struct Section {
explicit Section(QString fullString) : m_fullString(std::move(fullString))
{
qsizetype cutoff = m_fullString.size();
for (int i = 0; i < m_fullString.size(); i++) {
if (!m_fullString[i].isDigit()) {
cutoff = i;
break;
}
}
auto numPart = QStringView{ m_fullString }.left(cutoff);
if (!numPart.isEmpty()) {
m_isNull = false;
m_numPart = numPart.toInt();
}
auto stringPart = QStringView{ m_fullString }.mid(cutoff);
if (!stringPart.isEmpty()) {
m_isNull = false;
m_stringPart = stringPart.toString();
}
}
explicit Section() = default;
bool m_isNull = true;
int m_numPart = 0;
QString m_stringPart;
QString m_fullString;
inline bool isAppendix() const { return m_stringPart.startsWith('+'); }
inline bool isPreRelease() const { return m_stringPart.startsWith('-') && m_stringPart.length() > 1; }
inline bool operator==(const Section& other) const
{
if (m_isNull && !other.m_isNull)
return false;
if (!m_isNull && other.m_isNull)
return false;
if (!m_isNull && !other.m_isNull) {
return (m_numPart == other.m_numPart) && (m_stringPart == other.m_stringPart);
}
return true;
}
inline bool operator<(const Section& other) const
{
static auto unequal_is_less = [](Section const& non_null) -> bool {
if (non_null.m_stringPart.isEmpty())
return non_null.m_numPart == 0;
return (non_null.m_stringPart != QLatin1Char('.')) && non_null.isPreRelease();
};
if (!m_isNull && other.m_isNull)
return unequal_is_less(*this);
if (m_isNull && !other.m_isNull)
return !unequal_is_less(other);
if (!m_isNull && !other.m_isNull) {
if (m_numPart < other.m_numPart)
return true;
if (m_numPart == other.m_numPart && m_stringPart < other.m_stringPart)
return true;
if (!m_stringPart.isEmpty() && other.m_stringPart.isEmpty())
return false;
if (m_stringPart.isEmpty() && !other.m_stringPart.isEmpty())
return true;
return false;
}
return m_fullString < other.m_fullString;
}
inline bool operator!=(const Section& other) const { return !(*this == other); }
inline bool operator>(const Section& other) const { return !(*this < other || *this == other); }
};
private:
QString m_string;
QList<Section> m_sections;
};
void parse();
};

View file

@ -198,8 +198,9 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
return tr("Latest");
}
}
} else {
return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole);
}
return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole);
}
case Qt::DecorationRole: {
if (column == Name && hasRecommended) {

View file

@ -23,10 +23,7 @@
#include <archive_entry.h>
#include <QDir>
#include <QFileInfo>
#include <QUrl>
#include <functional>
#include <memory>
#include <optional>
namespace MMCZip {
QStringList ArchiveReader::getFiles()
@ -37,36 +34,25 @@ QStringList ArchiveReader::getFiles()
bool ArchiveReader::collectFiles(bool onlyFiles)
{
return parse([this, onlyFiles](File* f) {
if (!onlyFiles || f->isFile()) {
if (!onlyFiles || f->isFile())
m_fileNames << f->filename();
}
return f->skip();
});
}
using getPathFunc = std::function<const char*(archive_entry*)>;
static QString decodeLibArchivePath(archive_entry* entry, const getPathFunc& getUtf8Path, const getPathFunc& getPath)
{
auto fileName = QString::fromUtf8(getUtf8Path(entry));
if (fileName.isEmpty()) {
fileName = QString::fromLocal8Bit(getPath(entry));
}
return fileName;
}
QString ArchiveReader::File::filename()
{
return decodeLibArchivePath(m_entry, archive_entry_pathname_utf8, archive_entry_pathname);
return QString::fromUtf8(archive_entry_pathname_utf8(m_entry));
}
QByteArray ArchiveReader::File::readAll(int* outStatus)
{
QByteArray data;
const void* buff = nullptr;
size_t size = 0;
la_int64_t offset = 0;
const void* buff;
size_t size;
la_int64_t offset;
int status = 0;
int status;
while ((status = archive_read_data_block(m_archive.get(), &buff, &size, &offset)) == ARCHIVE_OK) {
data.append(static_cast<const char*>(buff), static_cast<qsizetype>(size));
}
@ -92,10 +78,10 @@ int ArchiveReader::File::readNextHeader()
return archive_read_next_header(m_archive.get(), &m_entry);
}
auto ArchiveReader::goToFile(const QString& filename) -> std::unique_ptr<File>
auto ArchiveReader::goToFile(QString filename) -> std::unique_ptr<File>
{
auto f = std::make_unique<File>();
auto* a = f->m_archive.get();
auto a = f->m_archive.get();
archive_read_support_format_all(a);
archive_read_support_filter_all(a);
auto fileName = m_archivePath.toStdWString();
@ -117,16 +103,15 @@ auto ArchiveReader::goToFile(const QString& filename) -> std::unique_ptr<File>
static int copy_data(struct archive* ar, struct archive* aw, bool notBlock = false)
{
int r = 0;
const void* buff = nullptr;
size_t size = 0;
la_int64_t offset = 0;
int r;
const void* buff;
size_t size;
la_int64_t offset;
for (;;) {
r = archive_read_data_block(ar, &buff, &size, &offset);
if (r == ARCHIVE_EOF) {
return ARCHIVE_OK;
}
if (r == ARCHIVE_EOF)
return (ARCHIVE_OK);
if (r < ARCHIVE_OK) {
qCritical() << "Failed reading data block:" << archive_error_string(ar);
return (r);
@ -143,43 +128,9 @@ static int copy_data(struct archive* ar, struct archive* aw, bool notBlock = fal
}
}
static bool willEscapeRoot(const QDir& root, archive_entry* entry)
bool ArchiveReader::File::writeFile(archive* out, QString targetFileName, bool notBlock)
{
auto entryPath = decodeLibArchivePath(entry, archive_entry_pathname_utf8, archive_entry_pathname);
auto linkTarget = decodeLibArchivePath(entry, archive_entry_symlink_utf8, archive_entry_symlink);
auto hardLink = decodeLibArchivePath(entry, archive_entry_hardlink_utf8, archive_entry_hardlink);
if (entryPath.isEmpty() || (linkTarget.isEmpty() && hardLink.isEmpty())) {
return false;
}
bool isHardLink = false;
if (isHardLink = linkTarget.isEmpty(); isHardLink) {
linkTarget = hardLink;
}
QString linkFullPath = root.filePath(entryPath);
auto rootDir = QUrl::fromLocalFile(root.absolutePath());
if (!rootDir.isParentOf(QUrl::fromLocalFile(linkFullPath))) {
return true;
}
QDir linkDir = QFileInfo(linkFullPath).dir();
if (!QDir::isAbsolutePath(linkTarget)) {
linkTarget = (!isHardLink ? linkDir : root).filePath(linkTarget);
}
return !rootDir.isParentOf(QUrl::fromLocalFile(QDir::cleanPath(linkTarget)));
}
bool ArchiveReader::File::writeFile(archive* out, const QString& targetFileName, bool notBlock)
{
return writeFile(out, targetFileName, {}, notBlock);
};
bool ArchiveReader::File::writeFile(archive* out, const QString& targetFileName, std::optional<QDir> root, bool notBlock)
{
auto* entry = m_entry;
auto entry = m_entry;
std::unique_ptr<archive_entry, decltype(&archive_entry_free)> entryClone(nullptr, &archive_entry_free);
if (!targetFileName.isEmpty()) {
entryClone.reset(archive_entry_clone(m_entry));
@ -187,34 +138,26 @@ bool ArchiveReader::File::writeFile(archive* out, const QString& targetFileName,
auto nameUtf8 = targetFileName.toUtf8();
archive_entry_set_pathname_utf8(entry, nameUtf8.constData());
}
if (root.has_value() && willEscapeRoot(root.value(), entry)) {
qCritical() << "Failed to write header to entry:" << filename() << "-" << "file outside root";
return false;
}
if (archive_write_header(out, entry) < ARCHIVE_OK) {
qCritical() << "Failed to write header to entry:" << filename() << "-" << archive_error_string(out) << targetFileName;
qCritical() << "Failed to write header to entry:" << filename() << "-" << archive_error_string(out);
return false;
}
if (archive_entry_size(m_entry) > 0) {
} else if (archive_entry_size(m_entry) > 0) {
auto r = copy_data(m_archive.get(), out, notBlock);
if (r < ARCHIVE_OK) {
if (r < ARCHIVE_OK)
qCritical() << "Failed reading data block:" << archive_error_string(out);
}
if (r < ARCHIVE_WARN) {
if (r < ARCHIVE_WARN)
return false;
}
}
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);
}
return (r >= ARCHIVE_WARN);
}
bool ArchiveReader::parse(const std::function<bool(File*, bool&)>& doStuff)
bool ArchiveReader::parse(std::function<bool(File*, bool&)> doStuff)
{
auto f = std::make_unique<File>();
auto* a = f->m_archive.get();
auto a = f->m_archive.get();
archive_read_support_format_all(a);
archive_read_support_filter_all(a);
auto fileName = m_archivePath.toStdWString();
@ -238,7 +181,7 @@ bool ArchiveReader::parse(const std::function<bool(File*, bool&)>& doStuff)
return true;
}
bool ArchiveReader::parse(const std::function<bool(File*)>& doStuff)
bool ArchiveReader::parse(std::function<bool(File*)> doStuff)
{
return parse([doStuff](File* f, bool&) { return doStuff(f); });
}
@ -262,32 +205,26 @@ QString ArchiveReader::getZipName()
bool ArchiveReader::exists(const QString& filePath) const
{
if (filePath == QLatin1String("/") || filePath.isEmpty()) {
if (filePath == QLatin1String("/") || filePath.isEmpty())
return true;
}
// Normalize input path (remove trailing slash, if any)
QString normalizedPath = QDir::cleanPath(filePath);
if (normalizedPath.startsWith('/')) {
if (normalizedPath.startsWith('/'))
normalizedPath.remove(0, 1);
}
if (normalizedPath == QLatin1String(".")) {
if (normalizedPath == QLatin1String("."))
return true;
}
if (normalizedPath == QLatin1String("..")) {
if (normalizedPath == QLatin1String(".."))
return false; // root only
}
// Check for exact file match
if (m_fileNames.contains(normalizedPath, Qt::CaseInsensitive)) {
if (m_fileNames.contains(normalizedPath, Qt::CaseInsensitive))
return true;
}
// Check for directory existence by seeing if any file starts with that path
QString dirPath = normalizedPath + QLatin1Char('/');
for (const QString& f : m_fileNames) {
if (f.startsWith(dirPath, Qt::CaseInsensitive)) {
if (f.startsWith(dirPath, Qt::CaseInsensitive))
return true;
}
}
return false;

View file

@ -19,11 +19,8 @@
#include <QByteArray>
#include <QDateTime>
#include <QDir>
#include <QStringList>
#include <memory>
#include <optional>
#include <utility>
struct archive;
struct archive_entry;
@ -31,7 +28,7 @@ namespace MMCZip {
class ArchiveReader {
public:
using ArchivePtr = std::unique_ptr<struct archive, int (*)(struct archive*)>;
explicit ArchiveReader(QString fileName) : m_archivePath(std::move(fileName)) {}
ArchiveReader(QString fileName) : m_archivePath(fileName) {}
virtual ~ArchiveReader() = default;
QStringList getFiles();
@ -51,8 +48,7 @@ class ArchiveReader {
QByteArray readAll(int* outStatus = nullptr);
bool skip();
bool writeFile(archive* out, const QString& targetFileName = "", bool notBlock = false);
bool writeFile(archive* out, const QString& targetFileName, std::optional<QDir> root, bool notBlock = false);
bool writeFile(archive* out, QString targetFileName = "", bool notBlock = false);
private:
int readNextHeader();
@ -63,14 +59,14 @@ class ArchiveReader {
archive_entry* m_entry;
};
std::unique_ptr<File> goToFile(const QString& filename);
bool parse(const std::function<bool(File*)>&);
bool parse(const std::function<bool(File*, bool&)>&);
std::unique_ptr<File> goToFile(QString filename);
bool parse(std::function<bool(File*)>);
bool parse(std::function<bool(File*, bool&)>);
private:
QString m_archivePath;
size_t m_blockSize = 10240;
QStringList m_fileNames;
QStringList m_fileNames = {};
};
} // namespace MMCZip

View file

@ -174,7 +174,7 @@ bool ArchiveWriter::addFile(const QString& fileName, const QString& fileDest)
if (fileInfo.isFile() && !fileInfo.isSymLink()) {
QFile file(fileInfo.absoluteFilePath());
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Failed to open file:" << fileInfo.filePath() << "error:" << file.errorString();
qCritical() << "Failed to open file:" << fileInfo.filePath();
return false;
}

View file

@ -95,7 +95,7 @@ auto ExtractZipTask::extractZip() -> ZipResult
return false;
}
if (!f->writeFile(ext, target_file_path, target)) {
if (!f->writeFile(ext, target_file_path)) {
result = ZipResult(tr("Failed to extract file %1 to %2").arg(original_name, target_file_path));
return false;
}

View file

@ -24,16 +24,12 @@
#endif
#include <windows.h>
#include <consoleapi.h>
#include <fcntl.h>
#include <fileapi.h>
#include <io.h>
#include <stdio.h>
#include <cstddef>
#include <iostream>
namespace console {
void RedirectHandle(DWORD handle, FILE* stream, const char* mode)
{
HANDLE stdHandle = GetStdHandle(handle);
@ -161,31 +157,3 @@ std::error_code EnableAnsiSupport()
return {};
}
void FreeWindowsConsole()
{
fclose(stdout);
fclose(stdin);
fclose(stderr);
FreeConsole();
}
WindowsConsoleGuard::WindowsConsoleGuard() : m_consoleAttached(false)
{
if (console::AttachWindowsConsole()) {
m_consoleAttached = true;
if (auto err = console::EnableAnsiSupport(); err) {
std::cout << "Error setting up ansi console" << err.message() << std::endl;
}
}
}
WindowsConsoleGuard::~WindowsConsoleGuard()
{
// Detach from Windows console
if (m_consoleAttached) {
console::FreeWindowsConsole();
}
}
} // namespace console

View file

@ -21,24 +21,8 @@
#pragma once
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <system_error>
namespace console {
void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr);
bool AttachWindowsConsole();
std::error_code EnableAnsiSupport();
void FreeWindowsConsole();
class WindowsConsoleGuard {
public:
WindowsConsoleGuard();
~WindowsConsoleGuard();
private:
bool m_consoleAttached;
};
} // namespace console

View file

@ -34,11 +34,26 @@
#include <DesktopServices.h>
#include <sys.h>
#if defined Q_OS_WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include "console/WindowsConsole.h"
#endif
#include <filesystem>
namespace fs = std::filesystem;
FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv), socket(new QLocalSocket(this))
{
#if defined Q_OS_WIN32
// attach the parent console
if (AttachWindowsConsole()) {
consoleAttached = true;
}
#endif
setOrganizationName(BuildConfig.LAUNCHER_NAME);
setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN);
setApplicationName(BuildConfig.LAUNCHER_NAME + "FileLink");
@ -221,4 +236,13 @@ FileLinkApp::~FileLinkApp()
qDebug() << "link program shutting down";
// Shut down logger by setting the logger function to nothing
qInstallMessageHandler(nullptr);
#if defined Q_OS_WIN32
// Detach from Windows console
if (consoleAttached) {
fclose(stdout);
fclose(stdin);
fclose(stderr);
}
#endif
}

View file

@ -38,6 +38,7 @@
#include "FileSystem.h"
class FileLinkApp : public QCoreApplication {
// friends for the purpose of limiting access to deprecated stuff
Q_OBJECT
public:
enum Status { Starting, Failed, Succeeded, Initialized };
@ -63,4 +64,8 @@ class FileLinkApp : public QCoreApplication {
QList<FS::LinkPair> m_links_to_make;
QList<FS::LinkResult> m_path_results;
#if defined Q_OS_WIN32
// used on Windows to attach the standard IO streams
bool consoleAttached = false;
#endif
};

View file

@ -22,17 +22,8 @@
#include "FileLink.h"
#if defined Q_OS_WIN32
#include "console/WindowsConsole.h"
#endif
int main(int argc, char* argv[])
{
#if defined Q_OS_WIN32
// attach the parent console
console::WindowsConsoleGuard _consoleGuard;
#endif
FileLinkApp ldh(argc, argv);
switch (ldh.status()) {

View file

@ -3,7 +3,6 @@
#define PRISM_PRECOMPILED_BASE_HEADERS_H
#include <algorithm>
#include <cstddef>
#include <functional>
#include <memory>
#include <optional>
@ -13,5 +12,6 @@
#include <FileSystem.h>
#include <Json.h>
#include <Version.h>
#include <sys.h>
#endif // PRISM_PRECOMPILED_BASE_HEADERS_H

View file

@ -5,15 +5,9 @@
#include <QEvent>
#include <QMetaType>
#include <QObject>
#include <QPointer>
#include <QSharedPointer>
#include <QVariant>
#include <QVariantMap>
#include <QDebug>
#include <QLoggingCategory>
#include <QCoreApplication>

View file

@ -163,7 +163,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
auto os_arch = results["os.arch"];
auto java_version = results["java.version"];
auto java_vendor = results["java.vendor"];
bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64" || os_arch == "riscv64" || os_arch == "ppc64le" || os_arch == "ppc64";
bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64" || os_arch == "riscv64";
result.validity = Result::Validity::Valid;
result.is_64bit = is_64;

View file

@ -49,7 +49,7 @@ bool JavaInstall::operator<(BaseVersion& a) const
{
try {
return operator<(dynamic_cast<JavaInstall&>(a));
} catch (const std::bad_cast&) {
} catch (const std::bad_cast& e) {
return BaseVersion::operator<(a);
}
}
@ -58,7 +58,7 @@ bool JavaInstall::operator>(BaseVersion& a) const
{
try {
return operator>(dynamic_cast<JavaInstall&>(a));
} catch (const std::bad_cast&) {
} catch (const std::bad_cast& e) {
return BaseVersion::operator>(a);
}
}

View file

@ -39,6 +39,7 @@ struct JavaInstall : public BaseVersion {
JavaVersion id;
QString arch;
QString path;
bool recommended = false;
bool is_64bit = false;
};

View file

@ -41,7 +41,6 @@
#include <algorithm>
#include "Application.h"
#include "settings/SettingsObject.h"
#include "java/JavaChecker.h"
#include "java/JavaInstallList.h"
#include "java/JavaUtils.h"
@ -51,9 +50,8 @@ JavaInstallList::JavaInstallList(QObject* parent, bool onlyManagedVersions)
: BaseVersionList(parent), m_only_managed_versions(onlyManagedVersions)
{}
Task::Ptr JavaInstallList::getLoadTask(bool forceReload)
Task::Ptr JavaInstallList::getLoadTask()
{
Q_UNUSED(forceReload)
load();
return getCurrentTask();
}
@ -109,7 +107,7 @@ QVariant JavaInstallList::data(const QModelIndex& index, int role) const
case VersionRole:
return version->id.toString();
case RecommendedRole:
return false;
return version->recommended;
case PathRole:
return version->path;
case CPUArchitectureRole:
@ -129,6 +127,10 @@ void JavaInstallList::updateListData(QList<BaseVersion::Ptr> versions)
beginResetModel();
m_vlist = versions;
sortVersions();
if (m_vlist.size()) {
auto best = std::dynamic_pointer_cast<JavaInstall>(m_vlist[0]);
best->recommended = true;
}
endResetModel();
m_status = Status::Done;
m_load_task.reset();

View file

@ -35,7 +35,7 @@ class JavaInstallList : public BaseVersionList {
public:
explicit JavaInstallList(QObject* parent = 0, bool onlyManagedVersions = false);
Task::Ptr getLoadTask(bool forceReload = false) override;
Task::Ptr getLoadTask() override;
bool isLoaded() override;
const BaseVersion::Ptr at(int i) const override;
int count() const override;

View file

@ -111,7 +111,7 @@ bool Metadata::operator<(BaseVersion& a) const
{
try {
return operator<(dynamic_cast<Metadata&>(a));
} catch (const std::bad_cast&) {
} catch (const std::bad_cast& e) {
return BaseVersion::operator<(a);
}
}
@ -120,7 +120,7 @@ bool Metadata::operator>(BaseVersion& a) const
{
try {
return operator>(dynamic_cast<Metadata&>(a));
} catch (const std::bad_cast&) {
} catch (const std::bad_cast& e) {
return BaseVersion::operator>(a);
}
}

View file

@ -42,7 +42,6 @@
#include <QDebug>
#include "Application.h"
#include "BuildConfig.h"
#include "FileSystem.h"
#include "java/JavaInstallList.h"
#include "java/JavaUtils.h"
@ -156,7 +155,7 @@ JavaInstallPtr JavaUtils::GetDefaultJava()
QStringList addJavasFromEnv(QList<QString> javas)
{
auto env = QProcessEnvironment::systemEnvironment().value(QStringLiteral("%1_JAVA_PATHS").arg(BuildConfig.LAUNCHER_ENVNAME));
auto env = qEnvironmentVariable("PRISMLAUNCHER_JAVA_PATHS"); // FIXME: use launcher name from buildconfig
#if defined(Q_OS_WIN32)
QList<QString> javaPaths = env.replace("\\", "/").split(QLatin1String(";"));

View file

@ -39,8 +39,9 @@ void ManifestDownloadTask::executeTask()
{
setStatus(tr("Downloading Java"));
auto download = makeShared<NetJob>(QString("JRE::DownloadJava"), APPLICATION->network());
auto files = std::make_shared<QByteArray>();
auto [action, files] = Net::Download::makeByteArray(m_url);
auto action = Net::Download::makeByteArray(m_url, files);
if (!m_checksum_hash.isEmpty() && !m_checksum_type.isEmpty()) {
auto hashType = QCryptographicHash::Algorithm::Sha1;
if (m_checksum_type == "sha256") {

View file

@ -51,14 +51,14 @@ void LaunchTask::init()
m_instance->setRunning(true);
}
std::unique_ptr<LaunchTask> LaunchTask::create(MinecraftInstance* inst)
shared_qobject_ptr<LaunchTask> LaunchTask::create(MinecraftInstancePtr inst)
{
auto task = std::unique_ptr<LaunchTask>(new LaunchTask(inst));
task->init();
return task;
shared_qobject_ptr<LaunchTask> proc(new LaunchTask(inst));
proc->init();
return proc;
}
LaunchTask::LaunchTask(MinecraftInstance* instance) : m_instance(instance) {}
LaunchTask::LaunchTask(MinecraftInstancePtr instance) : m_instance(instance) {}
void LaunchTask::appendStep(shared_qobject_ptr<LaunchStep> step)
{
@ -180,7 +180,7 @@ bool LaunchTask::abort()
return true;
case LaunchTask::NotStarted: {
state = LaunchTask::Aborted;
emitAborted();
emitFailed("Aborted");
return true;
}
case LaunchTask::Running:

View file

@ -39,6 +39,7 @@
#include <QObjectPtr.h>
#include <minecraft/MinecraftInstance.h>
#include <QProcess>
#include "BaseInstance.h"
#include "LaunchStep.h"
#include "LogModel.h"
#include "MessageLevel.h"
@ -47,21 +48,21 @@
class LaunchTask : public Task {
Q_OBJECT
protected:
explicit LaunchTask(MinecraftInstance* instance);
explicit LaunchTask(MinecraftInstancePtr instance);
void init();
public:
enum State { NotStarted, Running, Waiting, Failed, Aborted, Finished };
public: /* methods */
static std::unique_ptr<LaunchTask> create(MinecraftInstance* inst);
static shared_qobject_ptr<LaunchTask> create(MinecraftInstancePtr inst);
virtual ~LaunchTask() = default;
void appendStep(shared_qobject_ptr<LaunchStep> step);
void prependStep(shared_qobject_ptr<LaunchStep> step);
void setCensorFilter(QMap<QString, QString> filter);
MinecraftInstance* instance() { return m_instance; }
MinecraftInstancePtr instance() { return m_instance; }
void setPid(qint64 pid) { m_pid = pid; }
@ -118,7 +119,7 @@ class LaunchTask : public Task {
bool parseXmlLogs(QString const& line, MessageLevel level);
protected: /* data */
MinecraftInstance* m_instance;
MinecraftInstancePtr m_instance;
shared_qobject_ptr<LogModel> m_logModel;
QList<shared_qobject_ptr<LaunchStep>> m_steps;
QMap<QString, QString> m_censorFilter;

View file

@ -23,7 +23,10 @@ void TaskStepWrapper::executeTask()
return;
}
connect(m_task.get(), &Task::finished, this, &TaskStepWrapper::updateFinished);
propagateFromOther(m_task.get());
connect(m_task.get(), &Task::progress, this, &TaskStepWrapper::setProgress);
connect(m_task.get(), &Task::stepProgress, this, &TaskStepWrapper::propagateStepProgress);
connect(m_task.get(), &Task::status, this, &TaskStepWrapper::setStatus);
connect(m_task.get(), &Task::details, this, &TaskStepWrapper::setDetails);
emit progressReportingRequest();
}
@ -56,7 +59,9 @@ bool TaskStepWrapper::canAbort() const
bool TaskStepWrapper::abort()
{
if (m_task && m_task->canAbort()) {
return m_task->abort();
auto status = m_task->abort();
emitFailed("Aborted.");
return status;
}
return Task::abort();
}

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