Compare commits

...

103 Commits

Author SHA1 Message Date
Sefa Eyeoglu
770d5c92bc
Merge pull request #1453 from PrismLauncher/backport-1437-to-release-7.x 2023-07-27 09:42:42 +02:00
LostLuma
f326db11f1 Ignore cache files entirely, also apply to modpack export
Signed-off-by: LostLuma <lilly@lostluma.net>
(cherry picked from commit 4a9ea832ff)
2023-07-27 07:40:51 +00:00
LostLuma
f8c6a33134 Address review comments
Signed-off-by: LostLuma <lilly@lostluma.net>
(cherry picked from commit a9fefb2eeb)
2023-07-27 07:40:51 +00:00
LostLuma
64228bdddf Ignore cache files when exporting instances
Signed-off-by: LostLuma <lilly@lostluma.net>
(cherry picked from commit 361583edfc)
2023-07-27 07:40:51 +00:00
Sefa Eyeoglu
88be40c4bf
Merge pull request #1444 from PrismLauncher/backport-1438-to-release-7.x 2023-07-26 20:48:01 +02:00
Sefa Eyeoglu
d6095358ad fix: fix typo in README
Co-authored-by: Tayou <31988415+TayouVR@users.noreply.github.com>
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit 4ad448993c)
2023-07-26 18:30:21 +00:00
Sefa Eyeoglu
f7f2049223 chore: update information for downstreams
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit 6f652e1d2f)
2023-07-26 18:30:21 +00:00
Sefa Eyeoglu
cc43485650
Merge pull request #1442 from PrismLauncher/backport-1434-to-release-7.x 2023-07-26 16:23:21 +02:00
seth
b92d617df0 chore(ci): remove nix job
we're using garnix now

Signed-off-by: seth <getchoo@tuta.io>
(cherry picked from commit fbf29274e8)
2023-07-26 13:47:58 +00:00
seth
7b971a08a8 feat(ci): init garnix.yaml
Signed-off-by: seth <getchoo@tuta.io>
(cherry picked from commit 3c35d647b8)
2023-07-26 13:47:58 +00:00
Sefa Eyeoglu
7cc3d34498
Merge pull request #1441 from PrismLauncher/backport-1427-to-release-7.x 2023-07-26 14:46:53 +02:00
tjw123hh
d9df60368c
Delete some incorrect notr="true" tags
Signed-off-by: tjw123hh <tjw123hh@outlook.com>
(cherry picked from commit 280f041acb)
2023-07-26 14:45:59 +02:00
tjw123hh
ba49afcc6a
Update org.prismlauncher.PrismLauncher.desktop.in
Signed-off-by: tjw123hh <56749271+tjw123hh@users.noreply.github.com>
(cherry picked from commit 4ab630b832)
2023-07-26 14:45:59 +02:00
tjw123hh
685badb8cf
DCO Remediation Commit for tjw123hh <tjw123hh@outlook.com>
I, tjw123hh <tjw123hh@outlook.com>, hereby add my Signed-off-by to this commit: 6a01c277e8

Signed-off-by: tjw123hh <tjw123hh@outlook.com>
(cherry picked from commit a32ca43288)
2023-07-26 14:45:50 +02:00
tjw123hh
29fde6a322
Delete some incorrect tags
(cherry picked from commit 6a01c277e8)
2023-07-26 14:45:42 +02:00
Sefa Eyeoglu
e65d48bf36
Merge pull request #1440 from PrismLauncher/backport-1420-to-release-7.x 2023-07-26 14:41:17 +02:00
Sefa Eyeoglu
38144b3661
Merge pull request #1439 from PrismLauncher/backport-1419-to-release-7.x 2023-07-26 14:41:08 +02:00
github-actions[bot]
3a0e30684e chore(nix): update lockfile
Flake lock file updates:

• Updated input 'libnbtplusplus':
    'github:PrismLauncher/libnbtplusplus/2203af7eeb48c45398139b583615134efd8d407f' (2022-04-15)
  → 'github:PrismLauncher/libnbtplusplus/a5e8fd52b8bf4ab5d5bcc042b2a247867589985f' (2023-07-22)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/46ed466081b9cad1125b11f11a2af5cc40b942c7' (2023-07-15)
  → 'github:nixos/nixpkgs/f465da166263bc0d4b39dfd4ca28b777c92d4b73' (2023-07-22)
• Updated input 'pre-commit-hooks':
    'github:cachix/pre-commit-hooks.nix/5e28316db471d1ac234beb70031b635437421dd6' (2023-07-14)
  → 'github:cachix/pre-commit-hooks.nix/eb433bff05b285258be76513add6f6c57b441775' (2023-07-18)

(cherry picked from commit 6f6b0b9661)
2023-07-26 12:40:00 +00:00
PandaNinjas
0fe184251d Update libnbtplusplus submodule
Signed-off-by: PandaNinjas <admin@malwarefight.wip.la>
(cherry picked from commit 2253100ac6)
2023-07-26 12:39:57 +00:00
Tayou
f51a66dad5
Merge pull request #1432 from TheKodeToad/revert-revert-hide-provider-column 2023-07-25 14:32:07 +02:00
TheKodeToad
071e86fe68 Revert "Revert "feat(Mods): hide 'Provider' column when no mods have providers""
This reverts commit 5eb71fc6a9.

Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-07-25 12:27:50 +01:00
Sefa Eyeoglu
d7a02ee456
Merge pull request #1428 from PrismLauncher/backport-1426-to-release-7.x 2023-07-24 20:42:05 +02:00
TheKodeToad
648a69594f Fix token "0" being replaced
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
(cherry picked from commit f505d43fc0)
2023-07-24 18:41:43 +00:00
Sefa Eyeoglu
70fdfd1526
Merge pull request #1416 from PrismLauncher/bump-7.2
Bump to 7.2
2023-07-21 23:01:04 +02:00
TheKodeToad
1e324e3e2f Bump to 7.2
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
2023-07-21 21:44:51 +01:00
Sefa Eyeoglu
8bbe307a31
chore: trigger build
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
2023-07-21 21:54:44 +02:00
Sefa Eyeoglu
c946b2c4fb
Merge pull request #1408 from PrismLauncher/backport-1372-to-release-7.x 2023-07-19 08:36:56 +02:00
github-actions[bot]
2dcfab0a19 chore(nix): update lockfile
Flake lock file updates:

• Updated input 'flake-parts':
    'github:hercules-ci/flake-parts/267149c58a14d15f7f81b4d737308421de9d7152' (2023-07-01)
  → 'github:hercules-ci/flake-parts/8e8d955c22df93dbe24f19ea04f47a74adbdc5ec' (2023-07-04)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/cd99c2b3c9f160cd004318e0697f90bbd5960825' (2023-07-01)
  → 'github:nixos/nixpkgs/46ed466081b9cad1125b11f11a2af5cc40b942c7' (2023-07-15)
• Updated input 'pre-commit-hooks':
    'github:cachix/pre-commit-hooks.nix/42587d3414d1747999a5f71e92a83cf6547b62da' (2023-07-03)
  → 'github:cachix/pre-commit-hooks.nix/5e28316db471d1ac234beb70031b635437421dd6' (2023-07-14)

(cherry picked from commit 6597a5c860)
2023-07-18 22:17:26 +00:00
TheKodeToad
b0b9b89bce
Merge pull request #1406 from PrismLauncher/backport-1397-to-release-7.x
[Backport release-7.x] Make Launch Offline not launch online in 1.6+
2023-07-18 22:46:05 +01:00
Josiah Glosson
862f4fb061 Make Launch Offline not launch online in 1.6+
Signed-off-by: Josiah Glosson <soujournme@gmail.com>
(cherry picked from commit 1fbb41f5e2)
2023-07-18 21:40:28 +00:00
Sefa Eyeoglu
7c5d07e74d
Merge pull request #1404 from PrismLauncher/backport-1385-to-release-7.x 2023-07-18 22:46:25 +02:00
TheKodeToad
fe1ea7240e
Merge pull request #1334 from TheKodeToad/litemod-dl
LiteMod downloading
2023-07-18 22:43:35 +02:00
Sefa Eyeoglu
6f1d594f1c
Merge pull request #1128 from pandaninjas/fix-implicit-fallthrough 2023-07-18 22:43:35 +02:00
Dallas Strouse
7baaf83f1d Update Flatpak manifest
Signed-off-by: Dallas Strouse <dastrouses@gmail.com>
(cherry picked from commit 2eff1de560)
2023-07-18 20:41:33 +00:00
Sefa Eyeoglu
bab7105820
Merge pull request #1403 from PrismLauncher/backport-1362-to-release-7.x 2023-07-18 22:29:16 +02:00
Trial97
1389b74a8a fixed warning
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit 440afcedb0)
2023-07-18 20:28:46 +00:00
Trial97
5aa3aabdf9 insert header
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit b0a21c9389)
2023-07-18 20:28:46 +00:00
Trial97
42d2c7446a format
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit fc4a1ef193)
2023-07-18 20:28:46 +00:00
TheKodeToad
71a7358287
Merge pull request #1395 from PrismLauncher/backport-1387-to-release-7.x
[Backport release-7.x] Check if any modloader is installed
2023-07-17 17:42:00 +01:00
Sefa Eyeoglu
eaf125c31a fix: don't take modloaders by reference
Co-authored-by: TheKodeToad <TheKodeToad@proton.me>
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit 2be630904f)
2023-07-17 16:41:05 +00:00
Sefa Eyeoglu
f7e018d41a fix: check if any modloader is installed
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit 54a091ca59)
2023-07-17 16:41:05 +00:00
Sefa Eyeoglu
7d6e07ea71
Merge pull request #1384 from PrismLauncher/backport-1357-to-release-7.x
[Backport release-7.x] feat: add toggle for quilt beacon
2023-07-17 06:43:57 +02:00
seth
cd011a097b chore: better explain quilt loader beacon
Signed-off-by: seth <getchoo@tuta.io>
(cherry picked from commit a2a09ffe01)
2023-07-17 04:43:06 +00:00
seth
055bcc2721 feat: add toggle for quilt beacon
Signed-off-by: seth <getchoo@tuta.io>
(cherry picked from commit 89aaedc06c)
2023-07-17 04:43:06 +00:00
Sefa Eyeoglu
4bc4b29d5b
Merge pull request #1380 from PrismLauncher/backport-1276-to-release-7.x 2023-07-16 23:20:05 +02:00
Sefa Eyeoglu
82a9e7d372 fix(actions): set all build platforms to official
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit 40fb387185)
2023-07-16 21:16:11 +00:00
Sefa Eyeoglu
cb81cadee3 fix: set default platform to "unknown"
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit 63acf0a7b4)
2023-07-16 21:16:11 +00:00
Sefa Eyeoglu
0d8283df97 feat: print build platform in application log
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit fce000206f)
2023-07-16 21:16:11 +00:00
Sefa Eyeoglu
d4014534eb feat: print build platform in log
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit f5e4171df4)
2023-07-16 21:16:11 +00:00
Sefa Eyeoglu
3c5ec5d967 chore: remove obsolete macOS warning
We don't support that macOS version. This check also never worked, as we
never set the platform to that value.

Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
(cherry picked from commit 0aaec9ae4f)
2023-07-16 21:16:11 +00:00
Sefa Eyeoglu
e3625cad91
Merge pull request #1369 from Trial97/autoselect 2023-07-16 20:43:16 +02:00
Tayou
bb945c5165
Merge pull request #1368 from Trial97/crash_curse_import 2023-07-16 20:43:16 +02:00
Rachel Powers
daa5fcce67
Merge pull request #1351 from Trial97/ftb_import
fixed substatus on ftb_import
2023-07-16 20:43:16 +02:00
seth
2bcebe2989
Merge pull request #1336 from Ryex/packaging/fix-duplicate-share-directories
packaging: fix duplicate share directories (use only lowercase)
2023-07-16 20:43:15 +02:00
Sefa Eyeoglu
27f6debdaf
Merge pull request #1306 from Ryex/ci/address-sanitiser_on_debug_builds 2023-07-16 20:43:15 +02:00
seth
7926170073
Merge pull request #1335 from Ryex/fix/gh-1322-old-zip-mods-in-wrong-place
fix(flame install): don't assume .zip is a resource pack. default to mod
2023-07-16 20:43:15 +02:00
Rachel Powers
79537f2948
Merge pull request #1352 from Trial97/crash_after_abort
Do not reset shared pointer if it's already empty
2023-07-16 20:43:15 +02:00
Tayou
2b3021b7c2
Merge pull request #1345 from Trial97/time2
feat:Added option to use system locale
2023-07-16 20:43:15 +02:00
Rachel Powers
b11b86e026
Merge pull request #1350 from Trial97/fix_managed_pack_crash
fixed crash if no version is loaded on managed page
2023-07-16 20:43:15 +02:00
Sefa Eyeoglu
a0ddd85b32
Merge pull request #1333 from Ryex/fix/null_instance_edit_crash 2023-07-16 20:43:15 +02:00
seth
6f86e8b66e
Merge pull request #1331 from TheKodeToad/hungry-trash
Fix updating trashing resources
2023-07-16 20:43:15 +02:00
Rachel Powers
6cd259becd
Merge pull request #1337 from getchoo/fix-flake-workflow
fix(actions): give update-flake content write perms
2023-07-16 20:43:10 +02:00
Rachel Powers
d9de326f22
Merge pull request #1321 from TheKodeToad/mr-optional
Optional mods in mrpack export
2023-07-16 20:43:02 +02:00
seth
1d4cf0fd03
Merge pull request #1329 from Ryex/fix/progress_dialog_centering
fix(progress dialog): if there is a parent center on creation
2023-07-16 20:43:02 +02:00
Rachel Powers
a65e4af365
Merge pull request #1302 from Ryex/fix/progress-dialog-segfault
fix: segfault in progress dialog
2023-07-16 20:43:02 +02:00
seth
721ac015f3
Merge pull request #1325 from Scrumplex/validate-meta-url
Validate Meta URL
2023-07-16 20:42:55 +02:00
seth
c5572a5e0b
Merge pull request #1310 from getchoo/autoupdate-flake
feat(actions): add update-flake-lock
2023-07-16 20:42:49 +02:00
Sefa Eyeoglu
81757717f7
Merge pull request #1284 from Ryex/fix/properly-track-failed-copies-and-clones 2023-07-16 20:42:48 +02:00
seth
1ab35357e9
Merge pull request #1304 from Scrumplex/chore-flake-update-1
flake.lock: Update
2023-07-16 20:42:48 +02:00
Tayou
7025f75903
Merge pull request #1127 from Trial97/scale_cat 2023-07-16 20:42:48 +02:00
TheKodeToad
3cc68fcea4
Merge pull request #1232 from telans/screenshots-update
ScreenshotsPage fixes
2023-07-16 20:42:48 +02:00
Sefa Eyeoglu
34be098f12
Merge pull request #1298 from TurboWafflz/develop 2023-07-16 20:42:48 +02:00
seth
fb5655085f
Merge pull request #1292 from Trial97/export5
Removed logs from instance export
2023-07-16 20:42:48 +02:00
Sefa Eyeoglu
316ef9b725
Merge pull request #1266 from TheKodeToad/smol-tweaks 2023-07-16 20:42:48 +02:00
seth
5fdbc9d75e
Merge pull request #1280 from Trial97/shortcut
Fixed illegal characters in shortcuts name
2023-07-16 20:42:48 +02:00
seth
6464127d05
Merge pull request #1281 from Trial97/screenshot
Added more information to the screenshot upload warning
2023-07-16 20:42:48 +02:00
TheKodeToad
c349eff522
Merge pull request #1277 from Trial97/remove_mojang
Removed unused files
2023-07-16 20:42:47 +02:00
Sefa Eyeoglu
5fdf73a2ff
Merge pull request #1274 from TheKodeToad/java-signature 2023-07-16 20:42:47 +02:00
Sefa Eyeoglu
6963bfc6c9
Merge pull request #1275 from Scrumplex/git-blame-ignore 2023-07-16 20:42:47 +02:00
Sefa Eyeoglu
a25a5a7c9f
Merge pull request #1065 from leo78913/gamescope-close-button 2023-07-16 20:42:47 +02:00
Sefa Eyeoglu
20e9bf0e11
Merge pull request #1261 from telans/modrinthexport-url 2023-07-16 20:42:26 +02:00
Rachel Powers
21ccf47ea7
Merge pull request #1255 from Trial97/export4
Added Thumbs.db to excluded files in MrPackExport
2023-07-16 20:42:26 +02:00
Rachel Powers
6856c2f922
Merge pull request #1259 from PrismLauncher/update-devs
Update developers
2023-07-16 20:42:26 +02:00
TheKodeToad
e52fd9d4fe
Merge pull request #1256 from Trial97/fix11
Fixed hashers
2023-07-16 20:42:25 +02:00
Rachel Powers
05056e1abf
Merge pull request #1200 from Trial97/net_job_crash
Made ByteSynkArray to use shared_ptr
2023-07-16 20:42:25 +02:00
seth
9df3c5d3c0
Merge pull request #1251 from getchoo/github-clarify
chore: add 'suggest a feature' message in help
2023-07-16 20:42:25 +02:00
seth
1a4ea3b1cd
Merge pull request #1252 from getchoo/import-hehe
chore: avoid confusion in file/url import dialog
2023-07-16 20:42:25 +02:00
Tayou
0a7a7d9bfd
Merge pull request #1235 from ChrisLane/java-check-debug-msg-fix 2023-07-16 20:42:25 +02:00
Sefa Eyeoglu
8b017f9a5f
Merge pull request #1243 from Trial97/export
Added regex expresion to exclude .DS_Store files
2023-07-16 20:42:25 +02:00
TheKodeToad
b1fe4d1d93
Merge pull request #1228 from Trial97/curent_pack_crash
Fixes #1212
2023-07-16 20:42:25 +02:00
DioEgizio
80463f9761
Merge pull request #1231 from telans/modrinth-default-icon
Modrinth: use default icon for non-managed packs
2023-07-16 20:42:25 +02:00
TheKodeToad
567af5b22d
Merge pull request #1233 from p2js/ui-consistency-fix
Remove inconsistent/unneeded question marks in UI
2023-07-16 20:42:24 +02:00
seth
7ed15b2687
Merge pull request #1214 from PrismLauncher/renovate/cachix-install-nix-action-22.x
chore(deps): update cachix/install-nix-action action to v22
2023-07-16 20:42:24 +02:00
Sefa Eyeoglu
3d502b12a9
Merge pull request #1224 from DioEgizio/add-appstream 2023-07-16 19:59:30 +02:00
seth
f0d1df9139
Merge pull request #1210 from getchoo/opengl-appimage
fix(appimage): bundle generic opengl lib
2023-07-16 19:59:30 +02:00
Sefa Eyeoglu
0fcaa336dc
Merge pull request #1377 from PrismLauncher/backport-1184-to-release-7.x 2023-07-16 19:55:45 +02:00
clickdevin
949da6b50e Fix bugs when updating curseforge modpacks
Signed-off-by: clickdevin <git@clickdevin.me>
(cherry picked from commit d4f2059b78)
2023-07-16 17:55:05 +00:00
Sefa Eyeoglu
fd0ba70080
Merge pull request #1376 from PrismLauncher/backport-1234-to-release-7.x 2023-07-16 19:52:08 +02:00
Jakub Wroński
89dd981dd7 Fix compiling on FreeBSD
Signed-off-by: Jakub Wroński <kubawronski161@gmail.com>
(cherry picked from commit a32a3e25ad)
2023-07-16 17:51:53 +00:00
Sefa Eyeoglu
30fda57c64
Merge pull request #1375 from PrismLauncher/backport-1218-to-release-7.x 2023-07-16 19:49:45 +02:00
flow
1d75c58d6c 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>
(cherry picked from commit 2d00a727f6)
2023-07-16 17:49:15 +00:00
Sefa Eyeoglu
34d80a8c75
Merge pull request #1181 from DioEgizio/bump-7.1 2023-06-16 13:07:25 +02:00
DioEgizio
05360c1103
chore: bump to 7.1
Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
2023-06-16 12:50:08 +02:00
171 changed files with 2011 additions and 3045 deletions

4
.git-blame-ignore-revs Normal file
View File

@ -0,0 +1,4 @@
# .git-blame-ignore-revs
# tabs -> spaces
bbb3b3e6f6e3c0f95873f22e6d0a4aaf350f49d9

View File

@ -191,7 +191,7 @@ jobs:
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: | run: |
sudo apt-get -y update sudo apt-get -y update
sudo apt-get -y install ninja-build extra-cmake-modules scdoc sudo apt-get -y install ninja-build extra-cmake-modules scdoc appstream
- name: Install Dependencies (macOS) - name: Install Dependencies (macOS)
if: runner.os == 'macOS' if: runner.os == 'macOS'
@ -250,6 +250,7 @@ jobs:
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage" wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage"
${{ github.workspace }}/.github/scripts/prepare_JREs.sh ${{ github.workspace }}/.github/scripts/prepare_JREs.sh
sudo apt install libopengl0
- name: Add QT_HOST_PATH var (Windows MSVC arm64) - name: Add QT_HOST_PATH var (Windows MSVC arm64)
if: runner.os == 'Windows' && matrix.architecture == 'arm64' if: runner.os == 'Windows' && matrix.architecture == 'arm64'
@ -263,23 +264,23 @@ jobs:
- name: Configure CMake (macOS) - name: Configure CMake (macOS)
if: runner.os == 'macOS' && matrix.qt_ver == 6 if: runner.os == 'macOS' && matrix.qt_ver == 6
run: | 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=${{ 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 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
- name: Configure CMake (macOS-Legacy) - name: Configure CMake (macOS-Legacy)
if: runner.os == 'macOS' && matrix.qt_ver == 5 if: runner.os == 'macOS' && matrix.qt_ver == 5
run: | 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=${{ 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 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
- name: Configure CMake (Windows MinGW-w64) - name: Configure CMake (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != '' if: runner.os == 'Windows' && matrix.msystem != ''
shell: msys2 {0} shell: msys2 {0}
run: | 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=${{ 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 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
- name: Configure CMake (Windows MSVC) - name: Configure CMake (Windows MSVC)
if: runner.os == 'Windows' && matrix.msystem == '' if: runner.os == 'Windows' && matrix.msystem == ''
run: | 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=${{ matrix.name }} -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=official -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) # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
if ("${{ env.CCACHE_VAR }}") if ("${{ env.CCACHE_VAR }}")
{ {
@ -294,7 +295,7 @@ jobs:
- name: Configure CMake (Linux) - name: Configure CMake (Linux)
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: | run: |
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 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
## ##
# BUILD # BUILD
@ -467,7 +468,8 @@ jobs:
shell: bash shell: bash
run: | run: |
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
mv ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.metainfo.xml ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml
export "NO_APPSTREAM=1" # we have to skip appstream checking because appstream on ubuntu 20.04 is outdated
export OUTPUT="PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage" export OUTPUT="PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
chmod +x linuxdeploy-*.AppImage chmod +x linuxdeploy-*.AppImage
@ -482,7 +484,8 @@ jobs:
cp -r /home/runner/work/PrismLauncher/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines cp -r /home/runner/work/PrismLauncher/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}//usr/lib/ cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib" LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server" LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
@ -583,33 +586,3 @@ jobs:
with: with:
bundle: "Prism Launcher.flatpak" bundle: "Prism Launcher.flatpak"
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml 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@v21
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

28
.github/workflows/update-flake.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: Update Flake Lockfile
on:
schedule:
# run weekly on sunday
- cron: "0 0 * * 0"
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
update-flake:
if: github.repository == 'PrismLauncher/PrismLauncher'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: cachix/install-nix-action@v22
- uses: DeterminateSystems/update-flake-lock@v19
with:
commit-msg: "chore(nix): update lockfile"
pr-title: "chore(nix): update lockfile"
pr-labels: |
Linux
simple change

3
.gitmodules vendored
View File

@ -19,3 +19,6 @@
[submodule "libraries/cmark"] [submodule "libraries/cmark"]
path = libraries/cmark path = libraries/cmark
url = https://github.com/commonmark/cmark.git 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,6 +85,38 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0")
# set CXXFLAGS for build targets # set CXXFLAGS for build targets
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}") set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
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) option(ENABLE_LTO "Enable Link Time Optimization" off)
if(ENABLE_LTO) if(ENABLE_LTO)
@ -139,14 +171,14 @@ set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRIN
######## Set version numbers ######## ######## Set version numbers ########
set(Launcher_VERSION_MAJOR 7) set(Launcher_VERSION_MAJOR 7)
set(Launcher_VERSION_MINOR 0) set(Launcher_VERSION_MINOR 2)
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}") set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0") set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0") set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0")
# Build platform. # Build platform.
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.") 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.")
# Channel list URL # Channel list URL
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.") set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
@ -332,7 +364,7 @@ elseif(UNIX)
set(BINARY_DEST_DIR "bin") set(BINARY_DEST_DIR "bin")
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}") set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
set(JARS_DEST_DIR "share/${Launcher_APP_BINARY_NAME}") set(JARS_DEST_DIR "share/${Launcher_Name}")
# install as bundle with no dependencies included # install as bundle with no dependencies included
set(INSTALL_BUNDLE "nodeps") set(INSTALL_BUNDLE "nodeps")
@ -345,7 +377,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_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_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "${KDE_INSTALL_DATADIR}/${Launcher_Name}") install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}")
if(Launcher_ManPage) if(Launcher_ManPage)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")

