Compare commits

...

230 Commits
develop ... 8.4

Author SHA1 Message Date
Alexandru Ionut Tripon
a58f7bf240
Merge pull request #2537 from PrismLauncher/backport-2534-to-release-8.x
[Backport release-8.x] fix: update datapack and resourcepack versions
2024-06-18 21:07:06 +03:00
DioEgizio
81ea7cef5a fix: add more datapack formats and clang-format it
Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit a63842c727eb3f4c2b02b6392f5f66cfa6832206)
2024-06-18 18:06:49 +00:00
DioEgizio
0df7a8dd3c chore: clang-format ResourcePack.cpp
Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit c07e791d7fdc5899b01945df68e4e347256e975f)
2024-06-18 18:06:49 +00:00
DioEgizio
c254a8961c fix: update datapack and resourcepack versions
Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit 6af52c97c26aba0bd2c2375cb7e40640bd97c238)
2024-06-18 18:06:49 +00:00
Alexandru Ionut Tripon
2cd69664ab
Merge pull request #2535 from PrismLauncher/backport-2531-to-release-8.x
[Backport release-8.x] relax fat FS filename restrictions
2024-06-18 12:52:00 +03:00
Trial97
0ea9190d25 relax fat FS filename restrictions
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit a587de8553527b771bec22cb9ef8b8044a6e673b)
2024-06-18 09:51:34 +00:00
Alexandru Ionut Tripon
580e38a985
Merge pull request #2510 from PrismLauncher/backport-2509-to-release-8.x
[Backport release-8.x] fix(flatpak): disable docs for glfw
2024-06-14 08:59:31 +03:00
Sefa Eyeoglu
8d7f916f80 fix(flatpak): disable docs for glfw
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit 52338f0e884bd4f7f22cca6ae34ef6dd06f4374b)
2024-06-14 05:58:04 +00:00
Alexandru Ionut Tripon
658244574a
Merge pull request #2508 from PrismLauncher/backport-2405-to-release-8.x
[Backport release-8.x] Fix portable if manifest is missing
2024-06-13 19:16:39 +03:00
Alexandru Ionut Tripon
119295c87f Update launcher/updater/prismupdater/PrismUpdater.cpp
Signed-off-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
(cherry picked from commit ab5f628453de6fc9301f73413027846451a78bd0)
2024-06-13 16:16:28 +00:00
Alexandru Ionut Tripon
263b167aae Update launcher/updater/prismupdater/PrismUpdater.cpp
Signed-off-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
(cherry picked from commit 051e7886837090ba62ad81b6f835372649a93b69)
2024-06-13 16:16:28 +00:00
Trial97
56b64edc44 Fix portable if manifest is missing
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit c15f6fcd2a8a380b59f7f49cae5010cc93efd386)
2024-06-13 16:16:27 +00:00
Alexandru Ionut Tripon
1a51cb76ad
Merge pull request #2507 from PrismLauncher/backport-2439-to-release-8.x
[Backport release-8.x] fix vanilla technic modpacks
2024-06-13 19:16:21 +03:00
Trial97
e475f4ed36 fix vanilla technic modpacks
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit f4f0a61bf23d5c40fa4310c050e4c39e6a8ce746)
2024-06-13 16:15:57 +00:00
Alexandru Ionut Tripon
0a27d3859c
Merge pull request #2506 from PrismLauncher/backport-2499-to-release-8.x
[Backport release-8.x] update imgur upload
2024-06-12 23:45:55 +03:00
Trial97
cd6f061335 update imgur upload
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit c1c182fd0c9051a202a0cc7c1d471b9de6997697)
2024-06-12 20:45:31 +00:00
Alexandru Ionut Tripon
ab6c5c122e
Merge pull request #2497 from PrismLauncher/backport-2425-to-release-8.x
[Backport release-8.x] improve invalid path name
2024-06-10 12:13:25 +03:00
Trial97
317b127330 add some comments
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit d1286bbe909bb51b74ab266ed190682d38e29776)
2024-06-10 09:11:36 +00:00
Trial97
354f4ef3d8 fix CI
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 44a16c1ca1c340d3f7641fa12b0ffe59cdf92992)
2024-06-10 09:11:36 +00:00
Trial97
8984cd5f66 improve invalid path name
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit a7a1b2876569704e842d942976ce39061ee5e899)
2024-06-10 09:11:36 +00:00
Trial97
5934849d87 removed + from bad chars in filenames
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 428889a68d67aa4e110b7de9346474e27a4f3ab9)
2024-06-10 09:11:36 +00:00
Alexandru Ionut Tripon
786a452f28
Merge pull request #2492 from PrismLauncher/backport-2166-to-release-8.x
[Backport release-8.x] Updated ftb app import instance detection
2024-06-10 00:46:22 +03:00
Trial97
62a2217291 Added the new ftb settings path
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 1c3c9d24573d4f3cf25580129d3611f5082dcc29)
2024-06-09 21:45:37 +00:00
Trial97
1af7126582 Removed static path
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 27e76d0dcb69d0f34fa64fca89964b231c194387)
2024-06-09 21:45:37 +00:00
Trial97
4d0e4f434f Updated ftb app import instance detection
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 344398f2382b879689aef7d4d02785faf0d9bff8)
2024-06-09 21:45:37 +00:00
Alexandru Ionut Tripon
af936353b1
Merge pull request #2490 from PrismLauncher/backport-1032-to-release-8.x
[Backport release-8.x] refactor: shorten desktop entry comment field
2024-06-09 21:03:18 +03:00
deadmeu
c728f43533 refactor: shorten desktop entry comment field
Signed-off-by: deadmeu <dmu@duck.com>
(cherry picked from commit ac223a29ef61643a2313914d180801ce6dcae139)
2024-06-09 17:59:36 +00:00
Alexandru Ionut Tripon
bdf3497028
Merge pull request #2489 from Trial97/backport-2474-to-release-8.x
Backport 2474 to release 8.x
2024-06-09 16:37:40 +03:00
coolguy1842
9c7d1e80e3
DCO Remediation Commit for coolguy1842 <kian.goslett@protonmail.com>
I, coolguy1842 <kian.goslett@protonmail.com>, hereby add my Signed-off-by to this commit: 448ded2387025b11d122d51b0abd907bec0452aa

Signed-off-by: coolguy1842 <kian.goslett@protonmail.com>
(cherry picked from commit aeb174fc550b16d8e01f8fff58cc9f4bceba5c56)
2024-06-09 16:34:57 +03:00
coolguy1842
616556ea7b
fix formatting
Signed-off-by: coolguy1842 <kian.goslett@protonmail.com>
(cherry picked from commit 71b2c4b1fd0a45e2325d09ca93b0f441ee1b7a50)
2024-06-09 16:34:32 +03:00
coolguy1842
d23fa73f52
simple fix for resizing too small causing an index out of range crash
Signed-off-by: coolguy1842 <coolguy1842.deport391@passfwd.com>
(cherry picked from commit 448ded2387025b11d122d51b0abd907bec0452aa)
2024-06-09 16:34:32 +03:00
TheKodeToad
b2e5ea46d7
Merge pull request #2483 from PrismLauncher/backport-2428-to-release-8.x
[Backport release-8.x] add detection for IBM Semeru Java runtime and fix x86 detection on 64 bit Windows
2024-06-07 20:25:03 +01:00
Fourmisain
dda7e3073d please the pre-commit check
Signed-off-by: Fourmisain <8464472+Fourmisain@users.noreply.github.com>
(cherry picked from commit 23095b70a4cead08e8b0e38bdf62d4d14482c41d)
2024-06-07 19:17:34 +00:00
Fourmisain
a800b86689 detect IBM Semeru Certified Edition on linux
Signed-off-by: Fourmisain <8464472+Fourmisain@users.noreply.github.com>
(cherry picked from commit dbe4adc034a578484a6c4b7ba8d21c7c4d0bbb56)
2024-06-07 19:17:34 +00:00
Fourmisain
cfeb72450e fix x86 detection on 64 bit windows
Signed-off-by: Fourmisain <8464472+Fourmisain@users.noreply.github.com>
(cherry picked from commit af157c1613c697cd0cbff78abe28d6e958372775)
2024-06-07 19:17:34 +00:00
Fourmisain
67e0b68cd5 add detection for IBM Semeru Java runtime
Signed-off-by: Fourmisain <8464472+Fourmisain@users.noreply.github.com>
(cherry picked from commit 08918be11eaefd708634bf75ea6f688f0c3f3df9)
2024-06-07 19:17:34 +00:00
Alexandru Ionut Tripon
fbf819c034
Merge pull request #2480 from PrismLauncher/backport-2383-to-release-8.x
[Backport release-8.x] Made Custom New Instance scrollable
2024-06-07 09:38:10 +03:00
Trial97
be2ecff97c fix qt5 build
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 83883cadf9dd4e71db257821e0c46ef6421021eb)
2024-06-07 06:37:36 +00:00
Trial97
a053a4040c force initial size for the Add Instance dialog
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit f3509181869a789cb413f6704e276f58e847dcb0)
2024-06-07 06:37:36 +00:00
Trial97
975b0b5761 fix double borders
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit c9c39b8203bbfaf2205847e80a979baaf6c007a7)
2024-06-07 06:37:36 +00:00
Trial97
446740ba4f Made Custom New Instance scrollable
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit f7120a4f6ff64e8e28096a51f5d99dd2565471bc)
2024-06-07 06:37:36 +00:00
Alexandru Ionut Tripon
9808ab9602
Merge pull request #2478 from PrismLauncher/backport-2471-to-release-8.x
[Backport release-8.x] replace auth server ping
2024-06-07 00:19:38 +03:00
Trial97
865fd91038 replace auth server ping
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit b7008d2880091db64587362a3bb72fb51541be5f)
2024-06-06 21:19:04 +00:00
Alexandru Ionut Tripon
0cca84a335
Merge pull request #2462 from Trial97/list-image-append-fix-backport
List image append fix backport
2024-06-02 19:02:35 +03:00
SabrePenguin
fa2c2a7c85
Made Regex static and const, removed dist
Signed-off-by: SabrePenguin <lknofczynski@gmail.com>
2024-06-02 18:59:44 +03:00
SabrePenguin
f43873a8ba
Regex correction
Co-authored-by: TheKodeToad <TheKodeToad@proton.me>
Signed-off-by: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com>
2024-06-02 18:59:44 +03:00
SabrePenguin
6a49f513d8
Fixed more clang formatting
Signed-off-by: SabrePenguin <lknofczynski@gmail.com>
2024-06-02 18:59:44 +03:00
SabrePenguin
5d25e21719
Fixing CI format issues
Signed-off-by: SabrePenguin <lknofczynski@gmail.com>
2024-06-02 18:59:44 +03:00
SabrePenguin
da2a67227a
Added patch to all setHtml calls
Signed-off-by: SabrePenguin <lknofczynski@gmail.com>
2024-06-02 18:59:44 +03:00
SabrePenguin
875c6bbada
Switched </ul> to a Regex expression
Signed-off-by: SabrePenguin <lknofczynski@gmail.com>
2024-06-02 18:59:44 +03:00
SabrePenguin
401de52f61
Moved html patch to StringUtils
Signed-off-by: SabrePenguin <lknofczynski@gmail.com>
2024-06-02 18:59:44 +03:00
SabrePenguin
0c84d58915
Fix img tag allowing img....
Co-authored-by: TheKodeToad <TheKodeToad@proton.me>
Signed-off-by: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com>
2024-06-02 18:59:44 +03:00
SabrePenguin
096028f1a6
Pleasing the format check
Co-authored-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
Signed-off-by: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com>
2024-06-02 18:59:44 +03:00
SabrePenguin
fa0492f606
Update launcher/Markdown.cpp
Co-authored-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
Signed-off-by: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com>
2024-06-02 18:59:44 +03:00
SabrePenguin
e201cad3f8
Formatting and fixed img_pos allowed as negative
Signed-off-by: SabrePenguin <lknofczynski@gmail.com>
2024-06-02 18:59:44 +03:00
SabrePenguin
1d8ee37504
Added a naive implementation of a <br> inserter
Signed-off-by: SabrePenguin <lknofczynski@gmail.com>
2024-06-02 18:59:44 +03:00
Alexandru Ionut Tripon
672c8c8b4c
Merge pull request #2453 from PrismLauncher/backport-2423-to-release-8.x
[Backport release-8.x] fix incorrect launch time
2024-05-31 09:58:11 +03:00
Alexandru Ionut Tripon
87ca2ce303
Merge pull request #2452 from PrismLauncher/backport-2438-to-release-8.x
[Backport release-8.x] Added support for bcachefs
2024-05-31 09:58:04 +03:00
Trial97
905d9d2826 fix zero launch time
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 1b0e8260ac3b992a1e60e88d9a7217c8c8a66bfc)
2024-05-31 06:17:25 +00:00
Celeste Peláez
ca359330c2 Reformatted FileSystem.h
Signed-off-by: Celeste Peláez <KernelFreeze@users.noreply.github.com>
(cherry picked from commit f5f32e2c6e7644577e2f583a975d2f9b0df405a1)
2024-05-31 06:12:46 +00:00
Celeste Peláez
828222eb27 Added support for bcachefs
Signed-off-by: Celeste Peláez <KernelFreeze@users.noreply.github.com>
(cherry picked from commit a2b5ac73ff1464d9c2cb70412ac57d3c9811d5d9)
2024-05-31 06:12:46 +00:00
Alexandru Ionut Tripon
f7cad1110e
Merge pull request #2411 from PrismLauncher/backport-2377-to-release-8.x
[Backport release-8.x] Adding sdkman java location to MacOs
2024-05-18 13:16:44 +03:00
Samuel Stidham
bffb2a3609 Fixed the code.
Signed-off-by: Samuel Stidham <dqfan2012@gmail.com>
(cherry picked from commit adf0cfdebf2d27148139da2965900a9b3c1ec389)
2024-05-18 10:16:15 +00:00
Samuel Stidham
0d3596c864 Add extra java locations for MacOs.
Signed-off-by: Samuel Stidham <dqfan2012@gmail.com>
(cherry picked from commit 8e0af16de979ba92e9983b47c44ee0f321fcb116)
2024-05-18 10:16:15 +00:00
Alexandru Ionut Tripon
8eeab0805a
Merge pull request #2410 from PrismLauncher/backport-2382-to-release-8.x
[Backport release-8.x] Add neoforge support for technic packs and atlauncher packs
2024-05-18 10:41:15 +03:00
Trial97
8898eb0a19 Add neoforge support for technic packs and atlauncher packs
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit a95c1768a778f3ade8d61fef4c636a21a55bb4fe)
2024-05-18 07:40:59 +00:00
Alexandru Ionut Tripon
43286eea91
Merge pull request #2387 from PrismLauncher/backport-2379-to-release-8.x
[Backport release-8.x] Improve blocked mods dialog (permissions prompt, misc. fixes) on macOS
2024-05-09 08:28:59 +03:00
Kenneth Chew
37af82a3c0 Fix bug where watched directories are not clickable on macOS
Signed-off-by: Kenneth Chew <79120643+kthchew@users.noreply.github.com>
(cherry picked from commit e7c95c9ccb92474cbbaf125814d3085a63b2b746)
2024-05-09 05:27:53 +00:00
Kenneth Chew
e1555ff74e Improved blocked mods dialog permissions prompt
Targeted to macOS (where `~/Downloads` is considered a sensitive location that the system protects behind a prompt even from non-sandboxed apps)

Signed-off-by: Kenneth Chew <79120643+kthchew@users.noreply.github.com>
(cherry picked from commit 47871416982836e282e2da373a317d3df0cf4f38)
2024-05-09 05:27:53 +00:00
Alexandru Ionut Tripon
47e5855527
Merge pull request #2386 from PrismLauncher/backport-2366-to-release-8.x
[Backport release-8.x] Fix download packs with invalid characters
2024-05-09 08:23:49 +03:00
Alexandru Ionut Tripon
3328ec970a
Merge pull request #2385 from PrismLauncher/backport-2341-to-release-8.x
[Backport release-8.x] Fix image width in project description
2024-05-09 08:23:32 +03:00
Trial97
7caed93d2c Fixed imports
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 30ce8ecb5ff08bcec110b3b291025cfd72d4d4fe)
2024-05-09 05:16:33 +00:00
Trial97
c0706b30ee Ensure valid file names for resource names
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 5400c24c7324213a1af102c96e8bd6f9be672006)
2024-05-09 05:16:33 +00:00
Trial97
a57a21af3b Fix invalid characters filename download on windows
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit e06812037560036b2c1ade1e75b06fce1f8a13bf)
2024-05-09 05:16:33 +00:00
Trial97
b679a57939 Fixed NetRequest not failing on sink init
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 48f3ca56bafcf1a7e10b865a910221eefe6b84d0)
2024-05-09 05:16:32 +00:00
Alexandru Ionut Tripon
d3a16715b8 Update launcher/ui/widgets/VariableSizedImageObject.cpp
Signed-off-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
(cherry picked from commit 3c9a207192e680262d44f25b509a77d74dd6dbe0)
2024-05-09 05:15:48 +00:00
Trial97
28378c77aa Fix image width in project description
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit c902da446174840c38fee8c8ff20ab2b21699739)
2024-05-09 05:15:48 +00:00
Alexandru Ionut Tripon
cf87aed7ff
Merge pull request #2360 from PrismLauncher/backport-2348-to-release-8.x
[Backport release-8.x] Use proxy style to force ActivateItemOnSingleClick off
2024-04-30 10:24:52 +03:00
Alexandru Ionut Tripon
d43653605f
Merge pull request #2359 from PrismLauncher/backport-2292-to-release-8.x
[Backport release-8.x] Remove old version variants in instance creation
2024-04-30 10:24:22 +03:00
TheKodeToad
257297d966 Use proxy style to force ActivateItemOnSingleClick off
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit e3f55f68657f63c15933dc0fdde4319390600a98)
2024-04-30 05:56:01 +00:00
ColonelGerdauf
ea0c74b302 Change of parameters
To help deal with adjustments

Signed-off-by: ColonelGerdauf <ColonelGerdauf@users.noreply.github.com>
(cherry picked from commit a877f9146de17a09b4803f6241f675d9a2ba739c)
2024-04-30 05:53:48 +00:00
ColonelGerdauf
c02257024e Renaming and removing redundancies
old's do not make sense here

Signed-off-by: ColonelGerdauf <ColonelGerdauf@users.noreply.github.com>
(cherry picked from commit f91f97e56dd27fe4fb355a800e853c782f92baa6)
2024-04-30 05:53:48 +00:00
Alexandru Ionut Tripon
098f1f1281
Merge pull request #2357 from PrismLauncher/backport-2353-to-release-8.x
[Backport release-8.x] Fix CurseForge export caused by utf8 enabled
2024-04-29 19:30:37 +03:00
Trial97
119ee7a5b4 Fixed curseforge export
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit a5b059ebad1221d10eeb5d4b953817441fabfd1d)
2024-04-29 16:30:17 +00:00
Sefa Eyeoglu
87354d9a7d
Merge pull request #2354 from Trial97/bump8.4 2024-04-29 13:14:07 +02:00
Trial97
46a9880e2e
chore: bump to 8.4
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2024-04-29 13:48:56 +03:00
timoreo
1daf4ad85f
Merge pull request #2339 from PrismLauncher/backport-2337-to-release-8.x 2024-04-24 18:03:43 +02:00
Trial97
c74099ec7b Fixed windows bundled java path
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 5ae98f4f901200647ea3b4ff855248705f28fd3b)
2024-04-24 15:46:21 +00:00
TheKodeToad
4cd6ac5fe5
Merge pull request #2334 from PrismLauncher/backport-2333-to-release-8.x
[Backport release-8.x] Fix Minecraft Launcher runtime detection on Windows and macOS
2024-04-23 17:20:44 +01:00
Trial97
f06bd54d6f Removed duplicate local
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 10d1720da5c350250390fa8b38a5c4db6429462b)
2024-04-23 16:19:50 +00:00
Trial97
0b535e44e1 Changed env variable
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 73c3794c1bfa9d3cd01339039ad8597971b5fb53)
2024-04-23 16:19:50 +00:00
Trial97
877de6da40 Fixed windows appdata path
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit dac70278462913b7c0191c56a04e62529d5c774b)
2024-04-23 16:19:50 +00:00
Trial97
c330452e84 Fix bundled java detection on mac
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit dbfd535b38d799a773b3843709bbeae7f5a9e7d3)
2024-04-23 16:19:50 +00:00
Alexandru Ionut Tripon
3f3af4134e
Merge pull request #2330 from Trial97/appimage_backport
Removed java from appimage backport
2024-04-23 00:06:36 +03:00
Trial97
565c7d81b3
Removed java from appimage
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2024-04-23 00:04:51 +03:00
Alexandru Ionut Tripon
a532c99aa5
Merge pull request #2329 from PrismLauncher/backport-2327-to-release-8.x
[Backport release-8.x] Add Gradle Toolchains to java scan list
2024-04-21 20:56:05 +03:00
William Gray
47bd02637a Update JavaUtils.cpp
Signed-off-by: William Gray <ruwagray@gmail.com>
(cherry picked from commit 9f4654ede91a81a3d1099c949164690e295995a4)
2024-04-21 17:45:15 +00:00
Alexandru Ionut Tripon
64797b8bff
Merge pull request #2322 from PrismLauncher/backport-2319-to-release-8.x
[Backport release-8.x] Support mod info for NeoForge 20.5
2024-04-20 12:04:47 +03:00
TheKodeToad
7960282e07 Support neoforge.mods.toml
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 252406ba3e793b4b096ed1e7bde43c2d2a9c2eca)
2024-04-20 07:50:01 +00:00
Alexandru Ionut Tripon
cb0fe08bed
Merge pull request #2314 from PrismLauncher/backport-2294-to-release-8.x
[Backport release-8.x] Fixed application close on open file dialog
2024-04-19 00:13:36 +03:00
Trial97
160c8cb70d Fixed application close on open file dialog
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 9f48694eb2be5f62efb122b554c7066e49f3383d)
2024-04-18 21:13:03 +00:00
TheKodeToad
13afbd5699
Merge pull request #2286 from DioEgizio/updates-backport
Update to Qt 6.6.3
2024-04-15 11:23:34 +01:00
Alexandru Ionut Tripon
7b1c5a7e1a
Merge pull request #2293 from PrismLauncher/backport-2291-to-release-8.x
[Backport release-8.x] Fixed crash on invalid curseforge link on import
2024-04-09 00:20:31 +03:00
Trial97
1a44258c9d Fixed crash on invalid curseforge link on import
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 9cc58fe62c776dd206bb6dd99c886ce491662214)
2024-04-08 21:20:14 +00:00
DioEgizio
95cfcedc96 chore: update to qt 6.6.3 on 8.x
qt bug fix release

Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
2024-04-06 20:23:01 +02:00
Alexandru Ionut Tripon
5f7a5bfa2d
Merge pull request #2281 from Scrumplex/bump-8.3
Bump to 8.3
2024-04-06 16:15:38 +03:00
Sefa Eyeoglu
f221b08f11
chore: bump to 8.3
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
2024-04-06 15:12:41 +02:00
Alexandru Ionut Tripon
0af9bde06f
Merge pull request #2280 from PrismLauncher/backport-2225-to-release-8.x
[Backport release-8.x] Use UTF-8 for ZIP creation
2024-04-06 16:02:24 +03:00
TheKodeToad
d9b133a28d Use UTF-8 for ZIP creation
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit d72bd70c055d23223ee78fa62e6ae4ccfcb490d7)
2024-04-06 13:01:11 +00:00
Alexandru Ionut Tripon
3477b8c2c2
Merge pull request #2279 from PrismLauncher/backport-2277-to-release-8.x
[Backport release-8.x] Fix unicode characters being invalid in filenames
2024-04-06 15:57:24 +03:00
TheKodeToad
e43341ce92 Fix unicode characters being invalid in filenames
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 9986fe4c45be7f3ce22fdce64812677f16cf7b4c)
2024-04-06 12:54:15 +00:00
Alexandru Ionut Tripon
acfd05a70e
Merge pull request #2278 from PrismLauncher/backport-2247-to-release-8.x
[Backport release-8.x] Fixed crash on non-latin instance name
2024-04-06 15:41:38 +03:00
Trial97
b2cca30cd9 Moved getPathNameInLocal8bit inside FileSystem namespace
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 3ddcadcdd1f72f8135333986e55cf583204da1e2)
2024-04-06 12:40:35 +00:00
Trial97
3e5e131a94 Moved the creation of natives folder in ExtractNatives task
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 8ecab305ac67afb70a278d286df13cc85134dec2)
2024-04-06 12:40:35 +00:00
Trial97
1e5a29c423 Fixed crash on non-latin instance name
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 450b73328e20941eba12be3b7c7f8bb13dc0f944)
2024-04-06 12:40:35 +00:00
timoreo
5b168dd7b1
Merge pull request #2276 from PrismLauncher/backport-2275-to-release-8.x 2024-04-06 14:28:40 +02:00
TheKodeToad
17d5d78a5b Replace invalid characters when extracting ZIP
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 8056a2fa460b11739b080d96deda35cd8897c40c)
2024-04-06 12:18:29 +00:00
Alexandru Ionut Tripon
58d32ea1af
Merge pull request #2273 from PrismLauncher/backport-2268-to-release-8.x
[Backport release-8.x] [Linux] Add jdk21 to Nix packages
2024-04-04 20:39:00 +03:00
Sefa Eyeoglu
0a3b85dbc5 fix(nix): add jdk21
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit 0d372d2fbfd76b54f156faeca6a31fff80875999)
2024-04-04 17:38:47 +00:00
Alexandru Ionut Tripon
99203cbd9b
Merge pull request #2253 from Trial97/manual_backport
Manual backport to 8.x
2024-03-28 14:47:45 +02:00
Trial97
d262a081c5
fixed const stuff
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2024-03-28 14:44:32 +02:00
Alexandru Ionut Tripon
f9df20aae3
Update launcher/ui/pages/modplatform/ImportPage.cpp
Co-authored-by: TheKodeToad <TheKodeToad@proton.me>
Signed-off-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
2024-03-28 14:44:26 +02:00
Trial97
fc4b5205dd
Added all supported files filter
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2024-03-28 14:44:20 +02:00
Alexandru Ionut Tripon
7c2a9df98e
Update launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp
Signed-off-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
2024-03-28 14:43:49 +02:00
Alexandru Ionut Tripon
b854c56c8c
Update launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp
Co-authored-by: Tayou <git@tayou.org>
Signed-off-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
2024-03-28 14:43:40 +02:00
Trial97
2c864a4aad
added more microsoft error codes
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2024-03-28 14:43:30 +02:00
DioEgizio
4642c13ca4
fix: include libbz2.so.1.0
fixes portable installs not working on fedora anymore

