Compare commits

..

230 Commits

Author SHA1 Message Date
seth
55207a58a6 fix(packaging): only exclude *some* folders in win>=2h1h 2023-07-26 00:44:28 -04:00
seth
163c22ce4c TESTING: force an autoupdate 2023-07-16 17:18:44 -04:00
seth
615600ec13 fix(packaging): disable filesystem/reg virtualization
prism won't be able to make %appdata%\PrismLauncher on first launch,
which would be ok...but this in turn breaks opening and folders :(

Signed-off-by: seth <getchoo@tuta.io>
2023-07-15 22:38:18 -04:00
seth
17211c2100 TESTING: always check for updates on launch 2023-07-15 19:55:22 -04:00
seth
6f113ae9d6 fix(packaging): use 2018 schema in appinstaller
Signed-off-by: seth <getchoo@tuta.io>
2023-07-15 19:55:22 -04:00
seth
46ce358bb7 fix(packaging): install correct vc++ package in msix
Signed-off-by: seth <getchoo@tuta.io>
2023-07-15 19:55:15 -04:00
seth
aa693f86f4 fix(packaging): remove unnecessary/incompatible elements in msix
Signed-off-by: seth <getchoo@tuta.io>
2023-07-10 20:31:01 -04:00
seth
d6c708da42 disable some other workflows 2023-07-10 20:19:05 -04:00
seth
271fb8829a fix(actions): fix msixbundle versioning
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 15:27:48 -04:00
seth
15f34451b7 feat(packaging): add update/repair uris in msix
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 14:28:22 -04:00
seth
b3d8de2a7a temporarily disable some workflows 2023-07-07 07:44:55 -04:00
seth
2c83ef1c46 fix(actions): use correct variable for appx version
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:08 -04:00
seth
4562700653 fix(packaging): don't sign appinstaller
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:08 -04:00
seth
eb7739c0ca use temp url for testing 2023-07-07 06:46:08 -04:00
seth
a401a9ceb7 revert(actions): build setup.exes for modern msvc again
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:08 -04:00
seth
7dda497cc6 chore(actions): use msix for modern builds in winget release
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:07 -04:00
seth
5e5faa7239 chore(packaging): use org.prismlauncher in msix app id
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:07 -04:00
seth
55ac7625ff chore(packaging): use correct publisher name for msix
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:07 -04:00
seth
c065d06149 chore(actions): include prism_version in matrix for msix
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:07 -04:00
seth
76cc8a33d4 feat(packaging): register file associations in msix
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:06 -04:00
seth
84498285c0 feat(packaging): bundle vc++2022 with msix
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:06 -04:00
seth
faeadeccca feat(packaging): support older windows versions in msix
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:06 -04:00
seth
a67fb3e5ca chore(actions): add msix(bundle)/appinstaller to trigger_release
this probably works...right?

Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:06 -04:00
seth
32b9adec82 chore(actions): distribute appinstaller
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:05 -04:00
seth
6866e5367e feat(packaging): add support for appinstaller
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:05 -04:00
seth
1c66c4ed82 fix(actions): limit msix version suffix to 3 numbers
good thing i got an unlucky commit hash :p

Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:05 -04:00
seth
0df38008a1 fix(actions): checkout repo during bundle-msix
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:05 -04:00
seth
6daa653ab3 fix(packaging): don't use letters in msix version
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:04 -04:00
seth
a37f1dd69d feat(packaging): start using msixbundles
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:04 -04:00
seth
862acca151 fix(packaging): don't hardcode arch/version in appmanifest
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:04 -04:00
seth
c3770a9a32 feat(packaging): use msix for msvc builds
Signed-off-by: seth <getchoo@tuta.io>
2023-07-07 06:46:04 -04:00
seth
2cb22ad280 Merge pull request #1310 from getchoo/autoupdate-flake
feat(actions): add update-flake-lock
2023-07-05 23:25:12 +00:00
Sefa Eyeoglu
5ba13297c6 Merge pull request #1284 from Ryex/fix/properly-track-failed-copies-and-clones 2023-07-05 19:26:07 +02:00
seth
24b9ed106f feat(actions): add update-flake-lock
Signed-off-by: seth <getchoo@tuta.io>
2023-07-04 18:49:22 -04:00
seth
7c8a010378 Merge pull request #1304 from Scrumplex/chore-flake-update-1
flake.lock: Update
2023-07-04 22:43:00 +00:00
Tayou
dedc9e4edc Merge pull request #1127 from Trial97/scale_cat 2023-07-03 15:46:56 +02:00
Sefa Eyeoglu
14692eed40 flake.lock: Update
Flake lock file updates:

• Updated input 'flake-parts':
    'github:hercules-ci/flake-parts/006c75898cf814ef9497252b022e91c946ba8e17' (2023-05-08)
  → 'github:hercules-ci/flake-parts/267149c58a14d15f7f81b4d737308421de9d7152' (2023-07-01)
• Updated input 'flake-parts/nixpkgs-lib':
    'github:NixOS/nixpkgs/da45bf6ec7bbcc5d1e14d3795c025199f28e0de0?dir=lib' (2023-04-30)
  → 'github:NixOS/nixpkgs/4bc72cae107788bf3f24f30db2e2f685c9298dc9?dir=lib' (2023-06-29)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/aeb75dba965e790de427b73315d5addf91a54955' (2023-05-25)
  → 'github:nixos/nixpkgs/cd99c2b3c9f160cd004318e0697f90bbd5960825' (2023-07-01)
• Updated input 'pre-commit-hooks':
    'github:cachix/pre-commit-hooks.nix/61e567d6497bc9556f391faebe5e410e6623217f' (2023-05-23)
  → 'github:cachix/pre-commit-hooks.nix/42587d3414d1747999a5f71e92a83cf6547b62da' (2023-07-03)
• Updated input 'pre-commit-hooks/flake-utils':
    'github:numtide/flake-utils/5aed5285a952e0b949eb3ba02c12fa4fcfef535f' (2022-11-02)
  → 'github:numtide/flake-utils/a1720a10a6cfe8234c0e93907ffe81be440f4cef' (2023-05-31)
• Added input 'pre-commit-hooks/flake-utils/systems':
    'github:nix-systems/default/da67096a3b9bf56a91d16901293e51ba5b49a27e' (2023-04-09)

Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
2023-07-03 14:27:02 +02:00
TheKodeToad
43e6f05ed5 Merge pull request #1232 from telans/screenshots-update
ScreenshotsPage fixes
2023-07-02 16:44:54 +01:00
Sefa Eyeoglu
b51f1f1d41 Merge pull request #1298 from TurboWafflz/develop 2023-07-02 09:52:59 +02:00
Finian Wright
3fe518ff2b Fix compiling on OpenBSD
Signed-off-by: Finian Wright <turbowafflz@gmail.com>
2023-07-02 00:36:21 -04:00
seth
c523765c19 Merge pull request #1292 from Trial97/export5
Removed logs from instance export
2023-07-01 04:14:35 +00:00
Trial97
18e628e873 removed extra condition
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-30 12:10:00 +03:00
Trial97
81207c6502 Made sure the logs are ignored when collecting files
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-30 10:52:10 +03:00
Trial97
87efa700ab Removed logs from instance export
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-30 00:01:36 +03:00
Trial97
0008b22d8b Renamed function
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-28 18:41:47 +03:00
Trial97
8f9bd9617f Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into scale_cat 2023-06-28 18:29:04 +03:00
Sefa Eyeoglu
faec21d572 Merge pull request #1266 from TheKodeToad/smol-tweaks 2023-06-28 08:47:53 +02:00
seth
213f03351f Merge pull request #1280 from Trial97/shortcut
Fixed illegal characters in shortcuts name
2023-06-27 20:01:48 +00:00
Rachel Powers
00be211169 Update launcher/FileSystem.cpp
Co-authored-by: TheKodeToad <TheKodeToad@proton.me>
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
2023-06-27 13:01:28 -07:00
seth
90fc720190 Merge pull request #1281 from Trial97/screenshot
Added more information to the screenshot upload warning
2023-06-27 20:01:18 +00:00
Rachel Powers
23b3711f96 fix(filesystem): track failed copies and clones
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
2023-06-27 12:41:36 -07:00
Trial97
54cb077b40 Added more information to the screenshot upload warning
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-27 19:31:36 +03:00
Trial97
92847b9774 omit icon remove on macos
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-27 19:15:20 +03:00
Trial97
6e5716f097 Fixed illegal characters in shortcuts name
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-27 19:05:32 +03:00
TheKodeToad
904b128afe Merge pull request #1277 from Trial97/remove_mojang
Removed unused files
2023-06-26 23:09:27 +01:00
Trial97
dffffc784e Removed unused files
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-26 22:33:10 +03:00
Sefa Eyeoglu
d25452a64e Merge pull request #1274 from TheKodeToad/java-signature 2023-06-26 11:19:53 +02:00
Sefa Eyeoglu
41d0f74750 Merge pull request #1275 from Scrumplex/git-blame-ignore 2023-06-26 10:56:26 +02:00
Sefa Eyeoglu
f8f1c3cf23 chore: add Git Blame ignore file
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
2023-06-26 09:08:03 +02:00
Sefa Eyeoglu
ed4dce2fb6 fix: install logo to multimc theme in genicons.sh
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
2023-06-26 08:59:48 +02:00
Sefa Eyeoglu
4d49486cc9 Merge pull request #1065 from leo78913/gamescope-close-button 2023-06-26 08:45:28 +02:00
TheKodeToad
8bebd7f042 Generate special signature composed of multiple elements instead of relying on timestamp for Java version cache invalidation
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-26 01:30:47 +01:00
seth
903fae94be Merge pull request #1272 from Trial97/fix_selected
fix: Page container extra info set on logs page
2023-06-25 21:16:18 +00:00
Trial97
6d0e255ca1 fix: Page container extra info set on logs page
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-25 22:00:33 +03:00
TheKodeToad
1bd778d0ae Merge pull request #1270 from TheKodeToad/fix-unchecked-value
Fix unsafe usage of std::optional::value in FlameAPI
2023-06-25 18:24:29 +01:00
TheKodeToad
4745ab64cd Deduplicate launcher icon
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-25 15:02:25 +01:00
Leo
953a2590e2 Add fixme comment for no SSD detection
Co-authored-by: Sefa Eyeoglu <contact@scrumplex.net>
Signed-off-by: Leo <leo3758@riseup.net>
2023-06-25 10:11:58 -03:00
TheKodeToad
02b628653b Fix markdown header
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-25 13:43:03 +01:00
TheKodeToad
c75ba0f855 Fix big mistake :iea:
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-25 12:46:07 +01:00
TheKodeToad
514080653f Fix unsafe usage of std::optional::value in FlameAPI
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-25 11:56:28 +01:00
TheKodeToad
603ed22015 Replace accidental usages of QAbstractButton::pressed
This signal is not usually what you want, and creates an inconsistent experience.

Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-25 10:36:54 +01:00
TheKodeToad
5eb71fc6a9 Revert "feat(Mods): hide 'Provider' column when no mods have providers"
With Ryex's change, this causes issues. Apparently you need to sign off reverts! That's just weird...

Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-24 23:36:32 +01:00
TheKodeToad
8a3aba1634 Fix Open Global Settings, why not
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-24 23:30:51 +01:00
TheKodeToad
bb8e6ef35e Fix flat white launcher icon
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-24 23:27:01 +01:00
TheKodeToad
529e2054ea A few tweaks, with inspiration from Zeke :3
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-24 23:26:27 +01:00
TheKodeToad
bcf45c74a1 Merge pull request #986 from Trial97/develop
Mod dependencies
2023-06-24 14:04:27 +01:00
Trial97
8b576fd2bd Added translation
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-24 15:59:55 +03:00
Trial97
7fdd68d768 Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into develop 2023-06-24 15:58:55 +03:00
Sefa Eyeoglu
df6d46de9e Merge pull request #1261 from telans/modrinthexport-url 2023-06-24 10:51:14 +02:00
Rachel Powers
4322222acc Merge pull request #1255 from Trial97/export4
Added Thumbs.db to excluded files in MrPackExport
2023-06-23 21:40:55 -07:00
seth
cce6a54701 Merge pull request #1258 from leo78913/resources-pack-page-crash-fix
fix: fix crash when hiding columns on resource packs page
2023-06-24 00:39:47 -04:00
Rachel Powers
68865353cf Merge pull request #1259 from PrismLauncher/update-devs
Update developers
2023-06-23 21:38:16 -07:00
James Beddek
20ba6e5fb5 modrinth: use encoded url when exporting pack
Ensures that necessary url components such as spaces are encoded.
Prevents an error when submitting the resulting file to modrinth.

See https://discord.com/channels/734077874708938864/1120070731242410024

Fixes: #1226
Signed-off-by: James Beddek <telans@posteo.de>
2023-06-24 15:42:58 +12:00
TheKodeToad
d74a23d5b2 Update developers
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-23 21:00:55 +01:00
leo78913
750209b8bb fix: fix crash when hiding columns on resource packs page
Signed-off-by: leo78913 <leo3758@riseup.net>
2023-06-23 16:55:51 -03:00
TheKodeToad
741c23a72a Merge pull request #1256 from Trial97/fix11
Fixed hashers
2023-06-23 19:53:44 +01:00
Trial97
28de461067 Fixed hashers
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-23 21:38:41 +03:00
Trial97
69c709b05a Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into develop
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-23 20:01:17 +03:00
Rachel Powers
046d510134 Merge pull request #1200 from Trial97/net_job_crash
Made ByteSynkArray to use shared_ptr
2023-06-23 09:13:52 -07:00
Trial97
85bbab0e92 Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into net_job_crash 2023-06-23 14:38:30 +03:00
Trial97
67db141203 Renamed getResults to resultsReady
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-23 14:38:23 +03:00
Trial97
763b3c3236 Added Thumbs.db to excluded files in MrPackExport
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-23 10:38:26 +03:00
seth
8df5ab8aa7 Merge pull request #1251 from getchoo/github-clarify
chore: add 'suggest a feature' message in help
2023-06-22 17:13:00 -04:00
seth
83efdccfe1 Merge pull request #1252 from getchoo/import-hehe
chore: avoid confusion in file/url import dialog
2023-06-22 17:12:35 -04:00
Rachel Powers
f1ebec641a Merge pull request #1058 from Ryex/feature/images-for-resource-page
Feature: image coumn for Mod, Resource Pack, and Texturepack pages
2023-06-22 13:26:47 -07:00
seth
a4521ac0bb chore: avoid confusion in file/url import dialog
Signed-off-by: seth <getchoo@tuta.io>
2023-06-22 16:15:03 -04:00
Tayou
c5f16276d7 Merge pull request #1235 from ChrisLane/java-check-debug-msg-fix 2023-06-22 22:03:30 +02:00
seth
03361e51ef chore: add 'suggest a feature' message in help
Signed-off-by: seth <getchoo@tuta.io>
2023-06-22 15:58:53 -04:00
Sefa Eyeoglu
05a8232a8f Merge pull request #1243 from Trial97/export
Added regex expresion to exclude .DS_Store files
2023-06-22 08:09:31 +02:00
Trial97
2e82c1d40c Added regex expresion to exclude .DS_Store files
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-21 23:22:25 +03:00
TheKodeToad
0975dbc3dd Merge pull request #1228 from Trial97/curent_pack_crash
Fixes #1212
2023-06-21 15:35:09 +01:00
DioEgizio
8aa02320e0 Merge pull request #1231 from telans/modrinth-default-icon
Modrinth: use default icon for non-managed packs
2023-06-21 12:25:26 +02:00
Rachel Powers
470518eb3a fix: resize columns on hide ^& uniform heights
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
2023-06-21 02:31:40 -07:00
seth
ca659136e2 Merge pull request #1119 from Trial97/mods_txt
Added dynamic page extra info
2023-06-20 19:56:38 -04:00
seth
f1a6dc5332 Merge pull request #1184 from clickdevin/develop
Fix bugs when updating curseforge modpacks
2023-06-20 19:55:48 -04:00
seth
4eaa7dc8b1 Merge pull request #1234 from Szowisz/develop
Fix compiling on FreeBSD
2023-06-20 19:55:03 -04:00
Chris Lane
9ad29e8d85 Remove extra spaces in one more Java checker debug
Signed-off-by: Chris Lane <git@chrislane.com>
2023-06-20 15:51:31 +01:00
Chris Lane
07f3d27fb8 Clean up 'Running java checker' debug msg code
Signed-off-by: Chris Lane <git@chrislane.com>
2023-06-20 15:36:25 +01:00
Chris Lane
f2692e60f3 Add missing space in java checker debug message
Signed-off-by: Chris Lane <git@chrislane.com>
2023-06-20 12:44:44 +01:00
James Beddek
009623823d Modrinth: use default icon for non-managed packs
Fixes: #317
Signed-off-by: James Beddek <telans@posteo.de>
2023-06-20 23:00:13 +12:00
Jakub Wroński
a32a3e25ad Fix compiling on FreeBSD
Signed-off-by: Jakub Wroński <kubawronski161@gmail.com>
2023-06-20 12:59:47 +02:00
Trial97
3e3be9ae6f Added fallback for quilt if the API or Kotilin is not present
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-20 13:28:57 +03:00
TheKodeToad
3c648ff9fd Merge pull request #1233 from p2js/ui-consistency-fix
Remove inconsistent/unneeded question marks in UI
2023-06-20 11:05:26 +01:00
Alfio
f769b0b4c6 Remove inconsistent/unneeded question marks in UI
Signed-off-by: P2 <tomarchioalfio5@gmail.com>
2023-06-20 10:40:27 +02:00
James Beddek
75bd626f33 Screenshots: do not retry image thumbnailing on null result
This causes the thumbnailing thread pool to spend a lot of time attempting to
retry an image that failed. A null result is common where the image is too large
to be allocated (>128MiB alloc).

The repeated retries continue after page delete, causing hangs if a user
tries to exit the application.

Fixes: #1201
Signed-off-by: James Beddek <telans@posteo.de>
2023-06-20 16:59:59 +12:00
James Beddek
f2471f0f68 Screenshots: clear the thumbnailing pool on page delete
Removes pending QThreadPool jobs which linger after page delete.
May help with #1201 by allowing the pool to finish earlier.

Signed-off-by: James Beddek <telans@posteo.de>
2023-06-20 16:54:15 +12:00
James Beddek
d9b24f7705 Screenshots: remove path from watcher if it no longer exists
Signed-off-by: James Beddek <telans@posteo.de>
2023-06-20 16:52:57 +12:00
Trial97
b62e4c0cc7 Fixed build
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-19 21:32:19 +03:00
Trial97
f6f0fbbd9f Fixed removeIf
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-19 21:23:48 +03:00
Trial97
c13a90540c Added overide for Quilt/Fabric
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-19 21:20:35 +03:00
Trial97
6fd729e285 Fixed regresion regarding modrinth project_id in dependence array
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-19 17:42:16 +03:00
Trial97
8ad9692daa Changed qWarning to qDebug for raw data
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-19 14:10:29 +03:00
Trial97
d02858040e Fixes #1212
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-19 13:09:37 +03:00
Trial97
3ee0fec729 Removed mods from lambda
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-19 12:22:22 +03:00
Trial97
ec063470d7 Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into mods_txt 2023-06-19 12:20:50 +03:00
Trial97
b2ed2bf810 Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into net_job_crash 2023-06-19 12:01:35 +03:00
Sefa Eyeoglu
af6bf11793 Merge pull request #1224 from DioEgizio/add-appstream 2023-06-19 09:35:56 +02:00
DioEgizio
d5b5f0503c chore: change xml name to what linuxdeploy wants
Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
2023-06-19 09:17:15 +02:00
DioEgizio
555cb40efd chore: install appstream in appimage
Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
2023-06-19 09:17:15 +02:00
seth
9d22fce53f Merge pull request #1214 from PrismLauncher/renovate/cachix-install-nix-action-22.x
chore(deps): update cachix/install-nix-action action to v22
2023-06-18 22:06:47 -04:00
seth
aee0999daa Merge pull request #1210 from getchoo/opengl-appimage
fix(appimage): bundle generic opengl lib
2023-06-18 22:06:03 -04:00
Trial97
1bdde1f947 Small fixes
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-19 00:36:37 +03:00
Trial97
319ce8c19f Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into develop 2023-06-19 00:06:44 +03:00
Trial97
4e66f55d84 Removed extra headers
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-18 23:32:17 +03:00
Trial97
6826f1d605 Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into net_job_crash 2023-06-18 23:27:41 +03:00
Trial97
0161520b33 Fixed leaks
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-18 23:27:26 +03:00
Sefa Eyeoglu
12cd8a7bea Merge pull request #1218 from flowln/bad_commit_string_in_bundled_package
Hide the Git commit entry on the About dialog when the build doesn't have git stuff support
2023-06-17 18:04:54 +02:00
flow
2d00a727f6 fix: hide git commit when the build doesn't have git stuff support
This fixes the Git commit string being "GITDIR-NOTFOUND" on the About
page when building a package from the bundled source archive.

Signed-off-by: flow <flowlnlnln@gmail.com>
2023-06-17 11:09:05 -03:00
renovate[bot]
b123f4f948 chore(deps): update cachix/install-nix-action action to v22 2023-06-16 21:58:32 +00:00
seth
fd9a8d1551 Merge pull request #1110 from TheKodeToad/version-search
Add a search bar to version lists
2023-06-16 14:47:39 -04:00
seth
6bc1150ad0 Merge pull request #1187 from getchoo/rel-8.0
chore: bump to 8.0
2023-06-16 14:46:25 -04:00
seth
45cce1d19a fix(appimage): bundle generic opengl lib
Signed-off-by: seth <getchoo@tuta.io>
2023-06-16 14:34:44 -04:00
Trial97
147366bc0a Made ByteSynkArray to use shared_ptr
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-15 22:59:41 +03:00
Trial97
6667ff9330 Updated text and fixed mod page text update
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-15 12:32:48 +03:00
Trial97
5bf091149d Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into mods_txt 2023-06-15 11:46:44 +03:00
Trial97
1ff8136f98 Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into scale_cat 2023-06-15 10:36:05 +03:00
Trial97
182e19eae3 Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into develop 2023-06-15 10:35:26 +03:00
seth
e8b0a7c6f0 chore: bump to 8.0
Signed-off-by: seth <getchoo@tuta.io>
2023-06-14 12:45:08 -04:00
clickdevin
d4f2059b78 Fix bugs when updating curseforge modpacks
Signed-off-by: clickdevin <git@clickdevin.me>
2023-06-14 10:42:37 -04:00
TheKodeToad
b7d82354cb [ci skip] License headers!! (yay)
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-10 14:43:58 +01:00
TheKodeToad
0b4807dc1f Questionable fix two
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-07 22:55:37 +01:00
Trial97
d33de2e427 Made cat scalable
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-08 00:54:32 +03:00
TheKodeToad
1191c33c2b Remove flawed implementation
This seems to add the latest icon (bug) as a fallback if not provided...
but it mainly seems to cause problems... 🤷

I swear I did `git add .`.

Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-07 22:45:21 +01:00
TheKodeToad
52054469cd Fix *bug* lol
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-07 22:39:23 +01:00
Trial97
bbd9e4de9b Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into mods_txt 2023-06-07 20:18:56 +03:00
Trial97
1e702ee40f Added dynamic page extra info
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-07 00:16:23 +03:00
TheKodeToad
a2d0d5a71d Allow arrow key movement, fix auto-focus
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-06 18:26:26 +01:00
TheKodeToad
c343036d3b Simplify
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-06 12:24:53 +01:00
TheKodeToad
7c5047b2ac cAsE iNsEnSiTiVe
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-05 23:12:47 +01:00
TheKodeToad
961285d6ab Add a search bar to version lists
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-06-05 22:47:42 +01:00
Trial97
9ca74cd009 Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into develop
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-06-03 00:48:13 +03:00
Rachel Powers
5fe9e30f39 fix: use instance settings, make image column user resizeable, fix memory leak
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
2023-05-28 14:53:15 -07:00
Rachel Powers
4eb9083ddc refactor: column names as class property, use string names in setting
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
2023-05-28 13:00:08 -07:00
Rachel Powers
b28f682ad9 Merge branch 'develop' into feature/images-for-resource-page 2023-05-28 12:01:49 -07:00
Trial97
51c39ec681 Merge branch 'Fix_Assert' of github.com:Trial97/PrismLauncher into develop 2023-05-28 19:16:14 +03:00
Trial97
10436ed70c Fixed code quality
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-05-28 19:15:41 +03:00
Trial97
775236a1b3 Merge branch 'Fix_Assert' of github.com:Trial97/PrismLauncher into develop
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-05-28 18:27:54 +03:00
Trial97
b9503ff15f Added Q_DECLARE_METATYPE(ModPlatform::IndexedPack::Ptr)
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-05-28 18:27:02 +03:00
Trial97
b4dff181f7 Fixed Ptr logic
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-05-28 18:22:55 +03:00
Trial97
737fc1a2a4 Merge branch 'Fix_Assert' of github.com:Trial97/PrismLauncher into develop
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-05-28 18:01:39 +03:00
Trial97
bdff8591aa Removed extra loop
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-05-28 17:54:46 +03:00
Trial97
27c3775f99 Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into Fix_Assert
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-05-28 17:44:23 +03:00
Trial97
bf0a577fb9 Fixed repaint issue
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-05-28 16:57:35 +03:00
Trial97
7d79abb607 Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into Fix_Assert 2023-05-27 23:06:55 +03:00
Rachel Powers
086a7e19f0 feat: Column on left, hideable
- columns are hideable (saves to settings)
- image column moved to left
- datamodals can provide resize modes

Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
2023-05-24 20:15:34 -07:00
Trial97
2f37cb31d9 Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into develop 2023-05-20 13:36:05 +03:00
Trial97
e5534cd1f3 Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into Fix_Assert 2023-05-20 13:35:47 +03:00
leo78913
4f0ec908ec feat: add a close button to the main toolbar when running on gamescope
Signed-off-by: leo78913 <leo3758@riseup.net>
2023-05-17 00:57:28 -03:00
Trial97
94cd831e8d Made sure the metadata is valid when checking mod deps
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-05-14 22:13:53 +03:00
Trial97
ffcc58cb3e Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into develop 2023-05-14 14:20:24 +03:00
Trial97
8c7fd3327e Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into Fix_Assert 2023-05-14 14:20:04 +03:00
Trial97
ef6cbdfa2a Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into develop 2023-05-07 19:01:20 +03:00
Rachel Powers
74e7c13a17 feat: display license and issue tracker
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
2023-05-05 13:46:38 -07:00
Rachel Powers
3cfcc83ea9 change: don't toggle a resource's enabeling just by selecting it. only if they are on the checkbox.
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
2023-05-05 13:46:01 -07:00
Rachel Powers
ee94be624e use 32x32 images for image column
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
2023-05-05 11:28:19 -07:00
Rachel Powers
2fe3dc5960 fix: fix qchar conversion and codeql
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
2023-05-05 11:13:36 -07:00
Rachel Powers
fd7338d3f3 fix: grow pixmapcache if it is evicting too fast
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
2023-05-04 23:47:27 -07:00
Rachel Powers
ed185f047f feat(resourcePackPage): icon column
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
2023-05-04 23:46:00 -07:00
Rachel Powers
d384d991fa feat(texturepackPage): icon column
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
2023-05-04 23:45:24 -07:00
Rachel Powers
9913080a82 feat(modpage): mod icon in description and column
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
2023-05-04 23:44:28 -07:00
Rachel Powers
ec157b766e feat(mod parsing): load extra mod details
- (image, license, issuetracker)

Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
2023-05-04 23:42:42 -07:00
Trial97
469ef3e06d Fixed code error
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-05-05 00:04:24 +03:00
Trial97
107b470289 Updated required_by as dependency
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-05-04 23:54:46 +03:00
Trial97
8b14b946e2 Merge branch 'Fix_Assert' of github.com:Trial97/PrismLauncher into develop
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-05-04 22:35:16 +03:00
Trial97
f7b912fc9d Fixed comments
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-05-04 22:25:05 +03:00
Trial97
9fbec3793f Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into develop 2023-05-04 20:29:33 +03:00
Trial97
e4449a0ba3 Initialized variable
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-05-03 09:09:07 +03:00
Trial97
42dc3ed03f Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into Fix_Assert 2023-05-03 00:56:26 +03:00
Trial97
f6ed2036b3 Removed comment
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-05-03 00:55:18 +03:00
Trial97
f8bf71e152 Moved the selected resources to one list
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-05-03 00:49:54 +03:00
Trial97
61a2355618 Removed formated but not used files
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-27 01:41:26 +03:00
Trial97
248920a221 Removed extra code
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-27 01:33:46 +03:00
Trial97
b2ecb9ac09 Merge branch 'Fix_Assert' of github.com:Trial97/PrismLauncher into develop 2023-04-22 22:28:08 +03:00
Trial97
f738d7566e Fixed code qulity
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-22 22:27:33 +03:00
Trial97
1d167f8fda Merge branch 'Fix_Assert' of github.com:Trial97/PrismLauncher into develop
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-22 01:32:33 +03:00
Trial97
460e83207f Fixed removeIf for Qt version
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-22 01:18:27 +03:00
Trial97
75116364c6 Small Cleanup
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-22 00:55:11 +03:00
Trial97
f7931c2ee2 Better version handling
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-22 00:47:51 +03:00
Trial97
22bbf1bd1f Merge branch 'Fix_Assert' of github.com:Trial97/PrismLauncher into develop
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-21 21:11:09 +03:00
Trial97
10aac4fe17 Fixed assert
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-21 21:03:01 +03:00
Trial97
42bc91463e Updated links
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-21 20:37:17 +03:00
Trial97
b4fa6e120a Fixed tipo
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-21 18:42:06 +03:00
Trial97
2c744da9f7 More cleanup
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-21 18:42:06 +03:00
Trial97
ffaa47bf54 Small cleanup
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-21 18:42:06 +03:00
Trial97
63c4469475 Merge branch 'develop' of github.com:Trial97/PrismLauncher into develop 2023-04-21 18:42:06 +03:00
Trial97
c1490cd627 Refator task to work with multiple providers
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-21 18:42:06 +03:00
Alexandru Ionut Tripon
1a390b6043 Merge branch 'PrismLauncher:develop' into develop 2023-04-21 18:42:06 +03:00
Trial97
fac33498db Made some copy by reference
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-21 18:42:06 +03:00
Trial97
31e84780a8 Hope to fix windows build errors
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-21 18:42:06 +03:00
Trial97
f3f8f3574a Small headers removal
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-21 18:42:05 +03:00
Trial97
7bd26ce468 Semi fixed the Modrinth dependency implementation
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-21 18:42:05 +03:00
Trial97
bcea19b957 Tried to fix codeQL
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-21 18:42:05 +03:00
Trial97
5079ce8d64 Fixed headers
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-21 18:42:05 +03:00
Alexandru Ionut Tripon
f231a33f6e Merge branch 'PrismLauncher:develop' into develop 2023-04-21 18:42:05 +03:00
Trial97
4fe497cd68 First working version with curseforge mods
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-21 18:42:05 +03:00
Trial97
5655a33515 Added Dependency API
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-21 18:42:05 +03:00
Trial97
11f8d25d94 Added missing character
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-12 00:49:50 +03:00
Trial97
4fbd5abe41 Added task to load dependencies
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-12 00:45:44 +03:00
Trial97
d524935b67 Added task to load local mod information
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-11 20:55:10 +03:00
Trial97
6d5c629b43 Added dependencies to the APIs
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2023-04-10 00:04:35 +03:00
139 changed files with 2825 additions and 1181 deletions

1
.envrc
View File

@@ -1 +1,2 @@
use flake

View File

@@ -32,33 +32,33 @@ jobs:
matrix:
include:
- os: ubuntu-20.04
qt_ver: 5
#- os: ubuntu-20.04
# qt_ver: 5
- os: ubuntu-20.04
qt_ver: 6
qt_host: linux
qt_arch: ''
qt_version: '6.2.4'
qt_modules: 'qt5compat qtimageformats'
qt_tools: ''
#- os: ubuntu-20.04
# qt_ver: 6
# qt_host: linux
# qt_arch: ''
# qt_version: '6.2.4'
# qt_modules: 'qt5compat qtimageformats'
# qt_tools: ''
- os: windows-2022
name: "Windows-MinGW-w64"
msystem: clang64
vcvars_arch: 'amd64_x86'
#- os: windows-2022
# name: "Windows-MinGW-w64"
# msystem: clang64
# vcvars_arch: 'amd64_x86'
- os: windows-2022
name: "Windows-MSVC-Legacy"
msystem: ''
architecture: 'win32'
vcvars_arch: 'amd64_x86'
qt_ver: 5
qt_host: windows
qt_arch: 'win32_msvc2019'
qt_version: '5.15.2'
qt_modules: ''
qt_tools: 'tools_openssl_x86'
#- os: windows-2022
# name: "Windows-MSVC-Legacy"
# msystem: ''
# architecture: 'win32'
# vcvars_arch: 'amd64_x86'
# qt_ver: 5
# qt_host: windows
# qt_arch: 'win32_msvc2019'
# qt_version: '5.15.2'
# qt_modules: ''
# qt_tools: 'tools_openssl_x86'
- os: windows-2022
name: "Windows-MSVC"
@@ -71,6 +71,7 @@ jobs:
qt_version: '6.5.1'
qt_modules: 'qt5compat qtimageformats'
qt_tools: ''
prism_version: '8.0'
- os: windows-2022
name: "Windows-MSVC-arm64"
@@ -83,25 +84,26 @@ jobs:
qt_version: '6.5.1'
qt_modules: 'qt5compat qtimageformats'
qt_tools: ''
prism_version: '8.0'
- os: macos-12
name: macOS
macosx_deployment_target: 11.0
qt_ver: 6
qt_host: mac
qt_arch: ''
qt_version: '6.5.0'
qt_modules: 'qt5compat qtimageformats'
qt_tools: ''
#- os: macos-12
# name: macOS
# macosx_deployment_target: 11.0
# qt_ver: 6
# qt_host: mac
# qt_arch: ''
# qt_version: '6.5.0'
# qt_modules: 'qt5compat qtimageformats'
# qt_tools: ''
- os: macos-12
name: macOS-Legacy
macosx_deployment_target: 10.13
qt_ver: 5
qt_host: mac
qt_version: '5.15.2'
qt_modules: ''
qt_tools: ''
#- os: macos-12
# name: macOS-Legacy
# macosx_deployment_target: 10.13
# qt_ver: 5
# qt_host: mac
# qt_version: '5.15.2'
# qt_modules: ''
# qt_tools: ''
runs-on: ${{ matrix.os }}
@@ -264,23 +266,23 @@ jobs:
- name: Configure CMake (macOS)
if: runner.os == 'macOS' && matrix.qt_ver == 6
run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
- name: Configure CMake (macOS-Legacy)
if: runner.os == 'macOS' && matrix.qt_ver == 5
run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja
- name: Configure CMake (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != ''
shell: msys2 {0}
run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja
- name: Configure CMake (Windows MSVC)
if: runner.os == 'Windows' && matrix.msystem == ''
run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
if ("${{ env.CCACHE_VAR }}")
{
@@ -295,7 +297,7 @@ jobs:
- name: Configure CMake (Linux)
if: runner.os == 'Linux'
run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
##
# BUILD
@@ -428,13 +430,36 @@ jobs:
Get-ChildItem ${{ env.INSTALL_PORTABLE_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_PORTABLE_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
- name: Package (Windows, installer)
- name: Package (Windows, setup.exe)
if: runner.os == 'Windows'
run: |
cd ${{ env.INSTALL_DIR }}
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
- name: Sign installer (Windows)
- name: Package (Windows MSVC, MSIX)
if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'win32'
shell: pwsh
run: |
Set-Location ${{ env.INSTALL_DIR }}
# MSIX only allows numbers in their versions,
# so we need to use another clear identifier
$runNumber = "${{ github.run_number }}"
if ($runNumber.Length -gt 3) {
$version = $runNumber.Substring(($runNumber.Length - 3))
} else {
$version = $runNumber
}
Copy-Item ${{ github.workspace }}\program_info\prismlauncher_*x*.png .
(Get-Content ${{ github.workspace }}\program_info\AppxManifest.xml) `
-replace "PRISM_VERSION_REPLACEME","${{ matrix.prism_version }}.0.$version" `
-replace "PRISM_ARCH_REPLACEME","${{ matrix.architecture }}" `
> .\AppxManifest.xml
makeappx.exe pack /v /h SHA256 /d . /p prismlauncher-${{ matrix.architecture }}.msix
- name: Sign installer (Windows, setup.exe)
if: runner.os == 'Windows'
run: |
if (Get-Content ./codesign.pfx){
@@ -443,6 +468,15 @@ jobs:
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
}
- name: Sign installer (Windows MSVC, MSIX)
if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'win32'
run: |
if (Get-Content ./codesign.pfx){
SignTool sign /fd sha256 /td sha256 /f codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com ${{ env.INSTALL_DIR }}/prismlauncher-${{ matrix.architecture }}.msix
} else {
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
}
- name: Package (Linux)
if: runner.os == 'Linux'
run: |
@@ -521,13 +555,20 @@ jobs:
name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: ${{ env.INSTALL_PORTABLE_DIR }}/**
- name: Upload installer (Windows)
- name: Upload setup.exe (Windows)
if: runner.os == 'Windows'
uses: actions/upload-artifact@v3
with:
name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher-Setup.exe
- name: Upload MSIX Package (Windows MSVC)
if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'win32'
uses: actions/upload-artifact@v3
with:
name: PrismLauncher-${{ matrix.name }}-MSIX-${{ env.VERSION }}-${{ inputs.build_type }}
path: ${{ env.INSTALL_DIR }}/prismlauncher-${{ matrix.architecture }}.msix
- name: Upload binary tarball (Linux, Qt 5)
if: runner.os == 'Linux' && matrix.qt_ver != 6
uses: actions/upload-artifact@v3
@@ -569,50 +610,144 @@ jobs:
run: |
ccache -s
flatpak:
runs-on: ubuntu-latest
container:
image: bilelmoussaoui/flatpak-github-actions:kde-5.15-22.08
options: --privileged
appinstaller:
runs-on: windows-latest
needs: build
env:
BUNDLE_STAGING: "bundle_staging"
APPINSTALLER_STAGING: "appinstaller_staging"
PRISM_VERSION: '8.0'
steps:
- name: Install MSVC
uses: ilammy/msvc-dev-cmd@v1
with:
vsversion: 2022
- name: Checkout
uses: actions/checkout@v3
if: inputs.build_type == 'Debug'
with:
submodules: 'true'
- name: Build Flatpak (Linux)
if: inputs.build_type == 'Debug'
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
with:
bundle: "Prism Launcher.flatpak"
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
nix:
runs-on: ubuntu-latest
strategy:
matrix:
package:
- prismlauncher
- prismlauncher-qt5
steps:
- name: Clone repository
if: inputs.build_type == 'Debug'
uses: actions/checkout@v3
- name: Set short version
shell: bash
run: |
ver_short=`git rev-parse --short HEAD`
echo "VERSION=$ver_short" >> $GITHUB_ENV
- name: Set Appx version
shell: pwsh
run: |
$runNumber = "${{ github.run_number }}"
if ($runNumber.Length -gt 3) {
$version = $runNumber.Substring(($runNumber.Length - 3))
} else {
$version = $runNumber
}
"APPX_VERSION=${{ env.PRISM_VERSION }}.0.$version" >> $env:GITHUB_ENV
- name: Create Bundle Staging Directory
shell: pwsh
run: |
New-Item -Type Directory -Path ${{ env.BUNDLE_STAGING }}
New-Item -Type Directory -Path ${{ env.APPINSTALLER_STAGING }}
- name: Download MSIX (x64)
uses: actions/download-artifact@v2
with:
submodules: 'true'
- name: Install nix
if: inputs.build_type == 'Debug'
uses: cachix/install-nix-action@v22
name: PrismLauncher-Windows-MSVC-MSIX-${{ env.VERSION }}-${{ inputs.build_type }}
path: ${{ env.BUNDLE_STAGING }}/
- name: Download MSIX (arm64)
uses: actions/download-artifact@v2
with:
install_url: https://nixos.org/nix/install
extra_nix_config: |
auto-optimise-store = true
experimental-features = nix-command flakes
- uses: cachix/cachix-action@v12
if: inputs.build_type == 'Debug'
name: PrismLauncher-Windows-MSVC-arm64-MSIX-${{ env.VERSION }}-${{ inputs.build_type }}
path: ${{ env.BUNDLE_STAGING }}/
- name: Create MSIXBundle
shell: pwsh
run: |
makeappx.exe bundle /bv ${{ env.APPX_VERSION }} /d ${{ env.BUNDLE_STAGING }} /p ${{ env.BUNDLE_STAGING }}\prismlauncher-${{ env.APPX_VERSION }}.msixbundle
- name: Update AppInstaller File
shell: pwsh
run: |
(Get-Content ${{ github.workspace }}\program_info\prismlauncher.AppInstaller) `
-replace "PRISM_VERSION_REPLACEME","${{ env.APPX_VERSION }}" `
> ${{ env.APPINSTALLER_STAGING }}\prismlauncher-${{ env.APPX_VERSION }}.appInstaller
- name: Fetch codesign certificate
shell: bash
run: |
echo '${{ secrets.WINDOWS_CODESIGN_CERT }}' | base64 --decode > codesign.pfx
- name: Sign MSIXBundle
shell: pwsh
run: |
if (Get-Content ./codesign.pfx) {
SignTool sign /fd sha256 /td sha256 /f codesign.pfx `
/p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' `
/tr http://timestamp.digicert.com `
${{ env.BUNDLE_STAGING }}\prismlauncher-${{ env.APPX_VERSION }}.msixbundle
} else {
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
}
- name: Upload MSIXBundle
uses: actions/upload-artifact@v2
with:
name: prismlauncher
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- name: Build
if: inputs.build_type == 'Debug'
run: nix build .#${{ matrix.package }} --print-build-logs
name: PrismLauncher-Windows-MSVC-Universal-MSIXBundle-${{ env.VERSION }}-${{ inputs.build_type }}
path: ${{ env.BUNDLE_STAGING }}/prismlauncher-${{ env.APPX_VERSION }}.msixbundle
- name: Upload AppInstaller
uses: actions/upload-artifact@v2
with:
name: PrismLauncher-Windows-MSVC-Universal-AppInstaller-${{ env.VERSION }}-${{ inputs.build_type }}
path: ${{ env.APPINSTALLER_STAGING }}/prismlauncher-${{ env.APPX_VERSION }}.AppInstaller
#flatpak:
# runs-on: ubuntu-latest
# container:
# image: bilelmoussaoui/flatpak-github-actions:kde-5.15-22.08
# options: --privileged
# steps:
# - name: Checkout
# uses: actions/checkout@v3
# if: inputs.build_type == 'Debug'
# with:
# submodules: 'true'
# - name: Build Flatpak (Linux)
# if: inputs.build_type == 'Debug'
# uses: flatpak/flatpak-github-actions/flatpak-builder@v6
# with:
# bundle: "Prism Launcher.flatpak"
# manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
#nix:
# runs-on: ubuntu-latest
# strategy:
# matrix:
# package:
# - prismlauncher
# - prismlauncher-qt5
# steps:
# - name: Clone repository
# if: inputs.build_type == 'Debug'
# uses: actions/checkout@v3
# with:
# submodules: 'true'
# - name: Install nix
# if: inputs.build_type == 'Debug'
# uses: cachix/install-nix-action@v22
# with:
# install_url: https://nixos.org/nix/install
# extra_nix_config: |
# auto-optimise-store = true
# experimental-features = nix-command flakes
# - uses: cachix/cachix-action@v12
# if: inputs.build_type == 'Debug'
# with:
# name: prismlauncher
# authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
# - name: Build
# if: inputs.build_type == 'Debug'
# run: nix build .#${{ matrix.package }} --print-build-logs

View File

@@ -55,11 +55,17 @@ jobs:
ARM64="$(echo -n ${d} | grep -o arm64 || true)"
INST="$(echo -n ${d} | grep -o Setup || true)"
PORT="$(echo -n ${d} | grep -o Portable || true)"
MSIX="$(echo -n ${d} | grep -o 'msix$' || true)"
MSIXBUNDLE="$(echo -n ${d} | grep -o 'msixbundle$' || true)"
APPINSTALLER="$(echo -n ${d} | grep -o AppInstaller || true)"
NAME="PrismLauncher-Windows-MSVC"
test -z "${LEGACY}" || NAME="${NAME}-Legacy"
test -z "${ARM64}" || NAME="${NAME}-arm64"
test -z "${PORT}" || NAME="${NAME}-Portable"
test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
test -z "${MSIX}" || mv prismlauncher-*.msix ../${NAME}-${{ env.VERSION }}.msix
test -z "${MSIXBUNDLE}" || mv prismlauncher-*.msixbundle ../${NAME}-${{ env.VERSION }}.msixbundle
test -z "${APPINSTALLER}" || mv prismlauncher-*.AppInstaller ../${NAME}-${{ env.VERSION }}.AppInstaller
test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
cd ..
done
@@ -99,10 +105,13 @@ jobs:
PrismLauncher-Windows-MSVC-Legacy-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-arm64-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-arm64-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.msix
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.msixbundle
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.msix
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.msixbundle
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.AppInstaller
PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
PrismLauncher-${{ env.VERSION }}.tar.gz

View File

@@ -7,12 +7,10 @@ on:
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
update-flake:
if: github.repository == 'PrismLauncher/PrismLauncher'
runs-on: ubuntu-latest
steps:

View File

@@ -11,5 +11,5 @@ jobs:
with:
identifier: PrismLauncher.PrismLauncher
version: ${{ github.event.release.tag_name }}
installers-regex: 'PrismLauncher-Windows-MSVC(:?-arm64|-Legacy)?-Setup-.+\.exe$'
installers-regex: 'PrismLauncher-Windows-MSVC(-arm64|-Legacy-Setup)?-\d.+\.(exe|msix)?$'
token: ${{ secrets.WINGET_TOKEN }}

4
.gitignore vendored
View File

@@ -56,3 +56,7 @@ flatbuild
# Snap
*.snap
# msix
bundle_staging
microsoft.system.package.metadata

3
.gitmodules vendored
View File

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

View File

@@ -85,38 +85,6 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0")
# set CXXFLAGS for build targets
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" on)
# If this is a Debug build turn on address sanitiser
if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND DEBUG_ADDRESS_SANITIZER)
message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
# using clang with clang-cl front end
message(STATUS "Address Sanitizer available on Clang MSVC frontend")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-")
else()
# AppleClang and Clang
message(STATUS "Address Sanitizer available on Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
endif()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# GCC
message(STATUS "Address Sanitizer available on GCC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
link_libraries("asan")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
message(STATUS "Address Sanitizer available on MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-")
else()
message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
endif()
endif()
option(ENABLE_LTO "Enable Link Time Optimization" off)
if(ENABLE_LTO)
@@ -170,15 +138,15 @@ set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL th
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
######## Set version numbers ########
set(Launcher_VERSION_MAJOR 7)
set(Launcher_VERSION_MINOR 2)
set(Launcher_VERSION_MAJOR 8)
set(Launcher_VERSION_MINOR 0)
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_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0")
# Build platform.
set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
# Channel list URL
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
@@ -364,7 +332,7 @@ elseif(UNIX)
set(BINARY_DEST_DIR "bin")
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
set(JARS_DEST_DIR "share/${Launcher_Name}")
set(JARS_DEST_DIR "share/${Launcher_APP_BINARY_NAME}")
# install as bundle with no dependencies included
set(INSTALL_BUNDLE "nodeps")
@@ -377,7 +345,7 @@ elseif(UNIX)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps")
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}")
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "${KDE_INSTALL_DATADIR}/${Launcher_Name}")
if(Launcher_ManPage)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")

View File

@@ -1,3 +1,5 @@
test test
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="/program_info/org.prismlauncher.PrismLauncher.logo-darkmode.svg">

View File

@@ -65,7 +65,7 @@ Config::Config()
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
if (BUILD_PLATFORM == "macOS" && !MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
{
UPDATER_ENABLED = true;
}

View File

@@ -68,7 +68,7 @@ class Config {
bool UPDATER_ENABLED = false;
/// A short string identifying this build's platform or distribution.
/// A short string identifying this build's platform. For example, "lin64" or "win32".
QString BUILD_PLATFORM;
/// A string containing the build timestamp

18
flake.lock generated
View File

@@ -21,11 +21,11 @@
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1688466019,
"narHash": "sha256-VeM2akYrBYMsb4W/MmBo1zmaMfgbL4cH3Pu8PGyIwJ0=",
"lastModified": 1688254665,
"narHash": "sha256-8FHEgBrr7gYNiS/NzCxIO3m4hvtLRW9YY1nYo1ivm3o=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "8e8d955c22df93dbe24f19ea04f47a74adbdc5ec",
"rev": "267149c58a14d15f7f81b4d737308421de9d7152",
"type": "github"
},
"original": {
@@ -91,11 +91,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1689413807,
"narHash": "sha256-exuzOvOhGAEKWQKwDuZAL4N8a1I837hH5eocaTcIbLc=",
"lastModified": 1688221086,
"narHash": "sha256-cdW6qUL71cNWhHCpMPOJjlw0wzSRP0pVlRn2vqX/VVg=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "46ed466081b9cad1125b11f11a2af5cc40b942c7",
"rev": "cd99c2b3c9f160cd004318e0697f90bbd5960825",
"type": "github"
},
"original": {
@@ -138,11 +138,11 @@
]
},
"locked": {
"lastModified": 1689328505,
"narHash": "sha256-9B3+OeUn1a/CvzE3GW6nWNwS5J7PDHTyHGlpL3wV5oA=",
"lastModified": 1688386108,
"narHash": "sha256-Vffto9QaVonzYAcPlAzd0soqWYpPpKk60dfNLSIXcFA=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "5e28316db471d1ac234beb70031b635437421dd6",
"rev": "42587d3414d1747999a5f71e92a83cf6547b62da",
"type": "github"
},
"original": {

View File

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

View File

@@ -5,6 +5,13 @@ sdk: org.kde.Sdk
sdk-extensions:
- org.freedesktop.Sdk.Extension.openjdk17
- org.freedesktop.Sdk.Extension.openjdk8
add-extensions:
com.valvesoftware.Steam.Utility.gamescope:
version: stable
add-ld-path: lib
no-autodownload: true
autodelete: false
directory: utils/gamescope
command: prismlauncher
finish-args:
@@ -19,31 +26,21 @@ finish-args:
# Mod drag&drop
- --filesystem=xdg-download:ro
cleanup:
- /lib/libGLU*
modules:
# Might be needed by some Controller mods (see https://github.com/isXander/Controlify/issues/31)
- shared-modules/libusb/libusb.json
# Needed for proper Wayland support
- libdecor.json
- name: prismlauncher
buildsystem: cmake-ninja
builddir: true
config-opts:
- -DLauncher_BUILD_PLATFORM=flatpak
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
- -DCMAKE_BUILD_TYPE=Debug
- -DLauncher_QT_VERSION_MAJOR=5
build-options:
env:
JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17
JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac
sources:
- type: dir
path: ../
- type: dir
path: ../
builddir: true
- name: openjdk
buildsystem: simple
build-commands:
@@ -52,45 +49,14 @@ modules:
- mv /app/jre /app/jdk/17
- /usr/lib/sdk/openjdk8/install.sh
- mv /app/jre /app/jdk/8
cleanup:
- /jre
- name: glfw
buildsystem: cmake-ninja
config-opts:
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
- -DBUILD_SHARED_LIBS:BOOL=ON
- -DGLFW_USE_WAYLAND=ON
sources:
- type: git
url: https://github.com/glfw/glfw.git
commit: 3fa2360720eeba1964df3c0ecf4b5df8648a8e52
- type: patch
path: patches/0003-Don-t-crash-on-calls-to-focus-or-icon.patch
- type: patch
path: patches/0005-Add-warning-about-being-an-unofficial-patch.patch
- type: patch
path: patches/0007-Platform-Prefer-Wayland-over-X11.patch
cleanup:
- /include
- /lib/cmake
- /lib/pkgconfig
cleanup: [/jre]
- name: xrandr
buildsystem: autotools
sources:
- type: archive
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.2.tar.xz
sha256: c8bee4790d9058bacc4b6246456c58021db58a87ddda1a9d0139bf5f18f1f240
x-checker-data:
type: anitya
project-id: 14957
stable-only: true
url-template: https://xorg.freedesktop.org/archive/individual/app/xrandr-$version.tar.xz
cleanup:
- /share/man
- /bin/xkeystone
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.1.tar.xz
sha256: 7bc76daf9d72f8aff885efad04ce06b90488a1a169d118dea8a2b661832e8762
cleanup: [/share/man, /bin/xkeystone]
- name: gamemode
buildsystem: meson
config-opts:
@@ -101,56 +67,19 @@ modules:
# post-install is running inside the build dir, we need it from the source though
- install -Dm755 ../data/gamemoderun -t /app/bin
sources:
- type: archive
archive-type: tar-gzip
url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.7
sha256: 57ce73ba605d1cf12f8d13725006a895182308d93eba0f69f285648449641803
x-checker-data:
type: json
url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest
version-query: .tag_name
url-query: .tarball_url
timestamp-query: .published_at
cleanup:
- /include
- /lib/pkgconfig
- /lib/libgamemodeauto.a
- name: glxinfo
buildsystem: meson
config-opts:
- --bindir=/app/mesa-demos
- -Degl=disabled
- -Dglut=disabled
- -Dosmesa=disabled
- -Dvulkan=disabled
- -Dwayland=disabled
post-install:
- mv -v /app/mesa-demos/glxinfo /app/bin
sources:
- type: archive
url: https://archive.mesa3d.org/demos/mesa-demos-9.0.0.tar.xz
sha256: 3046a3d26a7b051af7ebdd257a5f23bfeb160cad6ed952329cdff1e9f1ed496b
x-checker-data:
type: anitya
project-id: 16781
stable-only: true
url-template: https://archive.mesa3d.org/demos/mesa-demos-$version.tar.xz
cleanup:
- /include
- /mesa-demos
- /share
modules:
- shared-modules/glu/glu-9.json
- type: git
url: https://github.com/FeralInteractive/gamemode
tag: "1.7"
commit: 4dc99dff76218718763a6b07fc1900fa6d1dafd9
- name: enhance
buildsystem: simple
build-commands:
- mkdir -p /app/utils/gamescope
- install -Dm755 prime-run /app/bin/prime-run
- mv /app/bin/prismlauncher /app/bin/prismrun
- install -Dm755 prismlauncher /app/bin/prismlauncher
sources:
- type: file
path: prime-run
path: ../flatpak/prime-run
- type: file
path: prismlauncher
path: ../flatpak/prismlauncher

View File

@@ -1,24 +0,0 @@
diff --git a/src/wl_window.c b/src/wl_window.c
index 52d3b9eb..4ac4eb5d 100644
--- a/src/wl_window.c
+++ b/src/wl_window.c
@@ -2117,8 +2117,7 @@ void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title)
void _glfwSetWindowIconWayland(_GLFWwindow* window,
int count, const GLFWimage* images)
{
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
- "Wayland: The platform does not support setting the window icon");
+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the window icon\n");
}
void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos)
@@ -2361,8 +2360,7 @@ void _glfwRequestWindowAttentionWayland(_GLFWwindow* window)
void _glfwFocusWindowWayland(_GLFWwindow* window)
{
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
- "Wayland: The platform does not support setting the input focus");
+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the input focus\n");
}
void _glfwSetWindowMonitorWayland(_GLFWwindow* window,

View File

@@ -1,17 +0,0 @@
diff --git a/src/init.c b/src/init.c
index 06dbb3f2..a7c6da86 100644
--- a/src/init.c
+++ b/src/init.c
@@ -449,6 +449,12 @@ GLFWAPI int glfwInit(void)
_glfw.initialized = GLFW_TRUE;
glfwDefaultWindowHints();
+
+ fprintf(stderr, "!!! Patched GLFW from https://github.com/Admicos/minecraft-wayland\n"
+ "!!! If any issues with the window, or some issues with rendering, occur, "
+ "first try with the built-in GLFW, and if that solves the issue, report there first.\n"
+ "!!! Use outside Minecraft is untested, and things might break.\n");
+
return GLFW_TRUE;
}