View File

@ -42,7 +42,7 @@ Feel free to create a GitHub issue if you find a bug or want to suggest a new fe
- **Our Matrix space:** - **Our Matrix space:**
[![PrismLauncher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&label=Matrix%20Space&logo=matrix&color=purple)](https://prismlauncher.org/matrix) [![Prism Launcher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&label=Matrix%20Space&logo=matrix&color=purple)](https://prismlauncher.org/matrix)
- **Our Subreddit:** - **Our Subreddit:**
@ -50,7 +50,7 @@ Feel free to create a GitHub issue if you find a bug or want to suggest a new fe
## Translations ## Translations
The translation effort for PrismLauncher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at <https://github.com/PrismLauncher/Translations> The translation effort for Prism Launcher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at <https://github.com/PrismLauncher/Translations>
## Building ## Building
@ -82,14 +82,16 @@ Thanks to the awesome people over at [MacStadium](https://www.macstadium.com/),
## Forking/Redistributing/Custom builds policy ## Forking/Redistributing/Custom builds policy
We don't care what you do with your fork/custom build as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy: You are free to fork, redistribute and provide custom builds as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy:
- Make it clear that your fork is not PrismLauncher and is not endorsed by or affiliated with the PrismLauncher project (<https://prismlauncher.org>). - Make it clear that your fork is not Prism Launcher and is not endorsed by or affiliated with the Prism Launcher project (<https://prismlauncher.org>).
- Go through [CMakeLists.txt](CMakeLists.txt) and change PrismLauncher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled). - Go through [CMakeLists.txt](CMakeLists.txt) and change Prism Launcher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled).
If you have any questions or want any clarification on the above conditions please make an issue and ask us. If you have any questions or want any clarification on the above conditions please make an issue and ask us.
Be aware that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions: If you are just building Prism Launcher for your distribution, please make sure to set the `Launcher_BUILD_PLATFORM` to a slug representing your distribution. Examples are `archlinux`, `fedora` and `nixpkgs`.
Note that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions:
- [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use) - [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
- [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions) - [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions)

View File

@ -65,7 +65,7 @@ Config::Config()
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@"; MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@"; MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
if (BUILD_PLATFORM == "macOS" && !MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty()) if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
{ {
UPDATER_ENABLED = true; UPDATER_ENABLED = true;
} }
@ -82,6 +82,7 @@ Config::Config()
{ {
GIT_REFSPEC = "refs/heads/stable"; GIT_REFSPEC = "refs/heads/stable";
GIT_TAG = versionString(); GIT_TAG = versionString();
GIT_COMMIT = "";
} }
if (GIT_REFSPEC.startsWith("refs/heads/")) if (GIT_REFSPEC.startsWith("refs/heads/"))

View File

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

View File

@ -21,11 +21,11 @@
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1683560683, "lastModified": 1688466019,
"narHash": "sha256-XAygPMN5Xnk/W2c1aW0jyEa6lfMDZWlQgiNtmHXytPc=", "narHash": "sha256-VeM2akYrBYMsb4W/MmBo1zmaMfgbL4cH3Pu8PGyIwJ0=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "006c75898cf814ef9497252b022e91c946ba8e17", "rev": "8e8d955c22df93dbe24f19ea04f47a74adbdc5ec",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -35,12 +35,15 @@
} }
}, },
"flake-utils": { "flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": { "locked": {
"lastModified": 1667395993, "lastModified": 1685518550,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -73,11 +76,11 @@
"libnbtplusplus": { "libnbtplusplus": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1650031308, "lastModified": 1690036783,
"narHash": "sha256-TvVOjkUobYJD9itQYueELJX3wmecvEdCbJ0FinW2mL4=", "narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=",
"owner": "PrismLauncher", "owner": "PrismLauncher",
"repo": "libnbtplusplus", "repo": "libnbtplusplus",
"rev": "2203af7eeb48c45398139b583615134efd8d407f", "rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -88,11 +91,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1685012353, "lastModified": 1690026219,
"narHash": "sha256-U3oOge4cHnav8OLGdRVhL45xoRj4Ppd+It6nPC9nNIU=", "narHash": "sha256-oOduRk/kzQxOBknZXTLSEYd7tk+GoKvr8wV6Ab+t4AU=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "aeb75dba965e790de427b73315d5addf91a54955", "rev": "f465da166263bc0d4b39dfd4ca28b777c92d4b73",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -105,11 +108,11 @@
"nixpkgs-lib": { "nixpkgs-lib": {
"locked": { "locked": {
"dir": "lib", "dir": "lib",
"lastModified": 1682879489, "lastModified": 1688049487,
"narHash": "sha256-sASwo8gBt7JDnOOstnps90K1wxmVfyhsTPPNTGBPjjg=", "narHash": "sha256-100g4iaKC9MalDjUW9iN6Jl/OocTDtXdeAj7pEGIRh4=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "da45bf6ec7bbcc5d1e14d3795c025199f28e0de0", "rev": "4bc72cae107788bf3f24f30db2e2f685c9298dc9",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -135,11 +138,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1684842236, "lastModified": 1689668210,
"narHash": "sha256-rYWsIXHvNhVQ15RQlBUv67W3YnM+Pd+DuXGMvCBq2IE=", "narHash": "sha256-XAATwDkaUxH958yXLs1lcEOmU6pSEIkatY3qjqk8X0E=",
"owner": "cachix", "owner": "cachix",
"repo": "pre-commit-hooks.nix", "repo": "pre-commit-hooks.nix",
"rev": "61e567d6497bc9556f391faebe5e410e6623217f", "rev": "eb433bff05b285258be76513add6f6c57b441775",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -156,6 +159,21 @@
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"pre-commit-hooks": "pre-commit-hooks" "pre-commit-hooks": "pre-commit-hooks"
} }
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",

22
flatpak/libdecor.json Normal file
View File

@ -0,0 +1,22 @@
{
"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,13 +5,6 @@ sdk: org.kde.Sdk
sdk-extensions: sdk-extensions:
- org.freedesktop.Sdk.Extension.openjdk17 - org.freedesktop.Sdk.Extension.openjdk17
- org.freedesktop.Sdk.Extension.openjdk8 - 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 command: prismlauncher
finish-args: finish-args:
@ -26,21 +19,31 @@ finish-args:
# Mod drag&drop # Mod drag&drop
- --filesystem=xdg-download:ro - --filesystem=xdg-download:ro
cleanup:
- /lib/libGLU*
modules: 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 - name: prismlauncher
buildsystem: cmake-ninja buildsystem: cmake-ninja
builddir: true
config-opts: config-opts:
- -DLauncher_BUILD_PLATFORM=flatpak - -DLauncher_BUILD_PLATFORM=flatpak
- -DCMAKE_BUILD_TYPE=Debug - -DCMAKE_BUILD_TYPE=RelWithDebInfo
- -DLauncher_QT_VERSION_MAJOR=5 - -DLauncher_QT_VERSION_MAJOR=5
build-options: build-options:
env: env:
JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17 JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17
JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac
sources: sources:
- type: dir - type: dir
path: ../ path: ../
builddir: true
- name: openjdk - name: openjdk
buildsystem: simple buildsystem: simple
build-commands: build-commands:
@ -49,14 +52,45 @@ modules:
- mv /app/jre /app/jdk/17 - mv /app/jre /app/jdk/17
- /usr/lib/sdk/openjdk8/install.sh - /usr/lib/sdk/openjdk8/install.sh
- mv /app/jre /app/jdk/8 - mv /app/jre /app/jdk/8
cleanup: [/jre] 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
- name: xrandr - name: xrandr
buildsystem: autotools buildsystem: autotools
sources: sources:
- type: archive - type: archive
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.1.tar.xz url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.2.tar.xz
sha256: 7bc76daf9d72f8aff885efad04ce06b90488a1a169d118dea8a2b661832e8762 sha256: c8bee4790d9058bacc4b6246456c58021db58a87ddda1a9d0139bf5f18f1f240
cleanup: [/share/man, /bin/xkeystone] x-checker-data:
type: anitya
project-id: 14957
stable-only: true
url-template: https://xorg.freedesktop.org/archive/individual/app/xrandr-$version.tar.xz
cleanup:
- /share/man
- /bin/xkeystone
- name: gamemode - name: gamemode
buildsystem: meson buildsystem: meson
config-opts: config-opts:
@ -67,19 +101,56 @@ modules:
# post-install is running inside the build dir, we need it from the source though # post-install is running inside the build dir, we need it from the source though
- install -Dm755 ../data/gamemoderun -t /app/bin - install -Dm755 ../data/gamemoderun -t /app/bin
sources: sources:
- type: git - type: archive
url: https://github.com/FeralInteractive/gamemode archive-type: tar-gzip
tag: "1.7" url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.7
commit: 4dc99dff76218718763a6b07fc1900fa6d1dafd9 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
- name: enhance - name: enhance
buildsystem: simple buildsystem: simple
build-commands: build-commands:
- mkdir -p /app/utils/gamescope
- install -Dm755 prime-run /app/bin/prime-run - install -Dm755 prime-run /app/bin/prime-run
- mv /app/bin/prismlauncher /app/bin/prismrun - mv /app/bin/prismlauncher /app/bin/prismrun
- install -Dm755 prismlauncher /app/bin/prismlauncher - install -Dm755 prismlauncher /app/bin/prismlauncher
sources: sources:
- type: file - type: file
path: ../flatpak/prime-run path: prime-run
- type: file - type: file
path: ../flatpak/prismlauncher path: prismlauncher

View File

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

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

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

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

@ -0,0 +1 @@
Subproject commit 45094ca570be383d06df729b6972830ec63bd3df

5
garnix.yaml Normal file
View File

@ -0,0 +1,5 @@
builds:
exclude: []
include:
- "devShells.*-linux.*"
- "packages.*-linux.*"

View File

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

View File

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

View File

@ -375,8 +375,6 @@ set(MINECRAFT_SOURCES
minecraft/services/SkinDelete.cpp minecraft/services/SkinDelete.cpp
minecraft/services/SkinDelete.h minecraft/services/SkinDelete.h
mojang/PackageManifest.h
mojang/PackageManifest.cpp
minecraft/Agent.h) minecraft/Agent.h)
# the screenshots feature # the screenshots feature
@ -682,6 +680,7 @@ SET(LAUNCHER_SOURCES
VersionProxyModel.h VersionProxyModel.h
VersionProxyModel.cpp VersionProxyModel.cpp
Markdown.h Markdown.h
Markdown.cpp
# Super secret! # Super secret!
KonamiCode.h KonamiCode.h
@ -825,8 +824,8 @@ SET(LAUNCHER_SOURCES
ui/pages/global/APIPage.h ui/pages/global/APIPage.h
# GUI - platform pages # GUI - platform pages
ui/pages/modplatform/VanillaPage.cpp ui/pages/modplatform/CustomPage.cpp
ui/pages/modplatform/VanillaPage.h ui/pages/modplatform/CustomPage.h
ui/pages/modplatform/ResourcePage.cpp ui/pages/modplatform/ResourcePage.cpp
ui/pages/modplatform/ResourcePage.h ui/pages/modplatform/ResourcePage.h
@ -1032,7 +1031,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/instance/ScreenshotsPage.ui ui/pages/instance/ScreenshotsPage.ui
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui
ui/pages/modplatform/atlauncher/AtlPage.ui ui/pages/modplatform/atlauncher/AtlPage.ui
ui/pages/modplatform/VanillaPage.ui ui/pages/modplatform/CustomPage.ui
ui/pages/modplatform/ResourcePage.ui ui/pages/modplatform/ResourcePage.ui
ui/pages/modplatform/flame/FlamePage.ui ui/pages/modplatform/flame/FlamePage.ui
ui/pages/modplatform/legacy_ftb/Page.ui ui/pages/modplatform/legacy_ftb/Page.ui

View File

@ -40,6 +40,7 @@
#include <QFileSystemModel> #include <QFileSystemModel>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QStack> #include <QStack>
#include <algorithm>
#include "FileSystem.h" #include "FileSystem.h"
#include "SeparatorPrefixTree.h" #include "SeparatorPrefixTree.h"
#include "StringUtils.h" #include "StringUtils.h"
@ -254,3 +255,25 @@ bool FileIgnoreProxy::filterAcceptsColumn(int source_column, const QModelIndex&
return true; return true;
} }
bool FileIgnoreProxy::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
auto fileInfo = fsm->fileInfo(index);
return !ignoreFile(fileInfo);
}
bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const
{
auto fileName = fileInfo.fileName();
auto path = relPath(fileInfo.absoluteFilePath());
return std::any_of(m_ignoreFiles.cbegin(), m_ignoreFiles.cend(), [fileName](auto iFileName) { return fileName == iFileName; }) ||
m_ignoreFilePaths.covers(path);
}
bool FileIgnoreProxy::filterFile(const QString& fileName) const
{
return blocked.covers(fileName) || ignoreFile(QFileInfo(QDir(root), fileName));
}

View File

@ -36,6 +36,7 @@
#pragma once #pragma once
#include <QFileInfo>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include "SeparatorPrefixTree.h" #include "SeparatorPrefixTree.h"
@ -63,10 +64,22 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return blocked; } inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return blocked; }
inline SeparatorPrefixTree<'/'>& blockedPaths() { return blocked; } inline SeparatorPrefixTree<'/'>& blockedPaths() { return blocked; }
// list of file names that need to be removed completely from model
inline QStringList& ignoreFilesWithName() { return m_ignoreFiles; }
// list of relative paths that need to be removed completely from model
inline SeparatorPrefixTree<'/'>& ignoreFilesWithPath() { return m_ignoreFilePaths; }
bool filterFile(const QString& fileName) const;
protected: protected:
bool filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const; bool filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const;
bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
bool ignoreFile(QFileInfo file) const;
private: private:
const QString root; const QString root;
SeparatorPrefixTree<'/'> blocked; SeparatorPrefixTree<'/'> blocked;
QStringList m_ignoreFiles;
SeparatorPrefixTree<'/'> m_ignoreFilePaths;
}; };

View File

@ -36,6 +36,7 @@
*/ */
#include "FileSystem.h" #include "FileSystem.h"
#include <QPair>
#include "BuildConfig.h" #include "BuildConfig.h"
@ -102,7 +103,7 @@ namespace fs = ghc::filesystem;
#include <linux/fs.h> #include <linux/fs.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) #elif defined(Q_OS_MACOS)
#include <sys/attr.h> #include <sys/attr.h>
#include <sys/clonefile.h> #include <sys/clonefile.h>
#elif defined(Q_OS_WIN) #elif defined(Q_OS_WIN)
@ -246,6 +247,7 @@ bool copy::operator()(const QString& offset, bool dryRun)
{ {
using copy_opts = fs::copy_options; using copy_opts = fs::copy_options;
m_copied = 0; // reset counter m_copied = 0; // reset counter
m_failedPaths.clear();
// NOTE always deep copy on windows. the alternatives are too messy. // NOTE always deep copy on windows. the alternatives are too messy.
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
@ -277,6 +279,9 @@ bool copy::operator()(const QString& offset, bool dryRun)
qWarning() << "Failed to copy files:" << QString::fromStdString(err.message()); qWarning() << "Failed to copy files:" << QString::fromStdString(err.message());
qDebug() << "Source file:" << src_path; qDebug() << "Source file:" << src_path;
qDebug() << "Destination file:" << dst_path; qDebug() << "Destination file:" << dst_path;
m_failedPaths.append(dst_path);
emit copyFailed(relative_dst_path);
return;
} }
m_copied++; m_copied++;
emit fileCopied(relative_dst_path); emit fileCopied(relative_dst_path);
@ -372,7 +377,7 @@ void create_link::make_link_list(const QString& offset)
auto src_path = source_it.next(); auto src_path = source_it.next();
auto relative_path = src_dir.relativeFilePath(src_path); auto relative_path = src_dir.relativeFilePath(src_path);
if (m_max_depth >= 0 && pathDepth(relative_path) > m_max_depth){ if (m_max_depth >= 0 && pathDepth(relative_path) > m_max_depth) {
relative_path = pathTruncate(relative_path, m_max_depth); relative_path = pathTruncate(relative_path, m_max_depth);
src_path = src_dir.filePath(relative_path); src_path = src_dir.filePath(relative_path);
if (linkedPaths.contains(src_path)) { if (linkedPaths.contains(src_path)) {
@ -663,7 +668,7 @@ QString pathTruncate(const QString& path, int depth)
QString trunc = QFileInfo(path).path(); QString trunc = QFileInfo(path).path();
if (pathDepth(trunc) > depth ) { if (pathDepth(trunc) > depth) {
return pathTruncate(trunc, depth); return pathTruncate(trunc, depth);
} }
@ -769,6 +774,9 @@ QString getDesktopDir()
// Cross-platform Shortcut creation // Cross-platform Shortcut creation
bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon) bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon)
{ {
if (destination.isEmpty()) {
destination = PathCombine(getDesktopDir(), RemoveInvalidFilenameChars(name));
}
#if defined(Q_OS_MACOS) #if defined(Q_OS_MACOS)
destination += ".command"; destination += ".command";
@ -791,6 +799,8 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
return true; return true;
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) #elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
if (!destination.endsWith(".desktop")) // in case of isFlatpak destination is already populated
destination += ".desktop";
QFile f(destination); QFile f(destination);
f.open(QIODevice::WriteOnly | QIODevice::Text); f.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream stream(&f); QTextStream stream(&f);
@ -974,7 +984,7 @@ FilesystemType getFilesystemType(const QString& name)
{ {
for (auto iter = s_filesystem_type_names.constBegin(); iter != s_filesystem_type_names.constEnd(); ++iter) { for (auto iter = s_filesystem_type_names.constBegin(); iter != s_filesystem_type_names.constEnd(); ++iter) {
auto fs_names = iter.value(); auto fs_names = iter.value();
if(fs_names.contains(name.toUpper())) if (fs_names.contains(name.toUpper()))
return iter.key(); return iter.key();
} }
return FilesystemType::UNKNOWN; return FilesystemType::UNKNOWN;
@ -1072,6 +1082,7 @@ bool clone::operator()(const QString& offset, bool dryRun)
} }
m_cloned = 0; // reset counter m_cloned = 0; // reset counter
m_failedClones.clear();
auto src = PathCombine(m_src.absolutePath(), offset); auto src = PathCombine(m_src.absolutePath(), offset);
auto dst = PathCombine(m_dst.absolutePath(), offset); auto dst = PathCombine(m_dst.absolutePath(), offset);
@ -1092,6 +1103,9 @@ bool clone::operator()(const QString& offset, bool dryRun)
qDebug() << "Failed to clone files: error" << err.value() << "message" << QString::fromStdString(err.message()); qDebug() << "Failed to clone files: error" << err.value() << "message" << QString::fromStdString(err.message());
qDebug() << "Source file:" << src_path; qDebug() << "Source file:" << src_path;
qDebug() << "Destination file:" << dst_path; qDebug() << "Destination file:" << dst_path;
m_failedClones.append(qMakePair(src_path, dst_path));
emit cloneFailed(src_path, dst_path);
return;
} }
m_cloned++; m_cloned++;
emit fileCloned(src_path, dst_path); emit fileCloned(src_path, dst_path);
@ -1151,7 +1165,7 @@ bool clone_file(const QString& src, const QString& dst, std::error_code& ec)
return false; return false;
} }
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) #elif defined(Q_OS_MACOS)
if (!macos_bsd_clonefile(src_path, dst_path, ec)) { if (!macos_bsd_clonefile(src_path, dst_path, ec)) {
qDebug() << "failed macos_bsd_clonefile:"; qDebug() << "failed macos_bsd_clonefile:";
@ -1380,7 +1394,7 @@ bool linux_ficlone(const std::string& src_path, const std::string& dst_path, std
return true; return true;
} }
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) #elif defined(Q_OS_MACOS)
bool macos_bsd_clonefile(const std::string& src_path, const std::string& dst_path, std::error_code& ec) bool macos_bsd_clonefile(const std::string& src_path, const std::string& dst_path, std::error_code& ec)
{ {

View File

@ -43,6 +43,7 @@
#include <system_error> #include <system_error>
#include <QDir> #include <QDir>
#include <QPair>
#include <QFlags> #include <QFlags>
#include <QLocalServer> #include <QLocalServer>
#include <QObject> #include <QObject>
@ -112,9 +113,12 @@ class copy : public QObject {
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
int totalCopied() { return m_copied; } int totalCopied() { return m_copied; }
int totalFailed() { return m_failedPaths.length(); }
QStringList failed() { return m_failedPaths; }
signals: signals:
void fileCopied(const QString& relativeName); void fileCopied(const QString& relativeName);
void copyFailed(const QString& relativeName);
// TODO: maybe add a "shouldCopy" signal in the future? // TODO: maybe add a "shouldCopy" signal in the future?
private: private:
@ -127,6 +131,7 @@ class copy : public QObject {
QDir m_src; QDir m_src;
QDir m_dst; QDir m_dst;
int m_copied; int m_copied;
QStringList m_failedPaths;
}; };
struct LinkPair { struct LinkPair {
@ -471,6 +476,9 @@ class clone : public QObject {
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
int totalCloned() { return m_cloned; } int totalCloned() { return m_cloned; }
int totalFailed() { return m_failedClones.length(); }
QList<QPair<QString, QString>> failed() { return m_failedClones; }
signals: signals:
void fileCloned(const QString& src, const QString& dst); void fileCloned(const QString& src, const QString& dst);
@ -485,6 +493,7 @@ class clone : public QObject {
QDir m_src; QDir m_src;
QDir m_dst; QDir m_dst;
int m_cloned; int m_cloned;
QList<QPair<QString, QString>> m_failedClones;
}; };
/** /**

View File

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

31
launcher/Markdown.cpp Normal file
View File

@ -0,0 +1,31 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2023 Joshua Goins <josh@redstrate.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 "Markdown.h"
QString markdownToHTML(const QString& markdown)
{
const QByteArray markdownData = markdown.toUtf8();
char* buffer = cmark_markdown_to_html(markdownData.constData(), markdownData.length(), CMARK_OPT_NOBREAKS | CMARK_OPT_UNSAFE);
QString htmlStr(buffer);
free(buffer);
return htmlStr;
}

View File

@ -21,14 +21,4 @@
#include <QString> #include <QString>
#include <cmark.h> #include <cmark.h>
static QString markdownToHTML(const QString& markdown) QString markdownToHTML(const QString& markdown);
{
const QByteArray markdownData = markdown.toUtf8();
char* buffer = cmark_markdown_to_html(markdownData.constData(), markdownData.length(), CMARK_OPT_NOBREAKS | CMARK_OPT_UNSAFE);
QString htmlStr(buffer);
free(buffer);
return htmlStr;
}

View File

@ -187,35 +187,24 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
} }
case Qt::ToolTipRole: case Qt::ToolTipRole:
{ {
switch(column) if(column == Name && hasRecommended)
{ {
case Name: auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
if(value.toBool())
{ {
if(hasRecommended) return tr("Recommended");
} else if(hasLatest) {
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
if(value.toBool())
{ {
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole); return tr("Latest");
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 if(index.row() == 0)
default:
{ {
return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole); return tr("Latest");
} }
} else {
return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole);
} }
} }
case Qt::DecorationRole: case Qt::DecorationRole:

View File

@ -85,7 +85,7 @@ void JavaChecker::performCheck()
process->setProgram(m_path); process->setProgram(m_path);
process->setProcessChannelMode(QProcess::SeparateChannels); process->setProcessChannelMode(QProcess::SeparateChannels);
process->setProcessEnvironment(CleanEnviroment()); process->setProcessEnvironment(CleanEnviroment());
qDebug() << "Running java checker: " + m_path + args.join(" ");; qDebug() << "Running java checker:" << m_path << args.join(" ");
connect(process.get(), QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &JavaChecker::finished); connect(process.get(), QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &JavaChecker::finished);
connect(process.get(), &QProcess::errorOccurred, this, &JavaChecker::error); connect(process.get(), &QProcess::errorOccurred, this, &JavaChecker::error);
@ -128,7 +128,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
result.outLog = m_stdout; result.outLog = m_stdout;
qDebug() << "STDOUT" << m_stdout; qDebug() << "STDOUT" << m_stdout;
qWarning() << "STDERR" << m_stderr; qWarning() << "STDERR" << m_stderr;
qDebug() << "Java checker finished with status " << status << " exit code " << exitcode; qDebug() << "Java checker finished with status" << status << "exit code" << exitcode;
if (status == QProcess::CrashExit || exitcode == 1) if (status == QProcess::CrashExit || exitcode == 1)
{ {

View File

@ -1,29 +1,64 @@
// 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 "JavaInstall.h"
#include "BaseVersion.h"
#include "StringUtils.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); auto archCompare = StringUtils::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive);
if(archCompare != 0) if (archCompare != 0)
return archCompare < 0; return archCompare < 0;
if(id < rhs.id) if (id < rhs.id) {
{
return true; return true;
} }
if(id > rhs.id) if (id > rhs.id) {
{
return false; return false;
} }
return StringUtils::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0; 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; 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)); 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,33 +1,40 @@
// 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 #pragma once
#include "BaseVersion.h" #include "BaseVersion.h"
#include "JavaVersion.h" #include "JavaVersion.h"
struct JavaInstall : public BaseVersion struct JavaInstall : public BaseVersion {
{ JavaInstall() {}
JavaInstall(){} JavaInstall(QString id, QString arch, QString path) : id(id), arch(arch), path(path) {}
JavaInstall(QString id, QString arch, QString path) virtual QString descriptor() { return id.toString(); }
: id(id), arch(arch), path(path)
{
}
virtual QString descriptor()
{
return id.toString();
}
virtual QString name() virtual QString name() { return id.toString(); }
{
return id.toString();
}
virtual QString typeString() const virtual QString typeString() const { return arch; }
{
return arch;
}
bool operator<(const JavaInstall & rhs); virtual bool operator<(BaseVersion& a) override;
bool operator==(const JavaInstall & rhs); 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);
JavaVersion id; JavaVersion id;
QString arch; QString arch;

View File

@ -81,15 +81,20 @@ void CheckJava::executeTask()
} }
QFileInfo javaInfo(realJavaPath); QFileInfo javaInfo(realJavaPath);
qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch(); qint64 javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch();
auto storedUnixTime = settings->get("JavaTimestamp").toLongLong(); auto storedSignature = settings->get("JavaSignature").toString();
auto storedArchitecture = settings->get("JavaArchitecture").toString(); auto storedArchitecture = settings->get("JavaArchitecture").toString();
auto storedRealArchitecture = settings->get("JavaRealArchitecture").toString(); auto storedRealArchitecture = settings->get("JavaRealArchitecture").toString();
auto storedVersion = settings->get("JavaVersion").toString(); auto storedVersion = settings->get("JavaVersion").toString();
auto storedVendor = settings->get("JavaVendor").toString(); auto storedVendor = settings->get("JavaVendor").toString();
m_javaUnixTime = javaUnixTime;
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(QByteArray::number(javaUnixTime));
hash.addData(m_javaPath.toUtf8());
m_javaSignature = hash.result().toHex();
// if timestamps are not the same, or something is missing, check! // if timestamps are not the same, or something is missing, check!
if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 if (m_javaSignature != storedSignature || storedVersion.size() == 0
|| storedArchitecture.size() == 0 || storedRealArchitecture.size() == 0 || storedArchitecture.size() == 0 || storedRealArchitecture.size() == 0
|| storedVendor.size() == 0) || storedVendor.size() == 0)
{ {
@ -140,7 +145,7 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
instance->settings()->set("JavaArchitecture", result.mojangPlatform); instance->settings()->set("JavaArchitecture", result.mojangPlatform);
instance->settings()->set("JavaRealArchitecture", result.realPlatform); instance->settings()->set("JavaRealArchitecture", result.realPlatform);
instance->settings()->set("JavaVendor", result.javaVendor); instance->settings()->set("JavaVendor", result.javaVendor);
instance->settings()->set("JavaTimestamp", m_javaUnixTime); instance->settings()->set("JavaSignature", m_javaSignature);
emitSucceeded(); emitSucceeded();
return; return;
} }

View File

@ -40,6 +40,6 @@ private:
private: private:
QString m_javaPath; QString m_javaPath;
qlonglong m_javaUnixTime; QString m_javaSignature;
JavaCheckerPtr m_JavaChecker; JavaCheckerPtr m_JavaChecker;
}; };

View File

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

View File

@ -4,6 +4,7 @@
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org> * Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me> * 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 * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -148,7 +149,7 @@ void MinecraftInstance::loadSpecificSettings()
m_settings->registerOverride(global_settings->getSetting("IgnoreJavaCompatibility"), javaOrLocation); m_settings->registerOverride(global_settings->getSetting("IgnoreJavaCompatibility"), javaOrLocation);
// special! // special!
m_settings->registerPassthrough(global_settings->getSetting("JavaTimestamp"), javaOrLocation); m_settings->registerPassthrough(global_settings->getSetting("JavaSignature"), javaOrLocation);
m_settings->registerPassthrough(global_settings->getSetting("JavaArchitecture"), javaOrLocation); m_settings->registerPassthrough(global_settings->getSetting("JavaArchitecture"), javaOrLocation);
m_settings->registerPassthrough(global_settings->getSetting("JavaRealArchitecture"), javaOrLocation); m_settings->registerPassthrough(global_settings->getSetting("JavaRealArchitecture"), javaOrLocation);
m_settings->registerPassthrough(global_settings->getSetting("JavaVersion"), javaOrLocation); m_settings->registerPassthrough(global_settings->getSetting("JavaVersion"), javaOrLocation);
@ -186,6 +187,10 @@ void MinecraftInstance::loadSpecificSettings()
m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride); m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride);
m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), 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"); m_settings->set("InstanceType", "OneSix");
} }
@ -391,6 +396,12 @@ QStringList MinecraftInstance::extraArguments()
agent->library()->getApplicableFiles(runtimeContext(), jar, temp1, temp2, temp3, getLocalLibraryPath()); agent->library()->getApplicableFiles(runtimeContext(), jar, temp1, temp2, temp3, getLocalLibraryPath());
list.append("-javaagent:"+jar[0]+(agent->argument().isEmpty() ? "" : "="+agent->argument())); 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; return list;
} }
@ -832,7 +843,7 @@ QMap<QString, QString> MinecraftInstance::createCensorFilterFromSession(AuthSess
{ {
addToFilter(sessionRef.session, tr("<SESSION ID>")); addToFilter(sessionRef.session, tr("<SESSION ID>"));
} }
if (sessionRef.access_token != "offline") { if (sessionRef.access_token != "0") {
addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>")); addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>"));
} }
if(sessionRef.client_token.size()) { if(sessionRef.client_token.size()) {

View File

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

View File

@ -374,6 +374,10 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
} }
yggdrasilToken = tokenFromJSONV3(data, "ygg"); 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"); minecraftProfile = profileFromJSONV3(data, "profile");
if(!entitlementFromJSONV3(data, minecraftEntitlement)) { if(!entitlementFromJSONV3(data, minecraftEntitlement)) {
if(minecraftProfile.validity != Katabasis::Validity::None) { if(minecraftProfile.validity != Katabasis::Validity::None) {

View File

@ -328,18 +328,21 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
case AccountState::Gone: { case AccountState::Gone: {
return tr("Gone", "Account status"); return tr("Gone", "Account status");
} }
default: {
return tr("Unknown", "Account status");
}
} }
} }
case MigrationColumn: { case MigrationColumn: {
if(account->isMSA() || account->isOffline()) { if(account->isMSA() || account->isOffline()) {
return tr("N/A", "Can Migrate?"); return tr("N/A", "Can Migrate");
} }
if (account->canMigrate()) { if (account->canMigrate()) {
return tr("Yes", "Can Migrate?"); return tr("Yes", "Can Migrate");
} }
else { else {
return tr("No", "Can Migrate?"); return tr("No", "Can Migrate");
} }
} }
@ -354,12 +357,13 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
return QVariant::fromValue(account); return QVariant::fromValue(account);
case Qt::CheckStateRole: case Qt::CheckStateRole:
switch (index.column()) if (index.column() == ProfileNameColumn) {
{ return account == m_defaultAccount ? Qt::Checked : Qt::Unchecked;
case ProfileNameColumn: } else {
return account == m_defaultAccount ? Qt::Checked : Qt::Unchecked; return QVariant();
} }
default: default:
return QVariant(); return QVariant();
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -89,6 +89,7 @@ std::pair<int, bool> Mod::compare(const Resource& other, SortType type) const
auto res = Resource::compare(other, type); auto res = Resource::compare(other, type);
if (res.first != 0) if (res.first != 0)
return res; return res;
break;
} }
case SortType::VERSION: { case SortType::VERSION: {
auto this_ver = Version(version()); auto this_ver = Version(version());
@ -97,11 +98,13 @@ std::pair<int, bool> Mod::compare(const Resource& other, SortType type) const
return { 1, type == SortType::VERSION }; return { 1, type == SortType::VERSION };
if (this_ver < other_ver) if (this_ver < other_ver)
return { -1, type == SortType::VERSION }; return { -1, type == SortType::VERSION };
break;
} }
case SortType::PROVIDER: { case SortType::PROVIDER: {
auto compare_result = QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive); auto compare_result = QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive);
if (compare_result != 0) if (compare_result != 0)
return { compare_result, type == SortType::PROVIDER }; return { compare_result, type == SortType::PROVIDER };
break;
} }
} }
return { 0, false }; return { 0, false };
@ -121,7 +124,7 @@ bool Mod::applyFilter(QRegularExpression filter) const
return Resource::applyFilter(filter); return Resource::applyFilter(filter);
} }
auto Mod::destroy(QDir& index_dir, bool preserve_metadata) -> bool auto Mod::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool
{ {
if (!preserve_metadata) { if (!preserve_metadata) {
qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name()); qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name());
@ -134,7 +137,7 @@ auto Mod::destroy(QDir& index_dir, bool preserve_metadata) -> bool
} }
} }
return Resource::destroy(); return Resource::destroy(attempt_trash);
} }
auto Mod::details() const -> const ModDetails& auto Mod::details() const -> const ModDetails&

View File

@ -79,7 +79,7 @@ public:
[[nodiscard]] bool applyFilter(QRegularExpression filter) const override; [[nodiscard]] bool applyFilter(QRegularExpression filter) const override;
// Delete all the files of this mod // Delete all the files of this mod
auto destroy(QDir& index_dir, bool preserve_metadata = false) -> bool; auto destroy(QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool;
void finishResolvingWithDetails(ModDetails&& details); void finishResolvingWithDetails(ModDetails&& details);

View File

@ -197,10 +197,10 @@ Task* ModFolderModel::createParseTask(Resource& resource)
bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata) bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata)
{ {
for(auto mod : allMods()){ for(auto mod : allMods()) {
if(mod->fileinfo().fileName() == filename){ if(mod->fileinfo().fileName() == filename) {
auto index_dir = indexDir(); auto index_dir = indexDir();
mod->destroy(index_dir, preserve_metadata); mod->destroy(index_dir, preserve_metadata, false);
update(); update();

View File

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

View File

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

View File

@ -156,7 +156,7 @@ bool ResourceFolderModel::uninstallResource(QString file_name)
{ {
for (auto& resource : m_resources) { for (auto& resource : m_resources) {
if (resource->fileinfo().fileName() == file_name) { if (resource->fileinfo().fileName() == file_name) {
auto res = resource->destroy(); auto res = resource->destroy(false);
update(); update();

View File

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

View File

@ -44,7 +44,11 @@ static const QMap<PackedResourceType, QString> s_packed_type_names = {
namespace ResourceUtils { namespace ResourceUtils {
PackedResourceType identify(QFileInfo file){ PackedResourceType identify(QFileInfo file){
if (file.exists() && file.isFile()) { if (file.exists() && file.isFile()) {
if (ResourcePackUtils::validate(file)) { 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)) {
qDebug() << file.fileName() << "is a resource pack"; qDebug() << file.fileName() << "is a resource pack";
return PackedResourceType::ResourcePack; return PackedResourceType::ResourcePack;
} else if (TexturePackUtils::validate(file)) { } else if (TexturePackUtils::validate(file)) {
@ -53,9 +57,6 @@ PackedResourceType identify(QFileInfo file){
} else if (DataPackUtils::validate(file)) { } else if (DataPackUtils::validate(file)) {
qDebug() << file.fileName() << "is a data pack"; qDebug() << file.fileName() << "is a data pack";
return PackedResourceType::DataPack; return PackedResourceType::DataPack;
} else if (ModUtils::validate(file)) {
qDebug() << file.fileName() << "is a mod";
return PackedResourceType::Mod;
} else if (WorldSaveUtils::validate(file)) { } else if (WorldSaveUtils::validate(file)) {
qDebug() << file.fileName() << "is a world save"; qDebug() << file.fileName() << "is a world save";
return PackedResourceType::WorldSave; return PackedResourceType::WorldSave;

View File

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

View File

@ -10,6 +10,7 @@
#include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameAPI.h"
#include "modplatform/flame/FlameModIndex.h" #include "modplatform/flame/FlameModIndex.h"
#include "modplatform/helpers/HashUtils.h"
#include "modplatform/modrinth/ModrinthAPI.h" #include "modplatform/modrinth/ModrinthAPI.h"
#include "modplatform/modrinth/ModrinthPackIndex.h" #include "modplatform/modrinth/ModrinthPackIndex.h"
@ -24,8 +25,8 @@ EnsureMetadataTask::EnsureMetadataTask(Mod* mod, QDir dir, ModPlatform::Resource
auto hash_task = createNewHash(mod); auto hash_task = createNewHash(mod);
if (!hash_task) if (!hash_task)
return; return;
connect(hash_task.get(), &Task::succeeded, [this, hash_task, mod] { m_mods.insert(hash_task->getResult(), mod); }); connect(hash_task.get(), &Hashing::Hasher::resultsReady, [this, mod](QString hash) { m_mods.insert(hash, mod); });
connect(hash_task.get(), &Task::failed, [this, hash_task, mod] { emitFail(mod, "", RemoveFromList::No); }); connect(hash_task.get(), &Task::failed, [this, mod] { emitFail(mod, "", RemoveFromList::No); });
hash_task->start(); hash_task->start();
} }
@ -37,8 +38,8 @@ EnsureMetadataTask::EnsureMetadataTask(QList<Mod*>& mods, QDir dir, ModPlatform:
auto hash_task = createNewHash(mod); auto hash_task = createNewHash(mod);
if (!hash_task) if (!hash_task)
continue; continue;
connect(hash_task.get(), &Task::succeeded, [this, hash_task, mod] { m_mods.insert(hash_task->getResult(), mod); }); connect(hash_task.get(), &Hashing::Hasher::resultsReady, [this, mod](QString hash) { m_mods.insert(hash, mod); });
connect(hash_task.get(), &Task::failed, [this, hash_task, mod] { emitFail(mod, "", RemoveFromList::No); }); connect(hash_task.get(), &Task::failed, [this, mod] { emitFail(mod, "", RemoveFromList::No); });
m_hashing_task->addTask(hash_task); m_hashing_task->addTask(hash_task);
} }
} }
@ -144,7 +145,8 @@ void EnsureMetadataTask::executeTask()
connect(project_task.get(), &Task::finished, this, [=] { connect(project_task.get(), &Task::finished, this, [=] {
invalidade_leftover(); invalidade_leftover();
project_task->deleteLater(); project_task->deleteLater();
m_current_task = nullptr; if (m_current_task)
m_current_task.reset();
}); });
m_current_task = project_task; m_current_task = project_task;
@ -153,7 +155,8 @@ void EnsureMetadataTask::executeTask()
connect(version_task.get(), &Task::finished, [=] { connect(version_task.get(), &Task::finished, [=] {
version_task->deleteLater(); version_task->deleteLater();
m_current_task = nullptr; if (m_current_task)
m_current_task.reset();
}); });
if (m_mods.size() > 1) if (m_mods.size() > 1)
@ -212,12 +215,12 @@ Task::Ptr EnsureMetadataTask::modrinthVersionsTask()
{ {
auto hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first(); auto hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first();
auto* response = new QByteArray(); auto response = std::make_shared<QByteArray>();
auto ver_task = modrinth_api.currentVersions(m_mods.keys(), hash_type, response); auto ver_task = modrinth_api.currentVersions(m_mods.keys(), hash_type, response);
// Prevents unfortunate timings when aborting the task // Prevents unfortunate timings when aborting the task
if (!ver_task) if (!ver_task)
return Task::Ptr{nullptr}; return Task::Ptr{ nullptr };
connect(ver_task.get(), &Task::succeeded, this, [this, response] { connect(ver_task.get(), &Task::succeeded, this, [this, response] {
QJsonParseError parse_error{}; QJsonParseError parse_error{};
@ -264,7 +267,7 @@ Task::Ptr EnsureMetadataTask::modrinthProjectsTask()
for (auto const& data : m_temp_versions) for (auto const& data : m_temp_versions)
addonIds.insert(data.addonId.toString(), data.hash); addonIds.insert(data.addonId.toString(), data.hash);
auto response = new QByteArray(); auto response = std::make_shared<QByteArray>();
Task::Ptr proj_task; Task::Ptr proj_task;
if (addonIds.isEmpty()) { if (addonIds.isEmpty()) {
@ -277,7 +280,7 @@ Task::Ptr EnsureMetadataTask::modrinthProjectsTask()
// Prevents unfortunate timings when aborting the task // Prevents unfortunate timings when aborting the task
if (!proj_task) if (!proj_task)
return Task::Ptr{nullptr}; return Task::Ptr{ nullptr };
connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] { connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] {
QJsonParseError parse_error{}; QJsonParseError parse_error{};
@ -345,7 +348,7 @@ Task::Ptr EnsureMetadataTask::modrinthProjectsTask()
// Flame // Flame
Task::Ptr EnsureMetadataTask::flameVersionsTask() Task::Ptr EnsureMetadataTask::flameVersionsTask()
{ {
auto* response = new QByteArray(); auto response = std::make_shared<QByteArray>();
QList<uint> fingerprints; QList<uint> fingerprints;
for (auto& murmur : m_mods.keys()) { for (auto& murmur : m_mods.keys()) {
@ -413,7 +416,7 @@ Task::Ptr EnsureMetadataTask::flameProjectsTask()
QHash<QString, QString> addonIds; QHash<QString, QString> addonIds;
for (auto const& hash : m_mods.keys()) { for (auto const& hash : m_mods.keys()) {
if (m_temp_versions.contains(hash)) { if (m_temp_versions.contains(hash)) {
auto const& data = m_temp_versions.find(hash).value(); auto data = m_temp_versions.find(hash).value();
auto id_str = data.addonId.toString(); auto id_str = data.addonId.toString();
if (!id_str.isEmpty()) if (!id_str.isEmpty())
@ -421,7 +424,7 @@ Task::Ptr EnsureMetadataTask::flameProjectsTask()
} }
} }
auto response = new QByteArray(); auto response = std::make_shared<QByteArray>();
Task::Ptr proj_task; Task::Ptr proj_task;
if (addonIds.isEmpty()) { if (addonIds.isEmpty()) {
@ -434,7 +437,7 @@ Task::Ptr EnsureMetadataTask::flameProjectsTask()
// Prevents unfortunate timings when aborting the task // Prevents unfortunate timings when aborting the task
if (!proj_task) if (!proj_task)
return Task::Ptr{nullptr}; return Task::Ptr{ nullptr };
connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] { connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] {
QJsonParseError parse_error{}; QJsonParseError parse_error{};

View File

@ -121,12 +121,12 @@ class ResourceAPI {
qWarning() << "TODO"; qWarning() << "TODO";
return nullptr; return nullptr;
} }
[[nodiscard]] virtual Task::Ptr getProject(QString addonId, QByteArray* response) const [[nodiscard]] virtual Task::Ptr getProject(QString addonId, std::shared_ptr<QByteArray> response) const
{ {
qWarning() << "TODO"; qWarning() << "TODO";
return nullptr; return nullptr;
} }
[[nodiscard]] virtual Task::Ptr getProjects(QStringList addonIds, QByteArray* response) const [[nodiscard]] virtual Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const
{ {
qWarning() << "TODO"; qWarning() << "TODO";
return nullptr; return nullptr;

View File

@ -82,9 +82,9 @@ void PackInstallTask::executeTask()
{ {
qDebug() << "PackInstallTask::executeTask: " << QThread::currentThreadId(); qDebug() << "PackInstallTask::executeTask: " << QThread::currentThreadId();
NetJob::Ptr netJob{ new NetJob("ATLauncher::VersionFetch", APPLICATION->network()) }; NetJob::Ptr netJob{ new NetJob("ATLauncher::VersionFetch", APPLICATION->network()) };
auto searchUrl = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.json") auto searchUrl =
.arg(m_pack_safe_name).arg(m_version_name); QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.json").arg(m_pack_safe_name).arg(m_version_name);
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response)); netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response));
QObject::connect(netJob.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded); QObject::connect(netJob.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded);
QObject::connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed); QObject::connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed);
@ -99,11 +99,12 @@ void PackInstallTask::onDownloadSucceeded()
qDebug() << "PackInstallTask::onDownloadSucceeded: " << QThread::currentThreadId(); qDebug() << "PackInstallTask::onDownloadSucceeded: " << QThread::currentThreadId();
jobPtr.reset(); jobPtr.reset();
QJsonParseError parse_error {}; QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error); QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
if(parse_error.error != QJsonParseError::NoError) { if (parse_error.error != QJsonParseError::NoError) {
qWarning() << "Error while parsing JSON response from ATLauncher at " << parse_error.offset << " reason: " << parse_error.errorString(); qWarning() << "Error while parsing JSON response from ATLauncher at " << parse_error.offset
qWarning() << response; << " reason: " << parse_error.errorString();
qWarning() << *response.get();
return; return;
} }
auto obj = doc.object(); auto obj = doc.object();

View File

@ -40,12 +40,13 @@
#include "ATLPackManifest.h" #include "ATLPackManifest.h"
#include "InstanceTask.h" #include "InstanceTask.h"
#include "net/NetJob.h" #include "meta/Version.h"
#include "settings/INISettingsObject.h"
#include "minecraft/MinecraftInstance.h" #include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h" #include "minecraft/PackProfile.h"
#include "meta/Version.h" #include "net/NetJob.h"
#include "settings/INISettingsObject.h"
#include <memory>
#include <optional> #include <optional>
namespace ATLauncher { namespace ATLauncher {
@ -57,8 +58,7 @@ enum class InstallMode {
}; };
class UserInteractionSupport { class UserInteractionSupport {
public:
public:
/** /**
* Requests a user interaction to select which optional mods should be installed. * Requests a user interaction to select which optional mods should be installed.
*/ */
@ -74,23 +74,27 @@ public:
* Requests a user interaction to display a message. * Requests a user interaction to display a message.
*/ */
virtual void displayMessage(QString message) = 0; virtual void displayMessage(QString message) = 0;
virtual ~UserInteractionSupport() = default;
}; };
class PackInstallTask : public InstanceTask class PackInstallTask : public InstanceTask {
{ Q_OBJECT
Q_OBJECT
public: public:
explicit PackInstallTask(UserInteractionSupport *support, QString packName, QString version, InstallMode installMode = InstallMode::Install); explicit PackInstallTask(UserInteractionSupport* support,
virtual ~PackInstallTask(){} QString packName,
QString version,
InstallMode installMode = InstallMode::Install);
virtual ~PackInstallTask() { delete m_support; }
bool canAbort() const override { return true; } bool canAbort() const override { return true; }
bool abort() override; bool abort() override;
protected: protected:
virtual void executeTask() override; virtual void executeTask() override;
private slots: private slots:
void onDownloadSucceeded(); void onDownloadSucceeded();
void onDownloadFailed(QString reason); void onDownloadFailed(QString reason);
void onDownloadAborted(); void onDownloadAborted();
@ -98,7 +102,7 @@ private slots:
void onModsDownloaded(); void onModsDownloaded();
void onModsExtracted(); void onModsExtracted();
private: private:
QString getDirForModType(ModType type, QString raw); QString getDirForModType(ModType type, QString raw);
QString getVersionForLoader(QString uid); QString getVersionForLoader(QString uid);
QString detectLibrary(VersionLibrary library); QString detectLibrary(VersionLibrary library);
@ -110,20 +114,18 @@ private:
void installConfigs(); void installConfigs();
void extractConfigs(); void extractConfigs();
void downloadMods(); void downloadMods();
bool extractMods( bool extractMods(const QMap<QString, VersionMod>& toExtract,
const QMap<QString, VersionMod> &toExtract, const QMap<QString, VersionMod>& toDecomp,
const QMap<QString, VersionMod> &toDecomp, const QMap<QString, QString>& toCopy);
const QMap<QString, QString> &toCopy
);
void install(); void install();
private: private:
UserInteractionSupport *m_support; UserInteractionSupport* m_support;
bool abortable = false; bool abortable = false;
NetJob::Ptr jobPtr; NetJob::Ptr jobPtr;
QByteArray response; std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>();
InstallMode m_install_mode; InstallMode m_install_mode;
QString m_pack_name; QString m_pack_name;
@ -145,7 +147,6 @@ private:
QFuture<bool> m_modExtractFuture; QFuture<bool> m_modExtractFuture;
QFutureWatcher<bool> m_modExtractFutureWatcher; QFutureWatcher<bool> m_modExtractFutureWatcher;
}; };
} } // namespace ATLauncher