i did this only on portable builds and not system installs so that it doesn't create problems to people packaging them, as to fix system installs i would have to put it in bin/

Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
2024-03-28 14:42:04 +02:00
Trial97
b255c8b17f
Changed the trait name
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2024-03-28 14:39:32 +02:00
Alexandru Ionut Tripon
c0f14c569b
Merge pull request #2252 from PrismLauncher/backport-2209-to-release-8.x
[Backport release-8.x] Add totalTimePlayed for FTBApp import
2024-03-28 14:36:47 +02:00
chouzz
ca3516d26d Add totalTimePlayed for FTBApp import
Signed-off-by: chouzz <zhouhua852@gmail.com>
(cherry picked from commit 92113e233aefbd2b255775d4e858e45845a94a2c)
2024-03-28 12:36:36 +00:00
timoreo
067fcaf0d9
Merge pull request #2250 from PrismLauncher/backport-2221-to-release-8.x
[Backport release-8.x] Set Forge version correctly when exporting to CurseForge
2024-03-28 09:56:40 +01:00
Trial97
1a379280b8 Fixed curesforge export
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 05487152fe4eae0ad3d12b18c398ea2a99b44e32)
2024-03-28 08:41:26 +00:00
Sefa Eyeoglu
284633ff91
Merge pull request #2188 from Scrumplex/bump-8.2 2024-03-03 20:37:21 +01:00
Sefa Eyeoglu
a7bfd08ce3
chore: bump to 8.2
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
2024-03-03 20:35:43 +01:00
Sefa Eyeoglu
8c5cc43fc5
Merge pull request #2186 from PrismLauncher/backport-1923-to-release-8.x 2024-03-03 18:40:15 +01:00
Tayou
a8553e7897 add line back to group separator
Signed-off-by: Tayou <git@tayou.org>
(cherry picked from commit 877eb4172a737c44505b3c784e84ab710b291e5e)
2024-03-03 17:38:44 +00:00
Tayou
ca1b68b7ca
Merge pull request #2185 from PrismLauncher/backport-2178-to-release-8.x
[Backport release-8.x] (More) mrpack export fixes
2024-03-03 16:46:48 +01:00
TheKodeToad
1fe710777a Allow multiline description
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 4101fbb63433d1ee90476f55d76a7a7f0ced6ccb)
2024-03-03 15:30:54 +00:00
TheKodeToad
e349e24548 Fix server side mods being marked as unsupported on client
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 138df66355b54151302f0b143ec1c2a5a4eb11c8)
2024-03-03 15:30:54 +00:00
Sefa Eyeoglu
8ad7c37d5e
Merge pull request #2184 from PrismLauncher/backport-2160-to-release-8.x 2024-03-03 15:18:38 +01:00
TheKodeToad
b7f079b006 Fix build on Windows and formatting
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit c0eb80947db57c4b0de376213d53aac4f694e1b0)
2024-03-03 14:07:08 +00:00
TheKodeToad
c39ca8852d Fix a mistake
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit fccf857d8e5040fd64d7bc302463c1c9184f110f)
2024-03-03 14:07:08 +00:00
TheKodeToad
69a10e4b3d Add upper-bound for randomisation
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit bd0cd828269ee259dd10439c84baeaec2f1220df)
2024-03-03 14:07:08 +00:00
TheKodeToad
3b02b6b7d7 Add error when staging folder could not be created
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit df60f5cc9650a111a65ada2ea589bf7ecf9ac506)
2024-03-03 14:07:08 +00:00
TheKodeToad
82584e5d7e Delete staging on abort
Previously instances would get "stuck" in the folder

Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 9574ebe480a6a837533fc4339c1f3605300555ca)
2024-03-03 14:07:08 +00:00
TheKodeToad
6c8ee3dfa9 Re-randomise key until it does not collide
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 6a9f5540d385cd2edf7e43f3ad308041571a9f83)
2024-03-03 14:07:08 +00:00
Trial97
f9ba4a746e Updated application manifest
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit aac6fea724e029b9ef4e4ed3e98b04c40149add0)
2024-03-03 14:07:08 +00:00
Trial97
e37cf125c3 Reduced temporary instance folder name
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 726b0ffb3b0488bd289d85feacea31066c464a45)
2024-03-03 14:07:08 +00:00
Sefa Eyeoglu
b7dc0b83b2
Merge pull request #2153 from PrismLauncher/backport-2141-to-release-8.x 2024-02-22 12:37:54 +01:00
crpz1
0113456dea Use empty list label code for welcome screen
Signed-off-by: crpz1 <8588315+crpz1@users.noreply.github.com>
(cherry picked from commit 9aa81cf00508c45ad6fe6b1d53c0933f1494571c)
2024-02-22 11:36:13 +00:00
seth
4add24696b
Merge pull request #2151 from DioEgizio/updates-backport
fix: fix sparkle signing backport
2024-02-22 06:10:30 -05:00
DioEgizio
c6441d231b fix: fix sparkle signing
Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
2024-02-22 11:53:48 +01:00
DioEgizio
438ccc41ef
Merge pull request #2136 from DioEgizio/updates-backport 2024-02-18 15:04:29 +01:00
Alexandru Ionut Tripon
80f863f6b6
Merge pull request #2135 from PrismLauncher/backport-2110-to-release-8.x
[Backport release-8.x] Fixed curseforge neoforge import for 1.20.1 versions
2024-02-18 15:02:54 +02:00
DioEgizio
d8e34e6c39 Merge pull request #2130 from DioEgizio/updates 2024-02-18 12:58:09 +01:00
Sefa Eyeoglu
c4cb36b9d5 Merge pull request #1951 from guihkx/sync-flatpak 2024-02-18 12:58:09 +01:00
Trial97
72c4a52119 Fixed curseforge neoforge export for 1.20.1
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit fdaaee64d193472144dd95c15c8d03f6fc4ed83a)
2024-02-18 11:42:43 +00:00
Alexandru Ionut Tripon
8eba326f62 Update launcher/modplatform/flame/FlameInstanceCreationTask.cpp
Signed-off-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
(cherry picked from commit 29bf6cc68c132c036f8c9ee90a337fb2db7d3cf2)
2024-02-18 11:42:43 +00:00
Trial97
69d675dc76 Fixed curseforge neoforge import for 1.20.1 versions
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 830ce246e17e381807d9394819c175dc0b730ff8)
2024-02-18 11:42:43 +00:00
Sefa Eyeoglu
e9991e99c0
Merge pull request #2129 from PrismLauncher/backport-2126-to-release-8.x
[Backport release-8.x] Use absolute path to load MangoHUD library
2024-02-17 09:22:51 +01:00
Echo J
6da36140eb Use absolute path to load MangoHUD library
This should finally fix the Arch Linux Java symbol issue with MangoHUD
enabled: https://bugs.archlinux.org/task/77183

Signed-off-by: Echo J <tcg96nougat@gmail.com>
(cherry picked from commit 01180c20e19fcdd9a902d9cd51514549eeceb531)
2024-02-17 00:57:29 +00:00
Sefa Eyeoglu
816c900edc
Merge pull request #2122 from PrismLauncher/backport-2112-to-release-8.x 2024-02-13 09:16:10 +01:00
TheKodeToad
4ebce79353 Use QTextFragment for inserting log lines
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 7cac8ad2b42f19dd3f38fe5c5ef5f93116ca5aa9)
2024-02-13 08:16:01 +00:00
Sefa Eyeoglu
256ed2a038
Merge pull request #2121 from PrismLauncher/backport-2116-to-release-8.x 2024-02-13 09:15:05 +01:00
TheKodeToad
3f30e4c691 Fix formatting issues
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 8cf2a04f317c59920e8af90d7e16fea872b2c122)
2024-02-13 08:14:52 +00:00
TheKodeToad
ad9dbb4a68 Fix immediate free in Exception.h
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 7f4c74ffbe413fcf191842606c12056fbc6493c7)
2024-02-13 08:14:52 +00:00
TheKodeToad
fb04b6fa85 Fix ResourceModel error display
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit ff989b4305d5ae4578d2154afe27ce6bf72cadaf)
2024-02-13 08:14:52 +00:00
TheKodeToad
6badd694c5 Fix NetJob use-after-free
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit ded77e61832661557a6985d9aa587f3b5d1e1f87)
2024-02-13 08:14:52 +00:00
TheKodeToad
6f5cddeec8
Merge pull request #2091 from PrismLauncher/revert-2084-backport-2050-to-release-8.x
Revert "[Backport release-8.x] Use `minecraft` instead of `.minecraft` for better accessibility"
2024-02-04 16:09:36 +00:00
TheKodeToad
25ba275230
Revert "[Backport release-8.x] Use minecraft instead of .minecraft for better accessibility" 2024-02-04 16:08:16 +00:00
Tayou
cf6ffc3954
Merge pull request #2084 from PrismLauncher/backport-2050-to-release-8.x
[Backport release-8.x] Use `minecraft` instead of `.minecraft` for better accessibility
2024-02-01 18:29:46 +01:00
TheKodeToad
5e3b7d88ec Use minecraft instead of .minecraft for better accessibility
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit f54ac25614b62b0a43d014d8b47f9faee8112d12)
2024-02-01 17:16:29 +00:00
Alexandru Ionut Tripon
1f5552dd81
Merge pull request #2064 from PrismLauncher/backport-2063-to-release-8.x
[Backport release-8.x] fix: remove alpha notice on modrinth packs
2024-01-24 18:13:58 +02:00
DioEgizio
a2aede0d94 fix: remove alpha notice on modrinth packs
with the modrinth app being released for quite a while now it's weird to still have this (tbh they've never been truly alpha)

Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit 8de1060bfb8eba7ad6efbb2093df61992e25e3cd)
2024-01-24 16:13:32 +00:00
Sefa Eyeoglu
2fd5635b2d
Merge pull request #2057 from PrismLauncher/backport-2053-to-release-8.x 2024-01-23 09:30:59 +01:00
Trial97
bc63e1906f Fixed open path a second time
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 12bf91376411d74432f5e88c85d68cef003fd899)
2024-01-23 08:30:16 +00:00
Alexandru Ionut Tripon
a8b168dc1f
Merge pull request #2043 from PrismLauncher/backport-1913-to-release-8.x
[Backport release-8.x] Fixed askIfShouldUpdate dialog on mac
2024-01-18 22:06:12 +02:00
Trial97
fce2163350 Fixed askIfShouldUpdate dialog on mac
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 577c737d686df43ee777a3b0382b788bb34ec170)
2024-01-18 20:05:30 +00:00
Sefa Eyeoglu
5d0c83dec2
Merge pull request #1768 from Trial97/toggle_deps2 2024-01-18 18:19:17 +01:00
Sefa Eyeoglu
557fce577a
Merge pull request #1825 from Trial97/ftb_import 2024-01-18 18:19:17 +01:00
Sefa Eyeoglu
bcb5b9c9c2
Merge pull request #2034 from Scrumplex/feat/macos-signing 2024-01-18 17:00:00 +01:00
Sefa Eyeoglu
428260b91f
Merge pull request #2035 from PrismLauncher/backport-1954-to-release-8.x 2024-01-17 15:19:48 +01:00
Sefa Eyeoglu
fec1a00dc8 fix: simplify openPath calls
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit 67d088dc53337fc2abeae3ce20c89c9495f5c9be)
2024-01-17 14:19:03 +00:00
Sefa Eyeoglu
8dd898f895 chore: improve param name
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit e5b608447a1c4860343aed15ef1e1bd5940ba17d)
2024-01-17 14:19:03 +00:00
Sefa Eyeoglu
f8585602d9 chore: remove unused methods
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit a8220cd296a5b9540df07bce7be7e1f6cc28ffe0)
2024-01-17 14:19:03 +00:00
Sefa Eyeoglu
0ec3b9ce0e chore: remove maybe_unused
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit 213963257cf2b2f7a265893c3112cd71bbce6199)
2024-01-17 14:19:03 +00:00
Sefa Eyeoglu
6f5d074b4b fix: open paths directly
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit 0ccdcd23e3f3889f3e516d48bb03b27d3ddb62cc)
2024-01-17 14:19:03 +00:00
seth
7d9b95b4ce
nix: add garnix cache to nixConfig
Signed-off-by: seth <getchoo@tuta.io>
(cherry picked from commit 839adfc90bac8204581b930efc7cf2ad55629546)
2024-01-11 09:51:53 +01:00
seth
c08890391c
nix: deduplicate nixpkgs input
Signed-off-by: seth <getchoo@tuta.io>
(cherry picked from commit 26474d13749dd81869e155d64125c4b7d73ff0f2)
2024-01-11 09:51:51 +01:00
Alexandru Ionut Tripon
422135b2ab
Merge pull request #1771 from Trial97/account
Try refreshing account on launch if needed
2024-01-10 16:24:34 +02:00
Tayou
b83b25cbe3
Merge pull request #2015 from PrismLauncher/backport-2012-to-release-8.x
[Backport release-8.x] Fixed openAL placeholder
2024-01-07 00:32:17 +01:00
Trial97
4a17799449 Fixed openAL placeholder
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 219e2862f9d67d6a81ef634846237e1eec177d69)
2024-01-06 23:23:49 +00:00
Alexandru Ionut Tripon
3fe18e0810
Merge pull request #2008 from PrismLauncher/backport-1983-to-release-8.x
[Backport release-8.x] remove legacy processArguments code
2024-01-05 15:24:54 +02:00
tildejustin
5f4e0dfc14 remove legacy processArguments code
Signed-off-by: tildejustin <spacepepper3.14@gmail.com>
(cherry picked from commit eca5d88576900d32edea2000258f37d700af5f06)
2024-01-05 12:27:13 +00:00
Alexandru Ionut Tripon
afc73ffa16
Merge pull request #2007 from PrismLauncher/backport-2005-to-release-8.x
[Backport release-8.x] Format java code
2024-01-04 22:29:21 +02:00
Trial97
7ae3c05f1d Format java code
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 786e03571b529c610ada33d87581e65cefc4cae3)
2024-01-04 20:18:56 +00:00
Alexandru Ionut Tripon
744b81fc30
Merge pull request #2004 from PrismLauncher/backport-1979-to-release-8.x
[Backport release-8.x] Mod downloader page improvements
2024-01-03 22:47:27 +02:00
lumiscosity
f4617d7c90 fix styling
Signed-off-by: lumiscosity <averyrudelphe@gmail.com>
(cherry picked from commit c29af83dc328612d340c54e3ec037b1264d66d51)
2024-01-03 20:46:44 +00:00
lumiscosity
a00966986c move simplify call to drawing
Signed-off-by: lumiscosity <averyrudelphe@gmail.com>
(cherry picked from commit 1e48bf838db3f72fd391ecf9c91aa8e693daf213)
2024-01-03 20:46:44 +00:00
lumiscosity
42ea2ecbc8 Strip newlines in mod descriptions
Mirrors Modrinth page behaviour.

Signed-off-by: lumiscosity <averyrudelphe@gmail.com>
(cherry picked from commit 1bf386603dece42df0815495ae6b43b0e2d665c8)
2024-01-03 20:46:44 +00:00
lumiscosity
568734eb8a add archived project warning
Signed-off-by: lumiscosity <averyrudelphe@gmail.com>
(cherry picked from commit 358cf5b3485cfb23cce226c02fef03523468d4eb)
2024-01-03 20:46:44 +00:00
Alexandru Ionut Tripon
3b578ecfe0
Merge pull request #2003 from PrismLauncher/backport-1978-to-release-8.x
[Backport release-8.x] Fixed openURL cast for resource
2024-01-03 22:44:59 +02:00
Trial97
bb843b86ab Fixed openURL cast for resource
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 9a33bdcf501655dbef2981094ee05662f34cd671)
2024-01-03 20:44:44 +00:00
Sefa Eyeoglu
b12dffe420
Merge pull request #2001 from PrismLauncher/backport-1999-to-release-8.x 2024-01-03 18:26:32 +01:00
TheKodeToad
36d8ffd3c1 Pass proxy
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 345cdf5c9d6417b5c2ce15e8967de3886d1113f1)
2024-01-03 17:25:14 +00:00
TheKodeToad
f31bf5a1f1 Add online mode fix for legacy versions; minor refactors in legacy
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 05e453309629d2c4cc1cf4b1322669cb8aed21ff)
2024-01-03 17:25:14 +00:00
Sefa Eyeoglu
270accf6d7
Merge pull request #2000 from PrismLauncher/backport-1998-to-release-8.x 2024-01-03 18:15:52 +01:00
TheKodeToad
6816364354 Happy new year :D
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 1130fe851012b3d7deff207db495b455501e17fc)
2024-01-03 17:13:30 +00:00
TheKodeToad
2f3715beed
Merge pull request #1968 from PrismLauncher/backport-1964-to-release-8.x
[Backport release-8.x] Don't log Microsoft refresh tokens
2023-12-19 11:30:19 +00:00
Sefa Eyeoglu
6912782f4b fix: don't log katabasis by default
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit a1e3901b3f4e4910e3fe40206879907a1f7ff03c)
2023-12-19 11:25:00 +00:00
Sefa Eyeoglu
d8809197f8
Merge pull request #1962 from PrismLauncher/backport-1960-to-release-8.x
[Backport release-8.x] Minor spelling fixes
2023-12-16 17:28:07 +01:00
lumiscosity
199312dd5a one more
Signed-off-by: lumiscosity <averyrudelphe@gmail.com>
(cherry picked from commit eb8e150fa804f0b34b21d7a95e840516cd119c97)
2023-12-16 16:25:35 +00:00
lumiscosity
a108a01be7 Update ModFolderPage.cpp
Signed-off-by: lumiscosity <averyrudelphe@gmail.com>
(cherry picked from commit 56397446dbe05ea346359f401587383d4a1a84dd)
2023-12-16 16:25:35 +00:00
lumiscosity
7568e90655 Update GetModDependenciesTask.cpp
Signed-off-by: lumiscosity <averyrudelphe@gmail.com>
(cherry picked from commit 8cbdecc454de7ee8f3dadbd00c35b339a8581c45)
2023-12-16 16:25:35 +00:00
Alexandru Ionut Tripon
c216713844
Merge pull request #1952 from PrismLauncher/backport-1947-to-release-8.x
[Backport release-8.x] fix: Minimized windows dont get shown
2023-12-13 20:31:31 +02:00
TheKodeToad
4dfe925cf9 Revert change to .gitignore
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit 2391ad200f80ef6a84ffef246ed236d66928f0e4)
2023-12-13 18:24:40 +00:00
theMackabu
534b4e4aa3 remove: maidfile.toml (local build tool)
Signed-off-by: theMackabu <theMackabu@gmail.com>
(cherry picked from commit 7c33a8e448d238960b89f1e1c2f14a18d12040eb)
2023-12-13 18:24:40 +00:00
theMackabu
9aa8e1d7ad clang-format: fix formatting
Signed-off-by: theMackabu <theMackabu@gmail.com>
(cherry picked from commit 201c9783d56cef2e1bba1fdbffdc377bdf294468)
2023-12-13 18:24:40 +00:00
theMackabu
ff146d5855 #1945 resolve issue on windows
Signed-off-by: theMackabu <theMackabu@gmail.com>
(cherry picked from commit 836f74cd4629748ce8056f9f186284fc55d0d8b4)
2023-12-13 18:24:40 +00:00
theMackabu
d0cb62cb62 #1945 resolve minimized windows pull from dock
Signed-off-by: theMackabu <theMackabu@gmail.com>
(cherry picked from commit 3567369d44c6ba9a3b702caf2db18b126f1b4fc4)
2023-12-13 18:24:40 +00:00
timoreo
743d48744a
Merge pull request #1712 from Trial97/current_time 2023-12-11 16:14:52 +01:00
Sefa Eyeoglu
2837ae8bff
Merge pull request #1738 from Trial97/some_removals 2023-12-11 11:31:55 +01:00
Sefa Eyeoglu
3e54d4ddbb
Merge pull request #1732 from Trial97/update_file2 2023-12-11 11:25:10 +01:00
Sefa Eyeoglu
426deb4454
Merge pull request #1694 from Trial97/concurrent 2023-12-11 11:20:17 +01:00
Sefa Eyeoglu
52ccf3d93b
Merge pull request #1857 from Trial97/copy_time 2023-12-11 11:02:12 +01:00
Sefa Eyeoglu
b6a366ed97
Merge pull request #1944 from PrismLauncher/backport-1908-to-release-8.x 2023-12-11 11:00:57 +01:00
DioEgizio
1ba6ed9fe9 chore: update qt to qt 6.6.1
Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
(cherry picked from commit eb011e6729fe49da867e6ae1ab2524424eedac46)
2023-12-11 10:00:43 +00:00
TheKodeToad
db4a7ce239
Fix code style
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-10-20 13:11:17 +01:00
TheKodeToad
983c943eef
Merge branch 'develop' into quick-play
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-08-12 21:48:41 +01:00
TheKodeToad
aaab95ba55 Basic Quick Play support
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-12 12:08:57 +01:00
156 changed files with 1612 additions and 945 deletions

View File

@ -1,41 +0,0 @@
#!/usr/bin/env bash
URL_JDK8="https://api.adoptium.net/v3/binary/version/jdk8u312-b07/linux/x64/jre/hotspot/normal/eclipse"
URL_JDK17="https://api.adoptium.net/v3/binary/latest/17/ga/linux/x64/jre/hotspot/normal/eclipse"
mkdir -p JREs
pushd JREs
wget --content-disposition "$URL_JDK8"
wget --content-disposition "$URL_JDK17"
for file in *;
do
mkdir temp
re='(OpenJDK([[:digit:]]+)U-jre_x64_linux_hotspot_([[:digit:]]+)(.*).tar.gz)'
if [[ $file =~ $re ]];
then
version_major=${BASH_REMATCH[2]}
version_trailing=${BASH_REMATCH[4]}
if [ $version_major = 17 ];
then
hyphen='-'
else
hyphen=''
fi
version_edit=$(echo $version_trailing | sed -e 's/_/+/g' | sed -e 's/b/-b/g')
dir_name=jdk$hyphen$version_major$version_edit-jre
mkdir jre$version_major
tar -xzf $file -C temp
pushd temp/$dir_name
cp -r . ../../jre$version_major
popd
fi
rm -rf temp
done
popd

View File