View File

@@ -1,20 +0,0 @@
diff --git a/src/platform.c b/src/platform.c
index c5966ae7..3e7442f9 100644
--- a/src/platform.c
+++ b/src/platform.c
@@ -49,12 +49,12 @@ static const struct
#if defined(_GLFW_COCOA)
{ GLFW_PLATFORM_COCOA, _glfwConnectCocoa },
#endif
-#if defined(_GLFW_X11)
- { GLFW_PLATFORM_X11, _glfwConnectX11 },
-#endif
#if defined(_GLFW_WAYLAND)
{ GLFW_PLATFORM_WAYLAND, _glfwConnectWayland },
#endif
+#if defined(_GLFW_X11)
+ { GLFW_PLATFORM_X11, _glfwConnectX11 },
+#endif
};
GLFWbool _glfwSelectPlatform(int desiredID, _GLFWplatform* platform)

View File

@@ -1,40 +0,0 @@
diff --git a/src/libdecor.c b/src/libdecor.c
index a9c1106..1aa38b3 100644
--- a/src/libdecor.c
+++ b/src/libdecor.c
@@ -1391,22 +1391,32 @@ calculate_priority(const struct libdecor_plugin_description *plugin_description)
static bool
check_symbol_conflicts(const struct libdecor_plugin_description *plugin_description)
{
+ bool ret = true;
char * const *symbol;
+ void* main_prog = dlopen(NULL, RTLD_LAZY);
+ if (!main_prog) {
+ fprintf(stderr, "Plugin \"%s\" couldn't check conflicting symbols: \"%s\".\n",
+ plugin_description->description, dlerror());
+ return false;
+ }
+
symbol = plugin_description->conflicting_symbols;
while (*symbol) {
dlerror();
- dlsym (RTLD_DEFAULT, *symbol);
+ dlsym (main_prog, *symbol);
if (!dlerror()) {
fprintf(stderr, "Plugin \"%s\" uses conflicting symbol \"%s\".\n",
plugin_description->description, *symbol);
- return false;
+ ret = false;
+ break;
}
symbol++;
}
- return true;
+ dlclose(main_prog);
+ return ret;
}
static struct plugin_loader *