View File

@ -21,19 +21,24 @@ bool Flame::FileResolvingTask::abort()
void Flame::FileResolvingTask::executeTask() 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...")); setStatus(tr("Resolving mod IDs..."));
setProgress(0, 3); setProgress(0, 3);
m_dljob.reset(new NetJob("Mod id resolver", m_network)); m_dljob.reset(new NetJob("Mod id resolver", m_network));
result.reset(new QByteArray()); result.reset(new QByteArray());
//build json data to send // build json data to send
QJsonObject object; QJsonObject object;
object["fileIds"] = QJsonArray::fromVariantList(std::accumulate(m_toProcess.files.begin(), m_toProcess.files.end(), QVariantList(), [](QVariantList& l, const File& s) { object["fileIds"] = QJsonArray::fromVariantList(
l.push_back(s.fileId); std::accumulate(m_toProcess.files.begin(), m_toProcess.files.end(), QVariantList(), [](QVariantList& l, const File& s) {
return l; l.push_back(s.fileId);
})); return l;
}));
QByteArray data = Json::toText(object); QByteArray data = Json::toText(object);
auto dl = Net::Upload::makeByteArray(QUrl("https://api.curseforge.com/v1/mods/files"), result.get(), data); auto dl = Net::Upload::makeByteArray(QUrl("https://api.curseforge.com/v1/mods/files"), result, data);
m_dljob->addNetAction(dl); m_dljob->addNetAction(dl);
auto step_progress = std::make_shared<TaskStepProgress>(); auto step_progress = std::make_shared<TaskStepProgress>();
@ -87,17 +92,15 @@ void Flame::FileResolvingTask::netJobFinished()
auto fileid = Json::requireInteger(Json::requireObject(file)["id"]); auto fileid = Json::requireInteger(Json::requireObject(file)["id"]);
auto& out = m_toProcess.files[fileid]; auto& out = m_toProcess.files[fileid];
try { try {
out.parseFromObject(Json::requireObject(file)); out.parseFromObject(Json::requireObject(file));
} catch (const JSONValidationError& e) { } catch (const JSONValidationError& e) {
qDebug() << "Blocked mod on curseforge" << out.fileName; qDebug() << "Blocked mod on curseforge" << out.fileName;
auto hash = out.hash; auto hash = out.hash;
if(!hash.isEmpty()) { if (!hash.isEmpty()) {
auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash); auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash);
auto output = std::make_shared<QByteArray>(); auto output = std::make_shared<QByteArray>();
auto dl = Net::Download::makeByteArray(QUrl(url), output.get()); auto dl = Net::Download::makeByteArray(QUrl(url), output);
QObject::connect(dl.get(), &Net::Download::succeeded, [&out]() { QObject::connect(dl.get(), &Net::Download::succeeded, [&out]() { out.resolved = true; });
out.resolved = true;
});
m_checkJob->addNetAction(dl); m_checkJob->addNetAction(dl);
blockedProjects.insert(&out, output); blockedProjects.insert(&out, output);
@ -129,12 +132,13 @@ void Flame::FileResolvingTask::netJobFinished()
m_checkJob->start(); m_checkJob->start();
} }
void Flame::FileResolvingTask::modrinthCheckFinished() { void Flame::FileResolvingTask::modrinthCheckFinished()
{
setProgress(2, 3); setProgress(2, 3);
qDebug() << "Finished with blocked mods : " << blockedProjects.size(); qDebug() << "Finished with blocked mods : " << blockedProjects.size();
for (auto it = blockedProjects.keyBegin(); it != blockedProjects.keyEnd(); it++) { for (auto it = blockedProjects.keyBegin(); it != blockedProjects.keyEnd(); it++) {
auto &out = *it; auto& out = *it;
auto bytes = blockedProjects[out]; auto bytes = blockedProjects[out];
if (!out->resolved) { if (!out->resolved) {
continue; continue;
@ -154,28 +158,26 @@ void Flame::FileResolvingTask::modrinthCheckFinished() {
out->resolved = false; 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 block = std::make_shared<QList<File*>>();
auto it = blockedProjects.keys(); auto it = blockedProjects.keys();
std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File *f) { std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File* f) { return !f->resolved; });
return !f->resolved; // Display not found mods early
});
//Display not found mods early
if (!block->empty()) { 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)); m_slugJob.reset(new NetJob("Slug Job", m_network));
int index = 0; int index = 0;
for (auto mod : *block) { for (auto mod : *block) {
auto projectId = mod->projectId; auto projectId = mod->projectId;
auto output = std::make_shared<QByteArray>(); auto output = std::make_shared<QByteArray>();
auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId); auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId);
auto dl = Net::Download::makeByteArray(url, output.get()); auto dl = Net::Download::makeByteArray(url, output);
qDebug() << "Fetching url slug for file:" << mod->fileName; qDebug() << "Fetching url slug for file:" << mod->fileName;
QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() { 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 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 json = QJsonDocument::fromJson(*output);
auto base = Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json),"data"),"links"), auto base =
"websiteUrl"); Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json), "data"), "links"), "websiteUrl");
auto link = QString("%1/download/%2").arg(base, QString::number(mod->fileId)); auto link = QString("%1/download/%2").arg(base, QString::number(mod->fileId));
mod->websiteUrl = link; mod->websiteUrl = link;
}); });