@ -21,8 +21,23 @@ on:
WINDOWS_CODESIGN_PASSWORD: WINDOWS_CODESIGN_PASSWORD:
description: Password for signing Windows builds description: Password for signing Windows builds
required: false required: false
CACHIX_AUTH_TOKEN: APPLE_CODESIGN_CERT:
description: Private token for authenticating against Cachix cache 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 required: false
GPG_PRIVATE_KEY: GPG_PRIVATE_KEY:
description: Private key for AppImage signing description: Private key for AppImage signing
@ -61,7 +76,7 @@ jobs:
qt_ver: 6 qt_ver: 6
qt_host: windows qt_host: windows
qt_arch: '' qt_arch: ''
qt_version: '6.6.0' qt_version: '6.6.3'
qt_modules: 'qt5compat qtimageformats' qt_modules: 'qt5compat qtimageformats'
qt_tools: '' qt_tools: ''
@ -73,7 +88,7 @@ jobs:
qt_ver: 6 qt_ver: 6
qt_host: windows qt_host: windows
qt_arch: 'win64_msvc2019_arm64' qt_arch: 'win64_msvc2019_arm64'
qt_version: '6.6.0' qt_version: '6.6.3'
qt_modules: 'qt5compat qtimageformats' qt_modules: 'qt5compat qtimageformats'
qt_tools: '' qt_tools: ''
@ -83,7 +98,7 @@ jobs:
qt_ver: 6 qt_ver: 6
qt_host: mac qt_host: mac
qt_arch: '' qt_arch: ''
qt_version: '6.6.0' qt_version: '6.6.3'
qt_modules: 'qt5compat qtimageformats' qt_modules: 'qt5compat qtimageformats'
qt_tools: '' qt_tools: ''
@ -244,7 +259,6 @@ jobs:
wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage" wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage"
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
sudo apt install libopengl0 sudo apt install libopengl0
- name: Add QT_HOST_PATH var (Windows MSVC arm64) - name: Add QT_HOST_PATH var (Windows MSVC arm64)
@ -336,6 +350,20 @@ jobs:
# PACKAGE BUILDS # 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'
run: | run: |
@ -343,9 +371,34 @@ jobs:
cd ${{ env.INSTALL_DIR }} cd ${{ env.INSTALL_DIR }}
chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher" chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "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" mv "PrismLauncher.app" "Prism Launcher.app"
tar -czf ../PrismLauncher.tar.gz *
- 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) - name: Make Sparkle signature (macOS)
if: matrix.name == 'macOS' if: matrix.name == 'macOS'
@ -353,7 +406,7 @@ jobs:
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
brew install openssl@3 brew install openssl@3
echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem
signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.tar.gz -inkey ed25519-priv.pem | openssl base64 | tr -d \\n) signature=$(/usr/local/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 rm ed25519-priv.pem
cat >> $GITHUB_STEP_SUMMARY << EOF cat >> $GITHUB_STEP_SUMMARY << EOF
### Artifact Information :information_source: ### Artifact Information :information_source:
@ -442,7 +495,6 @@ jobs:
run: | run: |
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }} cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }}
for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_DIR }}/manifest.txt for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_DIR }}/manifest.txt
cd ${{ env.INSTALL_DIR }} cd ${{ env.INSTALL_DIR }}
tar --owner root --group root -czf ../PrismLauncher.tar.gz * tar --owner root --group root -czf ../PrismLauncher.tar.gz *
@ -451,9 +503,12 @@ jobs:
run: | run: |
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
# workaround to make portable installs to work on fedora
mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib
cp /lib/x86_64-linux-gnu/libbz2.so.1.0 ${{ 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 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 }} cd ${{ env.INSTALL_PORTABLE_DIR }}
tar -czf ../PrismLauncher-portable.tar.gz * tar -czf ../PrismLauncher-portable.tar.gz *
@ -472,13 +527,9 @@ jobs:
chmod +x linuxdeploy-*.AppImage chmod +x linuxdeploy-*.AppImage
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-{8,17}-openjdk mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk
cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ 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.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
@ -486,10 +537,6 @@ jobs:
cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ 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" LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64"
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib/server"
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
export LD_LIBRARY_PATH export LD_LIBRARY_PATH
chmod +x AppImageUpdate-x86_64.AppImage chmod +x AppImageUpdate-x86_64.AppImage
@ -520,7 +567,7 @@ jobs:
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }} name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher.tar.gz path: PrismLauncher.zip
- name: Upload binary zip (Windows) - name: Upload binary zip (Windows)
if: runner.os == 'Windows' if: runner.os == 'Windows'

View File

@ -32,6 +32,11 @@ jobs:
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }} SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }} WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }} WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} 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: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }} GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}

View File

@ -16,7 +16,12 @@ jobs:
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }} SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }} WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }} WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} 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: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }} GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
@ -46,8 +51,8 @@ jobs:
mv PrismLauncher-Linux-Qt5*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux-Qt5*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt5-${{ 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.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz mv PrismLauncher-macOS-Legacy*/PrismLauncher.zip PrismLauncher-macOS-Legacy-${{ env.VERSION }}.zip
mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz 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 }}
@ -102,6 +107,6 @@ jobs:
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.zip PrismLauncher-Windows-MSVC-${{ env.VERSION }}.zip
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 }}.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.zip
PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.zip
PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }}.tar.gz

View File

@ -179,7 +179,7 @@ set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRIN
######## Set version numbers ######## ######## Set version numbers ########
set(Launcher_VERSION_MAJOR 8) set(Launcher_VERSION_MAJOR 8)
set(Launcher_VERSION_MINOR 0) set(Launcher_VERSION_MINOR 4)
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}") set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0") set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
@ -377,12 +377,12 @@ if(UNIX AND APPLE)
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}")
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns) set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
set(MACOSX_BUNDLE_COPYRIGHT "© 2022-2023 ${Launcher_Copyright_Mac}") set(MACOSX_BUNDLE_COPYRIGHT "${Launcher_Copyright_Mac}")
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed") set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed")
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed") set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed")
set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0/Sparkle-2.1.0.tar.xz" CACHE STRING "URL to Sparkle release archive") set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.5.2/Sparkle-2.5.2.tar.xz" CACHE STRING "URL to Sparkle release archive")
set(MACOSX_SPARKLE_SHA256 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive") set(MACOSX_SPARKLE_SHA256 "572dd67ae398a466f19f343a449e1890bac1ef74885b4739f68f979a8a89884b" CACHE STRING "SHA256 checksum for Sparkle release archive")
set(MACOSX_SPARKLE_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle") set(MACOSX_SPARKLE_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
# directories to look for dependencies # directories to look for dependencies
@ -504,11 +504,10 @@ else()
endif() endif()
if(NOT cmark_FOUND) if(NOT cmark_FOUND)
message(STATUS "Using bundled cmark") message(STATUS "Using bundled cmark")
set(CMARK_STATIC ON CACHE BOOL "Build static libcmark library" FORCE) set(BUILD_TESTING 0)
set(CMARK_SHARED OFF CACHE BOOL "Build shared libcmark library" FORCE) set(BUILD_SHARED_LIBS 0)
set(CMARK_TESTS OFF CACHE BOOL "Build cmark tests and enable testing" FORCE)
add_subdirectory(libraries/cmark EXCLUDE_FROM_ALL) # Markdown parser add_subdirectory(libraries/cmark EXCLUDE_FROM_ALL) # Markdown parser
add_library(cmark::cmark ALIAS cmark_static) add_library(cmark::cmark ALIAS cmark)
else() else()
message(STATUS "Using system cmark") message(STATUS "Using system cmark")
endif() endif()

View File

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

View File

@ -163,7 +163,6 @@ class Config {
QString RESOURCE_BASE = "https://resources.download.minecraft.net/"; QString RESOURCE_BASE = "https://resources.download.minecraft.net/";
QString LIBRARY_BASE = "https://libraries.minecraft.net/"; QString LIBRARY_BASE = "https://libraries.minecraft.net/";
QString AUTH_BASE = "https://authserver.mojang.com/";
QString IMGUR_BASE_URL = "https://api.imgur.com/3/"; QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists
QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists

View File

@ -6,6 +6,8 @@
<string>A Minecraft mod wants to access your camera.</string> <string>A Minecraft mod wants to access your camera.</string>
<key>NSMicrophoneUsageDescription</key> <key>NSMicrophoneUsageDescription</key>
<string>A Minecraft mod wants to access your microphone.</string> <string>A Minecraft mod wants to access your microphone.</string>
<key>NSDownloadsFolderUsageDescription</key>
<string>Prism uses access to your Downloads folder to help you more quickly add mods that can't be automatically downloaded to your instance. You can change where Prism scans for downloaded mods in Settings or the prompt that appears.</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>
<string>NSApplication</string> <string>NSApplication</string>
<key>NSHighResolutionCapable</key> <key>NSHighResolutionCapable</key>

22
flake.lock generated
View File

@ -18,7 +18,9 @@
}, },
"flake-parts": { "flake-parts": {
"inputs": { "inputs": {
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": [
"nixpkgs"
]
}, },
"locked": { "locked": {
"lastModified": 1698882062, "lastModified": 1698882062,
@ -120,24 +122,6 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs-lib": {
"locked": {
"dir": "lib",
"lastModified": 1698611440,
"narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735",
"type": "github"
},
"original": {
"dir": "lib",
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": { "pre-commit-hooks": {
"inputs": { "inputs": {
"flake-compat": [ "flake-compat": [

View File

@ -1,15 +1,25 @@
{ {
description = "A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once (Fork of MultiMC)"; description = "A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once (Fork of MultiMC)";
nixConfig = {
extra-substituters = ["https://cache.garnix.io"];
extra-trusted-public-keys = ["cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="];
};
inputs = { inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
flake-parts.url = "github:hercules-ci/flake-parts"; flake-parts = {
url = "github:hercules-ci/flake-parts";
inputs.nixpkgs-lib.follows = "nixpkgs";
};
nix-filter.url = "github:numtide/nix-filter"; nix-filter.url = "github:numtide/nix-filter";
pre-commit-hooks = { pre-commit-hooks = {
url = "github:cachix/pre-commit-hooks.nix"; url = "github:cachix/pre-commit-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs"; inputs = {
inputs.nixpkgs-stable.follows = "nixpkgs"; nixpkgs.follows = "nixpkgs";
inputs.flake-compat.follows = "flake-compat"; nixpkgs-stable.follows = "nixpkgs";
flake-compat.follows = "flake-compat";
};
}; };
flake-compat = { flake-compat = {
url = "github:edolstra/flake-compat"; url = "github:edolstra/flake-compat";

View File

@ -1,6 +1,6 @@
id: org.prismlauncher.PrismLauncher id: org.prismlauncher.PrismLauncher
runtime: org.kde.Platform runtime: org.kde.Platform
runtime-version: "5.15-23.08" runtime-version: 5.15-23.08
sdk: org.kde.Sdk sdk: org.kde.Sdk
sdk-extensions: sdk-extensions:
- org.freedesktop.Sdk.Extension.openjdk17 - org.freedesktop.Sdk.Extension.openjdk17
@ -62,7 +62,8 @@ modules:
config-opts: config-opts:
- -DCMAKE_BUILD_TYPE=RelWithDebInfo - -DCMAKE_BUILD_TYPE=RelWithDebInfo
- -DBUILD_SHARED_LIBS:BOOL=ON - -DBUILD_SHARED_LIBS:BOOL=ON
- -DGLFW_USE_WAYLAND=ON - -DGLFW_USE_WAYLAND:BOOL=ON
- -DGLFW_BUILD_DOCS:BOOL=OFF
sources: sources:
- type: git - type: git
url: https://github.com/glfw/glfw.git url: https://github.com/glfw/glfw.git
@ -104,18 +105,15 @@ modules:
- install -Dm755 ../data/gamemoderun -t /app/bin - install -Dm755 ../data/gamemoderun -t /app/bin
sources: sources:
- type: archive - type: archive
archive-type: tar-gzip dest-filename: gamemode.tar.gz
url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.7 url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.8.1
sha256: 57ce73ba605d1cf12f8d13725006a895182308d93eba0f69f285648449641803 sha256: 969cf85b5ca3944f3e315cd73a0ee9bea4f9c968cd7d485e9f4745bc1e679c4e
x-checker-data: x-checker-data:
type: json type: json
url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest
version-query: .tag_name version-query: .tag_name
url-query: .tarball_url url-query: .tarball_url
timestamp-query: .published_at timestamp-query: .published_at
# from https://github.com/flathub/net.gaijin.WarThunder/blob/7ea6f7a9f84b9c77150c003a7059dc03f8dcbc7f/gamemode.patch
- type: patch
path: patches/gamemode.patch
cleanup: cleanup:
- /include - /include
- /lib/pkgconfig - /lib/pkgconfig

View File

@ -1,12 +0,0 @@
diff -ruN a/common/common-pidfds.c b/common/common-pidfds.c
--- a/common/common-pidfds.c 2021-02-18 20:00:12.000000000 +0100
+++ b/common/common-pidfds.c 2023-09-07 08:57:42.954362763 +0200
@@ -58,6 +58,8 @@
{
return (int)syscall(__NR_pidfd_open, pid, flags);
}
+#else
+#include <sys/pidfd.h>
#endif
/* pidfd functions */

@ -1 +1 @@
Subproject commit 45094ca570be383d06df729b6972830ec63bd3df Subproject commit f2b0c16a2a217a1822ce5a6538ba8f755ed1dd32

View File

@ -225,6 +225,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// Don't quit on hiding the last window // Don't quit on hiding the last window
this->setQuitOnLastWindowClosed(false); this->setQuitOnLastWindowClosed(false);
this->setQuitLockEnabled(false);
// Commandline parsing // Commandline parsing
QCommandLineParser parser; QCommandLineParser parser;
@ -494,8 +495,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
} }
{ {
qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << ", (c) 2022-2023 " qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME + ", " + QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
<< qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", "));
qDebug() << "Version : " << BuildConfig.printableVersionString(); qDebug() << "Version : " << BuildConfig.printableVersionString();
qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM; qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM;
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
@ -749,6 +749,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("ModrinthToken", ""); m_settings->registerSetting("ModrinthToken", "");
m_settings->registerSetting("UserAgentOverride", ""); m_settings->registerSetting("UserAgentOverride", "");
// FTBApp instances
m_settings->registerSetting("FTBAppInstancesPath", "");
// Init page provider // Init page provider
{ {
m_globalSettingsProvider = std::make_shared<GenericPageProvider>(tr("Settings")); m_globalSettingsProvider = std::make_shared<GenericPageProvider>(tr("Settings"));
@ -1511,6 +1514,17 @@ InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString pa
auto& window = extras.window; auto& window = extras.window;
if (window) { if (window) {
// If the window is minimized on macOS or Windows, activate and bring it up
#ifdef Q_OS_MACOS
if (window->isMinimized()) {
window->setWindowState(window->windowState() & ~Qt::WindowMinimized);
}
#elif defined(Q_OS_WIN)
if (window->isMinimized()) {
window->showNormal();
}
#endif
window->raise(); window->raise();
window->activateWindow(); window->activateWindow();
} else { } else {
@ -1518,6 +1532,7 @@ InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString pa
m_openWindows++; m_openWindows++;
connect(window, &InstanceWindow::isClosing, this, &Application::on_windowClose); connect(window, &InstanceWindow::isClosing, this, &Application::on_windowClose);
} }
if (!page.isEmpty()) { if (!page.isEmpty()) {
window->selectPage(page); window->selectPage(page);
} }

View File

@ -64,6 +64,8 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
m_settings->registerSetting("lastLaunchTime", 0); m_settings->registerSetting("lastLaunchTime", 0);
m_settings->registerSetting("totalTimePlayed", 0); m_settings->registerSetting("totalTimePlayed", 0);
if (m_settings->get("totalTimePlayed").toLongLong() < 0)
m_settings->reset("totalTimePlayed");
m_settings->registerSetting("lastTimePlayed", 0); m_settings->registerSetting("lastTimePlayed", 0);
m_settings->registerSetting("linkedInstances", "[]"); m_settings->registerSetting("linkedInstances", "[]");

View File

@ -827,6 +827,8 @@ SET(LAUNCHER_SOURCES
ui/themes/DarkTheme.h ui/themes/DarkTheme.h
ui/themes/ITheme.cpp ui/themes/ITheme.cpp
ui/themes/ITheme.h ui/themes/ITheme.h
ui/themes/HintOverrideProxyStyle.cpp
ui/themes/HintOverrideProxyStyle.h
ui/themes/SystemTheme.cpp ui/themes/SystemTheme.cpp
ui/themes/SystemTheme.h ui/themes/SystemTheme.h
ui/themes/IconTheme.cpp ui/themes/IconTheme.cpp

View File

@ -37,140 +37,33 @@
#include <QDesktopServices> #include <QDesktopServices>
#include <QDir> #include <QDir>
#include <QProcess> #include <QProcess>
#include "FileSystem.h"
/**
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing.
*/
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
template <typename T>
bool IndirectOpen(T callable, qint64* pid_forked = nullptr)
{
auto pid = fork();
if (pid_forked) {
if (pid > 0)
*pid_forked = pid;
else
*pid_forked = 0;
}
if (pid == -1) {
qWarning() << "IndirectOpen failed to fork: " << errno;
return false;
}
// child - do the stuff
if (pid == 0) {
// unset all this garbage so it doesn't get passed to the child process
qunsetenv("LD_PRELOAD");
qunsetenv("LD_LIBRARY_PATH");
qunsetenv("LD_DEBUG");
qunsetenv("QT_PLUGIN_PATH");
qunsetenv("QT_FONTPATH");
// open the URL
auto status = callable();
// detach from the parent process group.
setsid();
// die. now. do not clean up anything, it would just hang forever.
_exit(status ? 0 : 1);
} else {
// parent - assume it worked.
int status;
while (waitpid(pid, &status, 0)) {
if (WIFEXITED(status)) {
return WEXITSTATUS(status) == 0;
}
if (WIFSIGNALED(status)) {
return false;
}
}
return true;
}
}
#endif
namespace DesktopServices { namespace DesktopServices {
bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists) bool openPath(const QFileInfo& path, bool ensureFolderPathExists)
{ {
qDebug() << "Opening directory" << path; qDebug() << "Opening path" << path;
QDir parentPath; if (ensureFolderPathExists) {
QDir dir(path); FS::ensureFolderPathExists(path);
if (ensureExists && !dir.exists()) {
parentPath.mkpath(dir.absolutePath());
} }
auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); }; return openUrl(QUrl::fromLocalFile(QFileInfo(path).absoluteFilePath()));
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
if (!isSandbox()) {
return IndirectOpen(f);
}
#endif
return f();
} }
bool openFile(const QString& path) bool openPath(const QString& path, bool ensureFolderPathExists)
{ {
qDebug() << "Opening file" << path; return openPath(QFileInfo(path), ensureFolderPathExists);
auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); };
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
if (!isSandbox()) {
return IndirectOpen(f);
} else {
return f();
}
#else
return f();
#endif
}
bool openFile(const QString& application, const QString& path, const QString& workingDirectory, qint64* pid)
{
qDebug() << "Opening file" << path << "using" << application;
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
if (!isSandbox()) {
return IndirectOpen([&]() { return QProcess::startDetached(application, QStringList() << path, workingDirectory); }, pid);
} else {
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
}
#else
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
#endif
} }
bool run(const QString& application, const QStringList& args, const QString& workingDirectory, qint64* pid) bool run(const QString& application, const QStringList& args, const QString& workingDirectory, qint64* pid)
{ {
qDebug() << "Running" << application << "with args" << args.join(' '); qDebug() << "Running" << application << "with args" << args.join(' ');
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
if (!isSandbox()) {
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
return IndirectOpen([&]() { return QProcess::startDetached(application, args, workingDirectory); }, pid);
} else {
return QProcess::startDetached(application, args, workingDirectory, pid); return QProcess::startDetached(application, args, workingDirectory, pid);
} }
#else
return QProcess::startDetached(application, args, workingDirectory, pid);
#endif
}
bool openUrl(const QUrl& url) bool openUrl(const QUrl& url)
{ {
qDebug() << "Opening URL" << url.toString(); qDebug() << "Opening URL" << url.toString();
auto f = [&]() { return QDesktopServices::openUrl(url); }; return QDesktopServices::openUrl(url);
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
if (!isSandbox()) {
return IndirectOpen(f);
} else {
return f();
}
#else
return f();
#endif
} }
bool isFlatpak() bool isFlatpak()
@ -191,9 +84,4 @@ bool isSnap()
#endif #endif
} }
bool isSandbox()
{
return isSnap() || isFlatpak();
}
} // namespace DesktopServices } // namespace DesktopServices

View File

@ -3,31 +3,30 @@
#include <QString> #include <QString>
#include <QUrl> #include <QUrl>
class QFileInfo;
/** /**
* This wraps around QDesktopServices and adds workarounds where needed * This wraps around QDesktopServices and adds workarounds where needed
* Use this instead of QDesktopServices! * Use this instead of QDesktopServices!
*/ */
namespace DesktopServices { namespace DesktopServices {
/** /**
* Open a file in whatever application is applicable * Open a path in whatever application is applicable.
* @param ensureFolderPathExists Make sure the path exists
*/ */
bool openFile(const QString& path); bool openPath(const QFileInfo& path, bool ensureFolderPathExists = false);
/** /**
* Open a file in the specified application * Open a path in whatever application is applicable.
* @param ensureFolderPathExists Make sure the path exists
*/ */
bool openFile(const QString& application, const QString& path, const QString& workingDirectory = QString(), qint64* pid = 0); bool openPath(const QString& path, bool ensureFolderPathExists = false);
/** /**
* Run an application * Run an application
*/ */
bool run(const QString& application, const QStringList& args, const QString& workingDirectory = QString(), qint64* pid = 0); bool run(const QString& application, const QStringList& args, const QString& workingDirectory = QString(), qint64* pid = 0);
/**
* Open a directory
*/
bool openDirectory(const QString& path, bool ensureExists = false);
/** /**
* Open the URL, most likely in a browser. Maybe. * Open the URL, most likely in a browser. Maybe.
*/ */
@ -42,9 +41,4 @@ bool isFlatpak();
* Determine whether the launcher is running in a Snap environment * Determine whether the launcher is running in a Snap environment
*/ */
bool isSnap(); bool isSnap();
/**
* Determine whether the launcher is running in a sandboxed (Flatpak or Snap) environment
*/
bool isSandbox();
} // namespace DesktopServices } // namespace DesktopServices

View File

@ -1,4 +1,37 @@
// Licensed under the Apache-2.0 license. See README.md for details. // SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2024 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once #pragma once
@ -8,12 +41,12 @@
class Exception : public std::exception { class Exception : public std::exception {
public: public:
Exception(const QString& message) : std::exception(), m_message(message) { qCritical() << "Exception:" << message; } Exception(const QString& message) : std::exception(), m_message(message.toUtf8()) { qCritical() << "Exception:" << message; }
Exception(const Exception& other) : std::exception(), m_message(other.cause()) {} Exception(const Exception& other) : std::exception(), m_message(other.m_message) {}
virtual ~Exception() noexcept {} virtual ~Exception() noexcept {}
const char* what() const noexcept { return m_message.toLatin1().constData(); } const char* what() const noexcept { return m_message.constData(); }
QString cause() const { return m_message; } QString cause() const { return QString::fromUtf8(m_message); }
private: private:
QString m_message; QByteArray m_message;
}; };

View File

@ -272,15 +272,19 @@ bool ensureFilePathExists(QString filenamepath)
return success; return success;
} }
bool ensureFolderPathExists(QString foldernamepath) bool ensureFolderPathExists(const QFileInfo folderPath)
{ {
QFileInfo a(foldernamepath);
QDir dir; QDir dir;
QString ensuredPath = a.filePath(); QString ensuredPath = folderPath.filePath();
bool success = dir.mkpath(ensuredPath); bool success = dir.mkpath(ensuredPath);
return success; return success;
} }
bool ensureFolderPathExists(const QString folderPathName)
{
return ensureFolderPathExists(QFileInfo(folderPathName));
}
bool copyFileAttributes(QString src, QString dst) bool copyFileAttributes(QString src, QString dst)
{ {
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
@ -797,18 +801,70 @@ QString NormalizePath(QString path)
} }
} }
QString badFilenameChars = "\"\\/?<>:;*|!+\r\n"; static const QString BAD_WIN_CHARS = "<>:\"|?*\r\n";
static const QString BAD_NTFS_CHARS = "<>:\"|?*";
static const QString BAD_HFS_CHARS = ":";
static const QString BAD_FILENAME_CHARS = BAD_WIN_CHARS + "\\/";
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith) QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
{ {
for (int i = 0; i < string.length(); i++) { for (int i = 0; i < string.length(); i++)
if (badFilenameChars.contains(string[i])) { if (string.at(i) < ' ' || BAD_FILENAME_CHARS.contains(string.at(i)))
string[i] = replaceWith; string[i] = replaceWith;
}
}
return string; return string;
} }
QString RemoveInvalidPathChars(QString path, QChar replaceWith)
{
QString invalidChars;
#ifdef Q_OS_WIN
invalidChars = BAD_WIN_CHARS;
#endif
// the null character is ignored in this check as it was not a problem until now
switch (statFS(path).fsType) {
case FilesystemType::FAT: // similar to NTFS
/* fallthrough */
case FilesystemType::NTFS:
/* fallthrough */
case FilesystemType::REFS: // similar to NTFS(should be available only on windows)
invalidChars += BAD_NTFS_CHARS;
break;
// case FilesystemType::EXT:
// case FilesystemType::EXT_2_OLD:
// case FilesystemType::EXT_2_3_4:
// case FilesystemType::XFS:
// case FilesystemType::BTRFS:
// case FilesystemType::NFS:
// case FilesystemType::ZFS:
case FilesystemType::APFS:
/* fallthrough */
case FilesystemType::HFS:
/* fallthrough */
case FilesystemType::HFSPLUS:
/* fallthrough */
case FilesystemType::HFSX:
invalidChars += BAD_HFS_CHARS;
break;
// case FilesystemType::FUSEBLK:
// case FilesystemType::F2FS:
// case FilesystemType::UNKNOWN:
default:
break;
}
if (invalidChars.size() != 0) {
for (int i = 0; i < path.length(); i++) {
if (path.at(i) < ' ' || invalidChars.contains(path.at(i))) {
path[i] = replaceWith;
}
}
}
return path;
}
QString DirNameFromString(QString string, QString inDir) QString DirNameFromString(QString string, QString inDir)
{ {
int num = 0; int num = 0;
@ -1581,4 +1637,44 @@ uintmax_t hardLinkCount(const QString& path)
return count; return count;
} }
#ifdef Q_OS_WIN
// returns 8.3 file format from long path
QString shortPathName(const QString& file)
{
auto input = file.toStdWString();
std::wstring output;
long length = GetShortPathNameW(input.c_str(), NULL, 0);
if (length == 0)
return {};
// NOTE: this resizing might seem weird...
// when GetShortPathNameW fails, it returns length including null character
// when it succeeds, it returns length excluding null character
// See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx
output.resize(length);
if (GetShortPathNameW(input.c_str(), (LPWSTR)output.c_str(), length) == 0)
return {};
output.resize(length - 1);
QString ret = QString::fromStdWString(output);
return ret;
}
// if the string survives roundtrip through local 8bit encoding...
bool fitsInLocal8bit(const QString& string)
{
return string == QString::fromLocal8Bit(string.toLocal8Bit());
}
QString getPathNameInLocal8bit(const QString& file)
{
if (!fitsInLocal8bit(file)) {
auto path = shortPathName(file);
if (!path.isEmpty()) {
return path;
}
// in case shortPathName fails just return the path as is
}
return file;
}
#endif
} // namespace FS } // namespace FS

View File

@ -91,7 +91,13 @@ bool ensureFilePathExists(QString filenamepath);
* Creates all the folders in a path for the specified path * Creates all the folders in a path for the specified path
* last segment of the path is treated as a folder name and is created! * last segment of the path is treated as a folder name and is created!
*/ */
bool ensureFolderPathExists(QString filenamepath); bool ensureFolderPathExists(const QFileInfo folderPath);
/**
* Creates all the folders in a path for the specified path
* last segment of the path is treated as a folder name and is created!
*/
bool ensureFolderPathExists(const QString folderPathName);
/** /**
* @brief Copies a directory and it's contents from src to dest * @brief Copies a directory and it's contents from src to dest
@ -336,6 +342,8 @@ QString NormalizePath(QString path);
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith = '-'); QString RemoveInvalidFilenameChars(QString string, QChar replaceWith = '-');
QString RemoveInvalidPathChars(QString string, QChar replaceWith = '-');
QString DirNameFromString(QString string, QString inDir = "."); QString DirNameFromString(QString string, QString inDir = ".");
/// Checks if the a given Path contains "!" /// Checks if the a given Path contains "!"
@ -370,6 +378,7 @@ enum class FilesystemType {
HFSX, HFSX,
FUSEBLK, FUSEBLK,
F2FS, F2FS,
BCACHEFS,
UNKNOWN UNKNOWN
}; };
@ -398,6 +407,7 @@ static const QMap<FilesystemType, QStringList> s_filesystem_type_names = { { Fil
{ FilesystemType::HFSX, { "HFSX" } }, { FilesystemType::HFSX, { "HFSX" } },
{ FilesystemType::FUSEBLK, { "FUSEBLK" } }, { FilesystemType::FUSEBLK, { "FUSEBLK" } },
{ FilesystemType::F2FS, { "F2FS" } }, { FilesystemType::F2FS, { "F2FS" } },
{ FilesystemType::BCACHEFS, { "BCACHEFS" } },
{ FilesystemType::UNKNOWN, { "UNKNOWN" } } }; { FilesystemType::UNKNOWN, { "UNKNOWN" } } };
/** /**
@ -450,7 +460,7 @@ QString nearestExistentAncestor(const QString& path);
FilesystemInfo statFS(const QString& path); FilesystemInfo statFS(const QString& path);
static const QList<FilesystemType> s_clone_filesystems = { FilesystemType::BTRFS, FilesystemType::APFS, FilesystemType::ZFS, static const QList<FilesystemType> s_clone_filesystems = { FilesystemType::BTRFS, FilesystemType::APFS, FilesystemType::ZFS,
FilesystemType::XFS, FilesystemType::REFS }; FilesystemType::XFS, FilesystemType::REFS, FilesystemType::BCACHEFS };
/** /**
* @brief if the Filesystem is reflink/clone capable * @brief if the Filesystem is reflink/clone capable
@ -545,4 +555,8 @@ bool canLink(const QString& src, const QString& dst);
uintmax_t hardLinkCount(const QString& path); uintmax_t hardLinkCount(const QString& path);
#ifdef Q_OS_WIN
QString getPathNameInLocal8bit(const QString& file);
#endif
} // namespace FS } // namespace FS

View File

@ -142,9 +142,8 @@ void InstanceCopyTask::copyFinished()
if (!m_keepPlaytime) { if (!m_keepPlaytime) {
inst->resetTimePlayed(); inst->resetTimePlayed();
} }
if (m_useLinks)
inst->addLinkedInstanceId(m_origInstance->id());
if (m_useLinks) { if (m_useLinks) {
inst->addLinkedInstanceId(m_origInstance->id());
auto allowed_symlinks_file = QFileInfo(FS::PathCombine(inst->gameRoot(), "allowed_symlinks.txt")); auto allowed_symlinks_file = QFileInfo(FS::PathCombine(inst->gameRoot(), "allowed_symlinks.txt"));
QByteArray allowed_symlinks; QByteArray allowed_symlinks;

View File

@ -47,9 +47,6 @@
#include <optional> #include <optional>
class QuaZip; class QuaZip;
namespace Flame {
class FileResolvingTask;
}
class InstanceImportTask : public InstanceTask { class InstanceImportTask : public InstanceTask {
Q_OBJECT Q_OBJECT
@ -79,7 +76,6 @@ class InstanceImportTask : public InstanceTask {
private: /* data */ private: /* data */
NetJob::Ptr m_filesNetJob; NetJob::Ptr m_filesNetJob;
shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver;
QUrl m_sourceUrl; QUrl m_sourceUrl;
QString m_archivePath; QString m_archivePath;
bool m_downloadRequired = false; bool m_downloadRequired = false;

View File

@ -38,6 +38,7 @@
#include <QDir> #include <QDir>
#include <QDirIterator> #include <QDirIterator>
#include <QFile> #include <QFile>
#include <QFileInfo>
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
@ -847,14 +848,16 @@ class InstanceStaging : public Task {
const unsigned maxBackoff = 16; const unsigned maxBackoff = 16;
public: public:
InstanceStaging(InstanceList* parent, InstanceTask* child, QString stagingPath, InstanceName const& instanceName, QString groupName) InstanceStaging(InstanceList* parent, InstanceTask* child, SettingsObjectPtr settings)
: m_parent(parent) : m_parent(parent), backoff(minBackoff, maxBackoff)
, backoff(minBackoff, maxBackoff)
, m_stagingPath(std::move(stagingPath))
, m_instance_name(std::move(instanceName))
, m_groupName(std::move(groupName))
{ {
m_stagingPath = parent->getStagedInstancePath();
m_child.reset(child); m_child.reset(child);
m_child->setStagingPath(m_stagingPath);
m_child->setParentSettings(std::move(settings));
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceeded); connect(child, &Task::succeeded, this, &InstanceStaging::childSucceeded);
connect(child, &Task::failed, this, &InstanceStaging::childFailed); connect(child, &Task::failed, this, &InstanceStaging::childFailed);
connect(child, &Task::aborted, this, &InstanceStaging::childAborted); connect(child, &Task::aborted, this, &InstanceStaging::childAborted);
@ -866,7 +869,7 @@ class InstanceStaging : public Task {
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceeded); connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceeded);
} }
virtual ~InstanceStaging(){}; virtual ~InstanceStaging() {}
// FIXME/TODO: add ability to abort during instance commit retries // FIXME/TODO: add ability to abort during instance commit retries
bool abort() override bool abort() override
@ -881,14 +884,22 @@ class InstanceStaging : public Task {
bool canAbort() const override { return (m_child && m_child->canAbort()); } bool canAbort() const override { return (m_child && m_child->canAbort()); }
protected: protected:
virtual void executeTask() override { m_child->start(); } virtual void executeTask() override
{
if (m_stagingPath.isNull()) {
emitFailed(tr("Could not create staging folder"));
return;
}
m_child->start();
}
QStringList warnings() const override { return m_child->warnings(); } QStringList warnings() const override { return m_child->warnings(); }
private slots: private slots:
void childSucceeded() void childSucceeded()
{ {
unsigned sleepTime = backoff(); unsigned sleepTime = backoff();
if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get())) { if (m_parent->commitStagedInstance(m_stagingPath, *m_child.get(), m_child->group(), *m_child.get())) {
emitSucceeded(); emitSucceeded();
return; return;
} }
@ -897,7 +908,7 @@ class InstanceStaging : public Task {
emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something.")); emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
return; return;
} }
qDebug() << "Failed to commit instance" << m_instance_name.name() << "Initiating backoff:" << sleepTime; qDebug() << "Failed to commit instance" << m_child->name() << "Initiating backoff:" << sleepTime;
m_backoffTimer.start(sleepTime * 500); m_backoffTimer.start(sleepTime * 500);
} }
void childFailed(const QString& reason) void childFailed(const QString& reason)
@ -906,7 +917,11 @@ class InstanceStaging : public Task {
emitFailed(reason); emitFailed(reason);
} }
void childAborted() { emitAborted(); } void childAborted()
{
m_parent->destroyStagingPath(m_stagingPath);
emitAborted();
}
private: private:
InstanceList* m_parent; InstanceList* m_parent;
@ -918,34 +933,35 @@ class InstanceStaging : public Task {
ExponentialSeries backoff; ExponentialSeries backoff;
QString m_stagingPath; QString m_stagingPath;
unique_qobject_ptr<InstanceTask> m_child; unique_qobject_ptr<InstanceTask> m_child;
InstanceName m_instance_name;
QString m_groupName;
QTimer m_backoffTimer; QTimer m_backoffTimer;
}; };
Task* InstanceList::wrapInstanceTask(InstanceTask* task) Task* InstanceList::wrapInstanceTask(InstanceTask* task)
{ {
auto stagingPath = getStagedInstancePath(); return new InstanceStaging(this, task, m_globalSettings);
task->setStagingPath(stagingPath);
task->setParentSettings(m_globalSettings);
return new InstanceStaging(this, task, stagingPath, *task, task->group());
} }
QString InstanceList::getStagedInstancePath() QString InstanceList::getStagedInstancePath()
{ {
QString key = QUuid::createUuid().toString(QUuid::WithoutBraces); const QString tempRoot = FS::PathCombine(m_instDir, ".tmp");
QString tempDir = ".LAUNCHER_TEMP/";
QString relPath = FS::PathCombine(tempDir, key); QString result;
QDir rootPath(m_instDir); int tries = 0;
auto path = FS::PathCombine(m_instDir, relPath);
if (!rootPath.mkpath(relPath)) { do {
return QString(); if (++tries > 256)
} return {};
const QString key = QUuid::createUuid().toString(QUuid::Id128).left(6);
result = FS::PathCombine(tempRoot, key);
} while (QFileInfo::exists(result));
if (!QDir::current().mkpath(result))
return {};
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
auto tempPath = FS::PathCombine(m_instDir, tempDir); SetFileAttributesA(tempRoot.toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
SetFileAttributesA(tempPath.toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
#endif #endif
return path; return result;
} }
bool InstanceList::commitStagedInstance(const QString& path, bool InstanceList::commitStagedInstance(const QString& path,

View File

@ -2,6 +2,8 @@
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
#include <QPushButton>
InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name) InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name)
{ {
auto dialog = auto dialog =
@ -27,16 +29,15 @@ ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name)
"separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before " "separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before "
"updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).") "updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).")
.arg(original_version_name), .arg(original_version_name),
QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort); QMessageBox::Information, QMessageBox::Cancel);
info->setButtonText(QMessageBox::Ok, QObject::tr("Update existing instance")); QAbstractButton* update = info->addButton(QObject::tr("Update existing instance"), QMessageBox::AcceptRole);
info->setButtonText(QMessageBox::Abort, QObject::tr("Create new instance")); QAbstractButton* skip = info->addButton(QObject::tr("Create new instance"), QMessageBox::ResetRole);
info->setButtonText(QMessageBox::Reset, QObject::tr("Cancel"));
info->exec(); info->exec();
if (info->clickedButton() == info->button(QMessageBox::Ok)) if (info->clickedButton() == update)
return ShouldUpdate::Update; return ShouldUpdate::Update;
if (info->clickedButton() == info->button(QMessageBox::Abort)) if (info->clickedButton() == skip)
return ShouldUpdate::SkipUpdating; return ShouldUpdate::SkipUpdating;
return ShouldUpdate::Cancel; return ShouldUpdate::Cancel;
} }

