Compare commits

..

229 Commits
develop ... 9.3

Author SHA1 Message Date
Seth Flynn
ce0a730531
[Backport release-9.x] Use default palette on all system themes (#3585) 2025-04-06 15:57:45 -04:00
Seth Flynn
f00226f797 fix(SystemTheme): use default palette on all system themes
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit e5861129ad58a8b5cdf55ed69bb9a7ce92811b87)
2025-04-06 19:55:13 +00:00
Seth Flynn
a96a915d79
[Backport release-9.x] Nix Flake spring cleaning (#3576) 2025-04-04 15:04:53 -04:00
Seth Flynn
83f79c93f8
chore(nix): set stable version
Signed-off-by: Seth Flynn <getchoo@tuta.io>
2025-04-04 05:24:38 -04:00
Seth Flynn
b228800de1
chore(gitignore): add more nix-related files
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit de08d7c3644e96a3694f232b53c81f53c40c077a)
2025-04-04 05:07:19 -04:00
Seth Flynn
e8bdc72476
refactor(nix): use date for version
Helps avoid needless rebuilds where only the revision changed. Also
better conforms to Nixpkgs' version standards

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit e9cac2e0e37c4531f662545bdc45b72499654642)
2025-04-04 05:07:19 -04:00
Seth Flynn
da9c359184
chore(nix): clone git submodules automatically
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 9b38226f8cb0e2f82bcb017c686fd03520990dd1)
2025-04-04 05:07:19 -04:00
Seth Flynn
b02718ee57
chore(nix): add nice welcome message to dev shell
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 38ec7def324891c5062d9b58c1180d44494f91d1)
2025-04-04 05:07:19 -04:00
Seth Flynn
ddcd61c808
refactor(nix): rely more on setup hooks in dev shell
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit de923a07d8a4b7c9f8cd688b9682587c78116e14)
2025-04-04 05:07:18 -04:00
Seth Flynn
760fbdf54f
build(nix): properly wrap development shell
Allows actually running the executables built in the development shell

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 2d4bc09cb9bec621997e00c23acac0b7cf3e99d7)
2025-04-04 05:07:18 -04:00
Seth Flynn
5c4bd3db52
fix(nix): only create compile_commands.json if it doesn't exist
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 58579539d071c569800ba0370a6d5918de025e33)
2025-04-04 05:07:18 -04:00
Seth Flynn
87c5b6cc83
refactor(nix): cleanup flake
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 32b49ecb84da5683bce42e2e888319f4517f1d53)
2025-04-04 05:07:16 -04:00
Seth Flynn
80ff62c96d
refactor(nix): pin flake-compat in default.nix
Avoids polluting downstream flake.locks

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit c367f48ec99f048f8d4bcaf0634370be33b29c62)
2025-04-04 05:06:05 -04:00
Seth Flynn
b5fc23952e
Reapply "refactor(nix): nix-filter -> lib.fileset"
After extensive (5 minutes) of testing, it seems we don't actually come
across any Nix bugs with lib.fileset! (aside from those inherit to
it...but 🤷)

This reverts commit a49a58bc4571f87f42f45bb9eeb1a957d5d12331.

Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 99852c972c6e9fc6a8caca727946a71a85c1672f)
2025-04-04 05:06:01 -04:00
Alexandru Ionut Tripon
5c8ef3a003
[Backport release-9.x] feat: add updater dialogues (#3568) 2025-04-01 18:31:09 +03:00
Sticks
b0ee4f6e58 fix: remove qWarning for unknown state
Co-authored-by: TheKodeToad <TheKodeToad@proton.me>
Signed-off-by: Sticks <sticks@teamhydra.dev>
(cherry picked from commit d4e1851e6779d99e3ffde79c7e4e8edb568415ce)
2025-04-01 15:10:15 +00:00
Sticks
25ce20a877 fix: Grammar correction in failed message for update dialogues
Co-authored-by: TheKodeToad <TheKodeToad@proton.me>
Signed-off-by: Sticks <sticks@teamhydra.dev>
(cherry picked from commit ea5458ed2253a4e8a9ef50d076fe72c57d2cb669)
2025-04-01 15:10:15 +00:00
sticks
2952b1d6b6 feat: add updater dialogues
Signed-off-by: sticks <tanner@teamhydra.dev>
(cherry picked from commit 998bc660fa97f4527443dd826eede5e8eab23651)
2025-04-01 15:10:15 +00:00
Alexandru Ionut Tripon
416495cda7
[Backport release-9.x] Fix typo in NetRequest.cpp (#3562) 2025-03-31 08:20:18 +03:00
LAHarbottle
6de8ed91a3 Fix typo in NetRequest.cpp
Signed-off-by: LAHarbottle <87842870+LAHarbottle@users.noreply.github.com>
(cherry picked from commit 66c6399adedcb52c2b7391cdbf38602f8b2814a6)
2025-03-31 05:13:04 +00:00
Alexandru Ionut Tripon
f9ab98f2cf
[Backport release-9.x] fix themes leak (#3556) 2025-03-30 01:55:52 +02:00
Trial97
fcc1e80237 fix themes leak
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 5b12d3cfff9092c0364e455c211fc189d87039cd)
2025-03-29 23:55:27 +00:00
Alexandru Ionut Tripon
1a56396e2e
[Backport release-9.x] Pin AppImage tooling in CI, drop libfuse2 (#3551) 2025-03-27 09:51:49 +02:00
Seth Flynn
0f21e01b46
ci: don't install libfuse2
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 671aad88f52e3fbba872e80f2805c0c5c64b7a8d)
2025-03-26 21:15:39 -04:00
Seth Flynn
88109eaa71
ci: pin appimage tooling
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 5d5155bb22f9935e17e33008a644736404629928)
2025-03-26 21:15:39 -04:00
Seth Flynn
1425c9801e
ci: use bundled linuxdeploy appimage plugin
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 6ef59fe984777e2620b445a78493afb86074eb8b)
2025-03-26 21:15:39 -04:00
Alexandru Ionut Tripon
1a06e34fe4
[Backport release-9.x] Use correct colours for all system themes (#3539) 2025-03-24 19:25:27 +02:00
TheKodeToad
d0a77df703 Try best to avoid regression
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 513959750f23719b55030cd34b9a8f5b248289c5)
2025-03-24 17:23:35 +00:00
TheKodeToad
57120e0c9d Use correct colours for all system themes
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit f08478c7ece0cd4e13b0bc188d0dcb9ea04c3199)
2025-03-24 17:23:35 +00:00
Alexandru Ionut Tripon
57981a070f
[Backport release-9.x] Fix crash with low disk space (#3538) 2025-03-24 19:04:38 +02:00
Alexandru Ionut Tripon
be1a1baba4
[Backport release-9.x] fix account help link (#3537) 2025-03-24 19:04:25 +02:00
Trial97
9686a3c81c fix crash on accessing reseted output
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 9ce5587f60d637d48bf20a8b9a40f020898710de)
2025-03-24 17:03:13 +00:00
Trial97
b348114dee rename variable
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 269938dfd866df5918eb44779667142f7a157c22)
2025-03-24 17:03:13 +00:00
Trial97
07604c9271 fix account help link
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit a4472406b96faabf2bdb78d152fc0e66bfe4ac63)
2025-03-24 16:58:49 +00:00
Alexandru Ionut Tripon
b49e9763bf
[Backport release-9.x] Fix crash with invalid quilt mod info (#3506) 2025-03-15 00:12:19 +02:00
Alexandru Ionut Tripon
b9d15a4781 Update launcher/minecraft/mod/tasks/LocalModParseTask.cpp
Signed-off-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
(cherry picked from commit 88bdb6541e3a57d6f84e75080dd6d24c1589a73d)
2025-03-14 22:08:50 +00:00
Trial97
190d17acd3 Fix crash with invalid quilt mod info
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 0626e354a026f9a2a35a4d6e28a0af105cb22986)
2025-03-14 22:08:50 +00:00
Alexandru Ionut Tripon
5de91076ea
[Backport release-9.x] fix mod load with empty gameversions (#3490) 2025-03-10 14:41:07 +02:00
Trial97
299cbf8009 fix mod load with empty gameversions
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 29b3eb5cc6fe3ba48a28598222212966bade1444)
2025-03-10 12:40:50 +00:00
Alexandru Ionut Tripon
70ca7af205
[Backport release-9.x] fix beginResetModel called before endResetModel (#3486) 2025-03-09 14:05:42 +02:00
Alexandru Ionut Tripon
19aaaa9988
[Backport release-9.x] fix null mainwindow in case of login on setup (#3485) 2025-03-09 14:05:35 +02:00
Trial97
fc639c0902 fix beginResetModel called before endResetModel
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit c441ae05df58eecf315c1379962a59d52c263f14)
2025-03-09 12:05:09 +00:00
Trial97
2bb094a90b fix null mainwindow in case of login on setup
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 7db717ee7c503aef3f3b70bbc4becb8c20485897)
2025-03-09 12:05:03 +00:00
Trial97
c206064976 rename some variables
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 162bbcfe19e67f23bf8d21142fdad253500a3b64)
2025-03-09 12:05:03 +00:00
Alexandru Ionut Tripon
d8f51e900a
[Backport release-9.x] chore: Update year range (#3462) 2025-03-03 08:27:33 +02:00
txtsd
b85e53eaf9 chore: Update year range
Signed-off-by: txtsd <code@ihavea.quest>
(cherry picked from commit f99b7e9ebd17129448aabd630c87e8a15fb2734c)
2025-03-03 06:27:10 +00:00
Alexandru Ionut Tripon
0073d82863
[Backport release-9.x] Default to Fusion based Dark/Bright themes on Windows (#3457) 2025-03-02 08:52:23 +02:00
TheKodeToad
70db5a2f92 Default to Fusion based Dark/Bright themes on Windows
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 5261b615d72eb0adbeb9c53e30b73448d722cf11)
2025-03-02 06:49:59 +00:00
Alexandru Ionut Tripon
07010f226e
[Backport release-9.x] Recognize riscv64 as a 64-bit platform (#3428) 2025-02-17 20:35:08 +02:00
thonkdifferent
d75b23c092 Recognize riscv64 as a 64-bit platform
Currently PrismLauncher complains about the installed JDK being a 32-bit version, despite it being compiled for 64-bit RISC-V `riscv64`.

This PR fixes this issue.

Signed-off-by: thonkdifferent <41342923+thonkdifferent@users.noreply.github.com>
(cherry picked from commit 8f1750df51668a94ab750dd05345ec5cc4e124c2)
2025-02-17 18:33:29 +00:00
Alexandru Ionut Tripon
748b92571c
[Backport release-9.x] make sure if user changes java path also disable java management (#3426) 2025-02-17 20:32:54 +02:00
Trial97
97180324ee
make sure if user changes java path also disable java management
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2025-02-17 16:14:15 +02:00
Rachel Powers
ec7aaaadf3
[Backport release-9.x] add libMangoHud_shim.so to mangohub preloadlist (#3418) 2025-02-13 14:42:35 -07:00
Rachel Powers
7d786987ad
[Backport release-9.x] make sure if user changes java path also disable java management (#3417) 2025-02-13 14:42:26 -07:00
Trial97
1ab6ce42b5 add libMangoHud_shim.so to mangohub preloadlist
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 9e954548dd702c05fbf33474c9c86f52fb67d4b4)
2025-02-13 21:41:14 +00:00
Trial97
05bf9c3689 make sure if user changes java path also disable java management
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 174cddcf42f26b24a5a74ce2eaf4923b5b269d47)
2025-02-13 21:40:54 +00:00
Alexandru Ionut Tripon
f760f08df6
[Backport release-9.x] Fix some undefined behaviour (#3408) 2025-02-11 21:11:17 +02:00
Alexandru Ionut Tripon
9456140155 Update launcher/ui/widgets/CheckComboBox.cpp
Signed-off-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
(cherry picked from commit 22b59e760c0f39749916052b9323e55f4217dd72)
2025-02-11 19:10:21 +00:00
Alexandru Ionut Tripon
159771f283 Apply suggestions from code review
Co-authored-by: TheKodeToad <TheKodeToad@proton.me>
Signed-off-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
(cherry picked from commit 1d1a4f1b30837e13ebd99c016af171d79697f22f)
2025-02-11 19:10:21 +00:00
Trial97
9616d898d4 Fix some undefined behaviour
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit bf1084d7fa6503b65bdb63374e5f92020ae4a3d1)
2025-02-11 19:10:21 +00:00
Alexandru Ionut Tripon
e2526c80c8
Fixed when game crashes, it doesn't log any time played (#3392) (#3395) 2025-02-06 09:41:41 +02:00
Alexandru Ionut Tripon
a37cac6f9b
Fixed when game crashes, it doesn't log any time played (#3392)
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2025-02-06 09:41:19 +02:00
Alexandru Ionut Tripon
2f889429da
Merge pull request #3376 from PrismLauncher/backport-3373-to-release-9.x
[Backport release-9.x] Show warning when adding new resources with no instances
2025-01-31 23:04:21 +02:00
Trial97
c7831fd697 fix add resource with no instance
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit c5efe081b4ff9ec81b882e6deabcbfc5be269329)
2025-01-31 21:04:07 +00:00
Alexandru Ionut Tripon
b4c3123957
Merge pull request #3375 from PrismLauncher/backport-3260-to-release-9.x
[Backport release-9.x] Map Minecraft snapshots to Modrinth
2025-01-31 23:03:25 +02:00
Trial97
ae3e1a262e ensure that the snapshot mapping is on all apis
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 8e5a7c6e333969b5412efd1349f9430ced5654c8)
2025-01-31 21:03:11 +00:00
Trial97
f8dc58665b map modrinth snapshots
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 1d8bf1d5a788e689d26e0afa8f5cfca0e3859e5d)
2025-01-31 21:03:11 +00:00
Alexandru Ionut Tripon
19247bad14
Merge pull request #3367 from PrismLauncher/backport-3359-to-release-9.x
[Backport release-9.x] Publish on FlakeHub
2025-01-30 18:31:44 +02:00
Seth Flynn
4371933a84 feat: publish on flakehub
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 86cc6d3c5ee7157d5f9978580e94b94360a119f1)
2025-01-30 16:31:27 +00:00
Seth Flynn
dd90049b30 ci: use generic workflow for publishing
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 61d7f088838e25ddb0a7089a6fa3f7d232331c24)
2025-01-30 16:31:27 +00:00
Alexandru Ionut Tripon
18170cc900
Merge pull request #3364 from PrismLauncher/backport-3314-to-release-9.x
[Backport release-9.x] update submodules
2025-01-29 23:41:13 +02:00
Trial97
71eced3e61 update submodules
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit dc00c47f2e2b79705c32f438298d968aaa4fdf26)
2025-01-29 21:40:21 +00:00
Alexandru Ionut Tripon
cb49b27513
Merge pull request #3363 from PrismLauncher/backport-3362-to-release-9.x
[Backport release-9.x] ci: use flakehub cache
2025-01-29 23:34:39 +02:00
Seth Flynn
7b502fe8c9 ci: run nix workflow on pull_request_target
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 169f5728b1019582db150f3b1618515eb1438517)
2025-01-29 21:34:15 +00:00
Seth Flynn
55578bf949 ci: use flakehub cache
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 8816be166821d5ce1a4cd8edfbe7b91727b6eb34)
2025-01-29 21:34:15 +00:00
Alexandru Ionut Tripon
ee33bf50c2
Merge pull request #3361 from getchoo-contrib/backport-3360-to-release-9.x
[Backport release-9.x] Split Flatpak and Nix CI jobs from main build workflow
2025-01-29 23:12:18 +02:00
Seth Flynn
702db71f61
ci: separate flatpak job
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 49d734f3142328a71506b28c8c1cdcb99788c34d)
Signed-off-by: Seth Flynn <getchoo@tuta.io>
2025-01-29 16:06:27 -05:00
Seth Flynn
8e8efc304f
ci: separate nix job
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit 417688089d756da23ca3f482a32871963d289211)
Signed-off-by: Seth Flynn <getchoo@tuta.io>
2025-01-29 16:06:27 -05:00
Alexandru Ionut Tripon
2b204c8169
Merge pull request #3333 from PrismLauncher/backport-3330-to-release-9.x
[Backport release-9.x] Use better compile flags for MINGW
2025-01-29 22:52:29 +02:00
Alexandru Ionut Tripon
ed2eddc258
Merge pull request #3335 from PrismLauncher/backport-3294-to-release-9.x
[Backport release-9.x] Fix updating Curseforge mods with missing mod loader
2025-01-21 23:45:40 +02:00
Trial97
855e49bda0 fix curseforge with empty loader as newest version
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit e4ad4051c8b87bd5102a76a860408de2b447d74d)
2025-01-21 21:44:27 +00:00
Seth Flynn
bbde47842b feat: use better compile flags for mingw
Signed-off-by: Seth Flynn <getchoo@tuta.io>
(cherry picked from commit e66f447ce5bca996b0a12fac02216b2b6ab8ccb6)
2025-01-21 13:18:57 +00:00
Alexandru Ionut Tripon
0508bbd05f
Merge pull request #3331 from getchoo-contrib/backport-ubuntu-22.04
[Backport release-9.x] Use Ubuntu 22.04 for Linux builds
2025-01-21 09:08:46 +02:00
Alexandru Ionut Tripon
808978080e
Merge pull request #3329 from getchoo-contrib/backport-qt-6.8
[Backport release-9.x] chore: update to qt 6.8.x
2025-01-21 09:07:58 +02:00
Alexandru Ionut Tripon
ea0d710aaf
Update .github/workflows/build.yml
Co-authored-by: Seth Flynn <getchoo@tuta.io>
Signed-off-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
(cherry picked from commit db766574a42a4b7db6b320b6d469b233ce5f3100)
Signed-off-by: Seth Flynn <getchoo@tuta.io>
2025-01-20 23:47:00 -05:00
Alexandru Ionut Tripon
02e1988779
Update .github/workflows/build.yml
Co-authored-by: seth <getchoo@tuta.io>
Signed-off-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
(cherry picked from commit 1b5d3c2bf903bbcade7b38c22c0b103a0c6999f8)
Signed-off-by: Seth Flynn <getchoo@tuta.io>
2025-01-20 23:46:59 -05:00
Trial97
fdab044c04
remove specific step for qt6
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit be3eca8c97fdbb8daede84cab20fbd7695334242)
Signed-off-by: Seth Flynn <getchoo@tuta.io>
2025-01-20 23:46:59 -05:00
Trial97
e77ac36c3f
fix appimage
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit bca517b8d3cd685540beebf69fbc1f80fe0a6ce4)
Signed-off-by: Seth Flynn <getchoo@tuta.io>
2025-01-20 23:46:59 -05:00
Edgars Cirulis
0d06fae7ae
chore: update Qt to 6.8.1
Signed-off-by: Edgars Cirulis <edgarsciruliss@gmail.com>
(cherry picked from commit eb8c375ec5023bbc7c02e45d63da866058e56b26)
Signed-off-by: Seth Flynn <getchoo@tuta.io>
2025-01-20 19:09:02 -05:00
Edgars Cirulis
a0c4cc59ac
ci: uprev qt install action to v4
Signed-off-by: Edgars Cirulis <edgarsciruliss@gmail.com>
(cherry picked from commit 0beaa94311a00cdc02ef5ff7d182849fce419aa9)
Signed-off-by: Seth Flynn <getchoo@tuta.io>
2025-01-20 19:09:02 -05:00
Edgars Cirulis
bd7371dbf1
ci: fix qt-6.8 workflow
Signed-off-by: Edgars Cirulis <edgarsciruliss@gmail.com>
(cherry picked from commit b39098dbc5b371e31ce585f7534ffdb00096c2b5)
Signed-off-by: Seth Flynn <getchoo@tuta.io>
2025-01-20 19:09:02 -05:00
Edgars Cirulis
d7756d29dc
chore: update Qt to 6.8.0
Signed-off-by: Edgars Cirulis <edgarsciruliss@gmail.com>
(cherry picked from commit 5b6d551650e4590567321647ce68ae2043a8180c)
Signed-off-by: Seth Flynn <getchoo@tuta.io>
2025-01-20 19:09:01 -05:00
Alexandru Ionut Tripon
0a7325ac12
Merge pull request #3324 from getchoo-contrib/backport-arm-stuff
[Backport release-9.x] Build Nix packages and Flatpaks for ARM
2025-01-20 09:21:29 +02:00
seth
ed2b5d4b0e
ci: build flatpaks for arm
Signed-off-by: seth <getchoo@tuta.io>
(cherry picked from commit 66f0397087d9b88c93e69c5e26a138d235344546)
Signed-off-by: seth <getchoo@tuta.io>
2025-01-19 19:47:44 -05:00
seth
9f199a470c
ci: build nix packages for aarch64-linux
Signed-off-by: seth <getchoo@tuta.io>
(cherry picked from commit 8e8538b506b5f3ae2b2424599078966600bb764c)
Signed-off-by: seth <getchoo@tuta.io>
2025-01-19 19:47:39 -05:00
Alexandru Ionut Tripon
1a98ef8bb7
Merge pull request #3309 from Trial97/bump9.3
bump to 9.3
2025-01-18 13:47:11 +02:00
Alexandru Ionut Tripon
d25d0d7ee6
Merge pull request #3312 from PrismLauncher/backport-1689-to-release-9.x
[Backport release-9.x] Added mouse interactions for labels in ProgressDialog
2025-01-17 21:10:03 +02:00
Trial97
beac093d62 Added mouse interactions for labels in ProgressDialog
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 08f5148a9a4e574dd3ef4e7dc1e46a05ffd9dd5e)
2025-01-17 19:00:06 +00:00
Trial97
81318d4953
bump to 9.3
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2025-01-17 09:17:49 +02:00
Alexandru Ionut Tripon
8c6602147b
Merge pull request #3308 from PrismLauncher/backport-3254-to-release-9.x
[Backport release-9.x] remove EditAccountDialog
2025-01-17 09:02:31 +02:00
Alexandru Ionut Tripon
9d8c3f251f
Merge pull request #3307 from PrismLauncher/backport-2737-to-release-9.x
[Backport release-9.x] Truncate logs for mclo.gs to fit 25k line limit
2025-01-17 09:02:01 +02:00
Alexandru Ionut Tripon
c787e65ed4
Merge pull request #3306 from PrismLauncher/backport-1555-to-release-9.x
[Backport release-9.x] Correctly expand environment variables for pre/post launch and wrapper commands
2025-01-17 09:01:21 +02:00
Alexandru Ionut Tripon
ae84a78470
Merge pull request #3303 from getchoo-contrib/backport-3179-to-release-9.x
[Backport release-9.x] Fix $INST_JAVA not being set for auto download java
2025-01-17 08:56:39 +02:00
Trial97
820b9e3fd0 remove EditAccountDialog
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit e01df73debc8cb578e3e1b1e557388a8d0a7a052)
2025-01-17 06:56:28 +00:00
maskers
c8c3370d36 fix off by one error
Signed-off-by: maskers <97827489+mskrss@users.noreply.github.com>
(cherry picked from commit 6a12c43c787db4a64748d6c3d03b34981948d7ce)
2025-01-17 06:50:00 +00:00
TheKodeToad
cffa9a2da0 Confirm Truncate -> Truncation
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 43fc9ba932ec65d274b60bafb4d3c3f1d5bf55f0)
2025-01-17 06:50:00 +00:00
maskers
01397896eb don't mention prism in the middle of the log
Signed-off-by: maskers <97827489+mskrss@users.noreply.github.com>
(cherry picked from commit 762d24bd02ae6a0c0f241626c9c06deeaae86574)
2025-01-17 06:50:00 +00:00
maskers
3048e43900 apply suggestion from code review
Co-authored-by: TheKodeToad <TheKodeToad@proton.me>
Signed-off-by: maskers <97827489+maskersss@users.noreply.github.com>
(cherry picked from commit a910337e9d148937fc6a7339a7bcdd9a2db1ec03)
2025-01-17 06:50:00 +00:00
maskers
417bb2fce6 fix formatting
Signed-off-by: maskers <97827489+mskrss@users.noreply.github.com>
(cherry picked from commit 6d017b5f0b9fd878d387bf33585221ea079e305b)
2025-01-17 06:50:00 +00:00
maskers
e234c66818 add a Cancel option
Co-authored-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
Signed-off-by: maskers <97827489+maskersss@users.noreply.github.com>
(cherry picked from commit 65f852615247f6c1ba3cd81cf9e51ebc0220341b)
2025-01-17 06:50:00 +00:00
maskers
589ee73bfd Apply suggestions from code review
Co-authored-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
Signed-off-by: maskers <97827489+maskersss@users.noreply.github.com>
(cherry picked from commit 99bd4a89373416cea641f1421d1082ff823eba2e)
2025-01-17 06:50:00 +00:00
maskers
0cb5ac7a06 add a warning about the log being too large
Signed-off-by: maskers <97827489+mskrss@users.noreply.github.com>
(cherry picked from commit cf914526bf5330600f3cadb9f4baddb5144af21f)
2025-01-17 06:50:00 +00:00
maskers
db35f47105 fix formatting
Signed-off-by: maskers <97827489+mskrss@users.noreply.github.com>
(cherry picked from commit 858f6aa9b8adcb5d2ac9eadd2702a21a88f9e4c4)
2025-01-17 06:50:00 +00:00
maskers
cf8a82e78a fix formatting
Signed-off-by: maskers <97827489+mskrss@users.noreply.github.com>
(cherry picked from commit e6f30c0ebe0ddd43b493f27156d6c0765105ded5)
2025-01-17 06:50:00 +00:00
maskers
15887c2d92 truncate logs for mclo.gs upload to fit 25k line limit
Signed-off-by: maskers <97827489+mskrss@users.noreply.github.com>
(cherry picked from commit 014fc142915ec4a0f96b3082f76d4d32ce7596aa)
2025-01-17 06:50:00 +00:00
Trial97
b7b5630588 expand env from wrapped cmd
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 09a118e85e3646ded9d7b2704d2f95a589ca19b9)
2025-01-17 06:47:08 +00:00
Trial97
43834e2148 correctly expand env vars
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit c12beb43a0f3dad6e66fd42192dbbf1f78fb6c53)
2025-01-17 06:47:08 +00:00
sshcrack
feb275a580
load meta first
Signed-off-by: sshcrack <34072808+sshcrack@users.noreply.github.com>
(cherry picked from commit cf2dcbd431b0c4bb03ff527465f972d72f713b0c)
Signed-off-by: seth <getchoo@tuta.io>
2025-01-16 21:46:13 -05:00
sshcrack
759db0c90f
change order of steps
Signed-off-by: sshcrack <34072808+sshcrack@users.noreply.github.com>
(cherry picked from commit dedb7a2343f7d433492dee5930802ed47294b722)
Signed-off-by: seth <getchoo@tuta.io>
2025-01-16 21:03:52 -05:00
seth
b2c75dde95
Merge pull request #3302 from PrismLauncher/backport-3292-to-release-9.x
[Backport release-9.x] Update Modrinth API authentication link
2025-01-16 20:55:38 -05:00
Trial97
62ed3f8406 fix modrinth link
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 3ee73916ca31666abc48f45e8b0bfcf8d678e6cf)
2025-01-17 01:42:14 +00:00
Alexandru Ionut Tripon
5e227ceaae
Merge pull request #3281 from PrismLauncher/backport-3255-to-release-9.x
[Backport release-9.x] propagate load component error
2025-01-13 08:18:00 +02:00
Trial97
a739db8bdb propagate load component error
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 6ebfcb91cf954048061ff39719629e7f581f1997)
2025-01-08 22:22:36 +00:00
Alexandru Ionut Tripon
fdfdf2eef1
Merge pull request #3224 from Trial97/backport_fix_auto_mod_provider
[backport]Fix automatically choose mod provider option
2024-12-19 10:34:01 +02:00
Trial97
bbfa5a5ed1
Fix automatically choose mod provider option
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2024-12-19 09:23:51 +02:00
Alexandru Ionut Tripon
43dc330e55
Merge pull request #3223 from Trial97/backport_fix_flame_loader_match
[backport]fix the flame loaders match
2024-12-18 23:30:10 +02:00
Trial97
0c962dff59
fix the flame loaders match
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2024-12-18 22:53:18 +02:00
timoreo
ca0dd583ba
Merge pull request #3222 from PrismLauncher/backport-3197-to-release-9.x
[Backport release-9.x] Do not fail curseforge import if modrinth file check fails
2024-12-18 19:53:32 +01:00
Trial97
7bb686c85b Do not fail curseforge import if modrinth file check fails
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit a1c1c0b769d77eb8c44cef3dc0966e7184b2ce78)
2024-12-18 18:53:13 +00:00
timoreo
f8d3d8399d
Merge pull request #3221 from PrismLauncher/backport-3214-to-release-9.x
[Backport release-9.x] Correct symbolic link destination on manifest (Mojang) Java download
2024-12-18 19:42:58 +01:00
Kenneth Chew
9cb2a3ab2d Correct symbolic link destination on manifest Java download
Signed-off-by: Kenneth Chew <79120643+kthchew@users.noreply.github.com>
(cherry picked from commit 8d53242952ceeed11bd1a57c9ac43344d854ac8c)
2024-12-18 18:42:39 +00:00
Alexandru Ionut Tripon
072c495ec6
Merge pull request #3206 from PrismLauncher/backport-3202-to-release-9.x
[Backport release-9.x] [macOS] Update Sparkle to v2.6.4
2024-12-14 18:39:04 +02:00
Kenneth Chew
335941fb46 Update Sparkle to v2.6.4
Signed-off-by: Kenneth Chew <79120643+kthchew@users.noreply.github.com>
(cherry picked from commit b6cd46ad27e8046962dcb7b3b43a973ff08564eb)
2024-12-14 16:36:04 +00:00
Alexandru Ionut Tripon
ebeff36da5
Merge pull request #3205 from PrismLauncher/backport-3199-to-release-9.x
[Backport release-9.x] Fix tab order in launcher settings
2024-12-14 18:34:38 +02:00
Kenneth Chew
1d29f63bec Fix tab order in launcher settings
Signed-off-by: Kenneth Chew <79120643+kthchew@users.noreply.github.com>
(cherry picked from commit c3712ba6483f6386e2930ec0ed059d8c94f671b6)
2024-12-14 16:32:21 +00:00
Alexandru Ionut Tripon
87dc1bb6a6
Merge pull request #3204 from PrismLauncher/backport-3198-to-release-9.x
[Backport release-9.x] [macOS] Add local network usage description
2024-12-14 18:19:20 +02:00
Alexandru Ionut Tripon
a85720b8d6
Merge pull request #3203 from PrismLauncher/backport-3181-to-release-9.x
[Backport release-9.x] fix crash with invalid mrpack format
2024-12-14 18:19:09 +02:00
Kenneth Chew
4c28a94de1 Add local network usage description
Signed-off-by: Kenneth Chew <79120643+kthchew@users.noreply.github.com>
(cherry picked from commit c3e44554abccfd16ff7f84740932848e59d28474)
2024-12-14 16:16:49 +00:00
Trial97
344ae87f25 fix crash with invalid mrpack format
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 1dd0728a58159bc835b2e7724a707776ae0b93ce)
2024-12-14 16:16:06 +00:00
Alexandru Ionut Tripon
577999bd35
Merge pull request #3183 from PrismLauncher/backport-3170-to-release-9.x
[Backport release-9.x] [Linux] Don't use fallback icon search paths
2024-12-09 09:15:53 +02:00
leia uwu
887db29f35 fix icon theme search paths
using fallback search paths breaks qadwaitadecorations

Signed-off-by: leia uwu <leia@tutamail.com>
(cherry picked from commit 614574f15c507f884fc2ceff74db8c919674a0f1)
2024-12-09 07:14:38 +00:00
Alexandru Ionut Tripon
7865b8128e
Merge pull request #3169 from PrismLauncher/backport-3166-to-release-9.x
[Backport release-9.x] chore(deps): update actions/cache action to v4.2.0
2024-12-06 08:47:51 +02:00
renovate[bot]
a58f125535 chore(deps): update actions/cache action to v4.2.0
(cherry picked from commit 94c893bd865ceba286dffc1d6392df07d47c11d9)
2024-12-06 06:47:20 +00:00
Alexandru Ionut Tripon
d1a05d312d
Merge pull request #3158 from PrismLauncher/backport-3119-to-release-9.x
[Backport release-9.x] Sync Flatpak manifest with Flathub's
2024-12-02 17:30:57 +02:00
guihkx
bbf21e5824 ci(flatpak): update build artifact name
Just aligning the name of the Flatpak package with other build artifacts.

Signed-off-by: guihkx <626206+guihkx@users.noreply.github.com>
(cherry picked from commit e0faee7f262fda898ceb8ea5ab40249dbd6c52ea)
2024-12-02 15:30:33 +00:00
guihkx
73b4223b61 flatpak: update KDE runtime to 6.8
This also switches to the Docker image provided and maintained by
Flathub collaborators through the 'flathub-infra' organization on
GitHub, because it looks better maintained at the moment.

Signed-off-by: guihkx <626206+guihkx@users.noreply.github.com>
(cherry picked from commit f6770a847a8419ef0b1acab30144077f745a48ee)
2024-12-02 15:30:33 +00:00
guihkx
686e0b7b18 flatpak: update xrandr to 1.5.3
Signed-off-by: guihkx <626206+guihkx@users.noreply.github.com>
(cherry picked from commit 4a50e949672461b5b69b039ff240ced6dad9bdd5)
2024-12-02 15:30:33 +00:00
Alexandru Ionut Tripon
189878e7e3
Merge pull request #3157 from PrismLauncher/backport-3135-to-release-9.x
[Backport release-9.x] Make FTB Import note italic to match others
2024-12-02 16:25:06 +02:00
Trial97
af74d5f410 Make FTB Import note italic to match others
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit a09af619cee3265710097724f539d9457695658d)
2024-12-02 14:17:44 +00:00
Tayou
cdfd0b1002
Merge pull request #3156 from PrismLauncher/backport-3152-to-release-9.x
[Backport release-9.x] Fix system icons on Linux
2024-12-02 15:02:21 +01:00
leia uwu
9791c306dc fix: icon search paths memory leak
Signed-off-by: leia uwu <leia@tutamail.com>
(cherry picked from commit 3f67ef968bb9be3b1cdfdfa68e2d38ddf8e1a3cf)
2024-12-02 14:00:59 +00:00
leia uwu
e7e9265c40 fix: fix system icons
This sets the fallback icon theme to the current(system default) icon theme before
launcher specific themes are applied

And removes `Inherits` line of multimc/legacy icon theme because it can end up making it
inherit a default theme set from /usr/share/icons/default/index.theme
instead of the user configured theme (probably a qt bug?)

Signed-off-by: leia uwu <leia@tutamail.com>
(cherry picked from commit fd9c80db62436b1a364cb8342ba45a059c53f899)
2024-12-02 14:00:59 +00:00
Alexandru Ionut Tripon
e0c323a190
Merge pull request #3138 from PrismLauncher/backport-3130-to-release-9.x
[Backport release-9.x] Improve MANIFEST.MF parsing
2024-11-25 12:09:01 +02:00
Kationor
a914747416 Improve MANIFEST.MF parsing
Previously, we would only properly parse LF-encoded manifests, and even
those only if they used the recommended casing.

This commit allows the parser to recognise CR and CRLF newlines, and
also makes the name comparison case insensitive to align with the
specification. (Though not completely: we still don't support multiline
values)