View File

@ -11,7 +11,7 @@
#include "net/NetJob.h" #include "net/NetJob.h"
#include "net/Upload.h" #include "net/Upload.h"
Task::Ptr FlameAPI::matchFingerprints(const QList<uint>& fingerprints, QByteArray* response) Task::Ptr FlameAPI::matchFingerprints(const QList<uint>& fingerprints, std::shared_ptr<QByteArray> response)
{ {
auto netJob = makeShared<NetJob>(QString("Flame::MatchFingerprints"), APPLICATION->network()); auto netJob = makeShared<NetJob>(QString("Flame::MatchFingerprints"), APPLICATION->network());
@ -28,8 +28,6 @@ Task::Ptr FlameAPI::matchFingerprints(const QList<uint>& fingerprints, QByteArra
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/fingerprints"), response, body_raw)); netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/fingerprints"), response, body_raw));
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
return netJob; return netJob;
} }
@ -43,7 +41,7 @@ auto FlameAPI::getModFileChangelog(int modId, int fileId) -> QString
netJob->addNetAction(Net::Download::makeByteArray( netJob->addNetAction(Net::Download::makeByteArray(
QString("https://api.curseforge.com/v1/mods/%1/files/%2/changelog") QString("https://api.curseforge.com/v1/mods/%1/files/%2/changelog")
.arg(QString::fromStdString(std::to_string(modId)), QString::fromStdString(std::to_string(fileId))), .arg(QString::fromStdString(std::to_string(modId)), QString::fromStdString(std::to_string(fileId))),
response.get())); response));
QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &changelog] { QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &changelog] {
QJsonParseError parse_error{}; QJsonParseError parse_error{};
@ -75,8 +73,8 @@ auto FlameAPI::getModDescription(int modId) -> QString
auto netJob = makeShared<NetJob>(QString("Flame::ModDescription"), APPLICATION->network()); auto netJob = makeShared<NetJob>(QString("Flame::ModDescription"), APPLICATION->network());
auto response = std::make_shared<QByteArray>(); auto response = std::make_shared<QByteArray>();
netJob->addNetAction(Net::Download::makeByteArray( netJob->addNetAction(
QString("https://api.curseforge.com/v1/mods/%1/description").arg(QString::number(modId)), response.get())); Net::Download::makeByteArray(QString("https://api.curseforge.com/v1/mods/%1/description").arg(QString::number(modId)), response));
QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &description] { QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &description] {
QJsonParseError parse_error{}; QJsonParseError parse_error{};
@ -115,7 +113,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe
auto response = std::make_shared<QByteArray>(); auto response = std::make_shared<QByteArray>();
ModPlatform::IndexedVersion ver; ModPlatform::IndexedVersion ver;
netJob->addNetAction(Net::Download::makeByteArray(versions_url, response.get())); netJob->addNetAction(Net::Download::makeByteArray(versions_url, response));
QObject::connect(netJob.get(), &NetJob::succeeded, [response, args, &ver] { QObject::connect(netJob.get(), &NetJob::succeeded, [response, args, &ver] {
QJsonParseError parse_error{}; QJsonParseError parse_error{};
@ -137,7 +135,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe
for (auto file : arr) { for (auto file : arr) {
auto file_obj = Json::requireObject(file); auto file_obj = Json::requireObject(file);
auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj); auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj);
if(file_tmp.date > ver_tmp.date) { if (file_tmp.date > ver_tmp.date) {
ver_tmp = file_tmp; ver_tmp = file_tmp;
latest_file_obj = file_obj; latest_file_obj = file_obj;
} }
@ -160,7 +158,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe
return ver; return ver;
} }
Task::Ptr FlameAPI::getProjects(QStringList addonIds, QByteArray* response) const Task::Ptr FlameAPI::getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const
{ {
auto netJob = makeShared<NetJob>(QString("Flame::GetProjects"), APPLICATION->network()); auto netJob = makeShared<NetJob>(QString("Flame::GetProjects"), APPLICATION->network());
@ -177,13 +175,12 @@ Task::Ptr FlameAPI::getProjects(QStringList addonIds, QByteArray* response) cons
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods"), response, body_raw)); netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods"), response, body_raw));
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; }); QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; });
return netJob; return netJob;
} }
Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, QByteArray* response) const Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const
{ {
auto netJob = makeShared<NetJob>(QString("Flame::GetFiles"), APPLICATION->network()); auto netJob = makeShared<NetJob>(QString("Flame::GetFiles"), APPLICATION->network());
@ -200,7 +197,6 @@ Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, QByteArray* response) c
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods/files"), response, body_raw)); netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods/files"), response, body_raw));
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; }); QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; });
return netJob; return netJob;

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <memory>
#include "modplatform/ModIndex.h" #include "modplatform/ModIndex.h"
#include "modplatform/helpers/NetworkResourceAPI.h" #include "modplatform/helpers/NetworkResourceAPI.h"
@ -14,12 +15,14 @@ class FlameAPI : public NetworkResourceAPI {
auto getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::IndexedVersion; auto getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::IndexedVersion;
Task::Ptr getProjects(QStringList addonIds, QByteArray* response) const override; Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
Task::Ptr matchFingerprints(const QList<uint>& fingerprints, QByteArray* response); Task::Ptr matchFingerprints(const QList<uint>& fingerprints, std::shared_ptr<QByteArray> response);
Task::Ptr getFiles(const QStringList& fileIds, QByteArray* response) const; Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const;
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override; [[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt); }
private: private:
static int getClassId(ModPlatform::ResourceType type) static int getClassId(ModPlatform::ResourceType type)
{ {
@ -41,14 +44,15 @@ class FlameAPI : public NetworkResourceAPI {
return 4; return 4;
// TODO: remove this once Quilt drops official Fabric support // TODO: remove this once Quilt drops official Fabric support
if (loaders & Quilt) // NOTE: Most if not all Fabric mods should work *currently* if (loaders & Quilt) // NOTE: Most if not all Fabric mods should work *currently*
return 4; // Quilt would probably be 5 return 4; // Quilt would probably be 5
return 0; return 0;
} }
private: private:
[[nodiscard]] std::optional<QString> getSearchURL(SearchArgs const& args) const override [[nodiscard]] std::optional<QString> getSearchURL(SearchArgs const& args) const override
{ {
auto gameVersionStr = args.versions.has_value() ? QString("gameVersion=%1").arg(args.versions.value().front().toString()) : QString(); auto gameVersionStr =
args.versions.has_value() ? QString("gameVersion=%1").arg(args.versions.value().front().toString()) : QString();
QStringList get_arguments; QStringList get_arguments;
get_arguments.append(QString("classId=%1").arg(getClassId(args.type))); get_arguments.append(QString("classId=%1").arg(getClassId(args.type)));
@ -73,7 +77,7 @@ class FlameAPI : public NetworkResourceAPI {
[[nodiscard]] std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override [[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())}; QString url{ QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&").arg(args.pack.addonId.toString()) };
QStringList get_parameters; QStringList get_parameters;
if (args.mcVersions.has_value()) if (args.mcVersions.has_value())

View File

@ -31,7 +31,7 @@ ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info)
auto get_project_job = new NetJob("Flame::GetProjectJob", APPLICATION->network()); auto get_project_job = new NetJob("Flame::GetProjectJob", APPLICATION->network());
auto response = new QByteArray(); auto response = std::make_shared<QByteArray>();
auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(ver_info.addonId.toString()); auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(ver_info.addonId.toString());
auto dl = Net::Download::makeByteArray(url, response); auto dl = Net::Download::makeByteArray(url, response);
get_project_job->addNetAction(dl); get_project_job->addNetAction(dl);
@ -75,7 +75,7 @@ ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId)
auto get_file_info_job = new NetJob("Flame::GetFileInfoJob", APPLICATION->network()); auto get_file_info_job = new NetJob("Flame::GetFileInfoJob", APPLICATION->network());
auto response = new QByteArray(); auto response = std::make_shared<QByteArray>();
auto url = QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(QString::number(addonId), QString::number(fileId)); auto url = QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(QString::number(addonId), QString::number(fileId));
auto dl = Net::Download::makeByteArray(url, response); auto dl = Net::Download::makeByteArray(url, response);
get_file_info_job->addNetAction(dl); get_file_info_job->addNetAction(dl);

View File

@ -153,6 +153,9 @@ bool FlameCreationTask::updateInstance()
old_files.remove(file.key()); old_files.remove(file.key());
files_iterator = files.erase(files_iterator); files_iterator = files.erase(files_iterator);
if (files_iterator != files.begin())
files_iterator--;
} }
} }
@ -179,7 +182,7 @@ bool FlameCreationTask::updateInstance()
fileIds.append(QString::number(file.fileId)); fileIds.append(QString::number(file.fileId));
} }
auto* raw_response = new QByteArray; auto raw_response = std::make_shared<QByteArray>();
auto job = api.getFiles(fileIds, raw_response); auto job = api.getFiles(fileIds, raw_response);
QEventLoop loop; QEventLoop loop;
@ -467,8 +470,9 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
switch (result.type) { switch (result.type) {
case Flame::File::Type::Folder: { case Flame::File::Type::Folder: {
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath)); logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
// fall-through intentional, we treat these as plain old mods and dump them wherever. // fallthrough intentional, we treat these as plain old mods and dump them wherever.
} }
/* fallthrough */
case Flame::File::Type::SingleFile: case Flame::File::Type::SingleFile:
case Flame::File::Type::Mod: { case Flame::File::Type::Mod: {
if (!result.url.isEmpty()) { if (!result.url.isEmpty()) {
@ -559,6 +563,8 @@ void FlameCreationTask::validateZIPResouces()
if (FS::move(localPath, destPath)) { if (FS::move(localPath, destPath)) {
return destPath; return destPath;
} }
} else {
qDebug() << "Target folder of" << fileName << "is correct at" << targetFolder;
} }
return localPath; return localPath;
}; };
@ -580,6 +586,9 @@ void FlameCreationTask::validateZIPResouces()
QString worldPath; QString worldPath;
switch (type) { switch (type) {
case PackedResourceType::Mod :
validatePath(fileName, targetFolder, "mods");
break;
case PackedResourceType::ResourcePack : case PackedResourceType::ResourcePack :
validatePath(fileName, targetFolder, "resourcepacks"); validatePath(fileName, targetFolder, "resourcepacks");
break; break;
@ -589,9 +598,6 @@ void FlameCreationTask::validateZIPResouces()
case PackedResourceType::DataPack : case PackedResourceType::DataPack :
validatePath(fileName, targetFolder, "datapacks"); validatePath(fileName, targetFolder, "datapacks");
break; break;
case PackedResourceType::Mod :
validatePath(fileName, targetFolder, "mods");
break;
case PackedResourceType::ShaderPack : case PackedResourceType::ShaderPack :
// in theroy flame API can't do this but who knows, that *may* change ? // 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 // better to handle it if it *does* occure in the future

View File

@ -76,13 +76,8 @@ bool Flame::File::parseFromObject(const QJsonObject& obj, bool throw_on_blocked
// It is also optional // It is also optional
type = File::Type::SingleFile; type = File::Type::SingleFile;
if (fileName.endsWith(".zip")) { targetFolder = "mods";
// 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 // get the hash
hash = QString(); hash = QString();
auto hashes = Json::ensureArray(obj, "hashes"); auto hashes = Json::ensureArray(obj, "hashes");

View File

@ -71,6 +71,7 @@ void ModrinthHasher::executeTask()
emitFailed("Empty hash!"); emitFailed("Empty hash!");
} else { } else {
emitSucceeded(); emitSucceeded();
emit resultsReady(m_hash);
} }
} }
@ -88,12 +89,12 @@ void FlameHasher::executeTask()
emitFailed("Empty hash!"); emitFailed("Empty hash!");
} else { } else {
emitSucceeded(); emitSucceeded();
emit resultsReady(m_hash);
} }
} }
BlockedModHasher::BlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider) : Hasher(file_path), provider(provider)
BlockedModHasher::BlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider) {
: Hasher(file_path), provider(provider) {
setObjectName(QString("BlockedModHasher: %1").arg(file_path)); setObjectName(QString("BlockedModHasher: %1").arg(file_path));
hash_type = ProviderCaps.hashType(provider).first(); hash_type = ProviderCaps.hashType(provider).first();
} }
@ -120,14 +121,17 @@ void BlockedModHasher::executeTask()
emitFailed("Empty hash!"); emitFailed("Empty hash!");
} else { } else {
emitSucceeded(); emitSucceeded();
emit resultsReady(m_hash);
} }
} }
QStringList BlockedModHasher::getHashTypes() { QStringList BlockedModHasher::getHashTypes()
{
return ProviderCaps.hashType(provider); return ProviderCaps.hashType(provider);
} }
bool BlockedModHasher::useHashType(QString type) { bool BlockedModHasher::useHashType(QString type)
{
auto types = ProviderCaps.hashType(provider); auto types = ProviderCaps.hashType(provider);
if (types.contains(type)) { if (types.contains(type)) {
hash_type = type; hash_type = type;

View File

@ -8,6 +8,7 @@
namespace Hashing { namespace Hashing {
class Hasher : public Task { class Hasher : public Task {
Q_OBJECT
public: public:
using Ptr = shared_qobject_ptr<Hasher>; using Ptr = shared_qobject_ptr<Hasher>;
@ -21,6 +22,9 @@ class Hasher : public Task {
QString getResult() const { return m_hash; }; QString getResult() const { return m_hash; };
QString getPath() const { return m_path; }; QString getPath() const { return m_path; };
signals:
void resultsReady(QString hash);
protected: protected:
QString m_hash; QString m_hash;
QString m_path; QString m_path;
@ -48,6 +52,7 @@ class BlockedModHasher : public Hasher {
QStringList getHashTypes(); QStringList getHashTypes();
bool useHashType(QString type); bool useHashType(QString type);
private: private:
ModPlatform::ResourceProvider provider; ModPlatform::ResourceProvider provider;
QString hash_type; QString hash_type;

View File

@ -3,6 +3,7 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
#include "NetworkResourceAPI.h" #include "NetworkResourceAPI.h"
#include <memory>
#include "Application.h" #include "Application.h"
#include "net/NetJob.h" #include "net/NetJob.h"
@ -19,12 +20,12 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&
auto search_url = search_url_optional.value(); auto search_url = search_url_optional.value();
auto response = new QByteArray(); auto response = std::make_shared<QByteArray>();
auto netJob = makeShared<NetJob>(QString("%1::Search").arg(debugName()), APPLICATION->network()); auto netJob = makeShared<NetJob>(QString("%1::Search").arg(debugName()), APPLICATION->network());
netJob->addNetAction(Net::Download::makeByteArray(QUrl(search_url), response)); netJob->addNetAction(Net::Download::makeByteArray(QUrl(search_url), response));
QObject::connect(netJob.get(), &NetJob::succeeded, [this, response, callbacks]{ QObject::connect(netJob.get(), &NetJob::succeeded, [this, response, callbacks] {
QJsonParseError parse_error{}; QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) { if (parse_error.error != QJsonParseError::NoError) {
@ -40,27 +41,21 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&
callbacks.on_succeed(doc); callbacks.on_succeed(doc);
}); });
QObject::connect(netJob.get(), &NetJob::failed, [&netJob, callbacks](QString reason){ QObject::connect(netJob.get(), &NetJob::failed, [&netJob, callbacks](QString reason) {
int network_error_code = -1; int network_error_code = -1;
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply) if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply)
network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
callbacks.on_fail(reason, network_error_code); callbacks.on_fail(reason, network_error_code);
}); });
QObject::connect(netJob.get(), &NetJob::aborted, [callbacks]{ QObject::connect(netJob.get(), &NetJob::aborted, [callbacks] { callbacks.on_abort(); });
callbacks.on_abort();
});
QObject::connect(netJob.get(), &NetJob::finished, [response] {
delete response;
});
return netJob; return netJob;
} }
Task::Ptr NetworkResourceAPI::getProjectInfo(ProjectInfoArgs&& args, ProjectInfoCallbacks&& callbacks) const Task::Ptr NetworkResourceAPI::getProjectInfo(ProjectInfoArgs&& args, ProjectInfoCallbacks&& callbacks) const
{ {
auto response = new QByteArray(); auto response = std::make_shared<QByteArray>();
auto job = getProject(args.pack.addonId.toString(), response); auto job = getProject(args.pack.addonId.toString(), response);
QObject::connect(job.get(), &NetJob::succeeded, [response, callbacks, args] { QObject::connect(job.get(), &NetJob::succeeded, [response, callbacks, args] {
@ -88,7 +83,7 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi
auto versions_url = versions_url_optional.value(); auto versions_url = versions_url_optional.value();
auto netJob = makeShared<NetJob>(QString("%1::Versions").arg(args.pack.name), APPLICATION->network()); auto netJob = makeShared<NetJob>(QString("%1::Versions").arg(args.pack.name), APPLICATION->network());
auto response = new QByteArray(); auto response = std::make_shared<QByteArray>();
netJob->addNetAction(Net::Download::makeByteArray(versions_url, response)); netJob->addNetAction(Net::Download::makeByteArray(versions_url, response));
@ -105,14 +100,10 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi
callbacks.on_succeed(doc, args.pack); callbacks.on_succeed(doc, args.pack);
}); });
QObject::connect(netJob.get(), &NetJob::finished, [response] {
delete response;
});
return netJob; return netJob;
} }
Task::Ptr NetworkResourceAPI::getProject(QString addonId, QByteArray* response) const Task::Ptr NetworkResourceAPI::getProject(QString addonId, std::shared_ptr<QByteArray> response) const
{ {
auto project_url_optional = getInfoURL(addonId); auto project_url_optional = getInfoURL(addonId);
if (!project_url_optional.has_value()) if (!project_url_optional.has_value())
@ -124,9 +115,5 @@ Task::Ptr NetworkResourceAPI::getProject(QString addonId, QByteArray* response)
netJob->addNetAction(Net::Download::makeByteArray(QUrl(project_url), response)); netJob->addNetAction(Net::Download::makeByteArray(QUrl(project_url), response));
QObject::connect(netJob.get(), &NetJob::finished, [response] {
delete response;
});
return netJob; return netJob;
} }

View File

@ -4,13 +4,14 @@
#pragma once #pragma once
#include <memory>
#include "modplatform/ResourceAPI.h" #include "modplatform/ResourceAPI.h"
class NetworkResourceAPI : public ResourceAPI { class NetworkResourceAPI : public ResourceAPI {
public: public:
Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const override; Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const override;
Task::Ptr getProject(QString addonId, QByteArray* response) const override; Task::Ptr getProject(QString addonId, std::shared_ptr<QByteArray> response) const override;
Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const override; Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const override;
Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const override; Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const override;

View File

@ -51,11 +51,11 @@ void PackFetchTask::fetch()
QUrl publicPacksUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/modpacks.xml"); QUrl publicPacksUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/modpacks.xml");
qDebug() << "Downloading public version info from" << publicPacksUrl.toString(); qDebug() << "Downloading public version info from" << publicPacksUrl.toString();
jobPtr->addNetAction(Net::Download::makeByteArray(publicPacksUrl, &publicModpacksXmlFileData)); jobPtr->addNetAction(Net::Download::makeByteArray(publicPacksUrl, publicModpacksXmlFileData));
QUrl thirdPartyUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/thirdparty.xml"); QUrl thirdPartyUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/thirdparty.xml");
qDebug() << "Downloading thirdparty version info from" << thirdPartyUrl.toString(); qDebug() << "Downloading thirdparty version info from" << thirdPartyUrl.toString();
jobPtr->addNetAction(Net::Download::makeByteArray(thirdPartyUrl, &thirdPartyModpacksXmlFileData)); jobPtr->addNetAction(Net::Download::makeByteArray(thirdPartyUrl, thirdPartyModpacksXmlFileData));
QObject::connect(jobPtr.get(), &NetJob::succeeded, this, &PackFetchTask::fileDownloadFinished); QObject::connect(jobPtr.get(), &NetJob::succeeded, this, &PackFetchTask::fileDownloadFinished);
QObject::connect(jobPtr.get(), &NetJob::failed, this, &PackFetchTask::fileDownloadFailed); QObject::connect(jobPtr.get(), &NetJob::failed, this, &PackFetchTask::fileDownloadFailed);
@ -64,22 +64,19 @@ void PackFetchTask::fetch()
jobPtr->start(); jobPtr->start();
} }
void PackFetchTask::fetchPrivate(const QStringList & toFetch) void PackFetchTask::fetchPrivate(const QStringList& toFetch)
{ {
QString privatePackBaseUrl = BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/%1.xml"; QString privatePackBaseUrl = BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/%1.xml";
for (auto &packCode: toFetch) for (auto& packCode : toFetch) {
{ auto data = std::make_shared<QByteArray>();
QByteArray *data = new QByteArray(); NetJob* job = new NetJob("Fetching private pack", m_network);
NetJob *job = new NetJob("Fetching private pack", m_network);
job->addNetAction(Net::Download::makeByteArray(privatePackBaseUrl.arg(packCode), data)); job->addNetAction(Net::Download::makeByteArray(privatePackBaseUrl.arg(packCode), data));
QObject::connect(job, &NetJob::succeeded, this, [this, job, data, packCode] QObject::connect(job, &NetJob::succeeded, this, [this, job, data, packCode] {
{
ModpackList packs; ModpackList packs;
parseAndAddPacks(*data, PackType::Private, packs); parseAndAddPacks(*data, PackType::Private, packs);
foreach(Modpack currentPack, packs) foreach (Modpack currentPack, packs) {
{
currentPack.packCode = packCode; currentPack.packCode = packCode;
emit privateFileDownloadFinished(currentPack); emit privateFileDownloadFinished(currentPack);
} }
@ -87,24 +84,20 @@ void PackFetchTask::fetchPrivate(const QStringList & toFetch)
job->deleteLater(); job->deleteLater();
data->clear(); data->clear();
delete data;
}); });
QObject::connect(job, &NetJob::failed, this, [this, job, packCode, data](QString reason) QObject::connect(job, &NetJob::failed, this, [this, job, packCode, data](QString reason) {
{
emit privateFileDownloadFailed(reason, packCode); emit privateFileDownloadFailed(reason, packCode);
job->deleteLater(); job->deleteLater();
data->clear(); data->clear();
delete data;
}); });
QObject::connect(job, &NetJob::aborted, this, [this, job, data]{ QObject::connect(job, &NetJob::aborted, this, [this, job, data] {
emit aborted(); emit aborted();
job->deleteLater(); job->deleteLater();
data->clear(); data->clear();
delete data;
}); });
job->start(); job->start();
@ -117,27 +110,22 @@ void PackFetchTask::fileDownloadFinished()
QStringList failedLists; QStringList failedLists;
if(!parseAndAddPacks(publicModpacksXmlFileData, PackType::Public, publicPacks)) if (!parseAndAddPacks(*publicModpacksXmlFileData, PackType::Public, publicPacks)) {
{
failedLists.append(tr("Public Packs")); failedLists.append(tr("Public Packs"));
} }
if(!parseAndAddPacks(thirdPartyModpacksXmlFileData, PackType::ThirdParty, thirdPartyPacks)) if (!parseAndAddPacks(*thirdPartyModpacksXmlFileData, PackType::ThirdParty, thirdPartyPacks)) {
{
failedLists.append(tr("Third Party Packs")); failedLists.append(tr("Third Party Packs"));
} }
if(failedLists.size() > 0) if (failedLists.size() > 0) {
{
emit failed(tr("Failed to download some pack lists: %1").arg(failedLists.join("\n- "))); emit failed(tr("Failed to download some pack lists: %1").arg(failedLists.join("\n- ")));
} } else {
else
{
emit finished(publicPacks, thirdPartyPacks); emit finished(publicPacks, thirdPartyPacks);
} }
} }
bool PackFetchTask::parseAndAddPacks(QByteArray &data, PackType packType, ModpackList &list) bool PackFetchTask::parseAndAddPacks(QByteArray& data, PackType packType, ModpackList& list)
{ {
QDomDocument doc; QDomDocument doc;
@ -145,8 +133,7 @@ bool PackFetchTask::parseAndAddPacks(QByteArray &data, PackType packType, Modpac
int errorLine = -1; int errorLine = -1;
int errorCol = -1; int errorCol = -1;
if(!doc.setContent(data, false, &errorMsg, &errorLine, &errorCol)) if (!doc.setContent(data, false, &errorMsg, &errorLine, &errorCol)) {
{
auto fullErrMsg = QString("Failed to fetch modpack data: %1 %2:%3!").arg(errorMsg).arg(errorLine).arg(errorCol); auto fullErrMsg = QString("Failed to fetch modpack data: %1 %2:%3!").arg(errorMsg).arg(errorLine).arg(errorCol);
qWarning() << fullErrMsg; qWarning() << fullErrMsg;
data.clear(); data.clear();
@ -154,8 +141,7 @@ bool PackFetchTask::parseAndAddPacks(QByteArray &data, PackType packType, Modpac
} }
QDomNodeList nodes = doc.elementsByTagName("modpack"); QDomNodeList nodes = doc.elementsByTagName("modpack");
for(int i = 0; i < nodes.length(); i++) for (int i = 0; i < nodes.length(); i++) {
{
QDomElement element = nodes.at(i).toElement(); QDomElement element = nodes.at(i).toElement();
Modpack modpack; Modpack modpack;
@ -169,26 +155,20 @@ bool PackFetchTask::parseAndAddPacks(QByteArray &data, PackType packType, Modpac
modpack.broken = false; modpack.broken = false;
modpack.bugged = false; modpack.bugged = false;
//remove empty if the xml is bugged // remove empty if the xml is bugged
for(QString curr : modpack.oldVersions) for (QString curr : modpack.oldVersions) {
{ if (curr.isNull() || curr.isEmpty()) {
if(curr.isNull() || curr.isEmpty())
{
modpack.oldVersions.removeAll(curr); modpack.oldVersions.removeAll(curr);
modpack.bugged = true; modpack.bugged = true;
qWarning() << "Removed some empty versions from" << modpack.name; qWarning() << "Removed some empty versions from" << modpack.name;
} }
} }
if(modpack.oldVersions.size() < 1) if (modpack.oldVersions.size() < 1) {
{ if (!modpack.currentVersion.isNull() && !modpack.currentVersion.isEmpty()) {
if(!modpack.currentVersion.isNull() && !modpack.currentVersion.isEmpty())
{
modpack.oldVersions.append(modpack.currentVersion); modpack.oldVersions.append(modpack.currentVersion);
qWarning() << "Added current version to oldVersions because oldVersions was empty! (" + modpack.name + ")"; qWarning() << "Added current version to oldVersions because oldVersions was empty! (" + modpack.name + ")";
} } else {
else
{
modpack.broken = true; modpack.broken = true;
qWarning() << "Broken pack:" << modpack.name << " => No valid version!"; qWarning() << "Broken pack:" << modpack.name << " => No valid version!";
} }
@ -218,4 +198,4 @@ void PackFetchTask::fileDownloadAborted()
emit aborted(); emit aborted();
} }
} } // namespace LegacyFTB

View File

@ -1,41 +1,41 @@
#pragma once #pragma once
#include "net/NetJob.h"
#include <QTemporaryDir>
#include <QByteArray> #include <QByteArray>
#include <QObject> #include <QObject>
#include <QTemporaryDir>
#include <memory>
#include "PackHelpers.h" #include "PackHelpers.h"
#include "net/NetJob.h"
namespace LegacyFTB { namespace LegacyFTB {
class PackFetchTask : public QObject { class PackFetchTask : public QObject {
Q_OBJECT Q_OBJECT
public: public:
PackFetchTask(shared_qobject_ptr<QNetworkAccessManager> network) : QObject(nullptr), m_network(network) {}; PackFetchTask(shared_qobject_ptr<QNetworkAccessManager> network) : QObject(nullptr), m_network(network){};
virtual ~PackFetchTask() = default; virtual ~PackFetchTask() = default;
void fetch(); void fetch();
void fetchPrivate(const QStringList &toFetch); void fetchPrivate(const QStringList& toFetch);
private: private:
shared_qobject_ptr<QNetworkAccessManager> m_network; shared_qobject_ptr<QNetworkAccessManager> m_network;
NetJob::Ptr jobPtr; NetJob::Ptr jobPtr;
QByteArray publicModpacksXmlFileData; std::shared_ptr<QByteArray> publicModpacksXmlFileData = std::make_shared<QByteArray>();
QByteArray thirdPartyModpacksXmlFileData; std::shared_ptr<QByteArray> thirdPartyModpacksXmlFileData = std::make_shared<QByteArray>();
bool parseAndAddPacks(QByteArray &data, PackType packType, ModpackList &list); bool parseAndAddPacks(QByteArray& data, PackType packType, ModpackList& list);
ModpackList publicPacks; ModpackList publicPacks;
ModpackList thirdPartyPacks; ModpackList thirdPartyPacks;
protected slots: protected slots:
void fileDownloadFinished(); void fileDownloadFinished();
void fileDownloadFailed(QString reason); void fileDownloadFailed(QString reason);
void fileDownloadAborted(); void fileDownloadAborted();
signals: signals:
void finished(ModpackList publicPacks, ModpackList thirdPartyPacks); void finished(ModpackList publicPacks, ModpackList thirdPartyPacks);
void failed(QString reason); void failed(QString reason);
void aborted(); void aborted();
@ -44,4 +44,4 @@ signals:
void privateFileDownloadFailed(QString reason, QString packCode); void privateFileDownloadFailed(QString reason, QString packCode);
}; };
} } // namespace LegacyFTB

View File

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

View File

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

View File

@ -9,19 +9,17 @@
#include "net/NetJob.h" #include "net/NetJob.h"
#include "net/Upload.h" #include "net/Upload.h"
Task::Ptr ModrinthAPI::currentVersion(QString hash, QString hash_format, QByteArray* response) Task::Ptr ModrinthAPI::currentVersion(QString hash, QString hash_format, std::shared_ptr<QByteArray> response)
{ {
auto netJob = makeShared<NetJob>(QString("Modrinth::GetCurrentVersion"), APPLICATION->network()); auto netJob = makeShared<NetJob>(QString("Modrinth::GetCurrentVersion"), APPLICATION->network());
netJob->addNetAction(Net::Download::makeByteArray( netJob->addNetAction(Net::Download::makeByteArray(
QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1?algorithm=%2").arg(hash, hash_format), response)); QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1?algorithm=%2").arg(hash, hash_format), response));
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
return netJob; return netJob;
} }
Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_format, QByteArray* response) Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_format, std::shared_ptr<QByteArray> response)
{ {
auto netJob = makeShared<NetJob>(QString("Modrinth::GetCurrentVersions"), APPLICATION->network()); auto netJob = makeShared<NetJob>(QString("Modrinth::GetCurrentVersions"), APPLICATION->network());
@ -35,8 +33,6 @@ Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_f
netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), response, body_raw)); netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), response, body_raw));
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
return netJob; return netJob;
} }
@ -44,7 +40,7 @@ Task::Ptr ModrinthAPI::latestVersion(QString hash,
QString hash_format, QString hash_format,
std::optional<std::list<Version>> mcVersions, std::optional<std::list<Version>> mcVersions,
std::optional<ModLoaderTypes> loaders, std::optional<ModLoaderTypes> loaders,
QByteArray* response) std::shared_ptr<QByteArray> response)
{ {
auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersion"), APPLICATION->network()); auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersion"), APPLICATION->network());
@ -67,8 +63,6 @@ Task::Ptr ModrinthAPI::latestVersion(QString hash,
netJob->addNetAction(Net::Upload::makeByteArray( netJob->addNetAction(Net::Upload::makeByteArray(
QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1/update?algorithm=%2").arg(hash, hash_format), response, body_raw)); QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1/update?algorithm=%2").arg(hash, hash_format), response, body_raw));
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
return netJob; return netJob;
} }
@ -76,7 +70,7 @@ Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes,
QString hash_format, QString hash_format,
std::optional<std::list<Version>> mcVersions, std::optional<std::list<Version>> mcVersions,
std::optional<ModLoaderTypes> loaders, std::optional<ModLoaderTypes> loaders,
QByteArray* response) std::shared_ptr<QByteArray> response)
{ {
auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersions"), APPLICATION->network()); auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersions"), APPLICATION->network());
@ -101,22 +95,16 @@ Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes,
netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), response, body_raw)); netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), response, body_raw));
QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
return netJob; return netJob;
} }
Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, QByteArray* response) const Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const
{ {
auto netJob = makeShared<NetJob>(QString("Modrinth::GetProjects"), APPLICATION->network()); auto netJob = makeShared<NetJob>(QString("Modrinth::GetProjects"), APPLICATION->network());
auto searchUrl = getMultipleModInfoURL(addonIds); auto searchUrl = getMultipleModInfoURL(addonIds);
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response)); netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response));
QObject::connect(netJob.get(), &NetJob::finished, [response, netJob] {
delete response;
});
return netJob; return netJob;
} }