View File

@ -42,7 +42,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/EditAccountDialog.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"
@ -144,6 +143,12 @@ void LaunchController::login()
bool tryagain = true; bool tryagain = true;
unsigned int tries = 0; unsigned int tries = 0;
if (m_accountToUse->accountType() != AccountType::Offline && m_accountToUse->accountState() == AccountState::Offline) {
// Force account refresh on the account used to launch the instance updating the AccountState
// only on first try and if it is not meant to be offline
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) {
auto result = auto result =
@ -250,12 +255,6 @@ void LaunchController::login()
progDialog.execWithTask(task.get()); progDialog.execWithTask(task.get());
continue; continue;
} }
// FIXME: this is missing - the meaning is that the account is queued for refresh and we should wait for that
/*
case AccountState::Queued: {
return;
}
*/
case AccountState::Expired: { case AccountState::Expired: {
auto errorString = tr("The account has expired and needs to be logged into manually again."); auto errorString = tr("The account has expired and needs to be logged into manually again.");
QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), errorString, QMessageBox::StandardButton::Ok, QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), errorString, QMessageBox::StandardButton::Ok,
@ -317,7 +316,7 @@ void LaunchController::launchInstance()
online_mode = "online"; online_mode = "online";
// Prepend Server Status // Prepend Server Status
QStringList servers = { "authserver.mojang.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" }; QStringList servers = { "login.microsoftonline.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" };
QString resolved_servers = ""; QString resolved_servers = "";
QHostInfo host_info; QHostInfo host_info;

View File