Signed-off-by: Kationor <n96211028@gmail.com>
(cherry picked from commit b40a1973bfe590fb21b486419a8d223b2c6aae7f)
2024-11-25 10:05:19 +00:00
Alexandru Ionut Tripon
d453240a94
Merge pull request #3105 from Trial97/backport_3019
[Backport release-9.x] fixed double deletion for tasks
2024-11-15 10:35:25 +02:00
Trial97
4c4017d7ca remove task parent from constuctor
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2024-11-15 09:35:36 +02:00
Alexandru Ionut Tripon
2a4c4ed8ea
Merge pull request #3103 from PrismLauncher/backport-3024-to-release-9.x
[Backport release-9.x] fix leak on resource search
2024-11-15 08:55:46 +02:00
Alexandru Ionut Tripon
947fbf7d64
Merge pull request #3102 from PrismLauncher/backport-3070-to-release-9.x
[Backport release-9.x] Add scrollbar to Settings -> Launcher -> Features
2024-11-15 08:55:17 +02:00
Trial97
1c94a3e3e5 fix leak on resource search
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 8e7d84d05beffac492522f4b2c0d9e638b15146b)
2024-11-15 05:48:32 +00:00
TheKodeToad
eefeb5799d Add scrollbar to Settings -> Launcher -> Features
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 71e55f88294976577ad0b2f8c21b635cf8fc9735)
2024-11-15 05:40:31 +00:00
timoreo
6c6a4625ab
Merge pull request #3101 from PrismLauncher/backport-3096-to-release-9.x
[Backport release-9.x] Fix file remaingin open after program closure
2024-11-15 06:15:22 +01:00
timoreo
6d2a96f6a7
Merge pull request #3098 from Trial97/resource_backport
Fix crash caused by invalid resource pointer
2024-11-15 06:15:06 +01:00
Trial97
ea9029d7b6 Fix file remaingin open after program closure
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit ce61ed2f86d313f63c999d012d768425ea3793c3)
2024-11-15 05:14:42 +00:00
Trial97
76602391f4 Fix crash caused by invalid resource pointer
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2024-11-14 16:26:21 +02:00
Alexandru Ionut Tripon
509f8c7307
Merge pull request #3079 from PrismLauncher/backport-3074-to-release-9.x
[Backport release-9.x] Close the window using the invokeMethod to not block
2024-11-07 23:48:40 +02:00
Trial97
984daa450b Close the window using the invokeMethod to not block
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 9601fbf2d5b36d3609cce6b1a3b458db0d170531)
2024-11-07 21:48:30 +00:00
Tayou
04e1a97d59
Merge pull request #3077 from PrismLauncher/backport-3036-to-release-9.x
[Backport release-9.x] Fix installed typo
2024-11-07 17:31:15 +01:00
Trial97
1dce1360bd Fix installed typo
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 670c932f32753fd67979f95810b052d7a62a0006)
2024-11-07 16:31:03 +00:00
timoreo
87d5b96760
Merge pull request #3061 from PrismLauncher/backport-3060-to-release-9.x
[Backport release-9.x] ci: bump linux qt version
2024-11-05 11:26:18 +01:00
Rachel Powers
7f074ca7b9 ci: libxcb-curcsor_dev ?
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
(cherry picked from commit ba6743d134589294ac3ba754e6ad83c2e59d4ffd)
2024-11-05 10:25:53 +00:00
Rachel Powers
c0394c52b7 ci: bump linux qt version
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
(cherry picked from commit d63a3364111a99337c2096c807550f3d7c62b47a)
2024-11-05 10:25:53 +00:00
Alexandru Ionut Tripon
53bc20f13f
Merge pull request #3052 from PrismLauncher/backport-3042-to-release-9.x
[Backport release-9.x] use isPortable to determine if the MSAStep should check for url handler
2024-11-03 11:56:20 +02:00
Trial97
40af3cf3d2 use isPortable to determine if the MSAStep should check for url handler
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 99cfb6237030f03c148e531da78b8da8f7d4c2f1)
2024-11-03 09:56:08 +00:00
Alexandru Ionut Tripon
3977918c5b
Merge pull request #3051 from PrismLauncher/backport-3045-to-release-9.x
[Backport release-9.x] disable retry for modrinth currentVersions API
2024-11-03 11:56:07 +02:00
Trial97
484d90899f disable retry for modrinth currentVersions API
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 6f2f3c2d3bf6a170060523ad47fe69fef57bd9b4)
2024-11-03 09:55:50 +00:00
Tayou
0f5eb03839
Merge pull request #3034 from PrismLauncher/backport-3010-to-release-9.x 2024-10-30 21:57:18 +01:00
Trial97
d21cda1880 lock m_instanceExtras
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 11d4d948aa1a1ba67cee59193eb98a7fdad2d909)
2024-10-30 20:56:30 +00:00
Alexandru Ionut Tripon
52a321b93b
Merge pull request #3033 from PrismLauncher/backport-3030-to-release-9.x
[Backport release-9.x] Fix system detection for Intel Macs
2024-10-30 21:21:49 +02:00
Trial97
dd8dec8be8 fix macos system detection
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 14c95b9d376975144faddc33e7154cca86621804)
2024-10-30 19:16:53 +00:00
Tayou
23d6f8aaed
Merge pull request #3028 from PrismLauncher/backport-3027-to-release-9.x 2024-10-29 23:15:22 +01:00
Ben Westover
d105d76f2a Fix some typos
Signed-off-by: Ben Westover <me@benthetechguy.net>
(cherry picked from commit f6511c601e1fd63a864f1681beef0f85adfe6f7e)
2024-10-29 22:06:15 +00:00
Ben Westover
698a838b56 Shorten metainfo.xml app summary
Signed-off-by: Ben Westover <me@benthetechguy.net>
(cherry picked from commit fd109c47401c755e35da9c2884e9b38532bed56c)
2024-10-29 22:06:15 +00:00
Alexandru Ionut Tripon
1129b4160c
Merge pull request #3022 from PrismLauncher/backport-2989-to-release-9.x
[Backport release-9.x] add extra protection against empty download link
2024-10-28 23:00:19 +02:00
Trial97
63a6f823fd add qassert
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 94f65f8727bfd12cc50eb8b62d52bca308cef9b5)
2024-10-28 20:59:25 +00:00
Trial97
db32b2ad99 remove message box
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 9cdda4377b0090a035e286a86fd78f126f995356)
2024-10-28 20:59:25 +00:00
Trial97
c3cf5d31da replace currentData with itemData on QComboBox::currentIndexChanged slots
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 0cafac84ac6328fefb8992de2c6a2385b1c0053c)
2024-10-28 20:59:25 +00:00
Trial97
1e696328bb fix #3001
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit b593ff09e9c4b5fa30ca082eea4cd69f12a286e5)
2024-10-28 20:59:25 +00:00
Trial97
1d9c97803a rename snake_case to camelCase
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 44894a29b1784cfa8a04807c17d21169230ff922)
2024-10-28 20:59:25 +00:00
Trial97
3d55236fdf add extra protection against empty download link
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 8daa1219a508ddb37070c01046c78b731a277887)
2024-10-28 20:59:25 +00:00
Alexandru Ionut Tripon
42190819ae
Merge pull request #3020 from PrismLauncher/backport-2837-to-release-9.x
[Backport release-9.x] translate standard buttons
2024-10-28 22:36:29 +02:00
Trial97
130714a9ab translate standard buttons
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit bd82737efbb99dfc488d6d073046e5fe62693a9e)
2024-10-28 20:34:51 +00:00
Tayou
bf57d77075
Merge pull request #3017 from PrismLauncher/backport-3002-to-release-9.x 2024-10-28 14:24:25 +01:00
Trial97
98fe035442 replace unzipping with unpacking
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 9eb1ce3ad7dde0b2ca551b0b281081724e6fd58e)
2024-10-28 13:23:42 +00:00
Trial97
8e4994590b fix unzipping typo
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 685d3c79ac78d7e41ab257de82dc743020189a33)
2024-10-28 13:23:42 +00:00
Tayou
0722dba820
Merge pull request #3016 from PrismLauncher/backport-3009-to-release-9.x 2024-10-28 14:04:52 +01:00
Trial97
9560447c92 swap search with filter button for modpacks
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 61448a61ea400eebd91f97aa07e9419cc2f35823)
2024-10-28 13:04:39 +00:00
Alexandru Ionut Tripon
4d89512ca1
Merge pull request #3013 from PrismLauncher/backport-3008-to-release-9.x
[Backport release-9.x] change contributors name
2024-10-27 22:30:02 +02:00
Trial97
42a6bd1151 change ZekeZ name
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit c4cd7cf6c173b7052f31614b4641d2172defa91a)
2024-10-27 19:31:56 +00:00
Trial97
f06505b138 change contributors name
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit f373a5cea0e816158cdabca3423107989aed0a85)
2024-10-27 19:31:56 +00:00
seth
d854087089
Merge pull request #2997 from PrismLauncher/backport-2992-to-release-9.x
[Backport release-9.x] Sync Flake with Nixpkgs
2024-10-25 21:20:31 -04:00
seth
31031ec923 chore(nix): use self for version
Signed-off-by: seth <getchoo@tuta.io>
(cherry picked from commit 63b10738b2a1065d55077e729fcada6e7f64d10a)
2024-10-26 00:57:00 +00:00
seth
5f06517b62 chore(nix): sync with nixpkgs
Signed-off-by: seth <getchoo@tuta.io>
(cherry picked from commit a5c554cf6ee32afa41c5a2af1ea8e8feb0087a49)
2024-10-26 00:57:00 +00:00
Tayou
dde5fc47d3
Merge pull request #2987 from Trial97/bump_9.2 2024-10-25 09:53:49 +02:00
Alexandru Ionut Tripon
76d5f632dc
Merge pull request #2993 from PrismLauncher/backport-2982-to-release-9.x
[Backport release-9.x] Use Launcher log level in AutoInstallJava
2024-10-25 09:46:30 +03:00
TheKodeToad
27c6596bcb Use Launcher log level in AutoInstallJava
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit d8702e1357d36423a0d40b916d49fd937284a204)
2024-10-25 06:42:32 +00:00
Alexandru Ionut Tripon
e2f5fb27a6
Merge pull request #2991 from PrismLauncher/backport-2963-to-release-9.x
[Backport release-9.x] chore(deps): update actions/cache action to v4.1.2
2024-10-25 09:33:52 +03:00
renovate[bot]
98819a0d02 chore(deps): update actions/cache action to v4.1.2
(cherry picked from commit 210d0d8aa25578cbbf8250797504ae1652544600)
2024-10-25 06:30:00 +00:00
seth
2dde2c4bec
Merge pull request #2990 from PrismLauncher/backport-2980-to-release-9.x
[Backport release-9.x] Sync with Flathub manifest
2024-10-25 02:20:10 -04:00
Arcitec
008d69e5e5 fix: bring back Flathub manifest improvements to the repo
- Brings back all manifest improvements, such as the incredibly important Mojang Java Downloader (without it, the Flatpak doesn't work), and important fixes for Wayland.

Signed-off-by: Arcitec <38923130+Arcitec@users.noreply.github.com>
(cherry picked from commit 4ce025c0a209a4eec20f7f1b9a7777bbe886be8c)
2024-10-25 06:19:25 +00:00
Trial97
a839258c07
bump develop to 9.2
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2024-10-25 00:19:06 +03:00
Alexandru Ionut Tripon
89041531e1
Merge pull request #2981 from PrismLauncher/backport-2975-to-release-9.x
[Backport release-9.x] fix: don't hang the ui for a full version load
2024-10-24 15:09:19 +03:00
Rachel Powers
729cec5f45 fix don't hang the ui for a full version load
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
(cherry picked from commit 7bd04ae928527b065174021fba98dc78d0d119a3)
2024-10-24 10:24:53 +00:00
TheKodeToad
80d675d2e6
Merge pull request #2974 from PrismLauncher/backport-2972-to-release-9.x
[Backport release-9.x] Fix launching Minecraft in portable Linux
2024-10-23 22:39:58 +01:00
TheKodeToad
fb7d19941f Fix CleanEnviroment()'s usage of stripVariableEntries
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit e7cd8fe078e3a68bd3179816020a9ed08f1b2847)
2024-10-23 21:30:57 +00:00
Alexandru Ionut Tripon
877ab62c2f
Merge pull request #2964 from PrismLauncher/backport-2958-to-release-9.x
[Backport release-9.x] skip parsing open QSaveFile temprary files as resources
2024-10-22 17:41:45 +03:00
Trial97
b983ae0fb0 fix small leak
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 836aebc0d91fe8658052b8c4a5a4ab31da4a4b15)
2024-10-22 14:41:34 +00:00
Trial97
85422427b9 Replaced QSet with QHash
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 73d33f93b30f658f9671358ac52bf4e03afeaefd)
2024-10-22 14:41:34 +00:00
Trial97
51a71d0471 skip QSaveFile temprary files
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 562c3013269dbb9cad411f58ded333dee1aea158)
2024-10-22 14:41:34 +00:00
Alexandru Ionut Tripon
25eaa4eba6
Merge pull request #2959 from PrismLauncher/backport-2954-to-release-9.x
[Backport release-9.x] do not try to import skin if path is empty
2024-10-22 00:15:10 +03:00
Alexandru Ionut Tripon
6da14d66bb Update launcher/ui/dialogs/skins/SkinManageDialog.cpp
Co-authored-by: TheKodeToad <TheKodeToad@proton.me>
Signed-off-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
(cherry picked from commit 69028969f1a6c42b698b900256ad2e6d9ee208eb)
2024-10-21 21:14:30 +00:00
Trial97
199c5497d3 do not try to import skin if path is empty
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 9a5b773e69e7198cc37413ab7d69ba6d0a66f854)
2024-10-21 21:14:30 +00:00
Alexandru Ionut Tripon
740db2db02
Merge pull request #2956 from PrismLauncher/backport-2953-to-release-9.x
[Backport release-9.x] Fix removing portable.txt on Linux portable build
2024-10-21 22:46:49 +03:00
TheKodeToad
577f8074e1 Fix removing portable.txt on Linux portable build
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit f3f4c446206647b69b9c19e63617b163b99c6164)
2024-10-21 19:45:10 +00:00
Alexandru Ionut Tripon
c5e7bb90c5
Merge pull request #2952 from PrismLauncher/backport-2947-to-release-9.x
[Backport release-9.x] do not require java if auto-download is enabled
2024-10-21 18:04:53 +03:00
Trial97
1cf91fa5d9 do not require java if auto-download is enabled
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit fcadbbb739076096a9057ec340f7f6d39bc4b72f)
2024-10-21 15:02:54 +00:00
Tayou
3bf4fbf8f4
Merge pull request #2951 from PrismLauncher/backport-2948-to-release-9.x 2024-10-21 16:55:28 +02:00
TheKodeToad
ff97affa72 Fix /norestart
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 78e24962f93b5cc2350d04bebd7e5231d1bc4819)
2024-10-21 14:54:35 +00:00
Alexandru Ionut Tripon
e8c9722a53
Merge pull request #2939 from PrismLauncher/backport-2938-to-release-9.x
[Backport release-9.x] fix sparkle signature
2024-10-21 00:08:40 +03:00
Trial97
9a810dfa6e fix sparkle signature
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 0586d38e03de6890bf5697c7e1a965aac9bd36fb)
2024-10-20 21:07:55 +00:00
642 changed files with 8801 additions and 18491 deletions

View File

@ -1,23 +1,5 @@
Checks: Checks:
- modernize-use-using - modernize-use-using
- readability-avoid-const-params-in-decls - 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. SystemHeaders: false
CheckOptions:
- { 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: SCREAMING_SNAKE_CASE }

2
.envrc
View File

@ -1,2 +1,2 @@
use nix use flake
watch_file nix/*.nix watch_file nix/*.nix

View File

@ -5,9 +5,3 @@ bbb3b3e6f6e3c0f95873f22e6d0a4aaf350f49d9
# (nix) alejandra -> nixfmt # (nix) alejandra -> nixfmt
4c81d8c53d09196426568c4a31a4e752ed05397a 4c81d8c53d09196426568c4a31a4e752ed05397a
# reformat codebase
1d468ac35ad88d8c77cc83f25e3704d9bd7df01b
# format a part of codebase
5c8481a118c8fefbfe901001d7828eaf6866eac4

View File

@ -1,103 +0,0 @@
# This file incorporates work covered by the following copyright and
# permission notice
#
# Copyright (c) 2003-2025 Eelco Dolstra and the Nixpkgs/NixOS contributors
#
# 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.
name: Get merge commit
description: Get a merge commit of a given pull request
inputs:
repository:
description: Repository containing the pull request
required: false
pull-request-id:
description: ID of a pull request
required: true
outputs:
merge-commit-sha:
description: Git SHA of a merge commit
value: ${{ steps.query.outputs.merge-commit-sha }}
runs:
using: composite
steps:
- name: Wait for GitHub to report merge commit
id: query
shell: bash
env:
GITHUB_REPO: ${{ inputs.repository || github.repository }}
PR_ID: ${{ inputs.pull-request-id }}
# https://github.com/NixOS/nixpkgs/blob/8f77f3600f1ee775b85dc2c72fd842768e486ec9/ci/get-merge-commit.sh
run: |
set -euo pipefail
log() {
echo "$@" >&2
}
# Retry the API query this many times
retryCount=5
# Start with 5 seconds, but double every retry
retryInterval=5
while true; do
log "Checking whether the pull request can be merged"
prInfo=$(gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/$GITHUB_REPO/pulls/$PR_ID")
# Non-open PRs won't have their mergeability computed no matter what
state=$(jq -r .state <<<"$prInfo")
if [[ "$state" != open ]]; then
log "PR is not open anymore"
exit 1
fi
mergeable=$(jq -r .mergeable <<<"$prInfo")
if [[ "$mergeable" == "null" ]]; then
if ((retryCount == 0)); then
log "Not retrying anymore. It's likely that GitHub is having internal issues: check https://www.githubstatus.com/"
exit 3
else
((retryCount -= 1)) || true
# null indicates that GitHub is still computing whether it's mergeable
# Wait a couple seconds before trying again
log "GitHub is still computing whether this PR can be merged, waiting $retryInterval seconds before trying again ($retryCount retries left)"
sleep "$retryInterval"
((retryInterval *= 2)) || true
fi
else
break
fi
done
if [[ "$mergeable" == "true" ]]; then
echo "merge-commit-sha=$(jq -r .merge_commit_sha <<<"$prInfo")" >> "$GITHUB_OUTPUT"
else
echo "# 🚨 The PR has a merge conflict!" >> "$GITHUB_STEP_SUMMARY"
exit 2
fi

View File

@ -1,124 +0,0 @@
name: Package for Linux
description: Create Linux packages for Prism Launcher
inputs:
version:
description: Launcher version
required: true
build-type:
description: Type for the build
required: true
default: Debug
artifact-name:
description: Name of the uploaded artifact
required: true
default: Linux
cmake-preset:
description: Base CMake preset previously used for the build
required: true
default: linux
qt-version:
description: Version of Qt to use
required: true
gpg-private-key:
description: Private key for AppImage signing
required: false
gpg-private-key-id:
description: ID for the gpg-private-key, to select the signing key
required: false
runs:
using: composite
steps:
- name: Package AppImage
shell: bash
env:
VERSION: ${{ inputs.version }}
BUILD_DIR: build
INSTALL_APPIMAGE_DIR: install-appdir
GPG_PRIVATE_KEY: ${{ inputs.gpg-private-key }}
run: |
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
mv ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.metainfo.xml ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml
export "NO_APPSTREAM=1" # we have to skip appstream checking because appstream on ubuntu 20.04 is outdated
export OUTPUT="PrismLauncher-Linux-x86_64.AppImage"
chmod +x linuxdeploy-*.AppImage
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
cp -r ${{ runner.workspace }}/Qt/${{ inputs.qt-version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
cp /usr/lib/x86_64-linux-gnu/libssl.so.* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
export LD_LIBRARY_PATH
chmod +x AppImageUpdate-x86_64.AppImage
cp AppImageUpdate-x86_64.AppImage ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin
export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-x86_64.AppImage.zsync"
if [ '${{ inputs.gpg-private-key-id }}' != '' ]; then
export SIGN=1
export SIGN_KEY=${{ inputs.gpg-private-key-id }}
mkdir -p ~/.gnupg/
echo "$GPG_PRIVATE_KEY" > ~/.gnupg/private.key
gpg --import ~/.gnupg/private.key
else
echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY
fi
./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.prismlauncher.PrismLauncher.svg
mv "PrismLauncher-Linux-x86_64.AppImage" "PrismLauncher-Linux-${{ env.VERSION }}-${{ inputs.build-type }}-x86_64.AppImage"
- name: Package portable tarball
shell: bash
env:
BUILD_DIR: build
CMAKE_PRESET: ${{ inputs.cmake-preset }}
INSTALL_PORTABLE_DIR: install-portable
run: |
cmake --preset "$CMAKE_PRESET" -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_PORTABLE_DIR }} -DINSTALL_BUNDLE=full
cmake --install ${{ env.BUILD_DIR }}
cmake --install ${{ env.BUILD_DIR }} --component portable
mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib
cp /lib/x86_64-linux-gnu/libbz2.so.1.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib
cp /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
cp /usr/lib/x86_64-linux-gnu/libssl.so.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
cp /usr/lib/x86_64-linux-gnu/libffi.so.*.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
mv ${{ env.INSTALL_PORTABLE_DIR }}/bin/*.so* ${{ env.INSTALL_PORTABLE_DIR }}/lib
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
cd ${{ env.INSTALL_PORTABLE_DIR }}
tar -czf ../PrismLauncher-portable.tar.gz *
- name: Upload binary tarball
uses: actions/upload-artifact@v4
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@v4
with:
name: PrismLauncher-${{ inputs.artifact-name }}-${{ inputs.version }}-${{ inputs.build-type }}-x86_64.AppImage
path: PrismLauncher-${{ runner.os }}-${{ inputs.version }}-${{ inputs.build-type }}-x86_64.AppImage
- name: Upload AppImage Zsync
uses: actions/upload-artifact@v4
with:
name: PrismLauncher-${{ inputs.artifact-name }}-${{ inputs.version }}-${{ inputs.build-type }}-x86_64.AppImage.zsync
path: PrismLauncher-Linux-x86_64.AppImage.zsync

View File

@ -1,121 +0,0 @@
name: Package for macOS
description: Create a macOS package for Prism Launcher
inputs:
version:
description: Launcher version
required: true
build-type:
description: Type for the build
required: true
default: Debug
artifact-name:
description: Name of the uploaded artifact
required: true
default: macOS
apple-codesign-cert:
description: Certificate for signing macOS builds
required: false
apple-codesign-password:
description: Password for signing macOS builds
required: false
apple-codesign-id:
description: Certificate ID for signing macOS builds
required: false
apple-notarize-apple-id:
description: Apple ID used for notarizing macOS builds
required: false
apple-notarize-team-id:
description: Team ID used for notarizing macOS builds
required: false
apple-notarize-password:
description: Password used for notarizing macOS builds
required: false
sparkle-ed25519-key:
description: Private key for signing Sparkle updates
required: false
runs:
using: composite
steps:
- name: Fetch codesign certificate
shell: bash
run: |
echo '${{ inputs.apple-codesign-cert }}' | base64 --decode > codesign.p12
if [ -n '${{ inputs.apple-codesign-id }}' ]; then
security create-keychain -p '${{ inputs.apple-codesign-password }}' build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p '${{ inputs.apple-codesign-password }}' build.keychain
security import codesign.p12 -k build.keychain -P '${{ inputs.apple-codesign-password }}' -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k '${{ inputs.apple-codesign-password }}' build.keychain
else
echo ":warning: Using ad-hoc code signing for macOS, as certificate was not present." >> $GITHUB_STEP_SUMMARY
fi
- name: Package
shell: bash
env:
BUILD_DIR: build
INSTALL_DIR: install
run: |
cmake --install ${{ env.BUILD_DIR }}
cd ${{ env.INSTALL_DIR }}
chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
if [ -n '${{ inputs.apple-codesign-id }}' ]; then
APPLE_CODESIGN_ID='${{ inputs.apple-codesign-id }}'
ENTITLEMENTS_FILE='../program_info/App.entitlements'
else
APPLE_CODESIGN_ID='-'
ENTITLEMENTS_FILE='../program_info/AdhocSignedApp.entitlements'
fi
sudo codesign --sign "$APPLE_CODESIGN_ID" --deep --force --entitlements "$ENTITLEMENTS_FILE" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher"
mv "PrismLauncher.app" "Prism Launcher.app"
- name: Notarize
shell: bash
env:
INSTALL_DIR: install
run: |
cd ${{ env.INSTALL_DIR }}
if [ -n '${{ inputs.apple-notarize-password }}' ]; then
ditto -c -k --sequesterRsrc --keepParent "Prism Launcher.app" ../PrismLauncher.zip
xcrun notarytool submit ../PrismLauncher.zip \
--wait --progress \
--apple-id '${{ inputs.apple-notarize-apple-id }}' \
--team-id '${{ inputs.apple-notarize-team-id }}' \
--password '${{ inputs.apple-notarize-password }}'
xcrun stapler staple "Prism Launcher.app"
else
echo ":warning: Skipping notarization as credentials are not present." >> $GITHUB_STEP_SUMMARY
fi
ditto -c -k --sequesterRsrc --keepParent "Prism Launcher.app" ../PrismLauncher.zip
- name: Make Sparkle signature
shell: bash
run: |
if [ '${{ inputs.sparkle-ed25519-key }}' != '' ]; then
echo '${{ inputs.sparkle-ed25519-key }}' > ed25519-priv.pem
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\`
EOF
else
cat >> $GITHUB_STEP_SUMMARY << EOF
### Artifact Information :information_source:
- :warning: Sparkle Signature (ed25519): No private key available (likely a pull request or fork)
EOF
fi
- name: Upload binary tarball
uses: actions/upload-artifact@v4
with:
name: PrismLauncher-${{ inputs.artifact-name }}-${{ inputs.version }}-${{ inputs.build-type }}
path: PrismLauncher.zip

View File

@ -1,143 +0,0 @@
name: Package for Windows
description: Create a Windows package for Prism Launcher
inputs:
version:
description: Launcher version
required: true
build-type:
description: Type for the build
required: true
default: Debug
artifact-name:
description: Name of the uploaded artifact
required: true
msystem:
description: MSYS2 subsystem to use
required: true
default: false
windows-codesign-cert:
description: Certificate for signing Windows builds
required: false
windows-codesign-password:
description: Password for signing Windows builds
required: false
runs:
using: composite
steps:
- name: Package (MinGW)
if: ${{ inputs.msystem != '' }}
shell: msys2 {0}
env:
BUILD_DIR: build
INSTALL_DIR: install
run: |
cmake --install ${{ env.BUILD_DIR }}
touch ${{ env.INSTALL_DIR }}/manifest.txt
for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_DIR }}/manifest.txt
- name: Package (MSVC)
if: ${{ inputs.msystem == '' }}
shell: pwsh
env:
BUILD_DIR: build
INSTALL_DIR: install
run: |
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build-type }}
cd ${{ github.workspace }}
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
- name: Fetch codesign certificate
shell: bash # yes, we are not using MSYS2 or PowerShell here
run: |
echo '${{ inputs.windows-codesign-cert }}' | base64 --decode > codesign.pfx
- name: Sign executable
shell: pwsh
env:
INSTALL_DIR: install
run: |
if (Get-Content ./codesign.pfx){
cd ${{ env.INSTALL_DIR }}
# We ship the exact same executable for portable and non-portable editions, so signing just once is fine
SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ inputs.windows-codesign-password }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_updater.exe prismlauncher_filelink.exe
} else {
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
}
- name: Package (MinGW, portable)
if: ${{ inputs.msystem != '' }}
shell: msys2 {0}
env:
BUILD_DIR: build
INSTALL_DIR: install
INSTALL_PORTABLE_DIR: install-portable
run: |
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
- name: Package (MSVC, portable)
if: ${{ inputs.msystem == '' }}
shell: pwsh
env:
BUILD_DIR: build
INSTALL_DIR: install
INSTALL_PORTABLE_DIR: install-portable
run: |
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
Get-ChildItem ${{ env.INSTALL_PORTABLE_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_PORTABLE_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
- name: Package (installer)
shell: pwsh
env:
BUILD_DIR: build
INSTALL_DIR: install
NSCURL_VERSION: "v24.9.26.122"
NSCURL_SHA256: "AEE6C4BE3CB6455858E9C1EE4B3AFE0DB9960FA03FE99CCDEDC28390D57CCBB0"
run: |
New-Item -Name NSISPlugins -ItemType Directory
Invoke-Webrequest https://github.com/negrutiu/nsis-nscurl/releases/download/"${{ env.NSCURL_VERSION }}"/NScurl.zip -OutFile NSISPlugins\NScurl.zip
$nscurl_hash = Get-FileHash NSISPlugins\NScurl.zip -Algorithm Sha256 | Select-Object -ExpandProperty Hash
if ( $nscurl_hash -ne "${{ env.nscurl_sha256 }}") {
echo "::error:: NSCurl.zip sha256 mismatch"
exit 1
}
Expand-Archive -Path NSISPlugins\NScurl.zip -DestinationPath NSISPlugins\NScurl
cd ${{ env.INSTALL_DIR }}
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
- name: Sign installer
shell: pwsh
run: |
if (Get-Content ./codesign.pfx){
SignTool sign /fd sha256 /td sha256 /f codesign.pfx /p '${{ inputs.windows-codesign-password }}' /tr http://timestamp.digicert.com PrismLauncher-Setup.exe
} else {
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
}
- name: Upload binary zip
uses: actions/upload-artifact@v4
with:
name: PrismLauncher-${{ inputs.artifact-name }}-${{ inputs.version }}-${{ inputs.build-type }}
path: install/**
- name: Upload portable zip
uses: actions/upload-artifact@v4
with:
name: PrismLauncher-${{ inputs.artifact-name }}-Portable-${{ inputs.version }}-${{ inputs.build-type }}
path: install-portable/**
- name: Upload installer
uses: actions/upload-artifact@v4
with:
name: PrismLauncher-${{ inputs.artifact-name }}-Setup-${{ inputs.version }}-${{ inputs.build-type }}
path: PrismLauncher-Setup.exe

View File

@ -1,78 +0,0 @@
name: Setup Dependencies
description: Install and setup dependencies for building Prism Launcher
inputs:
build-type:
description: Type for the build
required: true
default: Debug
msystem:
description: MSYS2 subsystem to use
required: false
vcvars-arch:
description: Visual Studio architecture to use
required: false
qt-architecture:
description: Qt architecture
required: false
qt-version:
description: Version of Qt to use
required: true
default: 6.8.1
outputs:
build-type:
description: Type of build used
value: ${{ inputs.build-type }}
qt-version:
description: Version of Qt used
value: ${{ inputs.qt-version }}
runs:
using: composite
steps:
- name: Setup Linux dependencies
if: ${{ runner.os == 'Linux' }}
uses: ./.github/actions/setup-dependencies/linux
- name: Setup macOS dependencies
if: ${{ runner.os == 'macOS' }}
uses: ./.github/actions/setup-dependencies/macos
- name: Setup Windows dependencies
if: ${{ runner.os == 'Windows' }}
uses: ./.github/actions/setup-dependencies/windows
with:
build-type: ${{ inputs.build-type }}
msystem: ${{ inputs.msystem }}
vcvars-arch: ${{ inputs.vcvars-arch }}
# 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.18
with:
variant: ${{ runner.os == 'Windows' && 'sccache' || 'ccache' }}
create-symlink: ${{ runner.os != 'Windows' }}
key: ${{ runner.os }}-qt${{ inputs.qt_ver }}-${{ inputs.architecture }}
- name: Use ccache on debug builds
if: ${{ inputs.build-type == 'Debug' }}
shell: bash
env:
# Only use sccache on MSVC
CCACHE_VARIANT: ${{ (runner.os == 'Windows' && inputs.msystem == '') && 'sccache' || 'ccache' }}
run: |
echo "CMAKE_C_COMPILER_LAUNCHER=$CCACHE_VARIANT" >> "$GITHUB_ENV"
echo "CMAKE_CXX_COMPILER_LAUNCHER=$CCACHE_VARIANT" >> "$GITHUB_ENV"
- name: Install Qt
if: ${{ inputs.msystem == '' }}
uses: jurplel/install-qt-action@v4
with:
aqtversion: "==3.1.*"
version: ${{ inputs.qt-version }}
arch: ${{ inputs.qt-architecture }}
modules: qt5compat qtimageformats qtnetworkauth
cache: ${{ inputs.build-type == 'Debug' }}

View File

@ -1,26 +0,0 @@
name: Setup Linux dependencies
runs:
using: composite
steps:
- name: Install host dependencies
shell: bash
run: |
sudo apt-get -y update
sudo apt-get -y install ninja-build extra-cmake-modules scdoc appstream libxcb-cursor-dev
- name: Setup AppImage tooling
shell: bash
run: |
declare -A appimage_deps
appimage_deps["https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20250213-2/linuxdeploy-x86_64.AppImage"]="4648f278ab3ef31f819e67c30d50f462640e5365a77637d7e6f2ad9fd0b4522a linuxdeploy-x86_64.AppImage"
appimage_deps["https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/1-alpha-20250213-1/linuxdeploy-plugin-qt-x86_64.AppImage"]="15106be885c1c48a021198e7e1e9a48ce9d02a86dd0a1848f00bdbf3c1c92724 linuxdeploy-plugin-qt-x86_64.AppImage"
appimage_deps["https://github.com/AppImageCommunity/AppImageUpdate/releases/download/2.0.0-alpha-1-20241225/AppImageUpdate-x86_64.AppImage"]="f1747cf60058e99f1bb9099ee9787d16c10241313b7acec81810ea1b1e568c11 AppImageUpdate-x86_64.AppImage"
for url in "${!appimage_deps[@]}"; do
curl -LO "$url"
sha256sum -c - <<< "${appimage_deps[$url]}"
done
sudo apt -y install libopengl0

View File

@ -1,16 +0,0 @@
name: Setup macOS dependencies
runs:
using: composite
steps:
- name: Install dependencies
shell: bash
run: |
brew update
brew install ninja extra-cmake-modules temurin@17
- name: Set JAVA_HOME
shell: bash
run: |
echo "JAVA_HOME=$(/usr/libexec/java_home -v 17)" >> "$GITHUB_ENV"

View File

@ -1,66 +0,0 @@
name: Setup Windows Dependencies
inputs:
build-type:
description: Type for the build
required: true
default: Debug
msystem:
description: MSYS2 subsystem to use
required: false
vcvars-arch:
description: Visual Studio architecture to use
required: true
default: amd64
runs:
using: composite
steps:
# NOTE: Installed on MinGW as well for SignTool
- name: Enter VS Developer shell
if: ${{ runner.os == 'Windows' }}
uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{ inputs.vcvars-arch }}
vsversion: 2022
- name: Setup MSYS2 (MinGW-64)
if: ${{ inputs.msystem != '' }}
uses: msys2/setup-msys2@v2
with:
msystem: ${{ inputs.msystem }}
update: true
install: >-
git
pacboy: >-
toolchain:p
ccache:p
cmake:p
extra-cmake-modules:p
ninja:p
qt6-base:p
qt6-svg:p
qt6-imageformats:p
qt6-5compat:p
qt6-networkauth:p
cmark:p
quazip-qt6:p
- name: Retrieve ccache cache (MinGW)
if: ${{ inputs.msystem != '' && inputs.build-type == 'Debug' }}
uses: actions/cache@v4.2.3
with:
path: '${{ github.workspace }}\.ccache'
key: ${{ runner.os }}-mingw-w64-ccache-${{ github.run_id }}
restore-keys: |
${{ runner.os }}-mingw-w64-ccache
- name: Setup ccache (MinGW)
if: ${{ inputs.msystem != '' && inputs.build-type == 'Debug' }}
shell: msys2 {0}
run: |
ccache --set-config=cache_dir='${{ github.workspace }}\.ccache'
ccache --set-config=max_size='500M'
ccache --set-config=compression=true
ccache -p # Show config

View File

@ -25,7 +25,7 @@ jobs:
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
- name: Create backport PRs - name: Create backport PRs
uses: korthout/backport-action@v3.2.0 uses: korthout/backport-action@v3.1.0
with: with:
# Config README: https://github.com/korthout/backport-action#backport-action # Config README: https://github.com/korthout/backport-action#backport-action
pull_description: |- pull_description: |-

View File

@ -1,255 +0,0 @@
name: Blocked/Stacked Pull Requests Automation
on:
pull_request_target:
types:
- opened
- reopened
- edited
- synchronize
workflow_dispatch:
inputs:
pr_id:
description: Local Pull Request number to work on
required: true
type: number
jobs:
blocked_status:
name: Check Blocked Status
runs-on: ubuntu-latest
steps:
- name: Generate token
id: generate-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.PULL_REQUEST_APP_ID }}
private-key: ${{ secrets.PULL_REQUEST_APP_PRIVATE_KEY }}
- name: Setup From Dispatch Event
if: github.event_name == 'workflow_dispatch'
id: dispatch_event_setup
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
PR_NUMBER: ${{ inputs.pr_id }}
run: |
# setup env for the rest of the workflow
OWNER=$(dirname "${{ github.repository }}")
REPO=$(basename "${{ github.repository }}")
PR_JSON=$(
gh api \
-H "Accept: application/vnd.github.raw+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/$OWNER/$REPO/pulls/$PR_NUMBER"
)
echo "PR_JSON=$PR_JSON" >> "$GITHUB_ENV"
- name: Setup Environment
id: env_setup
env:
EVENT_PR_JSON: ${{ toJSON(github.event.pull_request) }}
run: |
# setup env for the rest of the workflow
PR_JSON=${PR_JSON:-"$EVENT_PR_JSON"}
{
echo "REPO=$(jq -r '.base.repo.name' <<< "$PR_JSON")"
echo "OWNER=$(jq -r '.base.repo.owner.login' <<< "$PR_JSON")"
echo "PR_NUMBER=$(jq -r '.number' <<< "$PR_JSON")"
echo "JOB_DATA=$(jq -c '
{
"repo": .base.repo.name,
"owner": .base.repo.owner.login,
"repoUrl": .base.repo.html_url,
"prNumber": .number,
"prHeadSha": .head.sha,
"prHeadLabel": .head.label,
"prBody": (.body // ""),
"prLabels": (reduce .labels[].name as $l ([]; . + [$l]))
}
' <<< "$PR_JSON")"
} >> "$GITHUB_ENV"
- name: Find Blocked/Stacked PRs in body
id: pr_ids
run: |
prs=$(
jq -c '
.prBody as $body
| (
$body |
reduce (
. | scan("blocked (?:by|on):? #([0-9]+)")
| map({
"type": "Blocked on",
"number": ( . | tonumber )
})
) as $i ([]; . + [$i[]])
) as $bprs
| (
$body |
reduce (
. | scan("stacked on:? #([0-9]+)")
| map({
"type": "Stacked on",
"number": ( . | tonumber )
})
) as $i ([]; . + [$i[]])
) as $sprs
| ($bprs + $sprs) as $prs
| {
"blocking": $prs,
"numBlocking": ( $prs | length),
}
' <<< "$JOB_DATA"
)
echo "prs=$prs" >> "$GITHUB_OUTPUT"
- name: Collect Blocked PR Data
id: blocking_data
if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
BLOCKING_PRS: ${{ steps.pr_ids.outputs.prs }}
run: |
blocked_pr_data=$(
while read -r pr_data ; do
gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/$OWNER/$REPO/pulls/$(jq -r '.number' <<< "$pr_data")" \
| jq -c --arg type "$(jq -r '.type' <<< "$pr_data")" \
'
. | {
"type": $type,
"number": .number,
"merged": .merged,
"state": (if .state == "open" then "Open" elif .merged then "Merged" else "Closed" end),
"labels": (reduce .labels[].name as $l ([]; . + [$l])),
"basePrUrl": .html_url,
"baseRepoName": .head.repo.name,
"baseRepoOwner": .head.repo.owner.login,
"baseRepoUrl": .head.repo.html_url,
"baseSha": .head.sha,
"baseRefName": .head.ref,
}
'
done < <(jq -c '.blocking[]' <<< "$BLOCKING_PRS") | jq -c -s
)
{
echo "data=$blocked_pr_data";
echo "all_merged=$(jq -r 'all(.[] | (.type == "Stacked on" and .merged) or (.type == "Blocked on" and (.state != "Open")); .)' <<< "$blocked_pr_data")";
echo "current_blocking=$(jq -c 'map(
select(
(.type == "Stacked on" and (.merged | not)) or
(.type == "Blocked on" and (.state == "Open"))
) | .number
)' <<< "$blocked_pr_data" )";
} >> "$GITHUB_OUTPUT"
- name: Add 'blocked' Label if Missing
id: label_blocked
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 'blocked' "$PR_NUMBER"
- name: Remove 'blocked' Label if All Dependencies Are Merged
id: unlabel_blocked
if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0 && 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 --remove-label 'blocked' "$PR_NUMBER"
- name: Apply 'blocking' Label to Unmerged Dependencies
id: label_blocking
if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0
continue-on-error: true
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
BLOCKING_ISSUES: ${{ steps.blocking_data.outputs.current_blocking }}
run: |
while read -r pr ; do
gh -R ${{ github.repository }} issue edit --add-label 'blocking' "$pr" || true
done < <(jq -c '.[]' <<< "$BLOCKING_ISSUES")
- name: Apply Blocking PR Status Check
id: blocked_check
if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0
continue-on-error: true
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
BLOCKING_DATA: ${{ steps.blocking_data.outputs.data }}
run: |
pr_head_sha=$(jq -r '.prHeadSha' <<< "$JOB_DATA")
# create commit Status, overwrites previous identical context
while read -r pr_data ; do
DESC=$(
jq -r 'if .type == "Stacked on" then
"Stacked PR #" + (.number | tostring) + " is " + (if .merged then "" else "not yet " end) + "merged"
else
"Blocking PR #" + (.number | tostring) + " is " + (if .state == "Open" then "" else "not yet " end) + "merged or closed"
end ' <<< "$pr_data"
)
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/${OWNER}/${REPO}/statuses/${pr_head_sha}" \
-f "state=$(jq -r 'if (.type == "Stacked on" and .merged) or (.type == "Blocked on" and (.state != "Open")) then "success" else "failure" end' <<< "$pr_data")" \
-f "target_url=$(jq -r '.basePrUrl' <<< "$pr_data" )" \
-f "description=$DESC" \
-f "context=ci/blocking-pr-check:$(jq '.number' <<< "$pr_data")"
done < <(jq -c '.[]' <<< "$BLOCKING_DATA")
- name: Context Comment
id: generate-comment
if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0
continue-on-error: true
env:
BLOCKING_DATA: ${{ steps.blocking_data.outputs.data }}
run: |
COMMENT_PATH="$(pwd)/temp_comment_file.txt"
echo '<h3>PR Dependencies :pushpin:</h3>' > "$COMMENT_PATH"
echo >> "$COMMENT_PATH"
pr_head_label=$(jq -r '.prHeadLabel' <<< "$JOB_DATA")
while read -r pr_data ; do
base_pr=$(jq -r '.number' <<< "$pr_data")
base_ref_name=$(jq -r '.baseRefName' <<< "$pr_data")
base_repo_owner=$(jq -r '.baseRepoOwner' <<< "$pr_data")
base_repo_name=$(jq -r '.baseRepoName' <<< "$pr_data")
compare_url="https://github.com/$base_repo_owner/$base_repo_name/compare/$base_ref_name...$pr_head_label"
status=$(jq -r '
if .type == "Stacked on" then
if .merged then ":heavy_check_mark: Merged" else ":x: Not Merged (" + .state + ")" end
else
if .state != "Open" then ":white_check_mark: " + .state else ":x: Open" end
end
' <<< "$pr_data")
type=$(jq -r '.type' <<< "$pr_data")
echo " - $type #$base_pr $status [(compare)]($compare_url)" >> "$COMMENT_PATH"
done < <(jq -c '.[]' <<< "$BLOCKING_DATA")
{
echo 'body<<EOF';
cat "${COMMENT_PATH}";
echo 'EOF';
} >> "$GITHUB_OUTPUT"
- name: 💬 PR Comment
if: fromJSON(steps.pr_ids.outputs.prs).numBlocking > 0
continue-on-error: true
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
COMMENT_BODY: ${{ steps.generate-comment.outputs.body }}
run: |
gh -R ${{ github.repository }} issue comment "$PR_NUMBER" \
--body "$COMMENT_BODY" \
--create-if-none \
--edit-last

View File

@ -1,195 +1,638 @@
name: Build name: Build
on: on:
push:
branches-ignore:
- "renovate/**"
paths:
# File types
- "**.cpp"
- "**.h"
- "**.java"
# Directories
- "buildconfig/"
- "cmake/"
- "launcher/"
- "libraries/"
- "program_info/"
- "tests/"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/build.yml"
pull_request:
paths:
# File types
- "**.cpp"
- "**.h"
# Directories
- "buildconfig/"
- "cmake/"
- "launcher/"
- "libraries/"
- "program_info/"
- "tests/"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/build.yml"
workflow_call: workflow_call:
inputs: inputs:
build-type: build_type:
description: Type of build (Debug or Release) description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel)
type: string type: string
default: Debug default: Debug
workflow_dispatch: is_qt_cached:
inputs: description: Enable Qt caching or not
build-type:
description: Type of build (Debug or Release)
type: string type: string
default: Debug default: true
secrets:
SPARKLE_ED25519_KEY:
description: Private key for signing Sparkle updates
required: false
WINDOWS_CODESIGN_CERT:
description: Certificate for signing Windows builds
required: false
WINDOWS_CODESIGN_PASSWORD:
description: Password for signing Windows builds
required: false
APPLE_CODESIGN_CERT:
description: Certificate for signing macOS builds
required: false
APPLE_CODESIGN_PASSWORD:
description: Password for signing macOS builds
required: false
APPLE_CODESIGN_ID:
description: Certificate ID for signing macOS builds
required: false
APPLE_NOTARIZE_APPLE_ID:
description: Apple ID used for notarizing macOS builds
required: false
APPLE_NOTARIZE_TEAM_ID:
description: Team ID used for notarizing macOS builds
required: false
APPLE_NOTARIZE_PASSWORD:
description: Password used for notarizing macOS builds
required: false
GPG_PRIVATE_KEY:
description: Private key for AppImage signing
required: false
GPG_PRIVATE_KEY_ID:
description: ID for the GPG_PRIVATE_KEY, to select the signing key
required: false
jobs: jobs:
build: build:
name: Build (${{ matrix.artifact-name }})
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- os: ubuntu-20.04
qt_ver: 5
qt_host: linux
qt_arch: ""
qt_version: "5.15.2"
qt_modules: "qtnetworkauth"
- os: ubuntu-22.04 - os: ubuntu-22.04
artifact-name: Linux qt_ver: 6
base-cmake-preset: linux qt_host: linux
qt_arch: ""
qt_version: "6.5.3"
qt_modules: "qt5compat qtimageformats qtnetworkauth"
linuxdeploy_hash: "4648f278ab3ef31f819e67c30d50f462640e5365a77637d7e6f2ad9fd0b4522a linuxdeploy-x86_64.AppImage"
linuxdeploy_qt_hash: "15106be885c1c48a021198e7e1e9a48ce9d02a86dd0a1848f00bdbf3c1c92724 linuxdeploy-plugin-qt-x86_64.AppImage"
appimageupdate_hash: "f1747cf60058e99f1bb9099ee9787d16c10241313b7acec81810ea1b1e568c11 AppImageUpdate-x86_64.AppImage"
- os: windows-2022 - os: windows-2022
artifact-name: Windows-MinGW-w64 name: "Windows-MinGW-w64"
base-cmake-preset: windows_mingw msystem: clang64
msystem: CLANG64 vcvars_arch: "amd64_x86"
vcvars-arch: amd64_x86
- os: windows-11-arm
artifact-name: Windows-MinGW-arm64
base-cmake-preset: windows_mingw
msystem: CLANGARM64
vcvars-arch: arm64
- os: windows-2022 - os: windows-2022
artifact-name: Windows-MSVC name: "Windows-MSVC"
base-cmake-preset: windows_msvc msystem: ""
# TODO(@getchoo): This is the default in setup-dependencies/windows. Why isn't it working?!?! architecture: "x64"
vcvars-arch: amd64 vcvars_arch: "amd64"
qt_ver: 6
qt_host: "windows"
qt_arch: "win64_msvc2022_64"
qt_version: "6.8.1"
qt_modules: "qt5compat qtimageformats qtnetworkauth"
nscurl_tag: "v24.9.26.122"
nscurl_sha256: "AEE6C4BE3CB6455858E9C1EE4B3AFE0DB9960FA03FE99CCDEDC28390D57CCBB0"
- os: windows-2022 - os: windows-2022
artifact-name: Windows-MSVC-arm64 name: "Windows-MSVC-arm64"
base-cmake-preset: windows_msvc_arm64_cross msystem: ""
vcvars-arch: amd64_arm64 architecture: "arm64"
qt-architecture: win64_msvc2022_arm64_cross_compiled vcvars_arch: "amd64_arm64"
qt_ver: 6
qt_host: "windows"
qt_arch: "win64_msvc2022_arm64_cross_compiled"
qt_version: "6.8.1"
qt_modules: "qt5compat qtimageformats qtnetworkauth"
nscurl_tag: "v24.9.26.122"
nscurl_sha256: "AEE6C4BE3CB6455858E9C1EE4B3AFE0DB9960FA03FE99CCDEDC28390D57CCBB0"
- os: macos-14 - os: macos-14
artifact-name: macOS name: macOS
base-cmake-preset: ${{ (inputs.build-type || 'Debug') == 'Debug' && 'macos_universal' || 'macos' }} macosx_deployment_target: 11.0
macosx-deployment-target: 12.0 qt_ver: 6
qt_host: mac
qt_arch: ""
qt_version: "6.8.1"
qt_modules: "qt5compat qtimageformats qtnetworkauth"
- os: macos-14
name: macOS-Legacy
macosx_deployment_target: 10.13
qt_ver: 5
qt_host: mac
qt_version: "5.15.2"
qt_modules: "qtnetworkauth"
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
defaults:
run:
shell: ${{ matrix.msystem != '' && 'msys2 {0}' || 'bash' }}
env: env:
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx-deployment-target }} MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }}
INSTALL_DIR: "install"
INSTALL_PORTABLE_DIR: "install-portable"
INSTALL_APPIMAGE_DIR: "install-appdir"
BUILD_DIR: "build"
CCACHE_VAR: ""
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
steps: steps:
## ##
# SETUP # PREPARE
## ##
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
submodules: true submodules: "true"
- name: Setup dependencies - name: "Setup MSYS2"
id: setup-dependencies if: runner.os == 'Windows' && matrix.msystem != ''
uses: ./.github/actions/setup-dependencies uses: msys2/setup-msys2@v2
with: with:
build-type: ${{ inputs.build-type || 'Debug' }}
msystem: ${{ matrix.msystem }} msystem: ${{ matrix.msystem }}
vcvars-arch: ${{ matrix.vcvars-arch }} update: true
qt-architecture: ${{ matrix.qt-architecture }} install: >-
git
mingw-w64-x86_64-binutils
pacboy: >-
toolchain:p
cmake:p
extra-cmake-modules:p
ninja:p
qt6-base:p
qt6-svg:p
qt6-imageformats:p
quazip-qt6:p
ccache:p
qt6-5compat:p
qt6-networkauth:p
cmark:p
- name: Force newer ccache
if: runner.os == 'Windows' && matrix.msystem == '' && inputs.build_type == 'Debug'
run: |
choco install ccache --version 4.7.1
- name: Setup ccache
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
uses: hendrikmuhs/ccache-action@v1.2.14
with:
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}
- name: Retrieve ccache cache (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
uses: actions/cache@v4.2.0
with:
path: '${{ github.workspace }}\.ccache'
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
restore-keys: |
${{ matrix.os }}-mingw-w64-ccache
- name: Setup ccache (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
shell: msys2 {0}
run: |
ccache --set-config=cache_dir='${{ github.workspace }}\.ccache'
ccache --set-config=max_size='500M'
ccache --set-config=compression=true
ccache -p # Show config
ccache -z # Zero stats
- name: Use ccache on Debug builds only
if: inputs.build_type == 'Debug'
shell: bash
run: |
echo "CCACHE_VAR=ccache" >> $GITHUB_ENV
- name: Set short version
shell: bash
run: |
ver_short=`git rev-parse --short HEAD`
echo "VERSION=$ver_short" >> $GITHUB_ENV
- name: Install Dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get -y update
sudo apt-get -y install ninja-build extra-cmake-modules scdoc appstream libxcb-cursor-dev
- name: Install Dependencies (macOS)
if: runner.os == 'macOS'
run: |
brew update
brew install ninja extra-cmake-modules
- name: Install host Qt (Windows MSVC arm64)
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
uses: jurplel/install-qt-action@v4
with:
aqtversion: "==3.1.*"
py7zrversion: ">=0.20.2"
version: ${{ matrix.qt_version }}
host: "windows"
target: "desktop"
arch: ${{ matrix.qt_arch }}
modules: ${{ matrix.qt_modules }}
cache: ${{ inputs.is_qt_cached }}
cache-key-prefix: host-qt-arm64-windows
dir: ${{ github.workspace }}\HostQt
set-env: false
- name: Install Qt (macOS, Linux & Windows MSVC)
if: matrix.msystem == ''
uses: jurplel/install-qt-action@v4
with:
aqtversion: "==3.1.*"
py7zrversion: ">=0.20.2"
version: ${{ matrix.qt_version }}
target: "desktop"
arch: ${{ matrix.qt_arch }}
modules: ${{ matrix.qt_modules }}
tools: ${{ matrix.qt_tools }}
cache: ${{ inputs.is_qt_cached }}
- name: Install MSVC (Windows MSVC)
if: runner.os == 'Windows' # We want this for MinGW builds as well, as we need SignTool
uses: ilammy/msvc-dev-cmd@v1
with:
vsversion: 2022
arch: ${{ matrix.vcvars_arch }}
- name: Prepare AppImage (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 5
env:
APPIMAGEUPDATE_HASH: ${{ matrix.appimageupdate_hash }}
LINUXDEPLOY_HASH: ${{ matrix.linuxdeploy_hash }}
LINUXDEPLOY_QT_HASH: ${{ matrix.linuxdeploy_qt_hash }}
run: |
wget "https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20250213-2/linuxdeploy-x86_64.AppImage"
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/1-alpha-20250213-1/linuxdeploy-plugin-qt-x86_64.AppImage"
wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/2.0.0-alpha-1-20241225/AppImageUpdate-x86_64.AppImage"
sha256sum -c - <<< "$LINUXDEPLOY_HASH"
sha256sum -c - <<< "$LINUXDEPLOY_QT_HASH"
sha256sum -c - <<< "$APPIMAGEUPDATE_HASH"
sudo apt install libopengl0
- name: Add QT_HOST_PATH var (Windows MSVC arm64)
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
run: |
echo "QT_HOST_PATH=${{ github.workspace }}\HostQt\Qt\${{ matrix.qt_version }}\msvc2022_64" >> $env:GITHUB_ENV
- name: Setup java (macOS)
if: runner.os == 'macOS'
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "17"
##
# CONFIGURE
##
- name: Configure CMake (macOS)
if: runner.os == 'macOS' && matrix.qt_ver == 6
run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_ENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
- name: Configure CMake (macOS-Legacy)
if: runner.os == 'macOS' && matrix.qt_ver == 5
run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_ENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -DCMAKE_OSX_ARCHITECTURES="x86_64" -G Ninja
- name: Configure CMake (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != ''
shell: msys2 {0}
run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_ENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja
- name: Configure CMake (Windows MSVC)
if: runner.os == 'Windows' && matrix.msystem == ''
run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_ENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }}
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
if ("${{ env.CCACHE_VAR }}")
{
Copy-Item C:/ProgramData/chocolatey/lib/ccache/tools/ccache-4.7.1-windows-x86_64/ccache.exe -Destination C:/ProgramData/chocolatey/lib/ccache/tools/ccache-4.7.1-windows-x86_64/cl.exe
echo "CLToolExe=cl.exe" >> $env:GITHUB_ENV
echo "CLToolPath=C:/ProgramData/chocolatey/lib/ccache/tools/ccache-4.7.1-windows-x86_64/" >> $env:GITHUB_ENV
echo "TrackFileAccess=false" >> $env:GITHUB_ENV
}
# Needed for ccache, but also speeds up compile
echo "UseMultiToolTask=true" >> $env:GITHUB_ENV
- name: Configure CMake (Linux)
if: runner.os == 'Linux'
run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_ENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja
## ##
# BUILD # BUILD
## ##
- name: Get CMake preset - name: Build
id: cmake-preset if: runner.os != 'Windows'
env:
BASE_CMAKE_PRESET: ${{ matrix.base-cmake-preset }}
PRESET_TYPE: ${{ (inputs.build-type || 'Debug') == 'Debug' && 'debug' || 'ci' }}
run: | run: |
echo preset="$BASE_CMAKE_PRESET"_"$PRESET_TYPE" >> "$GITHUB_OUTPUT" cmake --build ${{ env.BUILD_DIR }}
- name: Run CMake workflow - name: Build (Windows MinGW-w64)
env: if: runner.os == 'Windows' && matrix.msystem != ''
CMAKE_PRESET: ${{ steps.cmake-preset.outputs.preset }} shell: msys2 {0}
run: | run: |
cmake --workflow --preset "$CMAKE_PRESET" cmake --build ${{ env.BUILD_DIR }}
- name: Build (Windows MSVC)
if: runner.os == 'Windows' && matrix.msystem == ''
run: |
cmake --build ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
## ##
# PACKAGE # TEST
## ##
- name: Get short version - name: Test
id: short-version if: runner.os != 'Windows'
shell: bash
run: | run: |
echo "version=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT" ctest -E "^example64|example$" --test-dir build --output-on-failure
- name: Package (Linux) - name: Test (Windows MinGW-w64)
if: ${{ runner.os == 'Linux' }} if: runner.os == 'Windows' && matrix.msystem != ''
uses: ./.github/actions/package/linux shell: msys2 {0}
with: run: |
version: ${{ steps.short-version.outputs.version }} ctest -E "^example64|example$" --test-dir build --output-on-failure
build-type: ${{ steps.setup-dependencies.outputs.build-type }}
cmake-preset: ${{ steps.cmake-preset.outputs.preset }}
qt-version: ${{ steps.setup-dependencies.outputs.qt-version }}
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} - name: Test (Windows MSVC)
gpg-private-key-id: ${{ secrets.GPG_PRIVATE_KEY_ID }} if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'arm64'
run: |
ctest -E "^example64|example$" --test-dir build --output-on-failure -C ${{ inputs.build_type }}
##
# PACKAGE BUILDS
##
- name: Fetch codesign certificate (macOS)
if: runner.os == 'macOS'
run: |
echo '${{ secrets.APPLE_CODESIGN_CERT }}' | base64 --decode > codesign.p12
if [ -n '${{ secrets.APPLE_CODESIGN_ID }}' ]; then
security create-keychain -p '${{ secrets.APPLE_CODESIGN_PASSWORD }}' build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p '${{ secrets.APPLE_CODESIGN_PASSWORD }}' build.keychain
security import codesign.p12 -k build.keychain -P '${{ secrets.APPLE_CODESIGN_PASSWORD }}' -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k '${{ secrets.APPLE_CODESIGN_PASSWORD }}' build.keychain
else
echo ":warning: Using ad-hoc code signing for macOS, as certificate was not present." >> $GITHUB_STEP_SUMMARY
fi
- name: Package (macOS) - name: Package (macOS)
if: ${{ runner.os == 'macOS' }} if: runner.os == 'macOS'
uses: ./.github/actions/package/macos run: |
cmake --install ${{ env.BUILD_DIR }}
cd ${{ env.INSTALL_DIR }}
chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
if [ -n '${{ secrets.APPLE_CODESIGN_ID }}' ]; then
APPLE_CODESIGN_ID='${{ secrets.APPLE_CODESIGN_ID }}'
else
APPLE_CODESIGN_ID='-'
fi
sudo codesign --sign "$APPLE_CODESIGN_ID" --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher"
mv "PrismLauncher.app" "Prism Launcher.app"
- name: Notarize (macOS)
if: runner.os == 'macOS'
run: |
cd ${{ env.INSTALL_DIR }}
if [ -n '${{ secrets.APPLE_NOTARIZE_PASSWORD }}' ]; then
ditto -c -k --sequesterRsrc --keepParent "Prism Launcher.app" ../PrismLauncher.zip
xcrun notarytool submit ../PrismLauncher.zip \
--wait --progress \
--apple-id '${{ secrets.APPLE_NOTARIZE_APPLE_ID }}' \
--team-id '${{ secrets.APPLE_NOTARIZE_TEAM_ID }}' \
--password '${{ secrets.APPLE_NOTARIZE_PASSWORD }}'
xcrun stapler staple "Prism Launcher.app"
else
echo ":warning: Skipping notarization as credentials are not present." >> $GITHUB_STEP_SUMMARY
fi
ditto -c -k --sequesterRsrc --keepParent "Prism Launcher.app" ../PrismLauncher.zip
- name: Make Sparkle signature (macOS)
if: matrix.name == 'macOS'
run: |
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem
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\`
EOF
else
cat >> $GITHUB_STEP_SUMMARY << EOF
### Artifact Information :information_source:
- :warning: Sparkle Signature (ed25519): No private key available (likely a pull request or fork)
EOF
fi
- name: Package (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != ''
shell: msys2 {0}
run: |
cmake --install ${{ env.BUILD_DIR }}
touch ${{ env.INSTALL_DIR }}/manifest.txt
for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_DIR }}/manifest.txt
- name: Package (Windows MSVC)
if: runner.os == 'Windows' && matrix.msystem == ''
run: |
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
cd ${{ github.workspace }}
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
- name: Fetch codesign certificate (Windows)
if: runner.os == 'Windows'
shell: bash # yes, we are not using MSYS2 or PowerShell here
run: |
echo '${{ secrets.WINDOWS_CODESIGN_CERT }}' | base64 --decode > codesign.pfx
- name: Sign executable (Windows)
if: runner.os == 'Windows'
run: |
if (Get-Content ./codesign.pfx){
cd ${{ env.INSTALL_DIR }}
# We ship the exact same executable for portable and non-portable editions, so signing just once is fine
SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_updater.exe prismlauncher_filelink.exe
} else {
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
}
- name: Package (Windows MinGW-w64, portable)
if: runner.os == 'Windows' && matrix.msystem != ''
shell: msys2 {0}
run: |
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
- name: Package (Windows MSVC, portable)
if: runner.os == 'Windows' && matrix.msystem == ''
run: |
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
Get-ChildItem ${{ env.INSTALL_PORTABLE_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_PORTABLE_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
- name: Package (Windows, installer)
if: runner.os == 'Windows'
run: |
if ('${{ matrix.nscurl_tag }}') {
New-Item -Name NSISPlugins -ItemType Directory
Invoke-Webrequest https://github.com/negrutiu/nsis-nscurl/releases/download/${{ matrix.nscurl_tag }}/NScurl.zip -OutFile NSISPlugins\NScurl.zip
$nscurl_hash = Get-FileHash NSISPlugins\NScurl.zip -Algorithm Sha256 | Select-Object -ExpandProperty Hash
if ( $nscurl_hash -ne "${{ matrix.nscurl_sha256 }}") {
echo "::error:: NSCurl.zip sha256 mismatch"
exit 1
}
Expand-Archive -Path NSISPlugins\NScurl.zip -DestinationPath NSISPlugins\NScurl
}
cd ${{ env.INSTALL_DIR }}
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
- name: Sign installer (Windows)
if: runner.os == 'Windows'
run: |
if (Get-Content ./codesign.pfx){
SignTool sign /fd sha256 /td sha256 /f codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com PrismLauncher-Setup.exe
} else {
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
}
- name: Package AppImage (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 5
shell: bash
env:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
run: |
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
mv ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.metainfo.xml ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml
export "NO_APPSTREAM=1" # we have to skip appstream checking because appstream on ubuntu 20.04 is outdated
export OUTPUT="PrismLauncher-Linux-x86_64.AppImage"
chmod +x linuxdeploy-*.AppImage
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
cp /usr/lib/x86_64-linux-gnu/libssl.so.* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
export LD_LIBRARY_PATH
chmod +x AppImageUpdate-x86_64.AppImage
cp AppImageUpdate-x86_64.AppImage ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin
export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-x86_64.AppImage.zsync"
if [ '${{ secrets.GPG_PRIVATE_KEY_ID }}' != '' ]; then
export SIGN=1
export SIGN_KEY=${{ secrets.GPG_PRIVATE_KEY_ID }}
mkdir -p ~/.gnupg/
echo "$GPG_PRIVATE_KEY" > ~/.gnupg/private.key
gpg --import ~/.gnupg/private.key
else
echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY
fi
./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.prismlauncher.PrismLauncher.svg
mv "PrismLauncher-Linux-x86_64.AppImage" "PrismLauncher-Linux-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
- name: Package (Linux, portable)
if: runner.os == 'Linux'
run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_PORTABLE_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -DINSTALL_BUNDLE=full -G Ninja
cmake --install ${{ env.BUILD_DIR }}
cmake --install ${{ env.BUILD_DIR }} --component portable
mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib
cp /lib/x86_64-linux-gnu/libbz2.so.1.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib
cp /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
cp /usr/lib/x86_64-linux-gnu/libssl.so.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
cp /usr/lib/x86_64-linux-gnu/libffi.so.*.* ${{ env.INSTALL_PORTABLE_DIR }}/lib
mv ${{ env.INSTALL_PORTABLE_DIR }}/bin/*.so* ${{ env.INSTALL_PORTABLE_DIR }}/lib
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
cd ${{ env.INSTALL_PORTABLE_DIR }}
tar -czf ../PrismLauncher-portable.tar.gz *
##
# UPLOAD BUILDS
##
- name: Upload binary tarball (macOS)
if: runner.os == 'macOS'
uses: actions/upload-artifact@v4
with: with:
version: ${{ steps.short-version.outputs.version }} name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
build-type: ${{ steps.setup-dependencies.outputs.build-type }} path: PrismLauncher.zip
artifact-name: ${{ matrix.artifact-name }}
apple-codesign-cert: ${{ secrets.APPLE-CODESIGN-CERT }} - name: Upload binary zip (Windows)
apple-codesign-password: ${{ secrets.APPLE-CODESIGN_PASSWORD }} if: runner.os == 'Windows'
apple-codesign-id: ${{ secrets.APPLE-CODESIGN_ID }} uses: actions/upload-artifact@v4
apple-notarize-apple-id: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
apple-notarize-team-id: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
apple-notarize-password: ${{ secrets.APPLE-NOTARIZE_PASSWORD }}
sparkle-ed25519-key: ${{ secrets.SPARKLE-ED25519_KEY }}
- name: Package (Windows)
if: ${{ runner.os == 'Windows' }}
uses: ./.github/actions/package/windows
with: with:
version: ${{ steps.short-version.outputs.version }} name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
build-type: ${{ steps.setup-dependencies.outputs.build-type }} path: ${{ env.INSTALL_DIR }}/**
artifact-name: ${{ matrix.artifact-name }}
msystem: ${{ matrix.msystem }}
windows-codesign-cert: ${{ secrets.WINDOWS_CODESIGN_CERT }} - name: Upload binary zip (Windows, portable)
windows-codesign-password: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }} if: runner.os == 'Windows'
uses: actions/upload-artifact@v4
with:
name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: ${{ env.INSTALL_PORTABLE_DIR }}/**
- name: Upload installer (Windows)
if: runner.os == 'Windows'
uses: actions/upload-artifact@v4
with:
name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher-Setup.exe
- name: Upload binary tarball (Linux, portable, Qt 5)
if: runner.os == 'Linux' && matrix.qt_ver != 6
uses: actions/upload-artifact@v4
with:
name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher-portable.tar.gz
- name: Upload binary tarball (Linux, portable, Qt 6)
if: runner.os == 'Linux' && matrix.qt_ver != 5
uses: actions/upload-artifact@v4
with:
name: PrismLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher-portable.tar.gz
- name: Upload AppImage (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 5
uses: actions/upload-artifact@v4
with:
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
- name: Upload AppImage Zsync (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 5
uses: actions/upload-artifact@v4
with:
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync
path: PrismLauncher-Linux-x86_64.AppImage.zsync
- name: ccache stats (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != ''
shell: msys2 {0}
run: |
ccache -s

View File

@ -1,50 +1,6 @@
name: "CodeQL Code Scanning" name: "CodeQL Code Scanning"
on: on: [ push, pull_request, workflow_dispatch ]
push:
paths:
# File types
- "**.cpp"
- "**.h"
- "**.java"
# Directories
- "buildconfig/"
- "cmake/"
- "launcher/"
- "libraries/"
- "program_info/"
- "tests/"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/codeql"
- ".github/workflows/codeql.yml"
pull_request:
paths:
# File types
- "**.cpp"
- "**.h"
# Directories
- "buildconfig/"
- "cmake/"
- "launcher/"
- "libraries/"
- "program_info/"
- "tests/"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/codeql"
- ".github/workflows/codeql.yml"
workflow_dispatch:
jobs: jobs:
CodeQL: CodeQL:
@ -54,7 +10,7 @@ jobs:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
submodules: "true" submodules: 'true'
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v3 uses: github/codeql-action/init@v3
@ -63,15 +19,17 @@ jobs:
queries: security-and-quality queries: security-and-quality
languages: cpp, java languages: cpp, java
- name: Setup dependencies - name: Install Dependencies
uses: ./.github/actions/setup-dependencies run:
with: sudo apt-get -y update
build-type: Debug
sudo apt-get -y install ninja-build extra-cmake-modules scdoc qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 libqt5networkauth5 libqt5networkauth5-dev
- name: Configure and Build - name: Configure and Build
run: | run: |
cmake --preset linux_debug cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr -DLauncher_QT_VERSION_MAJOR=5 -G Ninja
cmake --build --preset linux_debug
cmake --build build
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3 uses: github/codeql-action/analyze@v3

View File

@ -2,55 +2,22 @@ name: Flatpak
on: on:
push: push:
paths-ignore:
- "**.md"
- "**/LICENSE"
- ".github/ISSUE_TEMPLATE/**"
- ".markdownlint**"
- "nix/**"
# We don't do anything with these artifacts on releases. They go to Flathub # We don't do anything with these artifacts on releases. They go to Flathub
tags-ignore: tags-ignore:
- "*" - "*"
paths:
# File types
- "**.cpp"
- "**.h"
- "**.java"
# Build files
- "flatpak/"
# Directories
- "buildconfig/"
- "cmake/"
- "launcher/"
- "libraries/"
- "program_info/"
- "tests/"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/flatpak.yml"
pull_request: pull_request:
paths: paths-ignore:
# File types - "**.md"
- "**.cpp" - "**/LICENSE"
- "**.h" - ".github/ISSUE_TEMPLATE/**"
- ".markdownlint**"
# Build files - "nix/**"
- "flatpak/"
# Directories
- "buildconfig/"
- "cmake/"
- "launcher/"
- "libraries/"
- "program_info/"
- "tests/"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/flatpak.yml"
workflow_dispatch: workflow_dispatch:
permissions: permissions:

View File

@ -1,62 +0,0 @@
name: Merged Blocking Pull Request Automation
on:
pull_request_target:
types:
- closed
workflow_dispatch:
inputs:
pr_id:
description: Local Pull Request number to work on
required: true
type: number
jobs:
update-blocked-status:
name: Update Blocked Status
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, 'blocking') }}
steps:
- name: Generate token
id: generate-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.PULL_REQUEST_APP_ID }}
private-key: ${{ secrets.PULL_REQUEST_APP_PRIVATE_KEY }}
- name: Gather Dependent PRs
id: gather_deps
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
PR_NUMBER: ${{ inputs.pr_id || github.event.pull_request.number }}
run: |
blocked_prs=$(
gh -R ${{ github.repository }} pr list --label 'blocked' --json 'number,body' \
| jq -c --argjson pr "$PR_NUMBER" '
reduce ( .[] | select(
.body |
scan("(?:blocked (?:by|on)|stacked on):? #([0-9]+)") |
map(tonumber) |
any(.[]; . == $pr)
)) as $i ([]; . + [$i])
'
)
{
echo "deps=$blocked_prs"
echo "numdeps=$(jq -r '. | length' <<< "$blocked_prs")"
} >> "$GITHUB_OUTPUT"
- name: Trigger Blocked PR Workflows for Dependants
if: fromJSON(steps.gather_deps.outputs.numdeps) > 0
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
DEPS: ${{ steps.gather_deps.outputs.deps }}
run: |
while read -r pr ; do
gh -R ${{ github.repository }} workflow run 'blocked-prs.yml' -r "${{ github.ref_name }}" -f pr_id="$pr"
done < <(jq -c '.[].number' <<< "$DEPS")

View File

@ -2,58 +2,21 @@ name: Nix
on: on:
push: push:
paths-ignore:
- "**.md"
- "**/LICENSE"
- ".github/ISSUE_TEMPLATE/**"
- ".markdownlint**"
- "flatpak/**"
tags: tags:
- "*" - "*"
paths:
# File types
- "**.cpp"
- "**.h"
- "**.java"
# Build files
- "**.nix"
- "nix/"
- "flake.lock"
# Directories
- "buildconfig/"
- "cmake/"
- "launcher/"
- "libraries/"
- "program_info/"
- "tests/"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/nix.yml"
pull_request_target: pull_request_target:
paths: paths-ignore:
# File types - "**.md"
- "**.cpp" - "**/LICENSE"
- "**.h" - ".github/ISSUE_TEMPLATE/**"
- ".markdownlint**"
# Build files - "flatpak/**"
- "**.nix"
- "nix/"
- "flake.lock"
# Directories
- "buildconfig/"
- "cmake/"
- "launcher/"
- "libraries/"
- "program_info/"
- "tests/"
# Files
- "CMakeLists.txt"
- "COPYING.md"
# Workflows
- ".github/workflows/nix.yml"
workflow_dispatch: workflow_dispatch:
permissions: permissions:
@ -89,34 +52,23 @@ jobs:
id-token: write id-token: write
steps: steps:
- name: Get merge commit
if: ${{ github.event_name == 'pull_request_target' }}
id: merge-commit
uses: PrismLauncher/PrismLauncher/.github/actions/get-merge-commit@develop
with:
pull-request-id: ${{ github.event.number }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
ref: ${{ steps.merge-commit.outputs.merge-commit-sha || github.sha }}
- name: Install Nix - name: Install Nix
uses: DeterminateSystems/nix-installer-action@v17 uses: DeterminateSystems/nix-installer-action@v16
with: with:
determinate: ${{ env.USE_DETERMINATE }} determinate: ${{ env.USE_DETERMINATE }}
# For PRs # For PRs
- name: Setup Nix Magic Cache - name: Setup Nix Magic Cache
if: ${{ env.USE_DETERMINATE == 'true' }} if: ${{ env.USE_DETERMINATE }}
uses: DeterminateSystems/flakehub-cache-action@v1 uses: DeterminateSystems/flakehub-cache-action@v1
# For in-tree builds # For in-tree builds
- name: Setup Cachix - name: Setup Cachix
if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
uses: cachix/cachix-action@v16 uses: cachix/cachix-action@v15
with: with:
name: prismlauncher name: prismlauncher
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
@ -126,18 +78,11 @@ jobs:
nix flake check --print-build-logs --show-trace nix flake check --print-build-logs --show-trace
- name: Build debug package - name: Build debug package
if: ${{ env.DEBUG == 'true' }} if: ${{ env.DEBUG }}
run: | run: |
nix build \ nix build --print-build-logs .#prismlauncher-debug
--no-link --print-build-logs --print-out-paths \
.#prismlauncher-debug >> "$GITHUB_STEP_SUMMARY"
- name: Build release package - name: Build release package
if: ${{ env.DEBUG == 'false' }} if: ${{ !env.DEBUG }}
env:
TAG: ${{ github.ref_name }}
SYSTEM: ${{ matrix.system }}
run: | run: |
nix build --no-link --print-out-paths .#prismlauncher \ nix build --print-build-logs .#prismlauncher
| tee -a "$GITHUB_STEP_SUMMARY" \
| xargs cachix pin prismlauncher "$TAG"-"$SYSTEM"

View File

@ -8,6 +8,28 @@ permissions:
contents: read contents: read
jobs: jobs:
flakehub:
name: FlakeHub
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- name: Install Nix
uses: cachix/install-nix-action@v30
- name: Publish on FlakeHub
uses: determinatesystems/flakehub-push@v5
with:
visibility: "public"
winget: winget:
name: Winget name: Winget

View File

@ -1,29 +0,0 @@
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@v9
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

42
.github/workflows/trigger_builds.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name: Build Application
on:
push:
branches-ignore:
- "renovate/**"
paths-ignore:
- "**.md"
- "**/LICENSE"
- "flake.lock"
- "packages/**"
- ".github/ISSUE_TEMPLATE/**"
- ".markdownlint**"
pull_request:
paths-ignore:
- "**.md"
- "**/LICENSE"
- "flake.lock"
- "packages/**"
- ".github/ISSUE_TEMPLATE/**"
- ".markdownlint**"
workflow_dispatch:
jobs:
build_debug:
name: Build Debug
uses: ./.github/workflows/build.yml
with:
build_type: Debug
is_qt_cached: true
secrets:
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
APPLE_CODESIGN_CERT: ${{ secrets.APPLE_CODESIGN_CERT }}
APPLE_CODESIGN_PASSWORD: ${{ secrets.APPLE_CODESIGN_PASSWORD }}
APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }}
APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}

View File

@ -10,8 +10,20 @@ jobs:
name: Build Release name: Build Release
uses: ./.github/workflows/build.yml uses: ./.github/workflows/build.yml
with: with:
build-type: Release build_type: Release
secrets: inherit is_qt_cached: false
secrets:
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
APPLE_CODESIGN_CERT: ${{ secrets.APPLE_CODESIGN_CERT }}
APPLE_CODESIGN_PASSWORD: ${{ secrets.APPLE_CODESIGN_PASSWORD }}
APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }}
APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
create_release: create_release:
needs: build_release needs: build_release
@ -34,8 +46,10 @@ jobs:
run: | run: |
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }} mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
mv PrismLauncher-Linux-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage
mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
mv PrismLauncher-macOS-Legacy*/PrismLauncher.zip PrismLauncher-macOS-Legacy-${{ env.VERSION }}.zip
mv PrismLauncher-macOS*/PrismLauncher.zip PrismLauncher-macOS-${{ env.VERSION }}.zip mv PrismLauncher-macOS*/PrismLauncher.zip PrismLauncher-macOS-${{ env.VERSION }}.zip
tar --exclude='.git' -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }} tar --exclude='.git' -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }}
@ -76,15 +90,13 @@ jobs:
draft: true draft: true
prerelease: false prerelease: false
files: | files: |
PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-x86_64.AppImage PrismLauncher-Linux-x86_64.AppImage
PrismLauncher-Linux-x86_64.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe PrismLauncher-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-MinGW-arm64-${{ env.VERSION }}.zip
PrismLauncher-Windows-MinGW-arm64-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MinGW-arm64-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.zip PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-arm64-Portable-${{ env.VERSION }}.zip PrismLauncher-Windows-MSVC-arm64-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-arm64-Setup-${{ env.VERSION }}.exe PrismLauncher-Windows-MSVC-arm64-Setup-${{ env.VERSION }}.exe
@ -92,4 +104,5 @@ jobs:
PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-Setup-${{ env.VERSION }}.exe PrismLauncher-Windows-MSVC-Setup-${{ env.VERSION }}.exe
PrismLauncher-macOS-${{ env.VERSION }}.zip PrismLauncher-macOS-${{ env.VERSION }}.zip
PrismLauncher-macOS-Legacy-${{ env.VERSION }}.zip
PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }}.tar.gz

View File

@ -17,7 +17,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31 - uses: cachix/install-nix-action@08dcb3a5e62fa31e2da3d490afc4176ef55ecd72 # v30
- uses: DeterminateSystems/update-flake-lock@v24 - uses: DeterminateSystems/update-flake-lock@v24
with: with:

2
.gitignore vendored
View File

@ -14,7 +14,6 @@ CMakeLists.txt.user.*
CMakeSettings.json CMakeSettings.json
/CMakeFiles /CMakeFiles
CMakeCache.txt CMakeCache.txt
CMakeUserPresets.json
/.project /.project
/.settings /.settings
/.idea /.idea
@ -22,7 +21,6 @@ CMakeUserPresets.json
/.vs /.vs
cmake-build-*/ cmake-build-*/
Debug Debug
compile_commands.json
# Build dirs # Build dirs
build build

6
.gitmodules vendored
View File

@ -4,6 +4,9 @@
[submodule "libraries/tomlplusplus"] [submodule "libraries/tomlplusplus"]
path = libraries/tomlplusplus path = libraries/tomlplusplus
url = https://github.com/marzer/tomlplusplus.git url = https://github.com/marzer/tomlplusplus.git
[submodule "libraries/filesystem"]
path = libraries/filesystem
url = https://github.com/gulrak/filesystem
[submodule "libraries/libnbtplusplus"] [submodule "libraries/libnbtplusplus"]
path = libraries/libnbtplusplus path = libraries/libnbtplusplus
url = https://github.com/PrismLauncher/libnbtplusplus.git url = https://github.com/PrismLauncher/libnbtplusplus.git
@ -19,6 +22,3 @@
[submodule "flatpak/shared-modules"] [submodule "flatpak/shared-modules"]
path = flatpak/shared-modules path = flatpak/shared-modules
url = https://github.com/flathub/shared-modules.git url = https://github.com/flathub/shared-modules.git
[submodule "libraries/qt-qrcodegenerator/QR-Code-generator"]
path = libraries/qt-qrcodegenerator/QR-Code-generator
url = https://github.com/nayuki/QR-Code-generator

View File

