Compare commits

...

94 Commits

Author SHA1 Message Date
ebbfaf8713
Add custom login support 2023-06-21 13:23:24 -04:00
8e6c20dc99
Fix the various different bugs i initially created 2023-06-20 15:08:02 -04:00
daa73a756a
Merge branch 'develop' of https://github.com/fn2006/PollyMC into develop 2023-06-20 14:35:28 -04:00
94f3d61302
Add custom login support 2023-06-20 14:35:02 -04:00
Sayantan Chaudhuri
b2c9183c41
Fix tabs with spaces in build.yml (#65)
* Fix tabs with spaces

* Switching to manual dispatch for the built workflow

This is for private testing

* Switching to simpler manual dispatch trigger

* Back to original trigger
2023-06-12 17:23:01 +00:00
fn2006
a3e64b6f1b fix typo 2023-06-09 16:23:27 +01:00
fn2006
a13d3f4a38 Attempt to get flatpak builds working 2023-06-09 16:20:36 +01:00
fn2006
392822ec7a
Add support for 2FA on ely.by accounts (#64) 2023-06-09 13:42:13 +00:00
fn2006
cc85b44ff2 Merge branch 'release-7.x' of https://github.com/PrismLauncher/PrismLauncher into develop 2023-06-09 13:35:03 +01:00
DioEgizio
bfe7e3afed
Merge pull request #1133 from DioEgizio/bump-mac-11.0 2023-06-08 18:11:59 +02:00
DioEgizio
75b1eaed0c
chore: bump macOS requirement to 11.0
Noticed only now that Qt 6.5 bumps the macOS requirement to macOS 11.
This was basically already effective in prism since with the Qt 6.5 bump pr macOS 10.15 user's wouldn't be able to run this, but updating the requirement here makes it more clear for the end user trying to run prism on macOS 10.15

Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
2023-06-08 17:12:49 +02:00
fn2006
51adc79fe1 Revert "chore: remove FTB modpack support"
This reverts commit ff07714e8c.
2023-05-30 19:54:28 +01:00
fn2006
c00f476312 forgot to git add the deletion part of renaming Polly.png 2023-05-30 19:48:42 +01:00
fn2006
ca4f8ac546 add polly to background selection as a seperate option 2023-05-30 19:46:32 +01:00
fn2006
17eca6d497 update licenses to new filenames 2023-05-30 19:28:23 +01:00
fn2006
ffbb8aba85 rename polly background filenames to be consistent with the others 2023-05-30 19:26:16 +01:00
fn2006
d61ebca9bc fix things broken by the merge 2023-05-30 19:24:20 +01:00
fn2006
a951bd1c3e Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into develop 2023-05-30 17:59:26 +01:00
fn2006
eb03d0a2a2 update .gitignore 2023-05-30 17:35:56 +01:00
fn2006
ca635f7d3d don't build snap nix or flatpak 2023-02-05 22:33:05 +00:00
fn2006
c11f5e990e update copyright year 2023-02-05 22:21:41 +00:00
fn2006
6bfa4b1e0f Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into develop 2023-02-05 22:13:16 +00:00
fn2006
1821081521 Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into develop 2023-02-05 22:11:58 +00:00
fn2006
1ff4c683b2 Fix Polly background 2022-12-24 23:25:43 +00:00
fn2006
d02847271e
Update README.md 2022-12-20 23:48:42 +00:00
fn2006
876f12f71b add polly to cat selection 2022-12-20 22:43:57 +00:00
fn2006
979d33ab83
Use PolyMC's CurseForge workaround (#47)
* Curseforge workarounds

This should allow people to use Curseforge without having to manually
paste a working key into the settings or change the user agent.

Signed-off-by: Lenny McLennington <lenny@sneed.church>

* chore: update cf api key api url

Sascha says the domain name we're using is not gonna be renewed, so I'm
switching it to a domain controlled by me instead so that this won't be
a problem in the future.

Signed-off-by: Lenny McLennington <lenny@sneed.church>

* feat: add ability to disable cf api key fetching

by setting the cf api key api url to a blank string

Signed-off-by: Lenny McLennington <lenny@sneed.church>

* don't ask before fetching key

* change polymc mention to pollymc

Signed-off-by: Lenny McLennington <lenny@sneed.church>
Co-authored-by: Lenny McLennington <lenny@sneed.church>
2022-12-20 22:42:34 +00:00
fn2006
e71284c812 remove drm from download all button 2022-12-20 20:31:28 +00:00
fn2006
64b7562919 Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into develop 2022-12-20 20:23:42 +00:00
fn2006
ef713cf414 Merge branch 'develop' of upstream into develop 2022-11-13 23:28:58 +00:00
Ryan Bjb
413dd73a73
Possible fix to windows builds (#36)
Error logs show:
ERROR: Line 3: The value of attribute version in element assemblyIdentity is invalid.
this should fix it, as i copied the value from prismlauncher.
2022-11-02 15:12:50 +00:00
fn2006
53e1f96bea
attempt to fix windows builds 2022-11-02 10:23:33 +00:00
fn2006
66a98380d0 i fucked up the cherrypick 2022-11-01 20:24:40 +00:00
Sefa Eyeoglu
466f9c3ec7 fix: fix logo name
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
2022-11-01 20:08:46 +00:00
fn2006
d53a539764 update launcher.svg 2022-11-01 19:51:35 +00:00
fn2006
37fc2d5d79 fix build 2022-11-01 18:50:45 +00:00
fn2006
e6253c8091 update NIX.md 2022-11-01 18:49:21 +00:00
fn2006
44a5130926 Merge branch 'release-5.x' of https://github.com/PrismLauncher/PrismLauncher into prism 2022-11-01 18:48:31 +00:00
Sefa Eyeoglu
d8044ababe
Merge pull request #332 from Scrumplex/chore-bump-5.1 2022-11-01 11:01:49 +01:00
timoreo
32b526b729
Merge pull request #333 from flowln/fix_atl_packs_post_modpack_update 2022-11-01 11:01:15 +01:00
timoreo
7f6515dbe4
Merge pull request #329 from flowln/only_safe_workarounds 2022-11-01 11:01:15 +01:00
DioEgizio
a39390b8b4
Merge pull request #359 from Chrono-byte/develop 2022-11-01 09:48:13 +01:00
Sefa Eyeoglu
63a3dd1919
Merge pull request #351 from Scrumplex/fix-trash 2022-10-31 22:58:09 +01:00
DioEgizio
7a5a4de6ea
Merge pull request #354 from Scrumplex/translations-maybe
Improve display names of certain languages
2022-10-31 11:31:47 +01:00
DioEgizio
664d4e701e
Merge pull request #352 from TheLastRar/Win-setDarkWinTitlebar-10OrGreater 2022-10-31 11:31:47 +01:00
flow
a4ba8d8288
Merge pull request #353 from FayneAldan/accounts-consistency 2022-10-31 01:42:20 +01:00
txtsd
392bf7a97b
Merge pull request #342 from fn2006/prism-svg-fix 2022-10-29 23:11:37 +02:00
Sefa Eyeoglu
34687049b1
Merge pull request #338 from Scrumplex/fix-credits-1 2022-10-29 16:15:58 +02:00
fn2006
0f581efda5 fix hardcoded svg path 2022-10-29 14:16:14 +01:00
Sefa Eyeoglu
9337ec6706
Merge pull request #173 from Scrumplex/fix-icons 2022-10-29 00:36:15 +02:00
Sefa Eyeoglu
5bcb6962c4
chore: bump version
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
2022-10-28 21:54:10 +02:00
txtsd
0617b43190
Merge pull request #322 from DioEgizio/64bit-it 2022-10-28 21:34:59 +02:00
Sefa Eyeoglu
ed28234cfb
Merge pull request #319 from Scrumplex/fix-avoid-mr-segfault 2022-10-28 21:34:59 +02:00
DioEgizio
9c4455ca03
Merge pull request #301 from DioEgizio/clang-attempt
feat(actions): use clang32 for building on windows
2022-10-28 21:34:59 +02:00
Sefa Eyeoglu
9ec7837275
Merge pull request #318 from TheLastRar/manifest-platform
Fix: Don't specify x86 in manifest
2022-10-28 21:34:59 +02:00
Sefa Eyeoglu
549b5a6488
Merge pull request #231 from tobimori/patch-1 2022-10-28 21:34:59 +02:00
DioEgizio
2652f37453
Merge pull request #206 from flowln/changelog_height_fix 2022-10-28 21:34:59 +02:00
Sefa Eyeoglu
0eaff22145
Merge pull request #283 from flowln/fix_abort_on_autosearch 2022-10-28 21:34:59 +02:00
flow
2f5393b9d0
Merge pull request #281 from Scrumplex/fix-nsis-displayname 2022-10-28 21:34:59 +02:00
Sefa Eyeoglu
e28480a8e4
Merge pull request #274 from Protrikk/patch-1 2022-10-28 21:34:59 +02:00
flow
fcef6321fc
Merge pull request #228 from bensuperpc/change_cast 2022-10-28 21:34:59 +02:00
Sefa Eyeoglu
35e792c5de
Merge pull request #240 from jn64/fix/version-label-width 2022-10-28 21:34:59 +02:00
flow
c08b632b51
Merge pull request #234 from AliceDTRH/fix/dedupejava 2022-10-28 21:34:59 +02:00
flow
cac800bfd8
Merge pull request #233 from jamierocks/atl-fix-aborting-installs 2022-10-28 21:34:59 +02:00
Sefa Eyeoglu
2eb8173951
Merge pull request #224 from jamierocks/atl-abort-close-optional-mods-dialog 2022-10-28 21:34:59 +02:00
Sefa Eyeoglu
75abf2c124
Merge pull request #225 from Scrumplex/fix-segfault-fileresolver 2022-10-28 21:34:59 +02:00
flow
d40a18d6c5
Merge pull request #218 from getchoo/change-jars-path 2022-10-28 21:34:59 +02:00
Sefa Eyeoglu
a74fdc588c
Merge pull request #185 from flowln/fix_blocked_mods_crash 2022-10-28 21:34:59 +02:00
flow
25b0ec6eff
Merge pull request #147 from Minion3665/enhancement/update-nix-derivation 2022-10-28 21:34:59 +02:00
txtsd
04e8982d33
Merge pull request #198 from PrismLauncher/renovate/hendrikmuhs-ccache-action-1.x 2022-10-28 21:34:59 +02:00
txtsd
58bd449db8
Merge pull request #197 from PrismLauncher/renovate/actions-cache-3.x 2022-10-28 21:34:59 +02:00
Sefa Eyeoglu
c984e9b5d6
Merge pull request #202 from Heath123/patch-1 2022-10-28 21:34:59 +02:00
Sefa Eyeoglu
ddd319369a
Merge pull request #39 from Sebbl0508/mod_dialog_fontsize 2022-10-28 21:34:59 +02:00
Sefa Eyeoglu
de3c336213
Merge pull request #184 from Chrono-byte/develop 2022-10-28 21:34:59 +02:00
Sefa Eyeoglu
6e94e9bff1
Merge pull request #148 from ZombieNub/prismlauncher-rename 2022-10-28 21:34:59 +02:00
flow
93b8d9e454
Merge pull request #123 from MMK21Hub/patch-1 2022-10-28 21:34:59 +02:00
flow
6d46081864
Merge pull request #100 from TheEvilSkeleton/improve-approachability 2022-10-28 21:34:59 +02:00
fn2006
765fb42a7e
Update README.md 2022-10-24 16:33:52 +01:00
fn2006
ecade0f510 small fixes 2022-10-24 16:27:07 +01:00
fn2006
23e5773094 Merge branch 'release-5.x' of https://github.com/PrismLauncher/PrismLauncher into prism 2022-10-24 16:24:36 +01:00
fn2006
1b9b1ccf31
Update README.md 2022-10-19 15:54:32 +01:00
fn2006
121f6b2a4e
Update README.md 2022-10-17 23:15:07 +01:00
fn2006
7403918c89
Update README.md 2022-10-17 20:55:01 +01:00
fn2006
ba6a8d1469 Add getToken.py 2022-09-29 15:11:12 +01:00
fn2006
11f97a78fe Merge branch 'develop' of https://github.com/PolyMC/PolyMC into rebase 2022-09-29 15:10:32 +01:00
fn2006
761f20a3e2 Rebase changes into latest PolyMC 2022-09-15 11:51:36 +01:00
Fintan Martin
a6f2136940 Return nullptr instead of 0 2022-08-22 12:24:00 +01:00
Fintan Martin
763627ee65 Merge branch 'develop' of https://github.com/PolyMC/PolyMC into rebase 2022-08-22 11:58:41 +01:00
Fintan Martin
31652097e0 Merge branch 'develop' of https://github.com/PolyMC/PolyMC into rebase 2022-08-19 14:26:34 +01:00
Fintan Martin
2782588af7 Update README.md 2022-08-19 14:26:08 +01:00
Fintan Martin
03923c9e87 Rebrand to PollyMC 2022-08-19 13:56:46 +01:00
Fintan Martin
4cfaaefca4 Update CMakeLists.txt 2022-08-19 13:00:47 +01:00
fn2006
0170e8b177 Add Ely.by accounts (#17)
* Initial Ely.by support

* Fix profile pictures for Ely.by

* Disable upload and delete skin buttons for Ely.by accounts

* Port UltimMC's authlib injector to PollyMC
2022-08-19 12:22:38 +01:00
Fintan Martin
050ee33132 Remove offline mode DRM
This reverts commit e0a04c5031.
2022-08-19 12:17:48 +01:00
112 changed files with 5869 additions and 831 deletions

1
.github/FUNDING.yml vendored
View File

@ -1 +0,0 @@
open_collective: prismlauncher

View File

@ -5,8 +5,6 @@ body:
- type: markdown
attributes:
value: |
If you need help with running Minecraft, please visit us on our Discord before making a bug report.
Before submitting a bug report, please make sure you have read this *entire* form, and that:
* You have read the [Prism Launcher wiki](https://prismlauncher.org/wiki/) and it has not answered your question.
* Your bug is not caused by Minecraft or any mods you have installed.
@ -25,15 +23,15 @@ body:
- Other
- type: textarea
attributes:
label: Version of Prism Launcher
description: The version of Prism Launcher used in the bug report.
placeholder: Prism Launcher 5.0
label: Version of PollyMC
description: The version of PollyMC used in the bug report.
placeholder: PollyMC 5.1
validations:
required: true
- type: textarea
attributes:
label: Version of Qt
description: The version of Qt used in the bug report. You can find it in Help -> About Prism Launcher -> About Qt.
description: The version of Qt used in the bug report. You can find it in Help -> About PollyMC -> About Qt.
placeholder: Qt 6.3.0
validations:
required: true
@ -41,14 +39,14 @@ body:
attributes:
label: Description of bug
description: What did you expect to happen, what happened, and why is it incorrect?
placeholder: The cat button should show a cat, but it showed a dog instead!
placeholder: The parrot button should show a parrot, but it showed a cat instead!
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: A bulleted list, or an exported instance if relevant.
placeholder: "* Press the cat button"
placeholder: "* Press the parrot button"
validations:
required: true
- type: textarea

View File

@ -1,5 +1 @@
blank_issues_enabled: true
contact_links:
- name: Prism Launcher Matrix Support Room
url: https://matrix.to/#/#prism-support:matrix.org
about: Please ask for support here before opening an issue.

View File

@ -6,38 +6,38 @@ body:
- type: markdown
attributes:
value: |
### Use this form to suggest a larger change for Prism Launcher.
### Use this form to suggest a larger change for PollyMC.
- type: textarea
attributes:
label: Goal
description: Short description, 1-2 sentences.
placeholder: Remove the cat from the launcher.
placeholder: Remove the parrot from the launcher.
validations:
required: true
- type: textarea
attributes:
label: Motivation
description: |
Introduce the topic. If this is a not-well-known section of Prism Launcher, a detailed explanation of the background is recommended.
Introduce the topic. If this is a not-well-known section of PollyMC, a detailed explanation of the background is recommended.
Some example points of discussion:
- What specific problems are you facing right now that you're trying to address?
- Are there any previous discussions? Link to them and summarize them (don't force your readers to read them though!).
- Is there any precedent set by other software? If so, link to resources.
placeholder: I don't like cats. I think many users also don't like cats.
placeholder: I don't like parrots. I think many users also don't like parrots.
validations:
required: true
- type: textarea
attributes:
label: Specification
description: A concrete, thorough explanation of what is being planned.
placeholder: Remove the cat button and all references to the cat from the codebase. Including resource files.
placeholder: Remove the parrot button and all references to the parrot from the codebase. Including resource files.
validations:
required: true
- type: textarea
attributes:
label: Drawbacks
description: Carefully consider every possible objection and issue with your proposal. This section should be updated as feedback comes in from discussion.
placeholder: Some users might like cats.
placeholder: Some users might like parrots.
validations:
required: true
- type: textarea
@ -47,14 +47,14 @@ body:
Are there any portions of your proposal which need to be discussed with the community before the RFC can proceed?
Be careful here -- an RFC with a lot of remaining questions is likely to be stalled.
If your RFC is mostly unresolved questions and not too much substance, it may not be ready.
placeholder: Do a lot of users care about the cat?
placeholder: Do a lot of users care about the parrot?
validations:
required: true
- type: textarea
attributes:
label: Alternatives Considered
description: A list of alternatives, that have been considered and offer equal or similar features to the proposed change.
placeholder: Maybe the cat could be replaced with an axolotl?
placeholder: Maybe the parrot could be replaced with a dog?
validations:
required: true
- type: checkboxes

View File

@ -5,26 +5,26 @@ body:
- type: markdown
attributes:
value: |
### Use this form to suggest a feature for Prism Launcher.
### Use this form to suggest a feature for PollyMC.
- type: input
attributes:
label: Role
description: In what way do you use Prism Launcher that needs this feature?
description: In what way do you use PollyMC that needs this feature?
placeholder: I play modded Minecraft.
validations:
required: true
- type: input
attributes:
label: Suggestion
description: What do you want Prism Launcher to do?
placeholder: I want the cat button to meow.
description: What do you want PollyMC to do?
placeholder: I want the parrot button to squawk.
validations:
required: true
- type: input
attributes:
label: Benefit
description: Why do you need Prism Launcher to do this?
placeholder: so that I can always hear a cat when I need to.
description: Why do you need PollyMC to do this?
placeholder: so that I can always hear a parrot when I need to.
validations:
required: true
- type: checkboxes

View File

@ -86,7 +86,7 @@ jobs:
- os: macos-12
name: macOS
macosx_deployment_target: 10.15
macosx_deployment_target: 11.0
qt_ver: 6
qt_host: mac
qt_arch: ''
@ -346,10 +346,9 @@ jobs:
cmake --install ${{ env.BUILD_DIR }}
cd ${{ env.INSTALL_DIR }}
chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher"
mv "PrismLauncher.app" "Prism Launcher.app"
tar -czf ../PrismLauncher.tar.gz *
chmod +x "PollyMC.app/Contents/MacOS/pollymc"
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PollyMC.app/Contents/MacOS/pollymc"
tar -czf ../PollyMC.tar.gz *
- name: Make Sparkle signature (macOS)
if: matrix.name == 'macOS'
@ -357,7 +356,7 @@ jobs:
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
brew install openssl@3
echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem
signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.tar.gz -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PollyMC.tar.gz -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
rm ed25519-priv.pem
cat >> $GITHUB_STEP_SUMMARY << EOF
### Artifact Information :information_source:
@ -386,8 +385,8 @@ jobs:
cd ${{ env.INSTALL_DIR }}
if ("${{ matrix.qt_ver }}" -eq "5")
{
Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll
Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll
Copy-Item D:/a/PollyMC/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll
Copy-Item D:/a/PollyMC/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll
}
cd ${{ github.workspace }}
@ -406,7 +405,7 @@ jobs:
if (Get-Content ./codesign.pfx){
cd ${{ env.INSTALL_DIR }}
# We ship the exact same executable for portable and non-portable editions, so signing just once is fine
SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_filelink.exe
SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com pollymc.exe pollymc_filelink.exe
} else {
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
}
@ -437,7 +436,7 @@ jobs:
if: runner.os == 'Windows'
run: |
if (Get-Content ./codesign.pfx){
SignTool sign /fd sha256 /td sha256 /f codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com PrismLauncher-Setup.exe
SignTool sign /fd sha256 /td sha256 /f codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com PollyMC-Setup.exe
} else {
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
}
@ -449,7 +448,7 @@ jobs:
for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_DIR }}/manifest.txt
cd ${{ env.INSTALL_DIR }}
tar --owner root --group root -czf ../PrismLauncher.tar.gz *
tar --owner root --group root -czf ../PollyMC.tar.gz *
- name: Package (Linux, portable)
if: runner.os == 'Linux'
@ -460,7 +459,7 @@ jobs:
cd ${{ env.INSTALL_PORTABLE_DIR }}
tar -czf ../PrismLauncher-portable.tar.gz *
tar -czf ../PollyMC-portable.tar.gz *
- name: Package AppImage (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 5
@ -468,7 +467,7 @@ jobs:
run: |
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
export OUTPUT="PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
export OUTPUT="PollyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
chmod +x linuxdeploy-*.AppImage
@ -479,7 +478,7 @@ jobs:
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk
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/PollyMC/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/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}//usr/lib/
@ -491,7 +490,7 @@ jobs:
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
export LD_LIBRARY_PATH
./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.prismlauncher.PrismLauncher.svg
./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.fn2006.PollyMC.svg
##
# UPLOAD BUILDS
@ -501,64 +500,64 @@ jobs:
if: runner.os == 'macOS'
uses: actions/upload-artifact@v3
with:
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher.tar.gz
name: PollyMC-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
path: PollyMC.tar.gz
- name: Upload binary zip (Windows)
if: runner.os == 'Windows'
uses: actions/upload-artifact@v3
with:
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
name: PollyMC-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
path: ${{ env.INSTALL_DIR }}/**
- name: Upload binary zip (Windows, portable)
if: runner.os == 'Windows'
uses: actions/upload-artifact@v3
with:
name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
name: PollyMC-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: ${{ env.INSTALL_PORTABLE_DIR }}/**
- name: Upload installer (Windows)
if: runner.os == 'Windows'
uses: actions/upload-artifact@v3
with:
name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher-Setup.exe
name: PollyMC-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
path: PollyMC-Setup.exe
- name: Upload binary tarball (Linux, Qt 5)
if: runner.os == 'Linux' && matrix.qt_ver != 6
uses: actions/upload-artifact@v3
with:
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher.tar.gz
name: PollyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
path: PollyMC.tar.gz
- name: Upload binary tarball (Linux, portable, Qt 5)
if: runner.os == 'Linux' && matrix.qt_ver != 6
uses: actions/upload-artifact@v3
with:
name: PrismLauncher-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher-portable.tar.gz
name: PollyMC-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: PollyMC-portable.tar.gz
- name: Upload binary tarball (Linux, Qt 6)
if: runner.os == 'Linux' && matrix.qt_ver !=5
uses: actions/upload-artifact@v3
with:
name: PrismLauncher-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher.tar.gz
name: PollyMC-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }}
path: PollyMC.tar.gz
- name: Upload binary tarball (Linux, portable, Qt 6)
if: runner.os == 'Linux' && matrix.qt_ver != 5
uses: actions/upload-artifact@v3
with:
name: PrismLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: PrismLauncher-portable.tar.gz
name: PollyMC-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
path: PollyMC-portable.tar.gz
- name: Upload AppImage (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 5
uses: actions/upload-artifact@v3
with:
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
name: PollyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
path: PollyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
- name: ccache stats (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != ''
@ -574,42 +573,18 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
if: inputs.build_type == 'Debug'
if: inputs.build_type == 'Release'
with:
submodules: 'true'
- name: Build Flatpak (Linux)
if: inputs.build_type == 'Debug'
if: inputs.build_type == 'Release'
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
with:
bundle: "Prism Launcher.flatpak"
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
nix:
runs-on: ubuntu-latest
strategy:
matrix:
package:
- prismlauncher
- prismlauncher-qt5
steps:
- name: Clone repository
if: inputs.build_type == 'Debug'
uses: actions/checkout@v3
bundle: "PollyMC.flatpak"
manifest-path: flatpak/org.fn2006.PollyMC.yml
- name: Upload Flatpak (Linux)
if: runner.os == 'Linux' && matrix.qt_ver != 5
uses: actions/upload-artifact@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
name: PollyMC-${{ env.VERSION }}-x86_64.flatpak
path: PollyMC.flatpak

View File

@ -10,7 +10,6 @@ on:
- 'flake.lock'
- 'packages/**'
- '.github/ISSUE_TEMPLATE/**'
- '.markdownlint**'
pull_request:
paths-ignore:
- '**.md'
@ -18,7 +17,6 @@ on:
- 'flake.lock'
- 'packages/**'
- '.github/ISSUE_TEMPLATE/**'
- '.markdownlint**'
workflow_dispatch:
jobs:

View File

@ -29,7 +29,7 @@ jobs:
uses: actions/checkout@v3
with:
submodules: 'true'
path: 'PrismLauncher-source'
path: 'PollyMC-source'
- name: Download artifacts
uses: actions/download-artifact@v3
- name: Grab and store version
@ -38,39 +38,39 @@ jobs:
echo "VERSION=$tag_name" >> $GITHUB_ENV
- name: Package artifacts properly
run: |
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage
mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
mv ${{ github.workspace }}/PollyMC-source PollyMC-${{ env.VERSION }}
mv PollyMC-Linux-Qt6-Portable*/PollyMC-portable.tar.gz PollyMC-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
mv PollyMC-Linux-Qt6*/PollyMC.tar.gz PollyMC-Linux-Qt6-${{ env.VERSION }}.tar.gz
mv PollyMC-Linux-Portable*/PollyMC-portable.tar.gz PollyMC-Linux-Portable-${{ env.VERSION }}.tar.gz
mv PollyMC-Linux*/PollyMC.tar.gz PollyMC-Linux-${{ env.VERSION }}.tar.gz
mv PollyMC-*.AppImage/PollyMC-*.AppImage PollyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
mv PollyMC-macOS-Legacy*/PollyMC.tar.gz PollyMC-macOS-Legacy-${{ env.VERSION }}.tar.gz
mv PollyMC-macOS*/PollyMC.tar.gz PollyMC-macOS-${{ env.VERSION }}.tar.gz
tar -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }}
tar -czf PollyMC-${{ env.VERSION }}.tar.gz PollyMC-${{ env.VERSION }}
for d in PrismLauncher-Windows-MSVC*; do
for d in PollyMC-Windows-MSVC*; do
cd "${d}" || continue
LEGACY="$(echo -n ${d} | grep -o Legacy || true)"
ARM64="$(echo -n ${d} | grep -o arm64 || true)"
INST="$(echo -n ${d} | grep -o Setup || true)"
PORT="$(echo -n ${d} | grep -o Portable || true)"
NAME="PrismLauncher-Windows-MSVC"
NAME="PollyMC-Windows-MSVC"
test -z "${LEGACY}" || NAME="${NAME}-Legacy"
test -z "${ARM64}" || NAME="${NAME}-arm64"
test -z "${PORT}" || NAME="${NAME}-Portable"
test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
test -z "${INST}" || mv PollyMC-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
cd ..
done
for d in PrismLauncher-Windows-MinGW-w64*; do
for d in PollyMC-Windows-MinGW-w64*; do
cd "${d}" || continue
INST="$(echo -n ${d} | grep -o Setup || true)"
PORT="$(echo -n ${d} | grep -o Portable || true)"
NAME="PrismLauncher-Windows-MinGW-w64"
NAME="PollyMC-Windows-MinGW-w64"
test -z "${PORT}" || NAME="${NAME}-Portable"
test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
test -z "${INST}" || mv PollyMC-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
cd ..
done
@ -82,27 +82,28 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
name: Prism Launcher ${{ env.VERSION }}
name: PollyMC ${{ env.VERSION }}
draft: true
prerelease: false
files: |
PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-MSVC-Legacy-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-Legacy-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-Legacy-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-arm64-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-arm64-Setup-${{ env.VERSION }}.exe
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip
PrismLauncher-Windows-MSVC-Setup-${{ env.VERSION }}.exe
PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
PrismLauncher-${{ env.VERSION }}.tar.gz
PollyMC-Linux-${{ env.VERSION }}.tar.gz
PollyMC-Linux-Portable-${{ env.VERSION }}.tar.gz
PollyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
PollyMC-Linux-Qt6-${{ env.VERSION }}.tar.gz
PollyMC-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
PollyMC-Windows-MinGW-w64-${{ env.VERSION }}.zip
PollyMC-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
PollyMC-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe
PollyMC-Windows-MSVC-Legacy-${{ env.VERSION }}.zip
PollyMC-Windows-MSVC-Legacy-Portable-${{ env.VERSION }}.zip
PollyMC-Windows-MSVC-Legacy-Setup-${{ env.VERSION }}.exe
PollyMC-Windows-MSVC-arm64-${{ env.VERSION }}.zip
PollyMC-Windows-MSVC-arm64-Portable-${{ env.VERSION }}.zip
PollyMC-Windows-MSVC-arm64-Setup-${{ env.VERSION }}.exe
PollyMC-Windows-MSVC-${{ env.VERSION }}.zip
PollyMC-Windows-MSVC-Portable-${{ env.VERSION }}.zip
PollyMC-Windows-MSVC-Setup-${{ env.VERSION }}.exe
PollyMC-macOS-${{ env.VERSION }}.tar.gz
PollyMC-macOS-Legacy-${{ env.VERSION }}.tar.gz
PollyMC-${{ env.VERSION }}.tar.gz

1
.gitignore vendored
View File

@ -8,6 +8,7 @@ resources/CMakeFiles
html/
# Project Files
*.orig
*.pro.user
CMakeLists.txt.user
CMakeLists.txt.user.*

View File

@ -158,19 +158,19 @@ set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
# Bug tracker URL
set(Launcher_BUG_TRACKER_URL "https://github.com/PrismLauncher/PrismLauncher/issues" CACHE STRING "URL for the bug tracker.")
set(Launcher_BUG_TRACKER_URL "https://github.com/fn2006/PollyMC/issues" CACHE STRING "URL for the bug tracker.")
# Translations Platform URL
set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/prismlauncher/launcher/" CACHE STRING "URL for the translations platform.")
# Matrix Space
set(Launcher_MATRIX_URL "https://prismlauncher.org/matrix" CACHE STRING "URL to the Matrix Space")
set(Launcher_MATRIX_URL "" CACHE STRING "URL to the Matrix Space")
# Discord URL
set(Launcher_DISCORD_URL "https://prismlauncher.org/discord" CACHE STRING "URL for the Discord guild.")
set(Launcher_DISCORD_URL "" CACHE STRING "URL for the Discord guild.")
# Subreddit URL
set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL for the subreddit.")
set(Launcher_SUBREDDIT_URL "" CACHE STRING "URL for the subreddit.")
# Builds
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
@ -185,13 +185,13 @@ set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build agains
# By using this key in your builds you accept the terms of use laid down in
# https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use
set(Launcher_MSA_CLIENT_ID "c36a9fb6-4f2a-41ff-90bd-ae7cc92031eb" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
set(Launcher_MSA_CLIENT_ID "" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
# By using this key in your builds you accept the terms and conditions laid down in
# https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions
# NOTE: CurseForge requires you to change this if you make any kind of derivative work.
# This key was issued specifically for Prism Launcher
set(Launcher_CURSEFORGE_API_KEY "$2a$10$wuAJuNZuted3NORVmpgUC.m8sI.pv1tOPKZyBgLFGjxFp/br0lZCC" CACHE STRING "API key for the CurseForge platform")
set(Launcher_CURSEFORGE_API_KEY "" CACHE STRING "API key for the CurseForge platform")
set(Launcher_CURSEFORGE_API_KEY_API_URL "https://cf.polymc.org/api" CACHE STRING "URL to fetch the Curseforge API key from.")
#### Check the current Git commit and branch
@ -281,7 +281,7 @@ include(ECMQtDeclareLoggingCategory)
####################################### Program Info #######################################
set(Launcher_APP_BINARY_NAME "prismlauncher" CACHE STRING "Name of the Launcher binary")
set(Launcher_APP_BINARY_NAME "pollymc" CACHE STRING "Name of the Launcher binary")
add_subdirectory(program_info)
####################################### Install layout #######################################

12
PollyMC.iml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/libraries/javacheck" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/libraries/launcher" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

103
README.md
View File

@ -1,103 +1,14 @@
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="/program_info/org.prismlauncher.PrismLauncher.logo-darkmode.svg">
<source media="(prefers-color-scheme: light)" srcset="/program_info/org.prismlauncher.PrismLauncher.logo.svg">
<img alt="Prism Launcher" src="/program_info/org.prismlauncher.PrismLauncher.logo.svg" width="40%">
</picture>
<img src="./program_info/pollymc-header-black.svg#gh-light-mode-only" alt="PollyMC logo" width="60%"/>
<img src="./program_info/pollymc-header.svg#gh-dark-mode-only" alt="PollyMC logo" width="60%"/>
</p>
<p align="center">
Prism Launcher is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.<br />
<br />This is a <b>fork</b> of the MultiMC Launcher and is <b>not</b> endorsed by it.
</p>
PollyMC is a **fork** of Prism Launcher that adds support for Ely.by accounts and allows you to play offline mode without an account
## Installation
PollyMC is not endorsed by or affiliated with the Prism Launcher project.
If you have any problems open an issue here, do not bug the Prism Launcher maintainers.
<a href="https://repology.org/project/prismlauncher/versions">
<img src="https://repology.org/badge/vertical-allrepos/prismlauncher.svg" alt="Packaging status" align="right">
</a>
Binaries can be found in the [releases section](https://github.com/fn2006/PollyMC/releases/latest).
- All downloads and instructions for Prism Launcher can be found on our [Website](https://prismlauncher.org/download).
- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions).
To build this yourself, follow [the instructions on the Prism Launcher website](https://prismlauncher.org/wiki/development/build-instructions) but clone this repo instead.
### Development Builds
There are development builds available [here](https://github.com/PrismLauncher/PrismLauncher/actions). These have debug information in the binaries, so their file sizes are relatively larger.
Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**.
For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**, respectively, you can use these packages for the latest development versions:
[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=COPR&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher)
These packages are also availiable to all the distributions based on the ones mentioned above.
## Community & Support
Feel free to create a GitHub issue if you find a bug or want to suggest a new feature. We have multiple community spaces where other community members can help you:
- **Our Discord server:**
[![Prism Launcher Discord server](https://discordapp.com/api/guilds/1031648380885147709/widget.png?style=banner3)](https://prismlauncher.org/discord)
- **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)
- **Our Subreddit:**
[![r/PrismLauncher](https://img.shields.io/reddit/subreddit-subscribers/prismlauncher?style=for-the-badge&logo=reddit)](https://prismlauncher.org/reddit)
## 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>
## Building
If you want to build Prism Launcher yourself, check the [Build Instructions](https://prismlauncher.org/wiki/development/build-instructions/).
## Sponsors & Partners
We thank all the wonderful backers over at Open Collective! Support Prism Launcher by [becoming a backer](https://opencollective.com/prismlauncher).
[![OpenCollective Backers](https://opencollective.com/prismlauncher/backers.svg?width=890&limit=1000)](https://opencollective.com/prismlauncher#backers)
Thanks to JetBrains for providing us a few licenses for all their products, as part of their [Open Source program](https://www.jetbrains.com/opensource/).
[![JetBrains](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/opensource/)
Thanks to Weblate for hosting our translation efforts.
<a href="https://hosted.weblate.org/engage/prismlauncher/">
<img src="https://hosted.weblate.org/widgets/prismlauncher/-/open-graph.png" alt="Translation status" width="300" />
</a>
Thanks to Netlify for providing us their excellent web services, as part of their [Open Source program](https://www.netlify.com/open-source/).
<a href="https://www.netlify.com"> <img src="https://www.netlify.com/v3/img/components/netlify-color-accent.svg" alt="Deploys by Netlify" /> </a>
Thanks to the awesome people over at [MacStadium](https://www.macstadium.com/), for providing M1-Macs for development purposes!
<a href="https://www.macstadium.com"><img src="https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png" alt="Powered by MacStadium" width="300"></a>
## 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:
- Make it clear that your fork is not PrismLauncher and is not endorsed by or affiliated with the PrismLauncher 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).
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:
- [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)
If you do not agree with these terms and conditions, then remove the associated API keys from the [CMakeLists.txt](CMakeLists.txt) file by setting them to an empty string (`""`).
## License [![https://github.com/PrismLauncher/PrismLauncher/blob/develop/LICENSE](https://img.shields.io/github/license/PrismLauncher/PrismLauncher?label=License&logo=gnu&color=C4282D)](LICENSE)
All launcher code is available under the GPL-3.0-only license.
The logo and related assets are under the CC BY-SA 4.0 license.

View File

@ -2,6 +2,7 @@
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 Lenny McLennington <lenny@sneed.church>
*
* 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
@ -107,6 +108,7 @@ Config::Config()
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
FLAME_API_KEY_API_URL = "@Launcher_CURSEFORGE_API_KEY_API_URL@";
META_URL = "@Launcher_META_URL@";
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";

View File

@ -3,6 +3,7 @@
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 Lenny McLennington <lenny@sneed.church>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
@ -129,6 +130,11 @@ class Config {
*/
QString FLAME_API_KEY;
/**
* URL to fetch the Client API key for CurseForge from
*/
QString FLAME_API_KEY_API_URL;
/**
* Metadata repository URL prefix
*/

View File

@ -1,4 +1,4 @@
id: org.prismlauncher.PrismLauncher
id: org.fn2006.PollyMC
runtime: org.kde.Platform
runtime-version: "5.15-22.08"
sdk: org.kde.Sdk
@ -13,7 +13,7 @@ add-extensions:
autodelete: false
directory: utils/gamescope
command: prismlauncher
command: pollymc
finish-args:
- --share=ipc
- --socket=x11
@ -27,11 +27,11 @@ finish-args:
- --filesystem=xdg-download:ro
modules:
- name: prismlauncher
- name: pollymc
buildsystem: cmake-ninja
config-opts:
- -DLauncher_BUILD_PLATFORM=flatpak
- -DCMAKE_BUILD_TYPE=Debug
- -DCMAKE_BUILD_TYPE=Release
- -DLauncher_QT_VERSION_MAJOR=5
build-options:
env:
@ -76,10 +76,10 @@ modules:
build-commands:
- mkdir -p /app/utils/gamescope
- install -Dm755 prime-run /app/bin/prime-run
- mv /app/bin/prismlauncher /app/bin/prismrun
- install -Dm755 prismlauncher /app/bin/prismlauncher
- mv /app/bin/pollymc /app/bin/pollyrun
- install -Dm755 pollymc /app/bin/pollymc
sources:
- type: file
path: ../flatpak/prime-run
- type: file
path: ../flatpak/prismlauncher
path: ../flatpak/pollymc

View File

@ -8,4 +8,4 @@ done
export PATH="${PATH}${PATH:+:}/app/utils/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin"
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}${LD_LIBRARY_PATH:+:}/usr/lib/extensions/vulkan/MangoHud/\$LIB/"
exec /app/bin/prismrun "$@"
exec /app/bin/pollyrun "$@"

View File

@ -104,6 +104,8 @@
#include "icons/IconList.h"
#include "net/HttpMetaCache.h"
#include "ui/GuiUtil.h"
#include "java/JavaUtils.h"
#include "updater/ExternalUpdater.h"
@ -707,6 +709,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->set("FlameKeyOverride", flameKey);
m_settings->reset("CFKeyOverride");
}
m_settings->registerSetting("FlameKeyShouldBeFetchedOnStartup", true);
m_settings->registerSetting("ModrinthToken", "");
m_settings->registerSetting("UserAgentOverride", "");
@ -836,6 +839,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_metacache->addBase("translations", QDir("translations").absolutePath());
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
m_metacache->addBase("meta", QDir("meta").absolutePath());
m_metacache->addBase("injectors", QDir("injectors").absolutePath());
m_metacache->Load();
qDebug() << "<> Cache initialized.";
}
@ -1019,6 +1023,22 @@ void Application::performMainStartupAction()
return;
}
}
{
bool shouldFetch = m_settings->get("FlameKeyShouldBeFetchedOnStartup").toBool();
if (!BuildConfig.FLAME_API_KEY_API_URL.isEmpty() && shouldFetch && !(capabilities() & Capability::SupportsFlame))
{
// don't ask, just fetch
QString apiKey = GuiUtil::fetchFlameKey();
if (!apiKey.isEmpty())
{
m_settings->set("FlameKeyOverride", apiKey);
updateCapabilities();
}
m_settings->set("FlameKeyShouldBeFetchedOnStartup", false);
}
}
if(!m_mainWindow)
{
// normal main window

View File

@ -118,6 +118,8 @@ set(NET_SOURCES
net/ChecksumValidator.h
net/Download.cpp
net/Download.h
net/FetchFlameAPIKey.cpp
net/FetchFlameAPIKey.h
net/FileSink.cpp
net/FileSink.h
net/HttpMetaCache.cpp
@ -218,11 +220,21 @@ set(MINECRAFT_SOURCES
minecraft/auth/flows/MSA.h
minecraft/auth/flows/Offline.cpp
minecraft/auth/flows/Offline.h
minecraft/auth/flows/Elyby.cpp
minecraft/auth/flows/Elyby.h
minecraft/auth/flows/Custom.cpp
minecraft/auth/flows/Custom.h
minecraft/auth/steps/OfflineStep.cpp
minecraft/auth/steps/OfflineStep.h
minecraft/auth/steps/EntitlementsStep.cpp
minecraft/auth/steps/EntitlementsStep.h
minecraft/auth/steps/ElybyProfileStep.cpp
minecraft/auth/steps/ElybyProfileStep.h
minecraft/auth/steps/ElybyStep.cpp
minecraft/auth/steps/ElybyStep.h
minecraft/auth/steps/CustomProfileStep.cpp
minecraft/auth/steps/CustomProfileStep.h
minecraft/auth/steps/CustomStep.cpp
minecraft/auth/steps/CustomStep.h
minecraft/auth/steps/GetSkinStep.cpp
minecraft/auth/steps/GetSkinStep.h
minecraft/auth/steps/LauncherLoginStep.cpp
@ -235,6 +247,8 @@ set(MINECRAFT_SOURCES
minecraft/auth/steps/MinecraftProfileStepMojang.h
minecraft/auth/steps/MSAStep.cpp
minecraft/auth/steps/MSAStep.h
minecraft/auth/steps/OfflineStep.cpp
minecraft/auth/steps/OfflineStep.h
minecraft/auth/steps/XboxAuthorizationStep.cpp
minecraft/auth/steps/XboxAuthorizationStep.h
minecraft/auth/steps/XboxProfileStep.cpp
@ -270,6 +284,8 @@ set(MINECRAFT_SOURCES
minecraft/launch/LauncherPartLaunch.h
minecraft/launch/MinecraftServerTarget.cpp
minecraft/launch/MinecraftServerTarget.h
minecraft/launch/InjectAuthlib.cpp
minecraft/launch/InjectAuthlib.h
minecraft/launch/PrintInstanceInfo.cpp
minecraft/launch/PrintInstanceInfo.h
minecraft/launch/ReconstructAssets.cpp
@ -529,6 +545,13 @@ set(MODRINTH_SOURCES
modplatform/modrinth/ModrinthPackExportTask.h
)
set(MODPACKSCH_SOURCES
modplatform/modpacksch/FTBPackInstallTask.h
modplatform/modpacksch/FTBPackInstallTask.cpp
modplatform/modpacksch/FTBPackManifest.h
modplatform/modpacksch/FTBPackManifest.cpp
)
set(PACKWIZ_SOURCES
modplatform/packwiz/Packwiz.h
modplatform/packwiz/Packwiz.cpp
@ -658,6 +681,7 @@ set(LOGIC_SOURCES
${FTB_SOURCES}
${FLAME_SOURCES}
${MODRINTH_SOURCES}
${MODPACKSCH_SOURCES}
${PACKWIZ_SOURCES}
${TECHNIC_SOURCES}
${ATLAUNCHER_SOURCES}
@ -859,6 +883,13 @@ SET(LAUNCHER_SOURCES
ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp
ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h
ui/pages/modplatform/ftb/FtbFilterModel.cpp
ui/pages/modplatform/ftb/FtbFilterModel.h
ui/pages/modplatform/ftb/FtbListModel.cpp
ui/pages/modplatform/ftb/FtbListModel.h
ui/pages/modplatform/ftb/FtbPage.cpp
ui/pages/modplatform/ftb/FtbPage.h
ui/pages/modplatform/legacy_ftb/Page.cpp
ui/pages/modplatform/legacy_ftb/Page.h
ui/pages/modplatform/legacy_ftb/ListModel.h
@ -900,10 +931,14 @@ SET(LAUNCHER_SOURCES
ui/dialogs/ProfileSetupDialog.h
ui/dialogs/CopyInstanceDialog.cpp
ui/dialogs/CopyInstanceDialog.h
ui/dialogs/CustomLoginDialog.cpp
ui/dialogs/CustomLoginDialog.h
ui/dialogs/CustomMessageBox.cpp
ui/dialogs/CustomMessageBox.h
ui/dialogs/EditAccountDialog.cpp
ui/dialogs/EditAccountDialog.h
ui/dialogs/ElybyLoginDialog.cpp
ui/dialogs/ElybyLoginDialog.h
ui/dialogs/ExportInstanceDialog.cpp
ui/dialogs/ExportInstanceDialog.h
ui/dialogs/ExportMrPackDialog.cpp
@ -1037,6 +1072,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/modplatform/flame/FlamePage.ui
ui/pages/modplatform/legacy_ftb/Page.ui
ui/pages/modplatform/ImportPage.ui
ui/pages/modplatform/ftb/FtbPage.ui
ui/pages/modplatform/modrinth/ModrinthPage.ui
ui/pages/modplatform/technic/TechnicPage.ui
ui/widgets/InstanceCardWidget.ui
@ -1059,6 +1095,8 @@ qt_wrap_ui(LAUNCHER_UI
ui/dialogs/ImportResourceDialog.ui
ui/dialogs/MSALoginDialog.ui
ui/dialogs/OfflineLoginDialog.ui
ui/dialogs/ElybyLoginDialog.ui
ui/dialogs/CustomLoginDialog.ui
ui/dialogs/AboutDialog.ui
ui/dialogs/LoginDialog.ui
ui/dialogs/EditAccountDialog.ui

View File

@ -390,6 +390,13 @@ QStringList MinecraftInstance::extraArguments()
agent->library()->getApplicableFiles(runtimeContext(), jar, temp1, temp2, temp3, getLocalLibraryPath());
list.append("-javaagent:"+jar[0]+(agent->argument().isEmpty() ? "" : "="+agent->argument()));
}
// TODO: figure out how polymc's javaagent system works and use it instead of this hack
if (m_injector) {
list.append("-javaagent:"+m_injector->javaArg);
list.append("-Dauthlibinjector.noShowServerName");
}
return list;
}
@ -1021,7 +1028,17 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
if(!session->demo) {
process->appendStep(makeShared<ClaimAccount>(pptr, session));
}
// authlib patch
if (session->user_type == "elyby")
{
process->appendStep(makeShared<InjectAuthlib>(pptr, &m_injector, "ely.by"));
}
else if (session->user_type == "custom")
{
process->appendStep(makeShared<InjectAuthlib>(pptr, &m_injector, session->url));
}
process->appendStep(makeShared<Update>(pptr, Net::Mode::Online));
}
else
{

View File

@ -41,6 +41,7 @@
#include <QProcess>
#include <QDir>
#include "minecraft/launch/MinecraftServerTarget.h"
#include "minecraft/launch/InjectAuthlib.h"
class ModFolderModel;
class ResourceFolderModel;
@ -178,6 +179,7 @@ protected: // data
mutable std::shared_ptr<TexturePackFolderModel> m_texture_pack_list;
mutable std::shared_ptr<WorldList> m_world_list;
mutable std::shared_ptr<GameOptions> m_game_options;
mutable std::shared_ptr<AuthlibInjector> m_injector;
};
typedef std::shared_ptr<MinecraftInstance> MinecraftInstancePtr;

View File

@ -352,6 +352,10 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
type = AccountType::Mojang;
} else if (typeS == "Offline") {
type = AccountType::Offline;
} else if (typeS == "Elyby") {
type = AccountType::Elyby;
} else if (typeS == "Custom") {
type = AccountType::Custom;
} else {
qWarning() << "Failed to parse account data: type is not recognized.";
return false;
@ -373,6 +377,13 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
mojangservicesToken = tokenFromJSONV3(data, "xrp-mc");
}
if(type == AccountType::Custom) {
auto urlV = data.value("url");
if (urlV.isString()) {
customUrl = urlV.toString();
}
}
yggdrasilToken = tokenFromJSONV3(data, "ygg");
minecraftProfile = profileFromJSONV3(data, "profile");
if(!entitlementFromJSONV3(data, minecraftEntitlement)) {
@ -409,6 +420,13 @@ QJsonObject AccountData::saveState() const {
else if (type == AccountType::Offline) {
output["type"] = "Offline";
}
else if (type == AccountType::Elyby) {
output["type"] = "Elyby";
}
else if (type == AccountType::Custom) {
output["type"] = "Custom";
output["url"] = customUrl;
}
tokenToJSONV3(output, yggdrasilToken, "ygg");
profileToJSONV3(output, minecraftProfile, "profile");
@ -428,14 +446,14 @@ QString AccountData::accessToken() const {
}
QString AccountData::clientToken() const {
if(type != AccountType::Mojang) {
if(type != AccountType::Mojang && type != AccountType::Elyby && type != AccountType::Custom) {
return QString();
}
return yggdrasilToken.extra["clientToken"].toString();
}
void AccountData::setClientToken(QString clientToken) {
if(type != AccountType::Mojang) {
if(type != AccountType::Mojang && type != AccountType::Elyby && type != AccountType::Custom) {
return;
}
yggdrasilToken.extra["clientToken"] = clientToken;
@ -449,7 +467,7 @@ void AccountData::generateClientTokenIfMissing() {
}
void AccountData::invalidateClientToken() {
if(type != AccountType::Mojang) {
if(type != AccountType::Mojang && type != AccountType::Elyby && type != AccountType::Custom) {
return;
}
yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{-}]"));
@ -470,6 +488,8 @@ QString AccountData::profileName() const {
QString AccountData::accountDisplayString() const {
switch(type) {
case AccountType::Custom:
case AccountType::Elyby:
case AccountType::Mojang: {
return userName();
}

View File

@ -74,7 +74,9 @@ struct MinecraftProfile {
enum class AccountType {
MSA,
Mojang,
Offline
Offline,
Elyby,
Custom
};
enum class AccountState {
@ -113,6 +115,8 @@ struct AccountData {
QString lastError() const;
QString customUrl;
AccountType type = AccountType::MSA;
bool legacy = false;
bool canMigrateToMSA = false;

View File

@ -332,7 +332,7 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
}
case MigrationColumn: {
if(account->isMSA() || account->isOffline()) {
if(!account->isMojang()) {
return tr("N/A", "Can Migrate?");
}
if (account->canMigrate()) {

View File

@ -40,6 +40,8 @@ struct AuthSession
QString uuid;
// 'legacy' or 'mojang', depending on account type
QString user_type;
// Yggdrasil server url
QString url;
// Did the auth server reply?
bool auth_server_online = false;
// Did the user request online mode?

View File

@ -51,6 +51,8 @@
#include "flows/MSA.h"
#include "flows/Mojang.h"
#include "flows/Offline.h"
#include "flows/Elyby.h"
#include "flows/Custom.h"
MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) {
data.internalId = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
@ -106,6 +108,29 @@ MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username)
return account;
}
MinecraftAccountPtr MinecraftAccount::createElyby(const QString &username)
{
MinecraftAccountPtr account = makeShared<MinecraftAccount>();
account->data.type = AccountType::Elyby;
account->data.yggdrasilToken.extra["userName"] = username;
account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
account->data.minecraftEntitlement.ownsMinecraft = true;
account->data.minecraftEntitlement.canPlayMinecraft = true;
return account;
}
MinecraftAccountPtr MinecraftAccount::createCustom(const QString &username, const QString &url)
{
MinecraftAccountPtr account = makeShared<MinecraftAccount>();
account->data.type = AccountType::Custom;
account->data.yggdrasilToken.extra["userName"] = username;
account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
account->data.minecraftEntitlement.ownsMinecraft = true;
account->data.minecraftEntitlement.canPlayMinecraft = true;
account->data.customUrl = url;
return account;
}
QJsonObject MinecraftAccount::saveToJson() const
{
@ -162,6 +187,28 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::loginOffline() {
return m_currentTask;
}
shared_qobject_ptr<AccountTask> MinecraftAccount::loginElyby(QString password) {
Q_ASSERT(m_currentTask.get() == nullptr);
m_currentTask.reset(new ElybyLogin(&data, password));
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); });
emit activityChanged(true);
return m_currentTask;
}
shared_qobject_ptr<AccountTask> MinecraftAccount::loginCustom(QString password, QString url) {
Q_ASSERT(m_currentTask.get() == nullptr);
m_currentTask.reset(new CustomLogin(&data, password, url));
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); });
emit activityChanged(true);
return m_currentTask;
}
shared_qobject_ptr<AccountTask> MinecraftAccount::refresh() {
if(m_currentTask) {
return m_currentTask;
@ -173,6 +220,12 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::refresh() {
else if(data.type == AccountType::Offline) {
m_currentTask.reset(new OfflineRefresh(&data));
}
else if(data.type == AccountType::Elyby) {
m_currentTask.reset(new ElybyRefresh(&data));
}
else if (data.type == AccountType::Custom) {
m_currentTask.reset(new CustomRefresh(&data));
}
else {
m_currentTask.reset(new MojangRefresh(&data));
}
@ -302,6 +355,8 @@ void MinecraftAccount::fillSession(AuthSessionPtr session)
session->uuid = data.profileId();
// 'legacy' or 'mojang', depending on account type
session->user_type = typeString();
session->url = data.customUrl;
if (!session->access_token.isEmpty())
{
session->session = "token:" + data.accessToken() + ":" + data.profileId();

View File

@ -95,6 +95,10 @@ public: /* construction */
static MinecraftAccountPtr createOffline(const QString &username);
static MinecraftAccountPtr createElyby(const QString &username);
static MinecraftAccountPtr createCustom(const QString &username, const QString &url);
static MinecraftAccountPtr loadFromJsonV2(const QJsonObject &json);
static MinecraftAccountPtr loadFromJsonV3(const QJsonObject &json);
@ -113,6 +117,10 @@ public: /* manipulation */
shared_qobject_ptr<AccountTask> loginOffline();
shared_qobject_ptr<AccountTask> loginElyby(QString password);
shared_qobject_ptr<AccountTask> loginCustom(QString password, QString url);
shared_qobject_ptr<AccountTask> refresh();
shared_qobject_ptr<AccountTask> currentTask();
@ -142,6 +150,10 @@ public: /* queries */
return data.profileName();
}
QString customUrl() const {
return data.customUrl;
}
bool isActive() const;
bool canMigrate() const {
@ -152,10 +164,22 @@ public: /* queries */
return data.type == AccountType::MSA;
}
bool isMojang() const {
return data.type == AccountType::Mojang;
}
bool isOffline() const {
return data.type == AccountType::Offline;
}
bool isElyby() const {
return data.type == AccountType::Elyby;
}
bool isCustom() const {
return data.type == AccountType::Custom;
}
bool ownsMinecraft() const {
return data.minecraftEntitlement.ownsMinecraft;
}
@ -181,6 +205,14 @@ public: /* queries */
return "offline";
}
break;
case AccountType::Elyby: {
return "elyby";
}
break;
case AccountType::Custom: {
return "custom";
}
break;
default: {
return "unknown";
}

View File

@ -55,7 +55,7 @@ void Yggdrasil::sendRequest(QUrl endpoint, QByteArray content) {
void Yggdrasil::executeTask() {
}
void Yggdrasil::refresh() {
void Yggdrasil::refresh(QString baseUrl) {
start();
/*
* {
@ -84,13 +84,13 @@ void Yggdrasil::refresh() {
req.insert("requestUser", false);
QJsonDocument doc(req);
QUrl reqUrl("https://authserver.mojang.com/refresh");
QUrl reqUrl(baseUrl + "refresh");
QByteArray requestData = doc.toJson();
sendRequest(reqUrl, requestData);
}
void Yggdrasil::login(QString password) {
void Yggdrasil::login(QString password, QString baseUrl) {
start();
/*
* {
@ -129,7 +129,9 @@ void Yggdrasil::login(QString password) {
QJsonDocument doc(req);
QUrl reqUrl("https://authserver.mojang.com/authenticate");
this->customUrl = baseUrl;
QUrl reqUrl(baseUrl + "authenticate");
QNetworkRequest netRequest(reqUrl);
QByteArray requestData = doc.toJson();

View File

@ -40,8 +40,8 @@ public:
);
virtual ~Yggdrasil() = default;
void refresh();
void login(QString password);
void refresh(QString baseUrl);
void login(QString password, QString baseUrl);
struct Error
{
@ -97,6 +97,8 @@ protected:
QTimer counter;
int count = 0; // num msec since time reset
QString customUrl;
const int timeout_max = 30000;
const int time_step = 50;
};

View File

@ -0,0 +1,27 @@
#include "Custom.h"
#include "minecraft/auth/steps/CustomStep.h"
#include "minecraft/auth/steps/CustomProfileStep.h"
#include "minecraft/auth/steps/GetSkinStep.h"
CustomRefresh::CustomRefresh(
AccountData *data,
QObject *parent
) : AuthFlow(data, parent) {
m_steps.append(makeShared<CustomStep>(m_data, QString()));
m_steps.append(makeShared<CustomProfileStep>(m_data));
m_steps.append(makeShared<GetSkinStep>(m_data));
}
CustomLogin::CustomLogin(
AccountData *data,
QString password,
QString url,
QObject *parent
): AuthFlow(data, parent), m_password(password) {
auto step = makeShared<CustomStep>(m_data, m_password);
step.get()->setUrl(url);
m_steps.append(step);
m_steps.append(makeShared<CustomProfileStep>(m_data));
m_steps.append(makeShared<GetSkinStep>(m_data));
}

View File

@ -0,0 +1,27 @@
#pragma once
#include "AuthFlow.h"
class CustomRefresh : public AuthFlow
{
Q_OBJECT
public:
explicit CustomRefresh(
AccountData *data,
QObject *parent = 0
);
};
class CustomLogin : public AuthFlow
{
Q_OBJECT
public:
explicit CustomLogin(
AccountData *data,
QString password,
QString url,
QObject *parent = 0
);
private:
QString m_password;
};

View File

@ -0,0 +1,24 @@
#include "Elyby.h"
#include "minecraft/auth/steps/ElybyStep.h"
#include "minecraft/auth/steps/ElybyProfileStep.h"
#include "minecraft/auth/steps/GetSkinStep.h"
ElybyRefresh::ElybyRefresh(
AccountData *data,
QObject *parent
) : AuthFlow(data, parent) {
m_steps.append(makeShared<ElybyStep>(m_data, QString()));
m_steps.append(makeShared<ElybyProfileStep>(m_data));
m_steps.append(makeShared<GetSkinStep>(m_data));
}
ElybyLogin::ElybyLogin(
AccountData *data,
QString password,
QObject *parent
): AuthFlow(data, parent), m_password(password) {
m_steps.append(makeShared<ElybyStep>(m_data, m_password));
m_steps.append(makeShared<ElybyProfileStep>(m_data));
m_steps.append(makeShared<GetSkinStep>(m_data));
}

View File

@ -0,0 +1,26 @@
#pragma once
#include "AuthFlow.h"
class ElybyRefresh : public AuthFlow
{
Q_OBJECT
public:
explicit ElybyRefresh(
AccountData *data,
QObject *parent = 0
);
};
class ElybyLogin : public AuthFlow
{
Q_OBJECT
public:
explicit ElybyLogin(
AccountData *data,
QString password,
QObject *parent = 0
);
private:
QString m_password;
};

View File

@ -0,0 +1,96 @@
#include "CustomProfileStep.h"
#include <QNetworkRequest>
#include "minecraft/auth/AuthRequest.h"
#include "minecraft/auth/Parsers.h"
#include "net/NetUtils.h"
CustomProfileStep::CustomProfileStep(AccountData* data) : AuthStep(data) {
}
CustomProfileStep::~CustomProfileStep() noexcept = default;
QString CustomProfileStep::describe() {
return tr("Fetching the Minecraft profile.");
}
void CustomProfileStep::perform() {
if (m_data->minecraftProfile.id.isEmpty()) {
emit finished(AccountTaskState::STATE_FAILED_HARD, tr("A UUID is required to get the profile."));
return;
}
// m_data->
qWarning() << "Url: " << m_data->customUrl;
QUrl url = QUrl(m_data->customUrl + "/session/profile/" + m_data->minecraftProfile.id);
QNetworkRequest req = QNetworkRequest(url);
AuthRequest *request = new AuthRequest(this);
connect(request, &AuthRequest::finished, this, &CustomProfileStep::onRequestDone);
request->get(req);
}
void CustomProfileStep::rehydrate() {
// NOOP, for now. We only save bools and there's nothing to check.
}
void CustomProfileStep::onRequestDone(
QNetworkReply::NetworkError error,
QByteArray data,
QList<QNetworkReply::RawHeaderPair> headers
) {
auto requestor = qobject_cast<AuthRequest *>(QObject::sender());
requestor->deleteLater();
#ifndef NDEBUG
qDebug() << data;
#endif
if (error == QNetworkReply::ContentNotFoundError) {
// NOTE: Succeed even if we do not have a profile. This is a valid account state.
m_data->minecraftProfile = MinecraftProfile();
emit finished(
AccountTaskState::STATE_SUCCEEDED,
tr("Account has no Minecraft profile.")
);
return;
}
if (error != QNetworkReply::NoError) {
qWarning() << "Error getting profile:";
qWarning() << " HTTP Status: " << requestor->httpStatus_;
qWarning() << " Internal error no.: " << error;
qWarning() << " Error string: " << requestor->errorString_;
qWarning() << " Response:";
qWarning() << QString::fromUtf8(data);
if (Net::isApplicationError(error)) {
emit finished(
AccountTaskState::STATE_FAILED_SOFT,
tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)
);
}
else {
emit finished(
AccountTaskState::STATE_OFFLINE,
tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)
);
}
return;
}
if(!Parsers::parseMinecraftProfileMojang(data, m_data->minecraftProfile)) {
m_data->minecraftProfile = MinecraftProfile();
emit finished(
AccountTaskState::STATE_FAILED_SOFT,
tr("Minecraft Java profile response could not be parsed")
);
return;
}
emit finished(
AccountTaskState::STATE_WORKING,
tr("Minecraft Java profile acquisition succeeded.")
);
}

View File

@ -0,0 +1,22 @@
#pragma once
#include <QObject>
#include "QObjectPtr.h"
#include "minecraft/auth/AuthStep.h"
class CustomProfileStep : public AuthStep {
Q_OBJECT
public:
explicit CustomProfileStep(AccountData *data);
virtual ~CustomProfileStep() noexcept;
void perform() override;
void rehydrate() override;
QString describe() override;
private slots:
void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
};

View File

@ -0,0 +1,57 @@
#include "CustomStep.h"
#include "minecraft/auth/AuthRequest.h"
#include "minecraft/auth/Parsers.h"
#include "minecraft/auth/Yggdrasil.h"
CustomStep::CustomStep(AccountData* data, QString password) : AuthStep(data), m_password(password) {
m_yggdrasil = new Yggdrasil(m_data, this);
connect(m_yggdrasil, &Task::failed, this, &CustomStep::onAuthFailed);
connect(m_yggdrasil, &Task::succeeded, this, &CustomStep::onAuthSucceeded);
connect(m_yggdrasil, &Task::aborted, this, &CustomStep::onAuthFailed);
}
CustomStep::~CustomStep() noexcept = default;
QString CustomStep::describe() {
return tr("Logging in with Custom account.");
}
void CustomStep::rehydrate() {
// NOOP, for now.
}
void CustomStep::perform() {
qWarning() << "url: " << m_url << " / " << m_data->customUrl;
if(m_password.size()) {
m_yggdrasil->login(m_password, m_url + "/auth/");
}
else {
m_yggdrasil->refresh(m_data->customUrl + "/auth/");
}
}
void CustomStep::setUrl(QString url) {
this->m_url = url;
}
void CustomStep::onAuthSucceeded() {
emit finished(AccountTaskState::STATE_WORKING, tr("Logged in with Custom"));
}
void CustomStep::onAuthFailed() {
// TODO: hook these in again, expand to MSA
// m_error = m_yggdrasil->m_error;
// m_aborted = m_yggdrasil->m_aborted;
auto state = m_yggdrasil->taskState();
QString errorMessage = tr("Custom user authentication failed.");
// NOTE: soft error in the first step means 'offline'
if(state == AccountTaskState::STATE_FAILED_SOFT) {
state = AccountTaskState::STATE_OFFLINE;
errorMessage = tr("Custom user authentication ended with a network error. Is MutliFactor Auth correct?");
}
emit finished(state, errorMessage);
}

View File

@ -0,0 +1,31 @@
#pragma once
#include <QObject>
#include "QObjectPtr.h"
#include "minecraft/auth/AuthStep.h"
class Yggdrasil;
class CustomStep : public AuthStep {
Q_OBJECT
public:
explicit CustomStep(AccountData *data, QString password);
virtual ~CustomStep() noexcept;
void perform() override;
void rehydrate() override;
QString describe() override;
void setUrl(QString url);
private slots:
void onAuthSucceeded();
void onAuthFailed();
private:
Yggdrasil *m_yggdrasil = nullptr;
QString m_password;
QString m_url;
};

View File

@ -0,0 +1,93 @@
#include "ElybyProfileStep.h"
#include <QNetworkRequest>
#include "minecraft/auth/AuthRequest.h"
#include "minecraft/auth/Parsers.h"
#include "net/NetUtils.h"
ElybyProfileStep::ElybyProfileStep(AccountData* data) : AuthStep(data) {
}
ElybyProfileStep::~ElybyProfileStep() noexcept = default;
QString ElybyProfileStep::describe() {
return tr("Fetching the Minecraft profile.");
}
void ElybyProfileStep::perform() {
if (m_data->minecraftProfile.id.isEmpty()) {
emit finished(AccountTaskState::STATE_FAILED_HARD, tr("A UUID is required to get the profile."));
return;
}
QUrl url = QUrl("https://authserver.ely.by/session/profile/" + m_data->minecraftProfile.id);
QNetworkRequest req = QNetworkRequest(url);
AuthRequest *request = new AuthRequest(this);
connect(request, &AuthRequest::finished, this, &ElybyProfileStep::onRequestDone);
request->get(req);
}
void ElybyProfileStep::rehydrate() {
// NOOP, for now. We only save bools and there's nothing to check.
}
void ElybyProfileStep::onRequestDone(
QNetworkReply::NetworkError error,
QByteArray data,
QList<QNetworkReply::RawHeaderPair> headers
) {
auto requestor = qobject_cast<AuthRequest *>(QObject::sender());
requestor->deleteLater();
#ifndef NDEBUG
qDebug() << data;
#endif
if (error == QNetworkReply::ContentNotFoundError) {
// NOTE: Succeed even if we do not have a profile. This is a valid account state.
m_data->minecraftProfile = MinecraftProfile();
emit finished(
AccountTaskState::STATE_SUCCEEDED,
tr("Account has no Minecraft profile.")
);
return;
}
if (error != QNetworkReply::NoError) {
qWarning() << "Error getting profile:";
qWarning() << " HTTP Status: " << requestor->httpStatus_;
qWarning() << " Internal error no.: " << error;
qWarning() << " Error string: " << requestor->errorString_;
qWarning() << " Response:";
qWarning() << QString::fromUtf8(data);
if (Net::isApplicationError(error)) {
emit finished(
AccountTaskState::STATE_FAILED_SOFT,
tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)
);
}
else {
emit finished(
AccountTaskState::STATE_OFFLINE,
tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)
);
}
return;
}
if(!Parsers::parseMinecraftProfileMojang(data, m_data->minecraftProfile)) {
m_data->minecraftProfile = MinecraftProfile();
emit finished(
AccountTaskState::STATE_FAILED_SOFT,
tr("Minecraft Java profile response could not be parsed")
);
return;
}
emit finished(
AccountTaskState::STATE_WORKING,
tr("Minecraft Java profile acquisition succeeded.")
);
}

View File

@ -0,0 +1,22 @@
#pragma once
#include <QObject>
#include "QObjectPtr.h"
#include "minecraft/auth/AuthStep.h"
class ElybyProfileStep : public AuthStep {
Q_OBJECT
public:
explicit ElybyProfileStep(AccountData *data);
virtual ~ElybyProfileStep() noexcept;
void perform() override;
void rehydrate() override;
QString describe() override;
private slots:
void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
};

View File

@ -0,0 +1,52 @@
#include "ElybyStep.h"
#include "minecraft/auth/AuthRequest.h"
#include "minecraft/auth/Parsers.h"
#include "minecraft/auth/Yggdrasil.h"
ElybyStep::ElybyStep(AccountData* data, QString password) : AuthStep(data), m_password(password) {
m_yggdrasil = new Yggdrasil(m_data, this);
connect(m_yggdrasil, &Task::failed, this, &ElybyStep::onAuthFailed);
connect(m_yggdrasil, &Task::succeeded, this, &ElybyStep::onAuthSucceeded);
connect(m_yggdrasil, &Task::aborted, this, &ElybyStep::onAuthFailed);
}
ElybyStep::~ElybyStep() noexcept = default;
QString ElybyStep::describe() {
return tr("Logging in with Ely.by account.");
}
void ElybyStep::rehydrate() {
// NOOP, for now.
}
void ElybyStep::perform() {
if(m_password.size()) {
m_yggdrasil->login(m_password, "https://authserver.ely.by/auth/");
}
else {
m_yggdrasil->refresh("https://authserver.ely.by/auth/");
}
}
void ElybyStep::onAuthSucceeded() {
emit finished(AccountTaskState::STATE_WORKING, tr("Logged in with Ely.by"));
}
void ElybyStep::onAuthFailed() {
// TODO: hook these in again, expand to MSA
// m_error = m_yggdrasil->m_error;
// m_aborted = m_yggdrasil->m_aborted;
auto state = m_yggdrasil->taskState();
QString errorMessage = tr("Ely.by user authentication failed.");
// NOTE: soft error in the first step means 'offline'
if(state == AccountTaskState::STATE_FAILED_SOFT) {
state = AccountTaskState::STATE_OFFLINE;
errorMessage = tr("Ely.by user authentication ended with a network error.\nIf you are using 2FA make sure to enter your auth code.");
}
emit finished(state, errorMessage);
}

View File

@ -0,0 +1,28 @@
#pragma once
#include <QObject>
#include "QObjectPtr.h"
#include "minecraft/auth/AuthStep.h"
class Yggdrasil;
class ElybyStep : public AuthStep {
Q_OBJECT
public:
explicit ElybyStep(AccountData *data, QString password);
virtual ~ElybyStep() noexcept;
void perform() override;
void rehydrate() override;
QString describe() override;
private slots:
void onAuthSucceeded();
void onAuthFailed();
private:
Yggdrasil *m_yggdrasil = nullptr;
QString m_password;
};

View File

@ -24,10 +24,10 @@ void YggdrasilStep::rehydrate() {
void YggdrasilStep::perform() {
if(m_password.size()) {
m_yggdrasil->login(m_password);
m_yggdrasil->login(m_password, "https://authserver.mojang.com/");
}
else {
m_yggdrasil->refresh();
m_yggdrasil->refresh("https://authserver.mojang.com/");
}
}

View File

@ -0,0 +1,175 @@
/* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "InjectAuthlib.h"
#include <launch/LaunchTask.h>
#include <minecraft/MinecraftInstance.h>
#include <FileSystem.h>
#include <Application.h>
#include <Json.h>
InjectAuthlib::InjectAuthlib(LaunchTask *parent, AuthlibInjectorPtr* injector, QString url) : LaunchStep(parent)
{
m_injector = injector;
m_url = url;
}
void InjectAuthlib::executeTask()
{
if (m_aborted)
{
emitFailed(tr("Task aborted."));
return;
}
auto latestVersionInfo = QString("https://authlib-injector.yushi.moe/artifact/latest.json");
auto netJob = makeShared<NetJob>("Injector versions info download", APPLICATION->network());
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("injectors", "version.json");
if (!m_offlineMode)
{
entry->setStale(true);
auto task = Net::Download::makeCached(QUrl(latestVersionInfo), entry);
netJob->addNetAction(task);
jobPtr.reset(netJob);
QObject::connect(netJob.get(), &NetJob::succeeded, this, &InjectAuthlib::onVersionDownloadSucceeded);
QObject::connect(netJob.get(), &NetJob::failed, this, &InjectAuthlib::onDownloadFailed);
jobPtr->start();
}
else
{
onVersionDownloadSucceeded();
}
}
void InjectAuthlib::onVersionDownloadSucceeded()
{
QByteArray data;
try
{
data = FS::read(QDir("injectors").absoluteFilePath("version.json"));
}
catch (const Exception &e)
{
qCritical() << "Translations Download Failed: index file not readable";
jobPtr.reset();
emitFailed("Error while parsing JSON response from InjectorEndpoint");
return;
}
QJsonParseError parse_error;
QJsonDocument doc = QJsonDocument::fromJson(data, &parse_error);
if (parse_error.error != QJsonParseError::NoError)
{
qCritical() << "Error while parsing JSON response from InjectorEndpoint at " << parse_error.offset << " reason: " << parse_error.errorString();
qCritical() << data;
jobPtr.reset();
emitFailed("Error while parsing JSON response from InjectorEndpoint");
return;
}
if (!doc.isObject())
{
qCritical() << "Error while parsing JSON response from InjectorEndpoint root is not object";
qCritical() << data;
jobPtr.reset();
emitFailed("Error while parsing JSON response from InjectorEndpoint");
return;
}
QString downloadUrl;
try
{
downloadUrl = Json::requireString(doc.object(), "download_url");
}
catch (const JSONValidationError &e)
{
qCritical() << "Error while parsing JSON response from InjectorEndpoint download url is not string";
qCritical() << e.cause();
qCritical() << data;
jobPtr.reset();
emitFailed("Error while parsing JSON response from InjectorEndpoint");
return;
}
QFileInfo fi(downloadUrl);
m_versionName = fi.fileName();
qDebug() << "Authlib injector version:" << m_versionName;
if (!m_offlineMode)
{
auto netJob = makeShared<NetJob>("Injector download", APPLICATION->network());
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("injectors", m_versionName);
entry->setStale(true);
auto task = Net::Download::makeCached(QUrl(downloadUrl), entry);
netJob->addNetAction(task);
jobPtr.reset(netJob);
QObject::connect(netJob.get(), &NetJob::succeeded, this, &InjectAuthlib::onDownloadSucceeded);
QObject::connect(netJob.get(), &NetJob::failed, this, &InjectAuthlib::onDownloadFailed);
jobPtr->start();
}
else
{
onDownloadSucceeded();
}
}
void InjectAuthlib::onDownloadSucceeded()
{
QString injector = QString("%1=%2").arg(QDir("injectors").absoluteFilePath(m_versionName)).arg(m_url);
qDebug()
<< "Injecting " << injector;
auto inj = new AuthlibInjector(injector);
m_injector->reset(inj);
jobPtr.reset();
emitSucceeded();
}
void InjectAuthlib::onDownloadFailed(QString reason)
{
jobPtr.reset();
emitFailed(reason);
}
void InjectAuthlib::proceed()
{
}
bool InjectAuthlib::canAbort() const
{
if (jobPtr)
{
return jobPtr->canAbort();
}
return true;
}
bool InjectAuthlib::abort()
{
m_aborted = true;
if (jobPtr)
{
if (jobPtr->canAbort())
{
return jobPtr->abort();
}
}
return true;
}

View File

@ -0,0 +1,76 @@
/* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <launch/LaunchStep.h>
#include <QObjectPtr.h>
#include <LoggedProcess.h>
#include <java/JavaChecker.h>
#include <net/Mode.h>
#include <net/NetJob.h>
struct AuthlibInjector
{
QString javaArg;
AuthlibInjector(const QString arg)
{
javaArg = std::move(arg);
qDebug() << "NEW INJECTOR" << javaArg;
}
};
typedef std::shared_ptr<AuthlibInjector> AuthlibInjectorPtr;
// FIXME: stupid. should be defined by the instance type? or even completely abstracted away...
class InjectAuthlib : public LaunchStep
{
Q_OBJECT
public:
InjectAuthlib(LaunchTask *parent, AuthlibInjectorPtr *injector, QString url);
virtual ~InjectAuthlib(){};
void executeTask() override;
bool canAbort() const override;
void proceed() override;
void setAuthServer(QString server)
{
m_authServer = server;
};
void setOfflineMode(bool offline) {
m_offlineMode = offline;
}
public slots:
bool abort() override;
private slots:
void onVersionDownloadSucceeded();
void onDownloadSucceeded();
void onDownloadFailed(QString reason);
private:
shared_qobject_ptr<NetJob> jobPtr;
bool m_aborted = false;
bool m_offlineMode;
QString m_versionName;
QString m_authServer;
QString m_url;
AuthlibInjectorPtr *m_injector;
};

View File

@ -0,0 +1,387 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 flowln <flowlnlnln@gmail.com>
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright 2020-2021 Petr Mrazek <peterix@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "FTBPackInstallTask.h"
#include "FileSystem.h"
#include "Json.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
#include "modplatform/flame/PackManifest.h"
#include "net/ChecksumValidator.h"
#include "settings/INISettingsObject.h"
#include "Application.h"
#include "BuildConfig.h"
#include "ui/dialogs/BlockedModsDialog.h"
namespace ModpacksCH {
PackInstallTask::PackInstallTask(Modpack pack, QString version, QWidget* parent)
: m_pack(std::move(pack)), m_version_name(std::move(version)), m_parent(parent)
{}
bool PackInstallTask::abort()
{
if (!canAbort())
return false;
bool aborted = true;
if (m_net_job)
aborted &= m_net_job->abort();
if (m_mod_id_resolver_task)
aborted &= m_mod_id_resolver_task->abort();
return aborted ? InstanceTask::abort() : false;
}
void PackInstallTask::executeTask()
{
setStatus(tr("Getting the manifest..."));
setAbortable(false);
// Find pack version
auto version_it = std::find_if(m_pack.versions.constBegin(), m_pack.versions.constEnd(),
[this](ModpacksCH::VersionInfo const& a) { return a.name == m_version_name; });
if (version_it == m_pack.versions.constEnd()) {
emitFailed(tr("Failed to find pack version %1").arg(m_version_name));
return;
}
auto version = *version_it;
auto netJob = makeShared<NetJob>("ModpacksCH::VersionFetch", APPLICATION->network());
auto searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/%1/%2").arg(m_pack.id).arg(version.id);
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &m_response));
QObject::connect(netJob.get(), &NetJob::succeeded, this, &PackInstallTask::onManifestDownloadSucceeded);
QObject::connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onManifestDownloadFailed);
QObject::connect(netJob.get(), &NetJob::aborted, this, &PackInstallTask::abort);
QObject::connect(netJob.get(), &NetJob::progress, this, &PackInstallTask::setProgress);
m_net_job = netJob;
setAbortable(true);
netJob->start();
}
void PackInstallTask::onManifestDownloadSucceeded()
{
m_net_job.reset();
QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(m_response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
qWarning() << "Error while parsing JSON response from ModpacksCH at " << parse_error.offset
<< " reason: " << parse_error.errorString();
qWarning() << m_response;
return;
}
ModpacksCH::Version version;
try {
auto obj = Json::requireObject(doc);
ModpacksCH::loadVersion(version, obj);
} catch (const JSONValidationError& e) {
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
return;
}
m_version = version;
resolveMods();
}
void PackInstallTask::resolveMods()
{
setStatus(tr("Resolving mods..."));
setAbortable(false);
setProgress(0, 100);
m_file_id_map.clear();
Flame::Manifest manifest;
int index = 0;
for (auto const& file : m_version.files) {
if (!file.serverOnly && file.url.isEmpty()) {
if (file.curseforge.file_id <= 0) {
emitFailed(tr("Invalid manifest: There's no information available to download the file '%1'!").arg(file.name));
return;
}
Flame::File flame_file;
flame_file.projectId = file.curseforge.project_id;
flame_file.fileId = file.curseforge.file_id;
flame_file.hash = file.sha1;
manifest.files.insert(flame_file.fileId, flame_file);
m_file_id_map.append(flame_file.fileId);
} else {
m_file_id_map.append(-1);
}
index++;
}
m_mod_id_resolver_task.reset(new Flame::FileResolvingTask(APPLICATION->network(), manifest));
connect(m_mod_id_resolver_task.get(), &Flame::FileResolvingTask::succeeded, this, &PackInstallTask::onResolveModsSucceeded);
connect(m_mod_id_resolver_task.get(), &Flame::FileResolvingTask::failed, this, &PackInstallTask::onResolveModsFailed);
connect(m_mod_id_resolver_task.get(), &Flame::FileResolvingTask::aborted, this, &PackInstallTask::abort);
connect(m_mod_id_resolver_task.get(), &Flame::FileResolvingTask::progress, this, &PackInstallTask::setProgress);
setAbortable(true);
m_mod_id_resolver_task->start();
}
void PackInstallTask::onResolveModsSucceeded()
{
auto anyBlocked = false;
Flame::Manifest results = m_mod_id_resolver_task->getResults();
for (int index = 0; index < m_file_id_map.size(); index++) {
auto const file_id = m_file_id_map.at(index);
if (file_id < 0)
continue;
Flame::File results_file = results.files[file_id];
VersionFile& local_file = m_version.files[index];
// First check for blocked mods
if (!results_file.resolved || results_file.url.isEmpty()) {
BlockedMod blocked_mod;
blocked_mod.name = local_file.name;
blocked_mod.websiteUrl = results_file.websiteUrl;
blocked_mod.hash = results_file.hash;
blocked_mod.matched = false;
blocked_mod.localPath = "";
blocked_mod.targetFolder = results_file.targetFolder;
m_blocked_mods.append(blocked_mod);
anyBlocked = true;
} else {
local_file.url = results_file.url.toString();
}
}
m_mod_id_resolver_task.reset();
if (anyBlocked) {
qDebug() << "Blocked files found, displaying file list";
BlockedModsDialog message_dialog(m_parent, tr("Blocked files found"),
tr("The following files are not available for download in third party launchers.<br/>"
"You will need to manually download them and add them to the instance."),
m_blocked_mods);
message_dialog.setModal(true);
if (message_dialog.exec() == QDialog::Accepted) {
qDebug() << "Post dialog blocked mods list: " << m_blocked_mods;
createInstance();
} else {
abort();
}
} else {
createInstance();
}
}
void PackInstallTask::createInstance()
{
setAbortable(false);
setStatus(tr("Creating the instance..."));
QCoreApplication::processEvents();
auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
auto components = instance.getPackProfile();
components->buildingFromScratch();
for (auto target : m_version.targets) {
if (target.type == "game" && target.name == "minecraft") {
components->setComponentVersion("net.minecraft", target.version, true);
break;
}
}
for (auto target : m_version.targets) {
if (target.type != "modloader")
continue;
if (target.name == "forge") {
components->setComponentVersion("net.minecraftforge", target.version);
} else if (target.name == "fabric") {
components->setComponentVersion("net.fabricmc.fabric-loader", target.version);
}
}
// install any jar mods
QDir jarModsDir(FS::PathCombine(m_stagingPath, "minecraft", "jarmods"));
if (jarModsDir.exists()) {
QStringList jarMods;
for (const auto& info : jarModsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) {
jarMods.push_back(info.absoluteFilePath());
}
components->installJarMods(jarMods);
}
components->saveNow();
instance.setName(name());
instance.setIconKey(m_instIcon);
instance.setManagedPack("modpacksch", QString::number(m_pack.id), m_pack.name, QString::number(m_version.id), m_version.name);
instance.saveNow();
onCreateInstanceSucceeded();
}
void PackInstallTask::onCreateInstanceSucceeded()
{
downloadPack();
}
void PackInstallTask::downloadPack()
{
setStatus(tr("Downloading mods..."));
setAbortable(false);
auto jobPtr = makeShared<NetJob>(tr("Mod download"), APPLICATION->network());
for (auto const& file : m_version.files) {
if (file.serverOnly || file.url.isEmpty())
continue;
auto path = FS::PathCombine(m_stagingPath, ".minecraft", file.path, file.name);
qDebug() << "Will try to download" << file.url << "to" << path;
QFileInfo file_info(file.name);
auto dl = Net::Download::makeFile(file.url, path);
if (!file.sha1.isEmpty()) {
auto rawSha1 = QByteArray::fromHex(file.sha1.toLatin1());
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
}
jobPtr->addNetAction(dl);
}
connect(jobPtr.get(), &NetJob::succeeded, this, &PackInstallTask::onModDownloadSucceeded);
connect(jobPtr.get(), &NetJob::failed, this, &PackInstallTask::onModDownloadFailed);
connect(jobPtr.get(), &NetJob::aborted, this, &PackInstallTask::abort);
connect(jobPtr.get(), &NetJob::progress, this, &PackInstallTask::setProgress);
m_net_job = jobPtr;
setAbortable(true);
jobPtr->start();
}
void PackInstallTask::onModDownloadSucceeded()
{
m_net_job.reset();
if (!m_blocked_mods.isEmpty()) {
copyBlockedMods();
}
emitSucceeded();
}
void PackInstallTask::onManifestDownloadFailed(QString reason)
{
m_net_job.reset();
emitFailed(reason);
}
void PackInstallTask::onResolveModsFailed(QString reason)
{
m_net_job.reset();
emitFailed(reason);
}
void PackInstallTask::onCreateInstanceFailed(QString reason)
{
emitFailed(reason);
}
void PackInstallTask::onModDownloadFailed(QString reason)
{
m_net_job.reset();
emitFailed(reason);
}
/// @brief copy the matched blocked mods to the instance staging area
void PackInstallTask::copyBlockedMods()
{
setStatus(tr("Copying Blocked Mods..."));
setAbortable(false);
int i = 0;
int total = m_blocked_mods.length();
setProgress(i, total);
for (auto const& mod : m_blocked_mods) {
if (!mod.matched) {
qDebug() << mod.name << "was not matched to a local file, skipping copy";
continue;
}
auto dest_path = FS::PathCombine(m_stagingPath, ".minecraft", mod.targetFolder, mod.name);
setStatus(tr("Copying Blocked Mods (%1 out of %2 are done)").arg(QString::number(i), QString::number(total)));
qDebug() << "Will try to copy" << mod.localPath << "to" << dest_path;
if (!FS::copy(mod.localPath, dest_path)()) {
qDebug() << "Copy of" << mod.localPath << "to" << dest_path << "Failed";
}
i++;
setProgress(i, total);
}
setAbortable(true);
}
} // namespace ModpacksCH

View File

@ -0,0 +1,101 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 flowln <flowlnlnln@gmail.com>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright 2020-2021 Petr Mrazek <peterix@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "FTBPackManifest.h"
#include "InstanceTask.h"
#include "QObjectPtr.h"
#include "modplatform/flame/FileResolvingTask.h"
#include "net/NetJob.h"
#include "ui/dialogs/BlockedModsDialog.h"
#include <QWidget>
namespace ModpacksCH {
class PackInstallTask final : public InstanceTask
{
Q_OBJECT
public:
explicit PackInstallTask(Modpack pack, QString version, QWidget* parent = nullptr);
~PackInstallTask() override = default;
bool abort() override;
protected:
void executeTask() override;
private slots:
void onManifestDownloadSucceeded();
void onResolveModsSucceeded();
void onCreateInstanceSucceeded();
void onModDownloadSucceeded();
void onManifestDownloadFailed(QString reason);
void onResolveModsFailed(QString reason);
void onCreateInstanceFailed(QString reason);
void onModDownloadFailed(QString reason);
private:
void resolveMods();
void createInstance();
void downloadPack();
void copyBlockedMods();
private:
NetJob::Ptr m_net_job = nullptr;
shared_qobject_ptr<Flame::FileResolvingTask> m_mod_id_resolver_task = nullptr;
QList<int> m_file_id_map;
QByteArray m_response;
Modpack m_pack;
QString m_version_name;
Version m_version;
QMap<QString, QString> m_files_to_copy;
QList<BlockedMod> m_blocked_mods;
//FIXME: nuke
QWidget* m_parent;
};
}

View File

@ -0,0 +1,195 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2020 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright 2020-2021 Petr Mrazek <peterix@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "FTBPackManifest.h"
#include "Json.h"
static void loadSpecs(ModpacksCH::Specs & s, QJsonObject & obj)
{
s.id = Json::requireInteger(obj, "id");
s.minimum = Json::requireInteger(obj, "minimum");
s.recommended = Json::requireInteger(obj, "recommended");
}
static void loadTag(ModpacksCH::Tag & t, QJsonObject & obj)
{
t.id = Json::requireInteger(obj, "id");
t.name = Json::requireString(obj, "name");
}
static void loadArt(ModpacksCH::Art & a, QJsonObject & obj)
{
a.id = Json::requireInteger(obj, "id");
a.url = Json::requireString(obj, "url");
a.type = Json::requireString(obj, "type");
a.width = Json::requireInteger(obj, "width");
a.height = Json::requireInteger(obj, "height");
a.compressed = Json::requireBoolean(obj, "compressed");
a.sha1 = Json::requireString(obj, "sha1");
a.size = Json::requireInteger(obj, "size");
a.updated = Json::requireInteger(obj, "updated");
}
static void loadAuthor(ModpacksCH::Author & a, QJsonObject & obj)
{
a.id = Json::requireInteger(obj, "id");
a.name = Json::requireString(obj, "name");
a.type = Json::requireString(obj, "type");
a.website = Json::requireString(obj, "website");
a.updated = Json::requireInteger(obj, "updated");
}
static void loadVersionInfo(ModpacksCH::VersionInfo & v, QJsonObject & obj)
{
v.id = Json::requireInteger(obj, "id");
v.name = Json::requireString(obj, "name");
v.type = Json::requireString(obj, "type");
v.updated = Json::requireInteger(obj, "updated");
auto specs = Json::requireObject(obj, "specs");
loadSpecs(v.specs, specs);
}
void ModpacksCH::loadModpack(ModpacksCH::Modpack & m, QJsonObject & obj)
{
m.id = Json::requireInteger(obj, "id");
m.name = Json::requireString(obj, "name");
m.synopsis = Json::requireString(obj, "synopsis");
m.description = Json::requireString(obj, "description");
m.type = Json::requireString(obj, "type");
m.featured = Json::requireBoolean(obj, "featured");
m.installs = Json::requireInteger(obj, "installs");
m.plays = Json::requireInteger(obj, "plays");
m.updated = Json::requireInteger(obj, "updated");
m.refreshed = Json::requireInteger(obj, "refreshed");
auto artArr = Json::requireArray(obj, "art");
for (QJsonValueRef artRaw : artArr)
{
auto artObj = Json::requireObject(artRaw);
ModpacksCH::Art art;
loadArt(art, artObj);
m.art.append(art);
}
auto authorArr = Json::requireArray(obj, "authors");
for (QJsonValueRef authorRaw : authorArr)
{
auto authorObj = Json::requireObject(authorRaw);
ModpacksCH::Author author;
loadAuthor(author, authorObj);
m.authors.append(author);
}
auto versionArr = Json::requireArray(obj, "versions");
for (QJsonValueRef versionRaw : versionArr)
{
auto versionObj = Json::requireObject(versionRaw);
ModpacksCH::VersionInfo version;
loadVersionInfo(version, versionObj);
m.versions.append(version);
}
auto tagArr = Json::requireArray(obj, "tags");
for (QJsonValueRef tagRaw : tagArr)
{
auto tagObj = Json::requireObject(tagRaw);
ModpacksCH::Tag tag;
loadTag(tag, tagObj);
m.tags.append(tag);
}
m.updated = Json::requireInteger(obj, "updated");
}
static void loadVersionTarget(ModpacksCH::VersionTarget & a, QJsonObject & obj)
{
a.id = Json::requireInteger(obj, "id");
a.name = Json::requireString(obj, "name");
a.type = Json::requireString(obj, "type");
a.version = Json::requireString(obj, "version");
a.updated = Json::requireInteger(obj, "updated");
}
static void loadVersionFile(ModpacksCH::VersionFile & a, QJsonObject & obj)
{
a.id = Json::requireInteger(obj, "id");
a.type = Json::requireString(obj, "type");
a.path = Json::requireString(obj, "path");
a.name = Json::requireString(obj, "name");
a.version = Json::requireString(obj, "version");
a.url = Json::ensureString(obj, "url"); // optional
a.sha1 = Json::requireString(obj, "sha1");
a.size = Json::requireInteger(obj, "size");
a.clientOnly = Json::requireBoolean(obj, "clientonly");
a.serverOnly = Json::requireBoolean(obj, "serveronly");
a.optional = Json::requireBoolean(obj, "optional");
a.updated = Json::requireInteger(obj, "updated");
auto curseforgeObj = Json::ensureObject(obj, "curseforge"); // optional
a.curseforge.project_id = Json::ensureInteger(curseforgeObj, "project");
a.curseforge.file_id = Json::ensureInteger(curseforgeObj, "file");
}
void ModpacksCH::loadVersion(ModpacksCH::Version & m, QJsonObject & obj)
{
m.id = Json::requireInteger(obj, "id");
m.parent = Json::requireInteger(obj, "parent");
m.name = Json::requireString(obj, "name");
m.type = Json::requireString(obj, "type");
m.installs = Json::requireInteger(obj, "installs");
m.plays = Json::requireInteger(obj, "plays");
m.updated = Json::requireInteger(obj, "updated");
m.refreshed = Json::requireInteger(obj, "refreshed");
auto specs = Json::requireObject(obj, "specs");
loadSpecs(m.specs, specs);
auto targetArr = Json::requireArray(obj, "targets");
for (QJsonValueRef targetRaw : targetArr)
{
auto versionObj = Json::requireObject(targetRaw);
ModpacksCH::VersionTarget target;
loadVersionTarget(target, versionObj);
m.targets.append(target);
}
auto fileArr = Json::requireArray(obj, "files");
for (QJsonValueRef fileRaw : fileArr)
{
auto fileObj = Json::requireObject(fileRaw);
ModpacksCH::VersionFile file;
loadVersionFile(file, fileObj);
m.files.append(file);
}
}
//static void loadVersionChangelog(ModpacksCH::VersionChangelog & m, QJsonObject & obj)
//{
// m.content = Json::requireString(obj, "content");
// m.updated = Json::requireInteger(obj, "updated");
//}

View File

@ -0,0 +1,168 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright 2020 Petr Mrazek <peterix@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <QString>
#include <QVector>
#include <QUrl>
#include <QJsonObject>
#include <QMetaType>
namespace ModpacksCH
{
struct Specs
{
int id;
int minimum;
int recommended;
};
struct Tag
{
int id;
QString name;
};
struct Art
{
int id;
QString url;
QString type;
int width;
int height;
bool compressed;
QString sha1;
int size;
int64_t updated;
};
struct Author
{
int id;
QString name;
QString type;
QString website;
int64_t updated;
};
struct VersionInfo
{
int id;
QString name;
QString type;
int64_t updated;
Specs specs;
};
struct Modpack
{
int id;
QString name;
QString synopsis;
QString description;
QString type;
bool featured;
int installs;
int plays;
int64_t updated;
int64_t refreshed;
QVector<Art> art;
QVector<Author> authors;
QVector<VersionInfo> versions;
QVector<Tag> tags;
};
struct VersionTarget
{
int id;
QString type;
QString name;
QString version;
int64_t updated;
};
struct VersionFileCurseForge
{
int project_id;
int file_id;
};
struct VersionFile
{
int id;
QString type;
QString path;
QString name;
QString version;
QString url;
QString sha1;
int size;
bool clientOnly;
bool serverOnly;
bool optional;
int64_t updated;
VersionFileCurseForge curseforge;
};
struct Version
{
int id;
int parent;
QString name;
QString type;
int installs;
int plays;
int64_t updated;
int64_t refreshed;
Specs specs;
QVector<VersionTarget> targets;
QVector<VersionFile> files;
};
struct VersionChangelog
{
QString content;
int64_t updated;
};
void loadModpack(Modpack & m, QJsonObject & obj);
void loadVersion(Version & m, QJsonObject & obj);
}
Q_DECLARE_METATYPE(ModpacksCH::Modpack)

View File

@ -0,0 +1,81 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Lenny McLennington <lenny@sneed.church>
*
* 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 "FetchFlameAPIKey.h"
#include "Application.h"
#include <BuildConfig.h>
#include <Json.h>
#include <ui/dialogs/ProgressDialog.h>
#include <ui/dialogs/CustomMessageBox.h>
FetchFlameAPIKey::FetchFlameAPIKey(QObject *parent)
: Task{parent}
{
}
void FetchFlameAPIKey::executeTask()
{
QNetworkRequest req(BuildConfig.FLAME_API_KEY_API_URL);
m_reply.reset(APPLICATION->network()->get(req));
connect(m_reply.get(), &QNetworkReply::downloadProgress, this, &Task::setProgress);
connect(m_reply.get(), &QNetworkReply::finished, this, &FetchFlameAPIKey::downloadFinished);
connect(m_reply.get(),
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
&QNetworkReply::errorOccurred,
#else
qOverload<QNetworkReply::NetworkError>(&QNetworkReply::error),
#endif
this,
[this] (QNetworkReply::NetworkError error) {
qCritical() << "Network error: " << error;
emitFailed(m_reply->errorString());
});
setStatus(tr("Fetching Curseforge core API key"));
}
void FetchFlameAPIKey::downloadFinished()
{
auto res = m_reply->readAll();
auto doc = QJsonDocument::fromJson(res);
qDebug() << doc;
try {
auto obj = Json::requireObject(doc);
auto success = Json::requireBoolean(obj, "ok");
if (success)
{
m_result = Json::requireString(obj, "token");
emitSucceeded();
}
else
{
emitFailed("The API returned an output indicating failure.");
}
}
catch (Json::JsonException&)
{
qCritical() << "Output: " << res;
emitFailed("The API returned an unexpected JSON output.");
}
}

View File

@ -0,0 +1,44 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Lenny McLennington <lenny@sneed.church>
*
* 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/>.
*/
#ifndef FETCHFLAMEAPIKEY_H
#define FETCHFLAMEAPIKEY_H
#include <QObject>
#include <QNetworkReply>
#include <tasks/Task.h>
class FetchFlameAPIKey : public Task
{
Q_OBJECT
public:
explicit FetchFlameAPIKey(QObject *parent = nullptr);
QString m_result;
public slots:
void downloadFinished();
protected:
virtual void executeTask();
std::shared_ptr<QNetworkReply> m_reply;
};
#endif // FETCHFLAMEAPIKEY_H

View File

@ -1,57 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- 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"/>
<svg
width="64"
height="64"
viewBox="0 0 64 64"
version="1.1"
id="svg99"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="org.fn2006.PollyMC.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview101"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="12.697086"
inkscape:cx="35.638098"
inkscape:cy="31.345775"
inkscape:window-width="1920"
inkscape:window-height="994"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g5269" />
<defs
id="defs96">
<inkscape:path-effect
effect="powerclip"
id="path-effect5638"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5562"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5535"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g5269"
clip-path="none"
transform="matrix(1.25,0,0,1.25,-8,-10.5)">
<path
id="path300"
style="fill:#8acb01"
d="m 8,16 c 0,12 0,24 0,36 8.010417,0 16.020833,0 24.03125,0 0,-0.8 0,-1.6 0,-2.4 3.714062,0 7.428125,0 11.142187,0 0,-0.8 0,-1.6 0,-2.4 4.275521,0 8.551042,0 12.826563,0 C 56,36.8 56,26.4 56,16 40,16 24,16 8,16 Z"
inkscape:label="head" />
<path
id="rect3493"
style="fill:#b0af8d;stroke-width:1.39562"
inkscape:label="cheek"
d="M 19.959999,28.879064 H 32.253197 V 52 H 19.959999 Z" />
<path
id="rect3491"
inkscape:label="eye"
d="M 19.960001,28 H 32.333299 V 40 H 19.960001 Z"
style="stroke-width:1.00871" />
<path
id="path352"
style="display:inline;fill:#3b3b3b;fill-opacity:1;stroke-width:1.00922"
d="m 32,28 c 0,7.333333 0,14.666667 0,22 4,0 8,0 12,0 0,-0.814583 0,-1.629167 0,-2.44375 4,0 8,0 12,0 C 56,41.0375 56,34.51875 56,28 48,28 40,28 32,28 Z"
inkscape:label="beak" />
</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

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -25,5 +25,8 @@
<!-- https://commons.wikimedia.org/wiki/File:Teawie_Party.png -->
<file alias="teawie-spooky">teawie-spooky.png</file>
<!-- https://commons.wikimedia.org/wiki/File:Teawie_Halloween.png -->
<file alias="polly">polly.png</file>
<file alias="polly-xmas">polly-xmas.png</file>
<file alias="polly-bday">polly-bday.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,6 @@
licenses for images here;
polly.png cc0
santa-hat.png cc0
party-hat.png Pixabay License
polly-xmas.png cc0
polly-bday.png Pixabay License

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 KiB

View File

@ -1,57 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- 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"/>
<svg
width="64"
height="64"
viewBox="0 0 64 64"
version="1.1"
id="svg99"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="org.fn2006.PollyMC.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview101"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="12.697086"
inkscape:cx="35.638098"
inkscape:cy="31.345775"
inkscape:window-width="1920"
inkscape:window-height="994"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g5269" />
<defs
id="defs96">
<inkscape:path-effect
effect="powerclip"
id="path-effect5638"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5562"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5535"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g5269"
clip-path="none"
transform="matrix(1.25,0,0,1.25,-8,-10.5)">
<path
id="path300"
style="fill:#8acb01"
d="m 8,16 c 0,12 0,24 0,36 8.010417,0 16.020833,0 24.03125,0 0,-0.8 0,-1.6 0,-2.4 3.714062,0 7.428125,0 11.142187,0 0,-0.8 0,-1.6 0,-2.4 4.275521,0 8.551042,0 12.826563,0 C 56,36.8 56,26.4 56,16 40,16 24,16 8,16 Z"
inkscape:label="head" />
<path
id="rect3493"
style="fill:#b0af8d;stroke-width:1.39562"
inkscape:label="cheek"
d="M 19.959999,28.879064 H 32.253197 V 52 H 19.959999 Z" />
<path
id="rect3491"
inkscape:label="eye"
d="M 19.960001,28 H 32.333299 V 40 H 19.960001 Z"
style="stroke-width:1.00871" />
<path
id="path352"
style="display:inline;fill:#3b3b3b;fill-opacity:1;stroke-width:1.00922"
d="m 32,28 c 0,7.333333 0,14.666667 0,22 4,0 8,0 12,0 0,-0.814583 0,-1.629167 0,-2.44375 4,0 8,0 12,0 C 56,41.0375 56,34.51875 56,28 48,28 40,28 32,28 Z"
inkscape:label="beak" />
</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

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="64"
height="64"
viewBox="0 0 64 64"
version="1.1"
id="svg99"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="org.fn2006.PollyMC.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview101"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="12.697086"
inkscape:cx="35.638098"
inkscape:cy="31.345775"
inkscape:window-width="1920"
inkscape:window-height="994"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g5269" />
<defs
id="defs96">
<inkscape:path-effect
effect="powerclip"
id="path-effect5638"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5562"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5535"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g5269"
clip-path="none"
transform="matrix(1.25,0,0,1.25,-8,-10.5)">
<path
id="path300"
style="fill:#8acb01"
d="m 8,16 c 0,12 0,24 0,36 8.010417,0 16.020833,0 24.03125,0 0,-0.8 0,-1.6 0,-2.4 3.714062,0 7.428125,0 11.142187,0 0,-0.8 0,-1.6 0,-2.4 4.275521,0 8.551042,0 12.826563,0 C 56,36.8 56,26.4 56,16 40,16 24,16 8,16 Z"
inkscape:label="head" />
<path
id="rect3493"
style="fill:#b0af8d;stroke-width:1.39562"
inkscape:label="cheek"
d="M 19.959999,28.879064 H 32.253197 V 52 H 19.959999 Z" />
<path
id="rect3491"
inkscape:label="eye"
d="M 19.960001,28 H 32.333299 V 40 H 19.960001 Z"
style="stroke-width:1.00871" />
<path
id="path352"
style="display:inline;fill:#3b3b3b;fill-opacity:1;stroke-width:1.00922"
d="m 32,28 c 0,7.333333 0,14.666667 0,22 4,0 8,0 12,0 0,-0.814583 0,-1.629167 0,-2.44375 4,0 8,0 12,0 C 56,41.0375 56,34.51875 56,28 48,28 40,28 32,28 Z"
inkscape:label="beak" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,57 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- 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"/>
<svg
width="64"
height="64"
viewBox="0 0 64 64"
version="1.1"
id="svg99"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="org.fn2006.PollyMC.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview101"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="12.697086"
inkscape:cx="35.638098"
inkscape:cy="31.345775"
inkscape:window-width="1920"
inkscape:window-height="994"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g5269" />
<defs
id="defs96">
<inkscape:path-effect
effect="powerclip"
id="path-effect5638"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5562"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5535"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g5269"
clip-path="none"
transform="matrix(1.25,0,0,1.25,-8,-10.5)">
<path
id="path300"
style="fill:#8acb01"
d="m 8,16 c 0,12 0,24 0,36 8.010417,0 16.020833,0 24.03125,0 0,-0.8 0,-1.6 0,-2.4 3.714062,0 7.428125,0 11.142187,0 0,-0.8 0,-1.6 0,-2.4 4.275521,0 8.551042,0 12.826563,0 C 56,36.8 56,26.4 56,16 40,16 24,16 8,16 Z"
inkscape:label="head" />
<path
id="rect3493"
style="fill:#b0af8d;stroke-width:1.39562"
inkscape:label="cheek"
d="M 19.959999,28.879064 H 32.253197 V 52 H 19.959999 Z" />
<path
id="rect3491"
inkscape:label="eye"
d="M 19.960001,28 H 32.333299 V 40 H 19.960001 Z"
style="stroke-width:1.00871" />
<path
id="path352"
style="display:inline;fill:#3b3b3b;fill-opacity:1;stroke-width:1.00922"
d="m 32,28 c 0,7.333333 0,14.666667 0,22 4,0 8,0 12,0 0,-0.814583 0,-1.629167 0,-2.44375 4,0 8,0 12,0 C 56,41.0375 56,34.51875 56,28 48,28 40,28 32,28 Z"
inkscape:label="beak" />
</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

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,2 +1,96 @@
<?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>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="64"
height="64"
viewBox="0 0 64 64"
version="1.1"
id="svg99"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="org.fn2006.PollyMC.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview101"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="12.697086"
inkscape:cx="35.638098"
inkscape:cy="31.345775"
inkscape:window-width="1920"
inkscape:window-height="994"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g5269" />
<defs
id="defs96">
<inkscape:path-effect
effect="powerclip"
id="path-effect5638"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5562"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5535"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g5269"
clip-path="none"
transform="matrix(1.25,0,0,1.25,-8,-10.5)">
<path
id="path300"
style="fill:#8acb01"
d="m 8,16 c 0,12 0,24 0,36 8.010417,0 16.020833,0 24.03125,0 0,-0.8 0,-1.6 0,-2.4 3.714062,0 7.428125,0 11.142187,0 0,-0.8 0,-1.6 0,-2.4 4.275521,0 8.551042,0 12.826563,0 C 56,36.8 56,26.4 56,16 40,16 24,16 8,16 Z"
inkscape:label="head" />
<path
id="rect3493"
style="fill:#b0af8d;stroke-width:1.39562"
inkscape:label="cheek"
d="M 19.959999,28.879064 H 32.253197 V 52 H 19.959999 Z" />
<path
id="rect3491"
inkscape:label="eye"
d="M 19.960001,28 H 32.333299 V 40 H 19.960001 Z"
style="stroke-width:1.00871" />
<path
id="path352"
style="display:inline;fill:#3b3b3b;fill-opacity:1;stroke-width:1.00922"
d="m 32,28 c 0,7.333333 0,14.666667 0,22 4,0 8,0 12,0 0,-0.814583 0,-1.629167 0,-2.44375 4,0 8,0 12,0 C 56,41.0375 56,34.51875 56,28 48,28 40,28 32,28 Z"
inkscape:label="beak" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 660 B

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,57 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- 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"/>
<svg
width="64"
height="64"
viewBox="0 0 64 64"
version="1.1"
id="svg99"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="org.fn2006.PollyMC.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview101"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="12.697086"
inkscape:cx="35.638098"
inkscape:cy="31.345775"
inkscape:window-width="1920"
inkscape:window-height="994"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g5269" />
<defs
id="defs96">
<inkscape:path-effect
effect="powerclip"
id="path-effect5638"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5562"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5535"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g5269"
clip-path="none"
transform="matrix(1.25,0,0,1.25,-8,-10.5)">
<path
id="path300"
style="fill:#8acb01"
d="m 8,16 c 0,12 0,24 0,36 8.010417,0 16.020833,0 24.03125,0 0,-0.8 0,-1.6 0,-2.4 3.714062,0 7.428125,0 11.142187,0 0,-0.8 0,-1.6 0,-2.4 4.275521,0 8.551042,0 12.826563,0 C 56,36.8 56,26.4 56,16 40,16 24,16 8,16 Z"
inkscape:label="head" />
<path
id="rect3493"
style="fill:#b0af8d;stroke-width:1.39562"
inkscape:label="cheek"
d="M 19.959999,28.879064 H 32.253197 V 52 H 19.959999 Z" />
<path
id="rect3491"
inkscape:label="eye"
d="M 19.960001,28 H 32.333299 V 40 H 19.960001 Z"
style="stroke-width:1.00871" />
<path
id="path352"
style="display:inline;fill:#3b3b3b;fill-opacity:1;stroke-width:1.00922"
d="m 32,28 c 0,7.333333 0,14.666667 0,22 4,0 8,0 12,0 0,-0.814583 0,-1.629167 0,-2.44375 4,0 8,0 12,0 C 56,41.0375 56,34.51875 56,28 48,28 40,28 32,28 Z"
inkscape:label="beak" />
</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

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,57 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- 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"/>
<svg
width="64"
height="64"
viewBox="0 0 64 64"
version="1.1"
id="svg99"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="org.fn2006.PollyMC.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview101"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="12.697086"
inkscape:cx="35.638098"
inkscape:cy="31.345775"
inkscape:window-width="1920"
inkscape:window-height="994"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g5269" />
<defs
id="defs96">
<inkscape:path-effect
effect="powerclip"
id="path-effect5638"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5562"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5535"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g5269"
clip-path="none"
transform="matrix(1.25,0,0,1.25,-8,-10.5)">
<path
id="path300"
style="fill:#8acb01"
d="m 8,16 c 0,12 0,24 0,36 8.010417,0 16.020833,0 24.03125,0 0,-0.8 0,-1.6 0,-2.4 3.714062,0 7.428125,0 11.142187,0 0,-0.8 0,-1.6 0,-2.4 4.275521,0 8.551042,0 12.826563,0 C 56,36.8 56,26.4 56,16 40,16 24,16 8,16 Z"
inkscape:label="head" />
<path
id="rect3493"
style="fill:#b0af8d;stroke-width:1.39562"
inkscape:label="cheek"
d="M 19.959999,28.879064 H 32.253197 V 52 H 19.959999 Z" />
<path
id="rect3491"
inkscape:label="eye"
d="M 19.960001,28 H 32.333299 V 40 H 19.960001 Z"
style="stroke-width:1.00871" />
<path
id="path352"
style="display:inline;fill:#3b3b3b;fill-opacity:1;stroke-width:1.00922"
d="m 32,28 c 0,7.333333 0,14.666667 0,22 4,0 8,0 12,0 0,-0.814583 0,-1.629167 0,-2.44375 4,0 8,0 12,0 C 56,41.0375 56,34.51875 56,28 48,28 40,28 32,28 Z"
inkscape:label="beak" />
</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

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,57 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- 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"/>
<svg
width="64"
height="64"
viewBox="0 0 64 64"
version="1.1"
id="svg99"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="org.fn2006.PollyMC.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview101"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="12.697086"
inkscape:cx="35.638098"
inkscape:cy="31.345775"
inkscape:window-width="1920"
inkscape:window-height="994"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g5269" />
<defs
id="defs96">
<inkscape:path-effect
effect="powerclip"
id="path-effect5638"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5562"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5535"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g5269"
clip-path="none"
transform="matrix(1.25,0,0,1.25,-8,-10.5)">
<path
id="path300"
style="fill:#8acb01"
d="m 8,16 c 0,12 0,24 0,36 8.010417,0 16.020833,0 24.03125,0 0,-0.8 0,-1.6 0,-2.4 3.714062,0 7.428125,0 11.142187,0 0,-0.8 0,-1.6 0,-2.4 4.275521,0 8.551042,0 12.826563,0 C 56,36.8 56,26.4 56,16 40,16 24,16 8,16 Z"
inkscape:label="head" />
<path
id="rect3493"
style="fill:#b0af8d;stroke-width:1.39562"
inkscape:label="cheek"
d="M 19.959999,28.879064 H 32.253197 V 52 H 19.959999 Z" />
<path
id="rect3491"
inkscape:label="eye"
d="M 19.960001,28 H 32.333299 V 40 H 19.960001 Z"
style="stroke-width:1.00871" />
<path
id="path352"
style="display:inline;fill:#3b3b3b;fill-opacity:1;stroke-width:1.00922"
d="m 32,28 c 0,7.333333 0,14.666667 0,22 4,0 8,0 12,0 0,-0.814583 0,-1.629167 0,-2.44375 4,0 8,0 12,0 C 56,41.0375 56,34.51875 56,28 48,28 40,28 32,28 Z"
inkscape:label="beak" />
</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

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,57 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- 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"/>
<svg
width="64"
height="64"
viewBox="0 0 64 64"
version="1.1"
id="svg99"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="org.fn2006.PollyMC.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview101"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="12.697086"
inkscape:cx="35.638098"
inkscape:cy="31.345775"
inkscape:window-width="1920"
inkscape:window-height="994"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g5269" />
<defs
id="defs96">
<inkscape:path-effect
effect="powerclip"
id="path-effect5638"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5562"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5535"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g5269"
clip-path="none"
transform="matrix(1.25,0,0,1.25,-8,-10.5)">
<path
id="path300"
style="fill:#8acb01"
d="m 8,16 c 0,12 0,24 0,36 8.010417,0 16.020833,0 24.03125,0 0,-0.8 0,-1.6 0,-2.4 3.714062,0 7.428125,0 11.142187,0 0,-0.8 0,-1.6 0,-2.4 4.275521,0 8.551042,0 12.826563,0 C 56,36.8 56,26.4 56,16 40,16 24,16 8,16 Z"
inkscape:label="head" />
<path
id="rect3493"
style="fill:#b0af8d;stroke-width:1.39562"
inkscape:label="cheek"
d="M 19.959999,28.879064 H 32.253197 V 52 H 19.959999 Z" />
<path
id="rect3491"
inkscape:label="eye"
d="M 19.960001,28 H 32.333299 V 40 H 19.960001 Z"
style="stroke-width:1.00871" />
<path
id="path352"
style="display:inline;fill:#3b3b3b;fill-opacity:1;stroke-width:1.00922"
d="m 32,28 c 0,7.333333 0,14.666667 0,22 4,0 8,0 12,0 0,-0.814583 0,-1.629167 0,-2.44375 4,0 8,0 12,0 C 56,41.0375 56,34.51875 56,28 48,28 40,28 32,28 Z"
inkscape:label="beak" />
</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

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,57 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- 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"/>
<svg
width="64"
height="64"
viewBox="0 0 64 64"
version="1.1"
id="svg99"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="org.fn2006.PollyMC.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview101"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="12.697086"
inkscape:cx="35.638098"
inkscape:cy="31.345775"
inkscape:window-width="1920"
inkscape:window-height="994"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g5269" />
<defs
id="defs96">
<inkscape:path-effect
effect="powerclip"
id="path-effect5638"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5562"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5535"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g5269"
clip-path="none"
transform="matrix(1.25,0,0,1.25,-8,-10.5)">
<path
id="path300"
style="fill:#8acb01"
d="m 8,16 c 0,12 0,24 0,36 8.010417,0 16.020833,0 24.03125,0 0,-0.8 0,-1.6 0,-2.4 3.714062,0 7.428125,0 11.142187,0 0,-0.8 0,-1.6 0,-2.4 4.275521,0 8.551042,0 12.826563,0 C 56,36.8 56,26.4 56,16 40,16 24,16 8,16 Z"
inkscape:label="head" />
<path
id="rect3493"
style="fill:#b0af8d;stroke-width:1.39562"
inkscape:label="cheek"
d="M 19.959999,28.879064 H 32.253197 V 52 H 19.959999 Z" />
<path
id="rect3491"
inkscape:label="eye"
d="M 19.960001,28 H 32.333299 V 40 H 19.960001 Z"
style="stroke-width:1.00871" />
<path
id="path352"
style="display:inline;fill:#3b3b3b;fill-opacity:1;stroke-width:1.00922"
d="m 32,28 c 0,7.333333 0,14.666667 0,22 4,0 8,0 12,0 0,-0.814583 0,-1.629167 0,-2.44375 4,0 8,0 12,0 C 56,41.0375 56,34.51875 56,28 48,28 40,28 32,28 Z"
inkscape:label="beak" />
</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

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,57 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- 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"/>
<svg
width="64"
height="64"
viewBox="0 0 64 64"
version="1.1"
id="svg99"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="org.fn2006.PollyMC.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview101"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="12.697086"
inkscape:cx="35.638098"
inkscape:cy="31.345775"
inkscape:window-width="1920"
inkscape:window-height="994"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g5269" />
<defs
id="defs96">
<inkscape:path-effect
effect="powerclip"
id="path-effect5638"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5562"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5535"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g5269"
clip-path="none"
transform="matrix(1.25,0,0,1.25,-8,-10.5)">
<path
id="path300"
style="fill:#8acb01"
d="m 8,16 c 0,12 0,24 0,36 8.010417,0 16.020833,0 24.03125,0 0,-0.8 0,-1.6 0,-2.4 3.714062,0 7.428125,0 11.142187,0 0,-0.8 0,-1.6 0,-2.4 4.275521,0 8.551042,0 12.826563,0 C 56,36.8 56,26.4 56,16 40,16 24,16 8,16 Z"
inkscape:label="head" />
<path
id="rect3493"
style="fill:#b0af8d;stroke-width:1.39562"
inkscape:label="cheek"
d="M 19.959999,28.879064 H 32.253197 V 52 H 19.959999 Z" />
<path
id="rect3491"
inkscape:label="eye"
d="M 19.960001,28 H 32.333299 V 40 H 19.960001 Z"
style="stroke-width:1.00871" />
<path
id="path352"
style="display:inline;fill:#3b3b3b;fill-opacity:1;stroke-width:1.00922"
d="m 32,28 c 0,7.333333 0,14.666667 0,22 4,0 8,0 12,0 0,-0.814583 0,-1.629167 0,-2.44375 4,0 8,0 12,0 C 56,41.0375 56,34.51875 56,28 48,28 40,28 32,28 Z"
inkscape:label="beak" />
</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

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -42,6 +42,7 @@
#include <QFileDialog>
#include <QStandardPaths>
#include "net/FetchFlameAPIKey.h"
#include "ui/dialogs/ProgressDialog.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "net/PasteUpload.h"
@ -51,6 +52,31 @@
#include <DesktopServices.h>
#include <BuildConfig.h>
QString GuiUtil::fetchFlameKey(QWidget *parentWidget)
{
if (BuildConfig.FLAME_API_KEY_API_URL.isEmpty())
return "";
ProgressDialog prog(parentWidget);
auto flameKeyTask = std::make_unique<FetchFlameAPIKey>();
prog.execWithTask(flameKeyTask.get());
if (!flameKeyTask->wasSuccessful())
{
auto message = QObject::tr("Fetching the Curseforge API key failed. Reason: %1").arg(flameKeyTask->failReason());
if (!(APPLICATION->capabilities() & Application::SupportsFlame))
{
message += "\n\n" + QObject::tr("Downloading Curseforge modpacks will not work unless you manually set a valid Curseforge Core API key in the settings.");
}
CustomMessageBox::selectable(parentWidget,
QObject::tr("Failed to fetch Curseforge API key."),
message, QMessageBox::Critical)->exec();
}
return flameKeyTask->m_result;
}
std::optional<QString> GuiUtil::uploadPaste(const QString &name, const QString &text, QWidget *parentWidget)
{
ProgressDialog dialog(parentWidget);

View File

@ -5,6 +5,7 @@
namespace GuiUtil
{
QString fetchFlameKey(QWidget *parentWidget = nullptr);
std::optional<QString> uploadPaste(const QString &name, const QString &text, QWidget *parentWidget);
void setClipboardText(const QString &text);
QStringList BrowseForFiles(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget);

View File

@ -70,6 +70,17 @@ QString getCreditsHtml()
//: %1 is the name of the launcher, determined at build time, e.g. "Prism Launcher Developers"
stream << "<h3>" << QObject::tr("%1 Developers", "About Credits").arg(BuildConfig.LAUNCHER_DISPLAYNAME) << "</h3>\n";
stream << QString("<p>fn2006 %1</p>\n") .arg(getGitHub("fn2006"));
stream << "<br />\n";
//: %1 is the name of the launcher, determined at build time, e.g. "Prism Launcher Contributors"
stream << "<h3>" << QObject::tr("%1 Contributors", "About Credits").arg(BuildConfig.LAUNCHER_DISPLAYNAME) << "</h3>\n";
stream << QString("<p>anoraktrend %1</p>\n") .arg(getGitHub("anoraktrend"));
stream << QString("<p>Emma Tebibyte %1</p>\n") .arg(getWebsite("https://tebibyte.media/"));
stream << "<br />\n";
//: %1 is the name of the launcher, determined at build time, e.g. "Prism Launcher Developers"
stream << "<h3>" << QObject::tr("%1 Developers", "About Credits").arg("Prism Launcher") << "</h3>\n";
stream << QString("<p>Sefa Eyeoglu (Scrumplex) %1</p>\n") .arg(getWebsite("https://scrumplex.net"));
stream << QString("<p>dada513 %1</p>\n") .arg(getGitHub("dada513"));
stream << QString("<p>txtsd %1</p>\n") .arg(getWebsite("https://ihavea.quest"));
@ -165,7 +176,7 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia
QString urlText("<html><head/><body><p><a href=\"%1\">%1</a></p></body></html>");
ui->urlLabel->setText(urlText.arg(BuildConfig.LAUNCHER_GIT));
QString copyText("© 2022 %1");
QString copyText("%1");
ui->copyLabel->setText(copyText.arg(BuildConfig.LAUNCHER_COPYRIGHT));
connect(ui->closeButton, SIGNAL(clicked()), SLOT(close()));

View File

@ -0,0 +1,131 @@
/* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "CustomLoginDialog.h"
#include "ui_CustomLoginDialog.h"
#include "minecraft/auth/AccountTask.h"
#include <QtWidgets/QPushButton>
CustomLoginDialog::CustomLoginDialog(QWidget *parent) : QDialog(parent), ui(new Ui::CustomLoginDialog)
{
ui->setupUi(this);
ui->progressBar->setVisible(false);
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
CustomLoginDialog::~CustomLoginDialog()
{
delete ui;
}
// Stage 1: User interaction
void CustomLoginDialog::accept()
{
setUserInputsEnabled(false);
ui->progressBar->setVisible(true);
// Setup the login task and start it
m_account = MinecraftAccount::createCustom(ui->userTextBox->text(), ui->urlTextBox->text());
if (ui->mfaTextBox->text().length() > 0) {
m_loginTask = m_account->loginCustom(ui->passTextBox->text() + ':' + ui->mfaTextBox->text(), ui->urlTextBox->text());
}
else {
m_loginTask = m_account->loginCustom(ui->passTextBox->text(), ui->urlTextBox->text());
}
connect(m_loginTask.get(), &Task::failed, this, &CustomLoginDialog::onTaskFailed);
connect(m_loginTask.get(), &Task::succeeded, this, &CustomLoginDialog::onTaskSucceeded);
connect(m_loginTask.get(), &Task::status, this, &CustomLoginDialog::onTaskStatus);
connect(m_loginTask.get(), &Task::progress, this, &CustomLoginDialog::onTaskProgress);
m_loginTask->start();
}
void CustomLoginDialog::setUserInputsEnabled(bool enable)
{
ui->userTextBox->setEnabled(enable);
ui->passTextBox->setEnabled(enable);
ui->urlTextBox->setEnabled(enable);
ui->mfaTextBox->setEnabled(enable);
ui->buttonBox->setEnabled(enable);
}
// Enable the OK button only when both textboxes contain something.
void CustomLoginDialog::on_userTextBox_textEdited(const QString &newText)
{
ui->buttonBox->button(QDialogButtonBox::Ok)
->setEnabled(!newText.isEmpty() && !ui->passTextBox->text().isEmpty() && !ui->urlTextBox->text().isEmpty());
}
void CustomLoginDialog::on_passTextBox_textEdited(const QString &newText)
{
ui->buttonBox->button(QDialogButtonBox::Ok)
->setEnabled(!newText.isEmpty() && !ui->userTextBox->text().isEmpty() && !ui->urlTextBox->text().isEmpty());
}
void CustomLoginDialog::on_urlTextBox_textEdited(const QString &newText)
{
ui->buttonBox->button(QDialogButtonBox::Ok)
->setEnabled(!newText.isEmpty() && !ui->userTextBox->text().isEmpty() && !ui->passTextBox->text().isEmpty());
}
void CustomLoginDialog::onTaskFailed(const QString &reason)
{
// Set message
auto lines = reason.split('\n');
QString processed;
for(auto line: lines) {
if(line.size()) {
processed += "<font color='red'>" + line + "</font><br />";
}
else {
processed += "<br />";
}
}
ui->label->setText(processed);
// Re-enable user-interaction
setUserInputsEnabled(true);
ui->progressBar->setVisible(false);
}
void CustomLoginDialog::onTaskSucceeded()
{
QDialog::accept();
}
void CustomLoginDialog::onTaskStatus(const QString &status)
{
ui->label->setText(status);
}
void CustomLoginDialog::onTaskProgress(qint64 current, qint64 total)
{
ui->progressBar->setMaximum(total);
ui->progressBar->setValue(current);
}
// Public interface
MinecraftAccountPtr CustomLoginDialog::newAccount(QWidget *parent, QString msg)
{
CustomLoginDialog dlg(parent);
dlg.ui->label->setText(msg);
if (dlg.exec() == QDialog::Accepted)
{
return dlg.m_account;
}
return nullptr;
}

View File

@ -0,0 +1,60 @@
/* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <QtWidgets/QDialog>
#include <QtCore/QEventLoop>
#include "minecraft/auth/MinecraftAccount.h"
#include "tasks/Task.h"
namespace Ui
{
class CustomLoginDialog;
}
class CustomLoginDialog : public QDialog
{
Q_OBJECT
public:
~CustomLoginDialog();
static MinecraftAccountPtr newAccount(QWidget *parent, QString message);
private:
explicit CustomLoginDialog(QWidget *parent = 0);
void setUserInputsEnabled(bool enable);
protected
slots:
void accept();
void onTaskFailed(const QString &reason);
void onTaskSucceeded();
void onTaskStatus(const QString &status);
void onTaskProgress(qint64 current, qint64 total);
void on_userTextBox_textEdited(const QString &newText);
void on_passTextBox_textEdited(const QString &newText);
void on_urlTextBox_textEdited(const QString &newText);
private:
Ui::CustomLoginDialog *ui;
MinecraftAccountPtr m_account;
Task::Ptr m_loginTask;
};

View File

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CustomLoginDialog</class>
<widget class="QDialog" name="CustomLoginDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>421</width>
<height>198</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Add Account</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string notr="true">Message label placeholder.</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="userTextBox">
<property name="placeholderText">
<string>Email</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="passTextBox">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
<property name="placeholderText">
<string>Password</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="urlTextBox">
<property name="placeholderText">
<string>Url</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="mfaTextBox">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
<property name="placeholderText">
<string>2FA Code (Optional)</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,125 @@
/* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ElybyLoginDialog.h"
#include "ui_ElybyLoginDialog.h"
#include "minecraft/auth/AccountTask.h"
#include <QtWidgets/QPushButton>
ElybyLoginDialog::ElybyLoginDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ElybyLoginDialog)
{
ui->setupUi(this);
ui->progressBar->setVisible(false);
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
ElybyLoginDialog::~ElybyLoginDialog()
{
delete ui;
}
// Stage 1: User interaction
void ElybyLoginDialog::accept()
{
setUserInputsEnabled(false);
ui->progressBar->setVisible(true);
// Setup the login task and start it
m_account = MinecraftAccount::createElyby(ui->userTextBox->text());
if (ui->mfaTextBox->text().length() > 0) {
m_loginTask = m_account->loginElyby(ui->passTextBox->text() + ':' + ui->mfaTextBox->text());
}
else {
m_loginTask = m_account->loginElyby(ui->passTextBox->text());
}
connect(m_loginTask.get(), &Task::failed, this, &ElybyLoginDialog::onTaskFailed);
connect(m_loginTask.get(), &Task::succeeded, this, &ElybyLoginDialog::onTaskSucceeded);
connect(m_loginTask.get(), &Task::status, this, &ElybyLoginDialog::onTaskStatus);
connect(m_loginTask.get(), &Task::progress, this, &ElybyLoginDialog::onTaskProgress);
m_loginTask->start();
}
void ElybyLoginDialog::setUserInputsEnabled(bool enable)
{
ui->userTextBox->setEnabled(enable);
ui->passTextBox->setEnabled(enable);
ui->mfaTextBox->setEnabled(enable);
ui->buttonBox->setEnabled(enable);
}
// Enable the OK button only when both textboxes contain something.
void ElybyLoginDialog::on_userTextBox_textEdited(const QString &newText)
{
ui->buttonBox->button(QDialogButtonBox::Ok)
->setEnabled(!newText.isEmpty() && !ui->passTextBox->text().isEmpty());
}
void ElybyLoginDialog::on_passTextBox_textEdited(const QString &newText)
{
ui->buttonBox->button(QDialogButtonBox::Ok)
->setEnabled(!newText.isEmpty() && !ui->userTextBox->text().isEmpty());
}
void ElybyLoginDialog::onTaskFailed(const QString &reason)
{
// Set message
auto lines = reason.split('\n');
QString processed;
for(auto line: lines) {
if(line.size()) {
processed += "<font color='red'>" + line + "</font><br />";
}
else {
processed += "<br />";
}
}
ui->label->setText(processed);
// Re-enable user-interaction
setUserInputsEnabled(true);
ui->progressBar->setVisible(false);
}
void ElybyLoginDialog::onTaskSucceeded()
{
QDialog::accept();
}
void ElybyLoginDialog::onTaskStatus(const QString &status)
{
ui->label->setText(status);
}
void ElybyLoginDialog::onTaskProgress(qint64 current, qint64 total)
{
ui->progressBar->setMaximum(total);
ui->progressBar->setValue(current);
}
// Public interface
MinecraftAccountPtr ElybyLoginDialog::newAccount(QWidget *parent, QString msg)
{
ElybyLoginDialog dlg(parent);
dlg.ui->label->setText(msg);
if (dlg.exec() == QDialog::Accepted)
{
return dlg.m_account;
}
return nullptr;
}

View File

@ -0,0 +1,59 @@
/* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <QtWidgets/QDialog>
#include <QtCore/QEventLoop>
#include "minecraft/auth/MinecraftAccount.h"
#include "tasks/Task.h"
namespace Ui
{
class ElybyLoginDialog;
}
class ElybyLoginDialog : public QDialog
{
Q_OBJECT
public:
~ElybyLoginDialog();
static MinecraftAccountPtr newAccount(QWidget *parent, QString message);
private:
explicit ElybyLoginDialog(QWidget *parent = 0);
void setUserInputsEnabled(bool enable);
protected
slots:
void accept();
void onTaskFailed(const QString &reason);
void onTaskSucceeded();
void onTaskStatus(const QString &status);
void onTaskProgress(qint64 current, qint64 total);
void on_userTextBox_textEdited(const QString &newText);
void on_passTextBox_textEdited(const QString &newText);
private:
Ui::ElybyLoginDialog *ui;
MinecraftAccountPtr m_account;
Task::Ptr m_loginTask;
};

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ElybyLoginDialog</class>
<widget class="QDialog" name="ElybyLoginDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>421</width>
<height>198</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Add Account</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string notr="true">Message label placeholder.</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="userTextBox">
<property name="placeholderText">
<string>Email</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="passTextBox">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
<property name="placeholderText">
<string>Password</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="mfaTextBox">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
<property name="placeholderText">
<string>2FA Code (Optional)</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -56,6 +56,7 @@
#include "ui/widgets/PageContainer.h"
#include "ui/pages/modplatform/VanillaPage.h"
#include "ui/pages/modplatform/atlauncher/AtlPage.h"
#include "ui/pages/modplatform/ftb/FtbPage.h"
#include "ui/pages/modplatform/legacy_ftb/Page.h"
#include "ui/pages/modplatform/flame/FlamePage.h"
#include "ui/pages/modplatform/ImportPage.h"
@ -167,6 +168,7 @@ QList<BasePage *> NewInstanceDialog::getPages()
pages.append(new AtlPage(this));
if (APPLICATION->capabilities() & Application::SupportsFlame)
pages.append(new FlamePage(this));
pages.append(new FtbPage(this));
pages.append(new LegacyFTB::Page(this));
pages.append(new ModrinthPage(this));
pages.append(new TechnicPage(this));

View File

@ -53,6 +53,8 @@
#include "net/PasteUpload.h"
#include "BuildConfig.h"
#include "ui/GuiUtil.h"
APIPage::APIPage(QWidget *parent) :
QWidget(parent),
ui(new Ui::APIPage)
@ -88,11 +90,16 @@ APIPage::APIPage(QWidget *parent) :
ui->metaURL->setPlaceholderText(BuildConfig.META_URL);
ui->userAgentLineEdit->setPlaceholderText(BuildConfig.USER_AGENT);
if (BuildConfig.FLAME_API_KEY_API_URL.isEmpty())
ui->fetchKeyButton->hide();
loadSettings();
resetBaseURLNote();
connect(ui->pasteTypeComboBox, currentIndexChangedSignal, this, &APIPage::updateBaseURLNote);
connect(ui->baseURLEntry, &QLineEdit::textEdited, this, &APIPage::resetBaseURLNote);
connect(ui->fetchKeyButton, &QPushButton::clicked, this, &APIPage::fetchKeyButtonPressed);
}
APIPage::~APIPage()
@ -185,6 +192,14 @@ void APIPage::applySettings()
s->set("UserAgentOverride", ui->userAgentLineEdit->text());
}
void APIPage::fetchKeyButtonPressed()
{
QString apiKey = GuiUtil::fetchFlameKey(parentWidget());
if (!apiKey.isEmpty())
ui->flameKey->setText(apiKey);
}
bool APIPage::apply()
{
applySettings();

View File

@ -80,6 +80,7 @@ private:
void updateBaseURLPlaceholder(int index);
void loadSettings();
void applySettings();
void fetchKeyButtonPressed();
private:
Ui::APIPage *ui;

View File

@ -248,16 +248,9 @@
<property name="title">
<string>&amp;CurseForge Core API</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Note: you probably don't need to set this if CurseForge already works.</string>
</property>
</widget>
</item>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Enter a custom API Key for CurseForge here.</string>
</property>
@ -282,6 +275,29 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="fetchKeyButton">
<property name="text">
<string>Fetch Official Launcher's Key</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Note: you probably don't need to set this if CurseForge already works.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Using the Official Curseforge Launcher's key may break Curseforge's Terms of service, but should allow PollyMC to download all mods in a modpack without you needing to download any of them manually.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -48,6 +48,8 @@
#include "ui/dialogs/OfflineLoginDialog.h"
#include "ui/dialogs/LoginDialog.h"
#include "ui/dialogs/MSALoginDialog.h"
#include "ui/dialogs/ElybyLoginDialog.h"
#include "ui/dialogs/CustomLoginDialog.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/SkinUploadDialog.h"
@ -188,19 +190,6 @@ void AccountListPage::on_actionAddMicrosoft_triggered()
void AccountListPage::on_actionAddOffline_triggered()
{
if (!m_accounts->anyAccountIsValid()) {
QMessageBox::warning(
this,
tr("Error"),
tr(
"You must add a Microsoft or Mojang account that owns Minecraft before you can add an offline account."
"<br><br>"
"If you have lost your account you can contact Microsoft for support."
)
);
return;
}
MinecraftAccountPtr account = OfflineLoginDialog::newAccount(
this,
tr("Please enter your desired username to add your offline account.")
@ -215,6 +204,38 @@ void AccountListPage::on_actionAddOffline_triggered()
}
}
void AccountListPage::on_actionAddElyby_triggered()
{
MinecraftAccountPtr account = ElybyLoginDialog::newAccount(
this,
tr("Please enter your Ely.by account email and password to add your account.")
);
if (account)
{
m_accounts->addAccount(account);
if (m_accounts->count() == 1) {
m_accounts->setDefaultAccount(account);
}
}
}
void AccountListPage::on_actionAddCustom_triggered()
{
MinecraftAccountPtr account = CustomLoginDialog::newAccount(
this,
tr("Enter the custom yggdrasil server url, along with your username and password to add your account.")
);
if (account)
{
m_accounts->addAccount(account);
if (m_accounts->count() == 1) {
m_accounts->setDefaultAccount(account);
}
}
}
void AccountListPage::on_actionRemove_triggered()
{
QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes();
@ -258,17 +279,22 @@ void AccountListPage::updateButtonStates()
bool hasSelection = !selection.empty();
bool accountIsReady = false;
bool accountIsOnline = false;
bool accountIsElyby = false;
bool accountIsCustom = false;
if (hasSelection)
{
QModelIndex selected = selection.first();
MinecraftAccountPtr account = selected.data(AccountList::PointerRole).value<MinecraftAccountPtr>();
accountIsReady = !account->isActive();
accountIsOnline = !account->isOffline();
accountIsElyby = account->isElyby();
accountIsCustom = account->isCustom();
}
ui->actionRemove->setEnabled(accountIsReady);
ui->actionSetDefault->setEnabled(accountIsReady);
ui->actionUploadSkin->setEnabled(accountIsReady && accountIsOnline);
ui->actionDeleteSkin->setEnabled(accountIsReady && accountIsOnline);
ui->actionUploadSkin->setEnabled(accountIsReady && accountIsOnline && !accountIsElyby && !accountIsCustom);
ui->actionDeleteSkin->setEnabled(accountIsReady && accountIsOnline && !accountIsElyby && !accountIsCustom);
ui->actionRefresh->setEnabled(accountIsReady && accountIsOnline);
if(m_accounts->defaultAccount().get() == nullptr) {

View File

@ -85,6 +85,8 @@ public slots:
void on_actionAddMojang_triggered();
void on_actionAddMicrosoft_triggered();
void on_actionAddOffline_triggered();
void on_actionAddElyby_triggered();
void on_actionAddCustom_triggered();
void on_actionRemove_triggered();
void on_actionRefresh_triggered();
void on_actionSetDefault_triggered();

View File

@ -55,6 +55,8 @@
<addaction name="actionAddMicrosoft"/>
<addaction name="actionAddMojang"/>
<addaction name="actionAddOffline"/>
<addaction name="actionAddElyby"/>
<addaction name="actionAddCustom"/>
<addaction name="actionRefresh"/>
<addaction name="actionRemove"/>
<addaction name="actionSetDefault"/>
@ -109,6 +111,16 @@
<string>Add &amp;Offline</string>
</property>
</action>
<action name="actionAddElyby">
<property name="text">
<string>Add &amp;Ely.by</string>
</property>
</action>
<action name="actionAddCustom">
<property name="text">
<string>Add &amp;Custom</string>
</property>
</action>
<action name="actionRefresh">
<property name="text">
<string>&amp;Refresh</string>

View File

@ -485,16 +485,6 @@ void VersionPage::on_actionChange_version_triggered()
void VersionPage::on_actionDownload_All_triggered()
{
if (!APPLICATION->accounts()->anyAccountIsValid())
{
CustomMessageBox::selectable(
this, tr("Error"),
tr("Cannot download Minecraft or update instances unless you have at least "
"one account added.\nPlease add your Mojang or Minecraft account."),
QMessageBox::Warning)->show();
return;
}
auto updateTask = m_inst->createUpdateTask(Net::Mode::Online);
if (!updateTask)
{

View File

@ -0,0 +1,93 @@
/*
* Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "FtbFilterModel.h"
#include <QDebug>
#include "modplatform/modpacksch/FTBPackManifest.h"
#include "StringUtils.h"
namespace Ftb {
FilterModel::FilterModel(QObject *parent) : QSortFilterProxyModel(parent)
{
currentSorting = Sorting::ByPlays;
sortings.insert(tr("Sort by Plays"), Sorting::ByPlays);
sortings.insert(tr("Sort by Installs"), Sorting::ByInstalls);
sortings.insert(tr("Sort by Name"), Sorting::ByName);
}
const QMap<QString, FilterModel::Sorting> FilterModel::getAvailableSortings()
{
return sortings;
}
QString FilterModel::translateCurrentSorting()
{
return sortings.key(currentSorting);
}
void FilterModel::setSorting(Sorting sorting)
{
currentSorting = sorting;
invalidate();
}
FilterModel::Sorting FilterModel::getCurrentSorting()
{
return currentSorting;
}
void FilterModel::setSearchTerm(const QString& term)
{
searchTerm = term.trimmed();
invalidate();
}
bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
if (searchTerm.isEmpty()) {
return true;
}
auto index = sourceModel()->index(sourceRow, 0, sourceParent);
auto pack = sourceModel()->data(index, Qt::UserRole).value<ModpacksCH::Modpack>();
return pack.name.contains(searchTerm, Qt::CaseInsensitive);
}
bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
ModpacksCH::Modpack leftPack = sourceModel()->data(left, Qt::UserRole).value<ModpacksCH::Modpack>();
ModpacksCH::Modpack rightPack = sourceModel()->data(right, Qt::UserRole).value<ModpacksCH::Modpack>();
if (currentSorting == ByPlays) {
return leftPack.plays < rightPack.plays;
}
else if (currentSorting == ByInstalls) {
return leftPack.installs < rightPack.installs;
}
else if (currentSorting == ByName) {
return StringUtils::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0;
}
// Invalid sorting set, somehow...
qWarning() << "Invalid sorting set!";
return true;
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <QtCore/QSortFilterProxyModel>
namespace Ftb {
class FilterModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
FilterModel(QObject* parent = Q_NULLPTR);
enum Sorting {
ByPlays,
ByInstalls,
ByName,
};
const QMap<QString, Sorting> getAvailableSortings();
QString translateCurrentSorting();
void setSorting(Sorting sorting);
Sorting getCurrentSorting();
void setSearchTerm(const QString& term);
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
private:
QMap<QString, Sorting> sortings;
Sorting currentSorting;
QString searchTerm { "" };
};
}

View File

@ -0,0 +1,304 @@
/*
* Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "FtbListModel.h"
#include "BuildConfig.h"
#include "Application.h"
#include "Json.h"
#include <QPainter>
namespace Ftb {
ListModel::ListModel(QObject *parent) : QAbstractListModel(parent)
{
}
ListModel::~ListModel()
{
}
int ListModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : modpacks.size();
}
int ListModel::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : 1;
}
QVariant ListModel::data(const QModelIndex &index, int role) const
{
int pos = index.row();
if(pos >= modpacks.size() || pos < 0 || !index.isValid())
{
return QString("INVALID INDEX %1").arg(pos);
}
ModpacksCH::Modpack pack = modpacks.at(pos);
if(role == Qt::DisplayRole)
{
return pack.name;
}
else if (role == Qt::ToolTipRole)
{
return pack.synopsis;
}
else if(role == Qt::DecorationRole)
{
QIcon placeholder = APPLICATION->getThemedIcon("screenshot-placeholder");
auto iter = m_logoMap.find(pack.name);
if (iter != m_logoMap.end()) {
auto & logo = *iter;
if(!logo.result.isNull()) {
return logo.result;
}
return placeholder;
}
for(auto art : pack.art) {
if(art.type == "square") {
((ListModel *)this)->requestLogo(pack.name, art.url);
}
}
return placeholder;
}
else if(role == Qt::UserRole)
{
QVariant v;
v.setValue(pack);
return v;
}
return QVariant();
}
void ListModel::getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback)
{
if(m_logoMap.contains(logo))
{
callback(APPLICATION->metacache()->resolveEntry("ModpacksCHPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath());
}
else
{
requestLogo(logo, logoUrl);
}
}
void ListModel::request()
{
m_aborted = false;
beginResetModel();
modpacks.clear();
endResetModel();
auto netJob = makeShared<NetJob>("Ftb::Request", APPLICATION->network());
auto url = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/all");
netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response));
jobPtr = netJob;
jobPtr->start();
QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::requestFinished);
QObject::connect(netJob.get(), &NetJob::failed, this, &ListModel::requestFailed);
}
void ListModel::abortRequest()
{
m_aborted = jobPtr->abort();
jobPtr.reset();
}
void ListModel::requestFinished()
{
jobPtr.reset();
remainingPacks.clear();
QJsonParseError parse_error {};
QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
if(parse_error.error != QJsonParseError::NoError) {
qWarning() << "Error while parsing JSON response from ModpacksCH at " << parse_error.offset << " reason: " << parse_error.errorString();
qWarning() << response;
return;
}
auto packs = doc.object().value("packs").toArray();
for(auto pack : packs) {
auto packId = pack.toInt();
remainingPacks.append(packId);
}
if(!remainingPacks.isEmpty()) {
currentPack = remainingPacks.at(0);
requestPack();
}
}
void ListModel::requestFailed(QString reason)
{
jobPtr.reset();
remainingPacks.clear();
}
void ListModel::requestPack()
{
auto netJob = makeShared<NetJob>("Ftb::Search", APPLICATION->network());
auto searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/%1").arg(currentPack);
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
jobPtr = netJob;
jobPtr->start();
QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::packRequestFinished);
QObject::connect(netJob.get(), &NetJob::failed, this, &ListModel::packRequestFailed);
}
void ListModel::packRequestFinished()
{
if (!jobPtr || m_aborted)
return;
jobPtr.reset();
remainingPacks.removeOne(currentPack);
QJsonParseError parse_error;
QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
if(parse_error.error != QJsonParseError::NoError) {
qWarning() << "Error while parsing JSON response from ModpacksCH at " << parse_error.offset << " reason: " << parse_error.errorString();
qWarning() << response;
return;
}
auto obj = doc.object();
ModpacksCH::Modpack pack;
try
{
ModpacksCH::loadModpack(pack, obj);
}
catch (const JSONValidationError &e)
{
qDebug() << QString::fromUtf8(response);
qWarning() << "Error while reading pack manifest from ModpacksCH: " << e.cause();
return;
}
// Since there is no guarantee that packs have a version, this will just
// ignore those "dud" packs.
if (pack.versions.empty())
{
qWarning() << "ModpacksCH Pack " << pack.id << " ignored. reason: lacking any versions";
}
else
{
beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size());
modpacks.append(pack);
endInsertRows();
}
if(!remainingPacks.isEmpty()) {
currentPack = remainingPacks.at(0);
requestPack();
}
}
void ListModel::packRequestFailed(QString reason)
{
jobPtr.reset();
remainingPacks.removeOne(currentPack);
}
void ListModel::logoLoaded(QString logo, bool stale)
{
auto & logoObj = m_logoMap[logo];
logoObj.downloadJob.reset();
QString smallPath = logoObj.fullpath + ".small";
QFileInfo smallInfo(smallPath);
if(stale || !smallInfo.exists()) {
QImage image(logoObj.fullpath);
if (image.isNull())
{
logoObj.failed = true;
return;
}
QImage small;
if (image.width() > image.height()) {
small = image.scaledToWidth(512).scaledToWidth(256, Qt::SmoothTransformation);
}
else {
small = image.scaledToHeight(512).scaledToHeight(256, Qt::SmoothTransformation);
}
QPoint offset((256 - small.width()) / 2, (256 - small.height()) / 2);
QImage square(QSize(256, 256), QImage::Format_ARGB32);
square.fill(Qt::transparent);
QPainter painter(&square);
painter.drawImage(offset, small);
painter.end();
square.save(logoObj.fullpath + ".small", "PNG");
}
logoObj.result = QIcon(logoObj.fullpath + ".small");
for(int i = 0; i < modpacks.size(); i++) {
if(modpacks[i].name == logo) {
emit dataChanged(createIndex(i, 0), createIndex(i, 0), {Qt::DecorationRole});
}
}
}
void ListModel::logoFailed(QString logo)
{
m_logoMap[logo].failed = true;
m_logoMap[logo].downloadJob.reset();
}
void ListModel::requestLogo(QString logo, QString url)
{
if(m_logoMap.contains(logo)) {
return;
}
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("ModpacksCHPacks", QString("logos/%1").arg(logo.section(".", 0, 0)));
bool stale = entry->isStale();
auto job = makeShared<NetJob>(QString("ModpacksCH Icon Download %1").arg(logo), APPLICATION->network());
job->addNetAction(Net::Download::makeCached(QUrl(url), entry));
auto fullPath = entry->getFullPath();
QObject::connect(job.get(), &NetJob::finished, this, [this, logo, fullPath, stale]
{
logoLoaded(logo, stale);
});
QObject::connect(job.get(), &NetJob::failed, this, [this, logo]
{
logoFailed(logo);
});
auto &newLogoEntry = m_logoMap[logo];
newLogoEntry.downloadJob = job;
newLogoEntry.fullpath = fullPath;
job->start();
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <QAbstractListModel>
#include "modplatform/modpacksch/FTBPackManifest.h"
#include "net/NetJob.h"
#include <QIcon>
namespace Ftb {
struct Logo {
QString fullpath;
NetJob::Ptr downloadJob;
QIcon result;
bool failed = false;
};
typedef QMap<QString, Logo> LogoMap;
typedef std::function<void(QString)> LogoCallback;
class ListModel : public QAbstractListModel
{
Q_OBJECT
public:
ListModel(QObject *parent);
virtual ~ListModel();
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
void request();
void abortRequest();
void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback);
[[nodiscard]] bool isMakingRequest() const { return jobPtr.get(); }
[[nodiscard]] bool wasAborted() const { return m_aborted; }
private slots:
void requestFinished();
void requestFailed(QString reason);
void requestPack();
void packRequestFinished();
void packRequestFailed(QString reason);
void logoFailed(QString logo);
void logoLoaded(QString logo, bool stale);
private:
void requestLogo(QString file, QString url);
private:
bool m_aborted = false;
QList<ModpacksCH::Modpack> modpacks;
LogoMap m_logoMap;
NetJob::Ptr jobPtr;
int currentPack;
QList<int> remainingPacks;
QByteArray response;
};
}

View File

@ -0,0 +1,199 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright 2021 Philip T <me@phit.link>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "FtbPage.h"
#include "ui_FtbPage.h"
#include <QKeyEvent>
#include "ui/dialogs/NewInstanceDialog.h"
#include "modplatform/modpacksch/FTBPackInstallTask.h"
#include "Markdown.h"
FtbPage::FtbPage(NewInstanceDialog* dialog, QWidget *parent)
: QWidget(parent), ui(new Ui::FtbPage), dialog(dialog)
{
ui->setupUi(this);
filterModel = new Ftb::FilterModel(this);
listModel = new Ftb::ListModel(this);
filterModel->setSourceModel(listModel);
ui->packView->setModel(filterModel);
ui->packView->setSortingEnabled(true);
ui->packView->header()->hide();
ui->packView->setIndentation(0);
ui->searchEdit->installEventFilter(this);
ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
for(int i = 0; i < filterModel->getAvailableSortings().size(); i++)
{
ui->sortByBox->addItem(filterModel->getAvailableSortings().keys().at(i));
}
ui->sortByBox->setCurrentText(filterModel->translateCurrentSorting());
connect(ui->searchEdit, &QLineEdit::textChanged, this, &FtbPage::triggerSearch);
connect(ui->sortByBox, &QComboBox::currentTextChanged, this, &FtbPage::onSortingSelectionChanged);
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FtbPage::onSelectionChanged);
connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FtbPage::onVersionSelectionChanged);
ui->packDescription->setMetaEntry("FTBPacks");
}
FtbPage::~FtbPage()
{
delete ui;
}
bool FtbPage::eventFilter(QObject* watched, QEvent* event)
{
if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Return) {
triggerSearch();
keyEvent->accept();
return true;
}
}
return QWidget::eventFilter(watched, event);
}
bool FtbPage::shouldDisplay() const
{
return true;
}
void FtbPage::retranslate()
{
ui->retranslateUi(this);
}
void FtbPage::openedImpl()
{
if(!initialised || listModel->wasAborted())
{
listModel->request();
initialised = true;
}
suggestCurrent();
}
void FtbPage::closedImpl()
{
if (listModel->isMakingRequest())
listModel->abortRequest();
}
void FtbPage::suggestCurrent()
{
if(!isOpened)
{
return;
}
if (selectedVersion.isEmpty())
{
dialog->setSuggestedPack();
return;
}
dialog->setSuggestedPack(selected.name, selectedVersion, new ModpacksCH::PackInstallTask(selected, selectedVersion, this));
for(auto art : selected.art) {
if(art.type == "square") {
QString editedLogoName;
editedLogoName = selected.name;
listModel->getLogo(selected.name, art.url, [this, editedLogoName](QString logo)
{
dialog->setSuggestedIconFromFile(logo + ".small", editedLogoName);
});
}
}
}
void FtbPage::triggerSearch()
{
filterModel->setSearchTerm(ui->searchEdit->text());
}
void FtbPage::onSortingSelectionChanged(QString data)
{
auto toSet = filterModel->getAvailableSortings().value(data);
filterModel->setSorting(toSet);
}
void FtbPage::onSelectionChanged(QModelIndex first, QModelIndex second)
{
ui->versionSelectionBox->clear();
if(!first.isValid())
{
if(isOpened)
{
dialog->setSuggestedPack();
}
return;
}
selected = filterModel->data(first, Qt::UserRole).value<ModpacksCH::Modpack>();
QString output = markdownToHTML(selected.description.toUtf8());
ui->packDescription->setHtml(output);
// reverse foreach, so that the newest versions are first
for (auto i = selected.versions.size(); i--;) {
ui->versionSelectionBox->addItem(selected.versions.at(i).name);
}
suggestCurrent();
}
void FtbPage::onVersionSelectionChanged(QString data)
{
if(data.isNull() || data.isEmpty())
{
selectedVersion = "";
return;
}
selectedVersion = data;
suggestCurrent();
}

View File

@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "FtbFilterModel.h"
#include "FtbListModel.h"
#include <QWidget>
#include "Application.h"
#include "ui/pages/BasePage.h"
#include "tasks/Task.h"
namespace Ui
{
class FtbPage;
}
class NewInstanceDialog;
class FtbPage : public QWidget, public BasePage
{
Q_OBJECT
public:
explicit FtbPage(NewInstanceDialog* dialog, QWidget *parent = 0);
virtual ~FtbPage();
virtual QString displayName() const override
{
return "FTB";
}
virtual QIcon icon() const override
{
return APPLICATION->getThemedIcon("ftb_logo");
}
virtual QString id() const override
{
return "ftb";
}
virtual QString helpPage() const override
{
return "FTB-platform";
}
virtual bool shouldDisplay() const override;
void retranslate() override;
void openedImpl() override;
void closedImpl() override;
bool eventFilter(QObject * watched, QEvent * event) override;
private:
void suggestCurrent();
private slots:
void triggerSearch();
void onSortingSelectionChanged(QString data);
void onSelectionChanged(QModelIndex first, QModelIndex second);
void onVersionSelectionChanged(QString data);
private:
Ui::FtbPage *ui = nullptr;
NewInstanceDialog* dialog = nullptr;
Ftb::ListModel* listModel = nullptr;
Ftb::FilterModel* filterModel = nullptr;
ModpacksCH::Modpack selected;
QString selectedVersion;
bool initialised { false };
};

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FtbPage</class>
<widget class="QWidget" name="FtbPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>875</width>
<height>745</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0" rowminimumheight="0" columnminimumwidth="0,0,0">
<item row="0" column="2">
<widget class="QComboBox" name="versionSelectionBox"/>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>Version selected:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QComboBox" name="sortByBox"/>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLineEdit" name="searchEdit">
<property name="placeholderText">
<string>Search and filter...</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QTreeView" name="packView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="iconSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="ProjectDescriptionPage" name="packDescription">
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="openLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ProjectDescriptionPage</class>
<extends>QTextBrowser</extends>
<header>ui/widgets/ProjectDescriptionPage.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>searchEdit</tabstop>
<tabstop>versionSelectionBox</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -72,6 +72,7 @@ class ThemeCustomizationWidget : public QWidget {
{ "kitteh", QObject::tr("Background Cat (from MultiMC)") },
{ "rory", QObject::tr("Rory ID 11 (drawn by Ashtaka)") },
{ "rory-flat", QObject::tr("Rory ID 11 (flat edition, drawn by Ashtaka)") },
{ "teawie", QObject::tr("Teawie (drawn by SympathyTea)") }
{ "teawie", QObject::tr("Teawie (drawn by SympathyTea)") },
{ "polly", QObject::tr("Polly the Parrot (drawn by anoraktrend)") }
};
};

View File

@ -6,29 +6,29 @@
#### Directly installing
The `prismlauncher` flake provides a package which you can install along with
The `pollymc` flake provides a package which you can install along with
the rest of your packages
```nix
# In your flake.nix:
{
inputs = {
prismlauncher.url = "github:PrismLauncher/PrismLauncher";
pollymc.url = "github:fn2006/PollyMC";
};
}
```
```nix
# And in your system configuration:
environment.systemPackages = [ prismlauncher.packages.${pkgs.system}.prismlauncher ];
environment.systemPackages = [ pollymc.packages.${pkgs.system}.pollymc ];
# Or in your home-manager configuration:
home.packages = [ prismlauncher.packages.${pkgs.system}.prismlauncher ];
home.packages = [ pollymc.packages.${pkgs.system}.pollymc ];
```
#### Using the overlay
Alternatively, you can overlay the prismlauncher version in nixpkgs which will
Alternatively, you can overlay the pollymc version in nixpkgs which will
allow you to install using `pkgs` as you normally would while also using the
latest version
@ -36,19 +36,19 @@ latest version
# In your flake.nix:
{
inputs = {
prismlauncher.url = "github:PrismLauncher/PrismLauncher";
pollymc.url = "github:fn2006/PollyMC";
};
}
```
```nix
# And in your system configuration:
nixpkgs.overlays = [ inputs.prismlauncher.overlay ];
environment.systemPackages = [ pkgs.prismlauncher ];
nixpkgs.overlays = [ inputs.pollymc.overlay ];
environment.systemPackages = [ pkgs.pollymc ];
# Or in your home-manager configuration:
config.nixpkgs.overlays = [ inputs.prismlauncher.overlay ];
home.packages = [ pkgs.prismlauncher ];
config.nixpkgs.overlays = [ inputs.pollymc.overlay ];
home.packages = [ pkgs.pollymc ];
```
### Without flakes-enabled nix
@ -56,9 +56,9 @@ home.packages = [ pkgs.prismlauncher ];
#### Using channels
```sh
nix-channel --add https://github.com/PrismLauncher/PrismLauncher/archive/master.tar.gz prismlauncher
nix-channel --update prismlauncher
nix-env -iA prismlauncher
nix-channel --add https://github.com/fn2006/PollyMC/archive/master.tar.gz pollymc
nix-channel --update pollymc
nix-env -iA pollymc
```
#### Using the overlay
@ -67,10 +67,10 @@ nix-env -iA prismlauncher
# In your configuration.nix:
{
nixpkgs.overlays = [
(import (builtins.fetchTarball "https://github.com/PrismLauncher/PrismLauncher/archive/develop.tar.gz")).overlay
(import (builtins.fetchTarball "https://github.com/fn2006/PollyMC/archive/develop.tar.gz")).overlay
];
environment.systemPackages = with pkgs; [ prismlauncher ];
environment.systemPackages = with pkgs; [ pollymc ];
}
```
@ -79,5 +79,5 @@ nix-env -iA prismlauncher
If you're on a flakes-enabled nix you can run the launcher in one-line
```sh
nix run github:PrismLauncher/PrismLauncher
nix run github:fn2006/PollyMC
```

View File

@ -6,8 +6,8 @@
}: {
perSystem = {pkgs, ...}: {
packages = {
inherit (pkgs) prismlauncher-qt5-unwrapped prismlauncher-qt5 prismlauncher-unwrapped prismlauncher;
default = pkgs.prismlauncher;
inherit (pkgs) pollymc-qt5-unwrapped pollymc-qt5 pollymc-unwrapped pollymc;
default = pkgs.pollymc;
};
};
@ -20,10 +20,10 @@
inherit self version;
};
in {
prismlauncher-qt5-unwrapped = mkPrism final.libsForQt5;
prismlauncher-qt5 = prev.prismlauncher-qt5.override {prismlauncher-unwrapped = final.prismlauncher-qt5-unwrapped;};
prismlauncher-unwrapped = mkPrism final.qt6Packages;
prismlauncher = prev.prismlauncher.override {inherit (final) prismlauncher-unwrapped;};
pollymc-qt5-unwrapped = mkPrism final.libsForQt5;
pollymc-qt5 = prev.pollymc-qt5.override {pollymc-unwrapped = final.pollymc-qt5-unwrapped;};
pollymc-unwrapped = mkPrism final.qt6Packages;
pollymc = prev.pollymc.override {inherit (final) pollymc-unwrapped;};
};
};
}

View File

@ -19,7 +19,7 @@
libnbtplusplus,
}:
stdenv.mkDerivation rec {
pname = "prismlauncher-unwrapped";
pname = "pollymc-unwrapped";
inherit version;
src = lib.cleanSource self;
@ -50,7 +50,7 @@ stdenv.mkDerivation rec {
dontWrapQtApps = true;
meta = with lib; {
homepage = "https://prismlauncher.org/";
homepage = "https://github.com/fn2006/PollyMC";
description = "A free, open source launcher for Minecraft";
longDescription = ''
Allows you to have multiple, separate instances of Minecraft (each with
@ -58,8 +58,8 @@ stdenv.mkDerivation rec {
their associated options with a simple interface.
'';
platforms = platforms.linux;
changelog = "https://github.com/PrismLauncher/PrismLauncher/releases/tag/${version}";
changelog = "https://github.com/fn2006/PollyMC/releases/tag/${version}";
license = licenses.gpl3Only;
maintainers = with maintainers; [minion3665 Scrumplex];
maintainers = with maintainers; [fn2006];
};
}

View File

@ -8,49 +8,49 @@ if(UNIX)
endif()
endif()
set(Launcher_CommonName "PrismLauncher")
set(Launcher_DisplayName "Prism Launcher")
set(Launcher_CommonName "PollyMC")
set(Launcher_DisplayName "PollyMC")
set(Launcher_Name "${Launcher_CommonName}" PARENT_SCOPE)
set(Launcher_DisplayName "${Launcher_DisplayName}" PARENT_SCOPE)
set(Launcher_Copyright "Prism Launcher Contributors\\n© 2021-2022 PolyMC Contributors \\n© 2012-2021 MultiMC Contributors")
set(Launcher_Copyright_Mac "Prism Launcher Contributors, © 2021-2022 PolyMC Contributors and © 2012-2021 MultiMC Contributors" PARENT_SCOPE)
set(Launcher_Copyright "© 2022-2023 PollyMC Contributors\\n© 2022-2023 Prism Launcher Contributors\\n© 2021-2022 PolyMC Contributors \\n© 2012-2021 MultiMC Contributors")
set(Launcher_Copyright_Mac "© 2022-2023 PollyMC Contributors, © 2022-2023 Prism Launcher Contributors, © 2021-2022 PolyMC Contributors and © 2012-2021 MultiMC Contributors" PARENT_SCOPE)
set(Launcher_Copyright "${Launcher_Copyright}" PARENT_SCOPE)
set(Launcher_Domain "prismlauncher.org" PARENT_SCOPE)
set(Launcher_UserAgent "${Launcher_CommonName}/${Launcher_VERSION_NAME}" PARENT_SCOPE)
set(Launcher_ConfigFile "prismlauncher.cfg" PARENT_SCOPE)
set(Launcher_Git "https://github.com/PrismLauncher/PrismLauncher" PARENT_SCOPE)
set(Launcher_DesktopFileName "org.prismlauncher.PrismLauncher.desktop" PARENT_SCOPE)
set(Launcher_SVGFileName "org.prismlauncher.PrismLauncher.svg" PARENT_SCOPE)
set(Launcher_Domain "" PARENT_SCOPE)
set(Launcher_UserAgent "PrismLauncher/${Launcher_RELEASE_VERSION_NAME}" PARENT_SCOPE)
set(Launcher_ConfigFile "pollymc.cfg" PARENT_SCOPE)
set(Launcher_Git "https://github.com/fn2006/PollyMC" PARENT_SCOPE)
set(Launcher_DesktopFileName "org.fn2006.PollyMC.desktop" PARENT_SCOPE)
set(Launcher_SVGFileName "org.fn2006.PollyMC.svg" PARENT_SCOPE)
set(Launcher_Desktop "program_info/org.prismlauncher.PrismLauncher.desktop" PARENT_SCOPE)
set(Launcher_Desktop "program_info/org.fn2006.PollyMC.desktop" PARENT_SCOPE)
set(Launcher_mrpack_MIMEInfo "program_info/modrinth-mrpack-mime.xml" PARENT_SCOPE)
set(Launcher_MetaInfo "program_info/org.prismlauncher.PrismLauncher.metainfo.xml" PARENT_SCOPE)
set(Launcher_SVG "program_info/org.prismlauncher.PrismLauncher.svg" PARENT_SCOPE)
set(Launcher_MetaInfo "program_info/org.fn2006.PollyMC.metainfo.xml" PARENT_SCOPE)
set(Launcher_SVG "program_info/org.fn2006.PollyMC.svg" PARENT_SCOPE)
set(Launcher_Branding_ICNS "program_info/prismlauncher.icns" PARENT_SCOPE)
set(Launcher_Branding_ICO "program_info/prismlauncher.ico")
set(Launcher_Branding_ICO "program_info/pollymc.ico")
set(Launcher_Branding_ICO "${Launcher_Branding_ICO}" PARENT_SCOPE)
set(Launcher_Branding_WindowsRC "program_info/prismlauncher.rc" PARENT_SCOPE)
set(Launcher_Branding_WindowsRC "program_info/pollymc.rc" PARENT_SCOPE)
set(Launcher_Branding_LogoQRC "program_info/prismlauncher.qrc" PARENT_SCOPE)
set(Launcher_Portable_File "program_info/portable.txt" PARENT_SCOPE)
configure_file(org.prismlauncher.PrismLauncher.desktop.in org.prismlauncher.PrismLauncher.desktop)
configure_file(org.prismlauncher.PrismLauncher.metainfo.xml.in org.prismlauncher.PrismLauncher.metainfo.xml)
configure_file(prismlauncher.rc.in prismlauncher.rc @ONLY)
configure_file(prismlauncher.manifest.in prismlauncher.manifest @ONLY)
configure_file(prismlauncher.ico prismlauncher.ico COPYONLY)
configure_file(org.fn2006.PollyMC.desktop.in org.fn2006.PollyMC.desktop)
configure_file(org.fn2006.PollyMC.metainfo.xml.in org.fn2006.PollyMC.metainfo.xml)
configure_file(pollymc.rc.in pollymc.rc @ONLY)
configure_file(pollymc.manifest.in pollymc.manifest @ONLY)
configure_file(pollymc.ico pollymc.ico COPYONLY)
configure_file(win_install.nsi.in win_install.nsi @ONLY)
if(SCDOC_FOUND)
set(in_scd "${CMAKE_CURRENT_SOURCE_DIR}/prismlauncher.6.scd")
set(out_man "${CMAKE_CURRENT_BINARY_DIR}/prismlauncher.6")
set(in_scd "${CMAKE_CURRENT_SOURCE_DIR}/pollymc.6.scd")
set(out_man "${CMAKE_CURRENT_BINARY_DIR}/pollymc.6")
add_custom_command(
DEPENDS "${in_scd}"
OUTPUT "${out_man}"
COMMAND ${SCDOC_SCDOC} < "${in_scd}" > "${out_man}"
)
add_custom_target(man ALL DEPENDS ${out_man})
set(Launcher_ManPage "program_info/prismlauncher.6" PARENT_SCOPE)
set(Launcher_ManPage "program_info/pollymc.6" PARENT_SCOPE)
endif()

View File

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="64"
height="64"
viewBox="0 0 64 64"
version="1.1"
id="svg99"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="org.fn2006.PollyMC Source.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview101"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="9.4927604"
inkscape:cx="52.355688"
inkscape:cy="38.345011"
inkscape:window-width="1920"
inkscape:window-height="994"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs96">
<inkscape:path-effect
effect="powerclip"
id="path-effect5638"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5562"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
<inkscape:path-effect
effect="powerclip"
id="path-effect5535"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g5269"
clip-path="none"
transform="matrix(1.25,0,0,1.25,-8,-10.5)">
<g
id="g926"
inkscape:label="head">
<path
style="fill:#8acb01;fill-opacity:1"
id="rect883"
width="12"
height="33.599998"
x="31.173504"
y="16"
sodipodi:type="rect"
inkscape:label="head2"
d="m 31.173504,16 h 12 v 33.599998 h -12 z">
<title
id="title910">head2</title>
</path>
<path
style="fill:#8acb01;fill-opacity:1"
id="path916"
width="25.979649"
height="31.200001"
x="30.020351"
y="16"
sodipodi:type="rect"
inkscape:label="head1"
d="M 30.020351,16 H 56 V 47.200001 H 30.020351 Z">
<title
id="title914">head1</title>
</path>
<path
style="fill:#8acb01;fill-opacity:1"
id="path352"
width="24.03161"
height="36"
x="8"
y="16"
sodipodi:type="rect"
inkscape:label="head"
d="M 8,16 H 32.03161 V 52 H 8 Z">
<title
id="title908">head</title>
</path>
</g>
<g
id="g932"
transform="matrix(1.0033333,0,0,1.0033333,-0.14666667,-0.09333333)"
inkscape:label="beak">
<path
style="display:inline;fill:#313131;fill-opacity:1"
id="rect1721"
width="12"
height="21.6"
x="32"
y="28"
sodipodi:type="rect"
d="M 32,28 H 44 V 49.6 H 32 Z" />
<path
style="display:inline;fill:#313131;fill-opacity:1"
id="path928"
width="24"
height="19.200001"
x="32"
y="28"
sodipodi:type="rect"
d="M 32,28 H 56 V 47.200001 H 32 Z" />
</g>
<path
style="fill:#000000;fill-opacity:1"
id="rect3491"
width="12.08"
height="12.08"
x="19.960001"
y="28"
sodipodi:type="rect"
d="m 19.960001,28 h 12.08 v 12.08 h -12.08 z"
inkscape:label="eye" />
<path
style="fill:#b0af8d;fill-opacity:1"
id="rect3493"
width="12.08"
height="12.08"
x="19.959999"
y="40"
sodipodi:type="rect"
d="m 19.959999,40 h 12.08 v 12.08 h -12.08 z"
inkscape:label="cheek" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1,207 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="1024"
height="1024"
viewBox="0 0 1024 1024"
fill="none"
version="1.1"
id="svg1070"
sodipodi:docname="org.fn2006.PollyMC.bigsur.svg"
xml:space="preserve"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1072"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="0.01"
inkscape:cx="-125500"
inkscape:cy="-70400"
inkscape:window-width="1920"
inkscape:window-height="994"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1070" /><g
filter="url(#filter0_d_102_69)"
id="g998"><path
fill-rule="evenodd"
clip-rule="evenodd"
d="M924 354.627C924 344.845 924.004 335.062 923.944 325.279C923.895 317.038 923.8 308.799 923.576 300.562C923.092 282.609 922.033 264.502 918.84 246.749C915.602 228.741 910.314 211.98 901.981 195.617C893.789 179.534 883.088 164.817 870.32 152.058C857.555 139.299 842.834 128.605 826.746 120.418C810.366 112.083 793.587 106.797 775.558 103.56C757.803 100.372 739.691 99.315 721.738 98.83C713.495 98.607 705.253 98.513 697.008 98.462C687.22 98.402 677.432 98.407 667.644 98.407L553.997 98H468.997L357.361 98.407C347.554 98.407 337.747 98.402 327.94 98.462C319.678 98.513 311.42 98.607 303.161 98.83C285.167 99.315 267.014 100.373 249.217 103.565C231.164 106.801 214.36 112.085 197.958 120.414C181.835 128.602 167.083 139.297 154.291 152.058C141.501 164.816 130.78 179.53 122.573 195.61C114.217 211.981 108.919 228.752 105.673 246.77C102.477 264.516 101.418 282.617 100.931 300.562C100.709 308.8 100.613 317.039 100.563 325.279C100.503 335.063 100 347.216 100 356.999L100.003 467.089L100 552.998L100.508 665.427C100.508 675.223 100.504 685.019 100.563 694.815C100.613 703.067 100.709 711.317 100.932 719.566C101.418 737.542 102.479 755.675 105.678 773.452C108.923 791.484 114.22 808.269 122.569 824.653C130.777 840.759 141.5 855.495 154.291 868.272C167.082 881.049 181.83 891.757 197.95 899.956C214.362 908.302 231.174 913.595 249.238 916.836C267.027 920.029 285.174 921.088 303.161 921.573C311.42 921.796 319.679 921.891 327.941 921.941C337.748 922.001 347.554 921.997 357.361 921.997L470.006 922H555.217L667.644 921.996C677.432 921.996 687.22 922.001 697.008 921.941C705.253 921.891 713.495 921.796 721.738 921.573C739.698 921.087 757.816 920.027 775.579 916.832C793.597 913.591 810.368 908.3 826.739 899.959C842.831 891.761 857.554 881.051 870.32 868.272C883.086 855.497 893.786 840.763 901.978 824.66C910.316 808.268 915.604 791.475 918.844 773.431C922.034 755.661 923.092 737.535 923.577 719.566C923.8 711.316 923.895 703.066 923.944 694.815C924.005 685.019 924 675.223 924 665.427C924 665.427 923.994 554.983 923.994 552.998V466.999C923.994 465.533 924 354.627 924 354.627Z"
fill="url(#paint0_linear_102_69)"
id="path996" /></g><mask
id="mask0_102_69"
style="mask-type: alpha"
maskUnits="userSpaceOnUse"
x="100"
y="98"
width="824"
height="824"><path
fill-rule="evenodd"
clip-rule="evenodd"
d="M924 354.627C924 344.845 924.004 335.062 923.944 325.279C923.895 317.038 923.8 308.799 923.576 300.562C923.092 282.609 922.033 264.502 918.84 246.749C915.602 228.741 910.314 211.98 901.981 195.617C893.789 179.534 883.088 164.817 870.32 152.058C857.555 139.299 842.834 128.605 826.746 120.418C810.366 112.083 793.587 106.797 775.558 103.56C757.803 100.372 739.691 99.315 721.738 98.83C713.495 98.607 705.253 98.513 697.008 98.462C687.22 98.402 677.432 98.407 667.644 98.407L553.997 98H468.997L357.361 98.407C347.554 98.407 337.747 98.402 327.94 98.462C319.678 98.513 311.42 98.607 303.161 98.83C285.167 99.315 267.014 100.373 249.217 103.565C231.164 106.801 214.36 112.085 197.958 120.414C181.835 128.602 167.083 139.297 154.291 152.058C141.501 164.816 130.78 179.53 122.573 195.61C114.217 211.981 108.919 228.752 105.673 246.77C102.477 264.516 101.418 282.617 100.931 300.562C100.709 308.8 100.613 317.039 100.563 325.279C100.503 335.063 100 347.216 100 356.999L100.003 467.089L100 552.998L100.508 665.427C100.508 675.223 100.504 685.019 100.563 694.815C100.613 703.067 100.709 711.317 100.932 719.566C101.418 737.542 102.479 755.675 105.678 773.452C108.923 791.484 114.22 808.269 122.569 824.653C130.777 840.759 141.5 855.495 154.291 868.272C167.082 881.049 181.83 891.757 197.95 899.956C214.362 908.302 231.174 913.595 249.238 916.836C267.027 920.029 285.174 921.088 303.161 921.573C311.42 921.796 319.679 921.891 327.941 921.941C337.748 922.001 347.554 921.997 357.361 921.997L470.006 922H555.217L667.644 921.996C677.432 921.996 687.22 922.001 697.008 921.941C705.253 921.891 713.495 921.796 721.738 921.573C739.698 921.087 757.816 920.027 775.579 916.832C793.597 913.591 810.368 908.3 826.739 899.959C842.831 891.761 857.554 881.051 870.32 868.272C883.086 855.497 893.786 840.763 901.978 824.66C910.316 808.268 915.604 791.475 918.844 773.431C922.034 755.661 923.092 737.535 923.577 719.566C923.8 711.316 923.895 703.066 923.944 694.815C924.005 685.019 924 675.223 924 665.427C924 665.427 923.994 554.983 923.994 552.998V466.999C923.994 465.533 924 354.627 924 354.627Z"
fill="white"
id="path1000" /></mask><g
mask="url(#mask0_102_69)"
id="g1009"><rect
x="42"
y="36"
width="914"
height="914"
fill="url(#paint1_linear_102_69)"
id="rect1003" /><g
filter="url(#filter1_b_102_69)"
id="g1007"><rect
x="100"
y="98"
width="824"
height="824"
rx="126"
fill="black"
fill-opacity="0.01"
id="rect1005" /></g></g><defs
id="defs1068"><filter
id="filter0_d_102_69"
x="90"
y="98"
width="844"
height="844"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"><feFlood
flood-opacity="0"
result="BackgroundImageFix"
id="feFlood1025" /><feColorMatrix
in="SourceAlpha"
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
result="hardAlpha"
id="feColorMatrix1027" /><feOffset
dy="10"
id="feOffset1029" /><feGaussianBlur
stdDeviation="5"
id="feGaussianBlur1031" /><feColorMatrix
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0"
id="feColorMatrix1033" /><feBlend
mode="normal"
in2="BackgroundImageFix"
result="effect1_dropShadow_102_69"
id="feBlend1035" /><feBlend
mode="normal"
in="SourceGraphic"
in2="effect1_dropShadow_102_69"
result="shape"
id="feBlend1037" /></filter><filter
id="filter1_b_102_69"
x="89.1269"
y="87.1269"
width="845.746"
height="845.746"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"><feFlood
flood-opacity="0"
result="BackgroundImageFix"
id="feFlood1040" /><feGaussianBlur
in="BackgroundImage"
stdDeviation="5.43656"
id="feGaussianBlur1042" /><feComposite
in2="SourceAlpha"
operator="in"
result="effect1_backgroundBlur_102_69"
id="feComposite1044" /><feBlend
mode="normal"
in="SourceGraphic"
in2="effect1_backgroundBlur_102_69"
result="shape"
id="feBlend1046" /></filter><linearGradient
id="paint0_linear_102_69"
x1="-181.14"
y1="98"
x2="-181.14"
y2="1484.28"
gradientUnits="userSpaceOnUse"><stop
stop-color="white"
id="stop1049" /><stop
offset="0.489516"
stop-color="#EFEFEF"
id="stop1051" /><stop
offset="1"
stop-color="#C0C0C0"
id="stop1053" /></linearGradient><linearGradient
id="paint1_linear_102_69"
x1="928.377"
y1="992.826"
x2="928.377"
y2="134.072"
gradientUnits="userSpaceOnUse"><stop
stop-color="#F6F3F3"
id="stop1056" /><stop
offset="1"
stop-color="white"
id="stop1058" /></linearGradient><linearGradient
id="paint2_linear_102_69"
x1="394.815"
y1="372.239"
x2="629.182"
y2="542.528"
gradientUnits="userSpaceOnUse"><stop
stop-color="#88B858"
id="stop1061" /><stop
offset="0.5"
stop-color="#72B147"
id="stop1063" /><stop
offset="1"
stop-color="#5A9A30"
id="stop1065" /></linearGradient><inkscape:path-effect
effect="powerclip"
id="path-effect5638"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." /><inkscape:path-effect
effect="powerclip"
id="path-effect5562"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." /><inkscape:path-effect
effect="powerclip"
id="path-effect5535"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false"
message="Use fill-rule evenodd on &lt;b&gt;fill and stroke&lt;/b&gt; dialog if no flatten result after convert clip to paths." /></defs><g
id="g5269"
clip-path="none"
transform="matrix(10,0,0,10,192.005,170.0135)"><path
id="path352"
style="fill:#8acb01"
inkscape:label="head"
d="M 8,16 H 32.03161 V 52 H 8 Z m 22.020351,0 H 56 V 47.200001 H 30.020351 Z m 1.153153,0 h 12 v 33.599998 h -12 z"><title
id="title908">head</title></path><path
id="rect3493"
style="display:inline;fill:#b0af8d;stroke-width:1.39562"
inkscape:label="cheek"
d="M 19.959999,28.879064 H 32.253197 V 52 H 19.959999 Z" /><path
id="rect3491"
inkscape:label="eye"
d="M 19.960001,28 H 32.333299 V 40 H 19.960001 Z"
style="stroke-width:1.00871;fill:#000000;fill-opacity:1" /><path
id="path928"
style="display:inline;fill:#3b3b3b;fill-opacity:1;stroke-width:1.00922"
d="M 32,27.999999 H 56 V 47.555555 H 32 Z m 0,0 h 12 v 22 H 32 Z"
inkscape:label="beak" /></g></svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,13 @@
#!/usr/bin/env xdg-open
[Desktop Entry]
Version=1.0
Name=PollyMC
Comment=A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.
Type=Application
Terminal=false
Exec=@Launcher_APP_BINARY_NAME@
StartupNotify=true
Icon=org.fn2006.PollyMC
Categories=Game;
Keywords=game;minecraft;launcher;mc;
StartupWMClass=PollyMC

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<id>org.fn2006.PollyMC</id>
<provides>
<id>org.fn2006.PollyMC</id>
</provides>
<launchable type="desktop-id">org.fn2006.PollyMC.desktop</launchable>
<name>PollyMC</name>
<developer_name>fn2006C</developer_name>
<summary>A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once</summary>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0-only</project_license>
<url type="homepage">https://github.com/fn2006/PollyMC/</url>
<url type="help">https://polymc.org/wiki/</url>
<description>
<p>PollyMC is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.</p>
<p>Features:</p>
<ul>
<li>Easily install game modifications, such as Fabric, Forge and Quilt</li>
<li>Control your java settings</li>
<li>Manage worlds and resource packs from the launcher</li>
<li>See logs and other details easily</li>
<li>Kill Minecraft in case of a crash/freeze</li>
<li>Isolate minecraft instances to keep everything clean</li>
<li>Install and update mods directly from the launcher</li>
</ul>
</description>
<releases>
<release version="@Launcher_VERSION_NAME@" date="@Launcher_BUILD_TIMESTAMP@"></release>
</releases>
<content_rating type="oars-1.1">
<content_attribute id="violence-fantasy">moderate</content_attribute>
<content_attribute id="social-chat">intense</content_attribute>
</content_rating>
</component>

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