@ -119,6 +119,7 @@ bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool follow
bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks) bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks)
{ {
QuaZip zip(fileCompressed); QuaZip zip(fileCompressed);
zip.setUtf8Enabled(true);
QDir().mkpath(QFileInfo(fileCompressed).absolutePath()); QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
if (!zip.open(QuaZip::mdCreate)) { if (!zip.open(QuaZip::mdCreate)) {
QFile::remove(fileCompressed); QFile::remove(fileCompressed);
@ -141,6 +142,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files,
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods) bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods)
{ {
QuaZip zipOut(targetJarPath); QuaZip zipOut(targetJarPath);
zipOut.setUtf8Enabled(true);
if (!zipOut.open(QuaZip::mdCreate)) { if (!zipOut.open(QuaZip::mdCreate)) {
QFile::remove(targetJarPath); QFile::remove(targetJarPath);
qCritical() << "Failed to open the minecraft.jar for modding"; qCritical() << "Failed to open the minecraft.jar for modding";
@ -286,10 +288,11 @@ std::optional<QStringList> extractSubDir(QuaZip* zip, const QString& subdir, con
do { do {
QString file_name = zip->getCurrentFileName(); QString file_name = zip->getCurrentFileName();
file_name = FS::RemoveInvalidPathChars(file_name);
if (!file_name.startsWith(subdir)) if (!file_name.startsWith(subdir))
continue; continue;
auto relative_file_name = QDir::fromNativeSeparators(file_name.remove(0, subdir.size())); auto relative_file_name = QDir::fromNativeSeparators(file_name.mid(subdir.size()));
auto original_name = relative_file_name; auto original_name = relative_file_name;
// Fix subdirs/files ending with a / getting transformed into absolute paths // Fix subdirs/files ending with a / getting transformed into absolute paths

View File

@ -154,7 +154,12 @@ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, Q
#if defined(LAUNCHER_APPLICATION) #if defined(LAUNCHER_APPLICATION)
class ExportToZipTask : public Task { class ExportToZipTask : public Task {
public: public:
ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) ExportToZipTask(QString outputPath,
QDir dir,
QFileInfoList files,
QString destinationPrefix = "",
bool followSymlinks = false,
bool utf8Enabled = false)
: m_output_path(outputPath) : m_output_path(outputPath)
, m_output(outputPath) , m_output(outputPath)
, m_dir(dir) , m_dir(dir)
@ -163,9 +168,15 @@ class ExportToZipTask : public Task {
, m_follow_symlinks(followSymlinks) , m_follow_symlinks(followSymlinks)
{ {
setAbortable(true); setAbortable(true);
m_output.setUtf8Enabled(utf8Enabled);
}; };
ExportToZipTask(QString outputPath, QString dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) ExportToZipTask(QString outputPath,
: ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks){}; QString dir,
QFileInfoList files,
QString destinationPrefix = "",
bool followSymlinks = false,
bool utf8Enabled = false)
: ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks, utf8Enabled){};
virtual ~ExportToZipTask() = default; virtual ~ExportToZipTask() = default;

View File

@ -212,3 +212,25 @@ QPair<QString, QString> StringUtils::splitFirst(const QString& s, const QRegular
right = s.mid(end); right = s.mid(end);
return qMakePair(left, right); return qMakePair(left, right);
} }
static const QRegularExpression ulMatcher("<\\s*/\\s*ul\\s*>");
QString StringUtils::htmlListPatch(QString htmlStr)
{
int pos = htmlStr.indexOf(ulMatcher);
int imgPos;
while (pos != -1) {
pos = htmlStr.indexOf(">", pos) + 1; // Get the size of the </ul> tag. Add one for zeroeth index
imgPos = htmlStr.indexOf("<img ", pos);
if (imgPos == -1)
break; // no image after the tag
auto textBetween = htmlStr.mid(pos, imgPos - pos).trimmed(); // trim all white spaces
if (textBetween.isEmpty())
htmlStr.insert(pos, "<br>");
pos = htmlStr.indexOf(ulMatcher, pos);
}
return htmlStr;
}

View File

@ -85,4 +85,6 @@ QPair<QString, QString> splitFirst(const QString& s, const QString& sep, Qt::Cas
QPair<QString, QString> splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive); QPair<QString, QString> splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive);
QPair<QString, QString> splitFirst(const QString& s, const QRegularExpression& re); QPair<QString, QString> splitFirst(const QString& s, const QRegularExpression& re);
QString htmlListPatch(QString htmlStr);
} // namespace StringUtils } // namespace StringUtils

View File

@ -55,6 +55,9 @@ void JavaChecker::performCheck()
qDebug() << "Java checker library could not be found. Please check your installation."; qDebug() << "Java checker library could not be found. Please check your installation.";
return; return;
} }
#ifdef Q_OS_WIN
checkerJar = FS::getPathNameInLocal8bit(checkerJar);
#endif
QStringList args; QStringList args;

View File

@ -207,7 +207,7 @@ QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
QString newKeyName = keyName + "\\" + newSubkeyName + subkeySuffix; QString newKeyName = keyName + "\\" + newSubkeyName + subkeySuffix;
HKEY newKey; HKEY newKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, newKeyName.toStdWString().c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &newKey) == if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, newKeyName.toStdWString().c_str(), 0, KEY_READ | keyType, &newKey) ==
ERROR_SUCCESS) { ERROR_SUCCESS) {
// Read the JavaHome value to find where Java is installed. // Read the JavaHome value to find where Java is installed.
DWORD valueSz = 0; DWORD valueSz = 0;
@ -283,6 +283,12 @@ QList<QString> JavaUtils::FindJavaPaths()
QList<JavaInstallPtr> ADOPTIUMJDK64s = QList<JavaInstallPtr> ADOPTIUMJDK64s =
this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI");
// IBM Semeru
QList<JavaInstallPtr> SEMERUJRE32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Semeru\\JRE", "Path", "\\openj9\\MSI");
QList<JavaInstallPtr> SEMERUJRE64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Semeru\\JRE", "Path", "\\openj9\\MSI");
QList<JavaInstallPtr> SEMERUJDK32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Semeru\\JDK", "Path", "\\openj9\\MSI");
QList<JavaInstallPtr> SEMERUJDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Semeru\\JDK", "Path", "\\openj9\\MSI");
// Microsoft // Microsoft
QList<JavaInstallPtr> MICROSOFTJDK64s = QList<JavaInstallPtr> MICROSOFTJDK64s =
this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI"); this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI");
@ -300,6 +306,7 @@ QList<QString> JavaUtils::FindJavaPaths()
java_candidates.append(NEWJRE64s); java_candidates.append(NEWJRE64s);
java_candidates.append(ADOPTOPENJRE64s); java_candidates.append(ADOPTOPENJRE64s);
java_candidates.append(ADOPTIUMJRE64s); java_candidates.append(ADOPTIUMJRE64s);
java_candidates.append(SEMERUJRE64s);
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe"));
@ -308,6 +315,7 @@ QList<QString> JavaUtils::FindJavaPaths()
java_candidates.append(ADOPTOPENJDK64s); java_candidates.append(ADOPTOPENJDK64s);
java_candidates.append(FOUNDATIONJDK64s); java_candidates.append(FOUNDATIONJDK64s);
java_candidates.append(ADOPTIUMJDK64s); java_candidates.append(ADOPTIUMJDK64s);
java_candidates.append(SEMERUJDK64s);
java_candidates.append(MICROSOFTJDK64s); java_candidates.append(MICROSOFTJDK64s);
java_candidates.append(ZULU64s); java_candidates.append(ZULU64s);
java_candidates.append(LIBERICA64s); java_candidates.append(LIBERICA64s);
@ -316,6 +324,7 @@ QList<QString> JavaUtils::FindJavaPaths()
java_candidates.append(NEWJRE32s); java_candidates.append(NEWJRE32s);
java_candidates.append(ADOPTOPENJRE32s); java_candidates.append(ADOPTOPENJRE32s);
java_candidates.append(ADOPTIUMJRE32s); java_candidates.append(ADOPTIUMJRE32s);
java_candidates.append(SEMERUJRE32s);
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe"));
@ -324,6 +333,7 @@ QList<QString> JavaUtils::FindJavaPaths()
java_candidates.append(ADOPTOPENJDK32s); java_candidates.append(ADOPTOPENJDK32s);
java_candidates.append(FOUNDATIONJDK32s); java_candidates.append(FOUNDATIONJDK32s);
java_candidates.append(ADOPTIUMJDK32s); java_candidates.append(ADOPTIUMJDK32s);
java_candidates.append(SEMERUJDK32s);
java_candidates.append(ZULU32s); java_candidates.append(ZULU32s);
java_candidates.append(LIBERICA32s); java_candidates.append(LIBERICA32s);
@ -362,6 +372,12 @@ QList<QString> JavaUtils::FindJavaPaths()
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
} }
auto home = qEnvironmentVariable("HOME");
// javas downloaded by sdkman
javas.append(FS::PathCombine(home, ".sdkman/candidates/java"));
javas.append(getMinecraftJavaBundle()); javas.append(getMinecraftJavaBundle());
javas = addJavasFromEnv(javas); javas = addJavasFromEnv(javas);
javas.removeDuplicates(); javas.removeDuplicates();
@ -404,6 +420,7 @@ QList<QString> JavaUtils::FindJavaPaths()
// manually installed JDKs in /opt // manually installed JDKs in /opt
scanJavaDirs("/opt/jdk"); scanJavaDirs("/opt/jdk");
scanJavaDirs("/opt/jdks"); scanJavaDirs("/opt/jdks");
scanJavaDirs("/opt/ibm"); // IBM Semeru Certified Edition
// flatpak // flatpak
scanJavaDirs("/app/jdk"); scanJavaDirs("/app/jdk");
@ -413,6 +430,8 @@ QList<QString> JavaUtils::FindJavaPaths()
scanJavaDirs(FS::PathCombine(home, ".jdks")); scanJavaDirs(FS::PathCombine(home, ".jdks"));
// javas downloaded by sdkman // javas downloaded by sdkman
scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java")); scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java"));
// javas downloaded by gradle (toolchains)
scanJavaDirs(FS::PathCombine(home, ".gradle/jdks"));
javas.append(getMinecraftJavaBundle()); javas.append(getMinecraftJavaBundle());
javas = addJavasFromEnv(javas); javas = addJavasFromEnv(javas);
@ -439,26 +458,25 @@ QString JavaUtils::getJavaCheckPath()
QStringList getMinecraftJavaBundle() QStringList getMinecraftJavaBundle()
{ {
QString partialPath;
QString executable = "java"; QString executable = "java";
QStringList processpaths; QStringList processpaths;
#if defined(Q_OS_OSX) #if defined(Q_OS_OSX)
partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support"); processpaths << FS::PathCombine(QDir::homePath(), FS::PathCombine("Library", "Application Support", "minecraft", "runtime"));
#elif defined(Q_OS_WIN32) #elif defined(Q_OS_WIN32)
partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", "");
executable += "w.exe"; executable += "w.exe";
auto appDataPath = QProcessEnvironment::systemEnvironment().value("APPDATA", "");
processpaths << FS::PathCombine(QFileInfo(appDataPath).absoluteFilePath(), ".minecraft", "runtime");
// add the microsoft store version of the launcher to the search. the current path is: // add the microsoft store version of the launcher to the search. the current path is:
// C:\Users\USERNAME\AppData\Local\Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local\runtime // C:\Users\USERNAME\AppData\Local\Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local\runtime
auto localAppDataPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", "");
auto minecraftMSStorePath = auto minecraftMSStorePath =
FS::PathCombine(QFileInfo(partialPath).absolutePath(), "Local", "Packages", "Microsoft.4297127D64EC6_8wekyb3d8bbwe"); FS::PathCombine(QFileInfo(localAppDataPath).absoluteFilePath(), "Packages", "Microsoft.4297127D64EC6_8wekyb3d8bbwe");
minecraftMSStorePath = FS::PathCombine(minecraftMSStorePath, "LocalCache", "Local", "runtime"); processpaths << FS::PathCombine(minecraftMSStorePath, "LocalCache", "Local", "runtime");
processpaths << minecraftMSStorePath;
#else #else
partialPath = QDir::homePath(); processpaths << FS::PathCombine(QDir::homePath(), ".minecraft", "runtime");
#endif #endif
auto minecraftDataPath = FS::PathCombine(partialPath, ".minecraft", "runtime");
processpaths << minecraftDataPath;
QStringList javas; QStringList javas;
while (!processpaths.isEmpty()) { while (!processpaths.isEmpty()) {

View File

@ -50,6 +50,7 @@ void Library::getApplicableFiles(const RuntimeContext& runtimeContext,
{ {
bool local = isLocal(); bool local = isLocal();
auto actualPath = [&](QString relPath) { auto actualPath = [&](QString relPath) {
relPath = FS::RemoveInvalidPathChars(relPath);
QFileInfo out(FS::PathCombine(storagePrefix(), relPath)); QFileInfo out(FS::PathCombine(storagePrefix(), relPath));
if (local && !overridePath.isEmpty()) { if (local && !overridePath.isEmpty()) {
QString fileName = out.fileName(); QString fileName = out.fileName();

View File

@ -594,9 +594,6 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
QStringList preloadList; QStringList preloadList;
if (auto value = env.value("LD_PRELOAD"); !value.isEmpty()) if (auto value = env.value("LD_PRELOAD"); !value.isEmpty())
preloadList = value.split(QLatin1String(":")); preloadList = value.split(QLatin1String(":"));
QStringList libPaths;
if (auto value = env.value("LD_LIBRARY_PATH"); !value.isEmpty())
libPaths = value.split(QLatin1String(":"));
auto mangoHudLibString = MangoHud::getLibraryString(); auto mangoHudLibString = MangoHud::getLibraryString();
if (!mangoHudLibString.isEmpty()) { if (!mangoHudLibString.isEmpty()) {
@ -604,18 +601,16 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
QString libPath = mangoHudLib.absolutePath(); QString libPath = mangoHudLib.absolutePath();
auto appendLib = [libPath, &preloadList](QString fileName) { auto appendLib = [libPath, &preloadList](QString fileName) {
if (QFileInfo(FS::PathCombine(libPath, fileName)).exists()) if (QFileInfo(FS::PathCombine(libPath, fileName)).exists())
preloadList << fileName; preloadList << FS::PathCombine(libPath, fileName);
}; };
// dlsym variant is only needed for OpenGL and not included in the vulkan layer // dlsym variant is only needed for OpenGL and not included in the vulkan layer
appendLib("libMangoHud_dlsym.so"); appendLib("libMangoHud_dlsym.so");
appendLib("libMangoHud_opengl.so"); appendLib("libMangoHud_opengl.so");
appendLib(mangoHudLib.fileName()); appendLib(mangoHudLib.fileName());
libPaths << libPath;
} }
env.insert("LD_PRELOAD", preloadList.join(QLatin1String(":"))); env.insert("LD_PRELOAD", preloadList.join(QLatin1String(":")));
env.insert("LD_LIBRARY_PATH", libPaths.join(QLatin1String(":")));
env.insert("MANGOHUD", "1"); env.insert("MANGOHUD", "1");
} }
@ -662,9 +657,13 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, Mine
} }
if (serverToJoin && !serverToJoin->address.isEmpty()) { if (serverToJoin && !serverToJoin->address.isEmpty()) {
if (profile->hasTrait("feature:is_quick_play_multiplayer")) {
args_pattern += " --quickPlayMultiplayer " + serverToJoin->address + ':' + QString::number(serverToJoin->port);
} else {
args_pattern += " --server " + serverToJoin->address; args_pattern += " --server " + serverToJoin->address;
args_pattern += " --port " + QString::number(serverToJoin->port); args_pattern += " --port " + QString::number(serverToJoin->port);
} }
}
QMap<QString, QString> token_mapping; QMap<QString, QString> token_mapping;
// yggdrasil! // yggdrasil!
@ -993,7 +992,7 @@ QString MinecraftInstance::getStatusbarDescription()
QString description; QString description;
description.append(tr("Minecraft %1").arg(mcVersion)); description.append(tr("Minecraft %1").arg(mcVersion));
if (m_settings->get("ShowGameTime").toBool()) { if (m_settings->get("ShowGameTime").toBool()) {
if (lastTimePlayed() > 0) { if (lastTimePlayed() > 0 && lastLaunch() > 0) {
QDateTime lastLaunchTime = QDateTime::fromMSecsSinceEpoch(lastLaunch()); QDateTime lastLaunchTime = QDateTime::fromMSecsSinceEpoch(lastLaunch());
description.append( description.append(
tr(", last played on %1 for %2") tr(", last played on %1 for %2")

View File

@ -157,20 +157,6 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject& in, VersionFi
Bits::readString(in, "id", out->minecraftVersion); Bits::readString(in, "id", out->minecraftVersion);
Bits::readString(in, "mainClass", out->mainClass); Bits::readString(in, "mainClass", out->mainClass);
Bits::readString(in, "minecraftArguments", out->minecraftArguments); Bits::readString(in, "minecraftArguments", out->minecraftArguments);
if (out->minecraftArguments.isEmpty()) {
QString processArguments;
Bits::readString(in, "processArguments", processArguments);
QString toCompare = processArguments.toLower();
if (toCompare == "legacy") {
out->minecraftArguments = " ${auth_player_name} ${auth_session}";
} else if (toCompare == "username_session") {
out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}";
} else if (toCompare == "username_session_version") {
out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}";
} else if (!toCompare.isEmpty()) {
out->addProblem(ProblemSeverity::Error, QObject::tr("processArguments is set to unknown value '%1'").arg(processArguments));
}
}
Bits::readString(in, "type", out->type); Bits::readString(in, "type", out->type);
Bits::readString(in, "assets", out->assets); Bits::readString(in, "assets", out->assets);

View File

@ -52,8 +52,6 @@
#include <FileSystem.h> #include <FileSystem.h>
#include <QSaveFile> #include <QSaveFile>
#include <chrono>
enum AccountListVersion { MojangMSA = 3 }; enum AccountListVersion { MojangMSA = 3 };
AccountList::AccountList(QObject* parent) : QAbstractListModel(parent) AccountList::AccountList(QObject* parent) : QAbstractListModel(parent)

View File

@ -126,7 +126,35 @@ bool XboxAuthorizationStep::processSTSError(QNetworkReply::NetworkError error, Q
emit finished( emit finished(
AccountTaskState::STATE_FAILED_SOFT, AccountTaskState::STATE_FAILED_SOFT,
tr("This Microsoft account is underaged and is not linked to a family.\n\nPlease set up your account according to %1.") tr("This Microsoft account is underaged and is not linked to a family.\n\nPlease set up your account according to %1.")
.arg("<a href=\"https://help.minecraft.net/hc/en-us/articles/4403181904525\">help.minecraft.net</a>")); .arg("<a href=\"https://help.minecraft.net/hc/en-us/articles/4408968616077\">help.minecraft.net</a>"));
return true;
}
// the following codes where copied from: https://github.com/PrismarineJS/prismarine-auth/pull/44
case 2148916236: {
emit finished(AccountTaskState::STATE_FAILED_SOFT,
tr("This Microsoft account requires proof of age to play. Please login to %1 to provide proof of age.")
.arg("<a href=\"https://login.live.com/login.srf\">login.live.com</a>"));
return true;
}
case 2148916237:
emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account has reached its limit for playtime. This "
"Microsoft account has been blocked from logging in."));
return true;
case 2148916227: {
emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account was banned by Xbox for violating one or more "
"Community Standards for Xbox and is unable to be used."));
return true;
}
case 2148916229: {
emit finished(AccountTaskState::STATE_FAILED_SOFT,
tr("This Microsoft account is currently restricted and your guardian has not given you permission to play "
"online. Login to %1 and have your guardian change your permissions.")
.arg("<a href=\"https://account.microsoft.com/family/\">account.microsoft.com</a>"));
return true;
}
case 2148916234: {
emit finished(AccountTaskState::STATE_FAILED_SOFT,
tr("This Microsoft account has not accepted Xbox's Terms of Service. Please login and accept them."));
return true; return true;
} }
default: { default: {

View File

@ -79,6 +79,7 @@ void ExtractNatives::executeTask()
auto settings = minecraftInstance->settings(); auto settings = minecraftInstance->settings();
auto outputPath = minecraftInstance->getNativePath(); auto outputPath = minecraftInstance->getNativePath();
FS::ensureFolderPathExists(outputPath);
auto javaVersion = minecraftInstance->getJavaVersion(); auto javaVersion = minecraftInstance->getJavaVersion();
bool jniHackEnabled = javaVersion.major() >= 8; bool jniHackEnabled = javaVersion.major() >= 8;
for (const auto& source : toExtract) { for (const auto& source : toExtract) {

View File

@ -16,8 +16,6 @@
#pragma once #pragma once
#include <launch/LaunchStep.h> #include <launch/LaunchStep.h>
#include <memory>
#include "minecraft/auth/AuthSession.h"
// FIXME: temporary wrapper for existing task. // FIXME: temporary wrapper for existing task.
class ExtractNatives : public LaunchStep { class ExtractNatives : public LaunchStep {

View File

@ -66,32 +66,6 @@ LauncherPartLaunch::LauncherPartLaunch(LaunchTask* parent) : LaunchStep(parent)
connect(&m_process, &LoggedProcess::stateChanged, this, &LauncherPartLaunch::on_state); connect(&m_process, &LoggedProcess::stateChanged, this, &LauncherPartLaunch::on_state);
} }
#ifdef Q_OS_WIN
// returns 8.3 file format from long path
#include <windows.h>
QString shortPathName(const QString& file)
{
auto input = file.toStdWString();
std::wstring output;
long length = GetShortPathNameW(input.c_str(), NULL, 0);
// NOTE: this resizing might seem weird...
// when GetShortPathNameW fails, it returns length including null character
// when it succeeds, it returns length excluding null character
// See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx
output.resize(length);
GetShortPathNameW(input.c_str(), (LPWSTR)output.c_str(), length);
output.resize(length - 1);
QString ret = QString::fromStdWString(output);
return ret;
}
#endif
// if the string survives roundtrip through local 8bit encoding...
bool fitsInLocal8bit(const QString& string)
{
return string == QString::fromLocal8Bit(string.toLocal8Bit());
}
void LauncherPartLaunch::executeTask() void LauncherPartLaunch::executeTask()
{ {
QString jarPath = APPLICATION->getJarPath("NewLaunch.jar"); QString jarPath = APPLICATION->getJarPath("NewLaunch.jar");
@ -136,24 +110,15 @@ void LauncherPartLaunch::executeTask()
auto natPath = minecraftInstance->getNativePath(); auto natPath = minecraftInstance->getNativePath();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
if (!fitsInLocal8bit(natPath)) { natPath = FS::getPathNameInLocal8bit(natPath);
args << "-Djava.library.path=" + shortPathName(natPath);
} else {
args << "-Djava.library.path=" + natPath;
}
#else
args << "-Djava.library.path=" + natPath;
#endif #endif
args << "-Djava.library.path=" + natPath;
args << "-cp"; args << "-cp";
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
QStringList processed; QStringList processed;
for (auto& item : classPath) { for (auto& item : classPath) {
if (!fitsInLocal8bit(item)) { processed << FS::getPathNameInLocal8bit(item);
processed << shortPathName(item);
} else {
processed << item;
}
} }
args << processed.join(';'); args << processed.join(';');
#else #else

View File

@ -28,15 +28,52 @@
#include "Version.h" #include "Version.h"
// Values taken from: // Values taken from:
// https://minecraft.wiki/w/Tutorials/Creating_a_data_pack#%22pack_format%22 // https://minecraft.wiki/w/Pack_format#List_of_data_pack_formats
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = { static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = { { 4, { Version("1.13"), Version("1.14.4") } },
{ 4, { Version("1.13"), Version("1.14.4") } }, { 5, { Version("1.15"), Version("1.16.1") } }, { 5, { Version("1.15"), Version("1.16.1") } },
{ 6, { Version("1.16.2"), Version("1.16.5") } }, { 7, { Version("1.17"), Version("1.17.1") } }, { 6, { Version("1.16.2"), Version("1.16.5") } },
{ 8, { Version("1.18"), Version("1.18.1") } }, { 9, { Version("1.18.2"), Version("1.18.2") } }, { 7, { Version("1.17"), Version("1.17.1") } },
{ 10, { Version("1.19"), Version("1.19.3") } }, { 11, { Version("23w03a"), Version("23w05a") } }, { 8, { Version("1.18"), Version("1.18.1") } },
{ 12, { Version("1.19.4"), Version("1.19.4") } }, { 13, { Version("23w12a"), Version("23w14a") } }, { 9, { Version("1.18.2"), Version("1.18.2") } },
{ 14, { Version("23w16a"), Version("23w17a") } }, { 15, { Version("1.20"), Version("1.20") } }, { 10, { Version("1.19"), Version("1.19.3") } },
}; { 11, { Version("23w03a"), Version("23w05a") } },
{ 12, { Version("1.19.4"), Version("1.19.4") } },
{ 13, { Version("23w12a"), Version("23w14a") } },
{ 14, { Version("23w16a"), Version("23w17a") } },
{ 15, { Version("1.20"), Version("1.20.1") } },
{ 16, { Version("23w31a"), Version("23w31a") } },
{ 17, { Version("23w32a"), Version("23w35a") } },
{ 18, { Version("1.20.2"), Version("1.20.2") } },
{ 19, { Version("23w40a"), Version("23w40a") } },
{ 20, { Version("23w41a"), Version("23w41a") } },
{ 21, { Version("23w42a"), Version("23w42a") } },
{ 22, { Version("23w43a"), Version("23w43b") } },
{ 23, { Version("23w44a"), Version("23w44a") } },
{ 24, { Version("23w45a"), Version("23w45a") } },
{ 25, { Version("23w46a"), Version("23w46a") } },
{ 26, { Version("1.20.3"), Version("1.20.4") } },
{ 27, { Version("23w51a"), Version("23w51b") } },
{ 28, { Version("24w05a"), Version("24w05b") } },
{ 29, { Version("24w04a"), Version("24w04a") } },
{ 30, { Version("24w05a"), Version("24w05b") } },
{ 31, { Version("24w06a"), Version("24w06a") } },
{ 32, { Version("24w07a"), Version("24w07a") } },
{ 33, { Version("24w09a"), Version("24w09a") } },
{ 34, { Version("24w10a"), Version("24w10a") } },
{ 35, { Version("24w11a"), Version("24w11a") } },
{ 36, { Version("24w12a"), Version("24w12a") } },
{ 37, { Version("24w13a"), Version("24w13a") } },
{ 38, { Version("24w14a"), Version("24w14a") } },
{ 39, { Version("1.20.5-pre1"), Version("1.20.5-pre1") } },
{ 40, { Version("1.20.5-pre2"), Version("1.20.5-pre2") } },
{ 41, { Version("1.20.5"), Version("1.20.6") } },
{ 42, { Version("24w18a"), Version("24w18a") } },
{ 43, { Version("24w19a"), Version("24w19b") } },
{ 44, { Version("24w20a"), Version("24w20a") } },
{ 45, { Version("21w21a"), Version("21w21b") } },
{ 46, { Version("1.21-pre1"), Version("1.21-pre1") } },
{ 47, { Version("1.21-pre2"), Version("1.21-pre2") } },
{ 48, { Version("1.21"), Version("1.21") } } };
void DataPack::setPackFormat(int new_format_id) void DataPack::setPackFormat(int new_format_id)
{ {

View File

@ -306,7 +306,6 @@ void ResourceFolderModel::applyUpdates(QSet<QString>& current_set, QSet<QString>
auto removed_it = m_resources.begin() + removed_index; auto removed_it = m_resources.begin() + removed_index;
Q_ASSERT(removed_it != m_resources.end()); Q_ASSERT(removed_it != m_resources.end());
Q_ASSERT(removed_set.contains(removed_it->get()->internal_id()));
if ((*removed_it)->isResolving()) { if ((*removed_it)->isResolving()) {
auto ticket = (*removed_it)->resolutionTicket(); auto ticket = (*removed_it)->resolutionTicket();

View File

@ -11,7 +11,7 @@
#include "minecraft/mod/tasks/LocalResourcePackParseTask.h" #include "minecraft/mod/tasks/LocalResourcePackParseTask.h"
// Values taken from: // Values taken from:
// https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta // https://minecraft.wiki/w/Pack_format#List_of_resource_pack_formats
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = { static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = {
{ 1, { Version("1.6.1"), Version("1.8.9") } }, { 2, { Version("1.9"), Version("1.10.2") } }, { 1, { Version("1.6.1"), Version("1.8.9") } }, { 2, { Version("1.9"), Version("1.10.2") } },
{ 3, { Version("1.11"), Version("1.12.2") } }, { 4, { Version("1.13"), Version("1.14.4") } }, { 3, { Version("1.11"), Version("1.12.2") } }, { 4, { Version("1.13"), Version("1.14.4") } },
@ -19,7 +19,16 @@ static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = {
{ 7, { Version("1.17"), Version("1.17.1") } }, { 8, { Version("1.18"), Version("1.18.2") } }, { 7, { Version("1.17"), Version("1.17.1") } }, { 8, { Version("1.18"), Version("1.18.2") } },
{ 9, { Version("1.19"), Version("1.19.2") } }, { 11, { Version("22w42a"), Version("22w44a") } }, { 9, { Version("1.19"), Version("1.19.2") } }, { 11, { Version("22w42a"), Version("22w44a") } },
{ 12, { Version("1.19.3"), Version("1.19.3") } }, { 13, { Version("1.19.4"), Version("1.19.4") } }, { 12, { Version("1.19.3"), Version("1.19.3") } }, { 13, { Version("1.19.4"), Version("1.19.4") } },
{ 14, { Version("1.20"), Version("1.20") } } { 14, { Version("23w14a"), Version("23w16a") } }, { 15, { Version("1.20"), Version("1.20.1") } },
{ 16, { Version("23w31a"), Version("23w31a") } }, { 17, { Version("23w32a"), Version("23w35a") } },
{ 18, { Version("1.20.2"), Version("23w16a") } }, { 19, { Version("23w42a"), Version("23w42a") } },
{ 20, { Version("23w43a"), Version("23w44a") } }, { 21, { Version("23w45a"), Version("23w46a") } },
{ 22, { Version("1.20.3-pre1"), Version("23w51b") } }, { 24, { Version("24w03a"), Version("24w04a") } },
{ 25, { Version("24w05a"), Version("24w05b") } }, { 26, { Version("24w06a"), Version("24w07a") } },
{ 28, { Version("24w09a"), Version("24w10a") } }, { 29, { Version("24w11a"), Version("24w11a") } },
{ 30, { Version("24w12a"), Version("23w12a") } }, { 31, { Version("24w13a"), Version("1.20.5-pre3") } },
{ 32, { Version("1.20.5-pre4"), Version("1.20.6") } }, { 33, { Version("24w18a"), Version("24w20a") } },
{ 34, { Version("24w21a"), Version("1.21") } }
}; };
void ResourcePack::setPackFormat(int new_format_id) void ResourcePack::setPackFormat(int new_format_id)

View File

@ -182,7 +182,9 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
ResourceAPI::DependencySearchArgs args = { dep, m_version, m_loaderType }; ResourceAPI::DependencySearchArgs args = { dep, m_version, m_loaderType };
ResourceAPI::DependencySearchCallbacks callbacks; ResourceAPI::DependencySearchCallbacks callbacks;
callbacks.on_fail = [](QString reason, int) {
qCritical() << tr("A network error occurred. Could not load project dependencies:%1").arg(reason);
};
callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, [[maybe_unused]] auto& pack) { callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, [[maybe_unused]] auto& pack) {
try { try {
QJsonArray arr; QJsonArray arr;

View File

@ -469,7 +469,7 @@ bool processZIP(Mod& mod, [[maybe_unused]] ProcessingLevel level)
QuaZipFile file(&zip); QuaZipFile file(&zip);
if (zip.setCurrentFile("META-INF/mods.toml")) { if (zip.setCurrentFile("META-INF/mods.toml") || zip.setCurrentFile("META-INF/neoforge.mods.toml")) {
if (!file.open(QIODevice::ReadOnly)) { if (!file.open(QIODevice::ReadOnly)) {
zip.close(); zip.close();
return false; return false;

View File

@ -149,6 +149,7 @@ void EnsureMetadataTask::executeTask()
if (m_current_task) if (m_current_task)
m_current_task.reset(); m_current_task.reset();
}); });
connect(project_task.get(), &Task::failed, this, &EnsureMetadataTask::emitFailed);
m_current_task = project_task; m_current_task = project_task;
project_task->start(); project_task->start();

View File

@ -122,6 +122,8 @@ struct ExtraPackData {
QString wikiUrl; QString wikiUrl;
QString discordUrl; QString discordUrl;
QString status;
QString body; QString body;
}; };

View File

@ -96,6 +96,7 @@ class ResourceAPI {
}; };
struct VersionSearchCallbacks { struct VersionSearchCallbacks {
std::function<void(QJsonDocument&, ModPlatform::IndexedPack)> on_succeed; std::function<void(QJsonDocument&, ModPlatform::IndexedPack)> on_succeed;
std::function<void(QString const& reason, int network_error_code)> on_fail;
}; };
struct ProjectInfoArgs { struct ProjectInfoArgs {
@ -118,6 +119,7 @@ class ResourceAPI {
struct DependencySearchCallbacks { struct DependencySearchCallbacks {
std::function<void(QJsonDocument&, const ModPlatform::Dependency&)> on_succeed; std::function<void(QJsonDocument&, const ModPlatform::Dependency&)> on_succeed;
std::function<void(QString const& reason, int network_error_code)> on_fail;
}; };
public: public:

View File

@ -1031,6 +1031,12 @@ void PackInstallTask::install()
return; return;
components->setComponentVersion("net.minecraftforge", version); components->setComponentVersion("net.minecraftforge", version);
} else if (m_version.loader.type == QString("neoforge")) {
auto version = getVersionForLoader("net.neoforged");
if (version == Q_NULLPTR)
return;
components->setComponentVersion("net.neoforged", version);
} else if (m_version.loader.type == QString("fabric")) { } else if (m_version.loader.type == QString("fabric")) {
auto version = getVersionForLoader("net.fabricmc.fabric-loader"); auto version = getVersionForLoader("net.fabricmc.fabric-loader");
if (version == Q_NULLPTR) if (version == Q_NULLPTR)

View File

@ -119,7 +119,6 @@ void Flame::FileResolvingTask::netJobFinished()
connect(m_checkJob.get(), &NetJob::failed, this, [this, step_progress](QString reason) { connect(m_checkJob.get(), &NetJob::failed, this, [this, step_progress](QString reason) {
step_progress->state = TaskStepState::Failed; step_progress->state = TaskStepState::Failed;
stepProgress(*step_progress); stepProgress(*step_progress);
emitFailed(reason);
}); });
connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress); connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress);
connect(m_checkJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) { connect(m_checkJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) {

View File

@ -24,7 +24,7 @@ bool FlameCheckUpdate::abort()
return true; return true;
} }
ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info) ModPlatform::IndexedPack FlameCheckUpdate::getProjectInfo(ModPlatform::IndexedVersion& ver_info)
{ {
ModPlatform::IndexedPack pack; ModPlatform::IndexedPack pack;
@ -57,6 +57,7 @@ ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info)
} }
}); });
connect(get_project_job, &NetJob::failed, this, &FlameCheckUpdate::emitFailed);
QObject::connect(get_project_job, &NetJob::finished, [&loop, get_project_job] { QObject::connect(get_project_job, &NetJob::finished, [&loop, get_project_job] {
get_project_job->deleteLater(); get_project_job->deleteLater();
loop.quit(); loop.quit();
@ -68,7 +69,7 @@ ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info)
return pack; return pack;
} }
ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId) ModPlatform::IndexedVersion FlameCheckUpdate::getFileInfo(int addonId, int fileId)
{ {
ModPlatform::IndexedVersion ver; ModPlatform::IndexedVersion ver;
@ -100,7 +101,7 @@ ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId)
qDebug() << doc; qDebug() << doc;
} }
}); });
connect(get_file_info_job, &NetJob::failed, this, &FlameCheckUpdate::emitFailed);
QObject::connect(get_file_info_job, &NetJob::finished, [&loop, get_file_info_job] { QObject::connect(get_file_info_job, &NetJob::finished, [&loop, get_file_info_job] {
get_file_info_job->deleteLater(); get_file_info_job->deleteLater();
loop.quit(); loop.quit();

View File

@ -22,6 +22,9 @@ class FlameCheckUpdate : public CheckUpdateTask {
void executeTask() override; void executeTask() override;
private: private:
ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info);
ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId);
NetJob* m_net_job = nullptr; NetJob* m_net_job = nullptr;
bool m_was_aborted = false; bool m_was_aborted = false;

View File

@ -227,6 +227,7 @@ bool FlameCreationTask::updateInstance()
m_files_to_remove.append(old_minecraft_dir.absoluteFilePath(relative_path)); m_files_to_remove.append(old_minecraft_dir.absoluteFilePath(relative_path));
} }
}); });
connect(job.get(), &Task::failed, this, [](QString reason) { qCritical() << "Failed to get files: " << reason; });
connect(job.get(), &Task::finished, &loop, &QEventLoop::quit); connect(job.get(), &Task::finished, &loop, &QEventLoop::quit);
m_process_update_file_info_job = job; m_process_update_file_info_job = job;
@ -353,6 +354,8 @@ bool FlameCreationTask::createInstance()
auto id = loader.id; auto id = loader.id;
if (id.startsWith("neoforge-")) { if (id.startsWith("neoforge-")) {
id.remove("neoforge-"); id.remove("neoforge-");
if (id.startsWith("1.20.1-"))
id.remove("1.20.1-"); // this is a mess for curseforge
loaderType = "neoforge"; loaderType = "neoforge";
loaderUid = "net.neoforged"; loaderUid = "net.neoforged";
} else if (id.startsWith("forge-")) { } else if (id.startsWith("forge-")) {
@ -427,6 +430,9 @@ bool FlameCreationTask::createInstance()
// Don't add managed info to packs without an ID (most likely imported from ZIP) // Don't add managed info to packs without an ID (most likely imported from ZIP)
if (!m_managed_id.isEmpty()) if (!m_managed_id.isEmpty())
instance.setManagedPack("flame", m_managed_id, m_pack.name, m_managed_version_id, m_pack.version); instance.setManagedPack("flame", m_managed_id, m_pack.name, m_managed_version_id, m_pack.version);
else
instance.setManagedPack("flame", "", name(), "", "");
instance.setName(name()); instance.setName(name());
m_mod_id_resolver.reset(new Flame::FileResolvingTask(APPLICATION->network(), m_pack)); m_mod_id_resolver.reset(new Flame::FileResolvingTask(APPLICATION->network(), m_pack));
@ -531,7 +537,10 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
selectedOptionalMods = optionalModDialog.getResult(); selectedOptionalMods = optionalModDialog.getResult();
} }
for (const auto& result : results) { for (const auto& result : results) {
auto relpath = FS::PathCombine(result.targetFolder, result.fileName); auto fileName = result.fileName;
fileName = FS::RemoveInvalidPathChars(fileName);
auto relpath = FS::PathCombine(result.targetFolder, fileName);
if (!result.required && !selectedOptionalMods.contains(relpath)) { if (!result.required && !selectedOptionalMods.contains(relpath)) {
relpath += ".disabled"; relpath += ".disabled";
} }

View File

@ -1,5 +1,6 @@
#include "FlameModIndex.h" #include "FlameModIndex.h"
#include "FileSystem.h"
#include "Json.h" #include "Json.h"
#include "minecraft/MinecraftInstance.h" #include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h" #include "minecraft/PackProfile.h"
@ -138,6 +139,7 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
file.version = Json::requireString(obj, "displayName"); file.version = Json::requireString(obj, "displayName");
file.downloadUrl = Json::ensureString(obj, "downloadUrl"); file.downloadUrl = Json::ensureString(obj, "downloadUrl");
file.fileName = Json::requireString(obj, "fileName"); file.fileName = Json::requireString(obj, "fileName");
file.fileName = FS::RemoveInvalidPathChars(file.fileName);
ModPlatform::IndexedVersionType::VersionType ver_type; ModPlatform::IndexedVersionType::VersionType ver_type;
switch (Json::requireInteger(obj, "releaseType")) { switch (Json::requireInteger(obj, "releaseType")) {

View File

@ -201,7 +201,7 @@ void FlamePackExportTask::makeApiRequest()
<< " reason: " << parseError.errorString(); << " reason: " << parseError.errorString();
qWarning() << *response; qWarning() << *response;
failed(parseError.errorString()); emitFailed(parseError.errorString());
return; return;
} }
@ -213,6 +213,7 @@ void FlamePackExportTask::makeApiRequest()
if (dataArr.isEmpty()) { if (dataArr.isEmpty()) {
qWarning() << "No matches found for fingerprint search!"; qWarning() << "No matches found for fingerprint search!";
getProjectsInfo();
return; return;
} }
for (auto match : dataArr) { for (auto match : dataArr) {
@ -243,9 +244,9 @@ void FlamePackExportTask::makeApiRequest()
qDebug() << doc; qDebug() << doc;
} }
pendingHashes.clear(); pendingHashes.clear();
getProjectsInfo();
}); });
connect(task.get(), &Task::finished, this, &FlamePackExportTask::getProjectsInfo); connect(task.get(), &NetJob::failed, this, &FlamePackExportTask::getProjectsInfo);
connect(task.get(), &NetJob::failed, this, &FlamePackExportTask::emitFailed);
task->start(); task->start();
} }
@ -279,7 +280,7 @@ void FlamePackExportTask::getProjectsInfo()
qWarning() << "Error while parsing JSON response from CurseForge projects task at " << parseError.offset qWarning() << "Error while parsing JSON response from CurseForge projects task at " << parseError.offset
<< " reason: " << parseError.errorString(); << " reason: " << parseError.errorString();
qWarning() << *response; qWarning() << *response;
failed(parseError.errorString()); emitFailed(parseError.errorString());
return; return;
} }
@ -323,6 +324,7 @@ void FlamePackExportTask::getProjectsInfo()
} }
buildZip(); buildZip();
}); });
connect(projTask.get(), &Task::failed, this, &FlamePackExportTask::emitFailed);
task.reset(projTask); task.reset(projTask);
task->start(); task->start();
} }
@ -332,7 +334,7 @@ void FlamePackExportTask::buildZip()
setStatus(tr("Adding files...")); setStatus(tr("Adding files..."));
setProgress(4, 5); setProgress(4, 5);
auto zipTask = makeShared<MMCZip::ExportToZipTask>(output, gameRoot, files, "overrides/", true); auto zipTask = makeShared<MMCZip::ExportToZipTask>(output, gameRoot, files, "overrides/", true, false);
zipTask->addExtraFile("manifest.json", generateIndex()); zipTask->addExtraFile("manifest.json", generateIndex());
zipTask->addExtraFile("modlist.html", generateHTML()); zipTask->addExtraFile("modlist.html", generateHTML());
@ -392,13 +394,17 @@ QByteArray FlamePackExportTask::generateIndex()
version["version"] = minecraft->m_version; version["version"] = minecraft->m_version;
QString id; QString id;
if (quilt != nullptr) if (quilt != nullptr)
id = "quilt-" + quilt->getVersion(); id = "quilt-" + quilt->m_version;
else if (fabric != nullptr) else if (fabric != nullptr)
id = "fabric-" + fabric->getVersion(); id = "fabric-" + fabric->m_version;
else if (forge != nullptr) else if (forge != nullptr)
id = "forge-" + forge->getVersion(); id = "forge-" + forge->m_version;
else if (neoforge != nullptr) else if (neoforge != nullptr) {
id = "neoforge-" + neoforge->getVersion(); id = "neoforge-";
if (minecraft->m_version == "1.20.1")
id += "1.20.1-";
id += neoforge->m_version;
}
version["modLoaders"] = QJsonArray(); version["modLoaders"] = QJsonArray();
if (!id.isEmpty()) { if (!id.isEmpty()) {
QJsonObject loader; QJsonObject loader;

View File

@ -43,7 +43,7 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&
callbacks.on_succeed(doc); callbacks.on_succeed(doc);
}); });
QObject::connect(netJob.get(), &NetJob::failed, [&netJob, callbacks](QString reason) { QObject::connect(netJob.get(), &NetJob::failed, [netJob, callbacks](const QString& reason) {
int network_error_code = -1; int network_error_code = -1;
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply) if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply)
network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
@ -102,6 +102,13 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi
callbacks.on_succeed(doc, args.pack); callbacks.on_succeed(doc, args.pack);
}); });
QObject::connect(netJob.get(), &NetJob::failed, [netJob, callbacks](const QString& reason) {
int network_error_code = -1;
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply)
network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
callbacks.on_fail(reason, network_error_code);
});
return netJob; return netJob;
} }
@ -146,6 +153,12 @@ Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args,
callbacks.on_succeed(doc, args.dependency); callbacks.on_succeed(doc, args.dependency);
}); });
QObject::connect(netJob.get(), &NetJob::failed, [netJob, callbacks](const QString& reason) {
int network_error_code = -1;
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply)
network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
callbacks.on_fail(reason, network_error_code);
});
return netJob; return netJob;
} }