@ -88,8 +88,10 @@ else()
endif() endif()
endif() endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_WARN_DEPRECATED_UP_TO=0x060200") # Fix build with Qt 5.13
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_UP_TO=0x060000") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_BEFORE=0x050C00")
# Fix aarch64 build for toml++ # Fix aarch64 build for toml++
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0")
@ -97,12 +99,6 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0")
# set CXXFLAGS for build targets # set CXXFLAGS for build targets
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}") set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
# 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 STREQUAL "Ninja" AND CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
endif()
option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF) option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF)
# If this is a Debug build turn on address sanitiser # If this is a Debug build turn on address sanitiser
@ -191,13 +187,12 @@ set(Launcher_LOGIN_CALLBACK_URL "https://prismlauncher.org/successful-login" CAC
set(Launcher_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE STRING "URL for FML Libraries.") set(Launcher_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE STRING "URL for FML Libraries.")
######## Set version numbers ######## ######## Set version numbers ########
set(Launcher_VERSION_MAJOR 10) set(Launcher_VERSION_MAJOR 9)
set(Launcher_VERSION_MINOR 0) set(Launcher_VERSION_MINOR 3)
set(Launcher_VERSION_PATCH 0)
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}") set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}.0") set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},${Launcher_VERSION_PATCH},0") set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0")
# Build platform. # Build platform.
set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.") set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
@ -308,11 +303,23 @@ endif()
# Find the required Qt parts # Find the required Qt parts
include(QtVersionlessBackport) include(QtVersionlessBackport)
if(Launcher_QT_VERSION_MAJOR EQUAL 6) if(Launcher_QT_VERSION_MAJOR EQUAL 5)
set(QT_VERSION_MAJOR 5)
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml NetworkAuth)
if(NOT Launcher_FORCE_BUNDLED_LIBS)
find_package(QuaZip-Qt5 1.3 QUIET)
endif()
if (NOT QuaZip-Qt5_FOUND)
set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE)
set(FORCE_BUNDLED_QUAZIP 1)
endif()
# Qt 6 sets these by default. Notably causes Windows APIs to use UNICODE strings.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNICODE -D_UNICODE")
elseif(Launcher_QT_VERSION_MAJOR EQUAL 6)
set(QT_VERSION_MAJOR 6) set(QT_VERSION_MAJOR 6)
find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat NetworkAuth OpenGL) find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat NetworkAuth)
find_package(Qt6 COMPONENTS DBus)
list(APPEND Launcher_QT_DBUS Qt6::DBus)
list(APPEND Launcher_QT_LIBS Qt6::Core5Compat) list(APPEND Launcher_QT_LIBS Qt6::Core5Compat)
if(NOT Launcher_FORCE_BUNDLED_LIBS) if(NOT Launcher_FORCE_BUNDLED_LIBS)
@ -326,16 +333,29 @@ else()
message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported") message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported")
endif() endif()
if(Launcher_QT_VERSION_MAJOR EQUAL 6) if(Launcher_QT_VERSION_MAJOR EQUAL 5)
include(ECMQueryQt)
ecm_query_qt(QT_PLUGINS_DIR QT_INSTALL_PLUGINS)
ecm_query_qt(QT_LIBS_DIR QT_INSTALL_LIBS)
ecm_query_qt(QT_LIBEXECS_DIR QT_INSTALL_LIBEXECS)
else()
set(QT_PLUGINS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_PLUGINS}) set(QT_PLUGINS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_PLUGINS})
set(QT_LIBS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBS}) set(QT_LIBS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBS})
set(QT_LIBEXECS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBEXECS}) set(QT_LIBEXECS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBEXECS})
endif() endif()
# NOTE: Qt 6 already sets this by default
if (Qt5_POSITION_INDEPENDENT_CODE)
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
if(NOT Launcher_FORCE_BUNDLED_LIBS) if(NOT Launcher_FORCE_BUNDLED_LIBS)
# Find toml++ # Find toml++
find_package(tomlplusplus 3.2.0 QUIET) find_package(tomlplusplus 3.2.0 QUIET)
# Find ghc_filesystem
find_package(ghc_filesystem QUIET)
# Find cmark # Find cmark
find_package(cmark QUIET) find_package(cmark QUIET)
endif() endif()
@ -475,7 +495,6 @@ add_subdirectory(libraries/libnbtplusplus)
add_subdirectory(libraries/systeminfo) # system information library add_subdirectory(libraries/systeminfo) # system information library
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
add_subdirectory(libraries/javacheck) # java compatibility checker add_subdirectory(libraries/javacheck) # java compatibility checker
add_subdirectory(libraries/qt-qrcodegenerator) # qr code generator
if(FORCE_BUNDLED_ZLIB) if(FORCE_BUNDLED_ZLIB)
message(STATUS "Using bundled zlib") message(STATUS "Using bundled zlib")
@ -531,6 +550,12 @@ else()
endif() endif()
add_subdirectory(libraries/gamemode) add_subdirectory(libraries/gamemode)
add_subdirectory(libraries/murmur2) # Hash for usage with the CurseForge API add_subdirectory(libraries/murmur2) # Hash for usage with the CurseForge API
if (NOT ghc_filesystem_FOUND)
message(STATUS "Using bundled ghc_filesystem")
add_subdirectory(libraries/filesystem) # Implementation of std::filesystem for old C++, for usage in old macOS
else()
message(STATUS "Using system ghc_filesystem")
endif()
add_subdirectory(libraries/qdcss) # css parser add_subdirectory(libraries/qdcss) # css parser
############################### Built Artifacts ############################### ############################### Built Artifacts ###############################

View File

@ -1,14 +0,0 @@
{
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
"version": 8,
"cmakeMinimumRequired": {
"major": 3,
"minor": 28
},
"include": [
"cmake/linuxPreset.json",
"cmake/macosPreset.json",
"cmake/windowsMinGWPreset.json",
"cmake/windowsMSVCPreset.json"
]
}

View File

@ -2,59 +2,16 @@
## Code formatting ## Code formatting
All files are formatted with `clang-format` using the configuration in `.clang-format`. Ensure it is run on changed files before committing! Try to follow the existing formatting.
If there is no existing formatting, you may use `clang-format` with our included `.clang-format` configuration.
Please also follow the project's conventions for C++: In general, in order of importance:
- Class and type names should be formatted as `PascalCase`: `MyClass`. - Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
- Private or protected class data members should be formatted as `camelCase` prefixed with `m_`: `m_myCounter`. - Prefer readability over dogma.
- Private or protected `static` class data members should be formatted as `camelCase` prefixed with `s_`: `s_instance`. - Keep to the existing formatting.
- Public class data members should be formatted as `camelCase` without the prefix: `dateOfBirth`. - Indent with 4 space unless it's in a submodule.
- Public, private or protected `static const` class data members should be formatted as `SCREAMING_SNAKE_CASE`: `MAX_VALUE`. - Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
- Class function members should be formatted as `camelCase` without a prefix: `incrementCounter`.
- Global functions and non-`const` global variables should be formatted as `camelCase` without a prefix: `globalData`.
- `const` global variables, macros, and enum constants should be formatted as `SCREAMING_SNAKE_CASE`: `LIGHT_GRAY`.
- Avoid inventing acronyms or abbreviations especially for a name of multiple words - like `tp` for `texturePack`.
Most of these rules are included in the `.clang-tidy` file, so you can run `clang-tidy` to check for any violations.
Here is what these conventions with the formatting configuration look like:
```c++
#define AWESOMENESS 10
constexpr double PI = 3.14159;
enum class PizzaToppings { HAM_AND_PINEAPPLE, OREO_AND_KETCHUP };
struct Person {
QString name;
QDateTime dateOfBirth;
long daysOld() const { return dateOfBirth.daysTo(QDateTime::currentDateTime()); }
};
class ImportantClass {
public:
void incrementCounter()
{
if (m_counter + 1 > MAX_COUNTER_VALUE)
throw std::runtime_error("Counter has reached limit!");
++m_counter;
}
int counter() const { return m_counter; }
private:
static constexpr int MAX_COUNTER_VALUE = 100;
int m_counter;
};
ImportantClass importantClassInstance;
```
If you see any names which do not follow these conventions, it is preferred that you leave them be - renames increase the number of changes therefore make reviewing harder and make your PR more prone to conflicts. However, if you're refactoring a whole class anyway, it's fine.
## Signing your work ## Signing your work

View File

@ -108,7 +108,7 @@
Information on third party licenses used in MinGW-w64 can be found in its COPYING.MinGW-w64-runtime.txt. Information on third party licenses used in MinGW-w64 can be found in its COPYING.MinGW-w64-runtime.txt.
## Qt 6 ## Qt 5/6
Copyright (C) 2022 The Qt Company Ltd and other contributors. Copyright (C) 2022 The Qt Company Ltd and other contributors.
Contact: https://www.qt.io/licensing Contact: https://www.qt.io/licensing
@ -362,6 +362,28 @@
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. POSSIBILITY OF SUCH DAMAGE.
## gulrak/filesystem
Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
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.
## Breeze icons ## Breeze icons
Copyright (C) 2014 Uri Herrera <uri_herrera@nitrux.in> and others Copyright (C) 2014 Uri Herrera <uri_herrera@nitrux.in> and others
@ -403,12 +425,3 @@
You should have received a copy of the GNU Lesser General Public You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>. License along with this library. If not, see <http://www.gnu.org/licenses/>.
## qt-qrcodegenerator (`libraries/qt-qrcodegenerator`)
Copyright © 2024 Project Nayuki. (MIT License)
https://www.nayuki.io/page/qr-code-generator-library
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.

View File

@ -34,8 +34,8 @@
*/ */
#include <qstringliteral.h> #include <qstringliteral.h>
#include <QObject>
#include "BuildConfig.h" #include "BuildConfig.h"
#include <QObject>
const Config BuildConfig; const Config BuildConfig;
@ -49,7 +49,7 @@ Config::Config()
LAUNCHER_DOMAIN = "@Launcher_Domain@"; LAUNCHER_DOMAIN = "@Launcher_Domain@";
LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@"; LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@";
LAUNCHER_GIT = "@Launcher_Git@"; LAUNCHER_GIT = "@Launcher_Git@";
LAUNCHER_APPID = "@Launcher_AppID@"; LAUNCHER_DESKTOPFILENAME = "@Launcher_DesktopFileName@";
LAUNCHER_SVGFILENAME = "@Launcher_SVGFileName@"; LAUNCHER_SVGFILENAME = "@Launcher_SVGFileName@";
USER_AGENT = "@Launcher_UserAgent@"; USER_AGENT = "@Launcher_UserAgent@";
@ -58,7 +58,6 @@ Config::Config()
// Version information // Version information
VERSION_MAJOR = @Launcher_VERSION_MAJOR@; VERSION_MAJOR = @Launcher_VERSION_MAJOR@;
VERSION_MINOR = @Launcher_VERSION_MINOR@; VERSION_MINOR = @Launcher_VERSION_MINOR@;
VERSION_PATCH = @Launcher_VERSION_PATCH@;
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@"; BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@"; BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@";
@ -75,7 +74,8 @@ Config::Config()
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@"; MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@"; MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty()) { if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
{
UPDATER_ENABLED = true; UPDATER_ENABLED = true;
} else if(!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_ARTIFACT.isEmpty()) { } else if(!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_ARTIFACT.isEmpty()) {
UPDATER_ENABLED = true; UPDATER_ENABLED = true;
@ -89,19 +89,27 @@ Config::Config()
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@"; GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
// Assume that builds outside of Git repos are "stable" // Assume that builds outside of Git repos are "stable"
if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND") || GIT_TAG == QStringLiteral("GITDIR-NOTFOUND") || if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND")
GIT_REFSPEC == QStringLiteral("") || GIT_TAG == QStringLiteral("GIT-NOTFOUND")) { || GIT_TAG == QStringLiteral("GITDIR-NOTFOUND")
|| GIT_REFSPEC == QStringLiteral("")
|| GIT_TAG == QStringLiteral("GIT-NOTFOUND"))
{
GIT_REFSPEC = "refs/heads/stable"; GIT_REFSPEC = "refs/heads/stable";
GIT_TAG = versionString(); GIT_TAG = versionString();
GIT_COMMIT = ""; GIT_COMMIT = "";
} }
if (GIT_REFSPEC.startsWith("refs/heads/")) { if (GIT_REFSPEC.startsWith("refs/heads/"))
{
VERSION_CHANNEL = GIT_REFSPEC; VERSION_CHANNEL = GIT_REFSPEC;
VERSION_CHANNEL.remove("refs/heads/"); VERSION_CHANNEL.remove("refs/heads/");
} else if (!GIT_COMMIT.isEmpty()) { }
else if (!GIT_COMMIT.isEmpty())
{
VERSION_CHANNEL = GIT_COMMIT.mid(0, 8); VERSION_CHANNEL = GIT_COMMIT.mid(0, 8);
} else { }
else
{
VERSION_CHANNEL = "unknown"; VERSION_CHANNEL = "unknown";
} }
@ -128,7 +136,7 @@ Config::Config()
QString Config::versionString() const QString Config::versionString() const
{ {
return QString("%1.%2.%3").arg(VERSION_MAJOR).arg(VERSION_MINOR).arg(VERSION_PATCH); return QString("%1.%2").arg(VERSION_MAJOR).arg(VERSION_MINOR);
} }
QString Config::printableVersionString() const QString Config::printableVersionString() const
@ -136,7 +144,8 @@ QString Config::printableVersionString() const
QString vstr = versionString(); QString vstr = versionString();
// If the build is not a main release, append the channel // If the build is not a main release, append the channel
if (VERSION_CHANNEL != "stable" && GIT_TAG != vstr) { if(VERSION_CHANNEL != "stable" && GIT_TAG != vstr)
{
vstr += "-" + VERSION_CHANNEL; vstr += "-" + VERSION_CHANNEL;
} }
return vstr; return vstr;
@ -153,3 +162,4 @@ QString Config::systemID() const
{ {
return QStringLiteral("%1 %2 %3").arg(COMPILER_TARGET_SYSTEM, COMPILER_TARGET_SYSTEM_VERSION, COMPILER_TARGET_SYSTEM_PROCESSOR); return QStringLiteral("%1 %2 %3").arg(COMPILER_TARGET_SYSTEM, COMPILER_TARGET_SYSTEM_VERSION, COMPILER_TARGET_SYSTEM_PROCESSOR);
} }

View File