View File

@@ -5,7 +5,7 @@ for i in {0..9}; do
test -S "$XDG_RUNTIME_DIR"/discord-ipc-"$i" || ln -sf {app/com.discordapp.Discord,"$XDG_RUNTIME_DIR"}/discord-ipc-"$i";
done
export PATH="${PATH}${PATH:+:}/usr/lib/extensions/vulkan/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin"
export VK_LAYER_PATH="/usr/lib/extensions/vulkan/share/vulkan/implicit_layer.d/"
export PATH="${PATH}${PATH:+:}/app/utils/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin"
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}${LD_LIBRARY_PATH:+:}/usr/lib/extensions/vulkan/MangoHud/\$LIB/"
exec /app/bin/prismrun "$@"

View File

@@ -9,7 +9,6 @@
* Copyright (C) 2022 Tayou <tayou@gmx.net>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
* Copyright (C) 2023 seth <getchoo at tuta dot io>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -434,11 +433,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
}
// seach root path
if(!foundLoggingRules) {
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
logRulesPath = FS::PathCombine(m_rootPath, "share", BuildConfig.LAUNCHER_NAME, logRulesFile);
#else
logRulesPath = FS::PathCombine(m_rootPath, logRulesFile);
#endif
qDebug() << "Testing" << logRulesPath << "...";
foundLoggingRules = QFile::exists(logRulesPath);
}
@@ -476,7 +471,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT;
qDebug() << "Version : " << BuildConfig.printableVersionString();
qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM;
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC;
if (adjustedBy.size())
@@ -574,7 +568,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
// Language
m_settings->registerSetting("Language", QString());
m_settings->registerSetting("UseSystemLocale", false);
// Console
m_settings->registerSetting("ShowConsole", false);
@@ -611,9 +604,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->registerSetting("IgnoreJavaCompatibility", false);
m_settings->registerSetting("IgnoreJavaWizard", false);
// Mod loader settings
m_settings->registerSetting("DisableQuiltBeacon", false);
// Native library workarounds
m_settings->registerSetting("UseNativeOpenAL", false);
m_settings->registerSetting("UseNativeGLFW", false);
@@ -697,16 +687,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->reset("PastebinCustomAPIBase");
}
}
{
// Meta URL
m_settings->registerSetting("MetaURLOverride", "");
QUrl metaUrl(m_settings->get("MetaURLOverride").toString());
// get rid of invalid meta urls
if (!metaUrl.isValid() || metaUrl.scheme() != "http" || metaUrl.scheme() != "https")
m_settings->reset("MetaURLOverride");
}
// meta URL
m_settings->registerSetting("MetaURLOverride", "");
m_settings->registerSetting("CloseAfterLaunch", false);
m_settings->registerSetting("QuitAfterGameStop", false);
@@ -928,7 +910,12 @@ bool Application::createSetupWizard()
}
return false;
}();
bool languageRequired = settings()->get("Language").toString().isEmpty();
bool languageRequired = [&]()
{
if (settings()->get("Language").toString().isEmpty())
return true;
return false;
}();
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
bool themeInterventionRequired = settings()->get("ApplicationTheme") == "";
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
@@ -1572,7 +1559,7 @@ QString Application::getJarPath(QString jarFile)
{
QStringList potentialPaths = {
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
FS::PathCombine(m_rootPath, "share", BuildConfig.LAUNCHER_NAME),
FS::PathCombine(m_rootPath, "share/" + BuildConfig.LAUNCHER_APP_BINARY_NAME),
#endif
FS::PathCombine(m_rootPath, "jars"),
FS::PathCombine(applicationDirPath(), "jars"),

View File

@@ -15,15 +15,16 @@
#pragma once
#include <QMetaType>
#include <QString>
#include <memory>
#include <QString>
#include <QMetaType>
/*!
* An abstract base class for versions.
*/
class BaseVersion {
public:
class BaseVersion
{
public:
using Ptr = std::shared_ptr<BaseVersion>;
virtual ~BaseVersion() {}
/*!
@@ -44,8 +45,14 @@ class BaseVersion {
*/
virtual QString typeString() const = 0;
virtual bool operator<(BaseVersion& a) { return name() < a.name(); };
virtual bool operator>(BaseVersion& a) { return name() > a.name(); };
virtual bool operator<(BaseVersion &a)
{
return name() < a.name();
};
virtual bool operator>(BaseVersion &a)
{
return name() > a.name();
};
};
Q_DECLARE_METATYPE(BaseVersion::Ptr)

View File

@@ -362,6 +362,8 @@ set(MINECRAFT_SOURCES
minecraft/mod/tasks/LocalWorldSaveParseTask.cpp
minecraft/mod/tasks/LocalResourceParse.h
minecraft/mod/tasks/LocalResourceParse.cpp
minecraft/mod/tasks/GetModDependenciesTask.h
minecraft/mod/tasks/GetModDependenciesTask.cpp
# Assets
minecraft/AssetsUtils.h

View File

@@ -187,8 +187,8 @@ void LaunchController::login() {
switch(m_accountToUse->accountState()) {
case AccountState::Offline: {
m_session->wants_online = false;
// NOTE: fallthrough is intentional
}
/* fallthrough */
case AccountState::Online: {
if(!m_session->wants_online) {
// we ask the user for a player name
@@ -267,8 +267,8 @@ void LaunchController::login() {
// This means some sort of soft error that we can fix with a refresh ... so let's refresh.
case AccountState::Unchecked: {
m_accountToUse->refresh();
// NOTE: fallthrough intentional
}
/* fallthrough */
case AccountState::Working: {
// refresh is in progress, we need to wait for it to finish to proceed.
ProgressDialog progDialog(m_parentWidget);
@@ -390,10 +390,7 @@ void LaunchController::launchInstance()
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
// Prepend Version
{
auto versionString = QString("%1 version: %2 (%3)").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString(), BuildConfig.BUILD_PLATFORM);
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), versionString + "\n\n", MessageLevel::Launcher));
}
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), BuildConfig.LAUNCHER_DISPLAYNAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher));
m_launcher->start();
}

View File

@@ -3,6 +3,8 @@
#include <QCoreApplication>
#include <QPixmapCache>
#include <QThread>
#include <QTime>
#include <QDebug>
#define GET_TYPE() \
Qt::ConnectionType type; \
@@ -60,6 +62,8 @@ class PixmapCache final : public QObject {
DEFINE_FUNC_ONE_PARAM(remove, bool, const QPixmapCache::Key&)
DEFINE_FUNC_TWO_PARAM(replace, bool, const QPixmapCache::Key&, const QPixmap&)
DEFINE_FUNC_ONE_PARAM(setCacheLimit, bool, int)
DEFINE_FUNC_NO_PARAM(markCacheMissByEviciton, bool)
DEFINE_FUNC_ONE_PARAM(setFastEvictionThreshold, bool, int)
// NOTE: Every function returns something non-void to simplify the macros.
private slots:
@@ -90,6 +94,43 @@ class PixmapCache final : public QObject {
return true;
}
/**
* Mark that a cache miss occurred because of a eviction if too many of these occur too fast the cache size is increased
* @return if the cache size was increased
*/
bool _markCacheMissByEviciton()
{
auto now = QTime::currentTime();
if (!m_last_cache_miss_by_eviciton.isNull()) {
auto diff = m_last_cache_miss_by_eviciton.msecsTo(now);
if (diff < 1000) { // less than a second ago
++m_consecutive_fast_evicitons;
} else {
m_consecutive_fast_evicitons = 0;
}
}
m_last_cache_miss_by_eviciton = now;
if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) {
// double the cache size
auto newSize = _cacheLimit() * 2;
qDebug() << m_consecutive_fast_evicitons << "pixmap cache misses by eviction happened too fast, doubling cache size to"
<< newSize;
_setCacheLimit(newSize);
m_consecutive_fast_evicitons = 0;
return true;
}
return false;
}
bool _setFastEvictionThreshold(int threshold)
{
m_consecutive_fast_evicitons_threshold = threshold;
return true;
}
private:
static PixmapCache* s_instance;
QTime m_last_cache_miss_by_eviciton;
int m_consecutive_fast_evicitons = 0;
int m_consecutive_fast_evicitons_threshold = 15;
};

View File

@@ -38,6 +38,8 @@ class ResourceDownloadTask : public SequentialTask {
const QString& getFilename() const { return m_pack_version.fileName; }
const QString& getCustomPath() const { return m_custom_target_folder; }
const QVariant& getVersionID() const { return m_pack_version.fileId; }
const ModPlatform::IndexedVersion& getVersion() const { return m_pack_version; }
const ModPlatform::ResourceProvider& getProvider() const { return m_pack->provider; }
const QString& getName() const { return m_pack->name; }
ModPlatform::IndexedPack::Ptr getPack() { return m_pack; }

View File

@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2023 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
@@ -54,9 +55,14 @@ public:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
const auto &filters = m_parent->filters();
const QString &search = m_parent->search();
const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
if (!search.isEmpty() && !sourceModel()->data(idx, BaseVersionList::VersionRole).toString().contains(search, Qt::CaseInsensitive))
return false;
for (auto it = filters.begin(); it != filters.end(); ++it)
{
auto idx = sourceModel()->index(source_row, 0, source_parent);
auto data = sourceModel()->data(idx, it.key());
auto match = data.toString();
if(!it.value()->accepts(match))
@@ -187,24 +193,31 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
}
case Qt::ToolTipRole:
{
if(column == Name && hasRecommended)
switch(column)
{
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
if(value.toBool())
case Name:
{
return tr("Recommended");
} else if(hasLatest) {
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
if(value.toBool())
if(hasRecommended)
{
return tr("Latest");
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
if(value.toBool())
{
return tr("Recommended");
}
else if(hasLatest)
{
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
if(value.toBool())
{
return tr("Latest");
}
}
}
} else if(index.row() == 0)
{
return tr("Latest");
}
} else {
return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole);
default:
{
return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole);
}
}
}
case Qt::DecorationRole:
@@ -228,10 +241,6 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
return APPLICATION->getThemedIcon("bug");
}
}
else if(index.row() == 0)
{
return APPLICATION->getThemedIcon("bug");
}
QPixmap pixmap;
QPixmapCache::find("placeholder", &pixmap);
if(!pixmap)
@@ -420,6 +429,7 @@ QModelIndex VersionProxyModel::getVersion(const QString& version) const
void VersionProxyModel::clearFilters()
{
m_filters.clear();
m_search.clear();
filterModel->filterChanged();
}
@@ -429,11 +439,21 @@ void VersionProxyModel::setFilter(const BaseVersionList::ModelRoles column, Filt
filterModel->filterChanged();
}
void VersionProxyModel::setSearch(const QString &search) {
m_search = search;
filterModel->filterChanged();
}
const VersionProxyModel::FilterMap &VersionProxyModel::filters() const
{
return m_filters;
}
const QString &VersionProxyModel::search() const
{
return m_search;
}
void VersionProxyModel::sourceAboutToBeReset()
{
beginResetModel();

View File

@@ -38,7 +38,9 @@ public:
virtual void setSourceModel(QAbstractItemModel *sourceModel) override;
const FilterMap &filters() const;
const QString &search() const;
void setFilter(const BaseVersionList::ModelRoles column, Filter * filter);
void setSearch(const QString &search);
void clearFilters();
QModelIndex getRecommended() const;
QModelIndex getVersion(const QString & version) const;
@@ -59,6 +61,7 @@ private slots:
private:
QList<Column> m_columns;
FilterMap m_filters;
QString m_search;
BaseVersionList::RoleList roles;
VersionFilterModel * filterModel;
bool hasRecommended = false;

View File

@@ -1,64 +1,29 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "JavaInstall.h"
#include "BaseVersion.h"
#include "StringUtils.h"
bool JavaInstall::operator<(const JavaInstall& rhs)
bool JavaInstall::operator<(const JavaInstall &rhs)
{
auto archCompare = StringUtils::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive);
if (archCompare != 0)
if(archCompare != 0)
return archCompare < 0;
if (id < rhs.id) {
if(id < rhs.id)
{
return true;
}
if (id > rhs.id) {
if(id > rhs.id)
{
return false;
}
return StringUtils::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0;
}
bool JavaInstall::operator==(const JavaInstall& rhs)
bool JavaInstall::operator==(const JavaInstall &rhs)
{
return arch == rhs.arch && id == rhs.id && path == rhs.path;
}
bool JavaInstall::operator>(const JavaInstall& rhs)
bool JavaInstall::operator>(const JavaInstall &rhs)
{
return (!operator<(rhs)) && (!operator==(rhs));
}
bool JavaInstall::operator<(BaseVersion& a)
{
try {
return operator<(dynamic_cast<JavaInstall&>(a));
} catch (const std::bad_cast& e) {
return BaseVersion::operator<(a);
}
}
bool JavaInstall::operator>(BaseVersion& a)
{
try {
return operator>(dynamic_cast<JavaInstall&>(a));
} catch (const std::bad_cast& e) {
return BaseVersion::operator>(a);
}
}

View File

@@ -1,40 +1,33 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "BaseVersion.h"
#include "JavaVersion.h"
struct JavaInstall : public BaseVersion {
JavaInstall() {}
JavaInstall(QString id, QString arch, QString path) : id(id), arch(arch), path(path) {}
virtual QString descriptor() { return id.toString(); }
struct JavaInstall : public BaseVersion
{
JavaInstall(){}
JavaInstall(QString id, QString arch, QString path)
: id(id), arch(arch), path(path)
{
}
virtual QString descriptor()
{
return id.toString();
}
virtual QString name() { return id.toString(); }
virtual QString name()
{
return id.toString();
}
virtual QString typeString() const { return arch; }
virtual QString typeString() const
{
return arch;
}
virtual bool operator<(BaseVersion& a) override;
virtual bool operator>(BaseVersion& a) override;
bool operator<(const JavaInstall& rhs);
bool operator==(const JavaInstall& rhs);
bool operator>(const JavaInstall& rhs);
bool operator<(const JavaInstall & rhs);
bool operator==(const JavaInstall & rhs);
bool operator>(const JavaInstall & rhs);
JavaVersion id;
QString arch;

View File

@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2023 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
@@ -98,6 +99,8 @@ QVariant JavaInstallList::data(const QModelIndex &index, int role) const
auto version = std::dynamic_pointer_cast<JavaInstall>(m_vlist[index.row()]);
switch (role)
{
case SortRole:
return -index.row();
case VersionPointerRole:
return QVariant::fromValue(m_vlist[index.row()]);
case VersionIdRole:

View File

@@ -45,10 +45,10 @@ QVariant Index::data(const QModelIndex &index, int role) const
switch (role)
{
case Qt::DisplayRole:
if (index.column() == 0) {
return list->humanReadable();
} else {
break;
switch (index.column())
{
case 0: return list->humanReadable();
default: break;
}
case UidRole: return list->uid();
case NameRole: return list->name();

View File

@@ -4,7 +4,6 @@
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
* Copyright (c) 2023 seth <getchoo at tuta dot io>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -187,10 +186,6 @@ void MinecraftInstance::loadSpecificSettings()
m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride);
m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride);
// Mod loader specific options
auto modLoaderSettings = m_settings->registerSetting("OverrideModLoaderSettings", false);
m_settings->registerOverride(global_settings->getSetting("DisableQuiltBeacon"), modLoaderSettings);
m_settings->set("InstanceType", "OneSix");
}
@@ -396,12 +391,6 @@ QStringList MinecraftInstance::extraArguments()
agent->library()->getApplicableFiles(runtimeContext(), jar, temp1, temp2, temp3, getLocalLibraryPath());
list.append("-javaagent:"+jar[0]+(agent->argument().isEmpty() ? "" : "="+agent->argument()));
}
{
const auto loaders = version->getModLoaders();
if (loaders.has_value() && loaders.value() & ResourceAPI::Quilt && settings()->get("DisableQuiltBeacon").toBool())
list.append("-Dloader.disable_beacon=true");
}
return list;
}
@@ -843,7 +832,7 @@ QMap<QString, QString> MinecraftInstance::createCensorFilterFromSession(AuthSess
{
addToFilter(sessionRef.session, tr("<SESSION ID>"));
}
if (sessionRef.access_token != "0") {
if (sessionRef.access_token != "offline") {
addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>"));
}
if(sessionRef.client_token.size()) {

View File

@@ -65,8 +65,7 @@
static const QMap<QString, ResourceAPI::ModLoaderType> modloaderMapping{
{"net.minecraftforge", ResourceAPI::Forge},
{"net.fabricmc.fabric-loader", ResourceAPI::Fabric},
{"org.quiltmc.quilt-loader", ResourceAPI::Quilt},
{"com.mumfrey.liteloader", ResourceAPI::LiteLoader}
{"org.quiltmc.quilt-loader", ResourceAPI::Quilt}
};
PackProfile::PackProfile(MinecraftInstance * instance)

View File

@@ -374,10 +374,6 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
}
yggdrasilToken = tokenFromJSONV3(data, "ygg");
// versions before 7.2 used "offline" as the offline token
if (yggdrasilToken.token == "offline")
yggdrasilToken.token = "0";
minecraftProfile = profileFromJSONV3(data, "profile");
if(!entitlementFromJSONV3(data, minecraftEntitlement)) {
if(minecraftProfile.validity != Katabasis::Validity::None) {

View File

@@ -328,9 +328,6 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
case AccountState::Gone: {
return tr("Gone", "Account status");
}
default: {
return tr("Unknown", "Account status");
}
}
}
@@ -357,12 +354,11 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
return QVariant::fromValue(account);
case Qt::CheckStateRole:
if (index.column() == ProfileNameColumn) {
return account == m_defaultAccount ? Qt::Checked : Qt::Unchecked;
} else {
return QVariant();
switch (index.column())
{
case ProfileNameColumn:
return account == m_defaultAccount ? Qt::Checked : Qt::Unchecked;
}
default:
return QVariant();

View File

@@ -26,7 +26,6 @@ bool AuthSession::MakeOffline(QString offline_playername)
return false;
}
session = "-";
access_token = "0";
player_name = offline_playername;
status = PlayableOffline;
return true;

View File

@@ -93,7 +93,7 @@ MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username)
{
auto account = makeShared<MinecraftAccount>();
account->data.type = AccountType::Offline;
account->data.yggdrasilToken.token = "0";
account->data.yggdrasilToken.token = "offline";
account->data.yggdrasilToken.validity = Katabasis::Validity::Certain;
account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc();
account->data.yggdrasilToken.extra["userName"] = username;

View File

@@ -273,7 +273,6 @@ void Yggdrasil::processReply() {
AccountTaskState::STATE_FAILED_GONE,
tr("The Mojang account no longer exists. It may have been migrated to a Microsoft account.")
);
return;
}
default:
changeState(

View File

@@ -74,7 +74,6 @@ std::pair<int, bool> DataPack::compare(const Resource& other, SortType type) con
auto res = Resource::compare(other, type);
if (res.first != 0)
return res;
break;
}
case SortType::PACK_FORMAT: {
auto this_ver = packFormat();
@@ -84,7 +83,6 @@ std::pair<int, bool> DataPack::compare(const Resource& other, SortType type) con
return { 1, type == SortType::PACK_FORMAT };
if (this_ver < other_ver)
return { -1, type == SortType::PACK_FORMAT };
break;
}
}
return { 0, false };

View File

@@ -41,9 +41,11 @@
#include <QString>
#include <QRegularExpression>
#include "MTPixmapCache.h"
#include "MetadataHandler.h"
#include "Version.h"
#include "minecraft/mod/ModDetails.h"
#include "minecraft/mod/tasks/LocalModParseTask.h"
static ModPlatform::ProviderCapabilities ProviderCaps;
@@ -89,7 +91,6 @@ std::pair<int, bool> Mod::compare(const Resource& other, SortType type) const
auto res = Resource::compare(other, type);
if (res.first != 0)
return res;
break;
}
case SortType::VERSION: {
auto this_ver = Version(version());
@@ -98,13 +99,11 @@ std::pair<int, bool> Mod::compare(const Resource& other, SortType type) const
return { 1, type == SortType::VERSION };
if (this_ver < other_ver)
return { -1, type == SortType::VERSION };
break;
}
case SortType::PROVIDER: {
auto compare_result = QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive);
if (compare_result != 0)
return { compare_result, type == SortType::PROVIDER };
break;
}
}
return { 0, false };
@@ -124,7 +123,7 @@ bool Mod::applyFilter(QRegularExpression filter) const
return Resource::applyFilter(filter);
}
auto Mod::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool
auto Mod::destroy(QDir& index_dir, bool preserve_metadata) -> bool
{
if (!preserve_metadata) {
qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name());
@@ -137,7 +136,7 @@ auto Mod::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) -
}
}
return Resource::destroy(attempt_trash);
return Resource::destroy();
}
auto Mod::details() const -> const ModDetails&
@@ -204,7 +203,10 @@ void Mod::finishResolvingWithDetails(ModDetails&& details)
m_local_details = std::move(details);
if (metadata)
setMetadata(std::move(metadata));
};
if (!iconPath().isEmpty()) {
m_pack_image_cache_key.was_read_attempt = false;
}
}
auto Mod::provider() const -> std::optional<QString>
{
@@ -213,6 +215,56 @@ auto Mod::provider() const -> std::optional<QString>
return {};
}
auto Mod::licenses() const -> const QList<ModLicense>&
{
return details().licenses;
}
auto Mod::issueTracker() const -> QString
{
return details().issue_tracker;
}
void Mod::setIcon(QImage new_image) const
{
QMutexLocker locker(&m_data_lock);
Q_ASSERT(!new_image.isNull());
if (m_pack_image_cache_key.key.isValid())
PixmapCache::remove(m_pack_image_cache_key.key);
// scale the image to avoid flooding the pixmapcache
auto pixmap = QPixmap::fromImage(new_image.scaled({64, 64}, Qt::AspectRatioMode::KeepAspectRatioByExpanding));
m_pack_image_cache_key.key = PixmapCache::insert(pixmap);
m_pack_image_cache_key.was_ever_used = true;
m_pack_image_cache_key.was_read_attempt = true;
}
QPixmap Mod::icon(QSize size, Qt::AspectRatioMode mode) const
{
QPixmap cached_image;
if (PixmapCache::find(m_pack_image_cache_key.key, &cached_image)) {
if (size.isNull())
return cached_image;
return cached_image.scaled(size, mode);
}
// No valid image we can get
if ((!m_pack_image_cache_key.was_ever_used && m_pack_image_cache_key.was_read_attempt) || iconPath().isEmpty())
return {};
if (m_pack_image_cache_key.was_ever_used) {
qDebug() << "Mod" << name() << "Had it's icon evicted form the cache. reloading...";
PixmapCache::markCacheMissByEviciton();
}
// Image got evicted from the cache or an attempt to load it has not been made. load it and retry.
m_pack_image_cache_key.was_read_attempt = true;
ModUtils::loadIconFile(*this);
return icon(size);
}
bool Mod::valid() const
{
return !m_local_details.mod_id.isEmpty();

View File

@@ -38,6 +38,10 @@
#include <QDateTime>
#include <QFileInfo>
#include <QList>
#include <QImage>
#include <QMutex>
#include <QPixmap>
#include <QPixmapCache>
#include <optional>
@@ -64,6 +68,15 @@ public:
auto authors() const -> QStringList;
auto status() const -> ModStatus;
auto provider() const -> std::optional<QString>;
auto licenses() const -> const QList<ModLicense>&;
auto issueTracker() const -> QString;
/** Get the intneral path to the mod's icon file*/
QString iconPath() const { return m_local_details.icon_file; };
/** Gets the icon of the mod, converted to a QPixmap for drawing, and scaled to size. */
[[nodiscard]] QPixmap icon(QSize size, Qt::AspectRatioMode mode = Qt::AspectRatioMode::IgnoreAspectRatio) const;
/** Thread-safe. */
void setIcon(QImage new_image) const;
auto metadata() -> std::shared_ptr<Metadata::ModStruct>;
auto metadata() const -> const std::shared_ptr<Metadata::ModStruct>;
@@ -79,10 +92,19 @@ public:
[[nodiscard]] bool applyFilter(QRegularExpression filter) const override;
// Delete all the files of this mod
auto destroy(QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool;
auto destroy(QDir& index_dir, bool preserve_metadata = false) -> bool;
void finishResolvingWithDetails(ModDetails&& details);
protected:
ModDetails m_local_details;
mutable QMutex m_data_lock;
struct {
QPixmapCache::Key key;
bool was_ever_used = false;
bool was_read_attempt = false;
} mutable m_pack_image_cache_key;
};

View File

@@ -39,6 +39,7 @@
#include <QString>
#include <QStringList>
#include <QUrl>
#include "minecraft/mod/MetadataHandler.h"
@@ -49,6 +50,84 @@ enum class ModStatus {
Unknown, // Default status
};
struct ModLicense {
QString name = {};
QString id = {};
QString url = {};
QString description = {};
ModLicense() {}
ModLicense(const QString license) {
// FIXME: come up with a better license parseing.
// handle SPDX identifiers? https://spdx.org/licenses/
auto parts = license.split(' ');
QStringList notNameParts = {};
for (auto part : parts) {
auto url = QUrl(part);
if (part.startsWith("(") && part.endsWith(")"))
url = QUrl(part.mid(1, part.size() - 2));
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
this->url = url.toString();
notNameParts.append(part);
continue;
}
}
for (auto part : notNameParts) {
parts.removeOne(part);
}
auto licensePart = parts.join(' ');
this->name = licensePart;
this->description = licensePart;
if (parts.size() == 1) {
this->id = parts.first();
}
}
ModLicense(const QString name, const QString id, const QString url, const QString description) {
this->name = name;
this->id = id;
this->url = url;
this->description = description;
}
ModLicense(const ModLicense& other)
: name(other.name)
, id(other.id)
, url(other.url)
, description(other.description)
{}
ModLicense& operator=(const ModLicense& other)
{
this->name = other.name;
this->id = other.id;
this->url = other.url;
this->description = other.description;
return *this;
}
ModLicense& operator=(const ModLicense&& other)
{
this->name = other.name;
this->id = other.id;
this->url = other.url;
this->description = other.description;
return *this;
}
bool isEmpty() {
return this->name.isEmpty() && this->id.isEmpty() && this->url.isEmpty() && this->description.isEmpty();
}
};
struct ModDetails
{
/* Mod ID as defined in the ModLoader-specific metadata */
@@ -72,6 +151,15 @@ struct ModDetails
/* List of the author's names */
QStringList authors = {};
/* Issue Tracker URL */
QString issue_tracker = {};
/* License */
QList<ModLicense> licenses = {};
/* Path of mod logo */
QString icon_file = {};
/* Installation status of the mod */
ModStatus status = ModStatus::Unknown;
@@ -89,6 +177,9 @@ struct ModDetails
, homeurl(other.homeurl)
, description(other.description)
, authors(other.authors)
, issue_tracker(other.issue_tracker)
, licenses(other.licenses)
, icon_file(other.icon_file)
, status(other.status)
{}
@@ -101,6 +192,9 @@ struct ModDetails
this->homeurl = other.homeurl;
this->description = other.description;
this->authors = other.authors;
this->issue_tracker = other.issue_tracker;
this->licenses = other.licenses;
this->icon_file = other.icon_file;
this->status = other.status;
return *this;
@@ -115,6 +209,9 @@ struct ModDetails
this->homeurl = other.homeurl;
this->description = other.description;
this->authors = other.authors;
this->issue_tracker = other.issue_tracker;
this->licenses = other.licenses;
this->icon_file = other.icon_file;
this->status = other.status;
return *this;

View File

@@ -37,6 +37,7 @@
#include "ModFolderModel.h"
#include <FileSystem.h>
#include <qheaderview.h>
#include <QDebug>
#include <QFileSystemWatcher>
#include <QIcon>
@@ -52,12 +53,14 @@
#include "minecraft/mod/tasks/LocalModParseTask.h"
#include "minecraft/mod/tasks/ModFolderLoadTask.h"
#include "modplatform/ModIndex.h"
ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir)
: ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed)
{
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER };
m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") });
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME , SortType::VERSION, SortType::DATE, SortType::PROVIDER};
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents};
}
QVariant ModFolderModel::data(const QModelIndex &index, int role) const
@@ -118,7 +121,9 @@ QVariant ModFolderModel::data(const QModelIndex &index, int role) const
case Qt::DecorationRole: {
if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
return APPLICATION->getThemedIcon("status-yellow");
if (column == ImageColumn) {
return at(row)->icon({32, 32}, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
}
return {};
}
case Qt::CheckStateRole:
@@ -142,15 +147,12 @@ QVariant ModFolderModel::headerData(int section, Qt::Orientation orientation, in
switch (section)
{
case ActiveColumn:
return QString();
case NameColumn:
return tr("Name");
case VersionColumn:
return tr("Version");
case DateColumn:
return tr("Last changed");
case ProviderColumn:
return tr("Provider");
case ImageColumn:
return columnNames().at(section);
default:
return QVariant();
}
@@ -197,10 +199,10 @@ Task* ModFolderModel::createParseTask(Resource& resource)
bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata)
{
for(auto mod : allMods()) {
if(mod->fileinfo().fileName() == filename) {
for(auto mod : allMods()){
if(mod->fileinfo().fileName() == filename){
auto index_dir = indexDir();
mod->destroy(index_dir, preserve_metadata, false);
mod->destroy(index_dir, preserve_metadata);
update();

View File

@@ -64,6 +64,7 @@ public:
enum Columns
{
ActiveColumn = 0,
ImageColumn,
NameColumn,
VersionColumn,
DateColumn,
@@ -77,6 +78,8 @@ public:
};
ModFolderModel(const QString &dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true);
virtual QString id() const override { return "mods"; }
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;

View File

@@ -71,7 +71,6 @@ std::pair<int, bool> Resource::compare(const Resource& other, SortType type) con
return { 1, type == SortType::ENABLED };
if (!enabled() && other.enabled())
return { -1, type == SortType::ENABLED };
break;
case SortType::NAME: {
QString this_name{ name() };
QString other_name{ other.name() };
@@ -82,14 +81,12 @@ std::pair<int, bool> Resource::compare(const Resource& other, SortType type) con
auto compare_result = QString::compare(this_name, other_name, Qt::CaseInsensitive);
if (compare_result != 0)
return { compare_result, type == SortType::NAME };
break;
}
case SortType::DATE:
if (dateTimeChanged() > other.dateTimeChanged())
return { 1, type == SortType::DATE };
if (dateTimeChanged() < other.dateTimeChanged())
return { -1, type == SortType::DATE };
break;
}
return { 0, false };
@@ -148,10 +145,14 @@ bool Resource::enable(EnableAction action)
return true;
}
bool Resource::destroy(bool attemptTrash)
bool Resource::destroy()
{
m_type = ResourceType::UNKNOWN;
return (attemptTrash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath());
if (FS::trash(m_file_info.filePath()))
return true;
return FS::deletePath(m_file_info.filePath());
}
bool Resource::isSymLinkUnder(const QString& instPath) const

View File

@@ -92,7 +92,7 @@ class Resource : public QObject {
}
// Delete all files of this resource.
bool destroy(bool attemptTrash = true);
bool destroy();
[[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); }

View File

@@ -8,12 +8,15 @@
#include <QStyle>
#include <QThreadPool>
#include <QUrl>
#include <QMenu>
#include "Application.h"
#include "FileSystem.h"
#include "QVariantUtils.h"
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
#include "settings/Setting.h"
#include "tasks/Task.h"
ResourceFolderModel::ResourceFolderModel(QDir dir, BaseInstance* instance, QObject* parent, bool create_dir)
@@ -156,7 +159,7 @@ bool ResourceFolderModel::uninstallResource(QString file_name)
{
for (auto& resource : m_resources) {
if (resource->fileinfo().fileName() == file_name) {
auto res = resource->destroy(false);
auto res = resource->destroy();
update();
@@ -471,10 +474,10 @@ QVariant ResourceFolderModel::headerData(int section, Qt::Orientation orientatio
switch (role) {
case Qt::DisplayRole:
switch (section) {
case ACTIVE_COLUMN:
case NAME_COLUMN:
return tr("Name");
case DATE_COLUMN:
return tr("Last modified");
return columnNames().at(section);
default:
return {};
}
@@ -500,6 +503,75 @@ QVariant ResourceFolderModel::headerData(int section, Qt::Orientation orientatio
return {};
}
void ResourceFolderModel::setupHeaderAction(QAction* act, int column)
{
Q_ASSERT(act);
act->setText(columnNames().at(column));
}
void ResourceFolderModel::saveHiddenColumn(int column, bool hidden)
{
auto const setting_name = QString("UI/%1_Page/HiddenColumns").arg(id());
auto setting = (m_instance->settings()->contains(setting_name)) ?
m_instance->settings()->getSetting(setting_name) : m_instance->settings()->registerSetting(setting_name);
auto hiddenColumns = setting->get().toStringList();
auto name = columnNames(false).at(column);
auto index = hiddenColumns.indexOf(name);
if (index >= 0 && !hidden) {
hiddenColumns.removeAt(index);
} else if ( index < 0 && hidden) {
hiddenColumns.append(name);
}
setting->set(hiddenColumns);
}
void ResourceFolderModel::loadHiddenColumns(QTreeView *tree)
{
auto const setting_name = QString("UI/%1_Page/HiddenColumns").arg(id());
auto setting = (m_instance->settings()->contains(setting_name)) ?
m_instance->settings()->getSetting(setting_name) : m_instance->settings()->registerSetting(setting_name);
auto hiddenColumns = setting->get().toStringList();
auto col_names = columnNames(false);
for (auto col_name : hiddenColumns) {
auto index = col_names.indexOf(col_name);
if (index >= 0)
tree->setColumnHidden(index, true);
}
}
QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree)
{
auto menu = new QMenu(tree);
menu->addSeparator()->setText(tr("Show / Hide Columns"));
for (int col = 0; col < columnCount(); ++col) {
auto act = new QAction(menu);
setupHeaderAction(act, col);
act->setCheckable(true);
act->setChecked(!tree->isColumnHidden(col));
connect(act, &QAction::toggled, tree, [this, col, tree](bool toggled){
tree->setColumnHidden(col, !toggled);
for(int c = 0; c < columnCount(); ++c) {
if (m_column_resize_modes.at(c) == QHeaderView::ResizeToContents)
tree->resizeColumnToContents(c);
}
saveHiddenColumn(col, !toggled);
});
menu->addAction(act);
}
return menu;
}
QSortFilterProxyModel* ResourceFolderModel::createFilterProxyModel(QObject* parent)
{
return new ProxyModel(parent);

View File

@@ -1,5 +1,8 @@
#pragma once
#include <QHeaderView>
#include <QAction>
#include <QTreeView>
#include <QAbstractListModel>
#include <QDir>
#include <QFileSystemWatcher>
@@ -29,6 +32,8 @@ class ResourceFolderModel : public QAbstractListModel {
ResourceFolderModel(QDir, BaseInstance* instance, QObject* parent = nullptr, bool create_dir = true);
~ResourceFolderModel() override;
virtual QString id() const { return "resource"; }
/** Starts watching the paths for changes.
*
* Returns whether starting to watch all the paths was successful.
@@ -92,6 +97,7 @@ class ResourceFolderModel : public QAbstractListModel {
/* Basic columns */
enum Columns { ACTIVE_COLUMN = 0, NAME_COLUMN, DATE_COLUMN, NUM_COLUMNS };
QStringList columnNames(bool translated = true) const { return translated ? m_column_names_translated : m_column_names; };
[[nodiscard]] int rowCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : static_cast<int>(size()); }
[[nodiscard]] int columnCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : NUM_COLUMNS; };
@@ -110,6 +116,11 @@ class ResourceFolderModel : public QAbstractListModel {
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
void setupHeaderAction(QAction* act, int column);
void saveHiddenColumn(int column, bool hidden);
void loadHiddenColumns(QTreeView* tree);
QMenu* createHeaderContextMenu(QTreeView* tree);
/** This creates a proxy model to filter / sort the model for a UI.
*
* The actual comparisons and filtering are done directly by the Resource, so to modify behavior go there instead!
@@ -117,6 +128,7 @@ class ResourceFolderModel : public QAbstractListModel {
QSortFilterProxyModel* createFilterProxyModel(QObject* parent = nullptr);
[[nodiscard]] SortType columnToSortKey(size_t column) const;
[[nodiscard]] QList<QHeaderView::ResizeMode> columnResizeModes() const { return m_column_resize_modes; }
class ProxyModel : public QSortFilterProxyModel {
public:
@@ -187,6 +199,9 @@ class ResourceFolderModel : public QAbstractListModel {
// Represents the relationship between a column's index (represented by the list index), and it's sorting key.
// As such, the order in with they appear is very important!
QList<SortType> m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE };
QStringList m_column_names = {"Enable", "Name", "Last Modified"};
QStringList m_column_names_translated = {tr("Enable"), tr("Name"), tr("Last Modified")};
QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch, QHeaderView::ResizeToContents };
bool m_can_interact = true;

View File

@@ -40,7 +40,7 @@ void ResourcePack::setDescription(QString new_description)
m_description = new_description;
}
void ResourcePack::setImage(QImage new_image)
void ResourcePack::setImage(QImage new_image) const
{
QMutexLocker locker(&m_data_lock);
@@ -49,7 +49,10 @@ void ResourcePack::setImage(QImage new_image)
if (m_pack_image_cache_key.key.isValid())
PixmapCache::instance().remove(m_pack_image_cache_key.key);
m_pack_image_cache_key.key = PixmapCache::instance().insert(QPixmap::fromImage(new_image));
// scale the image to avoid flooding the pixmapcache
auto pixmap = QPixmap::fromImage(new_image.scaled({64, 64}, Qt::AspectRatioMode::KeepAspectRatioByExpanding));
m_pack_image_cache_key.key = PixmapCache::instance().insert(pixmap);
m_pack_image_cache_key.was_ever_used = true;
// This can happen if the pixmap is too big to fit in the cache :c
@@ -59,21 +62,25 @@ void ResourcePack::setImage(QImage new_image)
}
}
QPixmap ResourcePack::image(QSize size)
QPixmap ResourcePack::image(QSize size, Qt::AspectRatioMode mode) const
{
QPixmap cached_image;
if (PixmapCache::instance().find(m_pack_image_cache_key.key, &cached_image)) {
if (size.isNull())
return cached_image;
return cached_image.scaled(size);
return cached_image.scaled(size, mode);
}
// No valid image we can get
if (!m_pack_image_cache_key.was_ever_used)
if (!m_pack_image_cache_key.was_ever_used) {
return {};
} else {
qDebug() << "Resource Pack" << name() << "Had it's image evicted from the cache. reloading...";
PixmapCache::markCacheMissByEviciton();
}
// Imaged got evicted from the cache. Re-process it and retry.
ResourcePackUtils::process(*this);
ResourcePackUtils::processPackPNG(*this);
return image(size);
}
@@ -95,7 +102,6 @@ std::pair<int, bool> ResourcePack::compare(const Resource& other, SortType type)
auto res = Resource::compare(other, type);
if (res.first != 0)
return res;
break;
}
case SortType::PACK_FORMAT: {
auto this_ver = packFormat();
@@ -105,7 +111,6 @@ std::pair<int, bool> ResourcePack::compare(const Resource& other, SortType type)
return { 1, type == SortType::PACK_FORMAT };
if (this_ver < other_ver)
return { -1, type == SortType::PACK_FORMAT };
break;
}
}
return { 0, false };

View File

@@ -31,7 +31,7 @@ class ResourcePack : public Resource {
[[nodiscard]] QString description() const { return m_description; }
/** Gets the image of the resource pack, converted to a QPixmap for drawing, and scaled to size. */
[[nodiscard]] QPixmap image(QSize size);
[[nodiscard]] QPixmap image(QSize size, Qt::AspectRatioMode mode = Qt::AspectRatioMode::IgnoreAspectRatio) const;
/** Thread-safe. */
void setPackFormat(int new_format_id);
@@ -40,7 +40,7 @@ class ResourcePack : public Resource {
void setDescription(QString new_description);
/** Thread-safe. */
void setImage(QImage new_image);
void setImage(QImage new_image) const;
bool valid() const override;
@@ -67,5 +67,5 @@ class ResourcePack : public Resource {
struct {
QPixmapCache::Key key;
bool was_ever_used = false;
} m_pack_image_cache_key;
} mutable m_pack_image_cache_key;
};

View File

@@ -35,6 +35,8 @@
*/
#include "ResourcePackFolderModel.h"
#include <qnamespace.h>
#include <qsize.h>
#include <QIcon>
#include <QStyle>
@@ -48,7 +50,11 @@
ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstance* instance)
: ResourceFolderModel(QDir(dir), instance)
{
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE };
m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") });
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE};
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents };
}
QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
@@ -84,9 +90,11 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
return {};
}
case Qt::DecorationRole: {
if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
if (column == NameColumn && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
return APPLICATION->getThemedIcon("status-yellow");
if (column == ImageColumn) {
return at(row)->image({32, 32}, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
}
return {};
}
case Qt::ToolTipRole: {
@@ -94,7 +102,7 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
//: The string being explained by this is in the format: ID (Lower version - Upper version)
return tr("The resource pack format ID, as well as the Minecraft versions it was designed for.");
}
if (column == NAME_COLUMN) {
if (column == NameColumn) {
if (at(row)->isSymLinkUnder(instDirPath())) {
return m_resources[row]->internal_id() +
tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
@@ -126,13 +134,11 @@ QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orient
case Qt::DisplayRole:
switch (section) {
case ActiveColumn:
return QString();
case NameColumn:
return tr("Name");
case PackFormatColumn:
return tr("Pack Format");
case DateColumn:
return tr("Last changed");
case ImageColumn:
return columnNames().at(section);
default:
return {};
}
@@ -151,6 +157,11 @@ QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orient
default:
return {};
}
case Qt::SizeHintRole:
if (section == ImageColumn) {
return QSize(64,0);
}
return {};
default:
return {};
}

View File

@@ -11,6 +11,7 @@ public:
enum Columns
{
ActiveColumn = 0,
ImageColumn,
NameColumn,
PackFormatColumn,
DateColumn,
@@ -19,6 +20,8 @@ public:
explicit ResourcePackFolderModel(const QString &dir, BaseInstance* instance);
virtual QString id() const override { return "resourcepacks"; }
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;

View File

@@ -9,4 +9,6 @@ class ShaderPackFolderModel : public ResourceFolderModel {
explicit ShaderPackFolderModel(const QString& dir, BaseInstance* instance)
: ResourceFolderModel(QDir(dir), instance)
{}
virtual QString id() const override { return "shaderpacks"; }
};

View File

@@ -23,6 +23,8 @@
#include <QMap>
#include <QRegularExpression>
#include "MTPixmapCache.h"
#include "minecraft/mod/tasks/LocalTexturePackParseTask.h"
void TexturePack::setDescription(QString new_description)
@@ -32,34 +34,41 @@ void TexturePack::setDescription(QString new_description)
m_description = new_description;
}
void TexturePack::setImage(QImage new_image)
void TexturePack::setImage(QImage new_image) const
{
QMutexLocker locker(&m_data_lock);
Q_ASSERT(!new_image.isNull());
if (m_pack_image_cache_key.key.isValid())
QPixmapCache::remove(m_pack_image_cache_key.key);
PixmapCache::remove(m_pack_image_cache_key.key);
m_pack_image_cache_key.key = QPixmapCache::insert(QPixmap::fromImage(new_image));
// scale the image to avoid flooding the pixmapcache
auto pixmap = QPixmap::fromImage(new_image.scaled({64, 64}, Qt::AspectRatioMode::KeepAspectRatioByExpanding));
m_pack_image_cache_key.key = PixmapCache::insert(pixmap);
m_pack_image_cache_key.was_ever_used = true;
}
QPixmap TexturePack::image(QSize size)
QPixmap TexturePack::image(QSize size, Qt::AspectRatioMode mode) const
{
QPixmap cached_image;
if (QPixmapCache::find(m_pack_image_cache_key.key, &cached_image)) {
if (PixmapCache::find(m_pack_image_cache_key.key, &cached_image)) {
if (size.isNull())
return cached_image;
return cached_image.scaled(size);
return cached_image.scaled(size, mode);
}
// No valid image we can get
if (!m_pack_image_cache_key.was_ever_used)
if (!m_pack_image_cache_key.was_ever_used) {
return {};
} else {
qDebug() << "Texture Pack" << name() << "Had it's image evicted from the cache. reloading...";
PixmapCache::markCacheMissByEviciton();
}
// Imaged got evicted from the cache. Re-process it and retry.
TexturePackUtils::process(*this);
TexturePackUtils::processPackPNG(*this);
return image(size);
}

View File

@@ -40,13 +40,13 @@ class TexturePack : public Resource {
[[nodiscard]] QString description() const { return m_description; }
/** Gets the image of the texture pack, converted to a QPixmap for drawing, and scaled to size. */
[[nodiscard]] QPixmap image(QSize size);
[[nodiscard]] QPixmap image(QSize size, Qt::AspectRatioMode mode = Qt::AspectRatioMode::IgnoreAspectRatio) const;
/** Thread-safe. */
void setDescription(QString new_description);
/** Thread-safe. */
void setImage(QImage new_image);
void setImage(QImage new_image) const;
bool valid() const override;
@@ -65,5 +65,5 @@ class TexturePack : public Resource {
struct {
QPixmapCache::Key key;
bool was_ever_used = false;
} m_pack_image_cache_key;
} mutable m_pack_image_cache_key;
};

View File

@@ -33,6 +33,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <QCoreApplication>
#include "Application.h"
#include "TexturePackFolderModel.h"
@@ -41,7 +44,13 @@
TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* instance)
: ResourceFolderModel(QDir(dir), instance)
{}
{
m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified" });
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") });
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE };
m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents};
}
Task* TexturePackFolderModel::createUpdateTask()
{
@@ -52,3 +61,96 @@ Task* TexturePackFolderModel::createParseTask(Resource& resource)
{
return new LocalTexturePackParseTask(m_next_resolution_ticket, static_cast<TexturePack&>(resource));
}
QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const
{
if (!validateIndex(index))
return {};
int row = index.row();
int column = index.column();
switch (role) {
case Qt::DisplayRole:
switch (column) {
case NameColumn:
return m_resources[row]->name();
case DateColumn:
return m_resources[row]->dateTimeChanged();
default:
return {};
}
case Qt::ToolTipRole:
if (column == NameColumn) {
if (at(row)->isSymLinkUnder(instDirPath())) {
return m_resources[row]->internal_id() +
tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
"\nCanonical Path: %1")
.arg(at(row)->fileinfo().canonicalFilePath());;
}
if (at(row)->isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
}
}
return m_resources[row]->internal_id();
case Qt::DecorationRole: {
if (column == NameColumn && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
return APPLICATION->getThemedIcon("status-yellow");
if (column == ImageColumn) {
return at(row)->image({32, 32}, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
}
return {};
}
case Qt::CheckStateRole:
if (column == ActiveColumn) {
return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked;
}
return {};
default:
return {};
}
}
QVariant TexturePackFolderModel::headerData(int section, Qt::Orientation orientation, int role) const
{
switch (role) {
case Qt::DisplayRole:
switch (section) {
case ActiveColumn:
case NameColumn:
case DateColumn:
case ImageColumn:
return columnNames().at(section);
default:
return {};
}
case Qt::ToolTipRole: {
switch (section) {
case ActiveColumn:
//: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc.
return tr("Is the resource enabled?");
case NameColumn:
//: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc.
return tr("The name of the resource.");
case DateColumn:
//: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc.
return tr("The date and time this resource was last changed (or added).");
default:
return {};
}
}
default:
break;
}
return {};
}
int TexturePackFolderModel::columnCount(const QModelIndex& parent) const
{
return parent.isValid() ? 0 : NUM_COLUMNS;
}

View File

@@ -38,12 +38,35 @@
#include "ResourceFolderModel.h"
#include "TexturePack.h"
class TexturePackFolderModel : public ResourceFolderModel
{
Q_OBJECT
public:
enum Columns
{
ActiveColumn = 0,
ImageColumn,
NameColumn,
DateColumn,
NUM_COLUMNS
};
explicit TexturePackFolderModel(const QString &dir, std::shared_ptr<const BaseInstance> instance);
virtual QString id() const override { return "texturepacks"; }
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
[[nodiscard]] int columnCount(const QModelIndex &parent) const override;
explicit TexturePackFolderModel(const QString &dir, BaseInstance* instance);
[[nodiscard]] Task* createUpdateTask() override;
[[nodiscard]] Task* createParseTask(Resource&) override;
RESOURCE_HELPERS(TexturePack)
};

View File

@@ -0,0 +1,252 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "GetModDependenciesTask.h"
#include <QDebug>
#include <algorithm>
#include <memory>
#include "Json.h"
#include "QObjectPtr.h"
#include "minecraft/mod/MetadataHandler.h"
#include "modplatform/ModIndex.h"
#include "modplatform/ResourceAPI.h"
#include "modplatform/flame/FlameAPI.h"
#include "modplatform/modrinth/ModrinthAPI.h"
#include "tasks/ConcurrentTask.h"
#include "tasks/SequentialTask.h"
#include "ui/pages/modplatform/ModModel.h"
#include "ui/pages/modplatform/flame/FlameResourceModels.h"
#include "ui/pages/modplatform/modrinth/ModrinthResourceModels.h"
static Version mcVersion(BaseInstance* inst)
{
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion();
}
static ResourceAPI::ModLoaderTypes mcLoaders(BaseInstance* inst)
{
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getModLoaders().value();
}
GetModDependenciesTask::GetModDependenciesTask(QObject* parent,
BaseInstance* instance,
ModFolderModel* folder,
QList<std::shared_ptr<PackDependency>> selected)
: SequentialTask(parent, tr("Get dependencies"))
, m_selected(selected)
, m_flame_provider{ ModPlatform::ResourceProvider::FLAME, std::make_shared<ResourceDownload::FlameModModel>(*instance),
std::make_shared<FlameAPI>() }
, m_modrinth_provider{ ModPlatform::ResourceProvider::MODRINTH, std::make_shared<ResourceDownload::ModrinthModModel>(*instance),
std::make_shared<ModrinthAPI>() }
, m_version(mcVersion(instance))
, m_loaderType(mcLoaders(instance))
{
for (auto mod : folder->allMods())
if (auto meta = mod->metadata(); meta)
m_mods.append(meta);
prepare();
};
void GetModDependenciesTask::prepare()
{
for (auto sel : m_selected) {
for (auto dep : getDependenciesForVersion(sel->version, sel->pack->provider)) {
addTask(prepareDependencyTask(dep, sel->pack->provider, 20));
}
}
}
ModPlatform::Dependency GetModDependenciesTask::getOverride(const ModPlatform::Dependency& dep,
const ModPlatform::ResourceProvider providerName)
{
if (auto isQuilt = m_loaderType & ResourceAPI::Quilt; isQuilt || m_loaderType & ResourceAPI::Fabric) {
auto overide = ModPlatform::getOverrideDeps();
auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, providerName, isQuilt](auto o) {
return o.provider == providerName && dep.addonId == (isQuilt ? o.fabric : o.quilt);
});
if (over != overide.cend()) {
return { isQuilt ? over->quilt : over->fabric, dep.type };
}
}
return dep;
}
QList<ModPlatform::Dependency> GetModDependenciesTask::getDependenciesForVersion(const ModPlatform::IndexedVersion& version,
const ModPlatform::ResourceProvider providerName)
{
QList<ModPlatform::Dependency> c_dependencies;
for (auto ver_dep : version.dependencies) {
if (ver_dep.type != ModPlatform::DependencyType::REQUIRED)
continue;
auto isOnlyVersion = providerName == ModPlatform::ResourceProvider::MODRINTH && ver_dep.addonId.toString().isEmpty();
if (auto dep = std::find_if(c_dependencies.begin(), c_dependencies.end(),
[&ver_dep, isOnlyVersion](const ModPlatform::Dependency& i) {
return isOnlyVersion ? i.version == ver_dep.version : i.addonId == ver_dep.addonId;
});
dep != c_dependencies.end())
continue; // check the current dependency list
if (auto dep = std::find_if(m_selected.begin(), m_selected.end(),
[&ver_dep, providerName, isOnlyVersion](std::shared_ptr<PackDependency> i) {
return i->pack->provider == providerName && (isOnlyVersion ? i->version.version == ver_dep.version
: i->pack->addonId == ver_dep.addonId);
});
dep != m_selected.end())
continue; // check the selected versions
if (auto dep = std::find_if(m_mods.begin(), m_mods.end(),
[&ver_dep, providerName, isOnlyVersion](std::shared_ptr<Metadata::ModStruct> i) {
return i->provider == providerName &&
(isOnlyVersion ? i->file_id == ver_dep.version : i->project_id == ver_dep.addonId);
});
dep != m_mods.end())
continue; // check the existing mods
if (auto dep = std::find_if(m_pack_dependencies.begin(), m_pack_dependencies.end(),
[&ver_dep, providerName, isOnlyVersion](std::shared_ptr<PackDependency> i) {
return i->pack->provider == providerName && (isOnlyVersion ? i->version.version == ver_dep.addonId
: i->pack->addonId == ver_dep.addonId);
});
dep != m_pack_dependencies.end()) // check loaded dependencies
continue;
c_dependencies.append(getOverride(ver_dep, providerName));
}
return c_dependencies;
};
Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr<PackDependency> pDep)
{
auto provider = pDep->pack->provider == m_flame_provider.name ? m_flame_provider : m_modrinth_provider;
auto responseInfo = std::make_shared<QByteArray>();
auto info = provider.api->getProject(pDep->pack->addonId.toString(), responseInfo);
QObject::connect(info.get(), &NetJob::succeeded, [responseInfo, provider, pDep] {
QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(*responseInfo, &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset
<< " reason: " << parse_error.errorString();
qDebug() << *responseInfo;
return;
}
try {
auto obj = provider.name == ModPlatform::ResourceProvider::FLAME ? Json::requireObject(Json::requireObject(doc), "data")
: Json::requireObject(doc);
provider.mod->loadIndexedPack(*pDep->pack, obj);
} catch (const JSONValidationError& e) {
qDebug() << doc;
qWarning() << "Error while reading mod info: " << e.cause();
}
});
return info;
}
Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Dependency& dep,
const ModPlatform::ResourceProvider providerName,
int level)
{
auto pDep = std::make_shared<PackDependency>();
pDep->dependency = dep;
pDep->pack = std::make_shared<ModPlatform::IndexedPack>();
pDep->pack->addonId = dep.addonId;
pDep->pack->provider = providerName;
m_pack_dependencies.append(pDep);
auto provider = providerName == m_flame_provider.name ? m_flame_provider : m_modrinth_provider;
auto tasks = makeShared<SequentialTask>(
this, QString("DependencyInfo: %1").arg(dep.addonId.toString().isEmpty() ? dep.version : dep.addonId.toString()));
if (!dep.addonId.toString().isEmpty()) {
tasks->addTask(getProjectInfoTask(pDep));
}
ResourceAPI::DependencySearchArgs args = { dep, m_version, m_loaderType };
ResourceAPI::DependencySearchCallbacks callbacks;
callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, auto& pack) {
try {
QJsonArray arr;
if (dep.version.length() != 0 && doc.isObject()) {
arr.append(doc.object());
} else {
arr = doc.isObject() ? Json::ensureArray(doc.object(), "data") : doc.array();
}
pDep->version = provider.mod->loadDependencyVersions(dep, arr);
if (!pDep->version.addonId.isValid()) {
if (m_loaderType & ResourceAPI::Quilt) { // falback for quilt
auto overide = ModPlatform::getOverrideDeps();
auto over = std::find_if(overide.cbegin(), overide.cend(),
[dep, provider](auto o) { return o.provider == provider.name && dep.addonId == o.quilt; });
if (over != overide.cend()) {
removePack(dep.addonId);
addTask(prepareDependencyTask({ over->fabric, dep.type }, provider.name, level));
return;
}
}
qWarning() << "Error while reading mod version empty ";
qDebug() << doc;
return;
}
pDep->version.is_currently_selected = true;
pDep->pack->versions = { pDep->version };
pDep->pack->versionsLoaded = true;
} catch (const JSONValidationError& e) {
qDebug() << doc;
qWarning() << "Error while reading mod version: " << e.cause();
return;
}
if (level == 0) {
qWarning() << "Dependency cycle exeeded";
return;
}
if (dep.addonId.toString().isEmpty() && !pDep->version.addonId.toString().isEmpty()) {
pDep->pack->addonId = pDep->version.addonId;
auto dep = getOverride({ pDep->version.addonId, pDep->dependency.type }, provider.name);
if (dep.addonId != pDep->version.addonId) {
removePack(pDep->version.addonId);
addTask(prepareDependencyTask(dep, provider.name, level));
} else
addTask(getProjectInfoTask(pDep));
}
for (auto dep : getDependenciesForVersion(pDep->version, provider.name)) {
addTask(prepareDependencyTask(dep, provider.name, level - 1));
}
};
auto version = provider.api->getDependencyVersion(std::move(args), std::move(callbacks));
tasks->addTask(version);
return tasks;
};
void GetModDependenciesTask::removePack(const QVariant addonId)
{
auto pred = [addonId](const std::shared_ptr<PackDependency>& v) { return v->pack->addonId == addonId; };
#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
m_pack_dependencies.removeIf(pred);
#else
for (auto it = m_pack_dependencies.begin(); it != m_pack_dependencies.end();)
if (pred(*it))
it = m_pack_dependencies.erase(it);
else
++it;
#endif
}

View File

@@ -0,0 +1,84 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <QDir>
#include <QEventLoop>
#include <QList>
#include <QVariant>
#include <functional>
#include <memory>
#include "minecraft/mod/MetadataHandler.h"
#include "minecraft/mod/ModFolderModel.h"
#include "modplatform/ModIndex.h"
#include "modplatform/ResourceAPI.h"
#include "tasks/SequentialTask.h"
#include "tasks/Task.h"
#include "ui/pages/modplatform/ModModel.h"
class GetModDependenciesTask : public SequentialTask {
Q_OBJECT
public:
using Ptr = shared_qobject_ptr<GetModDependenciesTask>;
struct PackDependency {
ModPlatform::Dependency dependency;
ModPlatform::IndexedPack::Ptr pack;
ModPlatform::IndexedVersion version;
PackDependency() = default;
PackDependency(const ModPlatform::IndexedPack::Ptr p, const ModPlatform::IndexedVersion& v)
{
pack = p;
version = v;
}
};
struct Provider {
ModPlatform::ResourceProvider name;
std::shared_ptr<ResourceDownload::ModModel> mod;
std::shared_ptr<ResourceAPI> api;
};
explicit GetModDependenciesTask(QObject* parent,
BaseInstance* instance,
ModFolderModel* folder,
QList<std::shared_ptr<PackDependency>> selected);
auto getDependecies() const -> QList<std::shared_ptr<PackDependency>> { return m_pack_dependencies; }
protected slots:
Task::Ptr prepareDependencyTask(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider, int);
QList<ModPlatform::Dependency> getDependenciesForVersion(const ModPlatform::IndexedVersion&,
const ModPlatform::ResourceProvider providerName);
void prepare();
Task::Ptr getProjectInfoTask(std::shared_ptr<PackDependency> pDep);
ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider providerName);
void removePack(const QVariant addonId);
private:
QList<std::shared_ptr<PackDependency>> m_pack_dependencies;
QList<std::shared_ptr<Metadata::ModStruct>> m_mods;
QList<std::shared_ptr<PackDependency>> m_selected;
Provider m_flame_provider;
Provider m_modrinth_provider;
Version m_version;
ResourceAPI::ModLoaderTypes m_loaderType;
};

View File

@@ -52,6 +52,10 @@ ModDetails ReadMCModInfo(QByteArray contents)
authors = firstObj.value("authors").toArray();
}
if (firstObj.contains("logoFile")) {
details.icon_file = firstObj.value("logoFile").toString();
}
for (auto author : authors) {
details.authors.append(author.toString());
}
@@ -166,6 +170,31 @@ ModDetails ReadMCModTOML(QByteArray contents)
}
details.homeurl = homeurl;
QString issueTrackerURL = "";
if (auto issueTrackerURLDatum = tomlData["issueTrackerURL"].as_string()) {
issueTrackerURL = QString::fromStdString(issueTrackerURLDatum->get());
} else if (auto issueTrackerURLDatum = (*modsTable)["issueTrackerURL"].as_string()) {
issueTrackerURL = QString::fromStdString(issueTrackerURLDatum->get());
}
details.issue_tracker = issueTrackerURL;
QString license = "";
if (auto licenseDatum = tomlData["license"].as_string()) {
license = QString::fromStdString(licenseDatum->get());
} else if (auto licenseDatum =(*modsTable)["license"].as_string()) {
license = QString::fromStdString(licenseDatum->get());
}
if (!license.isEmpty())
details.licenses.append(ModLicense(license));
QString logoFile = "";
if (auto logoFileDatum = tomlData["logoFile"].as_string()) {
logoFile = QString::fromStdString(logoFileDatum->get());
} else if (auto logoFileDatum =(*modsTable)["logoFile"].as_string()) {
logoFile = QString::fromStdString(logoFileDatum->get());
}
details.icon_file = logoFile;
return details;
}
@@ -201,6 +230,57 @@ ModDetails ReadFabricModInfo(QByteArray contents)
if (contact.contains("homepage")) {
details.homeurl = contact.value("homepage").toString();
}
if (contact.contains("issues")) {
details.issue_tracker = contact.value("issues").toString();
}
}
if (object.contains("license")) {
auto license = object.value("license");
if (license.isArray()) {
for (auto l : license.toArray()) {
if (l.isString()) {
details.licenses.append(ModLicense(l.toString()));
} else if (l.isObject()) {
auto obj = l.toObject();
details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(),
obj.value("url").toString(), obj.value("description").toString()));
}
}
} else if (license.isString()) {
details.licenses.append(ModLicense(license.toString()));
} else if (license.isObject()) {
auto obj = license.toObject();
details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(), obj.value("url").toString(),
obj.value("description").toString()));
}
}
if (object.contains("icon")) {
auto icon = object.value("icon");
if (icon.isObject()) {
auto obj = icon.toObject();
// take the largest icon
int largest = 0;
for (auto key : obj.keys()) {
auto size = key.split('x').first().toInt();
if (size > largest) {
largest = size;
}
}
if (largest > 0) {
auto key = QString::number(largest) + "x" + QString::number(largest);
details.icon_file = obj.value(key).toString();
} else { // parsing the sizes failed
// take the first
for (auto i : obj) {
details.icon_file = i.toString();
break;
}
}
} else if (icon.isString()) {
details.icon_file = icon.toString();
}
}
}
return details;
@@ -238,6 +318,58 @@ ModDetails ReadQuiltModInfo(QByteArray contents)
if (modContact.contains("homepage")) {
details.homeurl = Json::requireString(modContact.value("homepage"));
}
if (modContact.contains("issues")) {
details.issue_tracker = Json::requireString(modContact.value("issues"));
}
if (modMetadata.contains("license")) {
auto license = modMetadata.value("license");
if (license.isArray()) {
for (auto l : license.toArray()) {
if (l.isString()) {
details.licenses.append(ModLicense(l.toString()));
} else if (l.isObject()) {
auto obj = l.toObject();
details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(),
obj.value("url").toString(), obj.value("description").toString()));
}
}
} else if (license.isString()) {
details.licenses.append(ModLicense(license.toString()));
} else if (license.isObject()) {
auto obj = license.toObject();
details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(), obj.value("url").toString(),
obj.value("description").toString()));
}
}
if (modMetadata.contains("icon")) {
auto icon = modMetadata.value("icon");
if (icon.isObject()) {
auto obj = icon.toObject();
// take the largest icon
int largest = 0;
for (auto key : obj.keys()) {
auto size = key.split('x').first().toInt();
if (size > largest) {
largest = size;
}
}
if (largest > 0) {
auto key = QString::number(largest) + "x" + QString::number(largest);
details.icon_file = obj.value(key).toString();
} else { // parsing the sizes failed
// take the first
for (auto i : obj) {
details.icon_file = i.toString();
break;
}
}
} else if (icon.isString()) {
details.icon_file = icon.toString();
}
}
}
return details;
}
@@ -515,6 +647,85 @@ bool validate(QFileInfo file)
return ModUtils::process(mod, ProcessingLevel::BasicInfoOnly) && mod.valid();
}
bool processIconPNG(const Mod& mod, QByteArray&& raw_data)
{
auto img = QImage::fromData(raw_data);
if (!img.isNull()) {
mod.setIcon(img);
} else {
qWarning() << "Failed to parse mod logo:" << mod.iconPath() << "from" << mod.name();
return false;
}
return true;
}
bool loadIconFile(const Mod& mod) {
if (mod.iconPath().isEmpty()) {
qWarning() << "No Iconfile set, be sure to parse the mod first";
return false;
}
auto png_invalid = [&mod]() {
qWarning() << "Mod at" << mod.fileinfo().filePath() << "does not have a valid icon";
return false;
};
switch (mod.type()) {
case ResourceType::FOLDER:
{
QFileInfo icon_info(FS::PathCombine(mod.fileinfo().filePath(), mod.iconPath()));
if (icon_info.exists() && icon_info.isFile()) {
QFile icon(icon_info.filePath());
if (!icon.open(QIODevice::ReadOnly))
return false;
auto data = icon.readAll();
bool icon_result = ModUtils::processIconPNG(mod, std::move(data));
icon.close();
if (!icon_result) {
return png_invalid(); // icon invalid
}
}
}
case ResourceType::ZIPFILE:
{
QuaZip zip(mod.fileinfo().filePath());
if (!zip.open(QuaZip::mdUnzip))
return false;
QuaZipFile file(&zip);
if (zip.setCurrentFile(mod.iconPath())) {
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Failed to open file in zip.";
zip.close();
return png_invalid();
}
auto data = file.readAll();
bool icon_result = ModUtils::processIconPNG(mod, std::move(data));
file.close();
if (!icon_result) {
return png_invalid(); // icon png invalid
}
} else {
return png_invalid(); // could not set icon as current file.
}
}
case ResourceType::LITEMOD:
{
return false; // can lightmods even have icons?
}
default:
qWarning() << "Invalid type for mod, can not load icon.";
return false;
}
}
} // namespace ModUtils
LocalModParseTask::LocalModParseTask(int token, ResourceType type, const QFileInfo& modFile)