View File

@ -43,6 +43,7 @@ Modpack parseDirectory(QString path)
modpack.version = Json::requireString(root, "version", "version"); modpack.version = Json::requireString(root, "version", "version");
modpack.mcVersion = Json::requireString(root, "mcVersion", "mcVersion"); modpack.mcVersion = Json::requireString(root, "mcVersion", "mcVersion");
modpack.jvmArgs = Json::ensureVariant(root, "jvmArgs", {}, "jvmArgs"); modpack.jvmArgs = Json::ensureVariant(root, "jvmArgs", {}, "jvmArgs");
modpack.totalPlayTime = Json::requireInteger(root, "totalPlayTime", "totalPlayTime");
} catch (const Exception& e) { } catch (const Exception& e) {
qDebug() << "Couldn't load ftb instance json: " << e.cause(); qDebug() << "Couldn't load ftb instance json: " << e.cause();
return {}; return {};

View File

@ -36,6 +36,7 @@ struct Modpack {
QString name; QString name;
QString version; QString version;
QString mcVersion; QString mcVersion;
int totalPlayTime;
// not needed for instance creation // not needed for instance creation
QVariant jvmArgs; QVariant jvmArgs;

View File

@ -55,6 +55,7 @@ void PackInstallTask::copySettings()
instanceSettings->suspendSave(); instanceSettings->suspendSave();
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath); MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
instance.settings()->set("InstanceType", "OneSix"); instance.settings()->set("InstanceType", "OneSix");
instance.settings()->set("totalTimePlayed", m_pack.totalPlayTime / 1000);
if (m_pack.jvmArgs.isValid() && !m_pack.jvmArgs.toString().isEmpty()) { if (m_pack.jvmArgs.isValid() && !m_pack.jvmArgs.toString().isEmpty()) {
instance.settings()->set("OverrideJavaArgs", true); instance.settings()->set("OverrideJavaArgs", true);

View File

@ -72,9 +72,7 @@ void ModrinthCheckUpdate::executeTask()
auto response = std::make_shared<QByteArray>(); auto response = std::make_shared<QByteArray>();
auto job = api.latestVersions(hashes, best_hash_type, m_game_versions, m_loaders, response); auto job = api.latestVersions(hashes, best_hash_type, m_game_versions, m_loaders, response);
QEventLoop lock; connect(job.get(), &Task::succeeded, this, [this, response, mappings, best_hash_type, job] {
connect(job.get(), &Task::succeeded, this, [this, response, &mappings, best_hash_type, job] {
QJsonParseError parse_error{}; QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) { if (parse_error.error != QJsonParseError::NoError) {
@ -82,7 +80,7 @@ void ModrinthCheckUpdate::executeTask()
<< " reason: " << parse_error.errorString(); << " reason: " << parse_error.errorString();
qWarning() << *response; qWarning() << *response;
failed(parse_error.errorString()); emitFailed(parse_error.errorString());
return; return;
} }
@ -167,19 +165,17 @@ void ModrinthCheckUpdate::executeTask()
m_deps.append(std::make_shared<GetModDependenciesTask::PackDependency>(pack, project_ver)); m_deps.append(std::make_shared<GetModDependenciesTask::PackDependency>(pack, project_ver));
} }
} catch (Json::JsonException& e) { } catch (Json::JsonException& e) {
failed(e.cause() + " : " + e.what()); emitFailed(e.cause() + " : " + e.what());
return;
} }
emitSucceeded();
}); });
connect(job.get(), &Task::finished, &lock, &QEventLoop::quit); connect(job.get(), &Task::failed, this, &ModrinthCheckUpdate::emitFailed);
setStatus(tr("Waiting for the API response from Modrinth...")); setStatus(tr("Waiting for the API response from Modrinth..."));
setProgress(1, 3); setProgress(1, 3);
m_net_job = qSharedPointerObjectCast<NetJob, Task>(job); m_net_job = qSharedPointerObjectCast<NetJob, Task>(job);
job->start(); job->start();
lock.exec();
emitSucceeded();
} }

View File

@ -226,6 +226,9 @@ bool ModrinthCreationTask::createInstance()
// Don't add managed info to packs without an ID (most likely imported from ZIP) // Don't add managed info to packs without an ID (most likely imported from ZIP)
if (!m_managed_id.isEmpty()) if (!m_managed_id.isEmpty())
instance.setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version()); instance.setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version());
else
instance.setManagedPack("modrinth", "", name(), "", "");
instance.setName(name()); instance.setName(name());
instance.saveNow(); instance.saveNow();
@ -235,11 +238,13 @@ bool ModrinthCreationTask::createInstance()
auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path); auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path);
for (auto file : m_files) { for (auto file : m_files) {
auto file_path = FS::PathCombine(root_modpack_path, file.path); auto fileName = file.path;
fileName = FS::RemoveInvalidPathChars(fileName);
auto file_path = FS::PathCombine(root_modpack_path, fileName);
if (!root_modpack_url.isParentOf(QUrl::fromLocalFile(file_path))) { if (!root_modpack_url.isParentOf(QUrl::fromLocalFile(file_path))) {
// This means we somehow got out of the root folder, so abort here to prevent exploits // This means we somehow got out of the root folder, so abort here to prevent exploits
setError(tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.") setError(tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.")
.arg(file.path)); .arg(fileName));
return false; return false;
} }
@ -289,7 +294,7 @@ bool ModrinthCreationTask::createInstance()
// Only change the name if it didn't use a custom name, so that the previous custom name // Only change the name if it didn't use a custom name, so that the previous custom name
// is preserved, but if we're using the original one, we update the version string. // is preserved, but if we're using the original one, we update the version string.
// NOTE: This needs to come before the copyManagedPack call! // NOTE: This needs to come before the copyManagedPack call!
if (inst->name().contains(inst->getManagedPackVersionName())) { if (inst->name().contains(inst->getManagedPackVersionName()) && inst->name() != instance.name()) {
if (askForChangingInstanceName(m_parent, inst->name(), instance.name()) == InstanceNameChange::ShouldChange) if (askForChangingInstanceName(m_parent, inst->name(), instance.name()) == InstanceNameChange::ShouldChange)
inst->setName(instance.name()); inst->setName(instance.name());
} }

View File

@ -200,7 +200,7 @@ void ModrinthPackExportTask::buildZip()
{ {
setStatus(tr("Adding files...")); setStatus(tr("Adding files..."));
auto zipTask = makeShared<MMCZip::ExportToZipTask>(output, gameRoot, files, "overrides/", true); auto zipTask = makeShared<MMCZip::ExportToZipTask>(output, gameRoot, files, "overrides/", true, true);
zipTask->addExtraFile("modrinth.index.json", generateIndex()); zipTask->addExtraFile("modrinth.index.json", generateIndex());
zipTask->setExcludeFiles(resolvedFiles.keys()); zipTask->setExcludeFiles(resolvedFiles.keys());
@ -287,16 +287,12 @@ QByteArray ModrinthPackExportTask::generateIndex()
env["client"] = "required"; env["client"] = "required";
env["server"] = "required"; env["server"] = "required";
} }
switch (iterator->side) {
case Metadata::ModSide::ClientSide: // a server side mod does not imply that the mod does not work on the client
// however, if a mrpack mod is marked as server-only it will not install on the client
if (iterator->side == Metadata::ModSide::ClientSide)
env["server"] = "unsupported"; env["server"] = "unsupported";
break;
case Metadata::ModSide::ServerSide:
env["client"] = "unsupported";
break;
case Metadata::ModSide::UniversalSide:
break;
}
fileOut["env"] = env; fileOut["env"] = env;
fileOut["path"] = path; fileOut["path"] = path;

View File

@ -17,6 +17,7 @@
*/ */
#include "ModrinthPackIndex.h" #include "ModrinthPackIndex.h"
#include "FileSystem.h"
#include "ModrinthAPI.h" #include "ModrinthAPI.h"
#include "Json.h" #include "Json.h"
@ -104,6 +105,8 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob
pack.extraData.donate.append(donate); pack.extraData.donate.append(donate);
} }
pack.extraData.status = Json::ensureString(obj, "status");
pack.extraData.body = Json::ensureString(obj, "body").remove("<br>"); pack.extraData.body = Json::ensureString(obj, "body").remove("<br>");
pack.extraDataLoaded = true; pack.extraDataLoaded = true;
@ -224,6 +227,7 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
if (parent.contains("url")) { if (parent.contains("url")) {
file.downloadUrl = Json::requireString(parent, "url"); file.downloadUrl = Json::requireString(parent, "url");
file.fileName = Json::requireString(parent, "filename"); file.fileName = Json::requireString(parent, "filename");
file.fileName = FS::RemoveInvalidPathChars(file.fileName);
file.is_preferred = Json::requireBoolean(parent, "primary") || (files.count() == 1); file.is_preferred = Json::requireBoolean(parent, "primary") || (files.count() == 1);
auto hash_list = Json::requireObject(parent, "hashes"); auto hash_list = Json::requireObject(parent, "hashes");

View File

@ -95,6 +95,8 @@ void loadIndexedInfo(Modpack& pack, QJsonObject& obj)
pack.extra.donate.append(donate); pack.extra.donate.append(donate);
} }
pack.extra.status = Json::ensureString(obj, "status");
pack.extraInfoLoaded = true; pack.extraInfoLoaded = true;
} }

View File