@ -52,15 +52,13 @@ class Config {
QString LAUNCHER_DOMAIN; QString LAUNCHER_DOMAIN;
QString LAUNCHER_CONFIGFILE; QString LAUNCHER_CONFIGFILE;
QString LAUNCHER_GIT; QString LAUNCHER_GIT;
QString LAUNCHER_APPID; QString LAUNCHER_DESKTOPFILENAME;
QString LAUNCHER_SVGFILENAME; QString LAUNCHER_SVGFILENAME;
/// The major version number. /// The major version number.
int VERSION_MAJOR; int VERSION_MAJOR;
/// The minor version number. /// The minor version number.
int VERSION_MINOR; int VERSION_MINOR;
/// The patch version number.
int VERSION_PATCH;
/** /**
* The version channel * The version channel

View File

@ -1,81 +0,0 @@
{
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
"version": 8,
"configurePresets": [
{
"name": "base",
"hidden": true,
"binaryDir": "build",
"installDir": "install",
"cacheVariables": {
"Launcher_BUILD_PLATFORM": "custom"
}
},
{
"name": "base_debug",
"hidden": true,
"inherits": [
"base"
],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "base_release",
"hidden": true,
"inherits": [
"base"
],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"ENABLE_LTO": "ON"
}
},
{
"name": "base_ci",
"hidden": true,
"inherits": [
"base_release"
],
"cacheVariables": {
"Launcher_BUILD_PLATFORM": "official",
"Launcher_FORCE_BUNDLED_LIBS": "ON"
}
}
],
"testPresets": [
{
"name": "base",
"hidden": true,
"output": {
"outputOnFailure": true
},
"execution": {
"noTestsAction": "error"
},
"filter": {
"exclude": {
"name": "^example64|example$"
}
}
},
{
"name": "base_debug",
"hidden": true,
"inherits": [
"base"
],
"output": {
"debug": true
}
},
{
"name": "base_release",
"hidden": true,
"inherits": [
"base"
]
}
]
}

View File

@ -1,180 +0,0 @@
{
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
"version": 8,
"include": [
"commonPresets.json"
],
"configurePresets": [
{
"name": "linux_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
},
"generator": "Ninja",
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "Linux-Qt6",
"Launcher_ENABLE_JAVA_DOWNLOADER": "ON"
}
},
{
"name": "linux_debug",
"inherits": [
"base_debug",
"linux_base"
],
"displayName": "Linux (Debug)"
},
{
"name": "linux_release",
"inherits": [
"base_release",
"linux_base"
],
"displayName": "Linux (Release)"
},
{
"name": "linux_ci",
"inherits": [
"base_ci",
"linux_base"
],
"displayName": "Linux (CI)",
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "Linux-Qt6"
},
"installDir": "/usr"
}
],
"buildPresets": [
{
"name": "linux_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
}
},
{
"name": "linux_debug",
"inherits": [
"linux_base"
],
"displayName": "Linux (Debug)",
"configurePreset": "linux_debug"
},
{
"name": "linux_release",
"inherits": [
"linux_base"
],
"displayName": "Linux (Release)",
"configurePreset": "linux_release"
},
{
"name": "linux_ci",
"inherits": [
"linux_base"
],
"displayName": "Linux (CI)",
"configurePreset": "linux_ci"
}
],
"testPresets": [
{
"name": "linux_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
}
},
{
"name": "linux_debug",
"inherits": [
"base_debug",
"linux_base"
],
"displayName": "Linux (Debug)",
"configurePreset": "linux_debug"
},
{
"name": "linux_release",
"inherits": [
"base_release",
"linux_base"
],
"displayName": "Linux (Release)",
"configurePreset": "linux_release"
},
{
"name": "linux_ci",
"inherits": [
"base_release",
"linux_base"
],
"displayName": "Linux (CI)",
"configurePreset": "linux_ci"
}
],
"workflowPresets": [
{
"name": "linux_debug",
"displayName": "Linux (Debug)",
"steps": [
{
"type": "configure",
"name": "linux_debug"
},
{
"type": "build",
"name": "linux_debug"
},
{
"type": "test",
"name": "linux_debug"
}
]
},
{
"name": "linux",
"displayName": "Linux (Release)",
"steps": [
{
"type": "configure",
"name": "linux_release"
},
{
"type": "build",
"name": "linux_release"
},
{
"type": "test",
"name": "linux_release"
}
]
},
{
"name": "linux_ci",
"displayName": "Linux (CI)",
"steps": [
{
"type": "configure",
"name": "linux_ci"
},
{
"type": "build",
"name": "linux_ci"
},
{
"type": "test",
"name": "linux_ci"
}
]
}
]
}

View File

@ -1,272 +0,0 @@
{
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
"version": 8,
"include": [
"commonPresets.json"
],
"configurePresets": [
{
"name": "macos_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Darwin"
},
"generator": "Ninja"
},
{
"name": "macos_universal_base",
"hidden": true,
"inherits": [
"macos_base"
],
"cacheVariables": {
"CMAKE_OSX_ARCHITECTURES": "x86_64;arm64",
"Launcher_BUILD_ARTIFACT": "macOS-Qt6"
}
},
{
"name": "macos_debug",
"inherits": [
"base_debug",
"macos_base"
],
"displayName": "macOS (Debug)"
},
{
"name": "macos_release",
"inherits": [
"base_release",
"macos_base"
],
"displayName": "macOS (Release)"
},
{
"name": "macos_universal_debug",
"inherits": [
"base_debug",
"macos_universal_base"
],
"displayName": "macOS (Universal Binary, Debug)"
},
{
"name": "macos_universal_release",
"inherits": [
"base_release",
"macos_universal_base"
],
"displayName": "macOS (Universal Binary, Release)"
},
{
"name": "macos_ci",
"inherits": [
"base_ci",
"macos_universal_base"
],
"displayName": "macOS (CI)",
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "macOS-Qt6"
}
}
],
"buildPresets": [
{
"name": "macos_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Darwin"
}
},
{
"name": "macos_debug",
"inherits": [
"macos_base"
],
"displayName": "macOS (Debug)",
"configurePreset": "macos_debug"
},
{
"name": "macos_release",
"inherits": [
"macos_base"
],
"displayName": "macOS (Release)",
"configurePreset": "macos_release"
},
{
"name": "macos_universal_debug",
"inherits": [
"macos_base"
],
"displayName": "macOS (Universal Binary, Debug)",
"configurePreset": "macos_universal_debug"
},
{
"name": "macos_universal_release",
"inherits": [
"macos_base"
],
"displayName": "macOS (Universal Binary, Release)",
"configurePreset": "macos_universal_release"
},
{
"name": "macos_ci",
"inherits": [
"macos_base"
],
"displayName": "macOS (CI)",
"configurePreset": "macos_ci"
}
],
"testPresets": [
{
"name": "macos_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Darwin"
}
},
{
"name": "macos_debug",
"inherits": [
"base_debug",
"macos_base"
],
"displayName": "MacOS (Debug)",
"configurePreset": "macos_debug"
},
{
"name": "macos_release",
"inherits": [
"base_release",
"macos_base"
],
"displayName": "macOS (Release)",
"configurePreset": "macos_release"
},
{
"name": "macos_universal_debug",
"inherits": [
"base_debug",
"macos_base"
],
"displayName": "MacOS (Universal Binary, Debug)",
"configurePreset": "macos_universal_debug"
},
{
"name": "macos_universal_release",
"inherits": [
"base_release",
"macos_base"
],
"displayName": "macOS (Universal Binary, Release)",
"configurePreset": "macos_universal_release"
},
{
"name": "macos_ci",
"inherits": [
"base_release",
"macos_base"
],
"displayName": "macOS (CI)",
"configurePreset": "macos_ci"
}
],
"workflowPresets": [
{
"name": "macos_debug",
"displayName": "macOS (Debug)",
"steps": [
{
"type": "configure",
"name": "macos_debug"
},
{
"type": "build",
"name": "macos_debug"
},
{
"type": "test",
"name": "macos_debug"
}
]
},
{
"name": "macos",
"displayName": "macOS (Release)",
"steps": [
{
"type": "configure",
"name": "macos_release"
},
{
"type": "build",
"name": "macos_release"
},
{
"type": "test",
"name": "macos_release"
}
]
},
{
"name": "macos_universal_debug",
"displayName": "macOS (Universal Binary, Debug)",
"steps": [
{
"type": "configure",
"name": "macos_universal_debug"
},
{
"type": "build",
"name": "macos_universal_debug"
},
{
"type": "test",
"name": "macos_universal_debug"
}
]
},
{
"name": "macos_universal",
"displayName": "macOS (Universal Binary, Release)",
"steps": [
{
"type": "configure",
"name": "macos_universal_release"
},
{
"type": "build",
"name": "macos_universal_release"
},
{
"type": "test",
"name": "macos_universal_release"
}
]
},
{
"name": "macos_ci",
"displayName": "macOS (CI)",
"steps": [
{
"type": "configure",
"name": "macos_ci"
},
{
"type": "build",
"name": "macos_ci"
},
{
"type": "test",
"name": "macos_ci"
}
]
}
]
}

View File

@ -1,311 +0,0 @@
{
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
"version": 8,
"include": [
"commonPresets.json"
],
"configurePresets": [
{
"name": "windows_msvc_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "Windows-MSVC-Qt6"
}
},
{
"name": "windows_msvc_arm64_cross_base",
"hidden": true,
"inherits": [
"windows_msvc_base"
],
"architecture": "arm64",
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "Windows-MSVC-arm64-Qt6"
}
},
{
"name": "windows_msvc_debug",
"inherits": [
"base_debug",
"windows_msvc_base"
],
"displayName": "Windows MSVC (Debug)",
"generator": "Ninja"
},
{
"name": "windows_msvc_release",
"inherits": [
"base_release",
"windows_msvc_base"
],
"displayName": "Windows MSVC (Release)"
},
{
"name": "windows_msvc_arm64_cross_debug",
"inherits": [
"base_debug",
"windows_msvc_arm64_cross_base"
],
"displayName": "Windows MSVC (ARM64 cross, Debug)"
},
{
"name": "windows_msvc_arm64_cross_release",
"inherits": [
"base_release",
"windows_msvc_arm64_cross_base"
],
"displayName": "Windows MSVC (ARM64 cross, Release)"
},
{
"name": "windows_msvc_ci",
"inherits": [
"base_ci",
"windows_msvc_base"
],
"displayName": "Windows MSVC (CI)",
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "Windows-MSVC-Qt6"
}
},
{
"name": "windows_msvc_arm64_cross_ci",
"inherits": [
"base_ci",
"windows_msvc_arm64_cross_base"
],
"displayName": "Windows MSVC (ARM64 cross, CI)",
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "Windows-MSVC-arm64-Qt6"
}
}
],
"buildPresets": [
{
"name": "windows_msvc_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "windows_msvc_debug",
"inherits": [
"windows_msvc_base"
],
"displayName": "Windows MSVC (Debug)",
"configurePreset": "windows_msvc_debug",
"configuration": "Debug"
},
{
"name": "windows_msvc_release",
"inherits": [
"windows_msvc_base"
],
"displayName": "Windows MSVC (Release)",
"configurePreset": "windows_msvc_release",
"configuration": "Release",
"nativeToolOptions": [
"/p:UseMultiToolTask=true",
"/p:EnforceProcessCountAcrossBuilds=true"
]
},
{
"name": "windows_msvc_arm64_cross_debug",
"inherits": [
"windows_msvc_base"
],
"displayName": "Windows MSVC (ARM64 cross, Debug)",
"configurePreset": "windows_msvc_arm64_cross_debug",
"configuration": "Debug",
"nativeToolOptions": [
"/p:UseMultiToolTask=true",
"/p:EnforceProcessCountAcrossBuilds=true"
]
},
{
"name": "windows_msvc_arm64_cross_release",
"inherits": [
"windows_msvc_base"
],
"displayName": "Windows MSVC (ARM64 cross, Release)",
"configurePreset": "windows_msvc_arm64_cross_release",
"configuration": "Release",
"nativeToolOptions": [
"/p:UseMultiToolTask=true",
"/p:EnforceProcessCountAcrossBuilds=true"
]
},
{
"name": "windows_msvc_ci",
"inherits": [
"windows_msvc_base"
],
"displayName": "Windows MSVC (CI)",
"configurePreset": "windows_msvc_ci",
"configuration": "Release",
"nativeToolOptions": [
"/p:UseMultiToolTask=true",
"/p:EnforceProcessCountAcrossBuilds=true"
]
},
{
"name": "windows_msvc_arm64_cross_ci",
"inherits": [
"windows_msvc_base"
],
"displayName": "Windows MSVC (ARM64 cross, CI)",
"configurePreset": "windows_msvc_arm64_cross_ci",
"configuration": "Release",
"nativeToolOptions": [
"/p:UseMultiToolTask=true",
"/p:EnforceProcessCountAcrossBuilds=true"
]
}
],
"testPresets": [
{
"name": "windows_msvc_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "windows_msvc_debug",
"inherits": [
"base_debug",
"windows_msvc_base"
],
"displayName": "Windows MSVC (Debug)",
"configurePreset": "windows_msvc_debug",
"configuration": "Debug"
},
{
"name": "windows_msvc_release",
"inherits": [
"base_release",
"windows_msvc_base"
],
"displayName": "Windows MSVC (Release)",
"configurePreset": "windows_msvc_release",
"configuration": "Release"
},
{
"name": "windows_msvc_ci",
"inherits": [
"base_release",
"windows_msvc_base"
],
"displayName": "Windows MSVC (CI)",
"configurePreset": "windows_msvc_ci",
"configuration": "Release"
}
],
"workflowPresets": [
{
"name": "windows_msvc_debug",
"displayName": "Windows MSVC (Debug)",
"steps": [
{
"type": "configure",
"name": "windows_msvc_debug"
},
{
"type": "build",
"name": "windows_msvc_debug"
},
{
"type": "test",
"name": "windows_msvc_debug"
}
]
},
{
"name": "windows_msvc",
"displayName": "Windows MSVC (Release)",
"steps": [
{
"type": "configure",
"name": "windows_msvc_release"
},
{
"type": "build",
"name": "windows_msvc_release"
},
{
"type": "test",
"name": "windows_msvc_release"
}
]
},
{
"name": "windows_msvc_arm64_cross_debug",
"displayName": "Windows MSVC (ARM64 cross, Debug)",
"steps": [
{
"type": "configure",
"name": "windows_msvc_arm64_cross_debug"
},
{
"type": "build",
"name": "windows_msvc_arm64_cross_debug"
}
]
},
{
"name": "windows_msvc_arm64_cross",
"displayName": "Windows MSVC (ARM64 cross, Release)",
"steps": [
{
"type": "configure",
"name": "windows_msvc_arm64_cross_release"
},
{
"type": "build",
"name": "windows_msvc_arm64_cross_release"
}
]
},
{
"name": "windows_msvc_ci",
"displayName": "Windows MSVC (CI)",
"steps": [
{
"type": "configure",
"name": "windows_msvc_ci"
},
{
"type": "build",
"name": "windows_msvc_ci"
},
{
"type": "test",
"name": "windows_msvc_ci"
}
]
},
{
"name": "windows_msvc_arm64_cross_ci",
"displayName": "Windows MSVC (ARM64 cross, CI)",
"steps": [
{
"type": "configure",
"name": "windows_msvc_arm64_cross_ci"
},
{
"type": "build",
"name": "windows_msvc_arm64_cross_ci"
}
]
}
]
}

View File

@ -1,183 +0,0 @@
{
"$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
"version": 8,
"include": [
"commonPresets.json"
],
"configurePresets": [
{
"name": "windows_mingw_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"generator": "Ninja",
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "Windows-MinGW-w64-Qt6"
}
},
{
"name": "windows_mingw_debug",
"inherits": [
"base_debug",
"windows_mingw_base"
],
"displayName": "Windows MinGW (Debug)"
},
{
"name": "windows_mingw_release",
"inherits": [
"base_release",
"windows_mingw_base"
],
"displayName": "Windows MinGW (Release)"
},
{
"name": "windows_mingw_ci",
"inherits": [
"base_ci",
"windows_mingw_base"
],
"displayName": "Windows MinGW (CI)",
"cacheVariables": {
"Launcher_BUILD_ARTIFACT": "Windows-MinGW-w64-Qt6"
}
}
],
"buildPresets": [
{
"name": "windows_mingw_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "windows_mingw_debug",
"inherits": [
"windows_mingw_base"
],
"displayName": "Windows MinGW (Debug)",
"configurePreset": "windows_mingw_debug"
},
{
"name": "windows_mingw_release",
"inherits": [
"windows_mingw_base"
],
"displayName": "Windows MinGW (Release)",
"configurePreset": "windows_mingw_release"
},
{
"name": "windows_mingw_ci",
"inherits": [
"windows_mingw_base"
],
"displayName": "Windows MinGW (CI)",
"configurePreset": "windows_mingw_ci"
}
],
"testPresets": [
{
"name": "windows_mingw_base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"filter": {
"exclude": {
"name": "^example64|example$"
}
}
},
{
"name": "windows_mingw_debug",
"inherits": [
"base_debug",
"windows_mingw_base"
],
"displayName": "Windows MinGW (Debug)",
"configurePreset": "windows_mingw_debug"
},
{
"name": "windows_mingw_release",
"inherits": [
"base_release",
"windows_mingw_base"
],
"displayName": "Windows MinGW (Release)",
"configurePreset": "windows_mingw_release"
},
{
"name": "windows_mingw_ci",
"inherits": [
"base_release",
"windows_mingw_base"
],
"displayName": "Windows MinGW (CI)",
"configurePreset": "windows_mingw_ci"
}
],
"workflowPresets": [
{
"name": "windows_mingw_debug",
"displayName": "Windows MinGW (Debug)",
"steps": [
{
"type": "configure",
"name": "windows_mingw_debug"
},
{
"type": "build",
"name": "windows_mingw_debug"
},
{
"type": "test",
"name": "windows_mingw_debug"
}
]
},
{
"name": "windows_mingw",
"displayName": "Windows MinGW (Release)",
"steps": [
{
"type": "configure",
"name": "windows_mingw_release"
},
{
"type": "build",
"name": "windows_mingw_release"
},
{
"type": "test",
"name": "windows_mingw_release"
}
]
},
{
"name": "windows_mingw_ci",
"displayName": "Windows MinGW (CI)",
"steps": [
{
"type": "configure",
"name": "windows_mingw_ci"
},
{
"type": "build",
"name": "windows_mingw_ci"
},
{
"type": "test",
"name": "windows_mingw_ci"
}
]
}
]
}

31
flake.lock generated
View File

@ -3,11 +3,11 @@
"libnbtplusplus": { "libnbtplusplus": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1744811532, "lastModified": 1699286814,
"narHash": "sha256-qhmjaRkt+O7A+gu6HjUkl7QzOEb4r8y8vWZMG2R/C6o=", "narHash": "sha256-yy0q+bky80LtK1GWzz7qpM+aAGrOqLuewbid8WT1ilk=",
"owner": "PrismLauncher", "owner": "PrismLauncher",
"repo": "libnbtplusplus", "repo": "libnbtplusplus",
"rev": "531449ba1c930c98e0bcf5d332b237a8566f9d78", "rev": "23b955121b8217c1c348a9ed2483167a6f3ff4ad",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -18,11 +18,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1745526057, "lastModified": 1743095683,
"narHash": "sha256-ITSpPDwvLBZBnPRS2bUcHY3gZSwis/uTe255QgMtTLA=", "narHash": "sha256-gWd4urRoLRe8GLVC/3rYRae1h+xfQzt09xOfb0PaHSk=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "f771eb401a46846c1aebd20552521b233dd7e18b", "rev": "5e5402ecbcb27af32284d4a62553c019a3a49ea6",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -32,27 +32,10 @@
"type": "github" "type": "github"
} }
}, },
"qt-qrcodegenerator": {
"flake": false,
"locked": {
"lastModified": 1737616857,
"narHash": "sha256-6SugPt0lp1Gz7nV23FLmsmpfzgFItkSw7jpGftsDPWc=",
"owner": "nayuki",
"repo": "QR-Code-generator",
"rev": "2c9044de6b049ca25cb3cd1649ed7e27aa055138",
"type": "github"
},
"original": {
"owner": "nayuki",
"repo": "QR-Code-generator",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"libnbtplusplus": "libnbtplusplus", "libnbtplusplus": "libnbtplusplus",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs"
"qt-qrcodegenerator": "qt-qrcodegenerator"
} }
} }
}, },

View File

@ -15,11 +15,6 @@
url = "github:PrismLauncher/libnbtplusplus"; url = "github:PrismLauncher/libnbtplusplus";
flake = false; flake = false;
}; };
qt-qrcodegenerator = {
url = "github:nayuki/QR-Code-generator";
flake = false;
};
}; };
outputs = outputs =
@ -27,7 +22,6 @@
self, self,
nixpkgs, nixpkgs,
libnbtplusplus, libnbtplusplus,
qt-qrcodegenerator,
}: }:
let let
@ -138,8 +132,6 @@
{ {
default = pkgs.mkShell { default = pkgs.mkShell {
name = "prism-launcher";
inputsFrom = [ packages'.prismlauncher-unwrapped ]; inputsFrom = [ packages'.prismlauncher-unwrapped ];
packages = with pkgs; [ packages = with pkgs; [
@ -175,7 +167,6 @@
prismlauncher-unwrapped = prev.callPackage ./nix/unwrapped.nix { prismlauncher-unwrapped = prev.callPackage ./nix/unwrapped.nix {
inherit inherit
libnbtplusplus libnbtplusplus
qt-qrcodegenerator
self self
; ;
}; };

@ -1 +1 @@
Subproject commit 73f08ed2c3187f6648ca04ebef030930a6c9f0be Subproject commit f5d368a31d6ef046eb2955c74ec6f54f32ed5c4e

View File

@ -59,6 +59,8 @@
#include "ui/pages/BasePageProvider.h" #include "ui/pages/BasePageProvider.h"
#include "ui/pages/global/APIPage.h" #include "ui/pages/global/APIPage.h"
#include "ui/pages/global/AccountListPage.h" #include "ui/pages/global/AccountListPage.h"
#include "ui/pages/global/CustomCommandsPage.h"
#include "ui/pages/global/EnvironmentVariablesPage.h"
#include "ui/pages/global/ExternalToolsPage.h" #include "ui/pages/global/ExternalToolsPage.h"
#include "ui/pages/global/JavaPage.h" #include "ui/pages/global/JavaPage.h"
#include "ui/pages/global/LanguagePage.h" #include "ui/pages/global/LanguagePage.h"
@ -96,7 +98,6 @@
#include <QList> #include <QList>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QStringList> #include <QStringList>
#include <QStringLiteral>
#include <QStyleFactory> #include <QStyleFactory>
#include <QTranslator> #include <QTranslator>
#include <QWindow> #include <QWindow>
@ -128,7 +129,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <sys.h> #include <sys.h>
#include <QStringLiteral>
#include "SysInfo.h" #include "SysInfo.h"
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
@ -155,16 +155,11 @@
#endif #endif
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h> #include <windows.h>
#include <QStyleHints> #include <QStyleHints>
#include "console/WindowsConsole.h" #include "WindowsConsole.h"
#endif #endif
#include "console/Console.h"
#define STRINGIFY(x) #x #define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x) #define TOSTRING(x) STRINGIFY(x)
@ -172,63 +167,6 @@ static const QLatin1String liveCheckFile("live.check");
PixmapCache* PixmapCache::s_instance = nullptr; PixmapCache* PixmapCache::s_instance = nullptr;
static bool isANSIColorConsole;
static QString defaultLogFormat = QStringLiteral(
"%{time process}"
" "
"%{if-debug}Debug:%{endif}"
"%{if-info}Info:%{endif}"
"%{if-warning}Warning:%{endif}"
"%{if-critical}Critical:%{endif}"
"%{if-fatal}Fatal:%{endif}"
" "
"%{if-category}[%{category}] %{endif}"
"%{message}"
" "
"(%{function}:%{line})");
#define ansi_reset "\x1b[0m"
#define ansi_bold "\x1b[1m"
#define ansi_reset_bold "\x1b[22m"
#define ansi_faint "\x1b[2m"
#define ansi_italic "\x1b[3m"
#define ansi_red_fg "\x1b[31m"
#define ansi_green_fg "\x1b[32m"
#define ansi_yellow_fg "\x1b[33m"
#define ansi_blue_fg "\x1b[34m"
#define ansi_purple_fg "\x1b[35m"
#define ansi_inverse "\x1b[7m"
// clang-format off
static QString ansiLogFormat = QStringLiteral(
ansi_faint "%{time process}" ansi_reset
" "
"%{if-debug}" ansi_bold ansi_green_fg "D:" ansi_reset "%{endif}"
"%{if-info}" ansi_bold ansi_blue_fg "I:" ansi_reset "%{endif}"
"%{if-warning}" ansi_bold ansi_yellow_fg "W:" ansi_reset_bold "%{endif}"
"%{if-critical}" ansi_bold ansi_red_fg "C:" ansi_reset_bold "%{endif}"
"%{if-fatal}" ansi_bold ansi_inverse ansi_red_fg "F:" ansi_reset_bold "%{endif}"
" "
"%{if-category}" ansi_bold "[%{category}]" ansi_reset_bold " %{endif}"
"%{message}"
" "
ansi_reset ansi_faint "(%{function}:%{line})" ansi_reset
);
// clang-format on
#undef ansi_inverse
#undef ansi_purple_fg
#undef ansi_blue_fg
#undef ansi_yellow_fg
#undef ansi_green_fg
#undef ansi_red_fg
#undef ansi_italic
#undef ansi_faint
#undef ansi_bold
#undef ansi_reset_bold
#undef ansi_reset
namespace { namespace {
/** This is used so that we can output to the log file in addition to the CLI. */ /** This is used so that we can output to the log file in addition to the CLI. */
@ -237,24 +175,11 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt
static std::mutex loggerMutex; static std::mutex loggerMutex;
const std::lock_guard<std::mutex> lock(loggerMutex); // synchronized, QFile logFile is not thread-safe const std::lock_guard<std::mutex> lock(loggerMutex); // synchronized, QFile logFile is not thread-safe
if (isANSIColorConsole) {
// ensure default is set for log file
qSetMessagePattern(defaultLogFormat);
}
QString out = qFormatLogMessage(type, context, msg); QString out = qFormatLogMessage(type, context, msg);
out += QChar::LineFeed; out += QChar::LineFeed;
APPLICATION->logFile->write(out.toUtf8()); APPLICATION->logFile->write(out.toUtf8());
APPLICATION->logFile->flush(); APPLICATION->logFile->flush();
if (isANSIColorConsole) {
// format ansi for console;
qSetMessagePattern(ansiLogFormat);
out = qFormatLogMessage(type, context, msg);
out += QChar::LineFeed;
}
QTextStream(stderr) << out.toLocal8Bit(); QTextStream(stderr) << out.toLocal8Bit();
fflush(stderr); fflush(stderr);
} }
@ -295,24 +220,14 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// attach the parent console if stdout not already captured // attach the parent console if stdout not already captured
if (AttachWindowsConsole()) { if (AttachWindowsConsole()) {
consoleAttached = true; 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 #endif
setOrganizationName(BuildConfig.LAUNCHER_NAME); setOrganizationName(BuildConfig.LAUNCHER_NAME);
setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN); setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN);
setApplicationName(BuildConfig.LAUNCHER_NAME); setApplicationName(BuildConfig.LAUNCHER_NAME);
setApplicationDisplayName(QString("%1 %2").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString())); setApplicationDisplayName(QString("%1 %2").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString()));
setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT); setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT);
setDesktopFileName(BuildConfig.LAUNCHER_APPID); setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME);
m_startTime = QDateTime::currentDateTime(); m_startTime = QDateTime::currentDateTime();
// Don't quit on hiding the last window // Don't quit on hiding the last window
@ -329,7 +244,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" }, { { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
{ { "w", "world" }, "Join the specified world on launch (only valid in combination with --launch)", "world" }, { { "w", "world" }, "Join the specified world on launch (only valid in combination with --launch)", "world" },
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" }, { { "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" }, { "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
{ { "I", "import" }, "Import instance or resource from specified local path or URL", "url" }, { { "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" } }); { "show", "Opens the window for the specified instance (by instance ID)", "show" } });
@ -345,10 +259,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_serverToJoin = parser.value("server"); m_serverToJoin = parser.value("server");
m_worldToJoin = parser.value("world"); m_worldToJoin = parser.value("world");
m_profileToUse = parser.value("profile"); m_profileToUse = parser.value("profile");
if (parser.isSet("offline")) {
m_offline = true;
m_offlineName = parser.value("offline");
}
m_liveCheck = parser.isSet("alive"); m_liveCheck = parser.isSet("alive");
m_instanceIdToShowWindowOf = parser.value("show"); m_instanceIdToShowWindowOf = parser.value("show");
@ -363,9 +273,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
} }
// error if --launch is missing with --server or --profile // error if --launch is missing with --server or --profile
if ((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty() || !m_profileToUse.isEmpty() || m_offline) && if (((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty()) || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) {
m_instanceIdToLaunch.isEmpty()) { std::cerr << "--server and --profile can only be used in combination with --launch!" << std::endl;
std::cerr << "--server, --profile and --offline can only be used in combination with --launch!" << std::endl;
m_status = Application::Failed; m_status = Application::Failed;
return; return;
} }
@ -462,20 +371,19 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_peerInstance = new LocalPeer(this, appID); m_peerInstance = new LocalPeer(this, appID);
connect(m_peerInstance, &LocalPeer::messageReceived, this, &Application::messageReceived); connect(m_peerInstance, &LocalPeer::messageReceived, this, &Application::messageReceived);
if (m_peerInstance->isClient()) { if (m_peerInstance->isClient()) {
bool sentMessage = false;
int timeout = 2000; int timeout = 2000;
if (m_instanceIdToLaunch.isEmpty()) { if (m_instanceIdToLaunch.isEmpty()) {
ApplicationMessage activate; ApplicationMessage activate;
activate.command = "activate"; activate.command = "activate";
sentMessage = m_peerInstance->sendMessage(activate.serialize(), timeout); m_peerInstance->sendMessage(activate.serialize(), timeout);
if (!m_urlsToImport.isEmpty()) { if (!m_urlsToImport.isEmpty()) {
for (auto url : m_urlsToImport) { for (auto url : m_urlsToImport) {
ApplicationMessage import; ApplicationMessage import;
import.command = "import"; import.command = "import";
import.args.insert("url", url.toString()); import.args.insert("url", url.toString());
sentMessage = m_peerInstance->sendMessage(import.serialize(), timeout); m_peerInstance->sendMessage(import.serialize(), timeout);
} }
} }
} else { } else {
@ -491,20 +399,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
if (!m_profileToUse.isEmpty()) { if (!m_profileToUse.isEmpty()) {
launch.args["profile"] = m_profileToUse; launch.args["profile"] = m_profileToUse;
} }
if (m_offline) { m_peerInstance->sendMessage(launch.serialize(), timeout);
launch.args["offline_enabled"] = "true";
launch.args["offline_name"] = m_offlineName;
} }
sentMessage = m_peerInstance->sendMessage(launch.serialize(), timeout);
}
if (sentMessage) {
m_status = Application::Succeeded; m_status = Application::Succeeded;
return; return;
} else {
std::cerr << "Unable to redirect command to already running instance\n";
// C function not Qt function - event loop not started yet
::exit(1);
}
} }
} }
@ -535,14 +433,27 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
return; return;
} }
qInstallMessageHandler(appDebugOutput); qInstallMessageHandler(appDebugOutput);
qSetMessagePattern(defaultLogFormat);
qSetMessagePattern(
"%{time process}"
" "
"%{if-debug}D%{endif}"
"%{if-info}I%{endif}"
"%{if-warning}W%{endif}"
"%{if-critical}C%{endif}"
"%{if-fatal}F%{endif}"
" "
"|"
" "
"%{if-category}[%{category}]: %{endif}"
"%{message}");
bool foundLoggingRules = false; bool foundLoggingRules = false;
auto logRulesFile = QStringLiteral("qtlogging.ini"); auto logRulesFile = QStringLiteral("qtlogging.ini");
auto logRulesPath = FS::PathCombine(dataPath, logRulesFile); auto logRulesPath = FS::PathCombine(dataPath, logRulesFile);
qInfo() << "Testing" << logRulesPath << "..."; qDebug() << "Testing" << logRulesPath << "...";
foundLoggingRules = QFile::exists(logRulesPath); foundLoggingRules = QFile::exists(logRulesPath);
// search the dataPath() // search the dataPath()
@ -550,7 +461,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
if (!foundLoggingRules && !isPortable() && dirParam.isEmpty() && dataDirEnv.isEmpty()) { if (!foundLoggingRules && !isPortable() && dirParam.isEmpty() && dataDirEnv.isEmpty()) {
logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile)); logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile));
if (!logRulesPath.isEmpty()) { if (!logRulesPath.isEmpty()) {
qInfo() << "Found" << logRulesPath << "..."; qDebug() << "Found" << logRulesPath << "...";
foundLoggingRules = true; foundLoggingRules = true;
} }
} }
@ -561,28 +472,28 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
#else #else
logRulesPath = FS::PathCombine(m_rootPath, logRulesFile); logRulesPath = FS::PathCombine(m_rootPath, logRulesFile);
#endif #endif
qInfo() << "Testing" << logRulesPath << "..."; qDebug() << "Testing" << logRulesPath << "...";
foundLoggingRules = QFile::exists(logRulesPath); foundLoggingRules = QFile::exists(logRulesPath);
} }
if (foundLoggingRules) { if (foundLoggingRules) {
// load and set logging rules // load and set logging rules
qInfo() << "Loading logging rules from:" << logRulesPath; qDebug() << "Loading logging rules from:" << logRulesPath;
QSettings loggingRules(logRulesPath, QSettings::IniFormat); QSettings loggingRules(logRulesPath, QSettings::IniFormat);
loggingRules.beginGroup("Rules"); loggingRules.beginGroup("Rules");
QStringList rule_names = loggingRules.childKeys(); QStringList rule_names = loggingRules.childKeys();
QStringList rules; QStringList rules;
qInfo() << "Setting log rules:"; qDebug() << "Setting log rules:";
for (auto rule_name : rule_names) { for (auto rule_name : rule_names) {
auto rule = QString("%1=%2").arg(rule_name).arg(loggingRules.value(rule_name).toString()); auto rule = QString("%1=%2").arg(rule_name).arg(loggingRules.value(rule_name).toString());
rules.append(rule); rules.append(rule);
qInfo() << " " << rule; qDebug() << " " << rule;
} }
auto rules_str = rules.join("\n"); auto rules_str = rules.join("\n");
QLoggingCategory::setFilterRules(rules_str); QLoggingCategory::setFilterRules(rules_str);
} }
qInfo() << "<> Log initialized."; qDebug() << "<> Log initialized.";
} }
{ {
@ -599,33 +510,33 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
} }
{ {
qInfo() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME + ", " + QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME + ", " + QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
qInfo() << "Version : " << BuildConfig.printableVersionString(); qDebug() << "Version : " << BuildConfig.printableVersionString();
qInfo() << "Platform : " << BuildConfig.BUILD_PLATFORM; qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM;
qInfo() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
qInfo() << "Git refspec : " << BuildConfig.GIT_REFSPEC; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC;
qInfo() << "Compiled for : " << BuildConfig.systemID(); qDebug() << "Compiled for : " << BuildConfig.systemID();
qInfo() << "Compiled by : " << BuildConfig.compilerID(); qDebug() << "Compiled by : " << BuildConfig.compilerID();
qInfo() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT; qDebug() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT;
qInfo() << "Updates Enabled : " << (updaterEnabled() ? "Yes" : "No"); qDebug() << "Updates Enabled : " << (updaterEnabled() ? "Yes" : "No");
if (adjustedBy.size()) { if (adjustedBy.size()) {
qInfo() << "Work dir before adjustment : " << origcwdPath; qDebug() << "Work dir before adjustment : " << origcwdPath;
qInfo() << "Work dir after adjustment : " << QDir::currentPath(); qDebug() << "Work dir after adjustment : " << QDir::currentPath();
qInfo() << "Adjusted by : " << adjustedBy; qDebug() << "Adjusted by : " << adjustedBy;
} else { } else {
qInfo() << "Work dir : " << QDir::currentPath(); qDebug() << "Work dir : " << QDir::currentPath();
} }
qInfo() << "Binary path : " << binPath; qDebug() << "Binary path : " << binPath;
qInfo() << "Application root path : " << m_rootPath; qDebug() << "Application root path : " << m_rootPath;
if (!m_instanceIdToLaunch.isEmpty()) { if (!m_instanceIdToLaunch.isEmpty()) {
qInfo() << "ID of instance to launch : " << m_instanceIdToLaunch; qDebug() << "ID of instance to launch : " << m_instanceIdToLaunch;
} }
if (!m_serverToJoin.isEmpty()) { if (!m_serverToJoin.isEmpty()) {
qInfo() << "Address of server to join :" << m_serverToJoin; qDebug() << "Address of server to join :" << m_serverToJoin;
} else if (!m_worldToJoin.isEmpty()) { } else if (!m_worldToJoin.isEmpty()) {
qInfo() << "Name of the world to join :" << m_worldToJoin; qDebug() << "Name of the world to join :" << m_worldToJoin;
} }
qInfo() << "<> Paths set."; qDebug() << "<> Paths set.";
} }
if (m_liveCheck) { if (m_liveCheck) {
@ -696,7 +607,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("IconsDir", "icons"); m_settings->registerSetting("IconsDir", "icons");
m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
m_settings->registerSetting("DownloadsDirWatchRecursive", false); m_settings->registerSetting("DownloadsDirWatchRecursive", false);
m_settings->registerSetting("MoveModsFromDownloadsDir", false);
m_settings->registerSetting("SkinsDir", "skins"); m_settings->registerSetting("SkinsDir", "skins");
m_settings->registerSetting("JavaDir", "java"); m_settings->registerSetting("JavaDir", "java");
@ -790,9 +700,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("ToolbarsLocked", false); m_settings->registerSetting("ToolbarsLocked", false);
// Instance
m_settings->registerSetting("InstSortMode", "Name"); m_settings->registerSetting("InstSortMode", "Name");
m_settings->registerSetting("InstRenamingMode", "AskEverytime");
m_settings->registerSetting("SelectedInstance", QString()); m_settings->registerSetting("SelectedInstance", QString());
// Window state and geometry // Window state and geometry
@ -884,6 +792,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_globalSettingsProvider->addPage<MinecraftPage>(); m_globalSettingsProvider->addPage<MinecraftPage>();
m_globalSettingsProvider->addPage<JavaPage>(); m_globalSettingsProvider->addPage<JavaPage>();
m_globalSettingsProvider->addPage<LanguagePage>(); m_globalSettingsProvider->addPage<LanguagePage>();
m_globalSettingsProvider->addPage<CustomCommandsPage>();
m_globalSettingsProvider->addPage<EnvironmentVariablesPage>();
m_globalSettingsProvider->addPage<ProxyPage>(); m_globalSettingsProvider->addPage<ProxyPage>();
m_globalSettingsProvider->addPage<ExternalToolsPage>(); m_globalSettingsProvider->addPage<ExternalToolsPage>();
m_globalSettingsProvider->addPage<AccountListPage>(); m_globalSettingsProvider->addPage<AccountListPage>();
@ -892,7 +802,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
PixmapCache::setInstance(new PixmapCache(this)); PixmapCache::setInstance(new PixmapCache(this));
qInfo() << "<> Settings loaded."; qDebug() << "<> Settings loaded.";
} }
#ifndef QT_NO_ACCESSIBILITY #ifndef QT_NO_ACCESSIBILITY
@ -908,7 +818,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
QString user = settings()->get("ProxyUser").toString(); QString user = settings()->get("ProxyUser").toString();
QString pass = settings()->get("ProxyPass").toString(); QString pass = settings()->get("ProxyPass").toString();
updateProxySettings(proxyTypeStr, addr, port, user, pass); updateProxySettings(proxyTypeStr, addr, port, user, pass);
qInfo() << "<> Network done."; qDebug() << "<> Network done.";
} }
// load translations // load translations
@ -916,8 +826,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_translations.reset(new TranslationsModel("translations")); m_translations.reset(new TranslationsModel("translations"));
auto bcp47Name = m_settings->get("Language").toString(); auto bcp47Name = m_settings->get("Language").toString();
m_translations->selectLanguage(bcp47Name); m_translations->selectLanguage(bcp47Name);
qInfo() << "Your language is" << bcp47Name; qDebug() << "Your language is" << bcp47Name;
qInfo() << "<> Translations loaded."; qDebug() << "<> Translations loaded.";
} }
// Instance icons // Instance icons
@ -927,8 +837,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
":/icons/multimc/128x128/instances/", ":/icons/multimc/scalable/instances/" }; ":/icons/multimc/128x128/instances/", ":/icons/multimc/scalable/instances/" };
m_icons.reset(new IconList(instFolders, setting->get().toString())); m_icons.reset(new IconList(instFolders, setting->get().toString()));
connect(setting.get(), &Setting::SettingChanged, connect(setting.get(), &Setting::SettingChanged,
[this](const Setting&, QVariant value) { m_icons->directoryChanged(value.toString()); }); [&](const Setting&, QVariant value) { m_icons->directoryChanged(value.toString()); });
qInfo() << "<> Instance icons initialized."; qDebug() << "<> Instance icons initialized.";
} }
// Themes // Themes
@ -940,25 +850,25 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// instance path: check for problems with '!' in instance path and warn the user in the log // instance path: check for problems with '!' in instance path and warn the user in the log
// and remember that we have to show him a dialog when the gui starts (if it does so) // and remember that we have to show him a dialog when the gui starts (if it does so)
QString instDir = InstDirSetting->get().toString(); QString instDir = InstDirSetting->get().toString();
qInfo() << "Instance path : " << instDir; qDebug() << "Instance path : " << instDir;
if (FS::checkProblemticPathJava(QDir(instDir))) { if (FS::checkProblemticPathJava(QDir(instDir))) {
qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!"; qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!";
} }
m_instances.reset(new InstanceList(m_settings, instDir, this)); m_instances.reset(new InstanceList(m_settings, instDir, this));
connect(InstDirSetting.get(), &Setting::SettingChanged, m_instances.get(), &InstanceList::on_InstFolderChanged); connect(InstDirSetting.get(), &Setting::SettingChanged, m_instances.get(), &InstanceList::on_InstFolderChanged);
qInfo() << "Loading Instances..."; qDebug() << "Loading Instances...";
m_instances->loadList(); m_instances->loadList();
qInfo() << "<> Instances loaded."; qDebug() << "<> Instances loaded.";
} }
// and accounts // and accounts
{ {
m_accounts.reset(new AccountList(this)); m_accounts.reset(new AccountList(this));
qInfo() << "Loading accounts..."; qDebug() << "Loading accounts...";
m_accounts->setListFilePath("accounts.json", true); m_accounts->setListFilePath("accounts.json", true);
m_accounts->loadList(); m_accounts->loadList();
m_accounts->fillQueue(); m_accounts->fillQueue();
qInfo() << "<> Accounts loaded."; qDebug() << "<> Accounts loaded.";
} }
// init the http meta cache // init the http meta cache
@ -979,7 +889,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_metacache->addBase("meta", QDir("meta").absolutePath()); m_metacache->addBase("meta", QDir("meta").absolutePath());
m_metacache->addBase("java", QDir("cache/java").absolutePath()); m_metacache->addBase("java", QDir("cache/java").absolutePath());
m_metacache->Load(); m_metacache->Load();
qInfo() << "<> Cache initialized."; qDebug() << "<> Cache initialized.";
} }
// now we have network, download translation updates // now we have network, download translation updates
@ -1162,11 +1072,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
bool Application::createSetupWizard() bool Application::createSetupWizard()
{ {
bool javaRequired = [this]() { bool javaRequired = [&]() {
if (BuildConfig.JAVA_DOWNLOADER_ENABLED && settings()->get("AutomaticJavaDownload").toBool()) { if (BuildConfig.JAVA_DOWNLOADER_ENABLED && m_settings->get("AutomaticJavaDownload").toBool()) {
return false; return false;
} }
bool ignoreJavaWizard = settings()->get("IgnoreJavaWizard").toBool(); bool ignoreJavaWizard = m_settings->get("IgnoreJavaWizard").toBool();
if (ignoreJavaWizard) { if (ignoreJavaWizard) {
return false; return false;
} }
@ -1180,8 +1090,8 @@ bool Application::createSetupWizard()
QString actualPath = FS::ResolveExecutable(currentJavaPath); QString actualPath = FS::ResolveExecutable(currentJavaPath);
return actualPath.isNull(); return actualPath.isNull();
}(); }();
bool askjava = BuildConfig.JAVA_DOWNLOADER_ENABLED && !javaRequired && !settings()->get("AutomaticJavaDownload").toBool() && bool askjava = BuildConfig.JAVA_DOWNLOADER_ENABLED && !javaRequired && !m_settings->get("AutomaticJavaDownload").toBool() &&
!settings()->get("AutomaticJavaSwitch").toBool() && !settings()->get("UserAskedAboutAutomaticJavaDownload").toBool(); !m_settings->get("AutomaticJavaSwitch").toBool() && !m_settings->get("UserAskedAboutAutomaticJavaDownload").toBool();
bool languageRequired = settings()->get("Language").toString().isEmpty(); bool languageRequired = settings()->get("Language").toString().isEmpty();
bool pasteInterventionRequired = settings()->get("PastebinURL") != ""; bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
bool validWidgets = m_themeManager->isValidApplicationTheme(settings()->get("ApplicationTheme").toString()); bool validWidgets = m_themeManager->isValidApplicationTheme(settings()->get("ApplicationTheme").toString());
@ -1312,7 +1222,7 @@ void Application::performMainStartupAction()
qDebug() << " Launching with account" << m_profileToUse; qDebug() << " Launching with account" << m_profileToUse;
} }
launch(inst, !m_offline, false, targetToJoin, accountToUse, m_offlineName); launch(inst, true, false, targetToJoin, accountToUse);
return; return;
} }
} }
@ -1414,8 +1324,6 @@ void Application::messageReceived(const QByteArray& message)
QString server = received.args["server"]; QString server = received.args["server"];
QString world = received.args["world"]; QString world = received.args["world"];
QString profile = received.args["profile"]; QString profile = received.args["profile"];
bool offline = received.args["offline_enabled"] == "true";
QString offlineName = received.args["offline_name"];
InstancePtr instance; InstancePtr instance;
if (!id.isEmpty()) { if (!id.isEmpty()) {
@ -1445,7 +1353,7 @@ void Application::messageReceived(const QByteArray& message)
} }
} }
launch(instance, !offline, false, serverObject, accountObject, offlineName); launch(instance, true, false, serverObject, accountObject);
} else { } else {
qWarning() << "Received invalid message" << message; qWarning() << "Received invalid message" << message;
} }
@ -1483,12 +1391,7 @@ bool Application::openJsonEditor(const QString& filename)
} }
} }
bool Application::launch(InstancePtr instance, bool Application::launch(InstancePtr instance, bool online, bool demo, MinecraftTarget::Ptr targetToJoin, MinecraftAccountPtr accountToUse)
bool online,
bool demo,
MinecraftTarget::Ptr targetToJoin,
MinecraftAccountPtr accountToUse,
const QString& offlineName)
{ {
if (m_updateRunning) { if (m_updateRunning) {
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed."; qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
@ -1509,7 +1412,6 @@ bool Application::launch(InstancePtr instance,
controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get()); controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
controller->setTargetToJoin(targetToJoin); controller->setTargetToJoin(targetToJoin);
controller->setAccountToUse(accountToUse); controller->setAccountToUse(accountToUse);
controller->setOfflineName(offlineName);
if (window) { if (window) {
controller->setParentWidget(window); controller->setParentWidget(window);
} else if (m_mainWindow) { } else if (m_mainWindow) {

View File

@ -211,8 +211,7 @@ class Application : public QApplication {
bool online = true, bool online = true,
bool demo = false, bool demo = false,
MinecraftTarget::Ptr targetToJoin = nullptr, MinecraftTarget::Ptr targetToJoin = nullptr,
MinecraftAccountPtr accountToUse = nullptr, MinecraftAccountPtr accountToUse = nullptr);
const QString& offlineName = QString());
bool kill(InstancePtr instance); bool kill(InstancePtr instance);
void closeCurrentWindow(); void closeCurrentWindow();
@ -302,8 +301,6 @@ class Application : public QApplication {
QString m_serverToJoin; QString m_serverToJoin;
QString m_worldToJoin; QString m_worldToJoin;
QString m_profileToUse; QString m_profileToUse;
bool m_offline = false;
QString m_offlineName;
bool m_liveCheck = false; bool m_liveCheck = false;
QList<QUrl> m_urlsToImport; QList<QUrl> m_urlsToImport;
QString m_instanceIdToShowWindowOf; QString m_instanceIdToShowWindowOf;

View File

@ -42,8 +42,8 @@
#include <QFileInfo> #include <QFileInfo>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QRegularExpression>
#include "Application.h"
#include "settings/INISettingsObject.h" #include "settings/INISettingsObject.h"
#include "settings/OverrideSetting.h" #include "settings/OverrideSetting.h"
#include "settings/Setting.h" #include "settings/Setting.h"
@ -174,12 +174,6 @@ void BaseInstance::copyManagedPack(BaseInstance& other)
m_settings->set("ManagedPackName", other.getManagedPackName()); m_settings->set("ManagedPackName", other.getManagedPackName());
m_settings->set("ManagedPackVersionID", other.getManagedPackVersionID()); m_settings->set("ManagedPackVersionID", other.getManagedPackVersionID());
m_settings->set("ManagedPackVersionName", other.getManagedPackVersionName()); m_settings->set("ManagedPackVersionName", other.getManagedPackVersionName());
if (APPLICATION->settings()->get("AutomaticJavaSwitch").toBool() && m_settings->get("AutomaticJava").toBool() &&
m_settings->get("OverrideJavaLocation").toBool()) {
m_settings->set("OverrideJavaLocation", false);
m_settings->set("JavaPath", "");
}
} }
int BaseInstance::getConsoleMaxLines() const int BaseInstance::getConsoleMaxLines() const
@ -392,12 +386,6 @@ void BaseInstance::setName(QString val)
emit propertiesChanged(this); emit propertiesChanged(this);
} }
bool BaseInstance::syncInstanceDirName(const QString& newRoot) const
{
auto oldRoot = instanceRoot();
return oldRoot == newRoot || QFile::rename(oldRoot, newRoot);
}
QString BaseInstance::name() const QString BaseInstance::name() const
{ {
return m_settings->get("name").toString(); return m_settings->get("name").toString();
@ -423,8 +411,3 @@ void BaseInstance::updateRuntimeContext()
{ {
// NOOP // NOOP
} }
bool BaseInstance::isLegacy()
{
return traits().contains("legacyLaunch") || traits().contains("alphaLaunch");
}

View File

@ -126,9 +126,6 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
QString name() const; QString name() const;
void setName(QString val); void setName(QString val);
/// Sync name and rename instance dir accordingly; returns true if successful
bool syncInstanceDirName(const QString& newRoot) const;
/// Value used for instance window titles /// Value used for instance window titles
QString windowTitle() const; QString windowTitle() const;
@ -151,6 +148,9 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
void setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version); void setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version);
void copyManagedPack(BaseInstance& other); void copyManagedPack(BaseInstance& other);
/// guess log level from a line of game log
virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString& line, MessageLevel::Enum level) { return level; }
virtual QStringList extraArguments(); virtual QStringList extraArguments();
/// Traits. Normally inside the version, depends on instance implementation. /// Traits. Normally inside the version, depends on instance implementation.
@ -195,10 +195,15 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
virtual QProcessEnvironment createEnvironment() = 0; virtual QProcessEnvironment createEnvironment() = 0;
virtual QProcessEnvironment createLaunchEnvironment() = 0; virtual QProcessEnvironment createLaunchEnvironment() = 0;
/*!
* Returns a matcher that can maps relative paths within the instance to whether they are 'log files'
*/
virtual IPathMatcher::Ptr getLogFileMatcher() = 0;
/*! /*!
* Returns the root folder to use for looking up log files * Returns the root folder to use for looking up log files
*/ */
virtual QStringList getLogFileSearchPaths() = 0; virtual QString getLogFileRoot() = 0;
virtual QString getStatusbarDescription() = 0; virtual QString getStatusbarDescription() = 0;
@ -264,8 +269,6 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
bool removeLinkedInstanceId(const QString& id); bool removeLinkedInstanceId(const QString& id);
bool isLinkedToInstanceId(const QString& id) const; bool isLinkedToInstanceId(const QString& id) const;
bool isLegacy();
protected: protected:
void changeStatus(Status newStatus); void changeStatus(Status newStatus);

View File

@ -21,8 +21,6 @@ set(CORE_SOURCES
BaseVersion.h BaseVersion.h
BaseInstance.h BaseInstance.h
BaseInstance.cpp BaseInstance.cpp
InstanceDirUpdate.h
InstanceDirUpdate.cpp
NullInstance.h NullInstance.h
MMCZip.h MMCZip.h
MMCZip.cpp MMCZip.cpp
@ -175,8 +173,6 @@ set(LAUNCH_SOURCES
launch/LogModel.h launch/LogModel.h
launch/TaskStepWrapper.cpp launch/TaskStepWrapper.cpp
launch/TaskStepWrapper.h launch/TaskStepWrapper.h
logs/LogParser.cpp
logs/LogParser.h
) )
# Old update system # Old update system
@ -349,12 +345,13 @@ set(MINECRAFT_SOURCES
minecraft/mod/TexturePackFolderModel.h minecraft/mod/TexturePackFolderModel.h
minecraft/mod/TexturePackFolderModel.cpp minecraft/mod/TexturePackFolderModel.cpp
minecraft/mod/ShaderPackFolderModel.h minecraft/mod/ShaderPackFolderModel.h
minecraft/mod/tasks/ResourceFolderLoadTask.h minecraft/mod/tasks/BasicFolderLoadTask.h
minecraft/mod/tasks/ResourceFolderLoadTask.cpp minecraft/mod/tasks/ModFolderLoadTask.h
minecraft/mod/tasks/ModFolderLoadTask.cpp
minecraft/mod/tasks/LocalModParseTask.h minecraft/mod/tasks/LocalModParseTask.h
minecraft/mod/tasks/LocalModParseTask.cpp minecraft/mod/tasks/LocalModParseTask.cpp
minecraft/mod/tasks/LocalResourceUpdateTask.h minecraft/mod/tasks/LocalModUpdateTask.h
minecraft/mod/tasks/LocalResourceUpdateTask.cpp minecraft/mod/tasks/LocalModUpdateTask.cpp
minecraft/mod/tasks/LocalDataPackParseTask.h minecraft/mod/tasks/LocalDataPackParseTask.h
minecraft/mod/tasks/LocalDataPackParseTask.cpp minecraft/mod/tasks/LocalDataPackParseTask.cpp
minecraft/mod/tasks/LocalResourcePackParseTask.h minecraft/mod/tasks/LocalResourcePackParseTask.h
@ -591,8 +588,8 @@ set(ATLAUNCHER_SOURCES
) )
set(LINKEXE_SOURCES set(LINKEXE_SOURCES
console/WindowsConsole.h WindowsConsole.cpp
console/WindowsConsole.cpp WindowsConsole.h
filelink/FileLink.h filelink/FileLink.h
filelink/FileLink.cpp filelink/FileLink.cpp
@ -661,14 +658,6 @@ set(PRISMUPDATER_SOURCES
) )
if(WIN32)
set(PRISMUPDATER_SOURCES
console/WindowsConsole.h
console/WindowsConsole.cpp
${PRISMUPDATER_SOURCES}
)
endif()
######## Logging categories ######## ######## Logging categories ########
ecm_qt_declare_logging_category(CORE_SOURCES ecm_qt_declare_logging_category(CORE_SOURCES
@ -796,9 +785,6 @@ SET(LAUNCHER_SOURCES
SysInfo.h SysInfo.h
SysInfo.cpp SysInfo.cpp
# console utils
console/Console.h
# GUI - general utilities # GUI - general utilities
DesktopServices.h DesktopServices.h
DesktopServices.cpp DesktopServices.cpp
@ -825,8 +811,7 @@ SET(LAUNCHER_SOURCES
resources/flat/flat.qrc resources/flat/flat.qrc
resources/flat_white/flat_white.qrc resources/flat_white/flat_white.qrc
resources/documents/documents.qrc resources/documents/documents.qrc
resources/shaders/shaders.qrc ../${Launcher_Branding_LogoQRC}
"${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_LogoQRC}"
# Icons # Icons
icons/MMCIcon.h icons/MMCIcon.h
@ -924,6 +909,7 @@ SET(LAUNCHER_SOURCES
ui/pages/instance/NotesPage.h ui/pages/instance/NotesPage.h
ui/pages/instance/LogPage.cpp ui/pages/instance/LogPage.cpp
ui/pages/instance/LogPage.h ui/pages/instance/LogPage.h
ui/pages/instance/InstanceSettingsPage.cpp
ui/pages/instance/InstanceSettingsPage.h ui/pages/instance/InstanceSettingsPage.h
ui/pages/instance/ScreenshotsPage.cpp ui/pages/instance/ScreenshotsPage.cpp
ui/pages/instance/ScreenshotsPage.h ui/pages/instance/ScreenshotsPage.h
@ -933,22 +919,21 @@ SET(LAUNCHER_SOURCES
ui/pages/instance/ServersPage.h ui/pages/instance/ServersPage.h
ui/pages/instance/WorldListPage.cpp ui/pages/instance/WorldListPage.cpp
ui/pages/instance/WorldListPage.h ui/pages/instance/WorldListPage.h
ui/pages/instance/McClient.cpp
ui/pages/instance/McClient.h
ui/pages/instance/McResolver.cpp
ui/pages/instance/McResolver.h
ui/pages/instance/ServerPingTask.cpp
ui/pages/instance/ServerPingTask.h
# GUI - global settings pages # GUI - global settings pages
ui/pages/global/AccountListPage.cpp ui/pages/global/AccountListPage.cpp
ui/pages/global/AccountListPage.h ui/pages/global/AccountListPage.h
ui/pages/global/CustomCommandsPage.cpp
ui/pages/global/CustomCommandsPage.h
ui/pages/global/EnvironmentVariablesPage.cpp
ui/pages/global/EnvironmentVariablesPage.h
ui/pages/global/ExternalToolsPage.cpp ui/pages/global/ExternalToolsPage.cpp
ui/pages/global/ExternalToolsPage.h ui/pages/global/ExternalToolsPage.h
ui/pages/global/JavaPage.cpp ui/pages/global/JavaPage.cpp
ui/pages/global/JavaPage.h ui/pages/global/JavaPage.h
ui/pages/global/LanguagePage.cpp ui/pages/global/LanguagePage.cpp
ui/pages/global/LanguagePage.h ui/pages/global/LanguagePage.h
ui/pages/global/MinecraftPage.cpp
ui/pages/global/MinecraftPage.h ui/pages/global/MinecraftPage.h
ui/pages/global/LauncherPage.cpp ui/pages/global/LauncherPage.cpp
ui/pages/global/LauncherPage.h ui/pages/global/LauncherPage.h
@ -1082,21 +1067,14 @@ SET(LAUNCHER_SOURCES
ui/dialogs/BlockedModsDialog.h ui/dialogs/BlockedModsDialog.h
ui/dialogs/ChooseProviderDialog.h ui/dialogs/ChooseProviderDialog.h
ui/dialogs/ChooseProviderDialog.cpp ui/dialogs/ChooseProviderDialog.cpp
ui/dialogs/ResourceUpdateDialog.cpp ui/dialogs/ModUpdateDialog.cpp
ui/dialogs/ResourceUpdateDialog.h ui/dialogs/ModUpdateDialog.h
ui/dialogs/InstallLoaderDialog.cpp ui/dialogs/InstallLoaderDialog.cpp
ui/dialogs/InstallLoaderDialog.h ui/dialogs/InstallLoaderDialog.h
ui/dialogs/skins/SkinManageDialog.cpp ui/dialogs/skins/SkinManageDialog.cpp
ui/dialogs/skins/SkinManageDialog.h ui/dialogs/skins/SkinManageDialog.h
ui/dialogs/skins/draw/SkinOpenGLWindow.h
ui/dialogs/skins/draw/SkinOpenGLWindow.cpp
ui/dialogs/skins/draw/Scene.h
ui/dialogs/skins/draw/Scene.cpp
ui/dialogs/skins/draw/BoxGeometry.h
ui/dialogs/skins/draw/BoxGeometry.cpp
# GUI - widgets # GUI - widgets
ui/widgets/CheckComboBox.cpp ui/widgets/CheckComboBox.cpp
ui/widgets/CheckComboBox.h ui/widgets/CheckComboBox.h
@ -1106,12 +1084,14 @@ SET(LAUNCHER_SOURCES
ui/widgets/CustomCommands.h ui/widgets/CustomCommands.h
ui/widgets/EnvironmentVariables.cpp ui/widgets/EnvironmentVariables.cpp
ui/widgets/EnvironmentVariables.h ui/widgets/EnvironmentVariables.h
ui/widgets/DropLabel.cpp
ui/widgets/DropLabel.h
ui/widgets/FocusLineEdit.cpp ui/widgets/FocusLineEdit.cpp
ui/widgets/FocusLineEdit.h ui/widgets/FocusLineEdit.h
ui/widgets/IconLabel.cpp ui/widgets/IconLabel.cpp
ui/widgets/IconLabel.h ui/widgets/IconLabel.h
ui/widgets/JavaWizardWidget.cpp ui/widgets/JavaSettingsWidget.cpp
ui/widgets/JavaWizardWidget.h ui/widgets/JavaSettingsWidget.h
ui/widgets/LabeledToolButton.cpp ui/widgets/LabeledToolButton.cpp
ui/widgets/LabeledToolButton.h ui/widgets/LabeledToolButton.h
ui/widgets/LanguageSelectionWidget.cpp ui/widgets/LanguageSelectionWidget.cpp
@ -1147,10 +1127,6 @@ SET(LAUNCHER_SOURCES
ui/widgets/WideBar.cpp ui/widgets/WideBar.cpp
ui/widgets/ThemeCustomizationWidget.h ui/widgets/ThemeCustomizationWidget.h
ui/widgets/ThemeCustomizationWidget.cpp ui/widgets/ThemeCustomizationWidget.cpp
ui/widgets/MinecraftSettingsWidget.h
ui/widgets/MinecraftSettingsWidget.cpp
ui/widgets/JavaSettingsWidget.h
ui/widgets/JavaSettingsWidget.cpp
# GUI - instance group view # GUI - instance group view
ui/instanceview/InstanceProxyModel.cpp ui/instanceview/InstanceProxyModel.cpp
@ -1177,8 +1153,8 @@ endif()
if(WIN32) if(WIN32)
set(LAUNCHER_SOURCES set(LAUNCHER_SOURCES
console/WindowsConsole.h WindowsConsole.cpp
console/WindowsConsole.cpp WindowsConsole.h
${LAUNCHER_SOURCES} ${LAUNCHER_SOURCES}
) )
endif() endif()
@ -1194,6 +1170,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/global/LauncherPage.ui ui/pages/global/LauncherPage.ui
ui/pages/global/APIPage.ui ui/pages/global/APIPage.ui
ui/pages/global/ProxyPage.ui ui/pages/global/ProxyPage.ui
ui/pages/global/MinecraftPage.ui
ui/pages/global/ExternalToolsPage.ui ui/pages/global/ExternalToolsPage.ui
ui/pages/instance/ExternalResourcesPage.ui ui/pages/instance/ExternalResourcesPage.ui
ui/pages/instance/NotesPage.ui ui/pages/instance/NotesPage.ui
@ -1201,6 +1178,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/instance/ServersPage.ui ui/pages/instance/ServersPage.ui
ui/pages/instance/GameOptionsPage.ui ui/pages/instance/GameOptionsPage.ui
ui/pages/instance/OtherLogsPage.ui ui/pages/instance/OtherLogsPage.ui
ui/pages/instance/InstanceSettingsPage.ui
ui/pages/instance/VersionPage.ui ui/pages/instance/VersionPage.ui
ui/pages/instance/ManagedPackPage.ui ui/pages/instance/ManagedPackPage.ui
ui/pages/instance/WorldListPage.ui ui/pages/instance/WorldListPage.ui
@ -1223,8 +1201,6 @@ qt_wrap_ui(LAUNCHER_UI
ui/widgets/ModFilterWidget.ui ui/widgets/ModFilterWidget.ui
ui/widgets/SubTaskProgressBar.ui ui/widgets/SubTaskProgressBar.ui
ui/widgets/ThemeCustomizationWidget.ui ui/widgets/ThemeCustomizationWidget.ui
ui/widgets/MinecraftSettingsWidget.ui
ui/widgets/JavaSettingsWidget.ui
ui/dialogs/CopyInstanceDialog.ui ui/dialogs/CopyInstanceDialog.ui
ui/dialogs/ProfileSetupDialog.ui ui/dialogs/ProfileSetupDialog.ui
ui/dialogs/ProgressDialog.ui ui/dialogs/ProgressDialog.ui
@ -1244,6 +1220,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/dialogs/ScrollMessageBox.ui ui/dialogs/ScrollMessageBox.ui
ui/dialogs/BlockedModsDialog.ui ui/dialogs/BlockedModsDialog.ui
ui/dialogs/ChooseProviderDialog.ui ui/dialogs/ChooseProviderDialog.ui
ui/dialogs/skins/SkinManageDialog.ui ui/dialogs/skins/SkinManageDialog.ui
) )
@ -1268,8 +1245,7 @@ qt_add_resources(LAUNCHER_RESOURCES
resources/iOS/iOS.qrc resources/iOS/iOS.qrc
resources/flat/flat.qrc resources/flat/flat.qrc
resources/documents/documents.qrc resources/documents/documents.qrc
resources/shaders/shaders.qrc ../${Launcher_Branding_LogoQRC}
"${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_LogoQRC}"
) )
qt_wrap_ui(PRISMUPDATER_UI qt_wrap_ui(PRISMUPDATER_UI
@ -1302,7 +1278,7 @@ target_link_libraries(Launcher_logic
qdcss qdcss
BuildConfig BuildConfig
Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Widgets
qrcode ghcFilesystem::ghc_filesystem
) )
if (UNIX AND NOT CYGWIN AND NOT APPLE) if (UNIX AND NOT CYGWIN AND NOT APPLE)
@ -1319,8 +1295,6 @@ target_link_libraries(Launcher_logic
Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::NetworkAuth Qt${QT_VERSION_MAJOR}::NetworkAuth
Qt${QT_VERSION_MAJOR}::OpenGL
${Launcher_QT_DBUS}
${Launcher_QT_LIBS} ${Launcher_QT_LIBS}
) )
target_link_libraries(Launcher_logic target_link_libraries(Launcher_logic
@ -1329,10 +1303,6 @@ target_link_libraries(Launcher_logic
LocalPeer LocalPeer
Launcher_rainbow Launcher_rainbow
) )
if (TARGET ${Launcher_QT_DBUS})
add_compile_definitions(WITH_QTDBUS)
endif()
if(APPLE) if(APPLE)
set(CMAKE_MACOSX_RPATH 1) set(CMAKE_MACOSX_RPATH 1)
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/") set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/")
@ -1389,6 +1359,7 @@ if(Launcher_BUILD_UPDATER)
${ZLIB_LIBRARIES} ${ZLIB_LIBRARIES}
systeminfo systeminfo
BuildConfig BuildConfig
ghcFilesystem::ghc_filesystem
Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Network
@ -1427,6 +1398,7 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
target_link_libraries(filelink_logic target_link_libraries(filelink_logic
systeminfo systeminfo
BuildConfig BuildConfig
ghcFilesystem::ghc_filesystem
Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Network

View File

@ -15,7 +15,7 @@
DataMigrationTask::DataMigrationTask(const QString& sourcePath, const QString& targetPath, const IPathMatcher::Ptr pathMatcher) DataMigrationTask::DataMigrationTask(const QString& sourcePath, const QString& targetPath, const IPathMatcher::Ptr pathMatcher)
: Task(), m_sourcePath(sourcePath), m_targetPath(targetPath), m_pathMatcher(pathMatcher), m_copy(sourcePath, targetPath) : Task(), m_sourcePath(sourcePath), m_targetPath(targetPath), m_pathMatcher(pathMatcher), m_copy(sourcePath, targetPath)
{ {
m_copy.matcher(m_pathMatcher).whitelist(true); m_copy.matcher(m_pathMatcher.get()).whitelist(true);
} }
void DataMigrationTask::executeTask() void DataMigrationTask::executeTask()
@ -24,7 +24,7 @@ void DataMigrationTask::executeTask()
// 1. Scan // 1. Scan
// Check how many files we gotta copy // Check how many files we gotta copy
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] { m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] {
return m_copy(true); // dry run to collect amount of files return m_copy(true); // dry run to collect amount of files
}); });
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::dryRunFinished); connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::dryRunFinished);
@ -37,7 +37,11 @@ void DataMigrationTask::dryRunFinished()
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::dryRunFinished); disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::dryRunFinished);
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &DataMigrationTask::dryRunAborted); disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &DataMigrationTask::dryRunAborted);
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
if (!m_copyFuture.isValid() || !m_copyFuture.result()) { if (!m_copyFuture.isValid() || !m_copyFuture.result()) {
#else
if (!m_copyFuture.result()) {
#endif
emitFailed(tr("Failed to scan source path.")); emitFailed(tr("Failed to scan source path."));
return; return;
} }
@ -53,7 +57,7 @@ void DataMigrationTask::dryRunFinished()
setProgress(m_copy.totalCopied(), m_toCopy); setProgress(m_copy.totalCopied(), m_toCopy);
setStatus(tr("Copying %1…").arg(shortenedName)); setStatus(tr("Copying %1…").arg(shortenedName));
}); });
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] { m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] {
return m_copy(false); // actually copy now return m_copy(false); // actually copy now
}); });
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::copyFinished); connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::copyFinished);
@ -71,7 +75,11 @@ void DataMigrationTask::copyFinished()
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::copyFinished); disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::copyFinished);
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &DataMigrationTask::copyAborted); disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &DataMigrationTask::copyAborted);
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
if (!m_copyFuture.isValid() || !m_copyFuture.result()) { if (!m_copyFuture.isValid() || !m_copyFuture.result()) {
#else
if (!m_copyFuture.result()) {
#endif
emitFailed(tr("Some paths could not be copied!")); emitFailed(tr("Some paths could not be copied!"));
return; return;
} }

View File

@ -40,11 +40,12 @@
#include <QFileSystemModel> #include <QFileSystemModel>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QStack> #include <QStack>
#include <algorithm>
#include "FileSystem.h" #include "FileSystem.h"
#include "SeparatorPrefixTree.h" #include "SeparatorPrefixTree.h"
#include "StringUtils.h" #include "StringUtils.h"
FileIgnoreProxy::FileIgnoreProxy(QString root, QObject* parent) : QSortFilterProxyModel(parent), m_root(root) {} FileIgnoreProxy::FileIgnoreProxy(QString root, QObject* parent) : QSortFilterProxyModel(parent), root(root) {}
// NOTE: Sadly, we have to do sorting ourselves. // NOTE: Sadly, we have to do sorting ourselves.
bool FileIgnoreProxy::lessThan(const QModelIndex& left, const QModelIndex& right) const bool FileIgnoreProxy::lessThan(const QModelIndex& left, const QModelIndex& right) const
{ {
@ -103,10 +104,10 @@ QVariant FileIgnoreProxy::data(const QModelIndex& index, int role) const
if (index.column() == 0 && role == Qt::CheckStateRole) { if (index.column() == 0 && role == Qt::CheckStateRole) {
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel()); QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
auto blockedPath = relPath(fsm->filePath(sourceIndex)); auto blockedPath = relPath(fsm->filePath(sourceIndex));
auto cover = m_blocked.cover(blockedPath); auto cover = blocked.cover(blockedPath);
if (!cover.isNull()) { if (!cover.isNull()) {
return QVariant(Qt::Unchecked); return QVariant(Qt::Unchecked);
} else if (m_blocked.exists(blockedPath)) { } else if (blocked.exists(blockedPath)) {
return QVariant(Qt::PartiallyChecked); return QVariant(Qt::PartiallyChecked);
} else { } else {
return QVariant(Qt::Checked); return QVariant(Qt::Checked);
@ -129,7 +130,7 @@ bool FileIgnoreProxy::setData(const QModelIndex& index, const QVariant& value, i
QString FileIgnoreProxy::relPath(const QString& path) const QString FileIgnoreProxy::relPath(const QString& path) const
{ {
return QDir(m_root).relativeFilePath(path); return QDir(root).relativeFilePath(path);
} }
bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state) bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state)
@ -145,18 +146,18 @@ bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state)
bool changed = false; bool changed = false;
if (state == Qt::Unchecked) { if (state == Qt::Unchecked) {
// blocking a path // blocking a path
auto& node = m_blocked.insert(blockedPath); auto& node = blocked.insert(blockedPath);
// get rid of all blocked nodes below // get rid of all blocked nodes below
node.clear(); node.clear();
changed = true; changed = true;
} else if (state == Qt::Checked || state == Qt::PartiallyChecked) { } else if (state == Qt::Checked || state == Qt::PartiallyChecked) {
if (!m_blocked.remove(blockedPath)) { if (!blocked.remove(blockedPath)) {
auto cover = m_blocked.cover(blockedPath); auto cover = blocked.cover(blockedPath);
qDebug() << "Blocked by cover" << cover; qDebug() << "Blocked by cover" << cover;
// uncover // uncover
m_blocked.remove(cover); blocked.remove(cover);
// block all contents, except for any cover // block all contents, except for any cover
QModelIndex rootIndex = fsm->index(FS::PathCombine(m_root, cover)); QModelIndex rootIndex = fsm->index(FS::PathCombine(root, cover));
QModelIndex doing = rootIndex; QModelIndex doing = rootIndex;
int row = 0; int row = 0;
QStack<QModelIndex> todo; QStack<QModelIndex> todo;
@ -178,7 +179,7 @@ bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state)
todo.push(node); todo.push(node);
} else { } else {
// or just block this one. // or just block this one.
m_blocked.insert(relpath); blocked.insert(relpath);
} }
row++; row++;
} }
@ -228,7 +229,7 @@ bool FileIgnoreProxy::shouldExpand(QModelIndex index)
return false; return false;
} }
auto blockedPath = relPath(fsm->filePath(sourceIndex)); auto blockedPath = relPath(fsm->filePath(sourceIndex));
auto found = m_blocked.find(blockedPath); auto found = blocked.find(blockedPath);
if (found) { if (found) {
return !found->leaf(); return !found->leaf();
} }
@ -238,8 +239,8 @@ bool FileIgnoreProxy::shouldExpand(QModelIndex index)
void FileIgnoreProxy::setBlockedPaths(QStringList paths) void FileIgnoreProxy::setBlockedPaths(QStringList paths)
{ {
beginResetModel(); beginResetModel();
m_blocked.clear(); blocked.clear();
m_blocked.insert(paths); blocked.insert(paths);
endResetModel(); endResetModel();
} }
@ -269,28 +270,7 @@ bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const
return m_ignoreFiles.contains(fileInfo.fileName()) || m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath())); return m_ignoreFiles.contains(fileInfo.fileName()) || m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath()));
} }
bool FileIgnoreProxy::filterFile(const QFileInfo& file) const bool FileIgnoreProxy::filterFile(const QString& fileName) const
{ {
return m_blocked.covers(relPath(file.absoluteFilePath())) || ignoreFile(file); return blocked.covers(fileName) || ignoreFile(QFileInfo(QDir(root), fileName));
}
void FileIgnoreProxy::loadBlockedPathsFromFile(const QString& fileName)
{
QFile ignoreFile(fileName);
if (!ignoreFile.open(QIODevice::ReadOnly)) {
return;
}
auto ignoreData = ignoreFile.readAll();
auto string = QString::fromUtf8(ignoreData);
setBlockedPaths(string.split('\n', Qt::SkipEmptyParts));
}
void FileIgnoreProxy::saveBlockedPathsToFile(const QString& fileName)
{
auto ignoreData = blockedPaths().toStringList().join('\n').toUtf8();
try {
FS::write(fileName, ignoreData);
} catch (const Exception& e) {
qWarning() << e.cause();
}
} }