View File

@@ -25,6 +25,9 @@ bool processLitemod(Mod& mod, ProcessingLevel level = ProcessingLevel::Full);
/** Checks whether a file is valid as a mod or not. */
bool validate(QFileInfo file);
bool processIconPNG(const Mod& mod, QByteArray&& raw_data);
bool loadIconFile(const Mod& mod);
} // namespace ModUtils
class LocalModParseTask : public Task {

View File

@@ -1,25 +1,24 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* 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/>.
*/
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "LocalModUpdateTask.h"
#include "Application.h"
#include "FileSystem.h"
#include "minecraft/mod/MetadataHandler.h"

View File

@@ -165,15 +165,16 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level)
bool pack_png_result = ResourcePackUtils::processPackPNG(pack, std::move(data));
file.close();
zip.close();
if (!pack_png_result) {
return png_invalid(); // pack.png invalid
}
} else {
zip.close();
return png_invalid(); // could not set pack.mcmeta as current file.
}
zip.close();
return true;
}
@@ -193,7 +194,7 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data)
return true;
}
bool processPackPNG(ResourcePack& pack, QByteArray&& raw_data)
bool processPackPNG(const ResourcePack& pack, QByteArray&& raw_data)
{
auto img = QImage::fromData(raw_data);
if (!img.isNull()) {
@@ -205,6 +206,68 @@ bool processPackPNG(ResourcePack& pack, QByteArray&& raw_data)
return true;
}
bool processPackPNG(const ResourcePack& pack)
{
auto png_invalid = [&pack]() {
qWarning() << "Resource pack at" << pack.fileinfo().filePath() << "does not have a valid pack.png";
return false;
};
switch (pack.type()) {
case ResourceType::FOLDER:
{
QFileInfo image_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.png"));
if (image_file_info.exists() && image_file_info.isFile()) {
QFile pack_png_file(image_file_info.filePath());
if (!pack_png_file.open(QIODevice::ReadOnly))
return png_invalid(); // can't open pack.png file
auto data = pack_png_file.readAll();
bool pack_png_result = ResourcePackUtils::processPackPNG(pack, std::move(data));
pack_png_file.close();
if (!pack_png_result) {
return png_invalid(); // pack.png invalid
}
} else {
return png_invalid(); // pack.png does not exists or is not a valid file.
}
}
case ResourceType::ZIPFILE:
{
Q_ASSERT(pack.type() == ResourceType::ZIPFILE);
QuaZip zip(pack.fileinfo().filePath());
if (!zip.open(QuaZip::mdUnzip))
return false; // can't open zip file
QuaZipFile file(&zip);
if (zip.setCurrentFile("pack.png")) {
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Failed to open file in zip.";
zip.close();
return png_invalid();
}
auto data = file.readAll();
bool pack_png_result = ResourcePackUtils::processPackPNG(pack, std::move(data));
file.close();
if (!pack_png_result) {
return png_invalid(); // pack.png invalid
}
} else {
return png_invalid(); // could not set pack.mcmeta as current file.
}
}
default:
qWarning() << "Invalid type for resource pack parse task!";
return false;
}
}
bool validate(QFileInfo file)
{
ResourcePack rp{ file };

View File

@@ -35,7 +35,10 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Ful
bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full);
bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data);
bool processPackPNG(ResourcePack& pack, QByteArray&& raw_data);
bool processPackPNG(const ResourcePack& pack, QByteArray&& raw_data);
/// processes ONLY the pack.png (rest of the pack may be invalid)
bool processPackPNG(const ResourcePack& pack);
/** Checks whether a file is valid as a resource pack or not. */
bool validate(QFileInfo file);