@ -77,6 +77,8 @@ struct ModpackExtra {
QString discordUrl; QString discordUrl;
QList<DonationData> donate; QList<DonationData> donate;
QString status;
}; };
struct ModpackVersion { struct ModpackVersion {

View File

@ -83,8 +83,10 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings,
data = file.readAll(); data = file.readAll();
file.close(); file.close();
} else { } else {
if (minecraftVersion.isEmpty()) if (minecraftVersion.isEmpty()) {
emit failed(tr("Could not find \"version.json\" inside \"bin/modpack.jar\", but Minecraft version is unknown")); emit failed(tr("Could not find \"version.json\" inside \"bin/modpack.jar\", but Minecraft version is unknown"));
return;
}
components->setComponentVersion("net.minecraft", minecraftVersion, true); components->setComponentVersion("net.minecraft", minecraftVersion, true);
components->installJarMods({ modpackJar }); components->installJarMods({ modpackJar });
@ -131,7 +133,9 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings,
file.close(); file.close();
} else { } else {
// This is the "Vanilla" modpack, excluded by the search code // This is the "Vanilla" modpack, excluded by the search code
emit failed(tr("Unable to find a \"version.json\"!")); components->setComponentVersion("net.minecraft", minecraftVersion, true);
components->saveNow();
emit succeeded();
return; return;
} }
@ -155,7 +159,25 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings,
auto libraryObject = Json::ensureObject(library, {}, ""); auto libraryObject = Json::ensureObject(library, {}, "");
auto libraryName = Json::ensureString(libraryObject, "name", "", ""); auto libraryName = Json::ensureString(libraryObject, "name", "", "");
if ((libraryName.startsWith("net.minecraftforge:forge:") || libraryName.startsWith("net.minecraftforge:fmlloader:")) && if (libraryName.startsWith("net.neoforged.fancymodloader:")) { // it is neoforge
// no easy way to get the version from the libs so use the arguments
auto arguments = Json::ensureObject(root, "arguments", {});
bool isVersionArg = false;
QString neoforgeVersion;
for (auto arg : Json::ensureArray(arguments, "game", {})) {
auto argument = Json::ensureString(arg, "");
if (isVersionArg) {
neoforgeVersion = argument;
break;
} else {
isVersionArg = "--fml.neoForgeVersion" == argument || "--fml.forgeVersion" == argument;
}
}
if (!neoforgeVersion.isEmpty()) {
components->setComponentVersion("net.neoforged", neoforgeVersion);
}
break;
} else if ((libraryName.startsWith("net.minecraftforge:forge:") || libraryName.startsWith("net.minecraftforge:fmlloader:")) &&
libraryName.contains('-')) { libraryName.contains('-')) {
QString libraryVersion = libraryName.section(':', 2); QString libraryVersion = libraryName.section(':', 2);
if (!libraryVersion.startsWith("1.7.10-")) { if (!libraryVersion.startsWith("1.7.10-")) {
@ -164,6 +186,7 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings,
// 1.7.10 versions sometimes look like 1.7.10-10.13.4.1614-1.7.10, this filters out the 10.13.4.1614 part // 1.7.10 versions sometimes look like 1.7.10-10.13.4.1614-1.7.10, this filters out the 10.13.4.1614 part
components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1, 1)); components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1, 1));
} }
break;
} else { } else {
// <Technic library name prefix> -> <our component name> // <Technic library name prefix> -> <our component name>
static QMap<QString, QString> loaderMap{ { "net.minecraftforge:minecraftforge:", "net.minecraftforge" }, static QMap<QString, QString> loaderMap{ { "net.minecraftforge:minecraftforge:", "net.minecraftforge" },

View File

@ -84,6 +84,7 @@ auto HttpMetaCache::getEntry(QString base, QString resource_path) -> MetaEntryPt
auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString expected_etag) -> MetaEntryPtr auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString expected_etag) -> MetaEntryPtr
{ {
resource_path = FS::RemoveInvalidPathChars(resource_path);
auto entry = getEntry(base, resource_path); auto entry = getEntry(base, resource_path);
// it's not present? generate a default stale entry // it's not present? generate a default stale entry
if (!entry) { if (!entry) {

View File

@ -36,6 +36,7 @@
*/ */
#include "NetJob.h" #include "NetJob.h"
#include "tasks/ConcurrentTask.h"
#if defined(LAUNCHER_APPLICATION) #if defined(LAUNCHER_APPLICATION)
#include "Application.h" #include "Application.h"
#endif #endif
@ -56,18 +57,15 @@ auto NetJob::addNetAction(NetAction::Ptr action) -> bool
return true; return true;
} }
void NetJob::startNext() void NetJob::executeNextSubTask()
{ {
if (m_queue.isEmpty() && m_doing.isEmpty()) {
// We're finished, check for failures and retry if we can (up to 3 times) // We're finished, check for failures and retry if we can (up to 3 times)
if (!m_failed.isEmpty() && m_try < 3) { if (isRunning() && m_queue.isEmpty() && m_doing.isEmpty() && !m_failed.isEmpty() && m_try < 3) {
m_try += 1; m_try += 1;
while (!m_failed.isEmpty()) while (!m_failed.isEmpty())
m_queue.enqueue(m_failed.take(*m_failed.keyBegin())); m_queue.enqueue(m_failed.take(*m_failed.keyBegin()));
} }
} ConcurrentTask::executeNextSubTask();
ConcurrentTask::startNext();
} }
auto NetJob::size() const -> int auto NetJob::size() const -> int

View File

@ -55,8 +55,6 @@ class NetJob : public ConcurrentTask {
explicit NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network); explicit NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network);
~NetJob() override = default; ~NetJob() override = default;
void startNext() override;
auto size() const -> int; auto size() const -> int;
auto canAbort() const -> bool override; auto canAbort() const -> bool override;
@ -69,6 +67,9 @@ class NetJob : public ConcurrentTask {
// Qt can't handle auto at the start for some reason? // Qt can't handle auto at the start for some reason?
bool abort() override; bool abort() override;
protected slots:
void executeNextSubTask() override;
protected: protected:
void updateState() override; void updateState() override;

View File

@ -68,7 +68,8 @@ void NetRequest::executeTask()
if (getState() == Task::State::AbortedByUser) { if (getState() == Task::State::AbortedByUser) {
qCWarning(logCat) << getUid().toString() << "Attempt to start an aborted Request:" << m_url.toString(); qCWarning(logCat) << getUid().toString() << "Attempt to start an aborted Request:" << m_url.toString();
emitAborted(); emit aborted();
emit finished();
return; return;
} }
@ -85,10 +86,12 @@ void NetRequest::executeTask()
break; break;
case State::Inactive: case State::Inactive:
case State::Failed: case State::Failed:
emitFailed(); emit failed("Failed to initilize sink");
emit finished();
return; return;
case State::AbortedByUser: case State::AbortedByUser:
emitAborted(); emit aborted();
emit finished();
return; return;
} }

View File

@ -5,6 +5,7 @@
qt.*.debug=false qt.*.debug=false
# don't log credentials by default # don't log credentials by default
launcher.auth.credentials.debug=false launcher.auth.credentials.debug=false
katabasis.*.debug=false
# remove the debug lines, other log levels still get through # remove the debug lines, other log levels still get through
launcher.task.net.download.debug=false launcher.task.net.download.debug=false
# enable or disable whole catageries # enable or disable whole catageries

View File

@ -51,7 +51,7 @@
Net::NetRequest::Ptr ImgurAlbumCreation::make(std::shared_ptr<ImgurAlbumCreation::Result> output, QList<ScreenShot::Ptr> screenshots) Net::NetRequest::Ptr ImgurAlbumCreation::make(std::shared_ptr<ImgurAlbumCreation::Result> output, QList<ScreenShot::Ptr> screenshots)
{ {
auto up = makeShared<ImgurAlbumCreation>(); auto up = makeShared<ImgurAlbumCreation>();
up->m_url = BuildConfig.IMGUR_BASE_URL + "album.json"; up->m_url = BuildConfig.IMGUR_BASE_URL + "album";
up->m_sink.reset(new Sink(output)); up->m_sink.reset(new Sink(output));
up->m_screenshots = screenshots; up->m_screenshots = screenshots;
return up; return up;
@ -72,7 +72,7 @@ void ImgurAlbumCreation::init()
qDebug() << "Setting up imgur upload"; qDebug() << "Setting up imgur upload";
auto api_headers = new Net::StaticHeaderProxy( auto api_headers = new Net::StaticHeaderProxy(
QList<Net::HeaderPair>{ { "Content-Type", "application/x-www-form-urlencoded" }, QList<Net::HeaderPair>{ { "Content-Type", "application/x-www-form-urlencoded" },
{ "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str() }, { "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toUtf8() },
{ "Accept", "application/json" } }); { "Accept", "application/json" } });
addHeaderProxy(api_headers); addHeaderProxy(api_headers);
} }

View File

@ -50,34 +50,33 @@
void ImgurUpload::init() void ImgurUpload::init()
{ {
qDebug() << "Setting up imgur upload"; qDebug() << "Setting up imgur upload";
auto api_headers = new Net::StaticHeaderProxy( auto api_headers = new Net::StaticHeaderProxy(QList<Net::HeaderPair>{
QList<Net::HeaderPair>{ { "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str() }, { "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toUtf8() }, { "Accept", "application/json" } });
{ "Accept", "application/json" } });
addHeaderProxy(api_headers); addHeaderProxy(api_headers);
} }
QNetworkReply* ImgurUpload::getReply(QNetworkRequest& request) QNetworkReply* ImgurUpload::getReply(QNetworkRequest& request)
{ {
auto file = new QFile(m_fileInfo.absoluteFilePath()); auto file = new QFile(m_fileInfo.absoluteFilePath(), this);
if (!file->open(QFile::ReadOnly)) { if (!file->open(QFile::ReadOnly)) {
emitFailed(); emitFailed();
return nullptr; return nullptr;
} }
QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType, this);
file->setParent(multipart); file->setParent(multipart);
QHttpPart filePart; QHttpPart filePart;
filePart.setBodyDevice(file); filePart.setBodyDevice(file);
filePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png"); filePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png");
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"image\""); filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"image\"; filename=\"" + file->fileName() + "\"");
multipart->append(filePart); multipart->append(filePart);
QHttpPart typePart; QHttpPart typePart;
typePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"type\""); typePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"type\"");
typePart.setBody("file"); typePart.setBody("file");
multipart->append(typePart); multipart->append(typePart);
QHttpPart namePart; QHttpPart namePart;
namePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"name\""); namePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"title\"");
namePart.setBody(m_fileInfo.baseName().toUtf8()); namePart.setBody(m_fileInfo.baseName().toUtf8());
multipart->append(namePart); multipart->append(namePart);
@ -124,7 +123,7 @@ auto ImgurUpload::Sink::finalize(QNetworkReply&) -> Task::State
Net::NetRequest::Ptr ImgurUpload::make(ScreenShot::Ptr m_shot) Net::NetRequest::Ptr ImgurUpload::make(ScreenShot::Ptr m_shot)
{ {
auto up = makeShared<ImgurUpload>(m_shot->m_file); auto up = makeShared<ImgurUpload>(m_shot->m_file);
up->m_url = std::move(BuildConfig.IMGUR_BASE_URL + "upload.json"); up->m_url = std::move(BuildConfig.IMGUR_BASE_URL + "image");
up->m_sink.reset(new Sink(m_shot)); up->m_sink.reset(new Sink(m_shot));
return up; return up;
} }

View File

@ -54,6 +54,7 @@ bool INIFile::saveFile(QString fileName)
insert("ConfigVersion", "1.2"); insert("ConfigVersion", "1.2");
QSettings _settings_obj{ fileName, QSettings::Format::IniFormat }; QSettings _settings_obj{ fileName, QSettings::Format::IniFormat };
_settings_obj.setFallbacksEnabled(false); _settings_obj.setFallbacksEnabled(false);
_settings_obj.clear();
for (Iterator iter = begin(); iter != end(); iter++) for (Iterator iter = begin(); iter != end(); iter++)
_settings_obj.setValue(iter.key(), iter.value()); _settings_obj.setValue(iter.key(), iter.value());

View File

@ -35,7 +35,6 @@
*/ */
#include "ConcurrentTask.h" #include "ConcurrentTask.h"
#include <QCoreApplication>
#include <QDebug> #include <QDebug>
#include "tasks/Task.h" #include "tasks/Task.h"
@ -47,9 +46,9 @@ ConcurrentTask::ConcurrentTask(QObject* parent, QString task_name, int max_concu
ConcurrentTask::~ConcurrentTask() ConcurrentTask::~ConcurrentTask()
{ {
for (auto task : m_queue) { for (auto task : m_doing) {
if (task) if (task)
task->deleteLater(); task->disconnect(this);
} }
} }
@ -65,15 +64,13 @@ void ConcurrentTask::addTask(Task::Ptr task)
void ConcurrentTask::executeTask() void ConcurrentTask::executeTask()
{ {
// Start one task, startNext handles starting the up to the m_total_max_size for (auto i = 0; i < m_total_max_size; i++)
// while tracking the number currently being done QMetaObject::invokeMethod(this, &ConcurrentTask::executeNextSubTask, Qt::QueuedConnection);
QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection);
} }
bool ConcurrentTask::abort() bool ConcurrentTask::abort()
{ {
m_queue.clear(); m_queue.clear();
m_aborted = true;
if (m_doing.isEmpty()) { if (m_doing.isEmpty()) {
// Don't call emitAborted() here, we want to bypass the 'is the task running' check // Don't call emitAborted() here, we want to bypass the 'is the task running' check
@ -108,29 +105,36 @@ void ConcurrentTask::clear()
m_failed.clear(); m_failed.clear();
m_queue.clear(); m_queue.clear();
m_aborted = false;
m_progress = 0; m_progress = 0;
m_stepProgress = 0; m_stepProgress = 0;
} }
void ConcurrentTask::startNext() void ConcurrentTask::executeNextSubTask()
{ {
if (m_aborted || m_doing.count() > m_total_max_size) if (!isRunning()) {
return; return;
}
if (m_queue.isEmpty() && m_doing.isEmpty() && !wasSuccessful()) { if (m_doing.count() >= m_total_max_size) {
return;
}
if (m_queue.isEmpty()) {
if (m_doing.isEmpty()) {
if (m_failed.isEmpty())
emitSucceeded(); emitSucceeded();
else
emitFailed(tr("One or more subtasks failed"));
}
return; return;
} }
if (m_queue.isEmpty()) startSubTask(m_queue.dequeue());
return; }
Task::Ptr next = m_queue.dequeue();
void ConcurrentTask::startSubTask(Task::Ptr next)
{
connect(next.get(), &Task::succeeded, this, [this, next]() { subTaskSucceeded(next); }); connect(next.get(), &Task::succeeded, this, [this, next]() { subTaskSucceeded(next); });
connect(next.get(), &Task::failed, this, [this, next](QString msg) { subTaskFailed(next, msg); }); connect(next.get(), &Task::failed, this, [this, next](QString msg) { subTaskFailed(next, msg); });
// this should never happen but if it does, it's better to fail the task than get stuck
connect(next.get(), &Task::aborted, this, [this, next] { subTaskFailed(next, "Aborted"); }); connect(next.get(), &Task::aborted, this, [this, next] { subTaskFailed(next, "Aborted"); });
connect(next.get(), &Task::status, this, [this, next](QString msg) { subTaskStatus(next, msg); }); connect(next.get(), &Task::status, this, [this, next](QString msg) { subTaskStatus(next, msg); });
@ -140,55 +144,42 @@ void ConcurrentTask::startNext()
connect(next.get(), &Task::progress, this, [this, next](qint64 current, qint64 total) { subTaskProgress(next, current, total); }); connect(next.get(), &Task::progress, this, [this, next](qint64 current, qint64 total) { subTaskProgress(next, current, total); });
m_doing.insert(next.get(), next); m_doing.insert(next.get(), next);
qsizetype num_starts = qMin(m_queue.size(), m_total_max_size - m_doing.size());
auto task_progress = std::make_shared<TaskStepProgress>(next->getUid()); auto task_progress = std::make_shared<TaskStepProgress>(next->getUid());
m_task_progress.insert(next->getUid(), task_progress); m_task_progress.insert(next->getUid(), task_progress);
updateState(); updateState();
updateStepProgress(*task_progress.get(), Operation::ADDED); updateStepProgress(*task_progress.get(), Operation::ADDED);
QCoreApplication::processEvents();
QMetaObject::invokeMethod(next.get(), &Task::start, Qt::QueuedConnection); QMetaObject::invokeMethod(next.get(), &Task::start, Qt::QueuedConnection);
}
// Allow going up the number of concurrent tasks in case of tasks being added in the middle of a running task. void ConcurrentTask::subTaskFinished(Task::Ptr task, TaskStepState state)
for (int i = 0; i < num_starts; i++) {
QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection); m_done.insert(task.get(), task);
(state == TaskStepState::Succeeded ? m_succeeded : m_failed).insert(task.get(), task);
m_doing.remove(task.get());
auto task_progress = m_task_progress.value(task->getUid());
task_progress->state = state;
disconnect(task.get(), 0, this, 0);
emit stepProgress(*task_progress);
updateState();
updateStepProgress(*task_progress, Operation::REMOVED);
QMetaObject::invokeMethod(this, &ConcurrentTask::executeNextSubTask, Qt::QueuedConnection);
} }
void ConcurrentTask::subTaskSucceeded(Task::Ptr task) void ConcurrentTask::subTaskSucceeded(Task::Ptr task)
{ {
m_done.insert(task.get(), task); subTaskFinished(task, TaskStepState::Succeeded);
m_succeeded.insert(task.get(), task);
m_doing.remove(task.get());
auto task_progress = m_task_progress.value(task->getUid());
task_progress->state = TaskStepState::Succeeded;
disconnect(task.get(), 0, this, 0);
emit stepProgress(*task_progress);
updateState();
updateStepProgress(*task_progress, Operation::REMOVED);
startNext();
} }
void ConcurrentTask::subTaskFailed(Task::Ptr task, [[maybe_unused]] const QString& msg) void ConcurrentTask::subTaskFailed(Task::Ptr task, [[maybe_unused]] const QString& msg)
{ {
m_done.insert(task.get(), task); subTaskFinished(task, TaskStepState::Failed);
m_failed.insert(task.get(), task);
m_doing.remove(task.get());
auto task_progress = m_task_progress.value(task->getUid());
task_progress->state = TaskStepState::Failed;
disconnect(task.get(), 0, this, 0);
emit stepProgress(*task_progress);
updateState();
updateStepProgress(*task_progress, Operation::REMOVED);
startNext();
} }
void ConcurrentTask::subTaskStatus(Task::Ptr task, const QString& msg) void ConcurrentTask::subTaskStatus(Task::Ptr task, const QString& msg)

View File

@ -72,10 +72,11 @@ class ConcurrentTask : public Task {
protected slots: protected slots:
void executeTask() override; void executeTask() override;
virtual void startNext(); virtual void executeNextSubTask();
void subTaskSucceeded(Task::Ptr); void subTaskSucceeded(Task::Ptr);
void subTaskFailed(Task::Ptr, const QString& msg); virtual void subTaskFailed(Task::Ptr, const QString& msg);
void subTaskFinished(Task::Ptr, TaskStepState);
void subTaskStatus(Task::Ptr task, const QString& msg); void subTaskStatus(Task::Ptr task, const QString& msg);
void subTaskDetails(Task::Ptr task, const QString& msg); void subTaskDetails(Task::Ptr task, const QString& msg);
void subTaskProgress(Task::Ptr task, qint64 current, qint64 total); void subTaskProgress(Task::Ptr task, qint64 current, qint64 total);
@ -90,6 +91,8 @@ class ConcurrentTask : public Task {
virtual void updateState(); virtual void updateState();
void startSubTask(Task::Ptr task);
protected: protected:
QString m_name; QString m_name;
QString m_step_status; QString m_step_status;
@ -107,6 +110,4 @@ class ConcurrentTask : public Task {
qint64 m_stepProgress = 0; qint64 m_stepProgress = 0;
qint64 m_stepTotalProgress = 100; qint64 m_stepTotalProgress = 100;
bool m_aborted = false;
}; };

View File

@ -36,9 +36,9 @@
#include <QDebug> #include <QDebug>
MultipleOptionsTask::MultipleOptionsTask(QObject* parent, const QString& task_name) : SequentialTask(parent, task_name) {} MultipleOptionsTask::MultipleOptionsTask(QObject* parent, const QString& task_name) : ConcurrentTask(parent, task_name, 1) {}
void MultipleOptionsTask::startNext() void MultipleOptionsTask::executeNextSubTask()
{ {
if (m_done.size() != m_failed.size()) { if (m_done.size() != m_failed.size()) {
emitSucceeded(); emitSucceeded();
@ -51,7 +51,7 @@ void MultipleOptionsTask::startNext()
return; return;
} }
ConcurrentTask::startNext(); ConcurrentTask::executeNextSubTask();
} }
void MultipleOptionsTask::updateState() void MultipleOptionsTask::updateState()

View File

@ -34,18 +34,18 @@
*/ */
#pragma once #pragma once
#include "SequentialTask.h" #include "ConcurrentTask.h"
/* This task type will attempt to do run each of it's subtasks in sequence, /* This task type will attempt to do run each of it's subtasks in sequence,
* until one of them succeeds. When that happens, the remaining tasks will not run. * until one of them succeeds. When that happens, the remaining tasks will not run.
* */ * */
class MultipleOptionsTask : public SequentialTask { class MultipleOptionsTask : public ConcurrentTask {
Q_OBJECT Q_OBJECT
public: public:
explicit MultipleOptionsTask(QObject* parent = nullptr, const QString& task_name = ""); explicit MultipleOptionsTask(QObject* parent = nullptr, const QString& task_name = "");
~MultipleOptionsTask() override = default; ~MultipleOptionsTask() override = default;
private slots: private slots:
void startNext() override; void executeNextSubTask() override;
void updateState() override; void updateState() override;
}; };

View File

@ -36,18 +36,15 @@
#include "SequentialTask.h" #include "SequentialTask.h"
#include <QDebug> #include <QDebug>
#include "tasks/ConcurrentTask.h"
SequentialTask::SequentialTask(QObject* parent, QString task_name) : ConcurrentTask(parent, task_name, 1) {} SequentialTask::SequentialTask(QObject* parent, QString task_name) : ConcurrentTask(parent, task_name, 1) {}
void SequentialTask::startNext() void SequentialTask::subTaskFailed(Task::Ptr task, const QString& msg)
{ {
if (m_failed.size() > 0) { emitFailed(msg);
emitFailed(tr("One of the tasks failed!")); qWarning() << msg;
qWarning() << m_failed.constBegin()->get()->failReason(); ConcurrentTask::subTaskFailed(task, msg);
return;
}
ConcurrentTask::startNext();
} }
void SequentialTask::updateState() void SequentialTask::updateState()

View File

@ -50,7 +50,9 @@ class SequentialTask : public ConcurrentTask {
explicit SequentialTask(QObject* parent = nullptr, QString task_name = ""); explicit SequentialTask(QObject* parent = nullptr, QString task_name = "");
~SequentialTask() override = default; ~SequentialTask() override = default;
protected slots:
virtual void subTaskFailed(Task::Ptr, const QString& msg) override;
protected: protected:
void startNext() override;
void updateState() override; void updateState() override;
}; };

View File

@ -1182,43 +1182,43 @@ void MainWindow::undoTrashInstance()
void MainWindow::on_actionViewLauncherRootFolder_triggered() void MainWindow::on_actionViewLauncherRootFolder_triggered()
{ {
DesktopServices::openDirectory("."); DesktopServices::openPath(".");
} }
void MainWindow::on_actionViewInstanceFolder_triggered() void MainWindow::on_actionViewInstanceFolder_triggered()
{ {
QString str = APPLICATION->settings()->get("InstanceDir").toString(); QString str = APPLICATION->settings()->get("InstanceDir").toString();
DesktopServices::openDirectory(str); DesktopServices::openPath(str);
} }
void MainWindow::on_actionViewCentralModsFolder_triggered() void MainWindow::on_actionViewCentralModsFolder_triggered()
{ {
DesktopServices::openDirectory(APPLICATION->settings()->get("CentralModsDir").toString(), true); DesktopServices::openPath(APPLICATION->settings()->get("CentralModsDir").toString(), true);
} }
void MainWindow::on_actionViewIconThemeFolder_triggered() void MainWindow::on_actionViewIconThemeFolder_triggered()
{ {
DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path(), true); DesktopServices::openPath(APPLICATION->themeManager()->getIconThemesFolder().path(), true);
} }
void MainWindow::on_actionViewWidgetThemeFolder_triggered() void MainWindow::on_actionViewWidgetThemeFolder_triggered()
{ {
DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path(), true); DesktopServices::openPath(APPLICATION->themeManager()->getApplicationThemesFolder().path(), true);
} }
void MainWindow::on_actionViewCatPackFolder_triggered() void MainWindow::on_actionViewCatPackFolder_triggered()
{ {
DesktopServices::openDirectory(APPLICATION->themeManager()->getCatPacksFolder().path(), true); DesktopServices::openPath(APPLICATION->themeManager()->getCatPacksFolder().path(), true);
} }
void MainWindow::on_actionViewIconsFolder_triggered() void MainWindow::on_actionViewIconsFolder_triggered()
{ {
DesktopServices::openDirectory(APPLICATION->icons()->getDirectory(), true); DesktopServices::openPath(APPLICATION->icons()->getDirectory(), true);
} }
void MainWindow::on_actionViewLogsFolder_triggered() void MainWindow::on_actionViewLogsFolder_triggered()
{ {
DesktopServices::openDirectory("logs", true); DesktopServices::openPath("logs", true);
} }
void MainWindow::refreshInstances() void MainWindow::refreshInstances()
@ -1437,7 +1437,7 @@ void MainWindow::on_actionViewSelectedInstFolder_triggered()
{ {
if (m_selectedInstance) { if (m_selectedInstance) {
QString str = m_selectedInstance->instanceRoot(); QString str = m_selectedInstance->instanceRoot();
DesktopServices::openDirectory(QDir(str).absolutePath()); DesktopServices::openPath(QFileInfo(str));
} }
} }

View File

@ -38,6 +38,7 @@
#include "Application.h" #include "Application.h"
#include "BuildConfig.h" #include "BuildConfig.h"
#include "Markdown.h" #include "Markdown.h"
#include "StringUtils.h"
#include "ui_AboutDialog.h" #include "ui_AboutDialog.h"
#include <net/NetJob.h> #include <net/NetJob.h>
@ -139,10 +140,10 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDia
setWindowTitle(tr("About %1").arg(launcherName)); setWindowTitle(tr("About %1").arg(launcherName));
QString chtml = getCreditsHtml(); QString chtml = getCreditsHtml();
ui->creditsText->setHtml(chtml); ui->creditsText->setHtml(StringUtils::htmlListPatch(chtml));
QString lhtml = getLicenseHtml(); QString lhtml = getLicenseHtml();
ui->licenseText->setHtml(lhtml); ui->licenseText->setHtml(StringUtils::htmlListPatch(lhtml));
ui->urlLabel->setOpenExternalLinks(true); ui->urlLabel->setOpenExternalLinks(true);
@ -174,8 +175,7 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDia
QString urlText("<html><head/><body><p><a href=\"%1\">%1</a></p></body></html>"); QString urlText("<html><head/><body><p><a href=\"%1\">%1</a></p></body></html>");
ui->urlLabel->setText(urlText.arg(BuildConfig.LAUNCHER_GIT)); ui->urlLabel->setText(urlText.arg(BuildConfig.LAUNCHER_GIT));
QString copyText("© 2022-2023 %1"); ui->copyLabel->setText(BuildConfig.LAUNCHER_COPYRIGHT);
ui->copyLabel->setText(copyText.arg(BuildConfig.LAUNCHER_COPYRIGHT));
connect(ui->closeButton, SIGNAL(clicked()), SLOT(close())); connect(ui->closeButton, SIGNAL(clicked()), SLOT(close()));

View File