View File

@ -61,19 +61,15 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
void setBlockedPaths(QStringList paths); void setBlockedPaths(QStringList paths);
inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return m_blocked; } inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return blocked; }
inline SeparatorPrefixTree<'/'>& blockedPaths() { return m_blocked; } inline SeparatorPrefixTree<'/'>& blockedPaths() { return blocked; }
// list of file names that need to be removed completely from model // list of file names that need to be removed completely from model
inline QStringList& ignoreFilesWithName() { return m_ignoreFiles; } inline QStringList& ignoreFilesWithName() { return m_ignoreFiles; }
// list of relative paths that need to be removed completely from model // list of relative paths that need to be removed completely from model
inline SeparatorPrefixTree<'/'>& ignoreFilesWithPath() { return m_ignoreFilePaths; } inline SeparatorPrefixTree<'/'>& ignoreFilesWithPath() { return m_ignoreFilePaths; }
bool filterFile(const QFileInfo& fileName) const; bool filterFile(const QString& fileName) const;
void loadBlockedPathsFromFile(const QString& fileName);
void saveBlockedPathsToFile(const QString& fileName);
protected: protected:
bool filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const; bool filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const;
@ -82,8 +78,8 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
bool ignoreFile(QFileInfo file) const; bool ignoreFile(QFileInfo file) const;
private: private:
const QString m_root; const QString root;
SeparatorPrefixTree<'/'> m_blocked; SeparatorPrefixTree<'/'> blocked;
QStringList m_ignoreFiles; QStringList m_ignoreFiles;
SeparatorPrefixTree<'/'> m_ignoreFilePaths; SeparatorPrefixTree<'/'> m_ignoreFilePaths;
}; };

View File

@ -77,8 +77,24 @@
#include <utime.h> #include <utime.h>
#endif #endif
// Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header
#ifdef __APPLE__
#include <Availability.h> // for deployment target to support pre-catalina targets without std::fs
#endif // __APPLE__
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS
#include <filesystem> #include <filesystem>
namespace fs = std::filesystem; namespace fs = std::filesystem;
#endif // MacOS min version check
#endif // Other OSes version check
#ifndef GHC_USE_STD_FS
#include <ghc/filesystem.hpp>
namespace fs = ghc::filesystem;
#endif
// clone // clone
#if defined(Q_OS_LINUX) #if defined(Q_OS_LINUX)
@ -325,7 +341,7 @@ bool copy::operator()(const QString& offset, bool dryRun)
opt |= copy_opts::overwrite_existing; opt |= copy_opts::overwrite_existing;
// Function that'll do the actual copying // Function that'll do the actual copying
auto copy_file = [this, dryRun, src, dst, opt, &err](QString src_path, QString relative_dst_path) { auto copy_file = [&](QString src_path, QString relative_dst_path) {
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist)) if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
return; return;
@ -412,7 +428,7 @@ void create_link::make_link_list(const QString& offset)
m_recursive = true; m_recursive = true;
// Function that'll do the actual linking // Function that'll do the actual linking
auto link_file = [this, dst](QString src_path, QString relative_dst_path) { auto link_file = [&](QString src_path, QString relative_dst_path) {
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist)) { if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist)) {
qDebug() << "path" << relative_dst_path << "in black list or not in whitelist"; qDebug() << "path" << relative_dst_path << "in black list or not in whitelist";
return; return;
@ -507,7 +523,7 @@ void create_link::runPrivileged(const QString& offset)
QString serverName = BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink_server" + StringUtils::getRandomAlphaNumeric(); QString serverName = BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink_server" + StringUtils::getRandomAlphaNumeric();
connect(&m_linkServer, &QLocalServer::newConnection, this, [this, &gotResults]() { connect(&m_linkServer, &QLocalServer::newConnection, this, [&]() {
qDebug() << "Client connected, sending out pairs"; qDebug() << "Client connected, sending out pairs";
// construct block of data to send // construct block of data to send
QByteArray block; QByteArray block;
@ -589,7 +605,7 @@ void create_link::runPrivileged(const QString& offset)
} }
ExternalLinkFileProcess* linkFileProcess = new ExternalLinkFileProcess(serverName, m_useHardLinks, this); ExternalLinkFileProcess* linkFileProcess = new ExternalLinkFileProcess(serverName, m_useHardLinks, this);
connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [this, gotResults]() { emit finishedPrivileged(gotResults); }); connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [&]() { emit finishedPrivileged(gotResults); });
connect(linkFileProcess, &ExternalLinkFileProcess::finished, linkFileProcess, &QObject::deleteLater); connect(linkFileProcess, &ExternalLinkFileProcess::finished, linkFileProcess, &QObject::deleteLater);
linkFileProcess->start(); linkFileProcess->start();
@ -679,6 +695,9 @@ bool deletePath(QString path)
bool trash(QString path, QString* pathInTrash) bool trash(QString path, QString* pathInTrash)
{ {
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
return false;
#else
// FIXME: Figure out trash in Flatpak. Qt seemingly doesn't use the Trash portal // FIXME: Figure out trash in Flatpak. Qt seemingly doesn't use the Trash portal
if (DesktopServices::isFlatpak()) if (DesktopServices::isFlatpak())
return false; return false;
@ -687,6 +706,7 @@ bool trash(QString path, QString* pathInTrash)
return false; return false;
#endif #endif
return QFile::moveToTrash(path, pathInTrash); return QFile::moveToTrash(path, pathInTrash);
#endif
} }
QString PathCombine(const QString& path1, const QString& path2) QString PathCombine(const QString& path1, const QString& path2)
@ -720,7 +740,11 @@ int pathDepth(const QString& path)
QFileInfo info(path); QFileInfo info(path);
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
auto parts = QDir::toNativeSeparators(info.path()).split(QDir::separator(), QString::SkipEmptyParts);
#else
auto parts = QDir::toNativeSeparators(info.path()).split(QDir::separator(), Qt::SkipEmptyParts); auto parts = QDir::toNativeSeparators(info.path()).split(QDir::separator(), Qt::SkipEmptyParts);
#endif
int numParts = parts.length(); int numParts = parts.length();
numParts -= parts.count("."); numParts -= parts.count(".");
@ -740,7 +764,11 @@ QString pathTruncate(const QString& path, int depth)
return pathTruncate(trunc, depth); return pathTruncate(trunc, depth);
} }
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
auto parts = QDir::toNativeSeparators(trunc).split(QDir::separator(), QString::SkipEmptyParts);
#else
auto parts = QDir::toNativeSeparators(trunc).split(QDir::separator(), Qt::SkipEmptyParts); auto parts = QDir::toNativeSeparators(trunc).split(QDir::separator(), Qt::SkipEmptyParts);
#endif
if (parts.startsWith(".") && !path.startsWith(".")) { if (parts.startsWith(".") && !path.startsWith(".")) {
parts.removeFirst(); parts.removeFirst();
@ -922,7 +950,7 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
QDir content = application.path() + "/Contents/"; QDir content = application.path() + "/Contents/";
QDir resources = content.path() + "/Resources/"; QDir resources = content.path() + "/Resources/";
QDir binaryDir = content.path() + "/MacOS/"; QDir binaryDir = content.path() + "/MacOS/";
QFile info(content.path() + "/Info.plist"); QFile info = content.path() + "/Info.plist";
if (!(content.mkpath(".") && resources.mkpath(".") && binaryDir.mkpath("."))) { if (!(content.mkpath(".") && resources.mkpath(".") && binaryDir.mkpath("."))) {
qWarning() << "Couldn't create directories within application"; qWarning() << "Couldn't create directories within application";
@ -1267,7 +1295,7 @@ bool clone::operator()(const QString& offset, bool dryRun)
std::error_code err; std::error_code err;
// Function that'll do the actual cloneing // Function that'll do the actual cloneing
auto cloneFile = [this, dryRun, dst, &err](QString src_path, QString relative_dst_path) { auto cloneFile = [&](QString src_path, QString relative_dst_path) {
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist)) if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
return; return;

View File

@ -115,7 +115,7 @@ class copy : public QObject {
m_followSymlinks = follow; m_followSymlinks = follow;
return *this; return *this;
} }
copy& matcher(IPathMatcher::Ptr filter) copy& matcher(const IPathMatcher* filter)
{ {
m_matcher = filter; m_matcher = filter;
return *this; return *this;
@ -147,7 +147,7 @@ class copy : public QObject {
private: private:
bool m_followSymlinks = true; bool m_followSymlinks = true;
IPathMatcher::Ptr m_matcher = nullptr; const IPathMatcher* m_matcher = nullptr;
bool m_whitelist = false; bool m_whitelist = false;
bool m_overwrite = false; bool m_overwrite = false;
QDir m_src; QDir m_src;
@ -209,7 +209,7 @@ class create_link : public QObject {
m_useHardLinks = useHard; m_useHardLinks = useHard;
return *this; return *this;
} }
create_link& matcher(IPathMatcher::Ptr filter) create_link& matcher(const IPathMatcher* filter)
{ {
m_matcher = filter; m_matcher = filter;
return *this; return *this;
@ -260,7 +260,7 @@ class create_link : public QObject {
private: private:
bool m_useHardLinks = false; bool m_useHardLinks = false;
IPathMatcher::Ptr m_matcher = nullptr; const IPathMatcher* m_matcher = nullptr;
bool m_whitelist = false; bool m_whitelist = false;
bool m_recursive = true; bool m_recursive = true;
@ -488,7 +488,7 @@ class clone : public QObject {
m_src.setPath(src); m_src.setPath(src);
m_dst.setPath(dst); m_dst.setPath(dst);
} }
clone& matcher(IPathMatcher::Ptr filter) clone& matcher(const IPathMatcher* filter)
{ {
m_matcher = filter; m_matcher = filter;
return *this; return *this;
@ -514,7 +514,7 @@ class clone : public QObject {
bool operator()(const QString& offset, bool dryRun = false); bool operator()(const QString& offset, bool dryRun = false);
private: private:
IPathMatcher::Ptr m_matcher = nullptr; const IPathMatcher* m_matcher = nullptr;
bool m_whitelist = false; bool m_whitelist = false;
QDir m_src; QDir m_src;
QDir m_dst; QDir m_dst;

View File

@ -36,8 +36,6 @@
#include "GZip.h" #include "GZip.h"
#include <zlib.h> #include <zlib.h>
#include <QByteArray> #include <QByteArray>
#include <QDebug>
#include <QFile>
bool GZip::unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes) bool GZip::unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes)
{ {
@ -138,81 +136,3 @@ bool GZip::zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes)
} }
return true; return true;
} }
int inf(QFile* source, std::function<bool(const QByteArray&)> handleBlock)
{
constexpr auto CHUNK = 16384;
int ret;
unsigned have;
z_stream strm;
memset(&strm, 0, sizeof(strm));
char in[CHUNK];
unsigned char out[CHUNK];
ret = inflateInit2(&strm, (16 + MAX_WBITS));
if (ret != Z_OK)
return ret;
/* decompress until deflate stream ends or end of file */
do {
strm.avail_in = source->read(in, CHUNK);
if (source->error()) {
(void)inflateEnd(&strm);
return Z_ERRNO;
}
if (strm.avail_in == 0)
break;
strm.next_in = reinterpret_cast<Bytef*>(in);
/* run inflate() on input until output buffer not full */
do {
strm.avail_out = CHUNK;
strm.next_out = out;
ret = inflate(&strm, Z_NO_FLUSH);
assert(ret != Z_STREAM_ERROR); /* state not clobbered */
switch (ret) {
case Z_NEED_DICT:
ret = Z_DATA_ERROR; /* and fall through */
case Z_DATA_ERROR:
case Z_MEM_ERROR:
(void)inflateEnd(&strm);
return ret;
}
have = CHUNK - strm.avail_out;
if (!handleBlock(QByteArray(reinterpret_cast<const char*>(out), have))) {
(void)inflateEnd(&strm);
return Z_OK;
}
} while (strm.avail_out == 0);
/* done when inflate() says it's done */
} while (ret != Z_STREAM_END);
/* clean up and return */
(void)inflateEnd(&strm);
return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}
QString zerr(int ret)
{
switch (ret) {
case Z_ERRNO:
return QObject::tr("error handling file");
case Z_STREAM_ERROR:
return QObject::tr("invalid compression level");
case Z_DATA_ERROR:
return QObject::tr("invalid or incomplete deflate data");
case Z_MEM_ERROR:
return QObject::tr("out of memory");
case Z_VERSION_ERROR:
return QObject::tr("zlib version mismatch!");
}
return {};
}
QString GZip::readGzFileByBlocks(QFile* source, std::function<bool(const QByteArray&)> handleBlock)
{
auto ret = inf(source, handleBlock);
return zerr(ret);
}

View File

@ -1,11 +1,8 @@
#pragma once #pragma once
#include <QByteArray> #include <QByteArray>
#include <QFile>
namespace GZip { class GZip {
public:
bool unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes); static bool unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes);
bool zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes); static bool zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes);
QString readGzFileByBlocks(QFile* source, std::function<bool(const QByteArray&)> handleBlock); };
} // namespace GZip

View File

@ -43,7 +43,7 @@ void InstanceCopyTask::executeTask()
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] { m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
if (m_useClone) { if (m_useClone) {
FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath); FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath);
folderClone.matcher(m_matcher); folderClone.matcher(m_matcher.get());
folderClone(true); folderClone(true);
setProgress(0, folderClone.totalCloned()); setProgress(0, folderClone.totalCloned());
@ -72,7 +72,7 @@ void InstanceCopyTask::executeTask()
} }
FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath); FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
int depth = m_linkRecursively ? -1 : 0; // we need to at least link the top level instead of the instance folder int depth = m_linkRecursively ? -1 : 0; // we need to at least link the top level instead of the instance folder
folderLink.linkRecursively(true).setMaxDepth(depth).useHardLinks(m_useHardLinks).matcher(m_matcher); folderLink.linkRecursively(true).setMaxDepth(depth).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
folderLink(true); folderLink(true);
setProgress(0, m_progressTotal + folderLink.totalToLink()); setProgress(0, m_progressTotal + folderLink.totalToLink());
@ -91,7 +91,7 @@ void InstanceCopyTask::executeTask()
QEventLoop loop; QEventLoop loop;
bool got_priv_results = false; bool got_priv_results = false;
connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&got_priv_results, &loop](bool gotResults) { connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&](bool gotResults) {
if (!gotResults) { if (!gotResults) {
qDebug() << "Privileged run exited without results!"; qDebug() << "Privileged run exited without results!";
} }
@ -127,7 +127,7 @@ void InstanceCopyTask::executeTask()
return !there_were_errors; return !there_were_errors;
} }
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath); FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
folderCopy.followSymlinks(false).matcher(m_matcher); folderCopy.followSymlinks(false).matcher(m_matcher.get());
folderCopy(true); folderCopy(true);
setProgress(0, folderCopy.totalCopied()); setProgress(0, folderCopy.totalCopied());

View File

@ -28,7 +28,7 @@ class InstanceCopyTask : public InstanceTask {
InstancePtr m_origInstance; InstancePtr m_origInstance;
QFuture<bool> m_copyFuture; QFuture<bool> m_copyFuture;
QFutureWatcher<bool> m_copyFutureWatcher; QFutureWatcher<bool> m_copyFutureWatcher;
IPathMatcher::Ptr m_matcher; std::unique_ptr<IPathMatcher> m_matcher;
bool m_keepPlaytime; bool m_keepPlaytime;
bool m_useLinks = false; bool m_useLinks = false;
bool m_useHardLinks = false; bool m_useHardLinks = false;

View File

@ -1,126 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
*
* 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 "InstanceDirUpdate.h"
#include <QCheckBox>
#include "Application.h"
#include "FileSystem.h"
#include "InstanceList.h"
#include "ui/dialogs/CustomMessageBox.h"
QString askToUpdateInstanceDirName(InstancePtr instance, const QString& oldName, const QString& newName, QWidget* parent)
{
if (oldName == newName)
return QString();
QString renamingMode = APPLICATION->settings()->get("InstRenamingMode").toString();
if (renamingMode == "MetadataOnly")
return QString();
auto oldRoot = instance->instanceRoot();
auto newDirName = FS::DirNameFromString(newName, QFileInfo(oldRoot).dir().absolutePath());
auto newRoot = FS::PathCombine(QFileInfo(oldRoot).dir().absolutePath(), newDirName);
if (oldRoot == newRoot)
return QString();
if (oldRoot == FS::PathCombine(QFileInfo(oldRoot).dir().absolutePath(), newName))
return QString();
// Check for conflict
if (QDir(newRoot).exists()) {
QMessageBox::warning(parent, QObject::tr("Cannot rename instance"),
QObject::tr("New instance root (%1) already exists. <br />Only the metadata will be renamed.").arg(newRoot));
return QString();
}
// Ask if we should rename
if (renamingMode == "AskEverytime") {
auto checkBox = new QCheckBox(QObject::tr("&Remember my choice"), parent);
auto dialog =
CustomMessageBox::selectable(parent, QObject::tr("Rename instance folder"),
QObject::tr("Would you also like to rename the instance folder?\n\n"
"Old name: %1\n"
"New name: %2")
.arg(oldName, newName),
QMessageBox::Question, QMessageBox::No | QMessageBox::Yes, QMessageBox::NoButton, checkBox);
auto res = dialog->exec();
if (checkBox->isChecked()) {
if (res == QMessageBox::Yes)
APPLICATION->settings()->set("InstRenamingMode", "PhysicalDir");
else
APPLICATION->settings()->set("InstRenamingMode", "MetadataOnly");
}
if (res == QMessageBox::No)
return QString();
}
// Check for linked instances
if (!checkLinkedInstances(instance->id(), parent, QObject::tr("Renaming")))
return QString();
// Now we can confirm that a renaming is happening
if (!instance->syncInstanceDirName(newRoot)) {
QMessageBox::warning(parent, QObject::tr("Cannot rename instance"),
QObject::tr("An error occurred when performing the following renaming operation: <br/>"
" - Old instance root: %1<br/>"
" - New instance root: %2<br/>"
"Only the metadata is renamed.")
.arg(oldRoot, newRoot));
return QString();
}
return newRoot;
}
bool checkLinkedInstances(const QString& id, QWidget* parent, const QString& verb)
{
auto linkedInstances = APPLICATION->instances()->getLinkedInstancesById(id);
if (!linkedInstances.empty()) {
auto response = CustomMessageBox::selectable(parent, QObject::tr("There are linked instances"),
QObject::tr("The following instance(s) might reference files in this instance:\n\n"
"%1\n\n"
"%2 it could break the other instance(s), \n\n"
"Do you wish to proceed?",
nullptr, linkedInstances.count())
.arg(linkedInstances.join("\n"))
.arg(verb),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec();
if (response != QMessageBox::Yes)
return false;
}
return true;
}

View File

@ -1,43 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "BaseInstance.h"
/// Update instanceRoot to make it sync with name/id; return newRoot if a directory rename happened
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

@ -72,6 +72,7 @@ bool InstanceImportTask::abort()
bool wasAborted = false; bool wasAborted = false;
if (m_task) if (m_task)
wasAborted = m_task->abort(); wasAborted = m_task->abort();
Task::abort();
return wasAborted; return wasAborted;
} }
@ -304,7 +305,7 @@ void InstanceImportTask::processFlame()
connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus); connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails); connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
connect(inst_creation_task.get(), &Task::aborted, this, &InstanceImportTask::emitAborted); connect(inst_creation_task.get(), &Task::aborted, this, &Task::abort);
connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable); connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
m_task.reset(inst_creation_task); m_task.reset(inst_creation_task);
@ -377,8 +378,8 @@ void InstanceImportTask::processModrinth()
} else { } else {
QString pack_id; QString pack_id;
if (!m_sourceUrl.isEmpty()) { if (!m_sourceUrl.isEmpty()) {
static const QRegularExpression s_regex(R"(data\/([^\/]*)\/versions)"); QRegularExpression regex(R"(data\/([^\/]*)\/versions)");
pack_id = s_regex.match(m_sourceUrl.toString()).captured(1); pack_id = regex.match(m_sourceUrl.toString()).captured(1);
} }
// FIXME: Find a way to get the ID in directly imported ZIPs // FIXME: Find a way to get the ID in directly imported ZIPs
@ -403,7 +404,7 @@ void InstanceImportTask::processModrinth()
connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus); connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails); connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
connect(inst_creation_task.get(), &Task::aborted, this, &InstanceImportTask::emitAborted); connect(inst_creation_task.get(), &Task::aborted, this, &Task::abort);
connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable); connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
m_task.reset(inst_creation_task); m_task.reset(inst_creation_task);

View File