View File

@@ -44,11 +44,7 @@ static const QMap<PackedResourceType, QString> s_packed_type_names = {
namespace ResourceUtils {
PackedResourceType identify(QFileInfo file){
if (file.exists() && file.isFile()) {
if (ModUtils::validate(file)) {
// mods can contain resource and data packs so they must be tested first
qDebug() << file.fileName() << "is a mod";
return PackedResourceType::Mod;
} else if (ResourcePackUtils::validate(file)) {
if (ResourcePackUtils::validate(file)) {
qDebug() << file.fileName() << "is a resource pack";
return PackedResourceType::ResourcePack;
} else if (TexturePackUtils::validate(file)) {
@@ -57,6 +53,9 @@ PackedResourceType identify(QFileInfo file){
} else if (DataPackUtils::validate(file)) {
qDebug() << file.fileName() << "is a data pack";
return PackedResourceType::DataPack;
} else if (ModUtils::validate(file)) {
qDebug() << file.fileName() << "is a mod";
return PackedResourceType::Mod;
} else if (WorldSaveUtils::validate(file)) {
qDebug() << file.fileName() << "is a world save";
return PackedResourceType::WorldSave;

View File

@@ -131,6 +131,7 @@ bool processZIP(TexturePack& pack, ProcessingLevel level)
bool packPNG_result = TexturePackUtils::processPackPNG(pack, std::move(data));
file.close();
zip.close();
if (!packPNG_result) {
return false;
}
@@ -147,7 +148,7 @@ bool processPackTXT(TexturePack& pack, QByteArray&& raw_data)
return true;
}
bool processPackPNG(TexturePack& pack, QByteArray&& raw_data)
bool processPackPNG(const TexturePack& pack, QByteArray&& raw_data)
{
auto img = QImage::fromData(raw_data);
if (!img.isNull()) {
@@ -159,6 +160,70 @@ bool processPackPNG(TexturePack& pack, QByteArray&& raw_data)
return true;
}
bool processPackPNG(const TexturePack& pack)
{
auto png_invalid = [&pack]() {
qWarning() << "Texture pack at" << pack.fileinfo().filePath() << "does not have a valid pack.png";
return false;
};
switch (pack.type()) {
case ResourceType::FOLDER:
{
QFileInfo image_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.png"));
if (image_file_info.exists() && image_file_info.isFile()) {
QFile pack_png_file(image_file_info.filePath());
if (!pack_png_file.open(QIODevice::ReadOnly))
return png_invalid(); // can't open pack.png file
auto data = pack_png_file.readAll();
bool pack_png_result = TexturePackUtils::processPackPNG(pack, std::move(data));
pack_png_file.close();
if (!pack_png_result) {
return png_invalid(); // pack.png invalid
}
} else {
return png_invalid(); // pack.png does not exists or is not a valid file.
}
}
case ResourceType::ZIPFILE:
{
Q_ASSERT(pack.type() == ResourceType::ZIPFILE);
QuaZip zip(pack.fileinfo().filePath());
if (!zip.open(QuaZip::mdUnzip))
return false; // can't open zip file
QuaZipFile file(&zip);
if (zip.setCurrentFile("pack.png")) {
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Failed to open file in zip.";
zip.close();
return png_invalid();
}
auto data = file.readAll();
bool pack_png_result = TexturePackUtils::processPackPNG(pack, std::move(data));
file.close();
if (!pack_png_result) {
zip.close();
return png_invalid(); // pack.png invalid
}
} else {
zip.close();
return png_invalid(); // could not set pack.mcmeta as current file.
}
}
default:
qWarning() << "Invalid type for resource pack parse task!";
return false;
}
}
bool validate(QFileInfo file)
{
TexturePack rp{ file };

View File

@@ -36,7 +36,10 @@ bool processZIP(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full
bool processFolder(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full);
bool processPackTXT(TexturePack& pack, QByteArray&& raw_data);
bool processPackPNG(TexturePack& pack, QByteArray&& raw_data);
bool processPackPNG(const TexturePack& pack, QByteArray&& raw_data);
/// processes ONLY the pack.png (rest of the pack may be invalid)
bool processPackPNG(const TexturePack& pack);
/** Checks whether a file is valid as a texture pack or not. */
bool validate(QFileInfo file);

View File

@@ -103,7 +103,7 @@ void ModFolderLoadTask::executeTask()
while (iter.hasNext()) {
auto mod = iter.next().value();
if (mod->status() == ModStatus::NotInstalled) {
mod->destroy(m_index_dir, false, false);
mod->destroy(m_index_dir, false);
iter.remove();
}
}

View File

@@ -145,8 +145,7 @@ void EnsureMetadataTask::executeTask()
connect(project_task.get(), &Task::finished, this, [=] {
invalidade_leftover();
project_task->deleteLater();
if (m_current_task)
m_current_task.reset();
m_current_task = nullptr;
});
m_current_task = project_task;
@@ -155,8 +154,7 @@ void EnsureMetadataTask::executeTask()
connect(version_task.get(), &Task::finished, [=] {
version_task->deleteLater();
if (m_current_task)
m_current_task.reset();
m_current_task = nullptr;
});
if (m_mods.size() > 1)

View File

@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,6 +34,8 @@ enum class ResourceProvider { MODRINTH, FLAME };
enum class ResourceType { MOD, RESOURCE_PACK, SHADER_PACK };
enum class DependencyType { REQUIRED, OPTIONAL, INCOMPATIBLE, EMBEDDED, TOOL, INCLUDE, UNKNOWN };
class ProviderCapabilities {
public:
auto name(ResourceProvider) -> const char*;
@@ -52,6 +55,12 @@ struct DonationData {
QString url;
};
struct Dependency {
QVariant addonId;
DependencyType type;
QString version;
};
struct IndexedVersion {
QVariant addonId;
QVariant fileId;
@@ -66,6 +75,7 @@ struct IndexedVersion {
QString hash;
bool is_preferred = true;
QString changelog;
QList<Dependency> dependencies;
// For internal use, not provided by APIs
bool is_currently_selected = false;
@@ -119,6 +129,22 @@ struct IndexedPack {
}
};
struct OverrideDep {
QString quilt;
QString fabric;
QString slug;
ModPlatform::ResourceProvider provider;
};
inline auto getOverrideDeps() -> QList<OverrideDep>
{
return { { "634179", "306612", "API", ModPlatform::ResourceProvider::FLAME },
{ "720410", "308769", "KotlinLibraries", ModPlatform::ResourceProvider::FLAME },
{ "qvIfYCYJ", "P7dR8mSH", "API", ModPlatform::ResourceProvider::MODRINTH },
{ "lwVhp9o5", "Ha28R6CL", "KotlinLibraries", ModPlatform::ResourceProvider::MODRINTH } };
};
} // namespace ModPlatform
Q_DECLARE_METATYPE(ModPlatform::IndexedPack)

View File

@@ -111,6 +111,16 @@ class ResourceAPI {
std::function<void(QJsonDocument&, ModPlatform::IndexedPack)> on_succeed;
};
struct DependencySearchArgs {
ModPlatform::Dependency dependency;
Version mcVersion;
ModLoaderTypes loader;
};
struct DependencySearchCallbacks {
std::function<void(QJsonDocument&, const ModPlatform::Dependency&)> on_succeed;
};
public:
/** Gets a list of available sorting methods for this API. */
[[nodiscard]] virtual auto getSortingMethods() const -> QList<SortingMethod> = 0;
@@ -143,6 +153,12 @@ class ResourceAPI {
return nullptr;
}
[[nodiscard]] virtual Task::Ptr getDependencyVersion(DependencySearchArgs&&, DependencySearchCallbacks&&) const
{
qWarning() << "TODO";
return nullptr;
}
static auto getModLoaderString(ModLoaderType type) -> const QString
{
switch (type) {

View File

@@ -21,10 +21,6 @@ bool Flame::FileResolvingTask::abort()
void Flame::FileResolvingTask::executeTask()
{
if (m_toProcess.files.isEmpty()) { // no file to resolve so leave it empty and emit success immediately
emitSucceeded();
return;
}
setStatus(tr("Resolving mod IDs..."));
setProgress(0, 3);
m_dljob.reset(new NetJob("Mod id resolver", m_network));
@@ -132,13 +128,12 @@ void Flame::FileResolvingTask::netJobFinished()
m_checkJob->start();
}
void Flame::FileResolvingTask::modrinthCheckFinished()
{
void Flame::FileResolvingTask::modrinthCheckFinished() {
setProgress(2, 3);
qDebug() << "Finished with blocked mods : " << blockedProjects.size();
for (auto it = blockedProjects.keyBegin(); it != blockedProjects.keyEnd(); it++) {
auto& out = *it;
auto &out = *it;
auto bytes = blockedProjects[out];
if (!out->resolved) {
continue;
@@ -158,13 +153,15 @@ void Flame::FileResolvingTask::modrinthCheckFinished()
out->resolved = false;
}
}
// copy to an output list and filter out projects found on modrinth
//copy to an output list and filter out projects found on modrinth
auto block = std::make_shared<QList<File*>>();
auto it = blockedProjects.keys();
std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File* f) { return !f->resolved; });
// Display not found mods early
std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File *f) {
return !f->resolved;
});
//Display not found mods early
if (!block->empty()) {
// blocked mods found, we need the slug for displaying.... we need another job :D !
//blocked mods found, we need the slug for displaying.... we need another job :D !
m_slugJob.reset(new NetJob("Slug Job", m_network));
int index = 0;
for (auto mod : *block) {
@@ -176,8 +173,8 @@ void Flame::FileResolvingTask::modrinthCheckFinished()
QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() {
auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done
auto json = QJsonDocument::fromJson(*output);
auto base =
Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json), "data"), "links"), "websiteUrl");
auto base = Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json),"data"),"links"),
"websiteUrl");
auto link = QString("%1/download/%2").arg(base, QString::number(mod->fileId));
mod->websiteUrl = link;
});

View File

@@ -4,8 +4,10 @@
#pragma once
#include <algorithm>
#include <memory>
#include "modplatform/ModIndex.h"
#include "modplatform/ResourceAPI.h"
#include "modplatform/helpers/NetworkResourceAPI.h"
class FlameAPI : public NetworkResourceAPI {
@@ -21,8 +23,6 @@ class FlameAPI : public NetworkResourceAPI {
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt); }
private:
static int getClassId(ModPlatform::ResourceType type)
{
@@ -77,14 +77,48 @@ class FlameAPI : public NetworkResourceAPI {
[[nodiscard]] std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override
{
QString url{ QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&").arg(args.pack.addonId.toString()) };
auto addonId = args.pack.addonId.toString();
QString url{ QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&").arg(addonId) };
QStringList get_parameters;
if (args.mcVersions.has_value())
get_parameters.append(QString("gameVersion=%1").arg(args.mcVersions.value().front().toString()));
if (args.loaders.has_value())
get_parameters.append(QString("modLoaderType=%1").arg(getMappedModLoader(args.loaders.value())));
if (args.loaders.has_value()) {
int mappedModLoader = getMappedModLoader(args.loaders.value());
if (args.loaders.value() & Quilt) {
auto overide = ModPlatform::getOverrideDeps();
auto over = std::find_if(overide.cbegin(), overide.cend(), [addonId](auto dep) {
return dep.provider == ModPlatform::ResourceProvider::FLAME && addonId == dep.quilt;
});
if (over != overide.cend()) {
mappedModLoader = 5;
}
}
get_parameters.append(QString("modLoaderType=%1").arg(mappedModLoader));
}
return url + get_parameters.join('&');
};
[[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
{
auto mappedModLoader = getMappedModLoader(args.loader);
auto addonId = args.dependency.addonId.toString();
if (args.loader & Quilt) {
auto overide = ModPlatform::getOverrideDeps();
auto over = std::find_if(overide.cbegin(), overide.cend(), [addonId](auto dep) {
return dep.provider == ModPlatform::ResourceProvider::FLAME && addonId == dep.quilt;
});
if (over != overide.cend()) {
mappedModLoader = 5;
}
}
return QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&gameVersion=%2&modLoaderType=%3")
.arg(addonId)
.arg(args.mcVersion.toString())
.arg(mappedModLoader);
};
};

View File

@@ -470,9 +470,8 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
switch (result.type) {
case Flame::File::Type::Folder: {
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
// fallthrough intentional, we treat these as plain old mods and dump them wherever.
// fall-through intentional, we treat these as plain old mods and dump them wherever.
}
/* fallthrough */
case Flame::File::Type::SingleFile:
case Flame::File::Type::Mod: {
if (!result.url.isEmpty()) {
@@ -563,8 +562,6 @@ void FlameCreationTask::validateZIPResouces()
if (FS::move(localPath, destPath)) {
return destPath;
}
} else {
qDebug() << "Target folder of" << fileName << "is correct at" << targetFolder;
}
return localPath;
};
@@ -586,9 +583,6 @@ void FlameCreationTask::validateZIPResouces()
QString worldPath;
switch (type) {
case PackedResourceType::Mod :
validatePath(fileName, targetFolder, "mods");
break;
case PackedResourceType::ResourcePack :
validatePath(fileName, targetFolder, "resourcepacks");
break;
@@ -598,6 +592,9 @@ void FlameCreationTask::validateZIPResouces()
case PackedResourceType::DataPack :
validatePath(fileName, targetFolder, "datapacks");
break;
case PackedResourceType::Mod :
validatePath(fileName, targetFolder, "mods");
break;
case PackedResourceType::ShaderPack :
// in theroy flame API can't do this but who knows, that *may* change ?
// better to handle it if it *does* occure in the future

View File

@@ -39,15 +39,15 @@ void FlameMod::loadURLs(ModPlatform::IndexedPack& pack, QJsonObject& obj)
auto links_obj = Json::ensureObject(obj, "links");
pack.extraData.issuesUrl = Json::ensureString(links_obj, "issuesUrl");
if(pack.extraData.issuesUrl.endsWith('/'))
if (pack.extraData.issuesUrl.endsWith('/'))
pack.extraData.issuesUrl.chop(1);
pack.extraData.sourceUrl = Json::ensureString(links_obj, "sourceUrl");
if(pack.extraData.sourceUrl.endsWith('/'))
if (pack.extraData.sourceUrl.endsWith('/'))
pack.extraData.sourceUrl.chop(1);
pack.extraData.wikiUrl = Json::ensureString(links_obj, "wikiUrl");
if(pack.extraData.wikiUrl.endsWith('/'))
if (pack.extraData.wikiUrl.endsWith('/'))
pack.extraData.wikiUrl.chop(1);
if (!pack.extraData.body.isEmpty())
@@ -56,7 +56,7 @@ void FlameMod::loadURLs(ModPlatform::IndexedPack& pack, QJsonObject& obj)
void FlameMod::loadBody(ModPlatform::IndexedPack& pack, QJsonObject& obj)
{
pack.extraData.body = api.getModDescription(pack.addonId.toInt());
pack.extraData.body = api.getModDescription(pack.addonId.toInt());
if (!pack.extraData.issuesUrl.isEmpty() || !pack.extraData.sourceUrl.isEmpty() || !pack.extraData.wikiUrl.isEmpty())
pack.extraDataLoaded = true;
@@ -64,12 +64,12 @@ void FlameMod::loadBody(ModPlatform::IndexedPack& pack, QJsonObject& obj)
static QString enumToString(int hash_algorithm)
{
switch(hash_algorithm){
default:
case 1:
return "sha1";
case 2:
return "md5";
switch (hash_algorithm) {
default:
case 1:
return "sha1";
case 2:
return "md5";
}
}
@@ -84,12 +84,12 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
for (auto versionIter : arr) {
auto obj = versionIter.toObject();
auto file = loadIndexedPackVersion(obj);
if(!file.addonId.isValid())
if (!file.addonId.isValid())
file.addonId = pack.addonId;
if(file.fileId.isValid()) // Heuristic to check if the returned value is valid
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
unsortedVersions.append(file);
}
@@ -136,8 +136,61 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
}
}
if(load_changelog)
auto dependencies = Json::ensureArray(obj, "dependencies");
for (auto d : dependencies) {
auto dep = Json::ensureObject(d);
ModPlatform::Dependency dependency;
dependency.addonId = Json::requireInteger(dep, "modId");
switch (Json::requireInteger(dep, "relationType")) {
case 1: // EmbeddedLibrary
dependency.type = ModPlatform::DependencyType::EMBEDDED;
break;
case 2: // OptionalDependency
dependency.type = ModPlatform::DependencyType::OPTIONAL;
break;
case 3: // RequiredDependency
dependency.type = ModPlatform::DependencyType::REQUIRED;
break;
case 4: // Tool
dependency.type = ModPlatform::DependencyType::TOOL;
break;
case 5: // Incompatible
dependency.type = ModPlatform::DependencyType::INCOMPATIBLE;
break;
case 6: // Include
dependency.type = ModPlatform::DependencyType::INCLUDE;
break;
default:
dependency.type = ModPlatform::DependencyType::UNKNOWN;
break;
}
file.dependencies.append(dependency);
}
if (load_changelog)
file.changelog = api.getModFileChangelog(file.addonId.toInt(), file.fileId.toInt());
return file;
}
ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr)
{
QVector<ModPlatform::IndexedVersion> versions;
for (auto versionIter : arr) {
auto obj = versionIter.toObject();
auto file = loadIndexedPackVersion(obj);
if (!file.addonId.isValid())
file.addonId = m.addonId;
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
versions.append(file);
}
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
// dates are in RFC 3339 format
return a.date > b.date;
};
std::sort(versions.begin(), versions.end(), orderSortPredicate);
return versions.front();
};

View File

@@ -6,8 +6,8 @@
#include "modplatform/ModIndex.h"
#include "BaseInstance.h"
#include <QNetworkAccessManager>
#include "BaseInstance.h"
namespace FlameMod {
@@ -19,5 +19,5 @@ void loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
const shared_qobject_ptr<QNetworkAccessManager>& network,
const BaseInstance* inst);
auto loadIndexedPackVersion(QJsonObject& obj, bool load_changelog = false) -> ModPlatform::IndexedVersion;
} // namespace FlameMod
auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion;
} // namespace FlameMod

View File

@@ -4,6 +4,7 @@
#include <QMetaType>
#include <QString>
#include <QVector>
#include "modplatform/ModIndex.h"
namespace Flame {
@@ -27,8 +28,7 @@ struct ModpackExtra {
QString sourceUrl;
};
struct IndexedPack
{
struct IndexedPack {
int addonId;
QString name;
QString description;
@@ -43,9 +43,9 @@ struct IndexedPack
ModpackExtra extra;
};
void loadIndexedPack(IndexedPack & m, QJsonObject & obj);
void loadIndexedPack(IndexedPack& m, QJsonObject& obj);
void loadIndexedInfo(IndexedPack&, QJsonObject&);
void loadIndexedPackVersions(IndexedPack & m, QJsonArray & arr);
}
void loadIndexedPackVersions(IndexedPack& m, QJsonArray& arr);
} // namespace Flame
Q_DECLARE_METATYPE(Flame::IndexedPack)

View File

@@ -76,8 +76,13 @@ bool Flame::File::parseFromObject(const QJsonObject& obj, bool throw_on_blocked
// It is also optional
type = File::Type::SingleFile;
targetFolder = "mods";
if (fileName.endsWith(".zip")) {
// this is probably a resource pack
targetFolder = "resourcepacks";
} else {
// this is probably a mod, dunno what else could modpacks download
targetFolder = "mods";
}
// get the hash
hash = QString();
auto hashes = Json::ensureArray(obj, "hashes");

View File

@@ -117,3 +117,32 @@ Task::Ptr NetworkResourceAPI::getProject(QString addonId, std::shared_ptr<QByteA
return netJob;
}
Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args, DependencySearchCallbacks&& callbacks) const
{
auto versions_url_optional = getDependencyURL(args);
if (!versions_url_optional.has_value())
return nullptr;
auto versions_url = versions_url_optional.value();
auto netJob = makeShared<NetJob>(QString("%1::Dependency").arg(args.dependency.addonId.toString()), APPLICATION->network());
auto response = std::make_shared<QByteArray>();
netJob->addNetAction(Net::Download::makeByteArray(versions_url, response));
QObject::connect(netJob.get(), &NetJob::succeeded, [=] {
QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
qWarning() << "Error while parsing JSON response for getting versions at " << parse_error.offset
<< " reason: " << parse_error.errorString();
qWarning() << *response;
return;
}
callbacks.on_succeed(doc, args.dependency);
});
return netJob;
};

View File

@@ -15,9 +15,11 @@ class NetworkResourceAPI : public ResourceAPI {
Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const override;
Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const override;
Task::Ptr getDependencyVersion(DependencySearchArgs&&, DependencySearchCallbacks&&) const override;
protected:
[[nodiscard]] virtual auto getSearchURL(SearchArgs const& args) const -> std::optional<QString> = 0;
[[nodiscard]] virtual auto getInfoURL(QString const& id) const -> std::optional<QString> = 0;
[[nodiscard]] virtual auto getVersionsURL(VersionSearchArgs const& args) const -> std::optional<QString> = 0;
[[nodiscard]] virtual auto getDependencyURL(DependencySearchArgs const& args) const -> std::optional<QString> = 0;
};

View File

@@ -37,16 +37,16 @@
#include <QtConcurrent>
#include "MMCZip.h"
#include "BaseInstance.h"
#include "FileSystem.h"
#include "MMCZip.h"
#include "minecraft/GradleSpecifier.h"
#include "settings/INISettingsObject.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
#include "settings/INISettingsObject.h"
#include "minecraft/GradleSpecifier.h"
#include "Application.h"
#include "BuildConfig.h"
#include "Application.h"
namespace LegacyFTB {
@@ -65,7 +65,6 @@ void PackInstallTask::executeTask()
void PackInstallTask::downloadPack()
{
setStatus(tr("Downloading zip for %1").arg(m_pack.name));
setProgress(1, 4);
setAbortable(false);
archivePath = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file);
@@ -79,10 +78,11 @@ void PackInstallTask::downloadPack()
}
netJobContainer->addNetAction(Net::Download::makeFile(url, archivePath));
connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip);
connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed);
connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded);
connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed);
connect(netJobContainer.get(), &NetJob::progress, this, &PackInstallTask::onDownloadProgress);
connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::emitAborted);
connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::onDownloadAborted);
netJobContainer->start();
@@ -90,6 +90,27 @@ void PackInstallTask::downloadPack()
progress(1, 4);
}
void PackInstallTask::onDownloadSucceeded()
{
unzip();
}
void PackInstallTask::onDownloadFailed(QString reason)
{
emitFailed(reason);
}
void PackInstallTask::onDownloadProgress(qint64 current, qint64 total)
{
progress(current, total * 4);
setStatus(tr("Downloading zip for %1 (%2%)").arg(m_pack.name).arg(current / 10));
}
void PackInstallTask::onDownloadAborted()
{
emitAborted();
}
void PackInstallTask::unzip()
{
setStatus(tr("Extracting modpack"));
@@ -99,17 +120,16 @@ void PackInstallTask::unzip()
QDir extractDir(m_stagingPath);
m_packZip.reset(new QuaZip(archivePath));
if (!m_packZip->open(QuaZip::mdUnzip)) {
if(!m_packZip->open(QuaZip::mdUnzip))
{
emitFailed(tr("Failed to open modpack file %1!").arg(archivePath));
return;
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload<QString, QString>::of(MMCZip::extractDir), archivePath,
extractDir.absolutePath() + "/unzip");
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload<QString, QString>::of(MMCZip::extractDir), archivePath, extractDir.absolutePath() + "/unzip");
#else
m_extractFuture =
QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/unzip");
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/unzip");
#endif
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &PackInstallTask::onUnzipFinished);
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &PackInstallTask::onUnzipCanceled);
@@ -131,9 +151,11 @@ void PackInstallTask::install()
setStatus(tr("Installing modpack"));
progress(3, 4);
QDir unzipMcDir(m_stagingPath + "/unzip/minecraft");
if (unzipMcDir.exists()) {
// ok, found minecraft dir, move contents to instance dir
if (!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/.minecraft")) {
if(unzipMcDir.exists())
{
//ok, found minecraft dir, move contents to instance dir
if(!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/.minecraft"))
{
emitFailed(tr("Failed to move unzipped Minecraft!"));
return;
}
@@ -150,20 +172,23 @@ void PackInstallTask::install()
bool fallback = true;
// handle different versions
//handle different versions
QFile packJson(m_stagingPath + "/.minecraft/pack.json");
QDir jarmodDir = QDir(m_stagingPath + "/unzip/instMods");
if (packJson.exists()) {
if(packJson.exists())
{
packJson.open(QIODevice::ReadOnly | QIODevice::Text);
QJsonDocument doc = QJsonDocument::fromJson(packJson.readAll());
packJson.close();
// we only care about the libs
//we only care about the libs
QJsonArray libs = doc.object().value("libraries").toArray();
foreach (const QJsonValue& value, libs) {
foreach (const QJsonValue &value, libs)
{
QString nameValue = value.toObject().value("name").toString();
if (!nameValue.startsWith("net.minecraftforge")) {
if(!nameValue.startsWith("net.minecraftforge"))
{
continue;
}
@@ -174,13 +199,16 @@ void PackInstallTask::install()
fallback = false;
break;
}
}
if (jarmodDir.exists()) {
if(jarmodDir.exists())
{
qDebug() << "Found jarmods, installing...";
QStringList jarmods;
for (auto info : jarmodDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) {
for (auto info: jarmodDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
{
qDebug() << "Jarmod:" << info.fileName();
jarmods.push_back(info.absoluteFilePath());
}
@@ -189,11 +217,12 @@ void PackInstallTask::install()
fallback = false;
}
// just nuke unzip directory, it s not needed anymore
//just nuke unzip directory, it s not needed anymore
FS::deletePath(m_stagingPath + "/unzip");
if (fallback) {
// TODO: Some fallback mechanism... or just keep failing!
if(fallback)
{
//TODO: Some fallback mechanism... or just keep failing!
emitFailed(tr("No installation method found!"));
return;
}
@@ -203,7 +232,8 @@ void PackInstallTask::install()
progress(4, 4);
instance.setName(name());
if (m_instIcon == "default") {
if(m_instIcon == "default")
{
m_instIcon = "ftb_logo";
}
instance.setIconKey(m_instIcon);
@@ -222,4 +252,4 @@ bool PackInstallTask::abort()
return InstanceTask::abort();
}
} // namespace LegacyFTB
}

View File

@@ -1,12 +1,12 @@
#pragma once
#include "InstanceTask.h"
#include "net/NetJob.h"
#include <quazip/quazip.h>
#include <quazip/quazipdir.h>
#include "InstanceTask.h"
#include "PackHelpers.h"
#include "meta/Index.h"
#include "meta/Version.h"
#include "meta/VersionList.h"
#include "net/NetJob.h"
#include "PackHelpers.h"
#include "net/NetJob.h"
@@ -14,31 +14,36 @@
namespace LegacyFTB {
class PackInstallTask : public InstanceTask {
class PackInstallTask : public InstanceTask
{
Q_OBJECT
public:
public:
explicit PackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, Modpack pack, QString version);
virtual ~PackInstallTask() {}
virtual ~PackInstallTask(){}
bool canAbort() const override { return true; }
bool abort() override;
protected:
protected:
//! Entry point for tasks.
virtual void executeTask() override;
private:
private:
void downloadPack();
void unzip();
void install();
private slots:
private slots:
void onDownloadSucceeded();
void onDownloadFailed(QString reason);
void onDownloadProgress(qint64 current, qint64 total);
void onDownloadAborted();
void onUnzipFinished();
void onUnzipCanceled();
private: /* data */
private: /* data */
shared_qobject_ptr<QNetworkAccessManager> m_network;
bool abortable = false;
std::unique_ptr<QuaZip> m_packZip;
@@ -51,4 +56,4 @@ class PackInstallTask : public InstanceTask {
QString m_version;
};
} // namespace LegacyFTB
}

View File

@@ -38,7 +38,7 @@ class ModrinthAPI : public NetworkResourceAPI {
static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList
{
QStringList l;
for (auto loader : { Forge, Fabric, Quilt, LiteLoader }) {
for (auto loader : { Forge, Fabric, Quilt }) {
if (types & loader) {
l << getModLoaderString(loader);
}
@@ -51,8 +51,7 @@ class ModrinthAPI : public NetworkResourceAPI {
static auto getModLoaderFilters(ModLoaderTypes types) -> const QString
{
QStringList l;
for (auto loader : getModLoaderStrings(types))
{
for (auto loader : getModLoaderStrings(types)) {
l << QString("\"categories:%1\"").arg(loader);
}
return l.join(',');
@@ -93,7 +92,7 @@ class ModrinthAPI : public NetworkResourceAPI {
{
if (args.loaders.has_value()) {
if (!validateModLoaders(args.loaders.value())) {
qWarning() << "Modrinth - or our interface - does not support any the provided mod loaders!";
qWarning() << "Modrinth only have Forge and Fabric-compatible mods!";
return {};
}
}
@@ -135,13 +134,22 @@ class ModrinthAPI : public NetworkResourceAPI {
auto getGameVersionsArray(std::list<Version> mcVersions) const -> QString
{
QString s;
for(auto& ver : mcVersions){
for (auto& ver : mcVersions) {
s += QString("\"versions:%1\",").arg(ver.toString());
}
s.remove(s.length() - 1, 1); //remove last comma
s.remove(s.length() - 1, 1); // remove last comma
return s.isEmpty() ? QString() : s;
}
static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt | LiteLoader); }
inline auto validateModLoaders(ModLoaderTypes loaders) const -> bool { return loaders & (Forge | Fabric | Quilt); }
[[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
{
return args.dependency.version.length() != 0 ? QString("%1/version/%2").arg(BuildConfig.MODRINTH_PROD_URL, args.dependency.version)
: QString("%1/project/%2/version?game_versions=[\"%3\"]&loaders=[\"%4\"]")
.arg(BuildConfig.MODRINTH_PROD_URL)
.arg(args.dependency.addonId.toString())
.arg(args.mcVersion.toString())
.arg(getModLoaderStrings(args.loader).join("\",\""));
};
};

View File

@@ -263,13 +263,13 @@ void ModrinthPackExportTask::finish()
QByteArray ModrinthPackExportTask::generateIndex()
{
QJsonObject out;
out["formatVersion"] = 1;
out["game"] = "minecraft";
out["name"] = name;
out["versionId"] = version;
QJsonObject obj;
obj["formatVersion"] = 1;
obj["game"] = "minecraft";
obj["name"] = name;
obj["versionId"] = version;
if (!summary.isEmpty())
out["summary"] = summary;
obj["summary"] = summary;
if (mcInstance) {
auto profile = mcInstance->getPackProfile();
@@ -290,40 +290,30 @@ QByteArray ModrinthPackExportTask::generateIndex()
if (forge != nullptr)
dependencies["forge"] = forge->m_version;
out["dependencies"] = dependencies;
obj["dependencies"] = dependencies;
}
QJsonArray filesOut;
for (auto iterator = resolvedFiles.constBegin(); iterator != resolvedFiles.constEnd(); iterator++) {
QJsonObject fileOut;
QJsonArray files;
QMapIterator<QString, ResolvedFile> iterator(resolvedFiles);
while (iterator.hasNext()) {
iterator.next();
QString path = iterator.key();
const ResolvedFile& value = iterator.value();
// detect disabled mod
const QFileInfo pathInfo(path);
if (pathInfo.suffix() == "disabled") {
// rename it
path = pathInfo.dir().filePath(pathInfo.completeBaseName());
// ...and make it optional
QJsonObject env;
env["client"] = "optional";
env["server"] = "optional";
fileOut["env"] = env;
}
fileOut["path"] = path;
fileOut["downloads"] = QJsonArray{ iterator.value().url };
QJsonObject file;
file["path"] = iterator.key();
file["downloads"] = QJsonArray({ iterator.value().url });
QJsonObject hashes;
hashes["sha1"] = value.sha1;
hashes["sha512"] = value.sha512;
fileOut["hashes"] = hashes;
fileOut["fileSize"] = value.size;
filesOut << fileOut;
file["hashes"] = hashes;
file["fileSize"] = value.size;
files << file;
}
out["files"] = filesOut;
obj["files"] = files;
return QJsonDocument(out).toJson(QJsonDocument::Compact);
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
}

View File

@@ -22,7 +22,7 @@
#include "Json.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
#include "net/NetJob.h"
#include "modplatform/ModIndex.h"
static ModrinthAPI api;
static ModPlatform::ProviderCapabilities ProviderCaps;
@@ -140,6 +140,28 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
file.version_number = Json::requireString(obj, "version_number");
file.changelog = Json::requireString(obj, "changelog");
auto dependencies = Json::ensureArray(obj, "dependencies");
for (auto d : dependencies) {
auto dep = Json::ensureObject(d);
ModPlatform::Dependency dependency;
dependency.addonId = Json::ensureString(dep, "project_id");
dependency.version = Json::ensureString(dep, "version_id");
auto depType = Json::requireString(dep, "dependency_type");
if (depType == "required")
dependency.type = ModPlatform::DependencyType::REQUIRED;
else if (depType == "optional")
dependency.type = ModPlatform::DependencyType::OPTIONAL;
else if (depType == "incompatible")
dependency.type = ModPlatform::DependencyType::INCOMPATIBLE;
else if (depType == "embedded")
dependency.type = ModPlatform::DependencyType::EMBEDDED;
else
dependency.type = ModPlatform::DependencyType::UNKNOWN;
file.dependencies.append(dependency);
}
auto files = Json::requireArray(obj, "files");
int i = 0;
@@ -195,3 +217,22 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
return {};
}
auto Modrinth::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion
{
QVector<ModPlatform::IndexedVersion> versions;
for (auto versionIter : arr) {
auto obj = versionIter.toObject();
auto file = loadIndexedPackVersion(obj);
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
versions.append(file);
}
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
// dates are in RFC 3339 format
return a.date > b.date;
};
std::sort(versions.begin(), versions.end(), orderSortPredicate);
return versions.length() != 0 ? versions.front() : ModPlatform::IndexedVersion();
}

View File

@@ -19,8 +19,8 @@
#include "modplatform/ModIndex.h"
#include "BaseInstance.h"
#include <QNetworkAccessManager>
#include "BaseInstance.h"
namespace Modrinth {
@@ -31,5 +31,6 @@ void loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
const shared_qobject_ptr<QNetworkAccessManager>& network,
const BaseInstance* inst);
auto loadIndexedPackVersion(QJsonObject& obj, QString hash_type = "sha512", QString filename_prefer = "") -> ModPlatform::IndexedVersion;
auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion;
} // namespace Modrinth

View File

@@ -42,7 +42,6 @@
#include <QDir>
#include <QLibraryInfo>
#include <QDebug>
#include <locale>
#include "FileSystem.h"
#include "net/NetJob.h"
@@ -455,7 +454,6 @@ QVariant TranslationsModel::data(const QModelIndex& index, int role) const
return QString("%1%").arg(lang.percentTranslated(), 3, 'f', 1);
}
}
qWarning("TranslationModel::data not implemented when role is DisplayRole");
}
case Qt::ToolTipRole:
{
@@ -528,34 +526,34 @@ Language * TranslationsModel::findLanguage(const QString& key)
}
}
void TranslationsModel::setUseSystemLocale(bool useSystemLocale)
{
APPLICATION->settings()->set("UseSystemLocale", useSystemLocale);
QLocale::setDefault(QLocale(useSystemLocale ? QString::fromStdString(std::locale().name()) : defaultLangCode));
}
bool TranslationsModel::selectLanguage(QString key)
{
QString& langCode = key;
QString &langCode = key;
auto langPtr = findLanguage(key);
if (langCode.isEmpty()) {
if (langCode.isEmpty())
{
d->no_language_set = true;
}
if (!langPtr) {
if(!langPtr)
{
qWarning() << "Selected invalid language" << key << ", defaulting to" << defaultLangCode;
langCode = defaultLangCode;
} else {
}
else
{
langCode = langPtr->key;
}
// uninstall existing translators if there are any
if (d->m_app_translator) {
if (d->m_app_translator)
{
QCoreApplication::removeTranslator(d->m_app_translator.get());
d->m_app_translator.reset();
}
if (d->m_qt_translator) {
if (d->m_qt_translator)
{
QCoreApplication::removeTranslator(d->m_qt_translator.get());
d->m_qt_translator.reset();
}
@@ -565,9 +563,8 @@ bool TranslationsModel::selectLanguage(QString key)
* In a multithreaded application, the default locale should be set at application startup, before any non-GUI threads are created.
* This function is not reentrant.
*/
QLocale::setDefault(
QLocale(APPLICATION->settings()->get("UseSystemLocale").toBool() ? QString::fromStdString(std::locale().name()) : langCode));
QLocale locale = QLocale(langCode);
QLocale::setDefault(locale);
// if it's the default UI language, finish
if(langCode == defaultLangCode)

View File

@@ -20,16 +20,17 @@
struct Language;
class TranslationsModel : public QAbstractListModel {
class TranslationsModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit TranslationsModel(QString path, QObject* parent = 0);
public:
explicit TranslationsModel(QString path, QObject *parent = 0);
virtual ~TranslationsModel();
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex & parent) const override;
bool selectLanguage(QString key);
void updateLanguage(QString key);
@@ -37,27 +38,27 @@ class TranslationsModel : public QAbstractListModel {
QString selectedLanguage();
void downloadIndex();
void setUseSystemLocale(bool useSystemLocale);
private:
Language* findLanguage(const QString& key);
private:
Language *findLanguage(const QString & key);
void reloadLocalFiles();
void downloadTranslation(QString key);
void downloadNext();
// hide copy constructor
TranslationsModel(const TranslationsModel&) = delete;
TranslationsModel(const TranslationsModel &) = delete;
// hide assign op
TranslationsModel& operator=(const TranslationsModel&) = delete;
TranslationsModel &operator=(const TranslationsModel &) = delete;
private slots:
private slots:
void indexReceived();
void indexFailed(QString reason);
void dlFailed(QString reason);
void dlGood();
void translationDirChanged(const QString& path);
void translationDirChanged(const QString &path);
private: /* data */
private: /* data */
struct Private;
std::unique_ptr<Private> d;
};

View File

@@ -1279,17 +1279,7 @@ void MainWindow::globalSettingsClosed()
void MainWindow::on_actionEditInstance_triggered()
{
if (!m_selectedInstance)
return;
if (m_selectedInstance->canEdit()) {
APPLICATION->showInstanceWindow(m_selectedInstance);
} else {
CustomMessageBox::selectable(this, tr("Instance not editable"),
tr("This instance is not editable. It may be broken, invalid, or too old. Check logs for details."),
QMessageBox::Critical)->show();
}
APPLICATION->showInstanceWindow(m_selectedInstance);
}
void MainWindow::on_actionManageAccounts_triggered()

View File

@@ -34,7 +34,6 @@
*/
#include "ProgressDialog.h"
#include <QPoint>
#include "ui_ProgressDialog.h"
#include <limits>
@@ -67,9 +66,8 @@ ProgressDialog::ProgressDialog(QWidget* parent) : QDialog(parent), ui(new Ui::Pr
ui->taskProgressScrollArea->setHidden(true);
this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
setAttribute(Qt::WidgetAttribute::WA_QuitOnClose, true);
changeProgress(0, 100);
updateSize(true);
setSkipButton(false);
changeProgress(0, 100);
}
void ProgressDialog::setSkipButton(bool present, QString label)
@@ -95,39 +93,25 @@ ProgressDialog::~ProgressDialog()
delete ui;
}
void ProgressDialog::updateSize(bool recenterParent)
void ProgressDialog::updateSize()
{
QSize lastSize = this->size();
QPoint lastPos = this->pos();
int minHeight = ui->globalStatusDetailsLabel->minimumSize().height() + (ui->verticalLayout->spacing() * 2);
minHeight += ui->globalProgressBar->minimumSize().height() + ui->verticalLayout->spacing();
if (!ui->taskProgressScrollArea->isHidden())
minHeight += ui->taskProgressScrollArea->minimumSizeHint().height() + ui->verticalLayout->spacing();
if (ui->skipButton->isVisible())
minHeight += ui->skipButton->height() + ui->verticalLayout->spacing();
minHeight = std::max(minHeight, 60);
QSize minSize = QSize(480, minHeight);
QSize qSize = QSize(480, minimumSizeHint().height());
setMinimumSize(minSize);
adjustSize();
QSize newSize = this->size();
// if the current window is a different size
auto parent = this->parentWidget();
if (recenterParent && parent) {
auto newX = std::max(0, parent->x() + ((parent->width() - newSize.width()) / 2));
auto newY = std::max(0, parent->y() + ((parent->height() - newSize.height()) / 2));
this->move(newX, newY);
}
else if (lastSize != newSize)
// if the current window is too small
if ((lastSize != qSize) && (lastSize.height() < qSize.height()))
{
// center on old position after resize
QSize sizeDiff = lastSize - newSize; // last size was smaller, the results should be negative
auto newX = std::max(0, lastPos.x() + (sizeDiff.width() / 2));
auto newY = std::max(0, lastPos.y() + (sizeDiff.height() / 2));
this->move(newX, newY);
resize(qSize);
// keep the dialog in the center after a resize
this->move(
this->parentWidget()->x() + (this->parentWidget()->width() - this->width()) / 2,
this->parentWidget()->y() + (this->parentWidget()->height() - this->height()) / 2
);
}
setMinimumSize(qSize);
}
int ProgressDialog::execWithTask(Task* task)
@@ -217,9 +201,7 @@ void ProgressDialog::onTaskSucceeded()
void ProgressDialog::changeStatus(const QString& status)
{
ui->globalStatusLabel->setText(task->getStatus());
ui->globalStatusLabel->adjustSize();
ui->globalStatusDetailsLabel->setText(task->getDetails());
ui->globalStatusDetailsLabel->adjustSize();
updateSize();
}

View File

@@ -62,7 +62,7 @@ public:
explicit ProgressDialog(QWidget *parent = 0);
~ProgressDialog();
void updateSize(bool recenterParent = false);
void updateSize();
int execWithTask(Task* task);
int execWithTask(std::unique_ptr<Task> &&task);

View File

@@ -18,6 +18,8 @@
*/
#include "ResourceDownloadDialog.h"
#include <QEventLoop>
#include <QList>
#include <QPushButton>
#include <algorithm>
@@ -30,6 +32,10 @@
#include "minecraft/mod/ShaderPackFolderModel.h"
#include "minecraft/mod/TexturePackFolderModel.h"
#include "minecraft/mod/tasks/GetModDependenciesTask.h"
#include "modplatform/ModIndex.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/ProgressDialog.h"
#include "ui/dialogs/ReviewMessageBox.h"
#include "ui/pages/modplatform/ResourcePage.h"
@@ -37,8 +43,6 @@
#include "ui/pages/modplatform/flame/FlameResourcePages.h"
#include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h"
#include "modplatform/flame/FlameAPI.h"
#include "modplatform/modrinth/ModrinthAPI.h"
#include "ui/widgets/PageContainer.h"
namespace ResourceDownload {
@@ -119,18 +123,71 @@ void ResourceDownloadDialog::connectButtons()
connect(HelpButton, &QPushButton::clicked, m_container, &PageContainer::help);
}
static ModPlatform::ProviderCapabilities ProviderCaps;
QStringList getRequiredBy(QList<ResourceDownloadDialog::DownloadTaskPtr> tasks, ResourceDownloadDialog::DownloadTaskPtr pack)
{
auto addonId = pack->getPack()->addonId;
auto provider = pack->getPack()->provider;
auto version = pack->getVersionID();
auto req = QStringList();
for (auto& task : tasks) {
if (provider != task->getPack()->provider)
continue;
auto deps = task->getVersion().dependencies;
if (auto dep = std::find_if(deps.begin(), deps.end(),
[addonId, provider, version](const ModPlatform::Dependency& d) {
return d.type == ModPlatform::DependencyType::REQUIRED &&
(provider == ModPlatform::ResourceProvider::MODRINTH && d.addonId.toString().isEmpty()
? version == d.version
: d.addonId == addonId);
});
dep != deps.end()) {
req.append(task->getName());
}
}
return req;
}
void ResourceDownloadDialog::confirm()
{
auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString()));
confirm_dialog->retranslateUi(resourcesString());
if (auto task = getModDependenciesTask(); task) {
connect(task.get(), &Task::failed, this,
[&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
connect(task.get(), &Task::succeeded, this, [&]() {
QStringList warnings = task->warnings();
if (warnings.count()) {
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec();
}
});
// Check for updates
ProgressDialog progress_dialog(this);
progress_dialog.setSkipButton(true, tr("Abort"));
progress_dialog.setWindowTitle(tr("Checking for dependencies..."));
auto ret = progress_dialog.execWithTask(task.get());
// If the dialog was skipped / some download error happened
if (ret == QDialog::DialogCode::Rejected) {
QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
return;
} else {
for (auto dep : task->getDependecies())
addResource(dep->pack, dep->version);
}
}
auto selected = getTasks();
std::sort(selected.begin(), selected.end(), [](const DownloadTaskPtr& a, const DownloadTaskPtr& b) {
return QString::compare(a->getName(), b->getName(), Qt::CaseInsensitive) < 0;
});
auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString()));
confirm_dialog->retranslateUi(resourcesString());
for (auto& task : selected) {
confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath() });
confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath(),
ProviderCaps.name(task->getProvider()), getRequiredBy(selected, task) });
}
if (confirm_dialog->exec()) {
@@ -224,11 +281,8 @@ QList<BasePage*> ModDownloadDialog::getPages()
{
QList<BasePage*> pages;
auto loaders = static_cast<MinecraftInstance*>(m_instance)->getPackProfile()->getModLoaders().value();
if (ModrinthAPI::validateModLoaders(loaders))
pages.append(ModrinthModPage::create(this, *m_instance));
if (APPLICATION->capabilities() & Application::SupportsFlame && FlameAPI::validateModLoaders(loaders))
pages.append(ModrinthModPage::create(this, *m_instance));
if (APPLICATION->capabilities() & Application::SupportsFlame)
pages.append(FlameModPage::create(this, *m_instance));
m_selectedPage = dynamic_cast<ModPage*>(pages[0]);
@@ -236,6 +290,19 @@ QList<BasePage*> ModDownloadDialog::getPages()
return pages;
}
GetModDependenciesTask::Ptr ModDownloadDialog::getModDependenciesTask()
{
if (auto model = dynamic_cast<ModFolderModel*>(getBaseModel().get()); model) {
QList<std::shared_ptr<GetModDependenciesTask::PackDependency>> selectedVers;
for (auto& selected : getTasks()) {
selectedVers.append(std::make_shared<GetModDependenciesTask::PackDependency>(selected->getPack(), selected->getVersion()));
}
return makeShared<GetModDependenciesTask>(this, m_instance, model, selectedVers);
}
return nullptr;
};
ResourcePackDownloadDialog::ResourcePackDownloadDialog(QWidget* parent,
const std::shared_ptr<ResourcePackFolderModel>& resource_packs,
BaseInstance* instance)

View File

@@ -25,6 +25,7 @@
#include <QLayout>
#include "QObjectPtr.h"
#include "minecraft/mod/tasks/GetModDependenciesTask.h"
#include "modplatform/ModIndex.h"
#include "ui/pages/BasePageProvider.h"
@@ -81,6 +82,8 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider {
[[nodiscard]] virtual QString geometrySaveKey() const { return ""; }
void setButtonStatus();
[[nodiscard]] virtual GetModDependenciesTask::Ptr getModDependenciesTask() { return nullptr; }
protected:
const std::shared_ptr<ResourceFolderModel> m_base_model;
@@ -103,6 +106,7 @@ class ModDownloadDialog final : public ResourceDownloadDialog {
[[nodiscard]] QString geometrySaveKey() const override { return "ModDownloadGeometry"; }
QList<BasePage*> getPages() override;
GetModDependenciesTask::Ptr getModDependenciesTask() override;
private:
BaseInstance* m_instance;

View File

@@ -40,7 +40,8 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info)
auto filenameItem = new QTreeWidgetItem(itemTop);
filenameItem->setText(0, tr("Filename: %1").arg(info.filename));
itemTop->insertChildren(0, { filenameItem });
auto childIndx = 0;
itemTop->insertChildren(childIndx++, { filenameItem });
if (!info.custom_file_path.isEmpty()) {
auto customPathItem = new QTreeWidgetItem(itemTop);
@@ -49,7 +50,31 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info)
itemTop->insertChildren(1, { customPathItem });
itemTop->setIcon(1, QIcon(APPLICATION->getThemedIcon("status-yellow")));
itemTop->setToolTip(1, tr("This file will be downloaded to a folder location different from the default, possibly due to its loader requiring it."));
itemTop->setToolTip(
childIndx++,
tr("This file will be downloaded to a folder location different from the default, possibly due to its loader requiring it."));
}
auto providerItem = new QTreeWidgetItem(itemTop);
providerItem->setText(0, tr("Provider: %1").arg(info.provider));
itemTop->insertChildren(childIndx++, { providerItem });
if (!info.required_by.isEmpty()) {
auto requiredByItem = new QTreeWidgetItem(itemTop);
if (info.required_by.length() == 1) {
requiredByItem->setText(0, tr("Required by: %1").arg(info.required_by.back()));
} else {
requiredByItem->setText(0, tr("Required by:"));
auto i = 0;
for (auto req : info.required_by) {
auto reqItem = new QTreeWidgetItem(requiredByItem);
reqItem->setText(0, req);
reqItem->insertChildren(i++, { reqItem });
}
}
itemTop->insertChildren(childIndx++, { requiredByItem });
}
ui->modTreeWidget->addTopLevelItem(itemTop);

View File

@@ -13,9 +13,11 @@ class ReviewMessageBox : public QDialog {
static auto create(QWidget* parent, QString&& title, QString&& icon = "") -> ReviewMessageBox*;
using ResourceInformation = struct res_info {
QString name;
QString filename;
QString custom_file_path {};
QString name;
QString filename;
QString custom_file_path{};
QString provider;
QStringList required_by;
};
void appendResource(ResourceInformation&& info);

View File

@@ -1,16 +1,36 @@
/* Copyright 2013-2021 MultiMC Contributors
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
*
* 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
* 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.
*
* http://www.apache.org/licenses/LICENSE-2.0
* 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.
*
* 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.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VersionSelectDialog.h"
@@ -22,15 +42,10 @@
#include <QtWidgets/QVBoxLayout>
#include <QDebug>
#include "ui/dialogs/ProgressDialog.h"
#include "ui/widgets/VersionSelectWidget.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "BaseVersion.h"
#include "BaseVersionList.h"
#include "tasks/Task.h"
#include "Application.h"
#include "VersionProxyModel.h"
VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent, bool cancelable)
: QDialog(parent)
@@ -40,7 +55,7 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title,
m_verticalLayout = new QVBoxLayout(this);
m_verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
m_versionWidget = new VersionSelectWidget(parent);
m_versionWidget = new VersionSelectWidget(true, parent);
m_verticalLayout->addWidget(m_versionWidget);
m_horizontalLayout = new QHBoxLayout();

View File

@@ -248,8 +248,8 @@ bool AccessibleInstanceView::selectColumn(int column)
if (view()->selectionBehavior() != QAbstractItemView::SelectColumns && rowCount() > 1) {
return false;
}
// fallthrough intentional
}
/* fallthrough */
case QAbstractItemView::ContiguousSelection: {
if ((!column || !view()->selectionModel()->isColumnSelected(column - 1, view()->rootIndex())) && !view()->selectionModel()->isColumnSelected(column + 1, view()->rootIndex())) {
view()->clearSelection();

View File

@@ -35,15 +35,16 @@
#pragma once
#include <QString>
#include <QIcon>
#include <QString>
#include <functional>
#include <memory>
#include "BasePageContainer.h"
class BasePage
{
public:
class BasePage {
public:
using updateExtraInfoFunc = std::function<void(QString, QString)>;
virtual ~BasePage() {}
virtual QString id() const = 0;
virtual QString displayName() const = 0;
@@ -63,17 +64,16 @@ public:
}
virtual void openedImpl() {}
virtual void closedImpl() {}
virtual void setParentContainer(BasePageContainer * container)
{
m_container = container;
};
virtual void retranslate() { }
virtual void setParentContainer(BasePageContainer* container) { m_container = container; };
virtual void retranslate() {}
public:
public:
int stackIndex = -1;
int listIndex = -1;
protected:
BasePageContainer * m_container = nullptr;
updateExtraInfoFunc updateExtraInfo;
protected:
BasePageContainer* m_container = nullptr;
bool isOpened = false;
};

View File

@@ -81,8 +81,6 @@ APIPage::APIPage(QWidget *parent) :
connect(ui->pasteTypeComboBox, currentIndexChangedSignal, this, &APIPage::updateBaseURLPlaceholder);
// This function needs to be called even when the ComboBox's index is still in its default state.
updateBaseURLPlaceholder(ui->pasteTypeComboBox->currentIndex());
// NOTE: this allows http://, but we replace that with https later anyway
ui->metaURL->setValidator(new QRegularExpressionValidator(validUrlRegExp, ui->metaURL));
ui->baseURLEntry->setValidator(new QRegularExpressionValidator(validUrlRegExp, ui->baseURLEntry));
ui->msaClientID->setValidator(new QRegularExpressionValidator(validMSAClientID, ui->msaClientID));
ui->flameKey->setValidator(new QRegularExpressionValidator(validFlameKey, ui->flameKey));
@@ -165,7 +163,7 @@ void APIPage::applySettings()
QString msaClientID = ui->msaClientID->text();
s->set("MSAClientIDOverride", msaClientID);
QUrl metaURL(ui->metaURL->text());
QUrl metaURL = ui->metaURL->text();
// Add required trailing slash
if (!metaURL.isEmpty() && !metaURL.path().endsWith('/'))
{

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