@ -15,7 +15,6 @@
#pragma once #pragma once
#include <net/NetJob.h>
#include <QDialog> #include <QDialog>
namespace Ui { namespace Ui {
@ -31,7 +30,4 @@ class AboutDialog : public QDialog {
private: private:
Ui::AboutDialog* ui; Ui::AboutDialog* ui;
NetJob::Ptr netJob;
QByteArray dataSink;
}; };

View File

@ -40,6 +40,7 @@
#include <QMimeData> #include <QMimeData>
#include <QPushButton> #include <QPushButton>
#include <QStandardPaths> #include <QStandardPaths>
#include <QTimer>
BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList<BlockedMod>& mods, QString hash_type) BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList<BlockedMod>& mods, QString hash_type)
: QDialog(parent), ui(new Ui::BlockedModsDialog), m_mods(mods), m_hash_type(hash_type) : QDialog(parent), ui(new Ui::BlockedModsDialog), m_mods(mods), m_hash_type(hash_type)
@ -60,8 +61,13 @@ BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, cons
qDebug() << "[Blocked Mods Dialog] Mods List: " << mods; qDebug() << "[Blocked Mods Dialog] Mods List: " << mods;
// defer setup of file system watchers until after the dialog is shown
// this allows OS (namely macOS) permission prompts to show after the relevant dialog appears
QTimer::singleShot(0, this, [this] {
setupWatch(); setupWatch();
scanPaths(); scanPaths();
update();
});
this->setWindowTitle(title); this->setWindowTitle(title);
ui->labelDescription->setText(text); ui->labelDescription->setText(text);
@ -158,7 +164,8 @@ void BlockedModsDialog::update()
QString watching; QString watching;
for (auto& dir : m_watcher.directories()) { for (auto& dir : m_watcher.directories()) {
watching += QString("<a href=\"%1\">%1</a><br/>").arg(dir); QUrl fileURL = QUrl::fromLocalFile(dir);
watching += QString("<a href=\"%1\">%2</a><br/>").arg(fileURL.toString(), dir);
} }
ui->textBrowserWatched->setText(watching); ui->textBrowserWatched->setText(watching);
@ -194,6 +201,10 @@ void BlockedModsDialog::setupWatch()
void BlockedModsDialog::watchPath(QString path, bool watch_recursive) void BlockedModsDialog::watchPath(QString path, bool watch_recursive)
{ {
auto to_watch = QFileInfo(path); auto to_watch = QFileInfo(path);
if (!to_watch.isReadable()) {
qWarning() << "[Blocked Mods Dialog] Failed to add Watch Path (unable to read):" << path;
return;
}
auto to_watch_path = to_watch.canonicalFilePath(); auto to_watch_path = to_watch.canonicalFilePath();
if (m_watcher.directories().contains(to_watch_path)) if (m_watcher.directories().contains(to_watch_path))
return; // don't watch the same path twice (no loops!) return; // don't watch the same path twice (no loops!)

View File

@ -146,7 +146,7 @@ void ExportInstanceDialog::doExport()
return; return;
} }
auto task = makeShared<MMCZip::ExportToZipTask>(output, m_instance->instanceRoot(), files, "", true); auto task = makeShared<MMCZip::ExportToZipTask>(output, m_instance->instanceRoot(), files, "", true, true);
connect(task.get(), &Task::failed, this, connect(task.get(), &Task::failed, this,
[this, output](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); [this, output](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });

View File

@ -47,11 +47,18 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPla
if (m_provider == ModPlatform::ResourceProvider::MODRINTH) { if (m_provider == ModPlatform::ResourceProvider::MODRINTH) {
setWindowTitle(tr("Export Modrinth Pack")); setWindowTitle(tr("Export Modrinth Pack"));
ui->summary->setText(instance->settings()->get("ExportSummary").toString());
ui->authorLabel->hide();
ui->author->hide();
ui->summary->setPlainText(instance->settings()->get("ExportSummary").toString());
} else { } else {
setWindowTitle(tr("Export CurseForge Pack")); setWindowTitle(tr("Export CurseForge Pack"));
ui->summaryLabel->setText(tr("&Author"));
ui->summary->setText(instance->settings()->get("ExportAuthor").toString()); ui->summaryLabel->hide();
ui->summary->hide();
ui->author->setText(instance->settings()->get("ExportAuthor").toString());
} }
// ensure a valid pack is generated // ensure a valid pack is generated
@ -108,9 +115,13 @@ void ExportPackDialog::done(int result)
auto settings = instance->settings(); auto settings = instance->settings();
settings->set("ExportName", ui->name->text()); settings->set("ExportName", ui->name->text());
settings->set("ExportVersion", ui->version->text()); settings->set("ExportVersion", ui->version->text());
settings->set(m_provider == ModPlatform::ResourceProvider::FLAME ? "ExportAuthor" : "ExportSummary", ui->summary->text());
settings->set("ExportOptionalFiles", ui->optionalFiles->isChecked()); settings->set("ExportOptionalFiles", ui->optionalFiles->isChecked());
if (m_provider == ModPlatform::ResourceProvider::MODRINTH)
settings->set("ExportSummary", ui->summary->toPlainText());
else
settings->set("ExportAuthor", ui->author->text());
if (result == Accepted) { if (result == Accepted) {
const QString name = ui->name->text().isEmpty() ? instance->name() : ui->name->text(); const QString name = ui->name->text().isEmpty() ? instance->name() : ui->name->text();
const QString filename = FS::RemoveInvalidFilenameChars(name); const QString filename = FS::RemoveInvalidFilenameChars(name);
@ -134,10 +145,10 @@ void ExportPackDialog::done(int result)
Task* task; Task* task;
if (m_provider == ModPlatform::ResourceProvider::MODRINTH) { if (m_provider == ModPlatform::ResourceProvider::MODRINTH) {
task = new ModrinthPackExportTask(name, ui->version->text(), ui->summary->text(), ui->optionalFiles->isChecked(), instance, task = new ModrinthPackExportTask(name, ui->version->text(), ui->summary->toPlainText(), ui->optionalFiles->isChecked(),
output, std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1)); instance, output, std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
} else { } else {
task = new FlamePackExportTask(name, ui->version->text(), ui->summary->text(), ui->optionalFiles->isChecked(), instance, output, task = new FlamePackExportTask(name, ui->version->text(), ui->author->text(), ui->optionalFiles->isChecked(), instance, output,
std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1)); std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
} }

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>650</width> <width>650</width>
<height>510</height> <height>532</height>
</rect> </rect>
</property> </property>
<property name="sizeGripEnabled"> <property name="sizeGripEnabled">
@ -19,21 +19,8 @@
<property name="title"> <property name="title">
<string>&amp;Description</string> <string>&amp;Description</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QVBoxLayout" name="verticalLayout_3">
<item row="3" column="0"> <item>
<widget class="QLabel" name="summaryLabel">
<property name="text">
<string>&amp;Summary</string>
</property>
<property name="buddy">
<cstring>summary</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="summary"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="nameLabel"> <widget class="QLabel" name="nameLabel">
<property name="text"> <property name="text">
<string>&amp;Name</string> <string>&amp;Name</string>
@ -43,7 +30,10 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item>
<widget class="QLineEdit" name="name"/>
</item>
<item>
<widget class="QLabel" name="versionLabel"> <widget class="QLabel" name="versionLabel">
<property name="text"> <property name="text">
<string>&amp;Version</string> <string>&amp;Version</string>
@ -53,16 +43,43 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item>
<widget class="QLineEdit" name="name"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="version"> <widget class="QLineEdit" name="version">
<property name="text"> <property name="text">
<string>1.0.0</string> <string>1.0.0</string>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QLabel" name="summaryLabel">
<property name="text">
<string>&amp;Summary</string>
</property>
<property name="buddy">
<cstring>summary</cstring>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="summary">
<property name="tabChangesFocus">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="authorLabel">
<property name="text">
<string>&amp;Author</string>
</property>
<property name="buddy">
<cstring>author</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="author"/>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -124,6 +141,7 @@
<tabstop>name</tabstop> <tabstop>name</tabstop>
<tabstop>version</tabstop> <tabstop>version</tabstop>
<tabstop>summary</tabstop> <tabstop>summary</tabstop>
<tabstop>author</tabstop>
<tabstop>files</tabstop> <tabstop>files</tabstop>
<tabstop>optionalFiles</tabstop> <tabstop>optionalFiles</tabstop>
</tabstops> </tabstops>

View File

@ -22,6 +22,7 @@
#include <QTextEdit> #include <QTextEdit>
#include "FileSystem.h" #include "FileSystem.h"
#include "Markdown.h" #include "Markdown.h"
#include "StringUtils.h"
#include "minecraft/MinecraftInstance.h" #include "minecraft/MinecraftInstance.h"
#include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ModFolderModel.h"
#include "modplatform/helpers/ExportToModList.h" #include "modplatform/helpers/ExportToModList.h"
@ -143,10 +144,10 @@ void ExportToModListDialog::triggerImp()
case ExportToModList::CUSTOM: case ExportToModList::CUSTOM:
return; return;
case ExportToModList::HTML: case ExportToModList::HTML:
ui->resultText->setHtml(txt); ui->resultText->setHtml(StringUtils::htmlListPatch(txt));
break; break;
case ExportToModList::MARKDOWN: case ExportToModList::MARKDOWN:
ui->resultText->setHtml(markdownToHTML(txt)); ui->resultText->setHtml(StringUtils::htmlListPatch(markdownToHTML(txt)));
break; break;
case ExportToModList::PLAINTXT: case ExportToModList::PLAINTXT:
break; break;

View File

@ -159,5 +159,5 @@ IconPickerDialog::~IconPickerDialog()
void IconPickerDialog::openFolder() void IconPickerDialog::openFolder()
{ {
DesktopServices::openDirectory(APPLICATION->icons()->getDirectory(), true); DesktopServices::openPath(APPLICATION->icons()->getDirectory(), true);
} }

View File

@ -3,6 +3,7 @@
#include "CustomMessageBox.h" #include "CustomMessageBox.h"
#include "ProgressDialog.h" #include "ProgressDialog.h"
#include "ScrollMessageBox.h" #include "ScrollMessageBox.h"
#include "StringUtils.h"
#include "minecraft/mod/tasks/GetModDependenciesTask.h" #include "minecraft/mod/tasks/GetModDependenciesTask.h"
#include "modplatform/ModIndex.h" #include "modplatform/ModIndex.h"
#include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameAPI.h"
@ -328,6 +329,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool
connect(modrinth_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { connect(modrinth_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) {
onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::MODRINTH); onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::MODRINTH);
}); });
connect(modrinth_task.get(), &EnsureMetadataTask::failed,
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
if (modrinth_task->getHashingTask()) if (modrinth_task->getHashingTask())
seq.addTask(modrinth_task->getHashingTask()); seq.addTask(modrinth_task->getHashingTask());
@ -341,6 +344,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool
connect(flame_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { connect(flame_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) {
onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::FLAME); onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::FLAME);
}); });
connect(flame_task.get(), &EnsureMetadataTask::failed,
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
if (flame_task->getHashingTask()) if (flame_task->getHashingTask())
seq.addTask(flame_task->getHashingTask()); seq.addTask(flame_task->getHashingTask());
@ -394,6 +399,8 @@ void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::R
auto task = makeShared<EnsureMetadataTask>(mod, index_dir, next(first_choice)); auto task = makeShared<EnsureMetadataTask>(mod, index_dir, next(first_choice));
connect(task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); connect(task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); });
connect(task.get(), &EnsureMetadataTask::metadataFailed, [this](Mod* candidate) { onMetadataFailed(candidate, false); }); connect(task.get(), &EnsureMetadataTask::metadataFailed, [this](Mod* candidate) { onMetadataFailed(candidate, false); });
connect(task.get(), &EnsureMetadataTask::failed,
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
m_second_try_metadata->addTask(task); m_second_try_metadata->addTask(task);
} else { } else {
@ -437,6 +444,9 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri
reqItem->insertChildren(i++, { reqItem }); reqItem->insertChildren(i++, { reqItem });
} }
} }
ui->toggleDepsButton->show();
m_deps << item_top;
} }
auto changelog_item = new QTreeWidgetItem(item_top); auto changelog_item = new QTreeWidgetItem(item_top);
@ -455,7 +465,7 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri
break; break;
} }
changelog_area->setHtml(text); changelog_area->setHtml(StringUtils::htmlListPatch(text));
changelog_area->setOpenExternalLinks(true); changelog_area->setOpenExternalLinks(true);
changelog_area->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth); changelog_area->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth);
changelog_area->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); changelog_area->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded);

View File

@ -52,6 +52,7 @@
#include <QFileDialog> #include <QFileDialog>
#include <QLayout> #include <QLayout>
#include <QPushButton> #include <QPushButton>
#include <QScreen>
#include <QValidator> #include <QValidator>
#include <utility> #include <utility>
@ -63,6 +64,7 @@
#include "ui/pages/modplatform/modrinth/ModrinthPage.h" #include "ui/pages/modplatform/modrinth/ModrinthPage.h"
#include "ui/pages/modplatform/technic/TechnicPage.h" #include "ui/pages/modplatform/technic/TechnicPage.h"
#include "ui/widgets/PageContainer.h" #include "ui/widgets/PageContainer.h"
NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, NewInstanceDialog::NewInstanceDialog(const QString& initialGroup,
const QString& url, const QString& url,
const QMap<QString, QString>& extra_info, const QMap<QString, QString>& extra_info,
@ -124,7 +126,17 @@ NewInstanceDialog::NewInstanceDialog(const QString& initialGroup,
updateDialogState(); updateDialogState();
if (APPLICATION->settings()->get("NewInstanceGeometry").isValid()) {
restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("NewInstanceGeometry").toByteArray())); restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("NewInstanceGeometry").toByteArray()));
} else {
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
auto screen = parent->screen();
#else
auto screen = QGuiApplication::primaryScreen();
#endif
auto geometry = screen->availableSize();
resize(width(), qMin(geometry.height() - 50, 710));
}
} }
void NewInstanceDialog::reject() void NewInstanceDialog::reject()

View File

@ -13,6 +13,7 @@ ReviewMessageBox::ReviewMessageBox(QWidget* parent, [[maybe_unused]] QString con
auto back_button = ui->buttonBox->button(QDialogButtonBox::Cancel); auto back_button = ui->buttonBox->button(QDialogButtonBox::Cancel);
back_button->setText(tr("Back")); back_button->setText(tr("Back"));
ui->toggleDepsButton->hide();
ui->modTreeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch); ui->modTreeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch);
ui->modTreeWidget->header()->setStretchLastSection(false); ui->modTreeWidget->header()->setStretchLastSection(false);
ui->modTreeWidget->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); ui->modTreeWidget->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
@ -75,6 +76,8 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info)
} }
itemTop->insertChildren(childIndx++, { requiredByItem }); itemTop->insertChildren(childIndx++, { requiredByItem });
ui->toggleDepsButton->show();
m_deps << itemTop;
} }
auto versionTypeItem = new QTreeWidgetItem(itemTop); auto versionTypeItem = new QTreeWidgetItem(itemTop);
@ -108,3 +111,10 @@ void ReviewMessageBox::retranslateUi(QString resources_name)
ui->explainLabel->setText(tr("You're about to download the following %1:").arg(resources_name)); ui->explainLabel->setText(tr("You're about to download the following %1:").arg(resources_name));
ui->onlyCheckedLabel->setText(tr("Only %1 with a check will be downloaded!").arg(resources_name)); ui->onlyCheckedLabel->setText(tr("Only %1 with a check will be downloaded!").arg(resources_name));
} }
void ReviewMessageBox::on_toggleDepsButton_clicked()
{
m_deps_checked = !m_deps_checked;
auto state = m_deps_checked ? Qt::Checked : Qt::Unchecked;
for (auto dep : m_deps)
dep->setCheckState(0, state);
};

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <QDialog> #include <QDialog>
#include <QTreeWidgetItem>
namespace Ui { namespace Ui {
class ReviewMessageBox; class ReviewMessageBox;
@ -28,8 +29,14 @@ class ReviewMessageBox : public QDialog {
~ReviewMessageBox() override; ~ReviewMessageBox() override;
protected slots:
void on_toggleDepsButton_clicked();
protected: protected:
ReviewMessageBox(QWidget* parent, const QString& title, const QString& icon); ReviewMessageBox(QWidget* parent, const QString& title, const QString& icon);
Ui::ReviewMessageBox* ui; Ui::ReviewMessageBox* ui;
QList<QTreeWidgetItem*> m_deps;
bool m_deps_checked = true;
}; };

View File

@ -44,15 +44,20 @@
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="explainLabel"> <widget class="QLabel" name="explainLabel"/>
</widget>
</item> </item>
<item row="5" column="0" rowspan="2"> <item row="5" column="0" rowspan="2">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QLabel" name="onlyCheckedLabel"> <widget class="QPushButton" name="toggleDepsButton">
<property name="text">
<string>Toggle Dependencies</string>
</property>
</widget> </widget>
</item> </item>
<item>
<widget class="QLabel" name="onlyCheckedLabel"/>
</item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons"> <property name="standardButtons">

View File

@ -25,6 +25,7 @@
#include "Application.h" #include "Application.h"
#include "BuildConfig.h" #include "BuildConfig.h"
#include "Markdown.h" #include "Markdown.h"
#include "StringUtils.h"
#include "ui_UpdateAvailableDialog.h" #include "ui_UpdateAvailableDialog.h"
UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion, UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion,
@ -43,7 +44,7 @@ UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion,
ui->icon->setPixmap(APPLICATION->getThemedIcon("checkupdate").pixmap(64)); ui->icon->setPixmap(APPLICATION->getThemedIcon("checkupdate").pixmap(64));
auto releaseNotesHtml = markdownToHTML(releaseNotes); auto releaseNotesHtml = markdownToHTML(releaseNotes);
ui->releaseNotes->setHtml(releaseNotesHtml); ui->releaseNotes->setHtml(StringUtils::htmlListPatch(releaseNotesHtml));
ui->releaseNotes->setOpenExternalLinks(true); ui->releaseNotes->setOpenExternalLinks(true);
connect(ui->skipButton, &QPushButton::clicked, this, [this]() { connect(ui->skipButton, &QPushButton::clicked, this, [this]() {

View File

@ -480,32 +480,42 @@ void InstanceView::paintEvent([[maybe_unused]] QPaintEvent* event)
if (model()->rowCount() == 0) { if (model()->rowCount() == 0) {
painter.save(); painter.save();
const QString line1 = tr("Welcome!"); QString emptyString = tr("Welcome!") + "\n" + tr("Click \"Add Instance\" to get started.");
const QString line2 = tr("Click \"Add Instance\" to get started.");
auto rect = this->viewport()->rect();
auto font = option.font;
font.setPointSize(37);
painter.setFont(font);
auto fm = painter.fontMetrics();
if (rect.height() <= (fm.height() * 5) || rect.width() <= fm.horizontalAdvance(line2)) { // calculate the rect for the overlay
auto s = rect.height() / (5. * fm.height()); painter.setRenderHint(QPainter::Antialiasing, true);
auto sx = rect.width() * 1. / fm.horizontalAdvance(line2); QFont font("sans", 20);
if (s >= sx) font.setBold(true);
s = sx;
auto ps = font.pointSize() * s; QRect bounds = viewport()->geometry();
if (ps <= 0) bounds.moveTop(0);
ps = 1; auto innerBounds = bounds;
font.setPointSize(ps); innerBounds.adjust(10, 10, -10, -10);
QColor background = QApplication::palette().color(QPalette::WindowText);
QColor foreground = QApplication::palette().color(QPalette::Base);
foreground.setAlpha(190);
painter.setFont(font); painter.setFont(font);
fm = painter.fontMetrics(); auto fontMetrics = painter.fontMetrics();
auto textRect = fontMetrics.boundingRect(innerBounds, Qt::AlignHCenter | Qt::TextWordWrap, emptyString);
textRect.moveCenter(bounds.center());
auto wrapRect = textRect;
wrapRect.adjust(-10, -10, 10, 10);
// check if we are allowed to draw in our area
if (!event->rect().intersects(wrapRect)) {
return;
} }
// text painter.setBrush(QBrush(background));
rect.setTop(rect.top() + fm.height() * 1.5); painter.setPen(foreground);
painter.drawText(rect, Qt::AlignHCenter, line1); painter.drawRoundedRect(wrapRect, 5.0, 5.0);
rect.setTop(rect.top() + fm.height());
painter.drawText(rect, Qt::AlignHCenter, line2); painter.setPen(foreground);
painter.setFont(font);
painter.drawText(textRect, Qt::AlignHCenter | Qt::TextWordWrap, emptyString);
painter.restore(); painter.restore();
return; return;
} }

View File

@ -66,6 +66,9 @@ void VisualGroup::update()
rows[currentRow].height = maxRowHeight; rows[currentRow].height = maxRowHeight;
rows[currentRow].top = offsetFromTop; rows[currentRow].top = offsetFromTop;
currentRow++; currentRow++;
if (currentRow >= rows.size()) {
currentRow = rows.size() - 1;
}
offsetFromTop += maxRowHeight + 5; offsetFromTop += maxRowHeight + 5;
positionInRow = 0; positionInRow = 0;
maxRowHeight = 0; maxRowHeight = 0;
@ -158,13 +161,14 @@ void VisualGroup::drawHeader(QPainter* painter, const QStyleOptionViewItem& opti
painter->setRenderHint(QPainter::Antialiasing); painter->setRenderHint(QPainter::Antialiasing);
// sizes and offsets, to keep things consistent below // sizes and offsets, to keep things consistent below
int arrowOffsetLeft = fontMetrics.height() / 2 + 7; const int arrowOffsetLeft = fontMetrics.height() / 2 + 7;
int textOffsetLeft = arrowOffsetLeft * 2; const int textOffsetLeft = arrowOffsetLeft * 2;
int arrowSize = 6; const int centerHeight = optRect.top() + fontMetrics.height() / 2;
int centerHeight = optRect.top() + fontMetrics.height() / 2; const QString& textToDraw = text.isEmpty() ? QObject::tr("Ungrouped") : text;
// BEGIN: arrow // BEGIN: arrow
{ {
constexpr int arrowSize = 6;
QPolygon arrowPolygon; QPolygon arrowPolygon;
if (collapsed) { if (collapsed) {
arrowPolygon << QPoint(arrowOffsetLeft - arrowSize / 2, centerHeight - arrowSize) arrowPolygon << QPoint(arrowOffsetLeft - arrowSize / 2, centerHeight - arrowSize)
@ -188,9 +192,26 @@ void VisualGroup::drawHeader(QPainter* painter, const QStyleOptionViewItem& opti
textRect.setHeight(fontMetrics.height()); textRect.setHeight(fontMetrics.height());
textRect.setRight(textRect.right() - 7); textRect.setRight(textRect.right() - 7);
painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, !text.isEmpty() ? text : QObject::tr("Ungrouped")); painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, textToDraw);
} }
// END: text // END: text
// BEGIN: horizontal line
{
penColor.setAlphaF(0.05);
pen.setColor(penColor);
painter->setPen(pen);
// startPoint is left + arrow + text + space
const int startPoint =
optRect.left() + fontMetrics.height() + fontMetrics.size(Qt::AlignLeft | Qt::AlignVCenter, textToDraw).width() + 20;
painter->setRenderHint(QPainter::Antialiasing, false);
QPolygon polygon;
// for some reason the height (yPos) doesn't look centered, so we are adding 1 to the center height
const int lineHeight = centerHeight + 1;
polygon << QPoint(startPoint, lineHeight) << QPoint(optRect.right() - 3, lineHeight);
painter->drawPolyline(polygon);
}
// END: horizontal line
} }
int VisualGroup::totalHeight() const int VisualGroup::totalHeight() const

View File

@ -206,7 +206,7 @@
<item> <item>
<widget class="QCheckBox" name="onlineFixes"> <widget class="QCheckBox" name="onlineFixes">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Emulates usages of old online services which are no longer operating.&lt;/p&gt;&lt;p&gt;This currently allows modern skins to be used.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Emulates usages of old online services which are no longer operating.&lt;/p&gt;&lt;p&gt;Current fixes include: skin and online mode support.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Enable online fixes (experimental)</string> <string>Enable online fixes (experimental)</string>

View File

@ -290,12 +290,12 @@ void ExternalResourcesPage::disableItem()
void ExternalResourcesPage::viewConfigs() void ExternalResourcesPage::viewConfigs()
{ {
DesktopServices::openDirectory(m_instance->instanceConfigFolder(), true); DesktopServices::openPath(m_instance->instanceConfigFolder(), true);
} }
void ExternalResourcesPage::viewFolder() void ExternalResourcesPage::viewFolder()
{ {
DesktopServices::openDirectory(m_model->dir().absolutePath(), true); DesktopServices::openPath(m_model->dir().absolutePath(), true);
} }
bool ExternalResourcesPage::current(const QModelIndex& current, const QModelIndex& previous) bool ExternalResourcesPage::current(const QModelIndex& current, const QModelIndex& previous)

View File

@ -346,7 +346,7 @@ void InstanceSettingsPage::loadSettings()
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
ui->lineEditOpenALPath->setPlaceholderText(APPLICATION->m_detectedOpenALPath); ui->lineEditOpenALPath->setPlaceholderText(APPLICATION->m_detectedOpenALPath);
#else #else
ui->lineEditGLFWPath->setPlaceholderText(tr("Path to %1 library file").arg(BuildConfig.OPENAL_LIBRARY_NAME)); ui->lineEditOpenALPath->setPlaceholderText(tr("Path to %1 library file").arg(BuildConfig.OPENAL_LIBRARY_NAME));
#endif #endif
// Performance // Performance

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