@ -428,7 +428,7 @@ static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr>&
QList<InstanceId> InstanceList::discoverInstances() QList<InstanceId> InstanceList::discoverInstances()
{ {
qInfo() << "Discovering instances in" << m_instDir; qDebug() << "Discovering instances in" << m_instDir;
QList<InstanceId> out; QList<InstanceId> out;
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks); QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks);
while (iter.hasNext()) { while (iter.hasNext()) {
@ -447,9 +447,13 @@ QList<InstanceId> InstanceList::discoverInstances()
} }
auto id = dirInfo.fileName(); auto id = dirInfo.fileName();
out.append(id); out.append(id);
qInfo() << "Found instance ID" << id; qDebug() << "Found instance ID" << id;
} }
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
instanceSet = QSet<QString>(out.begin(), out.end()); instanceSet = QSet<QString>(out.begin(), out.end());
#else
instanceSet = out.toSet();
#endif
m_instancesProbed = true; m_instancesProbed = true;
return out; return out;
} }
@ -464,7 +468,7 @@ InstanceList::InstListError InstanceList::loadList()
if (existingIds.contains(id)) { if (existingIds.contains(id)) {
auto instPair = existingIds[id]; auto instPair = existingIds[id];
existingIds.remove(id); existingIds.remove(id);
qInfo() << "Should keep and soft-reload" << id; qDebug() << "Should keep and soft-reload" << id;
} else { } else {
InstancePtr instPtr = loadInstance(id); InstancePtr instPtr = loadInstance(id);
if (instPtr) { if (instPtr) {
@ -483,7 +487,7 @@ InstanceList::InstListError InstanceList::loadList()
int front_bookmark = -1; int front_bookmark = -1;
int back_bookmark = -1; int back_bookmark = -1;
int currentItem = -1; int currentItem = -1;
auto removeNow = [this, &front_bookmark, &back_bookmark, &currentItem]() { auto removeNow = [&]() {
beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark); beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark);
m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1); m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1);
endRemoveRows(); endRemoveRows();

View File

@ -43,8 +43,11 @@ class InstancePageProvider : protected QObject, public BasePageProvider {
values.append(new ServersPage(onesix)); values.append(new ServersPage(onesix));
// values.append(new GameOptionsPage(onesix.get())); // values.append(new GameOptionsPage(onesix.get()));
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots"))); values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
values.append(new InstanceSettingsPage(onesix)); values.append(new InstanceSettingsPage(onesix.get()));
values.append(new OtherLogsPage(inst)); auto logMatcher = inst->getLogFileMatcher();
if (logMatcher) {
values.append(new OtherLogsPage(inst->getLogFileRoot(), logMatcher));
}
return values; return values;
} }

View File

@ -41,9 +41,7 @@
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget* parent) bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget* parent)
{ {
static const QRegularExpression s_memRegex("-Xm[sx]"); if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegularExpression("-Xm[sx]")) || jvmargs.contains("-XX-MaxHeapSize") ||
static const QRegularExpression s_versionRegex("-version:.*");
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(s_memRegex) || jvmargs.contains("-XX-MaxHeapSize") ||
jvmargs.contains("-XX:InitialHeapSize")) { jvmargs.contains("-XX:InitialHeapSize")) {
auto warnStr = QObject::tr( auto warnStr = QObject::tr(
"You tried to manually set a JVM memory option (using \"-XX:PermSize\", \"-XX-MaxHeapSize\", \"-XX:InitialHeapSize\", \"-Xmx\" " "You tried to manually set a JVM memory option (using \"-XX:PermSize\", \"-XX-MaxHeapSize\", \"-XX:InitialHeapSize\", \"-Xmx\" "
@ -54,7 +52,7 @@ bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget* parent)
return false; return false;
} }
// block lunacy with passing required version to the JVM // block lunacy with passing required version to the JVM
if (jvmargs.contains(s_versionRegex)) { if (jvmargs.contains(QRegularExpression("-version:.*"))) {
auto warnStr = QObject::tr( auto warnStr = QObject::tr(
"You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be " "You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be "
"allowed.\n" "allowed.\n"

View File

@ -24,7 +24,7 @@ class TestCheck : public QObject {
TestCheck(QWidget* parent, QString path, QString args, int minMem, int maxMem, int permGen) TestCheck(QWidget* parent, QString path, QString args, int minMem, int maxMem, int permGen)
: m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen) : m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen)
{} {}
virtual ~TestCheck() = default; virtual ~TestCheck() {};
void run(); void run();

View File

@ -188,10 +188,10 @@ T ensureIsType(const QJsonObject& parent, const QString& key, const T default_ =
} }
template <typename T> template <typename T>
QList<T> requireIsArrayOf(const QJsonDocument& doc) QVector<T> requireIsArrayOf(const QJsonDocument& doc)
{ {
const QJsonArray array = requireArray(doc); const QJsonArray array = requireArray(doc);
QList<T> out; QVector<T> out;
for (const QJsonValue val : array) { for (const QJsonValue val : array) {
out.append(requireIsType<T>(val, "Document")); out.append(requireIsType<T>(val, "Document"));
} }
@ -199,10 +199,10 @@ QList<T> requireIsArrayOf(const QJsonDocument& doc)
} }
template <typename T> template <typename T>
QList<T> ensureIsArrayOf(const QJsonValue& value, const QString& what = "Value") QVector<T> ensureIsArrayOf(const QJsonValue& value, const QString& what = "Value")
{ {
const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what); const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what);
QList<T> out; QVector<T> out;
for (const QJsonValue val : array) { for (const QJsonValue val : array) {
out.append(requireIsType<T>(val, what)); out.append(requireIsType<T>(val, what));
} }
@ -210,7 +210,7 @@ QList<T> ensureIsArrayOf(const QJsonValue& value, const QString& what = "Value")
} }
template <typename T> template <typename T>
QList<T> ensureIsArrayOf(const QJsonValue& value, const QList<T> default_, const QString& what = "Value") QVector<T> ensureIsArrayOf(const QJsonValue& value, const QVector<T> default_, const QString& what = "Value")
{ {
if (value.isUndefined()) { if (value.isUndefined()) {
return default_; return default_;
@ -220,7 +220,7 @@ QList<T> ensureIsArrayOf(const QJsonValue& value, const QList<T> default_, const
/// @throw JsonException /// @throw JsonException
template <typename T> template <typename T>
QList<T> requireIsArrayOf(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__") QVector<T> requireIsArrayOf(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__")
{ {
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
if (!parent.contains(key)) { if (!parent.contains(key)) {
@ -230,9 +230,9 @@ QList<T> requireIsArrayOf(const QJsonObject& parent, const QString& key, const Q
} }
template <typename T> template <typename T>
QList<T> ensureIsArrayOf(const QJsonObject& parent, QVector<T> ensureIsArrayOf(const QJsonObject& parent,
const QString& key, const QString& key,
const QList<T>& default_ = QList<T>(), const QVector<T>& default_ = QVector<T>(),
const QString& what = "__placeholder__") const QString& what = "__placeholder__")
{ {
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');

View File

@ -43,7 +43,6 @@
#include "ui/InstanceWindow.h" #include "ui/InstanceWindow.h"
#include "ui/MainWindow.h" #include "ui/MainWindow.h"
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/MSALoginDialog.h"
#include "ui/dialogs/ProfileSelectDialog.h" #include "ui/dialogs/ProfileSelectDialog.h"
#include "ui/dialogs/ProfileSetupDialog.h" #include "ui/dialogs/ProfileSetupDialog.h"
#include "ui/dialogs/ProgressDialog.h" #include "ui/dialogs/ProgressDialog.h"
@ -182,8 +181,7 @@ void LaunchController::login()
auto name = askOfflineName("Player", m_demo, ok); auto name = askOfflineName("Player", m_demo, ok);
if (ok) { if (ok) {
m_session = std::make_shared<AuthSession>(); m_session = std::make_shared<AuthSession>();
static const QRegularExpression s_removeChars("[{}-]"); m_session->MakeDemo(name, MinecraftAccount::uuidFromUsername(name).toString().remove(QRegularExpression("[{}-]")));
m_session->MakeDemo(name, MinecraftAccount::uuidFromUsername(name).toString().remove(s_removeChars));
launchInstance(); launchInstance();
return; return;
} }
@ -201,7 +199,8 @@ void LaunchController::login()
m_accountToUse->shouldRefresh()) { m_accountToUse->shouldRefresh()) {
// Force account refresh on the account used to launch the instance updating the AccountState // 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 // only on first try and if it is not meant to be offline
m_accountToUse->refresh(); auto accounts = APPLICATION->accounts();
accounts->requestRefresh(m_accountToUse->internalId());
} }
while (tryagain) { while (tryagain) {
if (tries > 0 && tries % 3 == 0) { if (tries > 0 && tries % 3 == 0) {
@ -220,34 +219,13 @@ void LaunchController::login()
m_session->demo = m_demo; m_session->demo = m_demo;
m_accountToUse->fillSession(m_session); m_accountToUse->fillSession(m_session);
MinecraftAccountPtr accountToCheck; // Launch immediately in true offline mode
if (m_accountToUse->accountType() == AccountType::Offline) {
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(); launchInstance();
else
emitFailed(tr("Launch cancelled - account does not own Minecraft."));
return; return;
} }
switch (accountToCheck->accountState()) { switch (m_accountToUse->accountState()) {
case AccountState::Offline: { case AccountState::Offline: {
m_session->wants_online = false; m_session->wants_online = false;
} }
@ -256,19 +234,16 @@ void LaunchController::login()
if (!m_session->wants_online) { if (!m_session->wants_online) {
// we ask the user for a player name // we ask the user for a player name
bool ok = false; bool ok = false;
QString name; auto name = askOfflineName(m_session->player_name, m_session->demo, ok);
if (m_offlineName.isEmpty()) {
name = askOfflineName(m_session->player_name, m_session->demo, ok);
if (!ok) { if (!ok) {
tryagain = false; tryagain = false;
break; break;
} }
} else {
name = m_offlineName;
}
m_session->MakeOffline(name); m_session->MakeOffline(name);
// offline flavored game from here :3 // offline flavored game from here :3
} else if (m_accountToUse == accountToCheck && !m_accountToUse->hasProfile()) { }
if (m_accountToUse->ownsMinecraft()) {
if (!m_accountToUse->hasProfile()) {
// Now handle setting up a profile name here... // Now handle setting up a profile name here...
ProfileSetupDialog dialog(m_accountToUse, m_parentWidget); ProfileSetupDialog dialog(m_accountToUse, m_parentWidget);
if (dialog.exec() == QDialog::Accepted) { if (dialog.exec() == QDialog::Accepted) {
@ -279,18 +254,26 @@ void LaunchController::login()
return; 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! // we own Minecraft, there is a profile, it's all ready to go!
launchInstance(); launchInstance();
return; return;
} else {
// play demo ?
if (!m_session->demo) {
m_session->demo = askPlayDemo();
}
if (m_session->demo) { // play demo here
launchInstance();
} else {
emitFailed(tr("Launch cancelled - account does not own Minecraft."));
}
}
return;
} }
case AccountState::Errored: case AccountState::Errored:
// This means some sort of soft error that we can fix with a refresh ... so let's refresh. // This means some sort of soft error that we can fix with a refresh ... so let's refresh.
case AccountState::Unchecked: { case AccountState::Unchecked: {
accountToCheck->refresh(); m_accountToUse->refresh();
} }
/* fallthrough */ /* fallthrough */
case AccountState::Working: { case AccountState::Working: {
@ -299,19 +282,19 @@ void LaunchController::login()
if (m_online) { if (m_online) {
progDialog.setSkipButton(true, tr("Play Offline")); progDialog.setSkipButton(true, tr("Play Offline"));
} }
auto task = accountToCheck->currentTask(); auto task = m_accountToUse->currentTask();
progDialog.execWithTask(task.get()); progDialog.execWithTask(task.get());
continue; continue;
} }
case AccountState::Expired: { case AccountState::Expired: {
if (reauthenticateAccount(accountToCheck)) auto errorString = tr("The account has expired and needs to be logged into manually again.");
continue; QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), errorString, QMessageBox::StandardButton::Ok,
QMessageBox::StandardButton::Ok);
emitFailed(errorString);
return; return;
} }
case AccountState::Disabled: { case AccountState::Disabled: {
auto errorString = tr("The launcher's client identification has changed. Please remove '%1' and try again.") auto errorString = tr("The launcher's client identification has changed. Please remove this account and add it again.");
.arg(accountToCheck->profileName());
QMessageBox::warning(m_parentWidget, tr("Client identification changed"), errorString, QMessageBox::StandardButton::Ok, QMessageBox::warning(m_parentWidget, tr("Client identification changed"), errorString, QMessageBox::StandardButton::Ok,
QMessageBox::StandardButton::Ok); QMessageBox::StandardButton::Ok);
emitFailed(errorString); emitFailed(errorString);
@ -319,9 +302,8 @@ void LaunchController::login()
} }
case AccountState::Gone: { case AccountState::Gone: {
auto errorString = auto errorString =
tr("'%1' no longer exists on the servers. It may have been migrated, in which case please add the new account " tr("The account no longer exists on the servers. It may have been migrated, in which case please add the new account "
"you migrated this one to.") "you migrated this one to.");
.arg(accountToCheck->profileName());
QMessageBox::warning(m_parentWidget, tr("Account gone"), errorString, QMessageBox::StandardButton::Ok, QMessageBox::warning(m_parentWidget, tr("Account gone"), errorString, QMessageBox::StandardButton::Ok,
QMessageBox::StandardButton::Ok); QMessageBox::StandardButton::Ok);
emitFailed(errorString); emitFailed(errorString);
@ -332,38 +314,6 @@ void LaunchController::login()
emitFailed(tr("Failed to launch.")); emitFailed(tr("Failed to launch."));
} }
bool LaunchController::reauthenticateAccount(MinecraftAccountPtr account)
{
auto button = QMessageBox::warning(
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();
bool isDefault = accounts->defaultAccount() == account;
accounts->removeAccount(accounts->index(accounts->findAccountByProfileId(account->profileId())));
if (account->accountType() == AccountType::MSA) {
auto newAccount = MSALoginDialog::newAccount(m_parentWidget);
if (newAccount != nullptr) {
accounts->addAccount(newAccount);
if (isDefault)
accounts->setDefaultAccount(newAccount);
if (m_accountToUse == account) {
m_accountToUse = nullptr;
decideAccount();
}
return true;
}
}
}
emitFailed(tr("The account has expired and needs to be reauthenticated"));
return false;
}
void LaunchController::launchInstance() void LaunchController::launchInstance()
{ {
Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL"); Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL");

View File

@ -56,8 +56,6 @@ class LaunchController : public Task {
void setOnline(bool online) { m_online = online; } void setOnline(bool online) { m_online = online; }
void setOfflineName(const QString& offlineName) { m_offlineName = offlineName; }
void setDemo(bool demo) { m_demo = demo; } void setDemo(bool demo) { m_demo = demo; }
void setProfiler(BaseProfilerFactory* profiler) { m_profiler = profiler; } void setProfiler(BaseProfilerFactory* profiler) { m_profiler = profiler; }
@ -78,7 +76,6 @@ class LaunchController : public Task {
void decideAccount(); void decideAccount();
bool askPlayDemo(); bool askPlayDemo();
QString askOfflineName(QString playerName, bool demo, bool& ok); QString askOfflineName(QString playerName, bool demo, bool& ok);
bool reauthenticateAccount(MinecraftAccountPtr account);
private slots: private slots:
void readyForLaunch(); void readyForLaunch();
@ -90,7 +87,6 @@ class LaunchController : public Task {
private: private:
BaseProfilerFactory* m_profiler = nullptr; BaseProfilerFactory* m_profiler = nullptr;
bool m_online = true; bool m_online = true;
QString m_offlineName;
bool m_demo = false; bool m_demo = false;
InstancePtr m_instance; InstancePtr m_instance;
QWidget* m_parentWidget = nullptr; QWidget* m_parentWidget = nullptr;

View File

@ -418,7 +418,7 @@ bool extractFile(QString fileCompressed, QString file, QString target)
return extractRelFile(&zip, file, target); return extractRelFile(&zip, file, target);
} }
bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFileFunction excludeFilter) bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter)
{ {
QDir rootDirectory(rootDir); QDir rootDirectory(rootDir);
if (!rootDirectory.exists()) if (!rootDirectory.exists())
@ -443,8 +443,8 @@ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, Q
// collect files // collect files
entries = directory.entryInfoList(QDir::Files); entries = directory.entryInfoList(QDir::Files);
for (const auto& e : entries) { for (const auto& e : entries) {
if (excludeFilter && excludeFilter(e)) {
QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath()); QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath());
if (excludeFilter && excludeFilter(relativeFilePath)) {
qDebug() << "Skipping file " << relativeFilePath; qDebug() << "Skipping file " << relativeFilePath;
continue; continue;
} }

View File

@ -56,7 +56,6 @@
namespace MMCZip { namespace MMCZip {
using FilterFunction = std::function<bool(const QString&)>; using FilterFunction = std::function<bool(const QString&)>;
using FilterFileFunction = std::function<bool(const QFileInfo&)>;
/** /**
* Merge two zip files, using a filter function * Merge two zip files, using a filter function
@ -150,7 +149,7 @@ bool extractFile(QString fileCompressed, QString file, QString dir);
* \param excludeFilter function to excludeFilter which files shouldn't be included (returning true means to excude) * \param excludeFilter function to excludeFilter which files shouldn't be included (returning true means to excude)
* \return true for success or false for failure * \return true for success or false for failure
*/ */
bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFileFunction excludeFilter); bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter);
#if defined(LAUNCHER_APPLICATION) #if defined(LAUNCHER_APPLICATION)
class ExportToZipTask : public Task { class ExportToZipTask : public Task {

View File

@ -2,22 +2,19 @@
MessageLevel::Enum MessageLevel::getLevel(const QString& levelName) MessageLevel::Enum MessageLevel::getLevel(const QString& levelName)
{ {
QString name = levelName.toUpper(); if (levelName == "Launcher")
if (name == "LAUNCHER")
return MessageLevel::Launcher; return MessageLevel::Launcher;
else if (name == "TRACE") else if (levelName == "Debug")
return MessageLevel::Trace;
else if (name == "DEBUG")
return MessageLevel::Debug; return MessageLevel::Debug;
else if (name == "INFO") else if (levelName == "Info")
return MessageLevel::Info; return MessageLevel::Info;
else if (name == "MESSAGE") else if (levelName == "Message")
return MessageLevel::Message; return MessageLevel::Message;
else if (name == "WARNING" || name == "WARN") else if (levelName == "Warning")
return MessageLevel::Warning; return MessageLevel::Warning;
else if (name == "ERROR") else if (levelName == "Error")
return MessageLevel::Error; return MessageLevel::Error;
else if (name == "FATAL") else if (levelName == "Fatal")
return MessageLevel::Fatal; return MessageLevel::Fatal;
// Skip PrePost, it's not exposed to !![]! // Skip PrePost, it's not exposed to !![]!
// Also skip StdErr and StdOut // Also skip StdErr and StdOut

View File

@ -12,7 +12,6 @@ enum Enum {
StdOut, /**< Undetermined stderr messages */ StdOut, /**< Undetermined stderr messages */
StdErr, /**< Undetermined stdout messages */ StdErr, /**< Undetermined stdout messages */
Launcher, /**< Launcher Messages */ Launcher, /**< Launcher Messages */
Trace, /**< Trace Messages */
Debug, /**< Debug Messages */ Debug, /**< Debug Messages */
Info, /**< Info Messages */ Info, /**< Info Messages */
Message, /**< Standard Messages */ Message, /**< Standard Messages */

View File

@ -57,7 +57,8 @@ class NullInstance : public BaseInstance {
QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); } QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); }
QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); } QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); }
QMap<QString, QString> getVariables() override { return QMap<QString, QString>(); } QMap<QString, QString> getVariables() override { return QMap<QString, QString>(); }
QStringList getLogFileSearchPaths() override { return {}; } IPathMatcher::Ptr getLogFileMatcher() override { return nullptr; }
QString getLogFileRoot() override { return instanceRoot(); }
QString typeName() const override { return "Null"; } QString typeName() const override { return "Null"; }
bool canExport() const override { return false; } bool canExport() const override { return false; }
bool canEdit() const override { return false; } bool canEdit() const override { return false; }

View File

@ -33,7 +33,7 @@ class shared_qobject_ptr : public QSharedPointer<T> {
{} {}
void reset() { QSharedPointer<T>::reset(); } void reset() { QSharedPointer<T>::reset(); }
void reset(T* other) void reset(T*&& other)
{ {
shared_qobject_ptr<T> t(other); shared_qobject_ptr<T> t(other);
this->swap(t); this->swap(t);

View File

@ -1,6 +1,7 @@
#include "RecursiveFileSystemWatcher.h" #include "RecursiveFileSystemWatcher.h"
#include <QDebug> #include <QDebug>
#include <QRegularExpression>
RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject* parent) : QObject(parent), m_watcher(new QFileSystemWatcher(this)) RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject* parent) : QObject(parent), m_watcher(new QFileSystemWatcher(this))
{ {

View File

@ -35,9 +35,9 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
QString custom_target_folder) QString custom_target_folder)
: m_pack(std::move(pack)), m_pack_version(std::move(version)), m_pack_model(packs), m_custom_target_folder(custom_target_folder) : m_pack(std::move(pack)), m_pack_version(std::move(version)), m_pack_model(packs), m_custom_target_folder(custom_target_folder)
{ {
if (is_indexed) { if (auto model = dynamic_cast<ModFolderModel*>(m_pack_model.get()); model && is_indexed) {
m_update_task.reset(new LocalResourceUpdateTask(m_pack_model->indexDir(), *m_pack, m_pack_version)); m_update_task.reset(new LocalModUpdateTask(model->indexDir(), *m_pack, m_pack_version));
connect(m_update_task.get(), &LocalResourceUpdateTask::hasOldResource, this, &ResourceDownloadTask::hasOldResource); connect(m_update_task.get(), &LocalModUpdateTask::hasOldMod, this, &ResourceDownloadTask::hasOldResource);
addTask(m_update_task); addTask(m_update_task);
} }
@ -91,8 +91,12 @@ void ResourceDownloadTask::downloadSucceeded()
m_filesNetJob.reset(); m_filesNetJob.reset();
auto name = std::get<0>(to_delete); auto name = std::get<0>(to_delete);
auto filename = std::get<1>(to_delete); auto filename = std::get<1>(to_delete);
if (!name.isEmpty() && filename != m_pack_version.fileName) if (!name.isEmpty() && filename != m_pack_version.fileName) {
m_pack_model->uninstallResource(filename, true); if (auto model = dynamic_cast<ModFolderModel*>(m_pack_model.get()); model)
model->uninstallMod(filename, true);
else
m_pack_model->uninstallResource(filename);
}
} }
void ResourceDownloadTask::downloadFailed(QString reason) void ResourceDownloadTask::downloadFailed(QString reason)

View File

@ -22,7 +22,7 @@
#include "net/NetJob.h" #include "net/NetJob.h"
#include "tasks/SequentialTask.h" #include "tasks/SequentialTask.h"
#include "minecraft/mod/tasks/LocalResourceUpdateTask.h" #include "minecraft/mod/tasks/LocalModUpdateTask.h"
#include "modplatform/ModIndex.h" #include "modplatform/ModIndex.h"
class ResourceFolderModel; class ResourceFolderModel;
@ -50,7 +50,7 @@ class ResourceDownloadTask : public SequentialTask {
QString m_custom_target_folder; QString m_custom_target_folder;
NetJob::Ptr m_filesNetJob; NetJob::Ptr m_filesNetJob;
LocalResourceUpdateTask::Ptr m_update_task; LocalModUpdateTask::Ptr m_update_task;
void downloadProgressChanged(qint64 current, qint64 total); void downloadProgressChanged(qint64 current, qint64 total);
void downloadFailed(QString reason); void downloadFailed(QString reason);

View File

@ -53,7 +53,7 @@ static inline QChar getNextChar(const QString& s, int location)
int StringUtils::naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs) int StringUtils::naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs)
{ {
int l1 = 0, l2 = 0; int l1 = 0, l2 = 0;
while (l1 <= s1.size() && l2 <= s2.size()) { while (l1 <= s1.count() && l2 <= s2.count()) {
// skip spaces, tabs and 0's // skip spaces, tabs and 0's
QChar c1 = getNextChar(s1, l1); QChar c1 = getNextChar(s1, l1);
while (c1.isSpace()) while (c1.isSpace())
@ -213,10 +213,11 @@ QPair<QString, QString> StringUtils::splitFirst(const QString& s, const QRegular
return qMakePair(left, right); return qMakePair(left, right);
} }
static const QRegularExpression ulMatcher("<\\s*/\\s*ul\\s*>");
QString StringUtils::htmlListPatch(QString htmlStr) QString StringUtils::htmlListPatch(QString htmlStr)
{ {
static const QRegularExpression s_ulMatcher("<\\s*/\\s*ul\\s*>"); int pos = htmlStr.indexOf(ulMatcher);
int pos = htmlStr.indexOf(s_ulMatcher);
int imgPos; int imgPos;
while (pos != -1) { while (pos != -1) {
pos = htmlStr.indexOf(">", pos) + 1; // Get the size of the </ul> tag. Add one for zeroeth index pos = htmlStr.indexOf(">", pos) + 1; // Get the size of the </ul> tag. Add one for zeroeth index
@ -229,7 +230,7 @@ QString StringUtils::htmlListPatch(QString htmlStr)
if (textBetween.isEmpty()) if (textBetween.isEmpty())
htmlStr.insert(pos, "<br>"); htmlStr.insert(pos, "<br>");
pos = htmlStr.indexOf(s_ulMatcher, pos); pos = htmlStr.indexOf(ulMatcher, pos);
} }
return htmlStr; return htmlStr;
} }

View File

@ -1,6 +1,7 @@
#include "Version.h" #include "Version.h"
#include <QDebug> #include <QDebug>
#include <QRegularExpression>
#include <QRegularExpressionMatch> #include <QRegularExpressionMatch>
#include <QUrl> #include <QUrl>
@ -78,7 +79,7 @@ void Version::parse()
if (m_string.isEmpty()) if (m_string.isEmpty())
return; return;
auto classChange = [&currentSection](QChar lastChar, QChar currentChar) { auto classChange = [&](QChar lastChar, QChar currentChar) {
if (lastChar.isNull()) if (lastChar.isNull())
return false; return false;
if (lastChar.isDigit() != currentChar.isDigit()) if (lastChar.isDigit() != currentChar.isDigit())

View File

@ -72,14 +72,22 @@ class Version {
} }
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
auto numPart = QStringView{ m_fullString }.left(cutoff); auto numPart = QStringView{ m_fullString }.left(cutoff);
#else
auto numPart = m_fullString.leftRef(cutoff);
#endif
if (!numPart.isEmpty()) { if (!numPart.isEmpty()) {
m_isNull = false; m_isNull = false;
m_numPart = numPart.toInt(); m_numPart = numPart.toInt();
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
auto stringPart = QStringView{ m_fullString }.mid(cutoff); auto stringPart = QStringView{ m_fullString }.mid(cutoff);
#else
auto stringPart = m_fullString.midRef(cutoff);
#endif
if (!stringPart.isEmpty()) { if (!stringPart.isEmpty()) {
m_isNull = false; m_isNull = false;

View File

@ -193,8 +193,8 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
if (value.toBool()) { if (value.toBool()) {
return tr("Recommended"); return tr("Recommended");
} else if (hasLatest) { } else if (hasLatest) {
auto latest = sourceModel()->data(parentIndex, BaseVersionList::LatestRole); auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
if (latest.toBool()) { if (value.toBool()) {
return tr("Latest"); return tr("Latest");
} }
} }
@ -203,7 +203,9 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
} }
} }
case Qt::DecorationRole: { case Qt::DecorationRole: {
if (column == Name && hasRecommended) { switch (column) {
case Name: {
if (hasRecommended) {
auto recommenced = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole); auto recommenced = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
if (recommenced.toBool()) { if (recommenced.toBool()) {
return APPLICATION->getThemedIcon("star"); return APPLICATION->getThemedIcon("star");
@ -223,8 +225,12 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
} }
return pixmap; return pixmap;
} }
}
default: {
return QVariant(); return QVariant();
} }
}
}
default: { default: {
if (roles.contains((BaseVersionList::ModelRoles)role)) { if (roles.contains((BaseVersionList::ModelRoles)role)) {
return sourceModel()->data(parentIndex, role); return sourceModel()->data(parentIndex, role);
@ -295,11 +301,13 @@ void VersionProxyModel::sourceDataChanged(const QModelIndex& source_top_left, co
void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw) void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw)
{ {
auto replacing = dynamic_cast<BaseVersionList*>(replacingRaw); auto replacing = dynamic_cast<BaseVersionList*>(replacingRaw);
beginResetModel();
m_columns.clear(); m_columns.clear();
if (!replacing) { if (!replacing) {
roles.clear(); roles.clear();
filterModel->setSourceModel(replacing); filterModel->setSourceModel(replacing);
endResetModel();
return; return;
} }
@ -341,6 +349,8 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw)
hasLatest = true; hasLatest = true;
} }
filterModel->setSourceModel(replacing); filterModel->setSourceModel(replacing);
endResetModel();
} }
QModelIndex VersionProxyModel::getRecommended() const QModelIndex VersionProxyModel::getRecommended() const

View File

@ -16,18 +16,13 @@
* *
*/ */
#include "WindowsConsole.h"
#include <system_error>
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif #endif
#include <windows.h>
#include <fcntl.h> #include <fcntl.h>
#include <io.h> #include <io.h>
#include <stdio.h> #include <stdio.h>
#include <cstddef> #include <windows.h>
#include <iostream> #include <iostream>
void RedirectHandle(DWORD handle, FILE* stream, const char* mode) void RedirectHandle(DWORD handle, FILE* stream, const char* mode)
@ -131,29 +126,3 @@ bool AttachWindowsConsole()
return false; return false;
} }
std::error_code EnableAnsiSupport()
{
// ref: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
// Using `CreateFileW("CONOUT$", ...)` to retrieve the console handle works correctly even if STDOUT and/or STDERR are redirected
HANDLE console_handle = CreateFileW(L"CONOUT$", FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
if (console_handle == INVALID_HANDLE_VALUE) {
return std::error_code(GetLastError(), std::system_category());
}
// ref: https://docs.microsoft.com/en-us/windows/console/getconsolemode
DWORD console_mode;
if (0 == GetConsoleMode(console_handle, &console_mode)) {
return std::error_code(GetLastError(), std::system_category());
}
// VT processing not already enabled?
if ((console_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0) {
// https://docs.microsoft.com/en-us/windows/console/setconsolemode
if (0 == SetConsoleMode(console_handle, console_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {
return std::error_code(GetLastError(), std::system_category());
}
}
return {};
}

View File

@ -21,8 +21,5 @@
#pragma once #pragma once
#include <system_error>
void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr); void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr);
bool AttachWindowsConsole(); bool AttachWindowsConsole();
std::error_code EnableAnsiSupport();

View File

@ -1,33 +0,0 @@
#pragma once
#include <QString>
#include <ostream>
#if defined Q_OS_WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#else
#include <unistd.h>
#include <cstdio>
#endif
namespace console {
inline bool isConsole()
{
#if defined Q_OS_WIN32
DWORD procIDs[2];
DWORD maxCount = 2;
DWORD result = GetConsoleProcessList((LPDWORD)procIDs, maxCount);
return result > 1;
#else
if (isatty(fileno(stdout))) {
return true;
}
return false;
#endif
}
} // namespace console

View File

@ -37,14 +37,27 @@
#include <sys.h> #include <sys.h>
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
#ifndef WIN32_LEAN_AND_MEAN #include "WindowsConsole.h"
#define WIN32_LEAN_AND_MEAN
#endif
#include "console/WindowsConsole.h"
#endif #endif
// Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header
#ifdef __APPLE__
#include <Availability.h> // for deployment target to support pre-catalina targets without std::fs
#endif // __APPLE__
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS
#include <filesystem> #include <filesystem>
namespace fs = std::filesystem; namespace fs = std::filesystem;
#endif // MacOS min version check
#endif // Other OSes version check
#ifndef GHC_USE_STD_FS
#include <ghc/filesystem.hpp>
namespace fs = ghc::filesystem;
#endif
FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv), socket(new QLocalSocket(this)) FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv), socket(new QLocalSocket(this))
{ {
@ -91,11 +104,11 @@ void FileLinkApp::joinServer(QString server)
in.setDevice(&socket); in.setDevice(&socket);
connect(&socket, &QLocalSocket::connected, this, []() { qDebug() << "connected to server"; }); connect(&socket, &QLocalSocket::connected, this, [&]() { qDebug() << "connected to server"; });
connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs); connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs);
connect(&socket, &QLocalSocket::errorOccurred, this, [this](QLocalSocket::LocalSocketError socketError) { connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError) {
m_status = Failed; m_status = Failed;
switch (socketError) { switch (socketError) {
case QLocalSocket::ServerNotFoundError: case QLocalSocket::ServerNotFoundError:
@ -119,7 +132,7 @@ void FileLinkApp::joinServer(QString server)
} }
}); });
connect(&socket, &QLocalSocket::disconnected, this, [this]() { connect(&socket, &QLocalSocket::disconnected, this, [&]() {
qDebug() << "disconnected from server, should exit"; qDebug() << "disconnected from server, should exit";
m_status = Succeeded; m_status = Succeeded;
exit(); exit();

View File

@ -37,6 +37,7 @@
#include "IconList.h" #include "IconList.h"
#include <FileSystem.h> #include <FileSystem.h>
#include <QDebug> #include <QDebug>
#include <QEventLoop>
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
#include <QMap> #include <QMap>
#include <QMimeData> #include <QMimeData>
@ -46,24 +47,24 @@
#define MAX_SIZE 1024 #define MAX_SIZE 1024
IconList::IconList(const QStringList& builtinPaths, const QString& path, QObject* parent) : QAbstractListModel(parent) IconList::IconList(const QStringList& builtinPaths, QString path, QObject* parent) : QAbstractListModel(parent)
{ {
QSet<QString> builtinNames; QSet<QString> builtinNames;
// add builtin icons // add builtin icons
for (const auto& builtinPath : builtinPaths) { for (auto& builtinPath : builtinPaths) {
QDir instanceIcons(builtinPath); QDir instance_icons(builtinPath);
auto fileInfoList = instanceIcons.entryInfoList(QDir::Files, QDir::Name); auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name);
for (const auto& fileInfo : fileInfoList) { for (auto file_info : file_info_list) {
builtinNames.insert(fileInfo.baseName()); builtinNames.insert(file_info.completeBaseName());
} }
} }
for (const auto& builtinName : builtinNames) { for (auto& builtinName : builtinNames) {
addThemeIcon(builtinName); addThemeIcon(builtinName);
} }
m_watcher.reset(new QFileSystemWatcher()); m_watcher.reset(new QFileSystemWatcher());
m_isWatching = false; is_watching = false;
connect(m_watcher.get(), &QFileSystemWatcher::directoryChanged, this, &IconList::directoryChanged); connect(m_watcher.get(), &QFileSystemWatcher::directoryChanged, this, &IconList::directoryChanged);
connect(m_watcher.get(), &QFileSystemWatcher::fileChanged, this, &IconList::fileChanged); connect(m_watcher.get(), &QFileSystemWatcher::fileChanged, this, &IconList::fileChanged);
@ -76,129 +77,91 @@ IconList::IconList(const QStringList& builtinPaths, const QString& path, QObject
void IconList::sortIconList() void IconList::sortIconList()
{ {
qDebug() << "Sorting icon list..."; qDebug() << "Sorting icon list...";
std::sort(m_icons.begin(), m_icons.end(), [](const MMCIcon& a, const MMCIcon& b) { std::sort(icons.begin(), icons.end(), [](const MMCIcon& a, const MMCIcon& b) { return a.m_key.localeAwareCompare(b.m_key) < 0; });
bool aIsSubdir = a.m_key.contains(QDir::separator());
bool bIsSubdir = b.m_key.contains(QDir::separator());
if (aIsSubdir != bIsSubdir) {
return !aIsSubdir; // root-level icons come first
}
return a.m_key.localeAwareCompare(b.m_key) < 0;
});
reindex(); reindex();
} }
// Helper function to add directories recursively
bool IconList::addPathRecursively(const QString& path)
{
QDir dir(path);
if (!dir.exists())
return false;
// Add the directory itself
bool watching = m_watcher->addPath(path);
// Add all subdirectories
QFileInfoList entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
for (const QFileInfo& entry : entries) {
if (addPathRecursively(entry.absoluteFilePath())) {
watching = true;
}
}
return watching;
}
QStringList IconList::getIconFilePaths() const
{
QStringList iconFiles{};
QStringList directories{ m_dir.absolutePath() };
while (!directories.isEmpty()) {
QString first = directories.takeFirst();
QDir dir(first);
for (QFileInfo& fileInfo : dir.entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot, QDir::Name)) {
if (fileInfo.isDir())
directories.push_back(fileInfo.absoluteFilePath());
else
iconFiles.push_back(fileInfo.absoluteFilePath());
}
}
return iconFiles;
}
QString formatName(const QDir& iconsDir, const QFileInfo& iconFile)
{
if (iconFile.dir() == iconsDir)
return iconFile.baseName();
constexpr auto delimiter = " » ";
QString relativePathWithoutExtension = iconsDir.relativeFilePath(iconFile.dir().path()) + QDir::separator() + iconFile.baseName();
return relativePathWithoutExtension.replace(QDir::separator(), delimiter);
}
/// Split into a separate function because the preprocessing impedes readability
QSet<QString> toStringSet(const QList<QString>& list)
{
QSet<QString> set(list.begin(), list.end());
return set;
}
void IconList::directoryChanged(const QString& path) void IconList::directoryChanged(const QString& path)
{ {
QDir newDir(path); QDir new_dir(path);
if (m_dir.absolutePath() != newDir.absolutePath()) { if (m_dir.absolutePath() != new_dir.absolutePath()) {
if (!path.startsWith(m_dir.absolutePath()))
m_dir.setPath(path); m_dir.setPath(path);
m_dir.refresh(); m_dir.refresh();
if (m_isWatching) if (is_watching)
stopWatching(); stopWatching();
startWatching(); startWatching();
} }
if (!m_dir.exists() && !FS::ensureFolderPathExists(m_dir.absolutePath())) if (!m_dir.exists())
if (!FS::ensureFolderPathExists(m_dir.absolutePath()))
return; return;
m_dir.refresh(); m_dir.refresh();
const QStringList newFileNamesList = getIconFilePaths(); auto new_list = m_dir.entryList(QDir::Files, QDir::Name);
const QSet<QString> newSet = toStringSet(newFileNamesList); for (auto it = new_list.begin(); it != new_list.end(); it++) {
QSet<QString> currentSet; QString& foo = (*it);
for (const MMCIcon& it : m_icons) { foo = m_dir.filePath(foo);
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QSet<QString> new_set(new_list.begin(), new_list.end());
#else
auto new_set = new_list.toSet();
#endif
QList<QString> current_list;
for (auto& it : icons) {
if (!it.has(IconType::FileBased)) if (!it.has(IconType::FileBased))
continue; continue;
QFileInfo icon(it.getFilePath()); current_list.push_back(it.m_images[IconType::FileBased].filename);
currentSet.insert(icon.absoluteFilePath());
} }
QSet<QString> toRemove = currentSet - newSet; #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QSet<QString> toAdd = newSet - currentSet; QSet<QString> current_set(current_list.begin(), current_list.end());
#else
QSet<QString> current_set = current_list.toSet();
#endif
for (const QString& removedPath : toRemove) { QSet<QString> to_remove = current_set;
qDebug() << "Removing icon " << removedPath; to_remove -= new_set;
QFileInfo removedFile(removedPath);
QString relativePath = m_dir.relativeFilePath(removedFile.absoluteFilePath()); QSet<QString> to_add = new_set;
QString key = QFileInfo(relativePath).completeBaseName(); to_add -= current_set;
for (auto remove : to_remove) {
qDebug() << "Removing " << remove;
QFileInfo rmfile(remove);
QString key = rmfile.completeBaseName();
QString suffix = rmfile.suffix();
// The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well
if (!IconUtils::isIconSuffix(suffix))
key = rmfile.fileName();
int idx = getIconIndex(key); int idx = getIconIndex(key);
if (idx == -1) if (idx == -1)
continue; continue;
m_icons[idx].remove(FileBased); icons[idx].remove(IconType::FileBased);
if (m_icons[idx].type() == ToBeDeleted) { if (icons[idx].type() == IconType::ToBeDeleted) {
beginRemoveRows(QModelIndex(), idx, idx); beginRemoveRows(QModelIndex(), idx, idx);
m_icons.remove(idx); icons.remove(idx);
reindex(); reindex();
endRemoveRows(); endRemoveRows();
} else { } else {
dataChanged(index(idx), index(idx)); dataChanged(index(idx), index(idx));
} }
m_watcher->removePath(removedPath); m_watcher->removePath(remove);
emit iconUpdated(key); emit iconUpdated(key);
} }
for (const QString& addedPath : toAdd) { for (auto add : to_add) {
qDebug() << "Adding icon " << addedPath; qDebug() << "Adding " << add;
QFileInfo addfile(addedPath); QFileInfo addfile(add);
QString relativePath = m_dir.relativeFilePath(addfile.absoluteFilePath()); QString key = addfile.completeBaseName();
QString key = QFileInfo(relativePath).completeBaseName();
QString name = formatName(m_dir, addfile);
if (addIcon(key, name, addfile.filePath(), IconType::FileBased)) { QString suffix = addfile.suffix();
m_watcher->addPath(addedPath); // The icon doesnt have a suffix, but it can have other .s in the name, so we account for those as well
if (!IconUtils::isIconSuffix(suffix))
key = addfile.fileName();
if (addIcon(key, QString(), addfile.filePath(), IconType::FileBased)) {
m_watcher->addPath(add);
emit iconUpdated(key); emit iconUpdated(key);
} }
} }
@ -208,24 +171,24 @@ void IconList::directoryChanged(const QString& path)
void IconList::fileChanged(const QString& path) void IconList::fileChanged(const QString& path)
{ {
qDebug() << "Checking icon " << path; qDebug() << "Checking " << path;
QFileInfo checkfile(path); QFileInfo checkfile(path);
if (!checkfile.exists()) if (!checkfile.exists())
return; return;
QString key = m_dir.relativeFilePath(checkfile.absoluteFilePath()); QString key = checkfile.completeBaseName();
int idx = getIconIndex(key); int idx = getIconIndex(key);
if (idx == -1) if (idx == -1)
return; return;
QIcon icon(path); QIcon icon(path);
if (icon.availableSizes().empty()) if (!icon.availableSizes().size())
return; return;
m_icons[idx].m_images[IconType::FileBased].icon = icon; icons[idx].m_images[IconType::FileBased].icon = icon;
dataChanged(index(idx), index(idx)); dataChanged(index(idx), index(idx));
emit iconUpdated(key); emit iconUpdated(key);
} }
void IconList::SettingChanged(const Setting& setting, const QVariant& value) void IconList::SettingChanged(const Setting& setting, QVariant value)
{ {
if (setting.id() != "IconsDir") if (setting.id() != "IconsDir")
return; return;
@ -237,8 +200,8 @@ void IconList::startWatching()
{ {
auto abs_path = m_dir.absolutePath(); auto abs_path = m_dir.absolutePath();
FS::ensureFolderPathExists(abs_path); FS::ensureFolderPathExists(abs_path);
m_isWatching = addPathRecursively(abs_path); is_watching = m_watcher->addPath(abs_path);
if (m_isWatching) { if (is_watching) {
qDebug() << "Started watching " << abs_path; qDebug() << "Started watching " << abs_path;
} else { } else {
qDebug() << "Failed to start watching " << abs_path; qDebug() << "Failed to start watching " << abs_path;
@ -249,7 +212,7 @@ void IconList::stopWatching()
{ {
m_watcher->removePaths(m_watcher->files()); m_watcher->removePaths(m_watcher->files());
m_watcher->removePaths(m_watcher->directories()); m_watcher->removePaths(m_watcher->directories());
m_isWatching = false; is_watching = false;
} }
QStringList IconList::mimeTypes() const QStringList IconList::mimeTypes() const
@ -279,7 +242,7 @@ bool IconList::dropMimeData(const QMimeData* data,
if (data->hasUrls()) { if (data->hasUrls()) {
auto urls = data->urls(); auto urls = data->urls();
QStringList iconFiles; QStringList iconFiles;
for (const auto& url : urls) { for (auto url : urls) {
// only local files may be dropped... // only local files may be dropped...
if (!url.isLocalFile()) if (!url.isLocalFile())
continue; continue;
@ -300,33 +263,33 @@ Qt::ItemFlags IconList::flags(const QModelIndex& index) const
QVariant IconList::data(const QModelIndex& index, int role) const QVariant IconList::data(const QModelIndex& index, int role) const
{ {
if (!index.isValid()) if (!index.isValid())
return {}; return QVariant();
int row = index.row(); int row = index.row();
if (row < 0 || row >= m_icons.size()) if (row < 0 || row >= icons.size())
return {}; return QVariant();
switch (role) { switch (role) {
case Qt::DecorationRole: case Qt::DecorationRole:
return m_icons[row].icon(); return icons[row].icon();
case Qt::DisplayRole: case Qt::DisplayRole:
return m_icons[row].name(); return icons[row].name();
case Qt::UserRole: case Qt::UserRole:
return m_icons[row].m_key; return icons[row].m_key;
default: default:
return {}; return QVariant();
} }
} }
int IconList::rowCount(const QModelIndex& parent) const int IconList::rowCount(const QModelIndex& parent) const
{ {
return parent.isValid() ? 0 : m_icons.size(); return parent.isValid() ? 0 : icons.size();
} }
void IconList::installIcons(const QStringList& iconFiles) void IconList::installIcons(const QStringList& iconFiles)
{ {
for (const QString& file : iconFiles) for (QString file : iconFiles)
installIcon(file, {}); installIcon(file, {});
} }
@ -349,13 +312,12 @@ bool IconList::iconFileExists(const QString& key) const
return iconEntry && iconEntry->has(IconType::FileBased); return iconEntry && iconEntry->has(IconType::FileBased);
} }
/// Returns the icon with the given key or nullptr if it doesn't exist.
const MMCIcon* IconList::icon(const QString& key) const const MMCIcon* IconList::icon(const QString& key) const
{ {
int iconIdx = getIconIndex(key); int iconIdx = getIconIndex(key);
if (iconIdx == -1) if (iconIdx == -1)
return nullptr; return nullptr;
return &m_icons[iconIdx]; return &icons[iconIdx];
} }
bool IconList::deleteIcon(const QString& key) bool IconList::deleteIcon(const QString& key)
@ -370,22 +332,22 @@ bool IconList::trashIcon(const QString& key)
bool IconList::addThemeIcon(const QString& key) bool IconList::addThemeIcon(const QString& key)
{ {
auto iter = m_nameIndex.find(key); auto iter = name_index.find(key);
if (iter != m_nameIndex.end()) { if (iter != name_index.end()) {
auto& oldOne = m_icons[*iter]; auto& oldOne = icons[*iter];
oldOne.replace(Builtin, key); oldOne.replace(Builtin, key);
dataChanged(index(*iter), index(*iter)); dataChanged(index(*iter), index(*iter));
return true; return true;
} }
// add a new icon // add a new icon
beginInsertRows(QModelIndex(), m_icons.size(), m_icons.size()); beginInsertRows(QModelIndex(), icons.size(), icons.size());
{ {
MMCIcon mmc_icon; MMCIcon mmc_icon;
mmc_icon.m_name = key; mmc_icon.m_name = key;
mmc_icon.m_key = key; mmc_icon.m_key = key;
mmc_icon.replace(Builtin, key); mmc_icon.replace(Builtin, key);
m_icons.push_back(mmc_icon); icons.push_back(mmc_icon);
m_nameIndex[key] = m_icons.size() - 1; name_index[key] = icons.size() - 1;
} }
endInsertRows(); endInsertRows();
return true; return true;
@ -397,22 +359,22 @@ bool IconList::addIcon(const QString& key, const QString& name, const QString& p
QIcon icon(path); QIcon icon(path);
if (icon.isNull()) if (icon.isNull())
return false; return false;
auto iter = m_nameIndex.find(key); auto iter = name_index.find(key);
if (iter != m_nameIndex.end()) { if (iter != name_index.end()) {
auto& oldOne = m_icons[*iter]; auto& oldOne = icons[*iter];
oldOne.replace(type, icon, path); oldOne.replace(type, icon, path);
dataChanged(index(*iter), index(*iter)); dataChanged(index(*iter), index(*iter));
return true; return true;
} }
// add a new icon // add a new icon
beginInsertRows(QModelIndex(), m_icons.size(), m_icons.size()); beginInsertRows(QModelIndex(), icons.size(), icons.size());
{ {
MMCIcon mmc_icon; MMCIcon mmc_icon;
mmc_icon.m_name = name; mmc_icon.m_name = name;
mmc_icon.m_key = key; mmc_icon.m_key = key;
mmc_icon.replace(type, icon, path); mmc_icon.replace(type, icon, path);
m_icons.push_back(mmc_icon); icons.push_back(mmc_icon);
m_nameIndex[key] = m_icons.size() - 1; name_index[key] = icons.size() - 1;
} }
endInsertRows(); endInsertRows();
return true; return true;
@ -427,32 +389,33 @@ void IconList::saveIcon(const QString& key, const QString& path, const char* for
void IconList::reindex() void IconList::reindex()
{ {
m_nameIndex.clear(); name_index.clear();
for (int i = 0; i < m_icons.size(); i++) { int i = 0;
m_nameIndex[m_icons[i].m_key] = i; for (auto& iter : icons) {
emit iconUpdated(m_icons[i].m_key); // prevents incorrect indices with proxy model name_index[iter.m_key] = i;
i++;
} }
} }
QIcon IconList::getIcon(const QString& key) const QIcon IconList::getIcon(const QString& key) const
{ {
int iconIndex = getIconIndex(key); int icon_index = getIconIndex(key);
if (iconIndex != -1) if (icon_index != -1)
return m_icons[iconIndex].icon(); return icons[icon_index].icon();
// Fallback for icons that don't exist.b // Fallback for icons that don't exist.
iconIndex = getIconIndex("grass"); icon_index = getIconIndex("grass");
if (iconIndex != -1) if (icon_index != -1)
return m_icons[iconIndex].icon(); return icons[icon_index].icon();
return {}; return QIcon();
} }
int IconList::getIconIndex(const QString& key) const int IconList::getIconIndex(const QString& key) const
{ {
auto iter = m_nameIndex.find(key == "default" ? "grass" : key); auto iter = name_index.find(key == "default" ? "grass" : key);
if (iter != m_nameIndex.end()) if (iter != name_index.end())
return *iter; return *iter;
return -1; return -1;
@ -462,15 +425,3 @@ QString IconList::getDirectory() const
{ {
return m_dir.absolutePath(); return m_dir.absolutePath();
} }
/// Returns the directory of the icon with the given key or the default directory if it's a builtin icon.
QString IconList::iconDirectory(const QString& key) const
{
for (const auto& mmcIcon : m_icons) {
if (mmcIcon.m_key == key && mmcIcon.has(IconType::FileBased)) {
QFileInfo iconFile(mmcIcon.getFilePath());
return iconFile.dir().path();
}
}
return getDirectory();
}

View File

@ -51,7 +51,7 @@ class QFileSystemWatcher;
class IconList : public QAbstractListModel { class IconList : public QAbstractListModel {
Q_OBJECT Q_OBJECT
public: public:
explicit IconList(const QStringList& builtinPaths, const QString& path, QObject* parent = 0); explicit IconList(const QStringList& builtinPaths, QString path, QObject* parent = 0);
virtual ~IconList() {}; virtual ~IconList() {};
QIcon getIcon(const QString& key) const; QIcon getIcon(const QString& key) const;
@ -72,7 +72,6 @@ class IconList : public QAbstractListModel {
bool deleteIcon(const QString& key); bool deleteIcon(const QString& key);
bool trashIcon(const QString& key); bool trashIcon(const QString& key);
bool iconFileExists(const QString& key) const; bool iconFileExists(const QString& key) const;
QString iconDirectory(const QString& key) const;
void installIcons(const QStringList& iconFiles); void installIcons(const QStringList& iconFiles);
void installIcon(const QString& file, const QString& name); void installIcon(const QString& file, const QString& name);
@ -92,20 +91,18 @@ class IconList : public QAbstractListModel {
IconList& operator=(const IconList&) = delete; IconList& operator=(const IconList&) = delete;
void reindex(); void reindex();
void sortIconList(); void sortIconList();
bool addPathRecursively(const QString& path);
QStringList getIconFilePaths() const;
public slots: public slots:
void directoryChanged(const QString& path); void directoryChanged(const QString& path);
protected slots: protected slots:
void fileChanged(const QString& path); void fileChanged(const QString& path);
void SettingChanged(const Setting& setting, const QVariant& value); void SettingChanged(const Setting& setting, QVariant value);
private: private:
shared_qobject_ptr<QFileSystemWatcher> m_watcher; shared_qobject_ptr<QFileSystemWatcher> m_watcher;
bool m_isWatching; bool is_watching;
QMap<QString, int> m_nameIndex; QMap<QString, int> name_index;
QList<MMCIcon> m_icons; QVector<MMCIcon> icons;
QDir m_dir; QDir m_dir;
}; };

View File

@ -137,7 +137,11 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
QMap<QString, QString> results; QMap<QString, QString> results;
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QStringList lines = m_stdout.split("\n", Qt::SkipEmptyParts); QStringList lines = m_stdout.split("\n", Qt::SkipEmptyParts);
#else
QStringList lines = m_stdout.split("\n", QString::SkipEmptyParts);
#endif
for (QString line : lines) { for (QString line : lines) {
line = line.trimmed(); line = line.trimmed();
// NOTE: workaround for GH-4125, where garbage is getting printed into stdout on bedrock linux // NOTE: workaround for GH-4125, where garbage is getting printed into stdout on bedrock linux
@ -145,7 +149,11 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
continue; continue;
} }
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
auto parts = line.split('=', Qt::SkipEmptyParts); auto parts = line.split('=', Qt::SkipEmptyParts);
#else
auto parts = line.split('=', QString::SkipEmptyParts);
#endif
if (parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty()) { if (parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty()) {
continue; continue;
} else { } else {

View File

@ -405,7 +405,7 @@ QList<QString> JavaUtils::FindJavaPaths()
{ {
QList<QString> javas; QList<QString> javas;
javas.append(this->GetDefaultJava()->path); javas.append(this->GetDefaultJava()->path);
auto scanJavaDir = [&javas]( auto scanJavaDir = [&](
const QString& dirPath, const QString& dirPath,
const std::function<bool(const QFileInfo&)>& filter = [](const QFileInfo&) { return true; }) { const std::function<bool(const QFileInfo&)>& filter = [](const QFileInfo&) { return true; }) {
QDir dir(dirPath); QDir dir(dirPath);
@ -424,7 +424,7 @@ QList<QString> JavaUtils::FindJavaPaths()
}; };
// java installed in a snap is installed in the standard directory, but underneath $SNAP // java installed in a snap is installed in the standard directory, but underneath $SNAP
auto snap = qEnvironmentVariable("SNAP"); auto snap = qEnvironmentVariable("SNAP");
auto scanJavaDirs = [scanJavaDir, snap](const QString& dirPath) { auto scanJavaDirs = [&](const QString& dirPath) {
scanJavaDir(dirPath); scanJavaDir(dirPath);
if (!snap.isNull()) { if (!snap.isNull()) {
scanJavaDir(snap + dirPath); scanJavaDir(snap + dirPath);
@ -442,15 +442,9 @@ QList<QString> JavaUtils::FindJavaPaths()
QString fileName = info.fileName(); QString fileName = info.fileName();
return fileName.startsWith("openjdk-") || fileName.startsWith("openj9-"); return fileName.startsWith("openjdk-") || fileName.startsWith("openj9-");
}; };
// AOSC OS's locations for openjdk
auto aoscFilter = [](const QFileInfo& info) {
QString fileName = info.fileName();
return fileName == "java" || fileName.startsWith("java-");
};
scanJavaDir("/usr/lib64", gentooFilter); scanJavaDir("/usr/lib64", gentooFilter);
scanJavaDir("/usr/lib", gentooFilter); scanJavaDir("/usr/lib", gentooFilter);
scanJavaDir("/opt", gentooFilter); scanJavaDir("/opt", gentooFilter);
scanJavaDir("/usr/lib", aoscFilter);
// javas stored in Prism Launcher's folder // javas stored in Prism Launcher's folder
scanJavaDirs("java"); scanJavaDirs("java");
// manually installed JDKs in /opt // manually installed JDKs in /opt
@ -552,12 +546,12 @@ QStringList getPrismJavaBundle()
{ {
QList<QString> javas; QList<QString> javas;
auto scanDir = [&javas](QString prefix) { auto scanDir = [&](QString prefix) {
javas.append(FS::PathCombine(prefix, "jre", "bin", JavaUtils::javaExecutable)); javas.append(FS::PathCombine(prefix, "jre", "bin", JavaUtils::javaExecutable));
javas.append(FS::PathCombine(prefix, "bin", JavaUtils::javaExecutable)); javas.append(FS::PathCombine(prefix, "bin", JavaUtils::javaExecutable));
javas.append(FS::PathCombine(prefix, JavaUtils::javaExecutable)); javas.append(FS::PathCombine(prefix, JavaUtils::javaExecutable));
}; };
auto scanJavaDir = [scanDir](const QString& dirPath) { auto scanJavaDir = [&](const QString& dirPath) {
QDir dir(dirPath); QDir dir(dirPath);
if (!dir.exists()) if (!dir.exists())
return; return;

View File

@ -19,13 +19,9 @@ JavaVersion& JavaVersion::operator=(const QString& javaVersionString)
QRegularExpression pattern; QRegularExpression pattern;
if (javaVersionString.startsWith("1.")) { if (javaVersionString.startsWith("1.")) {
static const QRegularExpression s_withOne( pattern = QRegularExpression("1[.](?<major>[0-9]+)([.](?<minor>[0-9]+))?(_(?<security>[0-9]+)?)?(-(?<prerelease>[a-zA-Z0-9]+))?");
"1[.](?<major>[0-9]+)([.](?<minor>[0-9]+))?(_(?<security>[0-9]+)?)?(-(?<prerelease>[a-zA-Z0-9]+))?");
pattern = s_withOne;
} else { } else {
static const QRegularExpression s_withoutOne( pattern = QRegularExpression("(?<major>[0-9]+)([.](?<minor>[0-9]+))?([.](?<security>[0-9]+))?(-(?<prerelease>[a-zA-Z0-9]+))?");
"(?<major>[0-9]+)([.](?<minor>[0-9]+))?([.](?<security>[0-9]+))?(-(?<prerelease>[a-zA-Z0-9]+))?");
pattern = s_withoutOne;
} }
auto match = pattern.match(m_string); auto match = pattern.match(m_string);

View File

@ -55,7 +55,6 @@ void ArchiveDownloadTask::executeTask()
connect(download.get(), &Task::stepProgress, this, &ArchiveDownloadTask::propagateStepProgress); connect(download.get(), &Task::stepProgress, this, &ArchiveDownloadTask::propagateStepProgress);
connect(download.get(), &Task::status, this, &ArchiveDownloadTask::setStatus); connect(download.get(), &Task::status, this, &ArchiveDownloadTask::setStatus);
connect(download.get(), &Task::details, this, &ArchiveDownloadTask::setDetails); connect(download.get(), &Task::details, this, &ArchiveDownloadTask::setDetails);
connect(download.get(), &Task::aborted, this, &ArchiveDownloadTask::emitAborted);
connect(download.get(), &Task::succeeded, [this, fullPath] { connect(download.get(), &Task::succeeded, [this, fullPath] {
// This should do all of the extracting and creating folders // This should do all of the extracting and creating folders
extractJava(fullPath); extractJava(fullPath);
@ -136,6 +135,7 @@ bool ArchiveDownloadTask::abort()
auto aborted = canAbort(); auto aborted = canAbort();
if (m_task) if (m_task)
aborted = m_task->abort(); aborted = m_task->abort();
emitAborted();
return aborted; return aborted;
}; };
} // namespace Java } // namespace Java

View File

@ -37,12 +37,12 @@
#include "launch/LaunchTask.h" #include "launch/LaunchTask.h"
#include <assert.h> #include <assert.h>
#include <QAnyStringView>
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
#include <QEventLoop>
#include <QRegularExpression>
#include <QStandardPaths> #include <QStandardPaths>
#include <variant>
#include "MessageLevel.h" #include "MessageLevel.h"
#include "tasks/Task.h" #include "tasks/Task.h"
@ -214,52 +214,6 @@ shared_qobject_ptr<LogModel> LaunchTask::getLogModel()
return m_logModel; return m_logModel;
} }
bool LaunchTask::parseXmlLogs(QString const& line, MessageLevel::Enum level)
{
LogParser* parser;
switch (level) {
case MessageLevel::StdErr:
parser = &m_stderrParser;
break;
case MessageLevel::StdOut:
parser = &m_stdoutParser;
break;
default:
return false;
}
parser->appendLine(line);
auto items = parser->parseAvailable();
if (auto err = parser->getError(); err.has_value()) {
auto& model = *getLogModel();
model.append(MessageLevel::Error, tr("[Log4j Parse Error] Failed to parse log4j log event: %1").arg(err.value().errMessage));
return false;
} else {
if (!items.isEmpty()) {
auto& model = *getLogModel();
for (auto const& item : items) {
if (std::holds_alternative<LogParser::LogEntry>(item)) {
auto entry = std::get<LogParser::LogEntry>(item);
auto msg = QString("[%1] [%2/%3] [%4]: %5")
.arg(entry.timestamp.toString("HH:mm:ss"))
.arg(entry.thread)
.arg(entry.levelText)
.arg(entry.logger)
.arg(entry.message);
msg = censorPrivateInfo(msg);
model.append(entry.level, msg);
} else if (std::holds_alternative<LogParser::PlainText>(item)) {
auto msg = std::get<LogParser::PlainText>(item).message;
level = LogParser::guessLevel(msg, model.previousLevel());
msg = censorPrivateInfo(msg);
model.append(level, msg);
}
}
}
}
return true;
}
void LaunchTask::onLogLines(const QStringList& lines, MessageLevel::Enum defaultLevel) void LaunchTask::onLogLines(const QStringList& lines, MessageLevel::Enum defaultLevel)
{ {
for (auto& line : lines) { for (auto& line : lines) {
@ -269,26 +223,21 @@ void LaunchTask::onLogLines(const QStringList& lines, MessageLevel::Enum default
void LaunchTask::onLogLine(QString line, MessageLevel::Enum level) void LaunchTask::onLogLine(QString line, MessageLevel::Enum level)
{ {
if (parseXmlLogs(line, level)) {
return;
}
// if the launcher part set a log level, use it // if the launcher part set a log level, use it
auto innerLevel = MessageLevel::fromLine(line); auto innerLevel = MessageLevel::fromLine(line);
if (innerLevel != MessageLevel::Unknown) { if (innerLevel != MessageLevel::Unknown) {
level = innerLevel; level = innerLevel;
} }
auto& model = *getLogModel();
// If the level is still undetermined, guess level // If the level is still undetermined, guess level
if (level == MessageLevel::Unknown) { if (level == MessageLevel::StdErr || level == MessageLevel::StdOut || level == MessageLevel::Unknown) {
level = LogParser::guessLevel(line, model.previousLevel()); level = m_instance->guessLevel(line, level);
} }
// censor private user info // censor private user info
line = censorPrivateInfo(line); line = censorPrivateInfo(line);
auto& model = *getLogModel();
model.append(level, line); model.append(level, line);
} }

View File

@ -43,7 +43,6 @@
#include "LaunchStep.h" #include "LaunchStep.h"
#include "LogModel.h" #include "LogModel.h"
#include "MessageLevel.h" #include "MessageLevel.h"
#include "logs/LogParser.h"
class LaunchTask : public Task { class LaunchTask : public Task {
Q_OBJECT Q_OBJECT
@ -115,9 +114,6 @@ class LaunchTask : public Task {
private: /*methods */ private: /*methods */
void finalizeSteps(bool successful, const QString& error); void finalizeSteps(bool successful, const QString& error);
protected:
bool parseXmlLogs(QString const& line, MessageLevel::Enum level);
protected: /* data */ protected: /* data */
MinecraftInstancePtr m_instance; MinecraftInstancePtr m_instance;
shared_qobject_ptr<LogModel> m_logModel; shared_qobject_ptr<LogModel> m_logModel;
@ -126,6 +122,4 @@ class LaunchTask : public Task {
int currentStep = -1; int currentStep = -1;
State state = NotStarted; State state = NotStarted;
qint64 m_pid = -1; qint64 m_pid = -1;
LogParser m_stdoutParser;
LogParser m_stderrParser;
}; };

View File

@ -100,7 +100,7 @@ void LogModel::setMaxLines(int maxLines)
return; return;
} }
// otherwise, we need to reorganize the data because it crosses the wrap boundary // otherwise, we need to reorganize the data because it crosses the wrap boundary
QList<entry> newContent; QVector<entry> newContent;
newContent.resize(maxLines); newContent.resize(maxLines);
if (m_numLines <= maxLines) { if (m_numLines <= maxLines) {
// if it all fits in the new buffer, just copy it over // if it all fits in the new buffer, just copy it over
@ -149,28 +149,3 @@ bool LogModel::wrapLines() const
{ {
return m_lineWrap; return m_lineWrap;
} }
void LogModel::setColorLines(bool state)
{
if (m_colorLines != state) {
m_colorLines = state;
}
}
bool LogModel::colorLines() const
{
return m_colorLines;
}
bool LogModel::isOverFlow()
{
return m_numLines >= m_maxLines && m_stopOnOverflow;
}
MessageLevel::Enum LogModel::previousLevel()
{
if (!m_content.isEmpty()) {
return m_content.last().level;
}
return MessageLevel::Unknown;
}

View File

@ -24,14 +24,9 @@ class LogModel : public QAbstractListModel {
void setMaxLines(int maxLines); void setMaxLines(int maxLines);
void setStopOnOverflow(bool stop); void setStopOnOverflow(bool stop);
void setOverflowMessage(const QString& overflowMessage); void setOverflowMessage(const QString& overflowMessage);
bool isOverFlow();
void setLineWrap(bool state); void setLineWrap(bool state);
bool wrapLines() const; bool wrapLines() const;
void setColorLines(bool state);
bool colorLines() const;
MessageLevel::Enum previousLevel();
enum Roles { LevelRole = Qt::UserRole }; enum Roles { LevelRole = Qt::UserRole };
@ -42,7 +37,7 @@ class LogModel : public QAbstractListModel {
}; };
private: /* data */ private: /* data */
QList<entry> m_content; QVector<entry> m_content;
int m_maxLines = 1000; int m_maxLines = 1000;
// first line in the circular buffer // first line in the circular buffer
int m_firstLine = 0; int m_firstLine = 0;
@ -52,7 +47,6 @@ class LogModel : public QAbstractListModel {
QString m_overflowMessage = "OVERFLOW"; QString m_overflowMessage = "OVERFLOW";
bool m_suspended = false; bool m_suspended = false;
bool m_lineWrap = true; bool m_lineWrap = true;
bool m_colorLines = true;
private: private:
Q_DISABLE_COPY(LogModel) Q_DISABLE_COPY(LogModel)

View File

@ -49,15 +49,19 @@ void PostLaunchCommand::executeTask()
{ {
auto cmd = m_parent->substituteVariables(m_command); auto cmd = m_parent->substituteVariables(m_command);
emit logLine(tr("Running Post-Launch command: %1").arg(cmd), MessageLevel::Launcher); emit logLine(tr("Running Post-Launch command: %1").arg(cmd), MessageLevel::Launcher);
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
auto args = QProcess::splitCommand(cmd); auto args = QProcess::splitCommand(cmd);
const QString program = args.takeFirst(); const QString program = args.takeFirst();
m_process.start(program, args); m_process.start(program, args);
#else
m_process.start(cmd);
#endif
} }
void PostLaunchCommand::on_state(LoggedProcess::State state) void PostLaunchCommand::on_state(LoggedProcess::State state)
{ {
auto getError = [this]() { return tr("Post-Launch command failed with code %1.\n\n").arg(m_process.exitCode()); }; auto getError = [&]() { return tr("Post-Launch command failed with code %1.\n\n").arg(m_process.exitCode()); };
switch (state) { switch (state) {
case LoggedProcess::Aborted: case LoggedProcess::Aborted:
case LoggedProcess::Crashed: case LoggedProcess::Crashed:

View File

@ -49,14 +49,18 @@ void PreLaunchCommand::executeTask()
{ {
auto cmd = m_parent->substituteVariables(m_command); auto cmd = m_parent->substituteVariables(m_command);
emit logLine(tr("Running Pre-Launch command: %1").arg(cmd), MessageLevel::Launcher); emit logLine(tr("Running Pre-Launch command: %1").arg(cmd), MessageLevel::Launcher);
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
auto args = QProcess::splitCommand(cmd); auto args = QProcess::splitCommand(cmd);
const QString program = args.takeFirst(); const QString program = args.takeFirst();
m_process.start(program, args); m_process.start(program, args);
#else
m_process.start(cmd);
#endif
} }
void PreLaunchCommand::on_state(LoggedProcess::State state) void PreLaunchCommand::on_state(LoggedProcess::State state)
{ {
auto getError = [this]() { return tr("Pre-Launch command failed with code %1.\n\n").arg(m_process.exitCode()); }; auto getError = [&]() { return tr("Pre-Launch command failed with code %1.\n\n").arg(m_process.exitCode()); };
switch (state) { switch (state) {
case LoggedProcess::Aborted: case LoggedProcess::Aborted:
case LoggedProcess::Crashed: case LoggedProcess::Crashed:

View File

@ -1,351 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2025 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "LogParser.h"
#include <QRegularExpression>
#include "MessageLevel.h"
using namespace Qt::Literals::StringLiterals;
void LogParser::appendLine(QAnyStringView data)
{
if (!m_partialData.isEmpty()) {
m_buffer = QString(m_partialData);
m_buffer.append("\n");
m_partialData.clear();
}
m_buffer.append(data.toString());
}
std::optional<LogParser::Error> LogParser::getError()
{
return m_error;
}
std::optional<LogParser::LogEntry> LogParser::parseAttributes()
{
LogParser::LogEntry entry{
"",
MessageLevel::Info,
};
auto attributes = m_parser.attributes();
for (const auto& attr : attributes) {
auto name = attr.name();
auto value = attr.value();
if (name == "logger"_L1) {
entry.logger = value.trimmed().toString();
} else if (name == "timestamp"_L1) {
if (value.trimmed().isEmpty()) {
m_parser.raiseError("log4j:Event Missing required attribute: timestamp");
return {};
}
entry.timestamp = QDateTime::fromSecsSinceEpoch(value.trimmed().toLongLong());
} else if (name == "level"_L1) {
entry.levelText = value.trimmed().toString();
entry.level = MessageLevel::getLevel(entry.levelText);
} else if (name == "thread"_L1) {
entry.thread = value.trimmed().toString();
}
}
if (entry.logger.isEmpty()) {
m_parser.raiseError("log4j:Event Missing required attribute: logger");
return {};
}
return entry;
}
void LogParser::setError()
{
m_error = {
m_parser.errorString(),
m_parser.error(),
};
}
void LogParser::clearError()
{
m_error = {}; // clear previous error
}
bool isPotentialLog4JStart(QStringView buffer)
{
static QString target = QStringLiteral("<log4j:event");
if (buffer.isEmpty() || buffer[0] != '<') {
return false;
}
auto bufLower = buffer.toString().toLower();
return target.startsWith(bufLower) || bufLower.startsWith(target);
}
std::optional<LogParser::ParsedItem> LogParser::parseNext()
{
clearError();
if (m_buffer.isEmpty()) {
return {};
}
if (m_buffer.trimmed().isEmpty()) {
auto text = QString(m_buffer);
m_buffer.clear();
return LogParser::PlainText{ text };
}
// check if we have a full xml log4j event
bool isCompleteLog4j = false;
m_parser.clear();
m_parser.setNamespaceProcessing(false);
m_parser.addData(m_buffer);
if (m_parser.readNextStartElement()) {
if (m_parser.qualifiedName().compare("log4j:Event"_L1, Qt::CaseInsensitive) == 0) {
int depth = 1;
bool eod = false;
while (depth > 0 && !eod) {
auto tok = m_parser.readNext();
switch (tok) {
case QXmlStreamReader::TokenType::StartElement: {
depth += 1;
} break;
case QXmlStreamReader::TokenType::EndElement: {
depth -= 1;
} break;
case QXmlStreamReader::TokenType::EndDocument: {
eod = true; // break outer while loop
} break;
default: {
// no op
}
}
if (m_parser.hasError()) {
break;
}
}
isCompleteLog4j = depth == 0;
}
}
if (isCompleteLog4j) {
return parseLog4J();
} else {
if (isPotentialLog4JStart(m_buffer)) {
m_partialData = QString(m_buffer);
return LogParser::Partial{ QString(m_buffer) };
}
int start = 0;
auto bufView = QStringView(m_buffer);
while (start < bufView.length()) {
if (qsizetype pos = bufView.right(bufView.length() - start).indexOf('<'); pos != -1) {
auto slicestart = start + pos;
auto slice = bufView.right(bufView.length() - slicestart);
if (isPotentialLog4JStart(slice)) {
if (slicestart > 0) {
auto text = m_buffer.left(slicestart);
m_buffer = m_buffer.right(m_buffer.length() - slicestart);
if (!text.trimmed().isEmpty()) {
return LogParser::PlainText{ text };
}
}
m_partialData = QString(m_buffer);
return LogParser::Partial{ QString(m_buffer) };
}
start = slicestart + 1;
} else {
break;
}
}
// no log4j found, all plain text
auto text = QString(m_buffer);
m_buffer.clear();
return LogParser::PlainText{ text };
}
}
QList<LogParser::ParsedItem> LogParser::parseAvailable()
{
QList<LogParser::ParsedItem> items;
bool doNext = true;
while (doNext) {
auto item_ = parseNext();
if (m_error.has_value()) {
return {};
}
if (item_.has_value()) {
auto item = item_.value();
if (std::holds_alternative<LogParser::Partial>(item)) {
break;
} else {
items.push_back(item);
}
} else {
doNext = false;
}
}
return items;
}
std::optional<LogParser::ParsedItem> LogParser::parseLog4J()
{
m_parser.clear();
m_parser.setNamespaceProcessing(false);
m_parser.addData(m_buffer);
m_parser.readNextStartElement();
if (m_parser.qualifiedName().compare("log4j:Event"_L1, Qt::CaseInsensitive) == 0) {
auto entry_ = parseAttributes();
if (!entry_.has_value()) {
setError();
return {};
}
auto entry = entry_.value();
bool foundMessage = false;
int depth = 1;
enum parseOp { noOp, entryReady, parseError };
auto foundStart = [&]() -> parseOp {
depth += 1;
if (m_parser.qualifiedName().compare("log4j:Message"_L1, Qt::CaseInsensitive) == 0) {
QString message;
bool messageComplete = false;
while (!messageComplete) {
auto tok = m_parser.readNext();
switch (tok) {
case QXmlStreamReader::TokenType::Characters: {
message.append(m_parser.text());
} break;
case QXmlStreamReader::TokenType::EndElement: {
if (m_parser.qualifiedName().compare("log4j:Message"_L1, Qt::CaseInsensitive) == 0) {
messageComplete = true;
}
} break;
case QXmlStreamReader::TokenType::EndDocument: {
return parseError; // parse fail
} break;
default: {
// no op
}
}
if (m_parser.hasError()) {
return parseError;
}
}
entry.message = message;
foundMessage = true;
depth -= 1;
}
return noOp;
};
auto foundEnd = [&]() -> parseOp {
depth -= 1;
if (depth == 0 && m_parser.qualifiedName().compare("log4j:Event"_L1, Qt::CaseInsensitive) == 0) {
if (foundMessage) {
auto consumed = m_parser.characterOffset();
if (consumed > 0 && consumed <= m_buffer.length()) {
m_buffer = m_buffer.right(m_buffer.length() - consumed);
// potential whitespace preserved for next item
}
clearError();
return entryReady;
}
m_parser.raiseError("log4j:Event Missing required attribute: message");
setError();
return parseError;
}
return noOp;
};
while (!m_parser.atEnd()) {
auto tok = m_parser.readNext();
parseOp op = noOp;
switch (tok) {
case QXmlStreamReader::TokenType::StartElement: {
op = foundStart();
} break;
case QXmlStreamReader::TokenType::EndElement: {
op = foundEnd();
} break;
case QXmlStreamReader::TokenType::EndDocument: {
return {};
} break;
default: {
// no op
}
}
switch (op) {
case parseError:
return {}; // parse fail or error
case entryReady:
return entry;
case noOp:
default: {
// no op
}
}
if (m_parser.hasError()) {
return {};
}
}
}
throw std::runtime_error("unreachable: already verified this was a complete log4j:Event");
}
MessageLevel::Enum LogParser::guessLevel(const QString& line, MessageLevel::Enum level)
{
static const QRegularExpression LINE_WITH_LEVEL("^\\[(?<timestamp>[0-9:]+)\\] \\[[^/]+/(?<level>[^\\]]+)\\]");
auto match = LINE_WITH_LEVEL.match(line);
if (match.hasMatch()) {
// New style logs from log4j
QString timestamp = match.captured("timestamp");
QString levelStr = match.captured("level");
level = MessageLevel::getLevel(levelStr);
} else {
// Old style forge logs
if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || line.contains("[FINER]") ||
line.contains("[FINEST]"))
level = MessageLevel::Info;
if (line.contains("[SEVERE]") || line.contains("[STDERR]"))
level = MessageLevel::Error;
if (line.contains("[WARNING]"))
level = MessageLevel::Warning;
if (line.contains("[DEBUG]"))
level = MessageLevel::Debug;
}
if (level != MessageLevel::Unknown)
return level;
if (line.contains("overwriting existing"))
return MessageLevel::Fatal;
return MessageLevel::Info;
}

View File

@ -1,76 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2025 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include <QAnyStringView>
#include <QDateTime>
#include <QList>
#include <QString>
#include <QStringView>
#include <QXmlStreamReader>
#include <optional>
#include <variant>
#include "MessageLevel.h"
class LogParser {
public:
struct LogEntry {
QString logger;
MessageLevel::Enum level;
QString levelText;
QDateTime timestamp;
QString thread;
QString message;
};
struct Partial {
QString data;
};
struct PlainText {
QString message;
};
struct Error {
QString errMessage;
QXmlStreamReader::Error error;
};
using ParsedItem = std::variant<LogEntry, PlainText, Partial>;
public:
LogParser() = default;
void appendLine(QAnyStringView data);
std::optional<ParsedItem> parseNext();
QList<ParsedItem> parseAvailable();
std::optional<Error> getError();
/// guess log level from a line of game log
static MessageLevel::Enum guessLevel(const QString& line, MessageLevel::Enum level);
protected:
std::optional<LogEntry> parseAttributes();
void setError();
void clearError();
std::optional<ParsedItem> parseLog4J();
private:
QString m_buffer;
QString m_partialData;
QXmlStreamReader m_parser;
std::optional<Error> m_error;
};

View File

@ -84,8 +84,6 @@ int main(int argc, char* argv[])
Q_INIT_RESOURCE(iOS); Q_INIT_RESOURCE(iOS);
Q_INIT_RESOURCE(flat); Q_INIT_RESOURCE(flat);
Q_INIT_RESOURCE(flat_white); Q_INIT_RESOURCE(flat_white);
Q_INIT_RESOURCE(shaders);
return app.exec(); return app.exec();
} }
case Application::Failed: case Application::Failed:

View File

@ -23,7 +23,7 @@
namespace Meta { namespace Meta {
Index::Index(QObject* parent) : QAbstractListModel(parent) {} Index::Index(QObject* parent) : QAbstractListModel(parent) {}
Index::Index(const QList<VersionList::Ptr>& lists, QObject* parent) : QAbstractListModel(parent), m_lists(lists) Index::Index(const QVector<VersionList::Ptr>& lists, QObject* parent) : QAbstractListModel(parent), m_lists(lists)
{ {
for (int i = 0; i < m_lists.size(); ++i) { for (int i = 0; i < m_lists.size(); ++i) {
m_uids.insert(m_lists.at(i)->uid(), m_lists.at(i)); m_uids.insert(m_lists.at(i)->uid(), m_lists.at(i));
@ -103,7 +103,7 @@ void Index::parse(const QJsonObject& obj)
void Index::merge(const std::shared_ptr<Index>& other) void Index::merge(const std::shared_ptr<Index>& other)
{ {
const QList<VersionList::Ptr> lists = other->m_lists; const QVector<VersionList::Ptr> lists = other->m_lists;
// initial load, no need to merge // initial load, no need to merge
if (m_lists.isEmpty()) { if (m_lists.isEmpty()) {
beginResetModel(); beginResetModel();

View File

@ -29,7 +29,7 @@ class Index : public QAbstractListModel, public BaseEntity {
Q_OBJECT Q_OBJECT
public: public:
explicit Index(QObject* parent = nullptr); explicit Index(QObject* parent = nullptr);
explicit Index(const QList<VersionList::Ptr>& lists, QObject* parent = nullptr); explicit Index(const QVector<VersionList::Ptr>& lists, QObject* parent = nullptr);
virtual ~Index() = default; virtual ~Index() = default;
enum { UidRole = Qt::UserRole, NameRole, ListPtrRole }; enum { UidRole = Qt::UserRole, NameRole, ListPtrRole };
@ -46,7 +46,7 @@ class Index : public QAbstractListModel, public BaseEntity {
Version::Ptr get(const QString& uid, const QString& version); Version::Ptr get(const QString& uid, const QString& version);
bool hasUid(const QString& uid) const; bool hasUid(const QString& uid) const;
QList<VersionList::Ptr> lists() const { return m_lists; } QVector<VersionList::Ptr> lists() const { return m_lists; }
Task::Ptr loadVersion(const QString& uid, const QString& version = {}, Net::Mode mode = Net::Mode::Online, bool force = false); Task::Ptr loadVersion(const QString& uid, const QString& version = {}, Net::Mode mode = Net::Mode::Online, bool force = false);
@ -60,7 +60,7 @@ class Index : public QAbstractListModel, public BaseEntity {
void parse(const QJsonObject& obj) override; void parse(const QJsonObject& obj) override;
private: private:
QList<VersionList::Ptr> m_lists; QVector<VersionList::Ptr> m_lists;
QHash<QString, VersionList::Ptr> m_uids; QHash<QString, VersionList::Ptr> m_uids;
void connectVersionList(int row, const VersionList::Ptr& list); void connectVersionList(int row, const VersionList::Ptr& list);

View File

@ -35,8 +35,8 @@ MetadataVersion currentFormatVersion()
// Index // Index
static std::shared_ptr<Index> parseIndexInternal(const QJsonObject& obj) static std::shared_ptr<Index> parseIndexInternal(const QJsonObject& obj)
{ {
const QList<QJsonObject> objects = requireIsArrayOf<QJsonObject>(obj, "packages"); const QVector<QJsonObject> objects = requireIsArrayOf<QJsonObject>(obj, "packages");
QList<VersionList::Ptr> lists; QVector<VersionList::Ptr> lists;
lists.reserve(objects.size()); lists.reserve(objects.size());
std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject& obj) { std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject& obj) {
VersionList::Ptr list = std::make_shared<VersionList>(requireString(obj, "uid")); VersionList::Ptr list = std::make_shared<VersionList>(requireString(obj, "uid"));
@ -79,8 +79,8 @@ static VersionList::Ptr parseVersionListInternal(const QJsonObject& obj)
{ {
const QString uid = requireString(obj, "uid"); const QString uid = requireString(obj, "uid");
const QList<QJsonObject> versionsRaw = requireIsArrayOf<QJsonObject>(obj, "versions"); const QVector<QJsonObject> versionsRaw = requireIsArrayOf<QJsonObject>(obj, "versions");
QList<Version::Ptr> versions; QVector<Version::Ptr> versions;
versions.reserve(versionsRaw.size()); versions.reserve(versionsRaw.size());
std::transform(versionsRaw.begin(), versionsRaw.end(), std::back_inserter(versions), [uid](const QJsonObject& vObj) { std::transform(versionsRaw.begin(), versionsRaw.end(), std::back_inserter(versions), [uid](const QJsonObject& vObj) {
auto version = parseCommonVersion(uid, vObj); auto version = parseCommonVersion(uid, vObj);

View File

@ -19,8 +19,8 @@
#include "BaseVersion.h" #include "BaseVersion.h"
#include <QJsonObject> #include <QJsonObject>
#include <QList>
#include <QStringList> #include <QStringList>
#include <QVector>
#include <memory> #include <memory>
#include "minecraft/VersionFile.h" #include "minecraft/VersionFile.h"

View File

@ -158,8 +158,8 @@ Version::Ptr VersionList::getVersion(const QString& version)
bool VersionList::hasVersion(QString version) const bool VersionList::hasVersion(QString version) const
{ {
auto ver = std::find_if(m_versions.constBegin(), m_versions.constEnd(), auto ver =
[version](Meta::Version::Ptr const& a) { return a->version() == version; }); std::find_if(m_versions.constBegin(), m_versions.constEnd(), [&](Meta::Version::Ptr const& a) { return a->version() == version; });
return (ver != m_versions.constEnd()); return (ver != m_versions.constEnd());
} }
@ -169,7 +169,7 @@ void VersionList::setName(const QString& name)
emit nameChanged(name); emit nameChanged(name);
} }
void VersionList::setVersions(const QList<Version::Ptr>& versions) void VersionList::setVersions(const QVector<Version::Ptr>& versions)
{ {
beginResetModel(); beginResetModel();
m_versions = versions; m_versions = versions;
@ -265,7 +265,7 @@ void VersionList::setupAddedVersion(const int row, const Version::Ptr& version)
disconnect(version.get(), &Version::typeChanged, this, nullptr); disconnect(version.get(), &Version::typeChanged, this, nullptr);
connect(version.get(), &Version::requiresChanged, this, connect(version.get(), &Version::requiresChanged, this,
[this, row]() { emit dataChanged(index(row), index(row), QList<int>() << RequiresRole); }); [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << RequiresRole); });
connect(version.get(), &Version::timeChanged, this, connect(version.get(), &Version::timeChanged, this,
[this, row]() { emit dataChanged(index(row), index(row), { TimeRole, SortRole }); }); [this, row]() { emit dataChanged(index(row), index(row), { TimeRole, SortRole }); });
connect(version.get(), &Version::typeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), { TypeRole }); }); connect(version.get(), &Version::typeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), { TypeRole }); });

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