View File

@ -12,27 +12,23 @@
class ModrinthAPI : public NetworkResourceAPI { class ModrinthAPI : public NetworkResourceAPI {
public: public:
auto currentVersion(QString hash, auto currentVersion(QString hash, QString hash_format, std::shared_ptr<QByteArray> response) -> Task::Ptr;
QString hash_format,
QByteArray* response) -> Task::Ptr;
auto currentVersions(const QStringList& hashes, auto currentVersions(const QStringList& hashes, QString hash_format, std::shared_ptr<QByteArray> response) -> Task::Ptr;
QString hash_format,
QByteArray* response) -> Task::Ptr;
auto latestVersion(QString hash, auto latestVersion(QString hash,
QString hash_format, QString hash_format,
std::optional<std::list<Version>> mcVersions, std::optional<std::list<Version>> mcVersions,
std::optional<ModLoaderTypes> loaders, std::optional<ModLoaderTypes> loaders,
QByteArray* response) -> Task::Ptr; std::shared_ptr<QByteArray> response) -> Task::Ptr;
auto latestVersions(const QStringList& hashes, auto latestVersions(const QStringList& hashes,
QString hash_format, QString hash_format,
std::optional<std::list<Version>> mcVersions, std::optional<std::list<Version>> mcVersions,
std::optional<ModLoaderTypes> loaders, std::optional<ModLoaderTypes> loaders,
QByteArray* response) -> Task::Ptr; std::shared_ptr<QByteArray> response) -> Task::Ptr;
Task::Ptr getProjects(QStringList addonIds, QByteArray* response) const override; Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
public: public:
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override; [[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
@ -42,7 +38,7 @@ class ModrinthAPI : public NetworkResourceAPI {
static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList
{ {
QStringList l; QStringList l;
for (auto loader : {Forge, Fabric, Quilt}) { for (auto loader : { Forge, Fabric, Quilt, LiteLoader }) {
if (types & loader) { if (types & loader) {
l << getModLoaderString(loader); l << getModLoaderString(loader);
} }
@ -97,7 +93,7 @@ class ModrinthAPI : public NetworkResourceAPI {
{ {
if (args.loaders.has_value()) { if (args.loaders.has_value()) {
if (!validateModLoaders(args.loaders.value())) { if (!validateModLoaders(args.loaders.value())) {
qWarning() << "Modrinth only have Forge and Fabric-compatible mods!"; qWarning() << "Modrinth - or our interface - does not support any the provided mod loaders!";
return {}; return {};
} }
} }
@ -146,9 +142,6 @@ class ModrinthAPI : public NetworkResourceAPI {
return s.isEmpty() ? QString() : s; return s.isEmpty() ? QString() : s;
} }
inline auto validateModLoaders(ModLoaderTypes loaders) const -> bool static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt | LiteLoader); }
{
return loaders & (Forge | Fabric | Quilt);
}
}; };

View File

@ -53,12 +53,11 @@ void ModrinthCheckUpdate::executeTask()
// (though it will rarely happen, if at all) // (though it will rarely happen, if at all)
if (mod->metadata()->hash_format != best_hash_type) { if (mod->metadata()->hash_format != best_hash_type) {
auto hash_task = Hashing::createModrinthHasher(mod->fileinfo().absoluteFilePath()); auto hash_task = Hashing::createModrinthHasher(mod->fileinfo().absoluteFilePath());
connect(hash_task.get(), &Task::succeeded, [&] { connect(hash_task.get(), &Hashing::Hasher::resultsReady, [&hashes, &mappings, mod](QString hash) {
QString hash(hash_task->getResult());
hashes.append(hash); hashes.append(hash);
mappings.insert(hash, mod); mappings.insert(hash, mod);
}); });
connect(hash_task.get(), &Task::failed, [this, hash_task] { failed("Failed to generate hash"); }); connect(hash_task.get(), &Task::failed, [this] { failed("Failed to generate hash"); });
hashing_task.addTask(hash_task); hashing_task.addTask(hash_task);
} else { } else {
hashes.append(hash); hashes.append(hash);
@ -71,7 +70,7 @@ void ModrinthCheckUpdate::executeTask()
hashing_task.start(); hashing_task.start();
loop.exec(); loop.exec();
auto* response = new QByteArray(); auto response = std::make_shared<QByteArray>();
auto job = api.latestVersions(hashes, best_hash_type, m_game_versions, m_loaders, response); auto job = api.latestVersions(hashes, best_hash_type, m_game_versions, m_loaders, response);
QEventLoop lock; QEventLoop lock;

View File

@ -214,7 +214,7 @@ bool ModrinthCreationTask::createInstance()
if (m_instIcon != "default") { if (m_instIcon != "default") {
instance.setIconKey(m_instIcon); instance.setIconKey(m_instIcon);
} else { } else if (!m_managed_id.isEmpty()) {
instance.setIconKey("modrinth"); instance.setIconKey("modrinth");
} }

View File

@ -134,8 +134,8 @@ void ModrinthPackExportTask::collectHashes()
QCryptographicHash sha1(QCryptographicHash::Algorithm::Sha1); QCryptographicHash sha1(QCryptographicHash::Algorithm::Sha1);
sha1.addData(data); sha1.addData(data);
ResolvedFile file{ sha1.result().toHex(), sha512.result().toHex(), url.toString(), openFile.size() }; ResolvedFile resolvedFile{ sha1.result().toHex(), sha512.result().toHex(), url.toEncoded(), openFile.size() };
resolvedFiles[relative] = file; resolvedFiles[relative] = resolvedFile;
// nice! we've managed to resolve based on local metadata! // nice! we've managed to resolve based on local metadata!
// no need to enqueue it // no need to enqueue it
@ -157,7 +157,7 @@ void ModrinthPackExportTask::makeApiRequest()
if (pendingHashes.isEmpty()) if (pendingHashes.isEmpty())
buildZip(); buildZip();
else { else {
QByteArray* response = new QByteArray; auto response = std::make_shared<QByteArray>();
task = api.currentVersions(pendingHashes.values(), "sha512", response); task = api.currentVersions(pendingHashes.values(), "sha512", response);
connect(task.get(), &NetJob::succeeded, [this, response]() { parseApiResponse(response); }); connect(task.get(), &NetJob::succeeded, [this, response]() { parseApiResponse(response); });
connect(task.get(), &NetJob::failed, this, &ModrinthPackExportTask::emitFailed); connect(task.get(), &NetJob::failed, this, &ModrinthPackExportTask::emitFailed);
@ -165,7 +165,7 @@ void ModrinthPackExportTask::makeApiRequest()
} }
} }
void ModrinthPackExportTask::parseApiResponse(const QByteArray* response) void ModrinthPackExportTask::parseApiResponse(const std::shared_ptr<QByteArray> response)
{ {
task = nullptr; task = nullptr;
@ -263,13 +263,13 @@ void ModrinthPackExportTask::finish()
QByteArray ModrinthPackExportTask::generateIndex() QByteArray ModrinthPackExportTask::generateIndex()
{ {
QJsonObject obj; QJsonObject out;
obj["formatVersion"] = 1; out["formatVersion"] = 1;
obj["game"] = "minecraft"; out["game"] = "minecraft";
obj["name"] = name; out["name"] = name;
obj["versionId"] = version; out["versionId"] = version;
if (!summary.isEmpty()) if (!summary.isEmpty())
obj["summary"] = summary; out["summary"] = summary;
if (mcInstance) { if (mcInstance) {
auto profile = mcInstance->getPackProfile(); auto profile = mcInstance->getPackProfile();
@ -290,30 +290,40 @@ QByteArray ModrinthPackExportTask::generateIndex()
if (forge != nullptr) if (forge != nullptr)
dependencies["forge"] = forge->m_version; dependencies["forge"] = forge->m_version;
obj["dependencies"] = dependencies; out["dependencies"] = dependencies;
} }
QJsonArray files; QJsonArray filesOut;
QMapIterator<QString, ResolvedFile> iterator(resolvedFiles); for (auto iterator = resolvedFiles.constBegin(); iterator != resolvedFiles.constEnd(); iterator++) {
while (iterator.hasNext()) { QJsonObject fileOut;
iterator.next();
QString path = iterator.key();
const ResolvedFile& value = iterator.value(); const ResolvedFile& value = iterator.value();
QJsonObject file; // detect disabled mod
file["path"] = iterator.key(); const QFileInfo pathInfo(path);
file["downloads"] = QJsonArray({ iterator.value().url }); 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 hashes; QJsonObject hashes;
hashes["sha1"] = value.sha1; hashes["sha1"] = value.sha1;
hashes["sha512"] = value.sha512; hashes["sha512"] = value.sha512;
fileOut["hashes"] = hashes;
file["hashes"] = hashes; fileOut["fileSize"] = value.size;
file["fileSize"] = value.size; filesOut << fileOut;
files << file;
} }
obj["files"] = files; out["files"] = filesOut;
return QJsonDocument(obj).toJson(QJsonDocument::Compact); return QJsonDocument(out).toJson(QJsonDocument::Compact);
} }

View File

@ -69,7 +69,7 @@ class ModrinthPackExportTask : public Task {
void collectFiles(); void collectFiles();
void collectHashes(); void collectHashes();
void makeApiRequest(); void makeApiRequest();
void parseApiResponse(const QByteArray* response); void parseApiResponse(const std::shared_ptr<QByteArray> response);
void buildZip(); void buildZip();
void finish(); void finish();

View File

@ -37,20 +37,19 @@
#include <FileSystem.h> #include <FileSystem.h>
#include <Json.h> #include <Json.h>
#include <QtConcurrentRun>
#include <MMCZip.h> #include <MMCZip.h>
#include <QtConcurrentRun>
#include "TechnicPackProcessor.h"
#include "SolderPackManifest.h" #include "SolderPackManifest.h"
#include "TechnicPackProcessor.h"
#include "net/ChecksumValidator.h" #include "net/ChecksumValidator.h"
Technic::SolderPackInstallTask::SolderPackInstallTask( Technic::SolderPackInstallTask::SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network,
shared_qobject_ptr<QNetworkAccessManager> network, const QUrl& solderUrl,
const QUrl &solderUrl, const QString& pack,
const QString &pack, const QString& version,
const QString &version, const QString& minecraftVersion)
const QString &minecraftVersion {
) {
m_solderUrl = solderUrl; m_solderUrl = solderUrl;
m_pack = pack; m_pack = pack;
m_version = version; m_version = version;
@ -58,9 +57,9 @@ Technic::SolderPackInstallTask::SolderPackInstallTask(
m_minecraftVersion = minecraftVersion; m_minecraftVersion = minecraftVersion;
} }
bool Technic::SolderPackInstallTask::abort() { bool Technic::SolderPackInstallTask::abort()
if(m_abortable) {
{ if (m_abortable) {
return m_filesNetJob->abort(); return m_filesNetJob->abort();
} }
return false; return false;
@ -72,7 +71,7 @@ void Technic::SolderPackInstallTask::executeTask()
m_filesNetJob.reset(new NetJob(tr("Resolving modpack files"), m_network)); m_filesNetJob.reset(new NetJob(tr("Resolving modpack files"), m_network));
auto sourceUrl = QString("%1/modpack/%2/%3").arg(m_solderUrl.toString(), m_pack, m_version); auto sourceUrl = QString("%1/modpack/%2/%3").arg(m_solderUrl.toString(), m_pack, m_version);
m_filesNetJob->addNetAction(Net::Download::makeByteArray(sourceUrl, &m_response)); m_filesNetJob->addNetAction(Net::Download::makeByteArray(sourceUrl, m_response));
auto job = m_filesNetJob.get(); auto job = m_filesNetJob.get();
connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::fileListSucceeded); connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::fileListSucceeded);
@ -85,11 +84,11 @@ void Technic::SolderPackInstallTask::fileListSucceeded()
{ {
setStatus(tr("Downloading modpack")); setStatus(tr("Downloading modpack"));
QJsonParseError parse_error {}; QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(m_response, &parse_error); QJsonDocument doc = QJsonDocument::fromJson(*m_response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) { if (parse_error.error != QJsonParseError::NoError) {
qWarning() << "Error while parsing JSON response from Solder at " << parse_error.offset << " reason: " << parse_error.errorString(); qWarning() << "Error while parsing JSON response from Solder at " << parse_error.offset << " reason: " << parse_error.errorString();
qWarning() << m_response; qWarning() << *m_response;
return; return;
} }
auto obj = doc.object(); auto obj = doc.object();
@ -110,7 +109,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded()
m_filesNetJob.reset(new NetJob(tr("Downloading modpack"), m_network)); m_filesNetJob.reset(new NetJob(tr("Downloading modpack"), m_network));
int i = 0; int i = 0;
for (const auto &mod : build.mods) { for (const auto& mod : build.mods) {
auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i)); auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i));
auto dl = Net::Download::makeFile(mod.url, path); auto dl = Net::Download::makeFile(mod.url, path);

View File

@ -40,45 +40,48 @@
#include <tasks/Task.h> #include <tasks/Task.h>
#include <QUrl> #include <QUrl>
#include <memory>
namespace Technic namespace Technic {
{ class SolderPackInstallTask : public InstanceTask {
class SolderPackInstallTask : public InstanceTask Q_OBJECT
{ public:
Q_OBJECT explicit SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network,
public: const QUrl& solderUrl,
explicit SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, const QUrl &solderUrl, const QString& pack, const QString& version, const QString &minecraftVersion); const QString& pack,
const QString& version,
const QString& minecraftVersion);
bool canAbort() const override { return true; } bool canAbort() const override { return true; }
bool abort() override; bool abort() override;
protected: protected:
//! Entry point for tasks. //! Entry point for tasks.
virtual void executeTask() override; virtual void executeTask() override;
private slots: private slots:
void fileListSucceeded(); void fileListSucceeded();
void downloadSucceeded(); void downloadSucceeded();
void downloadFailed(QString reason); void downloadFailed(QString reason);
void downloadProgressChanged(qint64 current, qint64 total); void downloadProgressChanged(qint64 current, qint64 total);
void downloadAborted(); void downloadAborted();
void extractFinished(); void extractFinished();
void extractAborted(); void extractAborted();
private: private:
bool m_abortable = false; bool m_abortable = false;
shared_qobject_ptr<QNetworkAccessManager> m_network; shared_qobject_ptr<QNetworkAccessManager> m_network;
NetJob::Ptr m_filesNetJob; NetJob::Ptr m_filesNetJob;
QUrl m_solderUrl; QUrl m_solderUrl;
QString m_pack; QString m_pack;
QString m_version; QString m_version;
QString m_minecraftVersion; QString m_minecraftVersion;
QByteArray m_response; std::shared_ptr<QByteArray> m_response = std::make_shared<QByteArray>();
QTemporaryDir m_outputDir; QTemporaryDir m_outputDir;
int m_modCount; int m_modCount;
QFuture<bool> m_extractFuture; QFuture<bool> m_extractFuture;
QFutureWatcher<bool> m_extractFutureWatcher; QFutureWatcher<bool> m_extractFutureWatcher;
}; };
} } // namespace Technic

View File

@ -1,427 +0,0 @@
#include "PackageManifest.h"
#include <Json.h>
#include <QDir>
#include <QDirIterator>
#include <QCryptographicHash>
#include <QDebug>
#ifndef Q_OS_WIN32
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif
namespace mojang_files {
const Hash hash_of_empty_string = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
int Path::compare(const Path& rhs) const
{
auto left_cursor = begin();
auto left_end = end();
auto right_cursor = rhs.begin();
auto right_end = rhs.end();
while (left_cursor != left_end && right_cursor != right_end)
{
if(*left_cursor < *right_cursor)
{
return -1;
}
else if(*left_cursor > *right_cursor)
{
return 1;
}
left_cursor++;
right_cursor++;
}
if(left_cursor == left_end)
{
if(right_cursor == right_end)
{
return 0;
}
return -1;
}
return 1;
}
void Package::addFile(const Path& path, const File& file) {
addFolder(path.parent_path());
files[path] = file;
}
void Package::addFolder(Path folder) {
if(!folder.has_parent_path()) {
return;
}
do {
folders.insert(folder);
folder = folder.parent_path();
} while(folder.has_parent_path());
}
void Package::addLink(const Path& path, const Path& target) {
addFolder(path.parent_path());
symlinks[path] = target;
}
void Package::addSource(const FileSource& source) {
sources[source.hash] = source;
}
namespace {
void fromJson(QJsonDocument & doc, Package & out) {
std::set<Path> seen_paths;
if (!doc.isObject())
{
throw JSONValidationError("file manifest is not an object");
}
QJsonObject root = doc.object();
auto filesObj = Json::ensureObject(root, "files");
auto iter = filesObj.begin();
while (iter != filesObj.end())
{
Path objectPath = Path(iter.key());
auto value = iter.value();
iter++;
if(seen_paths.count(objectPath)) {
throw JSONValidationError("duplicate path inside manifest, the manifest is invalid");
}
if (!value.isObject())
{
throw JSONValidationError("file entry inside manifest is not an an object");
}
seen_paths.insert(objectPath);
auto fileObject = value.toObject();
auto type = Json::requireString(fileObject, "type");
if(type == "directory") {
out.addFolder(objectPath);
continue;
}
else if(type == "file") {
FileSource bestSource;
File file;
file.executable = Json::ensureBoolean(fileObject, QString("executable"), false);
auto downloads = Json::requireObject(fileObject, "downloads");
for(auto iter2 = downloads.begin(); iter2 != downloads.end(); iter2++) {
FileSource source;
auto downloadObject = Json::requireObject(iter2.value());
source.hash = Json::requireString(downloadObject, "sha1");
source.size = Json::requireInteger(downloadObject, "size");
source.url = Json::requireString(downloadObject, "url");
auto compression = iter2.key();
if(compression == "raw") {
file.hash = source.hash;
file.size = source.size;
source.compression = Compression::Raw;
}
else if (compression == "lzma") {
source.compression = Compression::Lzma;
}
else {
continue;
}
bestSource.upgrade(source);
}
if(bestSource.isBad()) {
throw JSONValidationError("No valid compression method for file " + iter.key());
}
out.addFile(objectPath, file);
out.addSource(bestSource);
}
else if(type == "link") {
auto target = Json::requireString(fileObject, "target");
out.symlinks[objectPath] = target;
out.addLink(objectPath, target);
}
else {
throw JSONValidationError("Invalid item type in manifest: " + type);
}
}
// make sure the containing folder exists
out.folders.insert(Path());
}
}
Package Package::fromManifestContents(const QByteArray& contents)
{
Package out;
try
{
auto doc = Json::requireDocument(contents, "Manifest");
fromJson(doc, out);
return out;
}
catch (const Exception &e)
{
qDebug() << QString("Unable to parse manifest: %1").arg(e.cause());
out.valid = false;
return out;
}
}
Package Package::fromManifestFile(const QString & filename) {
Package out;
try
{
auto doc = Json::requireDocument(filename, filename);
fromJson(doc, out);
return out;
}
catch (const Exception &e)
{
qDebug() << QString("Unable to parse manifest file %1: %2").arg(filename, e.cause());
out.valid = false;
return out;
}
}
#ifndef Q_OS_WIN32
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
namespace {
// FIXME: Qt obscures symlink targets by making them absolute. that is useless. this is the workaround - we do it ourselves
bool actually_read_symlink_target(const QString & filepath, Path & out)
{
struct ::stat st;
// FIXME: here, we assume the native filesystem encoding. May the Gods have mercy upon our Souls.
QByteArray nativePath = filepath.toUtf8();
const char * filepath_cstr = nativePath.data();
if (lstat(filepath_cstr, &st) != 0)
{
return false;
}
auto size = st.st_size ? st.st_size + 1 : PATH_MAX;
std::string temp(size, '\0');
// because we don't realiably know how long the damn thing actually is, we loop and expand. POSIX is naff
do
{
auto link_length = ::readlink(filepath_cstr, &temp[0], temp.size());
if(link_length == -1)
{
return false;
}
if(std::string::size_type(link_length) < temp.size())
{
// buffer was long enough and we managed to read the link target. RETURN here.
temp.resize(link_length);
out = Path(QString::fromUtf8(temp.c_str()));
return true;
}
temp.resize(temp.size() * 2);
} while (true);
}
}
#endif
// FIXME: Qt filesystem abstraction is bad, but ... let's hope it doesn't break too much?
// FIXME: The error handling is just DEFICIENT
Package Package::fromInspectedFolder(const QString& folderPath)
{
QDir root(folderPath);
Package out;
QDirIterator iterator(folderPath, QDir::NoDotAndDotDot | QDir::AllEntries | QDir::System | QDir::Hidden, QDirIterator::Subdirectories);
while(iterator.hasNext()) {
iterator.next();
auto fileInfo = iterator.fileInfo();
auto relPath = root.relativeFilePath(fileInfo.filePath());
// FIXME: this is probably completely busted on Windows anyway, so just disable it.
// Qt makes shit up and doesn't understand the platform details
// TODO: Actually use a filesystem library that isn't terrible and has decen license.
// I only know one, and I wrote it. Sadly, currently proprietary. PAIN.
#ifndef Q_OS_WIN32
if(fileInfo.isSymLink()) {
Path targetPath;
if(!actually_read_symlink_target(fileInfo.filePath(), targetPath)) {
qCritical() << "Folder inspection: Unknown filesystem object:" << fileInfo.absoluteFilePath();
out.valid = false;
}
out.addLink(relPath, targetPath);
}
else
#endif
if(fileInfo.isDir()) {
out.addFolder(relPath);
}
else if(fileInfo.isFile()) {
File f;
f.executable = fileInfo.isExecutable();
f.size = fileInfo.size();
// FIXME: async / optimize the hashing
QFile input(fileInfo.absoluteFilePath());
if(!input.open(QIODevice::ReadOnly)) {
qCritical() << "Folder inspection: Failed to open file:" << fileInfo.absoluteFilePath();
out.valid = false;
break;
}
f.hash = QCryptographicHash::hash(input.readAll(), QCryptographicHash::Sha1).toHex().constData();
out.addFile(relPath, f);
}
else {
// Something else... oh my
qCritical() << "Folder inspection: Unknown filesystem object:" << fileInfo.absoluteFilePath();
out.valid = false;
break;
}
}
out.folders.insert(Path("."));
out.valid = true;
return out;
}
namespace {
struct shallow_first_sort
{
bool operator()(const Path &lhs, const Path &rhs) const
{
auto lhs_depth = lhs.length();
auto rhs_depth = rhs.length();
if(lhs_depth < rhs_depth)
{
return true;
}
else if(lhs_depth == rhs_depth)
{
if(lhs < rhs)
{
return true;
}
}
return false;
}
};
struct deep_first_sort
{
bool operator()(const Path &lhs, const Path &rhs) const
{
auto lhs_depth = lhs.length();
auto rhs_depth = rhs.length();
if(lhs_depth > rhs_depth)
{
return true;
}
else if(lhs_depth == rhs_depth)
{
if(lhs < rhs)
{
return true;
}
}
return false;
}
};
}
UpdateOperations UpdateOperations::resolve(const Package& from, const Package& to)
{
UpdateOperations out;
if(!from.valid || !to.valid) {
out.valid = false;
return out;
}
// Files
for(auto iter = from.files.begin(); iter != from.files.end(); iter++) {
const auto &current_hash = iter->second.hash;
const auto &current_executable = iter->second.executable;
const auto &path = iter->first;
auto iter2 = to.files.find(path);
if(iter2 == to.files.end()) {
// removed
out.deletes.push_back(path);
continue;
}
auto new_hash = iter2->second.hash;
auto new_executable = iter2->second.executable;
if (current_hash != new_hash) {
out.deletes.push_back(path);
out.downloads.emplace(
std::pair<Path, FileDownload>{
path,
FileDownload(to.sources.at(iter2->second.hash), iter2->second.executable)
}
);
}
else if (current_executable != new_executable) {
out.executable_fixes[path] = new_executable;
}
}
for(auto iter = to.files.begin(); iter != to.files.end(); iter++) {
auto path = iter->first;
if(!from.files.count(path)) {
out.downloads.emplace(
std::pair<Path, FileDownload>{
path,
FileDownload(to.sources.at(iter->second.hash), iter->second.executable)
}
);
}
}
// Folders
std::set<Path, deep_first_sort> remove_folders;
std::set<Path, shallow_first_sort> make_folders;
for(auto from_path: from.folders) {
auto iter = to.folders.find(from_path);
if(iter == to.folders.end()) {
remove_folders.insert(from_path);
}
}
for(auto & rmdir: remove_folders) {
out.rmdirs.push_back(rmdir);
}
for(auto to_path: to.folders) {
auto iter = from.folders.find(to_path);
if(iter == from.folders.end()) {
make_folders.insert(to_path);
}
}
for(auto & mkdir: make_folders) {
out.mkdirs.push_back(mkdir);
}
// Symlinks
for(auto iter = from.symlinks.begin(); iter != from.symlinks.end(); iter++) {
const auto &current_target = iter->second;
const auto &path = iter->first;
auto iter2 = to.symlinks.find(path);
if(iter2 == to.symlinks.end()) {
// removed
out.deletes.push_back(path);
continue;
}
const auto &new_target = iter2->second;
if (current_target != new_target) {
out.deletes.push_back(path);
out.mklinks[path] = iter2->second;
}
}
for(auto iter = to.symlinks.begin(); iter != to.symlinks.end(); iter++) {
auto path = iter->first;
if(!from.symlinks.count(path)) {
out.mklinks[path] = iter->second;
}
}
out.valid = true;
return out;
}
}

View File

@ -1,171 +0,0 @@
#pragma once
#include <QString>
#include <map>
#include <set>
#include <QStringList>
#include "tasks/Task.h"
namespace mojang_files {
using Hash = QString;
extern const Hash empty_hash;
// simple-ish path implementation. assumes always relative and does not allow '..' entries
class Path
{
public:
using parts_type = QStringList;
Path() = default;
Path(QString string) {
auto parts_in = string.split('/');
for(auto & part: parts_in) {
if(part.isEmpty() || part == ".") {
continue;
}
if(part == "..") {
if(parts.size()) {
parts.pop_back();
}
continue;
}
parts.push_back(part);
}
}
bool has_parent_path() const
{
return parts.size() > 0;
}
Path parent_path() const
{
if (parts.empty())
return Path();
return Path(parts.begin(), std::prev(parts.end()));
}
bool empty() const
{
return parts.empty();
}
int length() const
{
return parts.length();
}
bool operator==(const Path & rhs) const {
return parts == rhs.parts;
}
bool operator!=(const Path & rhs) const {
return parts != rhs.parts;
}
inline bool operator<(const Path& rhs) const
{
return compare(rhs) < 0;
}
parts_type::const_iterator begin() const
{
return parts.begin();
}
parts_type::const_iterator end() const
{
return parts.end();
}
QString toString() const {
return parts.join("/");
}
private:
Path(const parts_type::const_iterator & start, const parts_type::const_iterator & end) {
auto cursor = start;
while(cursor != end) {
parts.push_back(*cursor);
cursor++;
}
}
int compare(const Path& p) const;
parts_type parts;
};
enum class Compression {
Raw,
Lzma,
Unknown
};
struct FileSource
{
Compression compression = Compression::Unknown;
Hash hash;
QString url;
std::size_t size = 0;
void upgrade(const FileSource & other) {
if(compression == Compression::Unknown || other.size < size) {
*this = other;
}
}
bool isBad() const {
return compression == Compression::Unknown;
}
};
struct File
{
Hash hash;
bool executable;
std::uint64_t size = 0;
};
struct Package {
static Package fromInspectedFolder(const QString &folderPath);
static Package fromManifestFile(const QString &path);
static Package fromManifestContents(const QByteArray& contents);
explicit operator bool() const
{
return valid;
}
void addFolder(Path folder);
void addFile(const Path & path, const File & file);
void addLink(const Path & path, const Path & target);
void addSource(const FileSource & source);
std::map<Hash, FileSource> sources;
bool valid = true;
std::set<Path> folders;
std::map<Path, File> files;
std::map<Path, Path> symlinks;
};
struct FileDownload : FileSource
{
FileDownload(const FileSource& source, bool executable) {
static_cast<FileSource &> (*this) = source;
this->executable = executable;
}
bool executable = false;
};
struct UpdateOperations {
static UpdateOperations resolve(const Package & from, const Package & to);
bool valid = false;
std::vector<Path> deletes;
std::vector<Path> rmdirs;
std::vector<Path> mkdirs;
std::map<Path, FileDownload> downloads;
std::map<Path, Path> mklinks;
std::map<Path, bool> executable_fixes;
};
}

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com> * 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 * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -46,7 +47,7 @@ namespace Net {
*/ */
class ByteArraySink : public Sink { class ByteArraySink : public Sink {
public: public:
ByteArraySink(QByteArray* output) : m_output(output){}; ByteArraySink(std::shared_ptr<QByteArray> output) : m_output(output){};
virtual ~ByteArraySink() = default; virtual ~ByteArraySink() = default;
@ -93,6 +94,6 @@ class ByteArraySink : public Sink {
auto hasLocalData() -> bool override { return false; } auto hasLocalData() -> bool override { return false; }
private: private:
QByteArray* m_output; std::shared_ptr<QByteArray> m_output;
}; };
} // namespace Net } // namespace Net

View File

@ -41,6 +41,7 @@
#include <QDateTime> #include <QDateTime>
#include <QFileInfo> #include <QFileInfo>
#include <memory>
#include "ByteArraySink.h" #include "ByteArraySink.h"
#include "ChecksumValidator.h" #include "ChecksumValidator.h"
@ -69,7 +70,7 @@ auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Down
return dl; return dl;
} }
auto Download::makeByteArray(QUrl url, QByteArray* output, Options options) -> Download::Ptr auto Download::makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Options options) -> Download::Ptr
{ {
auto dl = makeShared<Download>(); auto dl = makeShared<Download>();
dl->m_url = url; dl->m_url = url;

View File

@ -60,7 +60,7 @@ class Download : public NetAction {
~Download() override = default; ~Download() override = default;
static auto makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions) -> Download::Ptr; static auto makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions) -> Download::Ptr;
static auto makeByteArray(QUrl url, QByteArray* output, Options options = Option::NoOptions) -> Download::Ptr; static auto makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, Options options = Option::NoOptions) -> Download::Ptr;
static auto makeFile(QUrl url, QString path, Options options = Option::NoOptions) -> Download::Ptr; static auto makeFile(QUrl url, QString path, Options options = Option::NoOptions) -> Download::Ptr;
public: public:

View File

@ -39,218 +39,226 @@
#include "Upload.h" #include "Upload.h"
#include <utility> #include <utility>
#include "ByteArraySink.h"
#include "BuildConfig.h"
#include "Application.h" #include "Application.h"
#include "BuildConfig.h"
#include "ByteArraySink.h"
#include "net/Logging.h" #include "net/Logging.h"
namespace Net { namespace Net {
bool Upload::abort() bool Upload::abort()
{ {
if (m_reply) { if (m_reply) {
m_reply->abort(); m_reply->abort();
} else { } else {
m_state = State::AbortedByUser; m_state = State::AbortedByUser;
}
return true;
}
void Upload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
setProgress(bytesReceived, bytesTotal);
}
void Upload::downloadError(QNetworkReply::NetworkError error)
{
if (error == QNetworkReply::OperationCanceledError) {
qCCritical(taskUploadLogC) << getUid().toString() << "Aborted " << m_url.toString();
m_state = State::AbortedByUser;
} else {
// error happened during download.
qCCritical(taskUploadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error;
m_state = State::Failed;
}
}
void Upload::sslErrors(const QList<QSslError>& errors)
{
int i = 1;
for (const auto& error : errors) {
qCCritical(taskUploadLogC) << getUid().toString() << "Upload" << m_url.toString() << "SSL Error #" << i << " : "
<< error.errorString();
auto cert = error.certificate();
qCCritical(taskUploadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText();
i++;
}
}
bool Upload::handleRedirect()
{
QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl();
if (!redirect.isValid()) {
if (!m_reply->hasRawHeader("Location")) {
// no redirect -> it's fine to continue
return false;
} }
return true; // there is a Location header, but it's not correct. we need to apply some workarounds...
} QByteArray redirectBA = m_reply->rawHeader("Location");
if (redirectBA.size() == 0) {
void Upload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { // empty, yet present redirect header? WTF?
setProgress(bytesReceived, bytesTotal); return false;
}
void Upload::downloadError(QNetworkReply::NetworkError error) {
if (error == QNetworkReply::OperationCanceledError) {
qCCritical(taskUploadLogC) << getUid().toString() << "Aborted " << m_url.toString();
m_state = State::AbortedByUser;
} else {
// error happened during download.
qCCritical(taskUploadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error;
m_state = State::Failed;
} }
} QString redirectStr = QString::fromUtf8(redirectBA);
void Upload::sslErrors(const QList<QSslError> &errors) {
int i = 1;
for (const auto& error : errors) {
qCCritical(taskUploadLogC) << getUid().toString() << "Upload" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString();
auto cert = error.certificate();
qCCritical(taskUploadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText();
i++;
}
}
bool Upload::handleRedirect()
{
QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl();
if (!redirect.isValid()) {
if (!m_reply->hasRawHeader("Location")) {
// no redirect -> it's fine to continue
return false;
}
// there is a Location header, but it's not correct. we need to apply some workarounds...
QByteArray redirectBA = m_reply->rawHeader("Location");
if (redirectBA.size() == 0) {
// empty, yet present redirect header? WTF?
return false;
}
QString redirectStr = QString::fromUtf8(redirectBA);
if (redirectStr.startsWith("//")) {
/*
* IF the URL begins with //, we need to insert the URL scheme.
* See: https://bugreports.qt.io/browse/QTBUG-41061
* See: http://tools.ietf.org/html/rfc3986#section-4.2
*/
redirectStr = m_reply->url().scheme() + ":" + redirectStr;
} else if (redirectStr.startsWith("/")) {
/*
* IF the URL begins with /, we need to process it as a relative URL
*/
auto url = m_reply->url();
url.setPath(redirectStr, QUrl::TolerantMode);
redirectStr = url.toString();
}
if (redirectStr.startsWith("//")) {
/* /*
* Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues. * IF the URL begins with //, we need to insert the URL scheme.
* FIXME: report Qt bug for this * See: https://bugreports.qt.io/browse/QTBUG-41061
* See: http://tools.ietf.org/html/rfc3986#section-4.2
*/ */
redirect = QUrl(redirectStr, QUrl::TolerantMode); redirectStr = m_reply->url().scheme() + ":" + redirectStr;
if (!redirect.isValid()) { } else if (redirectStr.startsWith("/")) {
qCWarning(taskUploadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr; /*
downloadError(QNetworkReply::ProtocolFailure); * IF the URL begins with /, we need to process it as a relative URL
return false; */
} auto url = m_reply->url();
qCDebug(taskUploadLogC) << getUid().toString() << "Fixed location header:" << redirect; url.setPath(redirectStr, QUrl::TolerantMode);
} else { redirectStr = url.toString();
qCDebug(taskUploadLogC) << getUid().toString() << "Location header:" << redirect;
} }
m_url = QUrl(redirect.toString()); /*
qCDebug(taskUploadLogC) << getUid().toString() << "Following redirect to " << m_url.toString(); * Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues.
startAction(m_network); * FIXME: report Qt bug for this
return true; */
redirect = QUrl(redirectStr, QUrl::TolerantMode);
if (!redirect.isValid()) {
qCWarning(taskUploadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr;
downloadError(QNetworkReply::ProtocolFailure);
return false;
}
qCDebug(taskUploadLogC) << getUid().toString() << "Fixed location header:" << redirect;
} else {
qCDebug(taskUploadLogC) << getUid().toString() << "Location header:" << redirect;
} }
void Upload::downloadFinished() { m_url = QUrl(redirect.toString());
// handle HTTP redirection first qCDebug(taskUploadLogC) << getUid().toString() << "Following redirect to " << m_url.toString();
// very unlikely for post requests, still can happen startAction(m_network);
if (handleRedirect()) { return true;
qCDebug(taskUploadLogC) << getUid().toString() << "Upload redirected:" << m_url.toString(); }
return;
}
// if the download failed before this point ... void Upload::downloadFinished()
if (m_state == State::Succeeded) { {
qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed but we are allowed to proceed:" << m_url.toString(); // handle HTTP redirection first
m_sink->abort(); // very unlikely for post requests, still can happen
m_reply.reset(); if (handleRedirect()) {
emit succeeded(); qCDebug(taskUploadLogC) << getUid().toString() << "Upload redirected:" << m_url.toString();
return; return;
} else if (m_state == State::Failed) { }
qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed in previous step:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit failed("");
return;
} else if (m_state == State::AbortedByUser) {
qCDebug(taskUploadLogC) << getUid().toString() << "Upload aborted in previous step:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit aborted();
return;
}
// make sure we got all the remaining data, if any // if the download failed before this point ...
auto data = m_reply->readAll(); if (m_state == State::Succeeded) {
if (data.size()) { qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed but we are allowed to proceed:" << m_url.toString();
qCDebug(taskUploadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes"; m_sink->abort();
m_state = m_sink->write(data);
}
// otherwise, finalize the whole graph
m_state = m_sink->finalize(*m_reply.get());
if (m_state != State::Succeeded) {
qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed to finalize:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit failed("");
return;
}
m_reply.reset(); m_reply.reset();
qCDebug(taskUploadLogC) << getUid().toString() << "Upload succeeded:" << m_url.toString();
emit succeeded(); emit succeeded();
return;
} else if (m_state == State::Failed) {
qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed in previous step:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit failed("");
return;
} else if (m_state == State::AbortedByUser) {
qCDebug(taskUploadLogC) << getUid().toString() << "Upload aborted in previous step:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit aborted();
return;
} }
void Upload::downloadReadyRead() { // make sure we got all the remaining data, if any
if (m_state == State::Running) { auto data = m_reply->readAll();
auto data = m_reply->readAll(); if (data.size()) {
m_state = m_sink->write(data); qCDebug(taskUploadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes";
} m_state = m_sink->write(data);
} }
void Upload::executeTask() { // otherwise, finalize the whole graph
setStatus(tr("Uploading %1").arg(m_url.toString())); m_state = m_sink->finalize(*m_reply.get());
if (m_state != State::Succeeded) {
qCDebug(taskUploadLogC) << getUid().toString() << "Upload failed to finalize:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit failed("");
return;
}
m_reply.reset();
qCDebug(taskUploadLogC) << getUid().toString() << "Upload succeeded:" << m_url.toString();
emit succeeded();
}
if (m_state == State::AbortedByUser) { void Upload::downloadReadyRead()
qCWarning(taskUploadLogC) << getUid().toString() << "Attempt to start an aborted Upload:" << m_url.toString(); {
emit aborted(); if (m_state == State::Running) {
auto data = m_reply->readAll();
m_state = m_sink->write(data);
}
}
void Upload::executeTask()
{
setStatus(tr("Uploading %1").arg(m_url.toString()));
if (m_state == State::AbortedByUser) {
qCWarning(taskUploadLogC) << getUid().toString() << "Attempt to start an aborted Upload:" << m_url.toString();
emit aborted();
return;
}
QNetworkRequest request(m_url);
m_state = m_sink->init(request);
switch (m_state) {
case State::Succeeded:
emitSucceeded();
qCDebug(taskUploadLogC) << getUid().toString() << "Upload cache hit " << m_url.toString();
return; return;
} case State::Running:
QNetworkRequest request(m_url); qCDebug(taskUploadLogC) << getUid().toString() << "Uploading " << m_url.toString();
m_state = m_sink->init(request); break;
switch (m_state) { case State::Inactive:
case State::Succeeded: case State::Failed:
emitSucceeded(); emitFailed("");
qCDebug(taskUploadLogC) << getUid().toString() << "Upload cache hit " << m_url.toString(); return;
return; case State::AbortedByUser:
case State::Running: emitAborted();
qCDebug(taskUploadLogC) << getUid().toString() << "Uploading " << m_url.toString(); return;
break; }
case State::Inactive:
case State::Failed:
emitFailed("");
return;
case State::AbortedByUser:
emitAborted();
return;
}
request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8()); request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8());
// TODO remove duplication // TODO remove duplication
if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) { if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) {
request.setRawHeader("x-api-key", APPLICATION->getFlameAPIKey().toUtf8()); request.setRawHeader("x-api-key", APPLICATION->getFlameAPIKey().toUtf8());
} else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || } else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() ||
request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) {
QString token = APPLICATION->getModrinthAPIToken(); QString token = APPLICATION->getModrinthAPIToken();
if (!token.isNull()) if (!token.isNull())
request.setRawHeader("Authorization", token.toUtf8()); request.setRawHeader("Authorization", token.toUtf8());
} }
//TODO other types of post requests ? // TODO other types of post requests ?
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkReply* rep = m_network->post(request, m_post_data); QNetworkReply* rep = m_network->post(request, m_post_data);
m_reply.reset(rep); m_reply.reset(rep);
connect(rep, &QNetworkReply::downloadProgress, this, &Upload::downloadProgress); connect(rep, &QNetworkReply::downloadProgress, this, &Upload::downloadProgress);
connect(rep, &QNetworkReply::finished, this, &Upload::downloadFinished); connect(rep, &QNetworkReply::finished, this, &Upload::downloadFinished);
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15
connect(rep, &QNetworkReply::errorOccurred, this, &Upload::downloadError); connect(rep, &QNetworkReply::errorOccurred, this, &Upload::downloadError);
#else #else
connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &Upload::downloadError); connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &Upload::downloadError);
#endif #endif
connect(rep, &QNetworkReply::sslErrors, this, &Upload::sslErrors); connect(rep, &QNetworkReply::sslErrors, this, &Upload::sslErrors);
connect(rep, &QNetworkReply::readyRead, this, &Upload::downloadReadyRead); connect(rep, &QNetworkReply::readyRead, this, &Upload::downloadReadyRead);
} }
Upload::Ptr Upload::makeByteArray(QUrl url, QByteArray *output, QByteArray m_post_data) { Upload::Ptr Upload::makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, QByteArray m_post_data)
auto up = makeShared<Upload>(); {
up->m_url = std::move(url); auto up = makeShared<Upload>();
up->m_sink.reset(new ByteArraySink(output)); up->m_url = std::move(url);
up->m_post_data = std::move(m_post_data); up->m_sink.reset(new ByteArraySink(output));
return up; up->m_post_data = std::move(m_post_data);
} return up;
} // Net }
} // namespace Net

View File

@ -42,31 +42,31 @@
namespace Net { namespace Net {
class Upload : public NetAction { class Upload : public NetAction {
Q_OBJECT Q_OBJECT
public: public:
using Ptr = shared_qobject_ptr<Upload>; using Ptr = shared_qobject_ptr<Upload>;
static Upload::Ptr makeByteArray(QUrl url, QByteArray *output, QByteArray m_post_data); static Upload::Ptr makeByteArray(QUrl url, std::shared_ptr<QByteArray> output, QByteArray m_post_data);
auto abort() -> bool override; auto abort() -> bool override;
auto canAbort() const -> bool override { return true; }; auto canAbort() const -> bool override { return true; };
protected slots: protected slots:
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
void downloadError(QNetworkReply::NetworkError error) override; void downloadError(QNetworkReply::NetworkError error) override;
void sslErrors(const QList<QSslError> & errors) override; void sslErrors(const QList<QSslError>& errors) override;
void downloadFinished() override; void downloadFinished() override;
void downloadReadyRead() override; void downloadReadyRead() override;
public slots: public slots:
void executeTask() override; void executeTask() override;
private:
std::unique_ptr<Sink> m_sink;
QByteArray m_post_data;
bool handleRedirect(); private:
}; std::unique_ptr<Sink> m_sink;
QByteArray m_post_data;
} // Net bool handleRedirect();
};
} // namespace Net

View File

@ -58,7 +58,7 @@ void NewsChecker::reloadNews()
qDebug() << "Reloading news."; qDebug() << "Reloading news.";
NetJob::Ptr job{ new NetJob("News RSS Feed", m_network) }; NetJob::Ptr job{ new NetJob("News RSS Feed", m_network) };
job->addNetAction(Net::Download::makeByteArray(m_feedUrl, &newsData)); job->addNetAction(Net::Download::makeByteArray(m_feedUrl, newsData));
QObject::connect(job.get(), &NetJob::succeeded, this, &NewsChecker::rssDownloadFinished); QObject::connect(job.get(), &NetJob::succeeded, this, &NewsChecker::rssDownloadFinished);
QObject::connect(job.get(), &NetJob::failed, this, &NewsChecker::rssDownloadFailed); QObject::connect(job.get(), &NetJob::failed, this, &NewsChecker::rssDownloadFailed);
m_newsNetJob.reset(job); m_newsNetJob.reset(job);
@ -79,32 +79,27 @@ void NewsChecker::rssDownloadFinished()
int errorCol = -1; int errorCol = -1;
// Parse the XML. // Parse the XML.
if (!doc.setContent(newsData, false, &errorMsg, &errorLine, &errorCol)) if (!doc.setContent(*newsData, false, &errorMsg, &errorLine, &errorCol)) {
{
QString fullErrorMsg = QString("Error parsing RSS feed XML. %1 at %2:%3.").arg(errorMsg).arg(errorLine).arg(errorCol); QString fullErrorMsg = QString("Error parsing RSS feed XML. %1 at %2:%3.").arg(errorMsg).arg(errorLine).arg(errorCol);
fail(fullErrorMsg); fail(fullErrorMsg);
newsData.clear(); newsData->clear();
return; return;
} }
newsData.clear(); newsData->clear();
} }
// If the parsing succeeded, read it. // If the parsing succeeded, read it.
QDomNodeList items = doc.elementsByTagName("entry"); QDomNodeList items = doc.elementsByTagName("entry");
m_newsEntries.clear(); m_newsEntries.clear();
for (int i = 0; i < items.length(); i++) for (int i = 0; i < items.length(); i++) {
{
QDomElement element = items.at(i).toElement(); QDomElement element = items.at(i).toElement();
NewsEntryPtr entry; NewsEntryPtr entry;
entry.reset(new NewsEntry()); entry.reset(new NewsEntry());
QString errorMsg = "An unknown error occurred."; QString errorMsg = "An unknown error occurred.";
if (NewsEntry::fromXmlElement(element, entry.get(), &errorMsg)) if (NewsEntry::fromXmlElement(element, entry.get(), &errorMsg)) {
{
qDebug() << "Loaded news entry" << entry->title; qDebug() << "Loaded news entry" << entry->title;
m_newsEntries.append(entry); m_newsEntries.append(entry);
} } else {
else
{
qWarning() << "Failed to load news entry at index" << i << ":" << errorMsg; qWarning() << "Failed to load news entry at index" << i << ":" << errorMsg;
} }
} }

View File

@ -85,7 +85,7 @@ protected: /* data */
//! True if news has been loaded. //! True if news has been loaded.
bool m_loadedNews; bool m_loadedNews;
QByteArray newsData; std::shared_ptr<QByteArray> newsData = std::make_shared<QByteArray>();
/*! /*!
* Gets the error message that was given last time the news was loaded. * Gets the error message that was given last time the news was loaded.

View File

@ -16,7 +16,6 @@
<file>scalable/jarmods.svg</file> <file>scalable/jarmods.svg</file>
<file>scalable/java.svg</file> <file>scalable/java.svg</file>
<file>scalable/language.svg</file> <file>scalable/language.svg</file>
<file>scalable/launcher.svg</file>
<file>scalable/loadermods.svg</file> <file>scalable/loadermods.svg</file>
<file>scalable/log.svg</file> <file>scalable/log.svg</file>
<file>scalable/minecraft.svg</file> <file>scalable/minecraft.svg</file>

View File

@ -1,57 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" version="1.1" viewBox="0 0 12.7 12.7" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<title>Prism Launcher Logo</title>
<g stroke-width=".26458">
<path d="m6.35 6.35" fill="#99cd61"/>
<path d="m6.35 0.52917-2.5208 4.3656 2.5208 1.4552 2.5203-1.4552 0.10955-3.0996c-1.1511-0.66459-2.3388-1.2661-2.6298-1.2661z" fill="#df6277"/>
<path d="m8.9798 1.7952-2.6298 4.5548 2.5203 1.4552 2.5208-4.3656c-0.14552-0.25205-1.2601-0.97975-2.4112-1.6443z" fill="#fb9168"/>
<path d="m11.391 3.4396-5.041 2.9104 2.5203 1.4552 2.7389-1.4552c0-1.3292-0.072554-2.6584-0.21808-2.9104z" fill="#f3db6c"/>
<path d="m6.35 6.35v2.9104h5.041c0.14552-0.25205 0.21807-1.5812 0.21808-2.9104h-5.2591z" fill="#7ab392"/>
<path d="m6.35 6.35v2.9104l2.6298 1.6443c1.1511-0.66459 2.2657-1.3923 2.4112-1.6443l-5.041-2.9104z" fill="#4b7cbc"/>
<path d="m6.35 6.35-2.5208 1.4552 2.5208 4.3656c0.29104 0 1.4787-0.60148 2.6298-1.2661l-2.6298-4.5548z" fill="#6f488c"/>
<path d="m3.8292 4.8948-2.5203 4.3656c0.29104 0.5041 4.459 2.9104 5.041 2.9104v-5.8208l-2.5208-1.4552z" fill="#4d3f33"/>
<path d="m1.309 3.4396c-0.29104 0.5041-0.29104 5.3167 0 5.8208l5.041-2.9104v-2.9104h-5.041z" fill="#7a573b"/>
<path d="m6.35 0.52917c-0.58208-2e-8 -4.75 2.4063-5.041 2.9104l5.041 2.9104v-5.8208z" fill="#99cd61"/>
</g>
<g transform="matrix(.88 0 0 .88 -10.906 -1.2421)">
<g transform="translate(13.26 2.2776)">
<path transform="matrix(.96975 0 0 .96975 .1921 .1921)" d="m6.3498 2.9393c-0.34105 0-2.7827 1.4099-2.9532 1.7052l2.9532 5.1157 2.9538-5.1157c-0.17052-0.29535-2.6127-1.7052-2.9538-1.7052z" fill="#fff" stroke-width=".26458"/>
</g>
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
</g>
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:title>Prism Launcher Logo</dc:title>
<dc:date>19/10/2022</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Prism Launcher</dc:title>
</cc:Agent>
</dc:creator>
<dc:contributor>
<cc:Agent>
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
</cc:Agent>
</dc:contributor>
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
<dc:publisher>
<cc:Agent>
<dc:title>Prism Launcher</dc:title>
</cc:Agent>
</dc:publisher>
<cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/"/>
</cc:Work>
<cc:License rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#ShareAlike"/>
</cc:License>
</rdf:RDF>
</metadata>
</svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -1,57 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" version="1.1" viewBox="0 0 12.7 12.7" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<title>Prism Launcher Logo</title>
<g stroke-width=".26458">
<path d="m6.35 6.35" fill="#99cd61"/>
<path d="m6.35 0.52917-2.5208 4.3656 2.5208 1.4552 2.5203-1.4552 0.10955-3.0996c-1.1511-0.66459-2.3388-1.2661-2.6298-1.2661z" fill="#df6277"/>
<path d="m8.9798 1.7952-2.6298 4.5548 2.5203 1.4552 2.5208-4.3656c-0.14552-0.25205-1.2601-0.97975-2.4112-1.6443z" fill="#fb9168"/>
<path d="m11.391 3.4396-5.041 2.9104 2.5203 1.4552 2.7389-1.4552c0-1.3292-0.072554-2.6584-0.21808-2.9104z" fill="#f3db6c"/>
<path d="m6.35 6.35v2.9104h5.041c0.14552-0.25205 0.21807-1.5812 0.21808-2.9104h-5.2591z" fill="#7ab392"/>
<path d="m6.35 6.35v2.9104l2.6298 1.6443c1.1511-0.66459 2.2657-1.3923 2.4112-1.6443l-5.041-2.9104z" fill="#4b7cbc"/>
<path d="m6.35 6.35-2.5208 1.4552 2.5208 4.3656c0.29104 0 1.4787-0.60148 2.6298-1.2661l-2.6298-4.5548z" fill="#6f488c"/>
<path d="m3.8292 4.8948-2.5203 4.3656c0.29104 0.5041 4.459 2.9104 5.041 2.9104v-5.8208l-2.5208-1.4552z" fill="#4d3f33"/>
<path d="m1.309 3.4396c-0.29104 0.5041-0.29104 5.3167 0 5.8208l5.041-2.9104v-2.9104h-5.041z" fill="#7a573b"/>
<path d="m6.35 0.52917c-0.58208-2e-8 -4.75 2.4063-5.041 2.9104l5.041 2.9104v-5.8208z" fill="#99cd61"/>
</g>
<g transform="matrix(.88 0 0 .88 -10.906 -1.2421)">
<g transform="translate(13.26 2.2776)">
<path transform="matrix(.96975 0 0 .96975 .1921 .1921)" d="m6.3498 2.9393c-0.34105 0-2.7827 1.4099-2.9532 1.7052l2.9532 5.1157 2.9538-5.1157c-0.17052-0.29535-2.6127-1.7052-2.9538-1.7052z" fill="#fff" stroke-width=".26458"/>
</g>
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
</g>
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:title>Prism Launcher Logo</dc:title>
<dc:date>19/10/2022</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Prism Launcher</dc:title>
</cc:Agent>
</dc:creator>
<dc:contributor>
<cc:Agent>
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
</cc:Agent>
</dc:contributor>
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
<dc:publisher>
<cc:Agent>
<dc:title>Prism Launcher</dc:title>
</cc:Agent>
</dc:publisher>
<cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/"/>
</cc:Work>
<cc:License rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#ShareAlike"/>
</cc:License>
</rdf:RDF>
</metadata>
</svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -18,7 +18,6 @@
<file>scalable/jarmods.svg</file> <file>scalable/jarmods.svg</file>
<file>scalable/java.svg</file> <file>scalable/java.svg</file>
<file>scalable/language.svg</file> <file>scalable/language.svg</file>
<file>scalable/launcher.svg</file>
<file>scalable/loadermods.svg</file> <file>scalable/loadermods.svg</file>
<file>scalable/log.svg</file> <file>scalable/log.svg</file>
<file>scalable/minecraft.svg</file> <file>scalable/minecraft.svg</file>

View File

@ -1,57 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" version="1.1" viewBox="0 0 12.7 12.7" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<title>Prism Launcher Logo</title>
<g stroke-width=".26458">
<path d="m6.35 6.35" fill="#99cd61"/>
<path d="m6.35 0.52917-2.5208 4.3656 2.5208 1.4552 2.5203-1.4552 0.10955-3.0996c-1.1511-0.66459-2.3388-1.2661-2.6298-1.2661z" fill="#df6277"/>
<path d="m8.9798 1.7952-2.6298 4.5548 2.5203 1.4552 2.5208-4.3656c-0.14552-0.25205-1.2601-0.97975-2.4112-1.6443z" fill="#fb9168"/>
<path d="m11.391 3.4396-5.041 2.9104 2.5203 1.4552 2.7389-1.4552c0-1.3292-0.072554-2.6584-0.21808-2.9104z" fill="#f3db6c"/>
<path d="m6.35 6.35v2.9104h5.041c0.14552-0.25205 0.21807-1.5812 0.21808-2.9104h-5.2591z" fill="#7ab392"/>
<path d="m6.35 6.35v2.9104l2.6298 1.6443c1.1511-0.66459 2.2657-1.3923 2.4112-1.6443l-5.041-2.9104z" fill="#4b7cbc"/>
<path d="m6.35 6.35-2.5208 1.4552 2.5208 4.3656c0.29104 0 1.4787-0.60148 2.6298-1.2661l-2.6298-4.5548z" fill="#6f488c"/>
<path d="m3.8292 4.8948-2.5203 4.3656c0.29104 0.5041 4.459 2.9104 5.041 2.9104v-5.8208l-2.5208-1.4552z" fill="#4d3f33"/>
<path d="m1.309 3.4396c-0.29104 0.5041-0.29104 5.3167 0 5.8208l5.041-2.9104v-2.9104h-5.041z" fill="#7a573b"/>
<path d="m6.35 0.52917c-0.58208-2e-8 -4.75 2.4063-5.041 2.9104l5.041 2.9104v-5.8208z" fill="#99cd61"/>
</g>
<g transform="matrix(.88 0 0 .88 -10.906 -1.2421)">
<g transform="translate(13.26 2.2776)">
<path transform="matrix(.96975 0 0 .96975 .1921 .1921)" d="m6.3498 2.9393c-0.34105 0-2.7827 1.4099-2.9532 1.7052l2.9532 5.1157 2.9538-5.1157c-0.17052-0.29535-2.6127-1.7052-2.9538-1.7052z" fill="#fff" stroke-width=".26458"/>
</g>
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
</g>
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:title>Prism Launcher Logo</dc:title>
<dc:date>19/10/2022</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Prism Launcher</dc:title>
</cc:Agent>
</dc:creator>
<dc:contributor>
<cc:Agent>
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
</cc:Agent>
</dc:contributor>
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
<dc:publisher>
<cc:Agent>
<dc:title>Prism Launcher</dc:title>
</cc:Agent>
</dc:publisher>
<cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/"/>
</cc:Work>
<cc:License rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#ShareAlike"/>
</cc:License>
</rdf:RDF>
</metadata>
</svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -18,7 +18,6 @@
<file>scalable/jarmods.svg</file> <file>scalable/jarmods.svg</file>
<file>scalable/java.svg</file> <file>scalable/java.svg</file>
<file>scalable/language.svg</file> <file>scalable/language.svg</file>
<file>scalable/launcher.svg</file>
<file>scalable/loadermods.svg</file> <file>scalable/loadermods.svg</file>
<file>scalable/log.svg</file> <file>scalable/log.svg</file>
<file>scalable/minecraft.svg</file> <file>scalable/minecraft.svg</file>

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" fill="#eeeeee" version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m20 4h-16v16h16zm0 18h-16c-1.1046 0-2-0.89543-2-2v-16c0-1.1046 0.89543-2 2-2h16c1.1046 0 2 0.89543 2 2v16c0 1.1046-0.89543 2-2 2z"/><path d="m7.2 18c-0.225 0-0.45-0.075-0.6-0.15-0.375-0.225-0.6-0.6-0.6-1.05v-9.6c0-0.45 0.225-0.825 0.6-1.05 0.225-0.15 0.375-0.15 0.6-0.15 0.15 0 0.375 0.075 0.525 0.15l9.6 4.8c0.375 0.225 0.675 0.6 0.675 1.05 0 0.45-0.225 0.9-0.675 1.05l-9.6 4.8c-0.15 0.075-0.375 0.15-0.525 0.15z" clip-rule="evenodd" fill="#eeeeee" fill-rule="evenodd" stroke-width=".99999"/></svg>

Before

Width:  |  Height:  |  Size: 660 B

View File

@ -16,7 +16,6 @@
<file>scalable/jarmods.svg</file> <file>scalable/jarmods.svg</file>
<file>scalable/java.svg</file> <file>scalable/java.svg</file>
<file>scalable/language.svg</file> <file>scalable/language.svg</file>
<file>scalable/launcher.svg</file>
<file>scalable/loadermods.svg</file> <file>scalable/loadermods.svg</file>
<file>scalable/log.svg</file> <file>scalable/log.svg</file>
<file>scalable/minecraft.svg</file> <file>scalable/minecraft.svg</file>

View File

@ -1,57 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" version="1.1" viewBox="0 0 12.7 12.7" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<title>Prism Launcher Logo</title>
<g stroke-width=".26458">
<path d="m6.35 6.35" fill="#99cd61"/>
<path d="m6.35 0.52917-2.5208 4.3656 2.5208 1.4552 2.5203-1.4552 0.10955-3.0996c-1.1511-0.66459-2.3388-1.2661-2.6298-1.2661z" fill="#df6277"/>
<path d="m8.9798 1.7952-2.6298 4.5548 2.5203 1.4552 2.5208-4.3656c-0.14552-0.25205-1.2601-0.97975-2.4112-1.6443z" fill="#fb9168"/>
<path d="m11.391 3.4396-5.041 2.9104 2.5203 1.4552 2.7389-1.4552c0-1.3292-0.072554-2.6584-0.21808-2.9104z" fill="#f3db6c"/>
<path d="m6.35 6.35v2.9104h5.041c0.14552-0.25205 0.21807-1.5812 0.21808-2.9104h-5.2591z" fill="#7ab392"/>
<path d="m6.35 6.35v2.9104l2.6298 1.6443c1.1511-0.66459 2.2657-1.3923 2.4112-1.6443l-5.041-2.9104z" fill="#4b7cbc"/>
<path d="m6.35 6.35-2.5208 1.4552 2.5208 4.3656c0.29104 0 1.4787-0.60148 2.6298-1.2661l-2.6298-4.5548z" fill="#6f488c"/>
<path d="m3.8292 4.8948-2.5203 4.3656c0.29104 0.5041 4.459 2.9104 5.041 2.9104v-5.8208l-2.5208-1.4552z" fill="#4d3f33"/>
<path d="m1.309 3.4396c-0.29104 0.5041-0.29104 5.3167 0 5.8208l5.041-2.9104v-2.9104h-5.041z" fill="#7a573b"/>
<path d="m6.35 0.52917c-0.58208-2e-8 -4.75 2.4063-5.041 2.9104l5.041 2.9104v-5.8208z" fill="#99cd61"/>
</g>
<g transform="matrix(.88 0 0 .88 -10.906 -1.2421)">
<g transform="translate(13.26 2.2776)">
<path transform="matrix(.96975 0 0 .96975 .1921 .1921)" d="m6.3498 2.9393c-0.34105 0-2.7827 1.4099-2.9532 1.7052l2.9532 5.1157 2.9538-5.1157c-0.17052-0.29535-2.6127-1.7052-2.9538-1.7052z" fill="#fff" stroke-width=".26458"/>
</g>
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
</g>
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:title>Prism Launcher Logo</dc:title>
<dc:date>19/10/2022</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Prism Launcher</dc:title>
</cc:Agent>
</dc:creator>
<dc:contributor>
<cc:Agent>
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
</cc:Agent>
</dc:contributor>
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
<dc:publisher>
<cc:Agent>
<dc:title>Prism Launcher</dc:title>
</cc:Agent>
</dc:publisher>
<cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/"/>
</cc:Work>
<cc:License rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#ShareAlike"/>
</cc:License>
</rdf:RDF>
</metadata>
</svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -16,7 +16,6 @@
<file>scalable/jarmods.svg</file> <file>scalable/jarmods.svg</file>
<file>scalable/java.svg</file> <file>scalable/java.svg</file>
<file>scalable/language.svg</file> <file>scalable/language.svg</file>
<file>scalable/launcher.svg</file>
<file>scalable/loadermods.svg</file> <file>scalable/loadermods.svg</file>
<file>scalable/log.svg</file> <file>scalable/log.svg</file>
<file>scalable/minecraft.svg</file> <file>scalable/minecraft.svg</file>

View File

@ -1,57 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" version="1.1" viewBox="0 0 12.7 12.7" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<title>Prism Launcher Logo</title>
<g stroke-width=".26458">
<path d="m6.35 6.35" fill="#99cd61"/>
<path d="m6.35 0.52917-2.5208 4.3656 2.5208 1.4552 2.5203-1.4552 0.10955-3.0996c-1.1511-0.66459-2.3388-1.2661-2.6298-1.2661z" fill="#df6277"/>
<path d="m8.9798 1.7952-2.6298 4.5548 2.5203 1.4552 2.5208-4.3656c-0.14552-0.25205-1.2601-0.97975-2.4112-1.6443z" fill="#fb9168"/>
<path d="m11.391 3.4396-5.041 2.9104 2.5203 1.4552 2.7389-1.4552c0-1.3292-0.072554-2.6584-0.21808-2.9104z" fill="#f3db6c"/>
<path d="m6.35 6.35v2.9104h5.041c0.14552-0.25205 0.21807-1.5812 0.21808-2.9104h-5.2591z" fill="#7ab392"/>
<path d="m6.35 6.35v2.9104l2.6298 1.6443c1.1511-0.66459 2.2657-1.3923 2.4112-1.6443l-5.041-2.9104z" fill="#4b7cbc"/>
<path d="m6.35 6.35-2.5208 1.4552 2.5208 4.3656c0.29104 0 1.4787-0.60148 2.6298-1.2661l-2.6298-4.5548z" fill="#6f488c"/>
<path d="m3.8292 4.8948-2.5203 4.3656c0.29104 0.5041 4.459 2.9104 5.041 2.9104v-5.8208l-2.5208-1.4552z" fill="#4d3f33"/>
<path d="m1.309 3.4396c-0.29104 0.5041-0.29104 5.3167 0 5.8208l5.041-2.9104v-2.9104h-5.041z" fill="#7a573b"/>
<path d="m6.35 0.52917c-0.58208-2e-8 -4.75 2.4063-5.041 2.9104l5.041 2.9104v-5.8208z" fill="#99cd61"/>
</g>
<g transform="matrix(.88 0 0 .88 -10.906 -1.2421)">
<g transform="translate(13.26 2.2776)">
<path transform="matrix(.96975 0 0 .96975 .1921 .1921)" d="m6.3498 2.9393c-0.34105 0-2.7827 1.4099-2.9532 1.7052l2.9532 5.1157 2.9538-5.1157c-0.17052-0.29535-2.6127-1.7052-2.9538-1.7052z" fill="#fff" stroke-width=".26458"/>
</g>
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
</g>
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:title>Prism Launcher Logo</dc:title>
<dc:date>19/10/2022</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Prism Launcher</dc:title>
</cc:Agent>
</dc:creator>
<dc:contributor>
<cc:Agent>
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
</cc:Agent>
</dc:contributor>
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
<dc:publisher>
<cc:Agent>
<dc:title>Prism Launcher</dc:title>
</cc:Agent>
</dc:publisher>
<cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/"/>
</cc:Work>
<cc:License rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#ShareAlike"/>
</cc:License>
</rdf:RDF>
</metadata>
</svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -16,7 +16,6 @@
<file>scalable/jarmods.svg</file> <file>scalable/jarmods.svg</file>
<file>scalable/java.svg</file> <file>scalable/java.svg</file>
<file>scalable/language.svg</file> <file>scalable/language.svg</file>
<file>scalable/launcher.svg</file>
<file>scalable/loadermods.svg</file> <file>scalable/loadermods.svg</file>
<file>scalable/log.svg</file> <file>scalable/log.svg</file>
<file>scalable/minecraft.svg</file> <file>scalable/minecraft.svg</file>

View File

@ -1,57 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" version="1.1" viewBox="0 0 12.7 12.7" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<title>Prism Launcher Logo</title>
<g stroke-width=".26458">
<path d="m6.35 6.35" fill="#99cd61"/>
<path d="m6.35 0.52917-2.5208 4.3656 2.5208 1.4552 2.5203-1.4552 0.10955-3.0996c-1.1511-0.66459-2.3388-1.2661-2.6298-1.2661z" fill="#df6277"/>
<path d="m8.9798 1.7952-2.6298 4.5548 2.5203 1.4552 2.5208-4.3656c-0.14552-0.25205-1.2601-0.97975-2.4112-1.6443z" fill="#fb9168"/>
<path d="m11.391 3.4396-5.041 2.9104 2.5203 1.4552 2.7389-1.4552c0-1.3292-0.072554-2.6584-0.21808-2.9104z" fill="#f3db6c"/>
<path d="m6.35 6.35v2.9104h5.041c0.14552-0.25205 0.21807-1.5812 0.21808-2.9104h-5.2591z" fill="#7ab392"/>
<path d="m6.35 6.35v2.9104l2.6298 1.6443c1.1511-0.66459 2.2657-1.3923 2.4112-1.6443l-5.041-2.9104z" fill="#4b7cbc"/>
<path d="m6.35 6.35-2.5208 1.4552 2.5208 4.3656c0.29104 0 1.4787-0.60148 2.6298-1.2661l-2.6298-4.5548z" fill="#6f488c"/>
<path d="m3.8292 4.8948-2.5203 4.3656c0.29104 0.5041 4.459 2.9104 5.041 2.9104v-5.8208l-2.5208-1.4552z" fill="#4d3f33"/>
<path d="m1.309 3.4396c-0.29104 0.5041-0.29104 5.3167 0 5.8208l5.041-2.9104v-2.9104h-5.041z" fill="#7a573b"/>
<path d="m6.35 0.52917c-0.58208-2e-8 -4.75 2.4063-5.041 2.9104l5.041 2.9104v-5.8208z" fill="#99cd61"/>
</g>
<g transform="matrix(.88 0 0 .88 -10.906 -1.2421)">
<g transform="translate(13.26 2.2776)">
<path transform="matrix(.96975 0 0 .96975 .1921 .1921)" d="m6.3498 2.9393c-0.34105 0-2.7827 1.4099-2.9532 1.7052l2.9532 5.1157 2.9538-5.1157c-0.17052-0.29535-2.6127-1.7052-2.9538-1.7052z" fill="#fff" stroke-width=".26458"/>
</g>
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
</g>
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:title>Prism Launcher Logo</dc:title>
<dc:date>19/10/2022</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Prism Launcher</dc:title>
</cc:Agent>
</dc:creator>
<dc:contributor>
<cc:Agent>
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
</cc:Agent>
</dc:contributor>
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
<dc:publisher>
<cc:Agent>
<dc:title>Prism Launcher</dc:title>
</cc:Agent>
</dc:publisher>
<cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/"/>
</cc:Work>
<cc:License rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
<cc:requires rdf:resource="http://creativecommons.org/ns#ShareAlike"/>
</cc:License>
</rdf:RDF>
</metadata>
</svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

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