Compare commits

..

847 Commits

Author SHA1 Message Date
Neptune
c95dfe89e1 Merge branch 'MultiMC:develop' into develop 2024-12-13 14:58:50 -05:00
Petr Mrázek
6c53068f32 NOISSUE remove dedication
Ah well... One thing didn't work out and an another one did. That's life.
2024-12-12 20:52:13 +01:00
Neptune
6cad5f358b CI: Update CMake version 2024-10-07 12:46:50 -05:00
Neptune
0b9bfc95dd Merge pull request #376 from BigBoyPro/ipv6-fix
Small fix for IPV6 only + DDNS Server
2024-08-26 15:14:42 -05:00
BigBoyPro
eff51d6511 For the authlib injector authentification server: i changed localhost to 127.0.0.1 to accomodate people who want to use the -Djava.net.preferIPv6Addresses=true Java argument to play on IPV6 in priority because localhost defaults to ::1 in IPV6 mode and crashes the game on launch 2024-08-16 22:53:27 +02:00
Neptune
e537265ade UltimMC: Update MSA client ID. 2024-06-24 23:03:53 -05:00
Neptune
f16a8fd234 UltimMC: Fix macOS Actions 2024-06-01 08:22:09 -05:00
Neptune
65e8bfba6c UltimMC: Fix Actions 2024-06-01 08:19:01 -05:00
Neptune
52833d9921 Merge branch 'MultiMC:develop' into develop 2024-05-31 07:50:33 -05:00
TacoGS
bb04cb09a3 hotfix:Generate Java Version 21 Error in logs 2024-05-17 21:52:46 -03:00
Neptune
e17a648c2b Merge branch 'MultiMC:develop' into develop 2024-03-29 13:26:54 -05:00
Petr Mrázek
d4247bf3ae Merge pull request #5380 from TacoGS/hotfix/ftba-path
HOTFIX: Fixing FTB app change of settings location
2024-03-28 09:50:45 +01:00
TacoGS
07811bddd7 HOTFIX: Fixing FTB app change of settings location 2024-03-27 22:36:27 -03:00
Neptune
f2d1f98cae Merge branch 'MultiMC:develop' into develop 2024-03-21 17:09:27 -05:00
Petr Mrázek
056faf3307 Merge pull request #5367 from arthomnix/feature/neoforged
NOISSUE Fix incorrect NeoForge versions being shown when changing version
2024-03-20 21:11:03 +01:00
arthomnix
45ec0b690d NOISSUE Fix incorrect NeoForge versions being shown when changing version 2024-03-20 08:25:53 +00:00
Neptune
2bd0d5d1a9 Merge branch 'MultiMC:develop' into develop 2024-03-15 19:07:33 -05:00
Petr Mrázek
0912669610 Merge pull request #5365 from arthomnix/feature/neoforged
NOISSUE Add NeoForge support to mrpack importer and exporter
2024-03-15 23:21:54 +01:00
arthomnix
1b77965225 NOISSUE Add NeoForge support to mrpack importer and exporter 2024-03-15 20:39:32 +00:00
Neptune
e425abf8d0 UltimMC: Remove unused code from Ely.by profile 2024-03-12 21:21:32 -05:00
Neptune
51fff1f02c UltimMC: Use proper Ely.by profile 2024-03-11 20:58:42 -05:00
Neptune
60aead3255 UltimMC: Fix Windows icon 2024-03-11 17:47:11 -05:00
Neptune
294147b859 UltimMC: Possible fix for #335 2024-03-11 17:31:17 -05:00
Neptune
e5d10913d4 Merge branch 'MultiMC:develop' into develop 2024-03-10 12:54:11 -05:00
Petr Mrázek
d22707c7fc Try to import neoforged packs from FTB
Maybe this will work, maybe it will not.
It's better than importing them completely wrong though.
2024-03-10 04:41:30 +01:00
Neptune
7747f08952 UltimMC: Fix macOS CI build (#336) 2024-03-09 12:38:10 -05:00
Neptune
14981321d4 UltimMC: Fix ely.by auth 2024-03-08 17:47:09 -05:00
Neptune
3993d1227e UltimMC: Clarify forking in README 2024-03-08 16:36:12 -05:00
Neptune
26d041ec21 UltimMC: Use our MSA client ID 2024-03-08 16:30:20 -05:00
Neptune
334812f74d UltimMC: Remove now unnecessary step from README 2024-03-08 00:07:00 -05:00
Neptune
0b4b9a1783 UltimMC: Actually make profile for local accounts, fixes #334 2024-03-07 23:57:59 -05:00
Neptune
17a06d8eab UltimMC: Improve README 2024-03-07 19:18:59 -05:00
Neptune
82df3ac9ac UltimMC: Misc improvements all over the place 2024-03-06 08:38:16 -05:00
Neptune
c57d58f6f8 UltimMC: Change notsecrets to secrets 2024-03-06 08:08:58 -05:00
Neptune
f9fb2443ab UltimMC: Misc changes to readme files 2024-03-06 07:38:02 -05:00
Neptune
fb323d8a0b UltimMC: Make separate section for local accounts 2024-03-05 17:50:57 -05:00
Neptune
eba8c1e33f UltimMC: Update README to reflect new downloads 2024-03-05 17:33:29 -05:00
Neptune
026070b836 UltimMC: Fix Windows CI 2024-03-05 17:33:29 -05:00
Neptune
0d01261956 UltimMC: Add our changes 2024-03-03 11:42:23 -05:00
Petr Mrázek
2c9e7b75f4 Merge pull request #5323 from arthomnix/feature/neoforged
GH-5232 Add Install NeoForge button
2024-03-01 02:27:27 +01:00
Petr Mrázek
87852db4ae Merge pull request #5286 from arthomnix/xerr
NOISSUE Add more XErr error descriptions
2024-02-01 17:37:30 +01:00
Petr Mrázek
0b91a983e2 Merge pull request #5226 from Evaryan-MMO/issue-#5211
fix: issue #5211
2024-02-01 00:36:47 +01:00
arthomnix
4fe664204e NOISSUE correct first NeoForge supported version 2024-01-11 09:55:39 +00:00
arthomnix
bb8e02a2f2 NOISSUE Improve modloader support detection 2024-01-11 08:34:42 +00:00
arthomnix
b69ab34f4b GH-5232 Add Install NeoForge button (WIP) 2024-01-10 22:02:18 +00:00
arthomnix
98099c8ec5 NOISSUE Update help link for under-18 accounts 2023-10-28 20:54:00 +01:00
arthomnix
1bb7e3d0f9 NOISSUE Add more XErr error descriptions 2023-10-28 20:44:55 +01:00
Petr Mrázek
7450faaf13 NOISSUE fix unable to log in issue 2023-09-17 12:57:48 +02:00
Petr Mrázek
17865bfd7d NOISSUE ensure PROCESSOR_ARCHITECTURE_ARM64 is defined
This fixes build on old mingw versions
2023-09-03 00:42:26 +02:00
Petr Mrázek
da4973dc94 NOISSUE disable analytics until further notice
Or MultiMC 6.
2023-09-03 00:33:24 +02:00
Petr Mrázek
f458204a52 NOISSUE add some arch probing code 2023-09-02 11:15:32 +02:00
Petr Mrázek
9ec1d43565 NOISSUE consider classifier when comparing library names 2023-09-01 23:13:03 +02:00
ChrisjStone
18b6792cc7 fix: issue #5211
Move gcc compiler option -Werror to CMAKE_CXX_FLAGS_DEBUG to work around
cmake using any options present in CMAKE_CXX_FLAGS as default options
for all build types.

This fix was necessary to prevent gcc from erroring out with indetifier
'requires' being a keyword sicne we are using c++11 and not c++20.
2023-07-25 21:14:15 -06:00
Petr Mrázek
33a8b916a1 Merge pull request #5063 from arthomnix/feature/mrpack_export
NOISSUE Set Modrinth export dialog window title
2023-05-24 10:03:38 +02:00
Petr Mrázek
35783d9dd9 Merge pull request #5163 from arthomnix/feature/mrpack-export-optional-files
GH-5146 mrpack exporter: add option to treat disabled files as optional
2023-05-24 10:02:48 +02:00
Petr Mrázek
30f8f5c65f Merge pull request #5162 from arthomnix/quickplay-shortcut-fix
NOISSUE Fix shortcut dialog bugs
2023-05-24 10:02:01 +02:00
arthomnix
574571b51b GH-5146 mrpack exporter: add option to treat disabled files as optional 2023-05-23 20:56:34 +01:00
arthomnix
39780c7238 NOISSUE Fix release date check 2023-05-23 16:52:27 +01:00
arthomnix
72e62058c0 NOISSUE Connect signal in code instead of ui file 2023-05-23 16:47:27 +01:00
arthomnix
d6dabfa67d NOISSUE Only reload the pack profile if the cast to MinecraftInstance was successful
Prevents a potential segfault if the instance is not a MinecraftInstance
2023-05-22 17:59:26 +01:00
arthomnix
29c78b56fc NOISSUE reload pack profile in online mode so the Minecraft release date is loaded properly 2023-05-22 17:39:37 +01:00
arthomnix
e1b7630677 NOISSUE add copyright header back to ui file 2023-05-22 17:34:43 +01:00
arthomnix
44a4712e61 NOISSUE Fix shortcut dialog bugs
Fixes bugs in the create shortcut dialog where the OK button and server address text box were always disabled on instances that don't support Quick Play.
2023-05-22 17:30:02 +01:00
Petr Mrázek
bd95c3ea33 NOISSUE fix session not being passed to args processing 2023-05-17 10:55:42 +02:00
Petr Mrázek
d05a9a6a5f Merge pull request #5125 from arthomnix/feature/singleplayer-quickplay
NOISSUE Add support for launching worlds directly via Quick Play
2023-05-16 21:12:28 +02:00
Petr Mrázek
d2deb1821f Merge pull request #5157 from aetherstrata/develop
NOISSUE Use better wording in some text strings
2023-05-16 21:09:32 +02:00
Davide Pierotti
a003b8e867 NOISSUE Use better wording for log upload popup 2023-05-16 20:24:26 +02:00
Davide Pierotti
ea262c45ed Merge branch 'MultiMC:develop' into develop 2023-05-16 09:21:40 +02:00
Petr Mrázek
b0844db1df Create dispatch.yml 2023-05-14 23:17:06 +02:00
arthomnix
89ac58256c NOISSUE Fix --world argument when launcher already running 2023-04-10 21:14:12 +01:00
arthomnix
5ff03584a5 NOISSUE Hide the join world buttons properly
This also fixes `QAction::setVisible` not working correctly in other parts of the codebase
2023-04-09 19:18:31 +01:00
arthomnix
1ad3e4417f NOISSUE Add singleplayer quickplay option to shortcut creation dialog 2023-04-09 15:12:30 +01:00
arthomnix
b8811039f2 NOISSUE Update copyright dates 2023-04-09 14:03:20 +01:00
arthomnix
55edaa2039 NOISSUE Hide join world buttons on pre-23w14a instances 2023-04-09 13:49:24 +01:00
arthomnix
1014141608 NOISSUE revert default instance settings tab 2023-04-08 21:04:34 +01:00
arthomnix
f756b25993 NOISSUE formatting 2023-04-08 21:00:32 +01:00
arthomnix
724eb76dc4 NOISSUE fix typo 2023-04-08 20:58:13 +01:00
Petr Mrázek
c6c56e56bd Merge pull request #5124 from arthomnix/feature/shortcut-quickplay
NOISSUE Fix directly joining servers on 23w14a and above
2023-04-08 21:00:27 +02:00
arthomnix
71cf4f8d18 NOISSUE Add support for launching worlds directly via Quick Play 2023-04-08 18:03:20 +01:00
arthomnix
22f82c34bf NOISSUE Add support for joining servers via Quick Play 2023-04-08 10:29:12 +01:00
Petr Mrázek
c1ed09e747 NOISSUE add an interface to pass information to CraftPresence
Specifically, the icon key and instance title.
2023-03-10 19:56:17 +01:00
arthomnix
f57ac946fd NOISSUE Set Modrinth export dialog window title 2023-02-08 17:06:24 +00:00
arthomnix
4c216753ba NOISSUE Remove some unused code 2023-02-06 16:29:24 +00:00
Petr Mrázek
d98022d3ae Merge pull request #5060 from arthomnix/feature/mrpack_export
NOISSUE Fix build on Qt 5.4 again
2023-02-06 09:17:12 +01:00
arthomnix
413397c3c1 NOISSUE Fix build on Qt 5.4 again 2023-02-06 07:36:34 +00:00
Petr Mrázek
34f8de4682 Merge pull request #5059 from arthomnix/feature/mrpack_export
NOISSUE Modrinth exporter: lookup all hashes in one API request
2023-02-06 03:10:25 +01:00
arthomnix
4f95e4618f NOISSUE Modrinth exporter: lookup all hashes in one request 2023-02-05 21:09:16 +00:00
Petr Mrázek
f9c2f818fe Merge pull request #5058 from arthomnix/feature/mrpack_export
NOISSUE Handle Modrinth files with multiple downloads
2023-02-05 19:16:42 +01:00
arthomnix
496440cbc8 NOISSUE Handle Modrinth files with multiple downloads 2023-02-05 17:36:44 +00:00
Petr Mrázek
67c140ce5c Merge pull request #5056 from arthomnix/feature/mrpack_export
NOISSUE Fix typo in Modrinth exporter
2023-02-05 17:51:13 +01:00
arthomnix
bea3251d9c NOISSUE Fix typo in Modrinth exporter 2023-02-05 16:44:44 +00:00
Petr Mrázek
3840684a54 Merge pull request #5055 from arthomnix/feature/mrpack_export
NOISSUE Fix build on Qt 5.4 (hopefully)
2023-02-05 17:35:26 +01:00
arthomnix
c7b0d15393 NOISSUE Fix build on Qt 5.4 (hopefully) 2023-02-05 16:33:20 +00:00
Petr Mrázek
e50cb5caa4 Merge pull request #5052 from arthomnix/feature/mrpack_export
GH-4699 Modrinth pack exporter
2023-02-05 17:18:10 +01:00
arthomnix
e463edb185 GH-4699 Warn when exporting instance with custom components 2023-02-05 15:55:49 +00:00
arthomnix
82dde9f426 Update copyright years 2023-02-05 14:48:28 +00:00
arthomnix
aae2f23eb6 GH-4699 Move ModrinthInstanceExportTask to modplatform/modrinth; truncate error messages in dialog 2023-02-05 14:28:18 +00:00
arthomnix
a452b7ee96 GH-4699 Set Modrinth exporter task status 2023-02-05 12:33:56 +00:00
arthomnix
01f3511e88 GH-4699 Improve export instance menu 2023-02-05 12:11:27 +00:00
arthomnix
a1f256a745 GH-4699 Add global datapacks support to Modrinth exporter 2023-02-05 12:03:00 +00:00
arthomnix
a6dff61ff7 GH-4699 Formatting 2023-02-05 10:15:29 +00:00
arthomnix
74addfb78b GH-4699 Clean some things up
Add a menu to select between MMC/Modrinth format packs instead of the custom dialog
Treat 404s on requests to the Modrinth API as success, as the API returns a 404 if a hash was not found, and we don't want to retry the download in this case
Improve logging
2023-02-05 10:13:13 +00:00
arthomnix
16cf56b7a4 GH-4699 Modrinth pack exporter (WIP) 2023-02-04 21:41:24 +00:00
Petr Mrázek
867d240a2f NOISSUE update copyright year a bit 2023-02-03 23:30:59 +01:00
Petr Mrázek
458944ad91 NOISSUE Remove concept of switching update channels
It is all develop from now on, we no longer make stable releases.
This means no maintenance of version numbers and removal
of all the overhead associated with making stable releases.

MultiMC 6 might have a better system, but with how infrequent and stable
MultiMC releases are getting, there's no need to have a distinction
between `stable` and `develop` anymore.
2023-02-03 23:05:27 +01:00
Petr Mrázek
75568ed04b NOISSUE and don't break the thing in the process... 2023-02-02 21:43:01 +01:00
Petr Mrázek
f624754f0c NOISSUE Improve path parsing for modrinth packs 2023-02-02 20:51:23 +01:00
Petr Mrázek
1f1cdbe0a8 Merge pull request #5032 from graytoowolf/patch-1
When re-logging in when the repair account expires, it still prompts that the account has expired
2023-01-13 18:44:38 +01:00
graywolf
cb81cb9835 Update LaunchController.cpp 2023-01-06 12:59:45 +08:00
Petr Mrázek
aba63c8f00 Merge pull request #4998 from arthomnix/feature/account-expired-login-button
GH-4978 Add button to log back in when account expired
2022-12-01 23:48:36 +01:00
arthomnix
73e1d15ab9 GH-4978 Correctly emit failed when attempting to relogin to msa on osx64 2022-12-01 20:54:04 +00:00
arthomnix
e5b7517d2f GH-4978 Remove testing code 2022-12-01 20:50:36 +00:00
arthomnix
0fd546a11c GH-4978 Add button to log back in when account expired 2022-12-01 20:21:37 +00:00
Petr Mrázek
e044744faf NOISSUE add a way to require object from Json value ref 2022-11-20 15:59:52 +01:00
Petr Mrázek
8bced9da83 Merge pull request #4985 from arthomnix/feature/playtime-format-customisation
NOISSUE Load ShowGameTimeHours setting correctly on settings page
2022-11-20 13:08:52 +01:00
arthomnix
149adbd1d6 NOISSUE Load ShowGameTimeHours setting correctly on settings page
Bugfix for #4964
2022-11-20 11:45:34 +00:00
Petr Mrázek
30312ea701 And fix a build issue 2022-11-20 00:12:46 +01:00
arthomnix
858487521e NOISSUE Escape quotes in paths
Just in case the user decides to place MMC in a path containing quotes.
.desktop files appear to require two backslashes to escape quotes, testing on other desktop environments would be appreciated to make sure this isn't just a KDE-specific bug
2022-11-20 00:04:03 +01:00
arthomnix
08dd08afc1 NOISSUE Enclose all arguments in quotes, fix batch scripts 2022-11-20 00:04:03 +01:00
arthomnix
6307689cf1 NOISSUE Use .command extension for shortcut scripts on macOS
This means that the script will run when clicked, instead of being opened in a text editor
2022-11-20 00:04:03 +01:00
arthomnix
86f68389c9 NOISSUE Add button to copy MSA code 2022-11-20 00:04:03 +01:00
Petr Mrázek
7354c578fd NOISSUE Move hour formatting for play time to a function 2022-11-20 00:01:21 +01:00
Petr Mrázek
90b16fb903 Merge pull request #4928 from ryanbrown535/develop
GH-4901 Confirm screenshot upload
2022-11-19 23:48:46 +01:00
Petr Mrázek
8335432543 Merge pull request #4947 from jamierocks/managed-packs
Record where packs come from
2022-11-19 23:48:16 +01:00
Petr Mrázek
319ba3c8f9 Merge pull request #4948 from jamierocks/technic-improvements
Technic: Display available versions for Solder packs
2022-11-19 23:44:24 +01:00
Petr Mrázek
9e5883c173 Merge pull request #4946 from jamierocks/atl-colours
Display mod colours in optional mod dialog
2022-11-19 23:43:06 +01:00
Petr Mrázek
597da783d7 Merge pull request #4964 from arthomnix/feature/playtime-format-customisation
NOISSUE Add setting to display playtime in hours only
2022-11-19 23:35:33 +01:00
Petr Mrázek
1849db93ec Fix a build problem 2022-11-19 23:23:11 +01:00
arthomnix
b82d667859 NOISSUE Add setting to display playtime in hours only 2022-11-02 16:11:56 +00:00
Jamie Mansfield
9bdfa5c8de NOISSUE Make Technic API base URL and build constants 2022-10-20 16:46:12 +01:00
Jamie Mansfield
fb0970b496 NOISSUE Verify checksums for pack build mods 2022-10-20 16:46:12 +01:00
Jamie Mansfield
b6290ac254 GH-3516 Display available versions for Solder packs 2022-10-20 16:46:12 +01:00
Jamie Mansfield
b8a736c673 NOISSUE Replace inline parsing code with Solder API models 2022-10-20 16:46:03 +01:00
Jamie Mansfield
18f790953a NOISSUE Add API models for Solder packs
This will be able to replace the ugly parsing/usage code that
currently exists in the Solder install task :)
2022-10-20 16:46:03 +01:00
Jamie Mansfield
2334a44221 NOISSUE Match CurseForge pack description format in Technic UI 2022-10-20 16:46:03 +01:00
Jamie Mansfield
65a4f8919a NOISSUE Include the modpack version in instance title 2022-10-20 16:46:03 +01:00
Jamie Mansfield
ddc094b76b NOISUE Prevent potential HTML injection 2022-10-20 16:46:03 +01:00
Jamie Mansfield
e35f2b6c2c NOISSUE Allow Technic pack API urls to be used in search
This mimics the behaviour that the Technic launcher has, and their
website displays API URLs for.

The big benefit of this, is to be able to install private packs now :)
2022-10-20 16:45:55 +01:00
Jamie Mansfield
064c0febd3 NOISSUE Make ATLauncher packs managed when installing 2022-10-19 16:56:26 +01:00
Jamie Mansfield
79204e5df0 NOISSUE Pass the full pack name through to the install task 2022-10-19 16:56:15 +01:00
Jamie Mansfield
79cd37be94 NOISSUE Add settings to support managed packs
Managed packs means an installation of a modpack through a modpack
provider. Managed packs track their origins (pack platform, name, id),
so that in future features can exist around this - such as updating, and
reinstalling.
2022-10-19 16:52:37 +01:00
Jamie Mansfield
dbe7d9ea2e NOISSUE Display mod colours in optional mod dialog 2022-10-19 13:00:54 +01:00
Jamie Mansfield
b433882ac1 NOISSUE Add missing QMap include
This should fix the build.
2022-10-19 12:51:15 +01:00
Petr Mrázek
301b44d1c4 Merge pull request #4943 from jamierocks/atl-messages
Display ATLauncher mod warnings and pack messages
2022-10-18 23:11:53 +02:00
Jamie Mansfield
c24a89f3af NOISSUE Display warnings when selecting optional mods 2022-10-18 21:40:05 +01:00
Jamie Mansfield
41f728b22f NOISSUE Pass the optional mod dialog the full version
We will need more information, let's just pass the whole thing.
2022-10-18 21:39:37 +01:00
Jamie Mansfield
af36e5c43f NOISSUE Display ATLauncher install messages 2022-10-18 21:39:36 +01:00
ryanbrown535
c37b7f771e Confirm screenshot upload
Adds a message box on upload asking if the user is sure they want to upload to imgur
2022-10-02 20:45:06 -04:00
Petr Mrázek
518568b803 Merge pull request #4911 from p2js/patch-1
Update README.md
2022-09-19 22:37:10 +02:00
Davide Pierotti
b069478f6e NOISSUE Consistently express 'exit code' in logs 2022-09-17 15:42:02 +02:00
Alfio
8d100ba97c Update README.md
fixed a couple of typos and reformatted some stuff to be a little more legible.
2022-09-11 16:59:00 +02:00
Petr Mrázek
6bfa07e728 Merge pull request #4891 from ajakk/develop
Drop execute bits from .desktop file
2022-08-25 09:34:03 +02:00
John Helmert III
5488ab11d4 Drop execute bits from .desktop file
There shouldn't be any reason for the desktop file to be executable,
and this is sometimes against policy in downstream distributions:

https://wiki.gentoo.org/wiki/.desktop_files#Executable_bit_in_.desktop_files

Bug: https://bugs.gentoo.org/866443
Signed-off-by: John Helmert III <ajak@gentoo.org>
2022-08-24 23:00:51 -05:00
Petr Mrázek
b5e81bbb0d Merge pull request #4842 from arthomnix/shortcut-path-fix
NOISSUE Fix shortcut creation on official Linux builds
2022-07-19 21:54:35 +02:00
Petr Mrázek
ec498074c1 NOISSUE improve Modrinth description's page rendering 2022-07-19 21:46:28 +02:00
arthomnix
2517671396 NOISSUE Use current working directory instead of applicationDirPath in shortcut creation
This fixes issues with official Linux builds which place the executable in its own bin directory
2022-07-19 20:43:54 +01:00
Petr Mrázek
e5c962b7b9 NOISSUE acknowledge the truth 2022-07-19 19:04:29 +02:00
Petr Mrázek
b88296029b Merge pull request #4841 from arthomnix/create-shortcut-old-qt-fix
NOISSUE Yeet the version check for shortcut creation
2022-07-19 18:32:49 +02:00
arthomnix
844ed61aa4 NOISSUE Yeet the version check 2022-07-19 17:14:46 +01:00
Petr Mrázek
bec8293f28 Merge pull request #4808 from arthomnix/feature-create-shortcut
NOISSUE (Re-)implement the ability to create instance shortcuts
2022-07-19 17:25:35 +02:00
Petr Mrázek
cf49f171b3 Merge pull request #4828 from arthomnix/feature-prefill-suggested-name
GH-4812 Prefill instance name to allow making adjustments
2022-07-19 17:23:55 +02:00
Petr Mrázek
05e3910fbd Merge pull request #4830 from kb-1000/unwrap-invocationtargetexception
[NOISSUE] Unwrap InvocationTargetException and allow non-public main classes
2022-07-19 17:23:16 +02:00
kb1000
81b7e5f769 [NOISSUE] Unwrap InvocationTargetException and allow non-public main classes 2022-07-14 16:45:41 +02:00
arthomnix
ffec1e1930 GH-4812 Set placeholder even if the user has changed the name 2022-07-11 19:55:36 +01:00
arthomnix
ec897aee95 GH-4812 More improvements related to instance name
Selects text on focus rather than selecting text and focusing by default. Text is not selected if the user has changed the name from the default.
If the user changes the instance name, don't change it when they select a new version or modpack.
Add a reset button that changes the instance name back to the default for the selected version/pack, and resets the flag that stops the name from being changed upon selecting a new version/pack.
2022-07-11 19:51:31 +01:00
arthomnix
00589b247a GH-4812 Prefill instance name to allow making adjustments
Fills the instance name in instead of just setting a placeholder. This allows adjustments to be made to the suggested name without typing the whole thing out.
The text is selected by default so that typing will overwrite the text, but users who want to adjust the default name instead of typing their own can deselect the text.
The placeholder name is still set so it is still visible if the user deletes the text.
Also sets the focus to the instance name textbox by default, whereas previously it was on the group name - this is required so the text gets overwritten on typing but also makes more sense generally.
Closes issue #4812.
2022-07-11 18:02:20 +01:00
Petr Mrázek
ff64d2ad59 Merge pull request #4822 from xcfrg/fix/mods-ambiguity
NOISSUE log file extension of mods to clear ambiguity
2022-07-09 11:37:26 +02:00
xcfrg
346496c3fd fix: log file extension of mods to clear ambiguity 2022-07-08 23:28:03 -04:00
arthomnix
518b19e667 NOISSUE shortcut creation: enclose paths in quotes 2022-07-06 08:03:04 +01:00
Petr Mrázek
0f41bea58a Merge pull request #4816 from xcfrg/log-confirmation
NOISSUE add log upload confirmation
2022-07-06 08:51:33 +02:00
xcfrg
3621594213 feat: add log upload confirmation 2022-07-05 23:33:04 -04:00
arthomnix
79910e3542 NOISSUE shortcut creation: put file dialog in save mode
This makes sure that the user is prompted if they are about to overwrite a file
2022-07-03 19:53:45 +01:00
arthomnix
0a2ad17f06 NOISSUE shortcut creation: add comments and todo 2022-07-03 18:11:18 +01:00
arthomnix
7df413db1a NOISSUE shortcut creation: blacklist versions by date instead of regex 2022-07-03 16:57:20 +01:00
arthomnix
6faa0ef711 NOISSUE shortcut creation: reload pack profile before checking version 2022-07-03 16:43:23 +01:00
arthomnix
363588789e NOISSUE shortcut creation: fix version check 2022-07-03 16:30:30 +01:00
arthomnix
2dc44b3ff5 NOISSUE fix build 2022-07-03 16:21:21 +01:00
arthomnix
7938585abb NOISSUE shortcut creation: add version blacklist for joining server on launch
These versions are known to crash when joining a server on launch (see MC-145102 and MC-228828)
2022-07-03 15:47:54 +01:00
arthomnix
bbdbe47e72 NOISSUE actually use the directory as the working directory 2022-07-03 14:19:50 +01:00
arthomnix
75b8765604 NOISSUE shortcut creation: set working directory in other shortcut types 2022-07-03 14:00:55 +01:00
arthomnix
7217e3991a NOISSUE shortcut support: set working directory in .desktop files 2022-07-03 13:45:58 +01:00
arthomnix
bf5be5568e NOISSUE curly brace formatting 2022-07-03 13:17:25 +01:00
arthomnix
018e6229ca NOISSUE shortcut creation: add windows .lnk support 2022-07-03 13:07:21 +01:00
arthomnix
21413b964a NOISSUE replace copyright header that got removed by qt designer 2022-07-03 09:56:16 +01:00
arthomnix
6a3ff58c8c NOISSUE shortcut creation: add option to create launch scripts
This allows shortcuts to be created on Macs (which don't have a concept of desktop shortcuts) as well as Linux systems that don't support the desktop file specification. Also included a windows batch file implementation.
2022-07-03 09:52:56 +01:00
arthomnix
645bc3f445 NOISSUE shortcut creation: set default shortcut filename 2022-07-03 08:33:46 +01:00
arthomnix
a0c44f7062 NOISSUE shortcut creation: add icons on linux 2022-07-03 08:27:13 +01:00
arthomnix
6c31125f02 NOISSUE fix arrangement of includes again 2022-07-03 07:57:15 +01:00
arthomnix
b326fef61b NOISSUE fix arrangement of includes 2022-07-03 07:56:25 +01:00
arthomnix
5d14dede50 NOISSUE WIP implementation of the ability to create instance shortcuts
Currently Linux-only and lacking some features
2022-07-03 07:54:20 +01:00
Petr Mrázek
bf80bd1143 NOISSUE implement handling of client-overrides for Modrinth 2022-06-13 23:41:47 +02:00
Petr Mrázek
0917706b22 GH-4724 set suggested version for Modrinth packs when it is changed 2022-06-11 17:44:51 +02:00
Petr Mrázek
7e63ddf7d4 NOISSUE Bump version 2022-06-11 13:21:44 +02:00
Petr Mrázek
583b63e90e NOISSUE fix typo in changelog 2022-06-10 02:10:07 +02:00
Petr Mrázek
c6b60969bb NOISSUE update changelog for 0.6.16 2022-06-09 23:50:30 +02:00
Petr Mrázek
c7256744c6 GH-3012 add --offline and --name arguments
These allow launching offline with a specified name from the command line.
2022-06-09 23:46:28 +02:00
Petr Mrázek
8235752dec NOISSUE stop marking Modrinth as WIP
Even though it might need more changes, it should be good to go.
2022-06-09 20:17:49 +02:00
Petr Mrázek
dd277cba08 NOISSUE properly assign license to Mojang binary package code 2022-06-06 22:12:21 +02:00
Petr Mrázek
fdedf7078e NOISSUE Add minimal thing for clang-tidy integration 2022-06-06 22:08:31 +02:00
Petr Mrázek
fe75a7f09e NOISSUE slightly improve Forge detection in FTBA import 2022-06-05 00:09:28 +02:00
Petr Mrázek
305157746a NOISSUE Do not log Mojang token response 2022-06-05 00:08:49 +02:00
Petr Mrázek
214d615d18 NOISSUE significantly more reliable detection of modloaders for FTBA 2022-05-30 01:38:19 +02:00
Petr Mrázek
66c6e6e05d NOISSUE disambiguate Json parsing calls 2022-05-29 04:58:42 +02:00
Petr Mrázek
f59822fd65 NOISSUE make FTBA import more lenient towards missing fields 2022-05-29 04:24:55 +02:00
Petr Mrázek
43ebd02dcf NOISSUE it's LOCALAPPDATA not APPDATALOCAL... 2022-05-29 00:27:28 +02:00
Petr Mrázek
8a6f673567 NOISSUE maybe fix FTBA path on Windows? 2022-05-29 00:00:38 +02:00
Petr Mrázek
efe181bd28 NOISSUE maybe fill in correct FTB App settings paths 2022-05-28 23:23:45 +02:00
Petr Mrázek
41e5b3b628 NOISSUE Add FTB App import page to new instance dialog 2022-05-28 22:42:09 +02:00
Petr Mrázek
399247a018 Merge pull request #4714 from jamierocks/h-atl-depends
NOISSUE Handle depends for main class and extra arguments for ATLauncher
2022-05-23 22:52:10 +02:00
Jamie Mansfield
0327a3cdbf NOISSUE Delete files from configs if they conflict with a mod 2022-05-23 21:40:14 +01:00
Jamie Mansfield
c88f147ae4 NOISSUE Handle extra arguments depends for ATLauncher 2022-05-23 20:42:37 +01:00
Jamie Mansfield
f9924af2a0 NOISSUE Handle main class depends for ATLauncher 2022-05-23 20:42:23 +01:00
Petr Mrázek
edc351c0d0 Merge pull request #4706 from TrymDev/patch-1
Typo in LoggedProcess.cpp
2022-05-22 16:39:49 +02:00
Trym
3c815b8b8d Update LoggedProcess.cpp
#4704
2022-05-22 00:45:34 +02:00
Petr Mrázek
66e165f4b8 Merge pull request #4496 from Janrupf/develop
NOISSUE Attempt to make exit codes more useful on Windows
2022-05-20 20:47:41 +02:00
Janrupf
aebd9aa745 NOISSUE Add extra warning to exit code logging 2022-05-20 20:28:25 +02:00
Janrupf
cbe6d0dbfd NOISSUE Move NtStatusGen to external repository 2022-05-20 19:59:09 +02:00
Petr Mrázek
0a827ba70e NOISSUE Remove FTB and Curse integration
This has been requested by Slowpoke, on behalf of both FTB and OverWolf.

Import from locally installed packs from the official clients will be
the replacement, but for now, you will have to do that manually.

It was nice while it lasted.
2022-05-19 20:43:50 +02:00
Petr Mrázek
3304798aa7 Merge pull request #4692 from jamierocks/h-4b59804
NOISSUE Apply the Minecraft version correctly
2022-05-17 22:12:51 +02:00
Jamie Mansfield
78003c5eed NOISSUE Apply the Minecraft version correctly
It was previously using a deprecated field.
2022-05-17 21:08:31 +01:00
Petr Mrázek
585c93fd7f Merge pull request #4662 from Scotsguy/develop
NOISSUE Mod metadata parsing for Quilt mods
2022-05-17 21:21:31 +02:00
Petr Mrázek
cfb6ac8a8f Merge pull request #4691 from jamierocks/h-d4b8b77
NOISSUE Match Vanilla launcher for launch arguments
2022-05-17 21:01:16 +02:00
Jamie Mansfield
a28adf7b62 NOISSUE Match Vanilla launcher for launch arguments 2022-05-17 18:51:19 +01:00
Petr Mrázek
3e1939de8e NOISSUE fix build 2022-05-17 00:30:12 +02:00
Petr Mrázek
ceaea5db2f NOISSUE Redo Curse import on top of modpacks.ch API 2022-05-17 00:22:23 +02:00
Jamie Mansfield
40e67d2bc6 NOISSUE Use ModpacksCH rather than FTB in error messages
As the platform will now be used more widely than Feed The Beast, its
more appropriate that these error messages use the platform name.
2022-05-16 01:50:02 +02:00
Jamie Mansfield
f7acde4389 NOISSUE Support installing CurseForge packs on modpacks.ch
There should be all the needed changes to install CurseForge modpacks
through modpacks.ch in place now.

Now the effort will need to move towards getting the GUI aspects to play
nice.
2022-05-16 01:50:02 +02:00
Jamie Mansfield
8c1d95b484 NOISSUE Support parsing CurseForge modpacks on modpacks.ch
CurseForge packs don't have specs, and files versions are integers.
2022-05-16 01:50:02 +02:00
Jamie Mansfield
863cf3807e NOISSUE Support cf-extract file type on modpacks.ch
This makes a start on supporting CurseForge pack installation through
the modpacks.ch API.
2022-05-16 01:50:02 +02:00
Petr Mrázek
cad522fe41 NOISSUE add Quilt support for Technic modpacks 2022-05-16 01:42:30 +02:00
Petr Mrázek
9d255901bc NOISSUE update Modrinth logo 2022-05-16 01:38:15 +02:00
Petr Mrázek
f948fd82ab NOISSUE fix build on Windows 2022-05-16 00:39:13 +02:00
Petr Mrázek
9c4fcb826d NOISSUE fix build on Linux 2022-05-16 00:37:29 +02:00
Petr Mrázek
5c1026bd12 NOISSUE Working import from Modrinth, license update to accomodate it 2022-05-16 00:25:36 +02:00
kb1000
acc637cfcb Start working on search 2022-05-12 00:20:35 +02:00
kb1000
831b76bb78 Add support for importing Modrinth packs from files 2022-05-11 23:51:33 +02:00
AppleTheGolden
6ce93de8c0 NOISSUE Mod metadata parsing for quilt.mod.json 2022-04-30 11:56:51 +02:00
Petr Mrázek
f45f831736 Merge pull request #4648 from WowbaggersLiquidLunch/patch-1
NOISSUE Add microphone entitlement for macOS build
2022-04-24 19:44:13 +02:00
Petr Mrázek
a8b3d470da Merge pull request #3760 from kb-1000/quilt
GH-3742 add Install Quilt button
2022-04-23 18:34:52 +02:00
Petr Mrázek
b39410a2c2 GH-4317 Detect forced migration state and show errors for it 2022-04-23 01:31:03 +02:00
冀卓疌
208c83e1df add microphone entitlement for macOS build
Added the `NSMicrophoneUsageDescription` entitlement key, along with an explanation message for this entitlement.
2022-04-18 02:06:12 +08:00
Petr Mrázek
ef1bf57b58 NOISSUE fix build 2022-04-06 23:15:39 +02:00
Petr Mrázek
6afff3e31f NOISSUE prevent -version being passed to the JRE
We want specific JREs, always, not something that is magically resolved.
This counteracts some really bad advice recently being spread on reddit.
2022-04-06 22:47:52 +02:00
Petr Mrázek
e4d8ae582d NOISSUE stop optimizing in debug builds, bump to 0.6.16 2022-04-06 22:47:52 +02:00
Petr Mrázek
1f6a1c7fee Merge pull request #4609 from vlaetansky/feature/save_offline_player_name
NOISSUE save custom offline player name
2022-03-28 21:31:45 +02:00
Vladislav Laetansky
40d77bc265 NOISSUE save custom offline player name 2022-03-28 15:40:34 +03:00
Petr Mrázek
2a2b18ddff NOISSUE update changelog and version for 0.6.15 2022-03-20 21:04:07 +01:00
Petr Mrázek
edeffef51d NOISSUE fix error string for Xbox authorization failures 2022-03-18 17:59:50 +01:00
kb1000
533cd09057 NOISSUE Rename Quilt intermediary references to hashed 2022-03-16 20:21:30 +01:00
kb1000
b2e18253a1 GH-3742 add Install Quilt button 2022-03-16 20:21:19 +01:00
Petr Mrázek
ed99c6a0dc Merge pull request #4529 from jamierocks/lh-2
NOISSUE Add missing licence headers to files I've authoured
2022-03-06 18:28:54 +01:00
Petr Mrázek
ae8667726e Merge pull request #4526 from jopejoe1/develop
NOISSUE Change Technic and FTB legacy layout to be more consistent
2022-02-22 00:53:13 +01:00
Jamie Mansfield
cbcd822e44 NOISSUE Add missing licence headers to files I've authoured 2022-02-21 19:38:41 +00:00
jopejoe1
0fa6c7fbbf Drop MS-PL 2022-02-21 04:15:21 +13:00
jopejoe1
68fbbf0156 Update FTB Lagacy layout tobe more consistent with others 2022-02-21 03:53:27 +13:00
jopejoe1
da36a5f8de Update Technic layout to be more consistent with others 2022-02-21 03:50:37 +13:00
Janrupf
918090e02a NOISSUE Integrate exit code reporting into MultiMC log 2022-02-06 21:27:15 +01:00
Janrupf
022aee7729 NOISSUE Implement status code lookup for Win32 2022-02-06 18:59:58 +01:00
Janrupf
a2f0cc29de NOISSUE Add ntstatus-gen to systeminfo library 2022-02-06 18:49:54 +01:00
Petr Mrázek
3ca661127f NOISSUE Add missing tooltip for Export Instance action 2022-02-03 18:09:51 +01:00
Petr Mrázek
f8c5d80c66 Merge pull request #4480 from Oreoezi/develop
Update BUILD.md
2022-02-01 22:33:45 +01:00
Oreoezi
eda06df878 Update BUILD.md 2022-02-01 20:33:38 +00:00
Petr Mrázek
2cf04d034a Merge pull request #4300 from Ghosty141/feature/screenshot_copy
GH-4044 Implemented copy screenshots to the clipboard
2022-01-30 16:11:47 +01:00
Petr Mrázek
049aafd0a1 Merge pull request #4461 from Jan200101/rpm
Update rpm spec to support OpenSuse and conform to Fedora guidelines
2022-01-30 16:07:51 +01:00
Jan200101
3aa9f5c376 Update rpm spec to support OpenSuse and conform to Fedora guidelines 2022-01-28 19:42:30 +01:00
Petr Mrázek
63d4486855 Merge pull request #4447 from Stypox/patch-1
Fix error message
2022-01-24 10:32:14 +01:00
Stypox
8b31c638f3 Fix error message
The code is trying to get a string from a json object, and if that fails it should log "is not a string", not "is not a timestamp".
2022-01-22 21:58:32 +01:00
Petr Mrázek
63098b6f19 Merge pull request #4428 from MandipJoshi/patch-1
Update README.md copyright date changed
2022-01-17 10:00:40 +01:00
MandipJoshi
9a33fb1f49 Update README.md 2022-01-17 12:09:36 +05:45
Petr Mrázek
917f8a31e3 NOISSUE log server response when failing to fetch profile 2022-01-16 12:51:42 +01:00
Petr Mrázek
aa770b63fb NOISSUE correctly set http status code in auth reply 2022-01-16 12:46:20 +01:00
Petr Mrázek
c1bf31cb27 NOISSUE in java checker, ignore invalid lines altogether
Declaring them as errors is just causing problems because Java
randomly prints garbage to STDOUT now.
2022-01-16 12:05:40 +01:00
Petr Mrázek
86d99f80c3 NOISSUE add some logging to profile fetching failures 2022-01-16 11:43:19 +01:00
Petr Mrázek
52420963cf GH-4125 fix it better 2022-01-08 12:26:16 +01:00
Petr Mrázek
addf5f4e52 NOISSUE update README for clarity 2022-01-08 11:45:10 +01:00
Petr Mrázek
03d7300732 GH-4125 workaround for java printing garbage to stdout on bedrock linux 2022-01-08 11:14:07 +01:00
Petr Mrázek
9579231ccc NOISSUE fix build and change how NetJob is used
Feed it network upfront...
2021-12-31 05:27:59 +01:00
Petr Mrázek
9cc168c526 NOISSUE fix some OS ifdefs 2021-12-31 00:36:25 +01:00
Petr Mrázek
94fdf13f4a NOISSUE proper fix for missing profile + demo mode 2021-12-30 21:26:29 +01:00
Petr Mrázek
3efcccf334 Merge pull request #4345 from graemeg/freebsd-support
Adds FreeBSD support to MultiMC
2021-12-30 20:00:45 +01:00
Petr Mrázek
5e909a4e85 Merge pull request #4394 from Janrupf/develop
GH-4299 Fix Screenshot upload
2021-12-30 19:58:16 +01:00
Petr Mrázek
e7b90a9a3c Merge pull request #4396 from Bildcraft1/develop
BUILD.MD: Changed from "Install Xcode" to install Xcode Command-Line tools
2021-12-30 19:25:31 +01:00
Petr Mrázek
a6e59cb4f4 NOISSUE detect when user loses entitlements 2021-12-30 17:19:41 +01:00
WhiXard
1f21d84fdc Update BUILD.md 2021-12-29 21:40:45 +01:00
Janrupf
be029ab360 GH-4299 Fix screenshot upload 2021-12-29 19:45:24 +01:00
Janrupf
6fe07561fe GH-4299 Don't hard crash on when missing network 2021-12-29 19:45:15 +01:00
Graeme Geldenhuys
f42c3a953c Restore getNativePath() to its original form. 2021-12-28 21:59:24 +00:00
Petr Mrázek
4063a496d7 Merge pull request #4392 from iGerman00/patch-1
BUILD.md - Clearer differences with binaries
2021-12-28 02:58:19 +01:00
iGerman00
01e4e62de3 BUILD.md - Clearer differences with binaries
Coming from personal inconvenience - I've tried to build this from source, and was faced with Microsoft accounts not working. Upon re-reading the READMEs, I didn't find a reason for it, so I resorted to messaging in the Discord, which, after some back and forth, allowed me to understand that additional private stuff is needed for it to work. The question could've been avoided entirely, in my opinion, if this was included in BUILD.md - the place where anyone intending to build from source will come for guidance.

(Not) Secrets should've been linked clearly because it's a crucial piece of info for someone building from source, since normally. I'd expect the source code to fully reflect the pre-built binaries.
2021-12-28 05:59:44 +05:00
Petr Mrázek
b1b615e17f Merge pull request #4379 from CreeperzEdge/develop
Use correct title on Java check during setup
2021-12-27 17:44:26 +01:00
CreeperzEdge
95c9a6d8f4 Use correct title on Java check during setup 2021-12-23 00:53:02 +11:00
Petr Mrázek
3dd6cea8da NOISSUE do people not even build their PRs when they make them? 2021-12-19 19:12:43 +01:00
Petr Mrázek
6d3eace2a8 NOISSUE fix up java dependency to 1.7 so builds work again
'7' is not a thing
2021-12-19 19:11:08 +01:00
Petr Mrázek
6f6fa6955a Merge pull request #4295 from kb-1000/instanceview-unused-code
NOISSUE Remove some unused code from InstanceView
2021-12-19 18:43:13 +01:00
Petr Mrázek
5961c69019 Merge pull request #4321 from davispuh/java
Update Java version to 7+
2021-12-19 18:42:29 +01:00
Petr Mrázek
526b322a4a Merge pull request #4330 from NewoIsTaken/develop
GH-4000 Scan for Adoptium JREs
2021-12-19 18:40:31 +01:00
Petr Mrázek
aaa12e1ddc Merge pull request #4354 from ISSOtm/patch-1
Indicate the data folder when related errors occur
2021-12-19 18:36:28 +01:00
Petr Mrázek
2f7fd221af Merge pull request #4367 from jamierocks/license-headers-jm
NOISSUE Add license headers to source files created by me
2021-12-19 18:35:43 +01:00
Petr Mrázek
1f4e3e83e3 Merge pull request #4364 from rfl890/patch-1
Change from Oracle JDK to AdoptOpenJDK
2021-12-19 18:35:32 +01:00
Petr Mrázek
fe12b58d06 Merge pull request #4373 from ImperatorStorm/update-man
Fixed small oversight in README.md
2021-12-19 18:29:50 +01:00
ImperatorStorm
d19ad15aec Fixed small oversight in README.md 2021-12-19 09:26:30 -08:00
rfl890
8bc3293ad0 Update BUILD.md 2021-12-19 07:59:19 -05:00
Jamie Mansfield
06bedee835 NOISSUE Add license headers to source files created by me 2021-12-18 00:14:25 +00:00
rfl890
7e1eb02ee6 Change from Oracle JDK to AdoptOpenJDK
The Oracle JDK 8 is now offered at a commercial license and requires an account to download. OpenJDK has no such license and is completely free to use.
2021-12-17 08:11:06 -05:00
Eldred Habert
cb67fc1d15 Indicate the data folder when related errors occur
This should help troubleshooting such errors when they occur
2021-12-15 00:46:35 +01:00
Petr Mrázek
7d047f9223 NOISSE add a shortcut to the loaders mods to main window 2021-12-12 22:39:25 +01:00
Graeme Geldenhuys
3bc450a6d7 Restore the previously deleted line. 2021-12-12 16:45:18 +00:00
Graeme Geldenhuys
fce98f5e16 Fixes compilation error expanding from macro 'major'. 2021-12-12 11:45:58 +00:00
Graeme Geldenhuys
7179e75e70 Changes required to support FreeBSD 2021-12-12 11:39:36 +00:00
Petr Mrázek
431d773eec NOISSUE We are using es_UY as latin american spanish
Make it actually say that in the UI.
2021-12-11 13:06:53 +01:00
NewoIsTaken
b1910642bf GH-4000 Scan for Adoptium JREs 2021-12-10 21:17:09 -05:00
Petr Mrázek
298564ed71 Merge pull request #4327 from NewoIsTaken/patch-1
NOISSUE Ignore Install Directory
2021-12-10 20:49:00 +01:00
Owen Wang
871ceb21dd NOISSUE Ignore Install Directory 2021-12-10 14:03:38 -05:00
Dāvis Mosāns
c29b616497 Update Java version to 7+
Otherwise currently it doesn't build with newer Java
2021-12-10 03:15:01 +02:00
Petr Mrázek
80beccb2c4 NOISSUE Add small workaround for presenting Japanese with Kanji 2021-12-08 01:23:12 +01:00
Petr Mrázek
dba4c452e0 NOISSUE account tweaks 2021-12-08 01:22:57 +01:00
Petr Mrázek
90a62c429a NOISSUE remove the hardcoded blocking of Forge installs
Doesn't make it work, but makes it theoretically possible.
2021-12-06 22:07:41 +01:00
Petr Mrázek
a5581b479e NOISSUE fix launching offline while online 2021-12-06 20:17:31 +01:00
Petr Mrázek
825ef52dd5 NOISSUE fix up some corner cases around migrating accounts 2021-12-06 20:16:13 +01:00
Petr Mrázek
3a940ffb52 NOISSUE Add cat xcf file 2021-12-06 02:40:53 +01:00
Petr Mrázek
70d400f205 NOISSUE party hat for a party cat
On the 30th of November, it's been 10 years since the first
(documented) release of MultiMC. Party hats for everyone :)
2021-12-05 23:26:04 +01:00
Petr Mrázek
cd513c02c4 NOISSUE bump version and update the changelog 2021-12-05 22:06:01 +01:00
Petr Mrázek
c8ca6acc15 NOISSUE fix some error mappings for Mojang accounts 2021-12-05 03:48:07 +01:00
Petr Mrázek
d37003b1de NOISSUE fix builds, make account refresh queue user friendly 2021-12-04 02:10:14 +01:00
Petr Mrázek
db6431d9e0 NOISSUE add missing chrono include 2021-12-04 01:27:58 +01:00
Petr Mrázek
3c46d8a412 GH-4071 Heavily refactor and rearchitect account system
This makes the account system much more modular
and makes it treat errors as something recoverable,
unless they come directly from the MSA refresh token
becoming invalid.
2021-12-04 01:18:05 +01:00
Ghosty
a97d0a36f4 NOISSUE Copy Image is not shown if the selection is > 1 2021-12-03 16:29:28 +01:00
Ghosty
e9c52ec696 NOISSUE Added Copy File(s) feature for the screenshot page
- Ctrl+C now copies the file instead of the image data
- Renamed Copy to Copy Image
2021-12-03 16:08:11 +01:00
Ghosty
75f2dab3c8 NOISSUE Implemented copy screenshots to the clipboard
- Added context-menu entry
- Ctrl+C keybind works as well
- If multiple screenshots are selected, only the first one gets copied
2021-12-03 03:11:53 +01:00
kb1000
eb1091a5f4 NOISSUE Remove some unused code from InstanceView 2021-12-01 21:21:05 +01:00
Petr Mrázek
ffcef673de Merge pull request #4285 from khenriks/develop
Fix instructions for building on Mac
2021-11-30 20:24:19 +01:00
Petr Mrázek
241086883e Merge pull request #4230 from NewoIsTaken/patch-1
GH-4224 Scan 64 bit lib directory to find Java
2021-11-28 21:45:36 +01:00
K Henriksson
20eada7bbe Fix instructions for building on Mac
Relative paths and .. components don't work with CMake's bundle support.

The also adds a note about potential code signing problems.
2021-11-28 10:44:51 -08:00
Petr Mrázek
859d710581 GH-4071 handle invalid MSA refresh token as a hard error 2021-11-28 19:01:21 +01:00
Petr Mrázek
285188ea53 GH-4071 handle network errors when logging in with MSA as 'soft'
This makes the tokens not expire when such errors happen.

Only applies to MSA, not the XBox and Mojang steps afterwards.
Further testing and improvements are still needed.
2021-11-28 18:42:01 +01:00
Petr Mrázek
0e31f77468 Merge pull request #4283 from khenriks/fixicon
Install launcher icns to correct file name
2021-11-28 13:10:15 +01:00
K Henriksson
024f5952ce Install launcher icns to correct file name 2021-11-25 15:14:28 -08:00
Petr Mrázek
a522cad6d6 NOISSUE also undo the AUTORCC change because it was also broken
It broke 32bit linux builds...
2021-11-23 01:37:13 +01:00
Petr Mrázek
b49987e876 NOISSUE fix fix fix the accounts again 2021-11-23 01:25:24 +01:00
Petr Mrázek
27e328c044 NOISSUE Do not rely on AUTOUIC
It is bugged and does not detect changes in .ui files, which makes development painful.
2021-11-22 14:29:38 +01:00
Petr Mrázek
b258eac215 NOISSUE continue reshuffling the codebase 2021-11-22 03:55:16 +01:00
Petr Mrázek
5040231f8d NOISSUE fix build on macOS 2021-11-21 23:42:55 +01:00
Petr Mrázek
9fc677c2a4 NOISSUE more refactoring 2021-11-21 23:36:55 +01:00
Petr Mrázek
69213b1206 NOISSUE continue refactoring things to make tests pass 2021-11-21 23:21:12 +01:00
Petr Mrázek
c2c56a2f6c NOISSUE fix build 2021-11-20 17:08:34 +01:00
Petr Mrázek
0c861db7a2 NOISSUE Some happy little refactors 2021-11-20 16:22:22 +01:00
Petr Mrázek
eafeb64dec NOISSUE qnam -> network 2021-11-17 13:20:50 +01:00
Petr Mrázek
0022aed8bb Merge pull request #4254 from jamierocks/java-17-requirement
NOISSUE Error on launch when launching 1.18 with < Java 17
2021-11-16 19:33:36 +01:00
Jamie Mansfield
014e65220e NOISSUE Error on launch when launching 1.18 with < Java 17 2021-11-16 18:25:16 +00:00
Petr Mrázek
6c82883206 NOISSUE fix account re-adding not updating current account 2021-11-16 02:21:59 +01:00
Petr Mrázek
25fbeb265a NOISSUE fix build some more 2021-11-10 03:16:04 +01:00
Petr Mrázek
30d5a7ab48 NOISSUE fix build 2021-11-10 03:12:01 +01:00
Petr Mrázek
475d949a1e GH-4217 Add support for GamePass accounts and MC profile setup
- We now use the new endpoint for loggiong in via XBox tokens (/launcher/login)
- We now check game entitlements instead of only relying on MC profile presence
- Accounts can now be added even when they do not have a profile
- The launcher will guide you through selecting a Minecraft name if you don't have one yet
2021-11-10 03:02:51 +01:00
Petr Mrázek
32f9c61c6e Merge pull request #4233 from ImperatorStorm/update-man
NOISSUE Update manpage to include new `-a / --profile` option.
2021-11-08 20:05:04 +01:00
Petr Mrázek
86f6a3e751 Merge pull request #4231 from NewoIsTaken/patch-2
GH-4200 Search Eclipse Foundation and Adoptium in Registry
2021-11-08 20:03:08 +01:00
ImperatorStorm
9ccce62f50 NOISSUE Update manpage to include new -a / --profile option. 2021-11-07 20:48:15 -08:00
Owen Wang
0660768478 GH-4200 Search Eclipse Foundation and Adoptium in Registry 2021-11-07 21:35:43 -05:00
Owen Wang
b1beeee11f GH-4224 Scan 64 bit lib directory to find Java 2021-11-07 21:19:35 -05:00
Petr Mrázek
30602363d7 Merge pull request #4229 from Janrupf/develop
GH-4227 Don't crash when mods.toml is invalid
2021-11-06 23:07:23 +01:00
Janrupf
0423464b88 GH-4227 Don't blindly trust mods.toml to be valid 2021-11-06 22:43:16 +01:00
Janrupf
2576a28f73 NOISSUE Exclude run directory 2021-11-06 22:20:26 +01:00
Petr Mrázek
7b4c52e1e3 NOISSUE fix some small build issues 2021-11-03 15:45:42 +01:00
Petr Mrázek
27f276ef13 GH-1795 add terminal launch option to use a specific Minecraft profile
Used like this:
```
./MultiMC --launch 1.17.1 --profile MultiMCTest --server mc.hypixel.net
```
2021-10-31 21:43:33 +01:00
Petr Mrázek
393d17b8d3 GH-4164 set instance drag image hotspot based on where the drag started 2021-10-27 10:25:18 +02:00
Petr Mrázek
ae4939e0d2 GH-4164 Assign instances to groups using drag & drop 2021-10-25 23:51:42 +02:00
Petr Mrázek
85ecbad467 GH-3490 sort instances by name is now aware of numbers 2021-10-25 21:43:00 +02:00
Petr Mrázek
040af58070 NOISSUE add more logging when java checker fails to start 2021-10-25 12:01:08 +02:00
Petr Mrázek
f11d68e2a2 Merge pull request #4197 from Sebastian-byte/patch-1
NOISSUE Fix typo making Windows build fail
2021-10-25 05:48:27 +02:00
Sebastian
73995106d3 NOISSUE Fix typo making Windows build fail 2021-10-24 18:16:00 -05:00
Petr Mrázek
6f6c9c6f68 NOISSUE fix debranding changes affecting version file format 2021-10-24 12:25:44 +02:00
Petr Mrázek
7f28f0bf01 Merge pull request #4186 from ImperatorStorm/addman
MultiMCGH-4120 Add manpage to ubuntu and rpm packages
2021-10-24 02:51:37 +02:00
Petr Mrázek
45520129d9 Merge pull request #4193 from jamierocks/modpack-name-version
Include modpack version in the default instance title
2021-10-24 02:03:03 +02:00
Jamie Mansfield
41d855fd11 GH-4185 Include the modpack version in instance title for ATLauncher 2021-10-24 00:39:42 +01:00
Jamie Mansfield
4b13577a59 GH-4185 Include the modpack version in instance title for modpacks.ch 2021-10-24 00:39:02 +01:00
Jamie Mansfield
c377ad6025 NOISSUE Use MiB suffix for memory and permgen 2021-10-24 00:26:37 +01:00
Jamie Mansfield
a6df7d709f NOISSUE Correct spelling of miscellaneous 2021-10-24 00:26:16 +01:00
Petr Mrázek
cbe1b3353c Merge pull request #4172 from jamierocks/global-playtime
NOISSUE Add option to disable global play time status
2021-10-24 01:21:26 +02:00
Jamie Mansfield
2e60413f7f NOISSUE Add option to disable global play time status 2021-10-24 00:17:07 +01:00
Petr Mrázek
98887911c1 Merge pull request #4173 from jamierocks/common-time-duration-format
NOISSUE Use common duration format for global and instances
2021-10-24 01:11:52 +02:00
Jamie Mansfield
5bc6dd8f97 NOISSUE Remove lingering full stop in playtime status 2021-10-24 00:11:09 +01:00
Jamie Mansfield
7cbca6ab20 NOISSUE Use common duration format for global and instances 2021-10-24 00:09:21 +01:00
Petr Mrázek
ddf98c59f5 Merge pull request #4184 from kb-1000/update-repo-links
NOISSUE Update repository links
2021-10-24 01:09:11 +02:00
Petr Mrázek
110c73edf2 Merge pull request #4178 from jamierocks/qt-clear-button
NOISSUE Use Qt's clear button for ATLauncher page
2021-10-24 01:01:58 +02:00
Petr Mrázek
e6cb7b7710 Merge pull request #4177 from jamierocks/mch-search
NOISSUE Fix modpacks.ch search
2021-10-24 01:01:38 +02:00
ImperatorStorm
aed8d5f8f5 MultiMCGH-4120 Add manpage to ubuntu and rpm packages 2021-10-21 19:17:51 -07:00
Petr Mrázek
8bc6560b5e NOISSUE fix translations 2021-10-21 23:27:01 +02:00
Petr Mrázek
7c86732a47 NOISSUE Update links to repo, fix up library README 2021-10-21 23:25:52 +02:00
Petr Mrázek
a8458e36ff NOISSUE remove extra file 2021-10-21 02:07:29 +02:00
kb1000
8bc5b5a01f NOISSUE Update repository links 2021-10-21 01:46:13 +02:00
Petr Mrázek
264d3017aa NOISSUE update notsecrets README.md 2021-10-21 01:11:08 +02:00
Petr Mrázek
d9b46289a1 NOISSUE finish up the obvious parts of debranding
This is not absolutely complete, but reasonably so
2021-10-21 00:47:53 +02:00
Petr Mrázek
5b3dffce62 NOISSUE continue debranding... 2021-10-21 00:47:53 +02:00
Petr Mrázek
297d4b4196 NOISSUE continue the debranding 2021-10-21 00:47:53 +02:00
Petr Mrázek
e12a769800 NOISSUE fix windows rc file and mac icon 2021-10-21 00:47:53 +02:00
Petr Mrázek
f39c313c5f NOISSUE fix _ICONFIX_EXPORT 2021-10-21 00:47:53 +02:00
Petr Mrázek
462a44b4be GH-4011 fix license text missing from About dialog 2021-10-21 00:47:53 +02:00
Petr Mrázek
441ab7eedc NOISSUE debranding for real, initial work
This is probably very broken on macOS and Windows and will need a lot of work to complete fully.
2021-10-21 00:47:53 +02:00
Petr Mrázek
6a4130c914 NOISSUE re-align the status bar 2021-10-21 00:47:53 +02:00
Jamie Mansfield
3f0e729815 NOISSUE Use Qt's clear button for ATLauncher page
This replaces our 'Reset' button.
2021-10-17 20:18:18 +01:00
Jamie Mansfield
b93997501d NOISSUE Use Qt's clear button for modpacks.ch page
This replaces our 'Reset' button.
2021-10-17 20:14:16 +01:00
Jamie Mansfield
1869dd0de3 NOISSUE Search as you type for modpacks.ch
Since we just filter data locally now, this isn't painfully slow -
indeed it's very quick. This also matches other platforms, such as
ATLauncher.
2021-10-16 23:36:31 +01:00
Jamie Mansfield
175132539b NOISSUE Filter all pack's by name to search for modpacks.ch
modpacks.ch searching has changed, and while likely a bug - we may as
well make this change while we fetch all packs anyway. This makes MMC
more reactive for searchs for the platform.

This should be reverted if/when the modpacks.ch hits a size where we
need to restrict how many packs are fetched.
2021-10-16 23:31:27 +01:00
Petr Mrázek
6cc7788b4a Merge pull request #4135 from Leo40Git/notsecrets-readme
Add README to notsecrets library
2021-10-08 23:25:17 +02:00
Petr Mrázek
0c798b8fc7 Merge pull request #3185 from kb-1000/remove-status
GH-4157 Remove broken Mojang services status
2021-10-08 23:18:15 +02:00
kb1000
fa7a7d52d0 Remove broken Mojang services status 2021-10-08 19:29:49 +02:00
Petr Mrázek
8c4fb86ba0 Merge pull request #4103 from tobfos2611/develop
Fix underaged account that isn't linked to a family help link.
2021-10-03 21:59:46 +02:00
Petr Mrázek
668d31b79e Merge pull request #4143 from phit/patch-1
NOISSUE Remove outdated SSL docs
2021-10-03 21:52:13 +02:00
Petr Mrázek
3138e58c75 NOISSUE fix typo in FMLLibrariesTask.cpp 2021-10-03 21:46:10 +02:00
Philip T
0b312956db NOISSUE Remove outdated SSL docs
the link is dead and the equivalent newer page is useless, https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/dn265983(v=ws.11)?redirectedfrom=MSDN#related-content
2021-10-03 13:43:43 +02:00
ADudeCalledLeo
04b2c7ee0d Add README to notsecrets library 2021-10-01 15:46:32 +03:00
Petr Mrázek
3f3fac8462 Merge pull request #4117 from jamierocks/fix-4114
GH-4114 Allow the same file for be downloaded to multiple paths
2021-09-28 18:25:42 +02:00
Petr Mrázek
e46dc03eb1 NOISSUE add all missing shader packs icons 2021-09-26 02:54:52 +02:00
Petr Mrázek
0bb0578f2b NOISSUE add more shaderpacks icons 2021-09-25 01:46:39 +02:00
Petr Mrázek
cdf8d20c95 NOISSUE add shaderpacks icon for iOS icon theme 2021-09-25 01:43:19 +02:00
Petr Mrázek
560f7fa61a NOISSUE bump deb package version 2021-09-24 15:39:21 +02:00
Petr Mrázek
0b0e7aa633 Merge pull request #4080 from Zetabite/patch-1
NOISSUE: Add notice on support of selfbuilds
2021-09-24 15:24:44 +02:00
Petr Mrázek
1752d47035 Merge pull request #4100 from kb-1000/run-sh-exec
Use exec to launch the MultiMC script in the Debian/RPM packages
2021-09-24 15:24:18 +02:00
Petr Mrázek
0f3cf0595b NOISSUE add shader pack page for instances
Only theme with an icon is simple colored, rest is TBD
2021-09-23 21:26:56 +02:00
Jamie Mansfield
433dd2d161 GH-4114 Allow the same file for be downloaded to multiple paths
This is a potential fix to GH-4114.
2021-09-23 00:52:10 +01:00
Petr Mrázek
a5956194df NOISSUE Remove Requestor, it is unused 2021-09-21 22:02:12 +02:00
Petr Mrázek
d2a44effb5 Merge pull request #4106 from jamierocks/fix-4055
GH-4055 Don't download multiple files to the same path
2021-09-21 09:12:56 +02:00
Jamie Mansfield
0dcd24a53b GH-4055 Don't download multiple files to the same path
FTB should fix their metadata, but this should resolve issues
downloading their packs at present.
2021-09-17 16:00:43 +01:00
tobfos
28d7c5d309 Removed unnecessary text in link.
Thanks @RDKRACZ.
2021-09-15 20:46:07 +10:00
Toby Foster
7229ebbb08 Fix underaged account that isn't linked to a family help link. 2021-09-15 18:10:00 +10:00
kb1000
29f304f070 Use exec to launch the MultiMC script in the Debian/RPM packages 2021-09-14 12:13:41 +02:00
Petr Mrázek
e2355eb276 NOISSUE enable listing symlinks in mod/world lists 2021-09-09 00:27:46 +02:00
Adrian
d1b1113798 Update BUILD.md 2021-09-08 20:45:01 +02:00
Petr Mrázek
7185fd9a8f Merge pull request #4060 from StaticRocket/develop
Add custom-commands icon for other themes
2021-09-08 20:22:13 +02:00
Adrian
06661bff35 NOISSUE: Add notice on support of selfbuilds
Add notice that building MultiMC, besides development purposes, is unsupported.
2021-09-08 19:17:44 +02:00
Petr Mrázek
6c9dc4c86a NOISSUE fix typos in changelog 2021-09-07 21:02:41 +02:00
Petr Mrázek
426135b76a NOISSUE bump version to 0.6.13 and update changelog 2021-09-05 22:21:59 +02:00
Petr Mrázek
46468c8f14 NOISSUE block MS account adding on macOS < 10.13 builds
It's never going to work with Qt 5.6, so there's no point.
People need to update.
2021-09-05 18:54:27 +02:00
Petr Mrázek
878c4fb810 NOISSUE Provide dummy implementation for the secrets library 2021-09-05 18:23:49 +02:00
Petr Mrázek
d644fb2094 GH-4014 do not switch to Qt 5.15.2 on Windows
It is unstable for reasons unknown.
2021-09-04 23:51:57 +02:00
Petr Mrázek
823e7d22c7 GH-4014 fix kernel version scanning on macOS and linux some more 2021-09-04 22:18:29 +02:00
Petr Mrázek
c17b359d03 GH-4014 fix kernel version scanning on macOS and linux 2021-09-04 22:10:57 +02:00
Petr Mrázek
938f896bfa GH-4014 change updater to recognize new Qt 5.15.2 builds 2021-09-04 21:27:09 +02:00
Petr Mrázek
cd87029e6f NOISSUE add style plugins to packaging if present 2021-08-31 18:55:56 +02:00
StaticRocket
4f7aad0f8d Add custom-commands to pe_light icon theme 2021-08-30 23:35:16 -04:00
StaticRocket
b47d986f22 Add custom-commands to pe_dark icon theme 2021-08-30 23:34:59 -04:00
StaticRocket
51cdb8c790 Add custom-commands to pe_colored icon theme 2021-08-30 23:34:21 -04:00
StaticRocket
acbca16013 Add custom-commands to pe_blue icon theme 2021-08-30 23:33:56 -04:00
StaticRocket
62ecb3e58d Add custom-commands to iOS icon theme 2021-08-30 23:33:41 -04:00
StaticRocket
92895f11d1 Add custom-commands to OSX icon theme 2021-08-30 23:33:22 -04:00
Petr Mrázek
23442442d8 GH-3392 fix a bunch of bugs and implement STS error states 2021-08-31 01:11:52 +02:00
Petr Mrázek
3171014301 GH-3392 checking for migration status and refresh button in accounts list 2021-08-29 22:55:33 +02:00
Petr Mrázek
7239502675 GH-3392 Add recognition of already migrated Mojang accounts 2021-08-29 19:59:18 +02:00
Petr Mrázek
1e1655bc4b NOISSUE update README.md
It has not been touched in a long time. This brings it a bit more up to date.
2021-08-29 19:59:18 +02:00
Petr Mrázek
d4a3fc5195 Merge pull request #4054 from StaticRocket/develop
Add flat icon for custom-commands
2021-08-29 03:40:33 +02:00
StaticRocket
93c527ed3d Add flat icon for custom-commands 2021-08-28 21:13:35 -04:00
Petr Mrázek
b2c1100b1c NOISSUE introduce the concept of secrets static library 2021-08-27 22:35:17 +02:00
Petr Mrázek
34a5459dce NOISSUE cut down Requestor 2021-08-25 21:27:51 +02:00
Petr Mrázek
5c0e70e237 Merge pull request #4017 from jamierocks/fix-4012
GH-4012 Disable Xbox login if no MS client token is specified
2021-08-22 20:34:25 +00:00
Petr Mrázek
e2be2ada05 NOISSUE fix build
Missing QUrl include
2021-08-22 20:10:57 +02:00
Petr Mrázek
eae65da110 GH-3392 Switch MS account login to use device flow instead
Device flow involves the user manually opening a web page and putting in
a code. We no longer need to interact with the browser.
2021-08-22 20:01:18 +02:00
Petr Mrázek
50b92c1af2 NOISSUE Markdown is not available in Qt 5.4 ... who would have thought? 2021-08-20 01:57:59 +02:00
Petr Mrázek
1b68d51da6 NOISSUE add setting capes, tweak missing profile message, fix cape IDs 2021-08-20 01:34:32 +02:00
Petr Mrázek
94fd9a3535 NOISSUE fix linux builds 2021-08-19 10:27:30 +02:00
Petr Mrázek
345641f7d2 NOISSUE sanitize some MSA auth logging 2021-08-19 00:43:19 +02:00
Petr Mrázek
4a283fe4c1 NOISSUE print errorString in Requestor 2021-08-18 04:18:59 +02:00
Petr Mrázek
f1a5f7bc4d NOISSUE add ssl error logging to Requestor 2021-08-18 03:43:55 +02:00
Jamie Mansfield
2a21e28ffd GH-4012 Disable Xbox login if no MS client token is specified 2021-08-17 13:19:04 +01:00
Petr Mrázek
4ea52f4758 GH-3392 make sure skin upload at least doesn't fail completely 2021-08-15 23:46:12 +02:00
Petr Mrázek
44d634f564 GH-3392 Fix strings in AuthContext and make them translateable 2021-08-15 23:40:37 +02:00
Petr Mrázek
3a53349e33 GH-3392 dirty initial MSA support that shares logic with Mojang flows
Both act as the first step of AuthContext.
2021-08-15 23:18:50 +02:00
Petr Mrázek
fca2e9e44c Merge pull request #3997 from phit/detectadoptium
GH-3996 Detect Adoptium JDK's
2021-08-09 19:14:23 +02:00
phit
c2ec2a4af5 GH-3996 Detect Adoptium JDK's 2021-08-09 18:43:55 +02:00
Petr Mrázek
17af6d70b4 GH-3974 fix pinning to taskbar in various linux environments 2021-07-27 21:46:38 +02:00
Petr Mrázek
d2de849c86 NOISSUE set the required defs for main library 2021-07-26 14:03:59 +02:00
Petr Mrázek
7921f47ec4 NOISSUE loosen requirements on CMake version in katabasis 2021-07-25 19:50:44 +02:00
Petr Mrázek
20b9f2b42a NOISSUE Flatten gui and logic libraries into MultiMC 2021-07-25 19:50:44 +02:00
Petr Mrázek
dd13368085 NOISSUE bulk addition of code from Katabasis 2021-07-25 19:50:44 +02:00
Petr Mrázek
2568752af5 Merge pull request #3961 from phit/patch-1
GH-3939 fix suggestion template
2021-07-25 18:16:47 +02:00
Philip T
fbd93a47a3 GH-3939 fix suggestion template 2021-07-23 20:41:23 +02:00
Petr Mrázek
0c466bc530 Merge pull request #3919 from hamarb123/develop
Stop application freezes on macOS by moving data to MultiMC.app/Data
2021-07-23 16:13:32 +02:00
Petr Mrázek
b902c5cd78 Merge pull request #3956 from jamierocks/block-forge-117-install
NOISSUE Disable Install Forge button on 1.17 (and above)
2021-07-23 13:41:23 +02:00
Petr Mrázek
78124f6fba Merge pull request #3958 from jamierocks/fix-3957
GH-3957 Fix 'Add Instance' label
2021-07-22 22:34:07 +02:00
Jamie Mansfield
34f74fff0a GH-3957 Fix 'Add Instance' label 2021-07-22 21:21:17 +01:00
Jamie Mansfield
211cfb4af7 NOISSUE Disable Install Forge button on 1.17 (and above)
It appears that Minecraft Forge for Minecraft 1.17 will require setting
JVM arguments to operate, this is not currently possible with MultiMC's
meta.

Therefore, when Forge for 1.17 is released a solution will need to be
looked into.
2021-07-22 20:50:58 +01:00
Petr Mrázek
c5d0348181 NOISSUE Fix minutes display for total playtime 2021-07-22 21:18:03 +02:00
Leland Liu
1762d2fc7d Added total playtime 2021-07-22 20:26:59 +02:00
Petr Mrázek
8ea500de68 Merge pull request #2997 from ChrisLane/man-page
Add man page doc
2021-07-22 18:18:40 +02:00
Petr Mrázek
9e9281f06e Merge pull request #3728 from jamierocks/sort-languages
NOISSUE Sort languages alphabetically by their key
2021-07-22 18:15:46 +02:00
Petr Mrázek
6f12b31ead Merge pull request #3299 from QazCetelic/develop
Update instance icons, using the newer minecraft textures
2021-07-22 17:56:18 +02:00
Petr Mrázek
3974f12643 Merge pull request #3831 from kb-1000/rpm-cleanup
NOISSUE Some cleanups of the RPM package
2021-07-22 17:42:30 +02:00
Petr Mrázek
66fc707105 Merge pull request #3903 from Sebastian-byte/patch-1
NOISSUE: Fix typo and remove SSH URL
2021-07-22 17:41:16 +02:00
Petr Mrázek
4fc37f5a35 Merge pull request #3931 from jamierocks/july-wrk2
NOISSUE Debranding, fix annoying warning, updated Patreon logo
2021-07-22 17:40:36 +02:00
Petr Mrázek
295bcbe3a7 Merge pull request #3952 from Tkain/windows-icon-fix
NOISSUE: Fix Windows icon scaling issues
2021-07-22 17:40:16 +02:00
Thomas Kain
4b3305afbe Fix Windows icon scaling issues
Recalculated the icon using the official logo SVG and GIMP.
Appears visually identical to the old one at all resolutions.
2021-07-21 13:46:21 -04:00
Jamie Mansfield
74f5255eef GH-3930 Track duration of previous game sessions 2021-07-13 17:01:53 +01:00
Jamie Mansfield
d63ef939be NOISSUE Update Patreon logo
The logo was taken from Patreon's brand assets [1] archive, and shrunk
to size with KolourPaint.

[1] https://www.patreon.com/brand
2021-07-13 16:02:04 +01:00
Jamie Mansfield
66fde9e6b7 NOISSUE Correct trending URL string for Technic packs
This seems to have just been an inadvertent mistake when copy-pasting.
This commit just removes an annoying warning.
2021-07-13 15:42:44 +01:00
Jamie Mansfield
4401b9e137 NOISSUE Specify Imgur client ID at build time 2021-07-13 15:15:42 +01:00
Jamie Mansfield
20c393321c NOISSUE Use constants for user agents
Here lies yet another early-stage move to debrand the MultiMC codebase,
as well as reducing the burden of updating strings across the codebase
for a future MultiMC6.
2021-07-13 15:15:33 +01:00
Hamish Arblaster
e883cf2359 GH-2529 Move data path on macOS to MultiMC.app/Data
macOS seems to dislike changing files in the APPBUNDLE.app/Contents directory because it has to re-scan the directory every launch. As a result, large amounts of data there seems to cause freezes of MultiMC. Moving the default location outside of this directory, and thus the data, stops these freezes.
There is also a dialogue when the user first opens the app that asks them if they'd like to migrate their data folder, if they select yes it will move it, and if they select no it will not move it and allow them to move it later with an option in settings.
2021-07-11 09:36:21 +10:00
Petr Mrázek
df1d3dbae2 Merge pull request #3925 from jamierocks/fix-1949
GH-1949 Allow modpack downloads to be aborted
2021-07-06 22:01:33 +02:00
Jamie Mansfield
220971fadd GH-1949 Allow ATLauncher pack downloads to be aborted 2021-07-06 15:22:41 +01:00
Jamie Mansfield
d5c4489313 GH-1949 Allow Technic pack downloads to be aborted
This supports both 'single zip' modpacks and Solder packs, through the
Technic mod platform page.
2021-07-06 15:22:26 +01:00
Jamie Mansfield
db392b4994 GH-1949 Allow modpacks.ch pack downloads to be aborted 2021-07-06 15:12:07 +01:00
Jamie Mansfield
d1a142f040 GH-1949 Allow Legacy FTB pack downloads to be aborted
It looks like this was just an mistake when implementing support for
legacy FTB.
2021-07-06 15:10:35 +01:00
Petr Mrázek
417994735a Merge pull request #3914 from jamierocks/mch-check-checksums
NOISSUE Cache modpacks.ch files and check their checksums
2021-06-30 17:25:50 +02:00
Jamie Mansfield
2e78b64058 NOISSUE Fix detection for 32-bit Azul or Bellsoft Java
Co-authored-by: Pedro Cunha <pedroagracio@gmail.com>
2021-06-30 00:54:19 +01:00
Jamie Mansfield
c15bd655f1 NOISSUE Cache file downloads for modpacks.ch 2021-06-28 22:36:32 +01:00
Jamie Mansfield
f51efc9109 NOISSUE Verify file checksums for modpacks.ch 2021-06-28 22:09:52 +01:00
Sebastían
665b9213c6 NOISSUE Fix typo and remove SSH URL 2021-06-25 20:15:19 -05:00
Petr Mrázek
dc3a4cebce Merge pull request #3896 from jamierocks/mib-suffix
NOISSUE Use MiB suffix for Java memory options
2021-06-25 12:27:57 +02:00
Petr Mrázek
d92733feae Merge pull request #3897 from jamierocks/atl-opt-mod-install-btn
NOISSUE Close optional mod dialog with Install button
2021-06-25 12:20:27 +02:00
Jamie Mansfield
a20a7e987f NOISSUE Fail launch if minimum Java requirement is not met
This will fail launch in the following conditions:
  1. A version greater than or equal to Minecraft 17w13a, and less than
     21w19a - and the Java version is less than 8.
  2. A version greater than or equal to Minecraft 21w19a - and the Java
     version is less than 16.
2021-06-23 21:24:25 +01:00
Jamie Mansfield
7c0fdaa730 NOISSUE Check mod and config checksums for ATLauncher 2021-06-23 18:20:25 +01:00
Jamie Mansfield
c77f5285e3 NOISSUE Close optional mod dialog with Install button
This was a silly ommision I made.
2021-06-23 15:49:31 +01:00
Jamie Mansfield
d8598d6901 NOISSUE Use MiB suffix for Java memory options 2021-06-23 09:57:55 +01:00
Petr Mrázek
27d3ae145a Merge pull request #3888 from jamierocks/atl-version-selection-width
NOISSUE Fix ATLauncher version selection combo box width
2021-06-22 19:57:49 +02:00
Petr Mrázek
5479fbec92 Merge pull request #3819 from jamierocks/atl-optional-mods
NOISSUE Support ATLauncher optional mods
2021-06-22 19:55:53 +02:00
Petr Mrázek
bc2c6cf030 Merge pull request #3893 from colbiedison/detect_jdks_in_opt
[Linux] Add detection of manually installed JDKs in /opt
2021-06-22 19:14:37 +02:00
Colbie Dison
ba8af797a9 Add detection of manually installed JDKs in /opt 2021-06-22 11:56:05 -05:00
Jamie Mansfield
4ba0c9c298 NOISSUE Support mod grouping and dependencies 2021-06-21 16:29:16 +01:00
Jamie Mansfield
74311a54cf NOISSUE Support ATLauncher optional mods 2021-06-21 16:29:11 +01:00
Jamie Mansfield
a87c64d7d1 NOISSUE Fix ATLauncher version selection combo box width
Resolves a bug that was introduced with [1], furthermore and in
specific relation to the intent of said commit, this brings the
version selection combo box inline with other mod platforms.

[1] f7c144c393
2021-06-21 15:26:10 +01:00
Petr Mrázek
8179a89103 Merge pull request #3886 from phit/fixftblegacy
NOISSUE Fix FTB Legacy Pack Selection
2021-06-21 00:46:16 +02:00
phit
e439ce6e0b NOISSUE Fix Modplatform Scrollbars properly 2021-06-20 22:59:58 +02:00
phit
f6d6e4c1c4 NOISSUE Fix FTB Legacy Pack Selection 2021-06-20 22:52:10 +02:00
Petr Mrázek
bace6fec1b Merge pull request #3880 from phit/fix/GH-3720
GH-3720 Fix UI inconsistencies with Modplatforms
2021-06-20 22:39:03 +02:00
Petr Mrázek
a487234968 Revert "GH-3507 Modernize MacOS Icon"
This reverts commit d08a2f00a2.
2021-06-20 21:53:02 +02:00
phit
f7c144c393 GH-3720 Fix UI inconsistencies with Modplatforms
Fixes GH-3118
Fixes GH-3720
Fixes GH-3731

Icons and Ok button state will now switch consistently when moving
between tabs. ATLaunchers packlist is now no longer redownloaded
each time you open its Tab. All lists are striped now. And all
search and filter fields now have a placeholder text.
2021-06-20 01:03:17 +02:00
Petr Mrázek
6c0ff0b46f Merge pull request #3881 from jamierocks/notice-login-msa
NOISSUE Add notice re MSA to login dialog
2021-06-20 00:07:01 +02:00
Jamie Mansfield
81d4dc09cc NOISSUE Add notice re MSA to login dialog
This commit should be reverted when support for Microsoft/Xbox
authentication is introduced.
2021-06-19 20:31:49 +01:00
Petr Mrázek
5f8d07c009 Merge pull request #3875 from jamierocks/feature/gh-3033
GH-3033 Add filtering for version components
2021-06-19 12:50:21 +02:00
Petr Mrázek
25955c0817 Merge pull request #3877 from Zetabite/feature_offline_status_log
NOISSUE Add info for instance launch mode and server status when online
2021-06-19 11:59:04 +02:00
Adrian
6db6ebe37f NOISSUE Add info for instance launch mode and server status when online 2021-06-19 03:15:21 +02:00
Jamie Mansfield
b246fc171e GH-2971 Brand mod model for resource/texture pack pages 2021-06-19 00:59:48 +01:00
Jamie Mansfield
c92b44e6d6 GH-3719 Translate some missing strings when changing locale 2021-06-19 00:14:36 +01:00
Jamie Mansfield
e148cfbbfd NOISSUE Don't translate logged entries 2021-06-18 23:57:58 +01:00
Jamie Mansfield
c0f72488d0 GH-3033 Add filtering for version components 2021-06-18 23:21:12 +01:00
Petr Mrázek
cb22d5fb09 Merge pull request #3874 from phit/macosicon
GH-3507 Modernize MacOS Icon
2021-06-18 20:39:32 +02:00
phit
d08a2f00a2 GH-3507 Modernize MacOS Icon
full credit to @ThePotatoKing55
2021-06-18 20:27:25 +02:00
Petr Mrázek
0c147fb5ee Merge pull request #3843 from phit/adoptjdkdetection
Update Java detection
2021-06-18 19:14:15 +02:00
Petr Mrázek
09ce3d8f40 Merge pull request #3872 from phit/fixcursedropdown
GH-3731 Limit Curserforge version height
2021-06-18 19:10:18 +02:00
phit
00820df656 GH-3731 Limit Curserforge version height 2021-06-18 18:31:14 +02:00
phit
c2c288a956 NOISSUE Add BellSoft and Azul Java 2021-06-18 17:12:44 +02:00
phit
7ac6c4f3d9 NOISSUE Cleanup duplication and Microsoft JDK 2021-06-18 17:02:41 +02:00
phit
fd04ff2b08 NOISSUE Add AdoptOpenJDK Java detection 2021-06-18 16:10:48 +02:00
Petr Mrázek
434adc4cd7 Merge pull request #3870 from jamierocks/build-time-support-links
NOISSUE Specify support URLs at build time
2021-06-18 13:43:35 +02:00
Jamie Mansfield
40f41e5fbe NOISSUE Specify support URLs at build time
Support URLs (bug tracker, Discord guild, subreddit) are now specified
as cache variables in cmake, and the buttons are not shown if no value
is set for them.

This is an early-stage move towards debranding the MultiMC codebase,
and will (hopefully) alleviate support requests coming to us from
illicit forks.
2021-06-18 12:24:20 +01:00
kb1000
663a1a5a83 Add xrandr to the dependencies of the RPM package 2021-06-14 20:34:51 +02:00
Petr Mrázek
9fafe3ffe6 Merge pull request #3833 from jamierocks/disable-forge-btn-when-game-running
NOISSUE Disable 'Install Forge' button when game is running
2021-06-09 21:54:22 +02:00
Jamie Mansfield
3390367d93 NOISSUE Support CurseForge modpacks using Fabric Loader 2021-06-09 20:49:26 +01:00
Jamie Mansfield
6dd1fdbaf9 NOISSUE Fail pack installation for download errors for FTB
This effectively reverts the changes made in [1] to resolve GH-3304.

[1] 05ffcf706b
2021-06-03 15:16:03 +01:00
Jamie Mansfield
efa3bb33f5 NOISSUE Disable 'Install Forge' button when game is running 2021-06-03 15:03:19 +01:00
Petr Mrázek
60b686f014 Merge pull request #3804 from Janrupf/feature/default-server
Add ability to select a server to join in the instance settings
2021-05-24 02:41:54 +02:00
Petr Mrázek
3a8068e75f Merge pull request #3803 from phit/patch-1
NOISSUE Fix new Oracle Java Detection on Windows
2021-05-24 02:41:08 +02:00
Janrupf
52c1150522 NOISSUE Add --server argument for --launch 2021-05-23 14:42:20 +02:00
Janrupf
58ab005f7e NOISSUE Add missing license header 2021-05-22 18:10:17 +02:00
Janrupf
ea6c42a93c NOISSUE Allow joining servers from the servers page 2021-05-22 18:07:08 +02:00
Janrupf
f33fe05e5f NOISSUE Use minecraft logic for parsing adresses 2021-05-22 17:24:37 +02:00
Janrupf
d97f13b4aa NOISSUE Use Vanilla logic for resolving servers 2021-05-22 17:00:14 +02:00
Janrupf
0ccd7223fd NOISSUE Make LauncherPart aware of server to join 2021-05-22 16:33:16 +02:00
Janrupf
23a706bbae NOISSUE Resolve minecraft server using DNS SRV 2021-05-22 16:14:25 +02:00
Janrupf
cc6cd0648a NOISSUE Add server launch arguments 2021-05-22 13:54:34 +02:00
Philip T
f78152d725 NOISSUE Fix new Oracle Java Detection on Windows
Those were changed with Java 9 https://docs.oracle.com/javase/9/install/installation-jdk-and-jre-microsoft-windows-platforms.htm#JSJIG-GUID-47C269A3-5220-412F-9E31-4B8C37A82BFB
2021-05-22 13:37:51 +02:00
Janrupf
f0eb5b4a0c NOISSUE Register settings for setting a server 2021-05-22 13:28:23 +02:00
Janrupf
2e2a5d0943 NOISSUE Required UI elements for setting a server 2021-05-22 13:15:59 +02:00
Petr Mrázek
911074e966 Merge pull request #3735 from kumquat-ir/develop
NOISSUE Parse META-INF/mods.toml for Forge 1.14+ mod metadata
2021-05-15 00:36:46 +02:00
Petr Mrázek
b8cd13bb21 Merge pull request #3747 from runlevel5/patch-1
Add limits header
2021-05-15 00:35:36 +02:00
Petr Mrázek
deac64e0a2 Merge pull request #3787 from JoelTroch/feature/gh-3450
GH-3450 Add checkboxes to display and record game time
2021-05-15 00:35:05 +02:00
Petr Mrázek
2f1e8e82a3 Merge pull request #3729 from jamierocks/atl-loader-targets
Various ATLauncher improvements and bug fixes
2021-05-15 00:33:53 +02:00
Joël Troch
de089195cd GH-3450 Add checkboxes to display and record game time 2021-05-13 19:38:24 +02:00
Jamie Mansfield
df7873eb9a GH-3764 Only install client mods for ATLauncher packs 2021-05-06 17:14:49 +01:00
Petr Mrázek
3d11f9a7e9 NOISSUE fix issue templates 2021-05-01 10:29:43 +02:00
Trung Lê
c2fd714f8d Add limits header 2021-04-29 12:42:49 +10:00
Petr Mrázek
a09d03d71d NOISSUE stop relying on forge servers for old FML libs 2021-04-29 02:32:21 +02:00
kumquat-ir
e2e5294fb9 reimplement parsing logic for tomlc99 2021-04-17 10:33:45 -07:00
kumquat-ir
e668aa0f95 switch to new toml library 2021-04-17 09:46:11 -07:00
kumquat-ir
13afad80fb replace ${file.jarVersion} with something useful 2021-04-16 17:45:55 -07:00
kumquat-ir
7156e086f6 parse META-INF/mods.toml for metadata 2021-04-16 13:33:56 -07:00
kumquat-ir
860706caec allow parsing toml from a QByteArray 2021-04-16 11:02:02 -07:00
kumquat-ir
42253150e4 add toml11 as dependency 2021-04-15 23:19:01 -07:00
Jamie Mansfield
438ddfb88d NOISSUE Support Fabric modpacks on ATLauncher
Annoyingly the metadata structure is loader dependent :(
2021-04-16 01:51:25 +01:00
Jamie Mansfield
73788f5d2f NOISSUE Emit failure on failure conditions
The 'Installing modpack' dialog will no longer stay open forever,
even though the installation has failed.
2021-04-16 01:51:25 +01:00
Jamie Mansfield
87dbe82474 NOISSUE Support custom,latest,recommended loader versions for ATL
This provides support for modpacks using the new loader mechanism in
ATLauncher and using a non-specific version target.
2021-04-16 01:51:23 +01:00
Jamie Mansfield
88ce42bc0a NOISSUE Sort languages alphabetically by their key
This will provide a consistent arrangement of languages in MultiMC.
2021-04-13 13:53:21 +01:00
Petr Mrázek
8b926d29d7 NOISSUE fix build 2021-04-08 22:28:55 +02:00
Petr Mrázek
4ac38991ad Merge pull request #3691 from phit/feature/fixcurse
NOISSUE Curseforge makeover
2021-04-08 22:07:03 +02:00
Petr Mrázek
4ca481b2b3 Merge pull request #3715 from jamierocks/atl-trycatch-json
NOISSUE Fedora build, ATL exceptions, and language tweaks
2021-04-08 21:29:10 +02:00
Jamie Mansfield
524fc5b6ec NOISSUE Fix string formatting issues
This allows translations to have more control over the output :)
2021-04-08 18:55:20 +01:00
Jamie Mansfield
64617201b0 GH-3334 Show English variants correctly
This will no longer show 'American English' with the statistics of
'British English', yet show the correct translations - and will now
display 'British English'.
2021-04-08 18:53:16 +01:00
Jamie Mansfield
d6dc22d57c NOISSUE Handle JSON exceptions in ATLauncher support
Thanks to phit for pointing this out :)
2021-04-08 18:51:04 +01:00
Jamie Mansfield
3a1abb555b GH-3575 Fix build on Fedora 34
Presumably this is caused by the bump to GCC 11 in Fedora 34. See
the error that did occur below...

    ./MultiMC5/application/KonamiCode.cpp: In member function ‘void KonamiCode::input(QEvent*)’:
    ./MultiMC5/application/KonamiCode.cpp:38:23: error: comparison of integer expressions of different signedness: ‘int’ and ‘std::array<Qt::Key, 10>::size_type’ {aka ‘long unsigned int’} [-Werror=sign-compare]
       38 |         if(m_progress == konamiCode.size())
          |            ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
2021-04-08 18:50:07 +01:00
phit
1f8408c793 NOISSUE Curseforge makeover
update UI to match other modpack platforms
add sorting
add version selection, fixes GH-3667
add installing beta versions, fixes GH-3611
2021-04-08 19:32:34 +02:00
Petr Mrázek
e5804b1279 NOISSUE add some logging to the system theme determination logic 2021-04-04 21:19:49 +02:00
Petr Mrázek
7246d8a779 NOISSUE improve GradleSpecifier and handle broken library names 2021-04-01 03:50:28 +02:00
Petr Mrázek
fbe9d15875 NOISSUE fix it some more in different file 2021-03-29 22:03:52 +02:00
Petr Mrázek
094ed0bc81 NOISSUE fix build issue with QJsonValueRef on macOS 11
This will remove support for macOS 10.7 and 10.8, but their numbers in analytics are 0
2021-03-29 21:51:28 +02:00
Petr Mrázek
43cf647642 Revert "GH-3666 Fix multi-monitor dpi scaling on windows"
This reverts commit 34bf688479.
2021-03-28 19:46:01 +02:00
Petr Mrázek
5400d4e613 Merge pull request #3690 from jamierocks/ftb-search-edge
NOISSUE Fix edgecase where new searches won't be processed
2021-03-26 20:37:48 +00:00
Petr Mrázek
adf2301b2a Merge pull request #3684 from KGB-8375/patch-1
GH-3666 Fix multi-monitor dpi scaling on windows
2021-03-26 20:36:28 +00:00
Petr Mrázek
9d76219434 Merge pull request #3686 from phit/fix/3633
GH-3633 assume latest MC version, if CurseForge pack supports multiple
2021-03-26 20:36:00 +00:00
Jamie Mansfield
8e6400e8d8 NOISSUE Fix edgecase where new searches won't be processed
This resolves an issue with the modpacks.ch search functionality, in
which a search issued while one is currently in progress won't be made
and the UI won't allow for the search to be made after.

Reproduction Steps:
1. Open the FTB pane in the Add Instance Dialog
2. Perform a search while MMC is still performing the initial search

The search won't be performed, the existing search will have been
aborted, and you are unable to try the search again (without trying a
different search in the meantime).

This was caused by 2 things:
1. A search cannot be re-attempted, and this logic doesn't consider
   failures.
2. The failure slot wasn't called when the NetJob was aborted, so
   the search would never be performed - but the term would be
   stored as if it had (trigering point 1).

I have resolved this by doing 2 things:
1. If the failure slot is called, set a searchState of Failed. Allow
   search re-attempts in this case.
2. If there is a present NetJob, abort and reset it. The immediately
   continue with the search.
2021-03-26 20:03:57 +00:00
KGB-8375
34bf688479 GH-3666 Fix multi-monitor dpi scaling on windows
Disable qt 5.6 DPI scaling and use windows' builtin DPI scaling, which works much better on multi-monitor setups
2021-03-26 09:54:28 -04:00
phit
ba13e33ccc GH-3633 assume latest MC version, if CurseForge pack supports multiple
right now these are unused anyway
2021-03-26 14:18:47 +01:00
Petr Mrázek
73af0f271a NOISSUE fix build - missing includes in RWStorage.h 2021-03-26 01:48:37 +01:00
Petr Mrázek
b8ee9a2a8e NOISSUE update ubuntu packaging bits 2021-03-26 01:48:37 +01:00
Petr Mrázek
369a243f1f Merge pull request #3682 from kb-1000/rename-twitch-curseforge
NOISSUE rename Twitch to flame internally for consistency and to CurseForge for user displayed strings
2021-03-26 00:38:58 +00:00
Petr Mrázek
1db6985be2 Merge pull request #3681 from kb-1000/fix-moc-warning
NOISSUE fix MOC warning complaining about an unused MOC file include
2021-03-26 00:37:25 +00:00
Petr Mrázek
cc8ac7bdbe Merge pull request #3680 from kb-1000/log-system-glfw-openal
NOISSUE print information about whether the system GLFW or OpenAL workarounds are active to the instance log
2021-03-26 00:37:09 +00:00
Petr Mrázek
8f4062b5e6 Merge pull request #3677 from kb-1000/datapacks-button-disable
NOISSUE disable datapacks button in the world screen if no world is selected
2021-03-26 00:36:30 +00:00
kb1000
a0cb1a0d42 NOISSUE rename Twitch to flame internally for consistency and to CurseForge for user displayed strings 2021-03-24 00:59:43 +01:00
kb1000
7acad35c3f NOISSUE fix MOC warning complaining about an unused MOC file include 2021-03-23 23:05:10 +01:00
kb1000
9d7ba275ab NOISSUE print information about whether the system GLFW or OpenAL workarounds are active to the instance log 2021-03-23 22:57:39 +01:00
kb1000
4f3328e71c NOISSUE disable datapacks button in the world screen if no world is selected 2021-03-23 21:54:48 +01:00
Petr Mrázek
cbc973a5af NOISSUE remove text about future plans
It was confusing people.
2021-03-22 02:17:01 +01:00
Petr Mrázek
464b05f6bf NOISSUE fix typo in changelog 2021-03-21 20:45:06 +01:00
Petr Mrázek
4b02b3f84a NOISSUE add warning to the ATLauncher pack page 2021-03-21 20:34:28 +01:00
Petr Mrázek
b1047d839e NOISSUE update changelog 2021-03-21 20:21:50 +01:00
AppleTheGolden
613afce705 NOISSUE add github issue forms 2021-03-19 00:24:52 +01:00
Petr Mrázek
af5b1beb64 Merge pull request #3646 from sambhavsaggi/openssl_doc_update
Fix #3638: OpenSSL download links in BUILD.md are dead
2021-03-10 04:19:43 +01:00
Petr Mrázek
84c673c5ba GH-3467 fix stall-out in ScanModFolders when the folders don't exist 2021-03-10 03:58:24 +01:00
Petr Mrázek
0c98589a7f GH-3602 Create .minecraft before running pre-launch commands 2021-03-10 03:58:24 +01:00
Sambhav Saggi
10e7f48347 Fix #3638 2021-03-08 09:24:37 -05:00
Petr Mrázek
0a869fc9ed Merge pull request #3623 from jamierocks/ftb/jarmods
Support jarmods and Fabric for modpacks.ch
2021-02-27 04:10:06 +01:00
Jamie Mansfield
9c69b0cdde NOISSUE Install jarmods for modpacks.ch
This patch will now install any jarmods from modpacks.ch packs,
searching the 'jarmods' directory to find them.
2021-02-25 14:43:04 +00:00
Jamie Mansfield
9d91cd496f NOISSUE Download all mods before writing the instance for modpacks.ch
This is prepatory work for implementing jarmods support for
modpacks.ch, where we will need to look through files in a directory -
which would require that those files are present at such time.

This might even fix some weird bugs, maybe - I've not encountered any
bugs from how this previously worked, but I feel that what's going on
is slightly clearer now.
2021-02-25 14:34:51 +00:00
Jamie Mansfield
384680ca13 NOISSUE Support Fabric mod loader for modpacks.ch
There aren't any (currently) packs on modpacks.ch that use Fabric, but
they do have support in their modpacklauncher. This patch just means
we preemptively have support should any packs that require it be
introduced.
2021-02-25 13:55:26 +00:00
Petr Mrázek
1edcd9b86e NOISSUE implement deleting skins 2021-02-11 02:23:00 +01:00
Petr Mrázek
280903e52b Merge pull request #3597 from jamierocks/atl/search
NOISSUE Add search to ATLauncher
2021-02-11 01:59:24 +01:00
Jamie Mansfield
152d476f20 NOISSUE Add search to ATLauncher 2021-02-11 00:57:07 +00:00
Petr Mrázek
40bdb0ffd6 Merge pull request #3593 from jamierocks/atl/description-view
NOISSUE Show ATLauncher pack descriptions in text browser
2021-02-10 18:38:04 +01:00
Petr Mrázek
1e59dafd75 Merge pull request #3594 from jamierocks/ftb/description-view
NOISSUE Show FTB pack descriptions in text browser
2021-02-10 18:37:34 +01:00
Jamie Mansfield
6eabd336ee NOISSUE Show FTB pack descriptions in text browser 2021-02-10 16:57:46 +00:00
Jamie Mansfield
003e019048 NOISSUE Show ATLauncher pack descriptions in text browser 2021-02-10 16:11:59 +00:00
Petr Mrázek
6b3b7ded2d GH-3130 fix uploading skins by using the new skins endpoint 2021-02-10 03:32:17 +01:00
Petr Mrázek
485f123362 Merge pull request #3311 from AbigailBuccaneer/develop
Fix endianness handling errors
2021-02-09 23:12:32 +01:00
Petr Mrázek
7265abf763 NOISSUE sprinkle suspendSave all over pack import tasks 2021-02-09 22:46:51 +01:00
Petr Mrázek
7ca5cf0e70 Merge pull request #3592 from Scotsguy/develop
NOISSUE Swap discord invite with vanity url
2021-02-09 22:02:12 +01:00
AppleTheGolden
f3205ebf25 NOISSUE Swap discord invite with vanity url 2021-02-09 22:00:20 +01:00
Petr Mrázek
e8b9176736 NOISSUE update macOS build instructions 2021-02-09 21:08:45 +01:00
Petr Mrázek
13a7f8d3b7 NOISSUE fix multiple issues in ATLauncher integration 2021-02-09 05:04:23 +01:00
Petr Mrázek
434369ca7c NOISSUE tweak ATLauncher pack cache names to avoid filesystem issues 2021-02-08 02:23:35 +01:00
Petr Mrázek
c54d922f26 NOISSUE and fix one more build fail 2021-02-07 23:54:29 +01:00
Petr Mrázek
85d5e9ff23 NOISSUE fix some build fails on linux and windows 2021-02-07 23:51:10 +01:00
Jamie Mansfield
ab19b86341 GH-405 ATLauncher Support 2021-02-07 23:30:24 +01:00
Petr Mrázek
5e980ceef2 Merge pull request #3556 from Zipdox/develop
Fix packaging issue caused by qt5-default
2021-02-07 02:24:42 +01:00
Petr Mrázek
1cf60a01d5 Merge pull request #3538 from Scotsguy/develop
NOISSUE Update Copyright Year
2021-02-07 02:23:31 +01:00
Petr Mrázek
ba4a1a68bd Merge pull request #3537 from rbutoi/argv0
Use argv[0] to print program name on a CLI error.
2021-02-07 02:21:25 +01:00
Petr Mrázek
11f7a432c4 Merge pull request #3583 from chrhasse/develop
Fix catching polymorphic type by value error
2021-02-07 02:16:59 +01:00
Christopher Hasse
047546c59e Fix catching polymorphic type by value error
fixes #3579
2021-02-06 18:55:44 -06:00
Petr Mrázek
75c14990e5 Merge pull request #3580 from kb-1000/remove-proxy-auth-from-log
NOISSUE Remove proxy user and password from the MultiMC log
2021-02-06 20:47:56 +01:00
kb1000
71bb50f64c NOISSUE Remove proxy user and password from the MultiMC log 2021-02-06 20:16:04 +01:00
Petr Mrázek
1868e0ccf1 GH-3229 fix copy seed button not working for newer worlds
Added the `optional-bare` library and refactored NBT reading
code to support this change.
2021-02-06 15:58:03 +01:00
Petr Mrázek
f8ca96a335 NOISSUE update changelog for 0.6.12 2021-02-06 13:13:53 +01:00
Petr Mrázek
5a7c1f9edf NOISSUE force kill button to reevaluate its style on state change 2021-02-05 03:18:21 +01:00
Petr Mrázek
359905a8ef NOISSUE set instance dialog launch/kill button object name 2021-02-05 02:46:59 +01:00
Petr Mrázek
8eee4e3e6c Merge pull request #3562 from kb-1000/technic-fix-1710-forge
Fix up 1.7.10 forge versions for Technic platform import by removing -1.7.10 suffix
2021-01-29 13:02:04 +01:00
kb1000
df2b9adc1e Fix up 1.7.10 forge versions for Technic platform import by removing -1.7.10 suffix 2021-01-29 10:18:33 +01:00
Zipdox
61004a25ec Fix packaging issue caused by qt5-default 2021-01-26 21:47:26 +01:00
AppleTheGolden
0de064eb35 NOISSUE Update Copyright Year 2021-01-18 08:28:54 +01:00
Radu Butoi
1cb9382282 Use argv[0] to print program name on a CLI error. 2021-01-17 16:58:32 -05:00
Petr Mrázek
02887536f7 Merge pull request #3524 from TreyRuffy/SessionServerFix
Switch Session Server
2021-01-17 18:53:03 +00:00
TreyRuffy
9100373af2 Switch Session Server
sessionserver.mojang.com has been down for a while and it looks like it is no longer in use.
https://bugs.mojang.com/browse/WEB-3166?focusedCommentId=820167&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-820167

I have switched the url to session.minecraft.net
2021-01-11 19:26:40 -07:00
Petr Mrázek
dc45d46e9c NOISSUE fix the metainfo file 2021-01-07 11:54:08 +01:00
Petr Mrázek
e9abf8a2d4 Merge pull request #3436 from Moresteck/develop
NOISSUE Corrected the fix for Classic saving, fixes Indev compatibility
2021-01-07 10:27:25 +00:00
Petr Mrázek
a1a3f0d492 Merge pull request #3476 from joshua-stone/metainfo-support
NOISSUE Add metainfo support
2021-01-07 10:05:59 +00:00
Joshua Stone
331f6df9e5 Improve metadata for screenshots 2020-12-10 11:43:37 -05:00
Joshua Stone
e9b779aa77 Update documentation WRT .rpm contents 2020-12-08 13:25:34 -05:00
Joshua Stone
42d124ed2b Move metainfo to ubuntu oflder so it can be used in both .rpm and .deb builds 2020-12-08 13:23:52 -05:00
Joshua Stone
a9a78c74db Fix contact email 2020-12-08 01:13:11 -05:00
Joshua Stone
d55030367e Use metainfodir macro directly 2020-12-08 00:55:50 -05:00
Joshua Stone
65990a1bf1 Remove disclaimer 2020-12-08 00:42:16 -05:00
Joshua Stone
399109728f Update RPM .spec to include metainfo file 2020-12-08 00:38:43 -05:00
Joshua Stone
c1161e2377 Add metainfo.xml for improving package metadata 2020-12-08 00:30:29 -05:00
Petr Mrázek
66b0ed2669 Merge pull request #3400 from MatejKafka/patch-1
Update BUILD.md with details for Windows build process
2020-12-02 01:57:30 +01:00
kb1000
f4d58e17ee NOISSUE Add pack author and description to technic modpack import page 2020-11-30 00:31:27 +01:00
Petr Mrázek
6aa126be30 Merge pull request #3460 from kb-1000/rpm-package
NOISSUE Add an RPM package
2020-11-30 00:19:25 +01:00
Petr Mrázek
58d168bda2 Merge pull request #3442 from jamierocks/ftb-sorts
NOISSUE Add sorting options to FTB pack install page
2020-11-30 00:13:41 +01:00
Jamie Mansfield
5eace10d51 NOISSUE Continue requesting packs after ignoring one
This was a mishap on my part, fortunately quickly exposed as the
combination of GH-3463 and GH-3464 made no packs visible.
2020-11-29 13:58:20 +00:00
Jamie Mansfield
b8c6a42f49 NOISSUE Add sorting options to FTB pack install page 2020-11-29 13:46:14 +00:00
Petr Mrázek
ed11d33054 Merge pull request #3464 from jamierocks/ftb/ignore-dud-packs
NOISSUE Ignore 'dud' FTB packs
2020-11-29 00:16:00 +01:00
Petr Mrázek
11a21b57e4 Merge pull request #3463 from jamierocks/ftb/new-api
NOISSUE Use new modpacks.ch route for listing packs
2020-11-29 00:15:24 +01:00
Jamie Mansfield
7321a4fd3d NOISSUE Ignore 'dud' FTB packs
There is no guarantee from modpacks.ch that modpacks contain any
versions, which is currently an issue with pack 63 (Direwolf20 1.5).
2020-11-28 22:13:53 +00:00
Jamie Mansfield
6249a54ba2 NOISSUE Use new modpacks.ch route for listing packs 2020-11-28 22:06:35 +00:00
kb1000
58c2228247 NOISSUE Add an RPM package for MultiMC
The RPM package reuses the files from the Ubuntu package.
2020-11-27 19:28:46 +01:00
Petr Mrázek
88d6b6ea3f GH-3427 add low effort datapack button to worlds 2020-11-24 22:31:49 +01:00
Petr Mrázek
66c0999901 Merge pull request #3438 from jamierocks/technic-zip-fix
GH-3437 Extract Technic single zip packs to correct place
2020-11-14 16:14:50 +01:00
Jamie Mansfield
edfea9894e NOISSUE Cleanup Technic support 2020-11-13 16:38:18 +00:00
Jamie Mansfield
fd1a8f039a GH-3437 Extract Technic single zip packs to correct place 2020-11-13 16:33:52 +00:00
Moresteck
49126fa8e2 NOISSUE Corrected the fix for Classic saving, fixes Indev compatibility 2020-11-12 14:58:12 +00:00
Matej Kafka
7da2fd5b8b Update BUILD.md
Extended description of Windows build process based on about 6 hours wasted on trial and error.
2020-10-25 03:54:59 +01:00
Petr Mrázek
eb3e6e4c6a NOISSUE give technic icon a darker outline 2020-10-13 22:35:43 +02:00
Petr Mrázek
4197ae0128 NOISSUE remove obsolete TODO comment from SolderPackInstallTask 2020-10-13 22:20:28 +02:00
Petr Mrázek
a7216ecca1 NOISSUE use Json parsing helpers to process technic search results 2020-10-13 22:18:42 +02:00
Petr Mrázek
4689571c24 NOISSUE fix build: QTemporaryDir::filePath was added in Qt 5.9 2020-10-13 22:01:01 +02:00
Petr Mrázek
4c62776044 Merge pull request #3195 from kb-1000/technic-import
Technic pack import
2020-10-13 21:44:20 +02:00
kb1000
8021fb25d0 GH-469 Implement support for importing and searching for Technic Platform and Solder modpacks
This does not support any custom modpack.jar for 1.6 or newer, it simply uses standard Forge then.
Supports Forge and Fabric, and JAR mods for 1.5 and older.
2020-10-13 21:34:16 +02:00
Petr Mrázek
762ddaea65 Merge pull request #3372 from jamierocks/ftb-data-temp-fix
GH-3304 Temporarily ignore download failures for FTB packs
2020-10-12 01:36:56 +02:00
Jamie Mansfield
05ffcf706b GH-3304 Temporarily ignore download failures for FTB packs
This is a temporary measure, to match FTB's own installer, to allow
packs to download while FTB work on fixing the data from their API.
2020-10-12 00:28:05 +01:00
Petr Mrázek
dded11004f NOISSUE adapt to work on Windows, fix reading symlink targets on POSIX 2020-10-12 01:01:51 +02:00
Petr Mrázek
f1284ab96b NOISSUE just eliminate QCOMPARE. it doesn't work at all. 2020-10-11 23:50:36 +02:00
Petr Mrázek
3aca4cbe20 NOISSUE tweak QCOMPARE vs QVERIFY
... the Qt test framework is annoyingly complicated and brittle.
... it may have to be replaced with something more reliable.
2020-10-11 23:45:41 +02:00
Petr Mrázek
015c34bb38 NOISSUE dis-ambiguate call to ensureBoolean in PackageManifest 2020-10-11 23:32:57 +02:00
Petr Mrázek
a49472349d NOISSUE fix constructing Path from iterators
Old Qt doesn't have the iterator-based constructors...
2020-10-11 23:28:38 +02:00
Petr Mrázek
0946c7c138 NOISSUE basic code for downloading JREs from Mojang
Not integrated yet, but the logic has tests and shouldn't
be too shaky. Integration comes next.
2020-10-11 23:20:35 +02:00
Petr Mrázek
5180536cc3 NOISSUE add a way to use native system versions of OpenAL and GLFW
If your OS comes with patched/fixed/newer versions of those,
you can now check the checkboxes and stop using the old ones
shipped by Mojang.
2020-09-10 23:10:17 +02:00
Petr Mrázek
27e43d037e NOISSUE remove translations from CheckJava launch step 2020-09-07 22:33:57 +02:00
Petr Mrázek
feae420450 NOISSUE add 'java.vendor' to the checker and display/log the value 2020-09-07 22:28:41 +02:00
AbigailBuccaneer
6995a2e1ba Avoid undefined behaviour when byteswapping
`a << b` is undefined when `a` is negative, and `a >> b` is
implementation-defined. The correct thing to do here is to cast to
unsigned, swap the bytes there and then swap back.

This also improves performance on some compilers: Clang is smart enough
to recognise that we're byteswapping here and reduce it to a single
`bswap` instruction on x86_64, but only for the unsigned versions.
2020-08-24 18:52:08 +01:00
AbigailBuccaneer
0f2757f000 Remove extra semicolons from big-endian handling
Fixes #3296.
2020-08-24 17:43:58 +01:00
Petr Mrázek
8a0027c73a NOISSUE Add world icons and world icon reset button 2020-08-22 01:34:55 +02:00
Petr Mrázek
c6c9feb3a2 NOISSUE attempt to fix build on macOS 2020-08-21 02:40:19 +02:00
Jamie Mansfield
b0f5f4cb13 GH-3095 New FTB platform support
Models are based on the models from my go-modpacksch library.

License:
========
The MIT License (MIT)

Copyright (c) Jamie Mansfield <https://www.jamiemansfield.me/>
Copyright (c) contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
2020-08-21 02:24:29 +02:00
Qaz
f6097ef2b7 Update iron.png 2020-08-19 17:45:59 +02:00
Qaz
bb88697354 Update gold.png 2020-08-19 17:45:57 +02:00
Qaz
762a019a49 Update diamond.png 2020-08-19 17:45:53 +02:00
Qaz
f44bab0e0c Update brick.png 2020-08-19 17:45:50 +02:00
Qaz
883dbe2933 Update stone.png 2020-08-19 17:42:16 +02:00
Qaz
6753e217a9 Update planks.png
Replace it with the new plank texture
2020-08-19 17:40:01 +02:00
Petr Mrázek
e7f373496e GH-3234 Add support for custom meta URLs at build time
This is not particularly interesting for non-developers.
Also includes some internal restructuring of URL constants in general.
2020-07-18 16:22:05 +02:00
Petr Mrázek
3158082b16 Merge pull request #3239 from haykam821/intermediary-errors
Fix Fabric loader error messages being reused for intermediary mappings
2020-07-18 12:31:45 +02:00
Petr Mrázek
814407ccec Merge pull request #3241 from Bowser65/feature/render-overlay
Minor: Render the skin overlay in SkinUtils
2020-07-18 12:31:14 +02:00
Bowser65
155f4f7471 NOISSUE render the skin overlay in SkinUtils 2020-07-12 19:36:40 +02:00
haykam821
90d45d2abf Fix Fabric loader error messages being reused for intermediary mappings 2020-07-09 12:17:59 -04:00
Petr Mrázek
cd57e354fe NOISSUE fix a bunch of valgrind errors 2020-06-28 13:25:53 +02:00
Petr Mrázek
a0ef20a264 NOISSUE rename ComponentList to PackProfile
It's not just components, so the naming needed cleaning up.
2020-06-27 12:02:31 +02:00
Petr Mrázek
4ca62916f5 GH-3189 update the nbt library 2020-06-07 21:32:42 +02:00
Petr Mrázek
41590f2e50 NOISSUE Switch twitch modpack page to use a QListView
This makes the lazy loading of the paginated search results
actually work.
2020-06-07 11:15:17 +02:00
Petr Mrázek
03dc8ddf81 Merge pull request #3169 from kb-1000/fix-accessibility-2
Allow compiling MultiMC using Qt builds without accessibility again
2020-05-30 15:34:18 +02:00
kb1000
79208a7383 Allow compiling MultiMC using Qt builds without accessibility again 2020-05-30 15:32:38 +02:00
Petr Mrázek
3680f1e599 Merge pull request #2864 from Hubcapp/patch-1
Update BUILD.md
2020-05-29 01:56:53 +02:00
Petr Mrázek
c4779e5a35 Merge pull request #3127 from OverMighty/develop
fix: add support for args with spaces to MultiMC::messageReceived()
2020-05-29 01:55:27 +02:00
Petr Mrázek
aad34f3679 Merge pull request #3165 from kvverti/wsl-messages
Clarify WSL error messages
2020-05-29 00:59:28 +02:00
Thalia Nero
0047ca454f Clarify WSL error messages 2020-05-28 17:59:54 -04:00
Petr Mrázek
07a1052c69 Fix. 2020-05-28 23:31:50 +02:00
Petr Mrázek
ba6a97557a NOISSUE prevent evil from winning 2020-05-28 23:17:50 +02:00
Petr Mrázek
e7f79c9076 Remove some old forge hacks
Forge apparently removed all `.pack.xz` files without warning.
It broke a bunch of stuff, as always. But it also means we don't need some ugly code anymore.

This is removed:

- Support for 'forge-pack-xz' and the forge-specific file download compression.
- The pack200 library we no longer need.

This stays:

- The LZMA decompression library - we may still want to use it.
2020-05-19 15:13:16 +02:00
Petr Mrázek
9eaa636908 GH-2802 remove extra remapping of indexes in VersionPage model 2020-05-17 19:56:59 +02:00
Petr Mrázek
997c25620a GH-3131 fix not working with proxy ports > 32767 2020-05-17 13:59:30 +02:00
OverMighty
bbcacec6ec fix: add support for args with spaces to MultiMC::messageReceived()
Previously, when the main instance of MultiMC would receive an `import`
or `launch` message from another instance, it would split the message on
each space, and only read the first word of the argument (zip path/URL
or instance ID). This commit fixes that problem by sectioning the
message string instead.
2020-05-04 11:37:02 +02:00
OverMighty
313a6574c1 chore: pull changes from upstream 2020-05-04 11:36:41 +02:00
Petr Mrázek
5ca5661c23 NOISSUE expose twitch pack url, description and author list 2020-04-29 21:17:51 +02:00
Petr Mrázek
cb0887ffa8 Merge branch 'develop' of https://github.com/Moresteck/MultiMC5 into develop 2020-04-29 20:32:47 +02:00
Moresteck
1f9378af9f NOISSUE Fixed online saving in early Classic versions 2020-04-19 20:31:04 +01:00
John C. Allwein
5c921589f1 NOISSUE fix compiling of api/logic/Version.cpp
-Wrange-loop-construct triggers this error in clang
2020-04-19 14:36:42 -04:00
Petr Mrázek
130e1263b6 Merge pull request #3046 from Ghalid/mac-build-instructions
Fix macOS build instructions so that git submodule commands succeed
2020-04-11 02:33:02 +02:00
Petr Mrázek
296ff6de96 NOISSUE Add pagination support to twitch pack search
Try searching for 'craft'. Now it gives ALL the results, not just
the first page of 25.
2020-04-01 02:08:11 +02:00
Petr Mrázek
5e951299b2 NOISSUE fix build 2020-04-01 00:59:18 +02:00
Petr Mrázek
3ff93a4216 NOISSUE Bare-bones twitch pack browser 2020-04-01 00:44:24 +02:00
Petr Mrázek
21ac860e27 Bump dev version to 0.6.12 2020-03-29 04:03:04 +02:00
Petr Mrázek
3ad9ea507e NOISSUE update version number, changelog and credits in about dialog 2020-03-29 03:12:57 +02:00
Petr Mrázek
c9e851f12f GH-2544 enable Forge install button for >= 1.13 2020-03-28 15:55:12 +01:00
Petr Mrázek
0281845fc8 GH-2544 allow adding files to libraries without affecting classpath
This is done by adding library-like objects into the `mavenFiles`
list in version JSONs.
2020-03-27 02:23:15 +01:00
Petr Mrázek
e6cc65cf69 NOISSUE no means no, #2 2020-03-26 10:38:13 +01:00
Petr Mrázek
ebb17cb5f8 NOISSUE no means no. 2020-03-26 03:51:14 +01:00
Ghalid
164bb6c78c NOISSUE change directory before running git submodule commands in mac instructions 2020-03-22 12:17:56 -04:00
Petr Mrázek
69490535d0 Merge pull request #2979 from Brottweiler/desktop-entry-tweak
Tweak GenericName for Linux desktop entry
2020-02-25 18:02:09 +01:00
Brottweiler
95a09ba099 GH-2979 Tweak GenericName 2020-02-24 19:39:22 +01:00
Petr Mrázek
6cb956b45b NOISSUE Nice. 2020-02-24 18:59:36 +01:00
OverMighty
47fa7b3f8c GH-2988 add --import command-line option
When specified, opens the "Import from zip" dialog as soon as the main
window is shown, with the URL field prefilled with the argument given to
the option.

Closes #2998
2020-02-24 18:52:26 +01:00
OverMighty
381c12547f feat: allow telling the main process to import a zip
This commit enhances the IPC message system and adds a new IPC command
in order to allow secondary MultiMC processes that were started by
executing MultiMC with the `--import` command-line option to tell the
main MutliMC process to open the "Import from zip" dialog window and
prefill the URL field with the specified URL.
2020-02-09 16:17:41 +01:00
OverMighty
060d6955e1 style: follow upstream code style 2020-02-09 15:45:31 +01:00
Petr Mrázek
d58481e0de NOISSUE fix some changelog wording 2020-02-09 00:03:20 +01:00
Chris Lane
a6085db0ae Add manpage doc 2020-02-08 16:58:45 +00:00
Petr Mrázek
08f85f1a93 Update changelog and set version to 0.6.8 2020-02-08 15:00:20 +01:00
OverMighty
bc04d89c32 feat: add --import command-line option
When specified, opens the "Import from zip" dialog as soon as the main
window is shown, with the URL field prefilled with the argument given to
the option.

Closes #2998
2020-02-05 12:37:09 +01:00
Petr Mrázek
bc98181ec2 GH-2769 add an option to not copy play time when copying instances 2020-01-09 15:31:32 +01:00
Petr Mrázek
6a095deea6 GH-2832 add .minecraft and libraries buttons to version page 2020-01-09 13:45:46 +01:00
Petr Mrázek
355e5e24da GH-2819 mod list filter now also looks at descriptions and authors 2020-01-08 21:12:45 +01:00
Petr Mrázek
8bdff97ac0 GH-2839 remove username wording from login and account dialogs 2020-01-08 13:37:05 +01:00
Petr Mrázek
6288805f37 GH-2853 fix collapsing state being sticky in group view
Now it resets properly and collapsing a group doesn't make
all subsequent clicks collapse groups.
2020-01-08 09:00:54 +01:00
Petr Mrázek
0d157d86b5 NOISSUE add wget as a dependency for deb package 2020-01-08 06:28:42 +01:00
Petr Mrázek
f413e61cd8 NOISSUE Do not crash when dependencies are customized and conflict 2020-01-08 04:41:47 +01:00
Petr Mrázek
3581f5384f GH-2880 make services status widget open a valid page
https://help.mojang.com/ no longer has API status
2020-01-07 08:56:00 +01:00
Petr Mrázek
480b298635 NOISSUE Attempt to add build status and test teamcity build reporting 2019-11-27 00:35:42 +01:00
Petr Mrázek
b54b25231e Remove travis-ci and github actions leftovers 2019-11-26 21:09:37 +01:00
Petr Mrázek
43628556ed NOISSUE fix comment in Commandline.h 2019-11-26 19:06:45 +01:00
Petr Mrázek
b5adff14ab NOISSUE remove dead reference to TwitchPage.ui 2019-11-18 02:38:08 +01:00
Petr Mrázek
af5120c828 GH-2859 remove twitch page and modpack import from URL
The functionality was broken, beyond repair and an ongoing maintenance
nightmare.
2019-11-18 00:38:36 +01:00
Petr Mrázek
47ed2f48d4 NOISSUE put legacy FTB support in a namespace, fix its base URL 2019-11-03 23:48:12 +01:00
Petr Mrázek
0c9340a3d2 NOISSUE fix translation string for Twitch drop area 2019-10-14 23:51:36 +02:00
Petr Mrázek
9cc5ebcdd1 GH-2859 improve UI for twitch pack import with drag&drop 2019-10-14 02:31:53 +02:00
Petr Mrázek
c60647523e NOISSUE remove remains of what could have been technic integration 2019-10-14 01:05:38 +02:00
Petr Mrázek
9165232ba4 NOISSUE remove unused 'PackagesPage' 2019-10-13 22:31:14 +02:00
Petr Mrázek
31d0507e07 NOISSUE make the main window title only 'MultiMC' for screen readers 2019-10-05 22:51:55 +02:00
Petr Mrázek
cca766cfd9 Merge pull request #2871 from kb-1000/fix-accessibility-fix
Remove include of qtguiglobal.h
2019-10-01 16:15:45 +02:00
kb1000
0d41bc5e13 Remove include of qtguiglobal.h 2019-10-01 15:55:40 +02:00
Petr Mrázek
e27309d08a Merge pull request #2870 from kb-1000/fix-accessibility
Add checks for QT_NO_ACCESSIBILITY to prevent build issues with Qt without accessibility
2019-10-01 15:40:39 +02:00
kb1000
dec6759e61 Add checks for QT_NO_ACCESSIBILITY to prevent build issues with Qt without accessibility 2019-10-01 14:28:06 +02:00
Petr Mrázek
ce7917048a NOISSUE remove more html rom About dialog 2019-09-30 23:50:32 +02:00
Hubcapp
e6b5d9656a Update BUILD.md
the `git clone` command provided doesn't work and gives this error on Debian
```
$ git clone --recursive git@github.com:MultiMC/MultiMC5.git src
Cloning into 'src'...
The authenticity of host 'github.com (192.30.253.113)' can't be established.
RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'github.com,192.30.253.113' (RSA) to the list of known hosts.
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
```
I am suggesting an alternative command to provide to users in BUILD.md instead.
2019-09-27 09:01:25 -05:00
Petr Mrázek
e6936212d6 NOISSUE remove some ugly html strings to make translating slightly nicer 2019-09-27 00:23:30 +02:00
Petr Mrázek
1210d3abf1 NOISSUE fix display of european portuguese in language lists 2019-09-27 00:23:03 +02:00
Petr Mrázek
ffe84d6ec7 NOISSUE remove some dead things 2019-09-26 20:08:47 +02:00
Petr Mrázek
19015de258 NOISSUE correct some parts of the README, one more translation string change 2019-09-26 09:03:25 +02:00
Petr Mrázek
5c0c26cd25 TRANSLATIONS-82 fix typos: resove -> resolve 2019-09-25 22:31:09 +02:00
Petr Mrázek
4cc7427eb4 TRANSLATIONS-73 remove some strings that don't show up in the UI 2019-09-25 22:29:50 +02:00
Petr Mrázek
6531aaa7bc Try using github actions
Let's see how badly this goes
2019-09-19 10:56:11 +02:00
Petr Mrázek
a35a2e877e NOISSUE remove nonsensical logic related to 'MultiMC.app/' prefixes in update manifests 2019-09-19 01:13:02 +02:00
Petr Mrázek
4e93c4d012 NOISSUE escape tcversion more 2019-09-17 01:26:37 +02:00
Petr Mrázek
7bb23b4142 NOISSUE add some escaping to make the tc version print actually do something 2019-09-17 01:22:00 +02:00
Petr Mrázek
b420f4bafb Merge pull request #2849 from AshleighTheCutie/develop
Message cleanup
2019-09-17 01:12:45 +02:00
Petr Mrázek
0e0a017175 NOISSUE add a way to extract the version into a TC variable 2019-09-17 00:53:30 +02:00
Ashleigh
137fe7e3c0 Cleaned up messages, made some more descriptive, made some nicer. 2019-09-14 22:31:13 -05:00
Ashleigh
9689e5cea7 Added text notifying that password change may cause logout 2019-09-14 22:12:23 -05:00
Petr Mrázek
1ede75aa79 Merge pull request #2848 from AshleighTheCutie/develop
Window Size to 800x600
2019-09-15 04:34:03 +02:00
Ashleigh
9ee3a84817 Window Size to 800x600 2019-09-14 21:15:51 -05:00
Petr Mrázek
8750ca8b36 NOISSUE add bee icon 2019-08-26 02:25:42 +02:00
Petr Mrázek
1747f413b9 GH-851 save, load and use group expansion status 2019-08-20 02:58:27 +02:00
Petr Mrázek
6d975748c0 Merge pull request #2796 from QuImUfu/bug_fix
GH-2787 fix "Download All" button
2019-08-14 22:12:58 +02:00
Petr Mrázek
b050c7c075 Merge pull request #2803 from kb-1000/escape-semicolon
Escape ; too in instance folder names
2019-08-14 22:12:39 +02:00
Kaeptm Blaubaer
84e0cb1daa Escape ; too in instance folder names 2019-08-13 07:39:00 +02:00
Petr Mrázek
5074a97cb3 NOISSUE tweak toolbar and custom widget titles for better translations 2019-08-10 20:12:42 +02:00
Petr Mrázek
441e8980b8 NOISSUE fix small memory leaks 2019-08-10 19:58:58 +02:00
Johannes Michael Andreas Merl
84c53273ce GH-2787 fix "Download All" button 2019-08-09 00:11:42 +02:00
Petr Mrázek
c291946d2a NOISSUE do not lose selection on mod enable/disable toggle 2019-08-05 00:46:59 +02:00
Petr Mrázek
dfb30d9139 NOISSUE warn users about MS Edge being bad 2019-08-04 21:14:59 +02:00
Petr Mrázek
4ed67413ac GH-988 add ability to toggle mods with keyboard 2019-08-04 21:13:50 +02:00
Petr Mrázek
d31184f9a4 GH-2738 check for being in the temp folder better 2019-08-04 18:11:55 +02:00
Petr Mrázek
b75ba53d4b GH-2785 fix crash caused by starting multiple mod folder update tasks 2019-08-04 11:12:19 +02:00
Petr Mrázek
9037873928 NOISSUE update changelog 2019-08-04 06:26:09 +02:00
Petr Mrázek
ce4a55bc3b NOISSUE fix listing of mods in log, improve display with unicode 2019-08-04 05:08:40 +02:00
Petr Mrázek
6b82e942d0 NOISSUE fix build on linux 2019-08-04 03:39:25 +02:00
Petr Mrázek
a3ffa3d665 NOISSUE asynchronous, parallel mod folder listing and mod resolving 2019-08-04 03:27:53 +02:00
Petr Mrázek
7d13e31198 NOISSUE refactor Mod a bunch, get rid of dead code 2019-08-03 05:30:46 +02:00
Petr Mrázek
40c9af1a8b NOISSUE remove dependency of legacy mod list on the Mod class 2019-08-03 03:12:48 +02:00
Petr Mrázek
f5f3149dcf NOISSUE update changelog and version 2019-08-03 00:48:34 +02:00
Petr Mrázek
7b00d47fe0 NOISSUE tweak UI geometry and remove old language selection 2019-08-02 23:52:19 +02:00
Petr Mrázek
930d39b5f2 GH-2550 soring of mods by enabled status, cascade sorting to name and version 2019-07-31 01:28:55 +02:00
Petr Mrázek
bafcf93eb1 NOISSUE fix bug with drag & drop not working with empty mod list 2019-07-31 01:27:35 +02:00
Petr Mrázek
bd93c3b4e0 NOISSUE fix build 2019-07-30 01:25:37 +02:00
Petr Mrázek
09f7a426ab GH-2722 GH-2762 Improve mod list sorting
Sorting by version understands version numbers
Sorting by name removes 'The' prefixes before sorting
2019-07-30 01:16:56 +02:00
Petr Mrázek
e4fd50e210 NOISSUE make the language translation prompt translateable 2019-07-30 01:16:02 +02:00
Petr Mrázek
3ee5a63c5c NOISSUE make notes page focusable with tab key 2019-07-25 01:13:47 +02:00
Petr Mrázek
7dfe73df0c NOISSUE add context menus to pages with toolbars 2019-07-25 01:02:30 +02:00
Petr Mrázek
c3e61536a3 NOISSUE automatically open the log page when starting the instance 2019-07-24 00:24:02 +02:00
Petr Mrázek
a0e45c5d1d NOISSUE fix build 2019-07-23 01:05:23 +02:00
Petr Mrázek
bf38021937 NOISSUE improve toolbars 2019-07-23 00:48:14 +02:00
Petr Mrázek
1e5b595923 NOISSUE fix build failures 2019-07-22 01:44:19 +02:00
Petr Mrázek
d6c6653872 NOISSUE Add basic accessibility support to GroupView 2019-07-22 01:40:52 +02:00
Petr Mrázek
3b32730526 Merge pull request #2758 from telans/patch-1
Fix translation repository link
2019-07-21 01:33:31 +02:00
telans
1703dbeb57 Fix translation repository link 2019-07-21 10:50:30 +12:00
Petr Mrázek
81fdde6fdd NOISSUE convert accounts page to use a toolbar for the side menu 2019-07-19 08:29:31 +02:00
Petr Mrázek
3d5869e1cf NOISSUE fix overly large margins in the instance settings page 2019-07-17 02:17:09 +02:00
Petr Mrázek
edc5378333 NOISSUE add spacer to the screenshot page toolbar 2019-07-17 02:09:54 +02:00
Petr Mrázek
95febe5436 NOISSUE convert rest of the instance pages to use toolbars for side menus 2019-07-17 02:01:29 +02:00
Petr Mrázek
5b153a5165 GH-2748 disable the version page toolbar labels 2019-07-16 08:58:00 +02:00
Petr Mrázek
decd4ae7ab NOISSUE Make mod folder pages use toolbars instead of button layouts 2019-07-16 01:30:53 +02:00
Petr Mrázek
2eec1df1a0 NOISSUE hide main toolbar toggle action instead of working around it 2019-07-16 01:30:09 +02:00
Petr Mrázek
6fde775b90 NOISSUE Show Version page while the instancer is running.
All controls are disabled.
2019-07-15 23:16:34 +02:00
Petr Mrázek
80b3efff11 NOISSUE Do not hide mods list pages when the instance is running.
Instead, disable (most of) the controls.
2019-07-15 01:07:21 +02:00
Petr Mrázek
e4273d6a17 GH-358 Make version page use a toolbar for all the actions
This should make it possible to make it fit on small screens again.
2019-07-14 05:37:10 +02:00
1386 changed files with 41590 additions and 26189 deletions

View File

@@ -1,5 +0,0 @@
{
"project_id": "MultiMC5",
"conduit_uri": "http://ph.multimc.org"
}

View File

@@ -1,25 +0,0 @@
UseTab: false
IndentWidth: 4
TabWidth: 4
ConstructorInitializerIndentWidth: 4
AccessModifierOffset: -4
IndentCaseLabels: false
IndentFunctionDeclarationAfterType: false
NamespaceIndentation: None
BreakBeforeBraces: Allman
AllowShortIfStatementsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
ColumnLimit: 160
MaxEmptyLinesToKeep: 1
Standard: Cpp11
Cpp11BracedListStyle: true
SpacesInParentheses: false
SpaceInEmptyParentheses: false
SpacesInCStyleCastParentheses: false
SpaceAfterControlStatementKeyword: true
AlignTrailingComments: true
SpacesBeforeTrailingComments: 1

View File

@@ -1,51 +0,0 @@
<!--
Before submitting this issue, please make sure you have:
1. Filled out this form completely, the only optional field is "additional info".
- Use as many details as possible and state the problem clearly.
2. Proof-read your ENTIRE issue report.
- Grammar and spelling mistakes make issue reports harder to understand.
3. Made sure your problem is not caused by an issue in your own modpack.
- We provide support for MultiMC, not your modpack. Problems with your modpack will be ignored.
4. Given the issue a descriptive title.
- A good title includes a brief summary of the issue and avoids things such as "Help" and "What?!".
Use of UPPERCASE is discouraged, as it reads like someone is screaming.
5. Place all information below the ---- of lines.
- It makes the issue look pretty
If you believe your issue to be a bug, please make sure you check the wiki page: https://github.com/MultiMC/MultiMC5/wiki/Report-a-Bug
-->
System Information
-----------------------------
MultiMC version:
Operating System:
Summary of the issue or suggestion:
----------------------------------------------
What should happen:
------------------------------
Steps to reproduce the issue (Add more if needed):
-------------------------------------------------------------
1.
2.
3.
Suspected cause:
---------------------------
Logs/Screenshots:
----------------------------
[//]: # (Please refer to https://github.com/MultiMC/MultiMC5/wiki/Log-Upload for instructions on how to attach your logs.)
Additional Info:
---------------------------

51
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: Bug Report
description: File a bug report
labels: [bug, needs-triage]
body:
- type: markdown
attributes:
value: |
If you need help with running Minecraft, please visit us [on our Discord](https://discord.gg/multimc) 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 [FAQ](https://github.com/MultiMC/Launcher/wiki/FAQ) and it has not answered your question
* Your bug is not caused by Minecraft or any mods you have installed.
* Your issue has not been reported before, [make sure to use the search function!](https://github.com/MultiMC/Launcher/issues)
**Do not forget to give your issue a descriptive title.** "Bug in the instance screen" makes it hard to distinguish issues at a glance.
- type: dropdown
attributes:
label: Operating System
description: If you know this bug occurs on multiple operating systems, select all you have tested.
multiple: true
options:
- Windows
- macOS
- Linux
- Other
- type: textarea
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!
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"
validations:
required: true
- type: textarea
attributes:
label: Suspected cause
description: If you know what could be causing this bug, describe it here.
validations:
required: false
- type: checkboxes
attributes:
label: This issue is unique
options:
- label: I have searched the issue tracker and did not find an issue describing my bug.
required: true

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1 @@
blank_issues_enabled: true

38
.github/ISSUE_TEMPLATE/suggestion.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
name: Suggestion
description: Make a suggestion
labels: [idea, needs-triage]
body:
- type: markdown
attributes:
value: |
### Use this form to suggest a feature for MultiMC.
- type: input
attributes:
label: Role
description: In what way do you use MultiMC that needs this feature?
placeholder: I play modded Minecraft.
validations:
required: true
- type: input
attributes:
label: Suggestion
description: What do you want MultiMC to do?
placeholder: I want the cat button to meow.
validations:
required: true
- type: input
attributes:
label: Benefit
description: Why do you need MultiMC to do this?
placeholder: so that I can always hear a cat when I need to.
validations:
required: true
- type: checkboxes
attributes:
label: This suggestion is unique
options:
- label: I have searched the issue tracker and did not find an issue describing my suggestion, especially not one that has been rejected.
required: true
- type: textarea
attributes:
label: You may use the editor below to elaborate further.

20
.github/workflows/dispatch.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: Dispatcher
on:
push:
branches: ['6']
jobs:
dispatch:
name: Dispatch
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Extract branch name
shell: bash
run: echo "branch=$(echo ${GITHUB_REF#refs/heads/})" >>$GITHUB_OUTPUT
id: extract_branch
- name: Dispatch to workflows
run: |
curl -H "Accept: application/vnd.github.everest-preview+json" \
-H "Authorization: token ${{ secrets.DISPATCH_TOKEN }}" \
--request POST \
--data '{"event_type": "push_to_main_repo", "client_payload": { "branch": "${{ steps.extract_branch.outputs.branch }}" }}' https://api.github.com/repos/MultiMC/Build/dispatches

286
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,286 @@
name: CI
on:
push:
branches: [ develop ]
pull_request:
branches: [ develop ]
workflow_dispatch:
schedule:
- cron: "0 0 1 * *"
jobs:
build-linux:
name: build-linux
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
with:
submodules: 'recursive'
- name: Install Dependencies
run: |
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32
sudo add-apt-repository 'deb http://dk.archive.ubuntu.com/ubuntu/ bionic main'
sudo add-apt-repository 'deb http://dk.archive.ubuntu.com/ubuntu/ bionic universe'
sudo apt update
sudo apt install libgl1-mesa-dev qttools5-dev g++-5 gcc-5
- name: Build
run: |
export JAVA_HOME=$JAVA_HOME_8_X64
mkdir build
cd build
cmake \
-DCMAKE_C_COMPILER=/usr/bin/gcc-5 \
-DCMAKE_CXX_COMPILER=/usr/bin/g++-5 \
-DCMAKE_BUILD_TYPE=Release \
-DLauncher_NOTIFICATION_URL:STRING=https://files.multimc.org/notifications.json \
-DCMAKE_INSTALL_PREFIX:PATH=/home/runner/UltimMC/UltimMC \
-DLauncher_UPDATER_BASE=https://files.multimc.org/update/ \
-DLauncher_PASTE_EE_API_KEY:STRING=utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ \
-DLauncher_ANALYTICS_ID:STRING=UA-87731965-2 \
-DLauncher_LAYOUT=lin-nodeps \
-DLauncher_BUILD_PLATFORM=lin64 \
-DLauncher_BUG_TRACKER_URL=https://github.com/UltimMC/Launcher/issues \
-DLauncher_EMBED_SECRETS=On \
$GITHUB_WORKSPACE
- name: Compile
run: |
cd build
make -j$(nproc)
- name: Test
run: |
cd build
make test
cmake -E remove_directory "/home/runner/UltimMC/UltimMC"
- name: Install
run: |
cd build
make install
chmod +x /home/runner/UltimMC/UltimMC/UltimMC
chmod +x /home/runner/UltimMC/UltimMC/bin/UltimMC
- name: Upload Artifacts
uses: actions/upload-artifact@main
with:
name: mmc-cracked-lin64
path: /home/runner/UltimMC
build-windows:
name: build-windows
runs-on: windows-latest
steps:
- uses: actions/checkout@main
with:
submodules: 'recursive'
- name: Cache Qt
uses: actions/cache@main
id: qt-cached
with:
path: "D:/Qt"
key: ${{ runner.os }}-qt56-installed-d
- name: Cache Qt Installer
uses: actions/cache@main
if: steps.qt-cached.outputs.cache-hit != 'true'
id: installer-cached
with:
path: "installer.exe"
key: ${{ runner.os }}-qt56-installer
- name: Create QtAccount File
if: steps.qt-cached.outputs.cache-hit != 'true'
run: |
mkdir C:/Users/runneradmin/AppData/Roaming/Qt/
curl https://gist.github.com/Neptune650/1086e0a3126be6a66580b71afcf8bd99/raw/797d8b90edf07ce88f265b38a573cc6b1fb45bfb/qtaccount.txt --output C:/Users/runneradmin/AppData/Roaming/Qt/qtaccount.ini
- name: Download Qt Installer
if: steps.installer-cached.outputs.cache-hit != 'true' && steps.qt-cached.outputs.cache-hit != 'true'
run: curl https://download.qt.io/new_archive/qt/5.6/5.6.3/qt-opensource-windows-x86-mingw492-5.6.3.exe --output installer.exe
- name: Download Qt non-Interactive Script
if: steps.qt-cached.outputs.cache-hit != 'true'
run: curl https://gist.githubusercontent.com/Neptune650/aa6c051abc17e7d9d609add7f6dfd16a/raw/074dedb7525c0ffc010b39871615b008c2efbcd6/qt-installer-noninteractive.qs --output nonInteractive.qs
- name: Install Qt
if: steps.qt-cached.outputs.cache-hit != 'true'
shell: cmd
run: installer.exe -v --script nonInteractive.qs --silent
- name: Setup CMake
run: |
curl -L https://github.com/Kitware/CMake/releases/download/v3.30.4/cmake-3.30.4-windows-i386.zip -o cmake.zip
unzip cmake.zip
- name: Setup JDK
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '8'
architecture: x86
- name: Setup zlib
run: |
mkdir zlib
cd zlib
C:\msys64\usr\bin\wget.exe -O zlib.zip https://downloads.sourceforge.net/project/gnuwin32/zlib/1.2.3/zlib-1.2.3-bin.zip
C:\msys64\usr\bin\wget.exe -O zliblibs.zip https://downloads.sourceforge.net/project/gnuwin32/zlib/1.2.3/zlib-1.2.3-lib.zip
unzip zlib.zip
unzip zliblibs.zip
- name: Setup OpenSSL
run: |
mkdir OpenSSL
cd OpenSSL
curl -L https://files.catbox.moe/ctwswu.dll -o libeay32.dll
curl -L https://files.catbox.moe/ie9e77.dll -o ssleay32.dll
- name: Build
shell: cmd
if: steps.build-cached.outputs.cache-hit != 'true'
run: |
for /f "tokens=*" %%n in ('powershell -NoLogo -Command "$(ls $pwd\cmake-*-windows-i386\bin).Fullname"') do @(set PATHCM=%%n)
set PATH=D:\Qt\5.6.3\mingw49_32\bin;D:\Qt\Tools\mingw492_32\bin;
set PATH=%CD%\zlib;%CD%\zlib\bin;%CD%\zlib\lib;%CD%\zlib\include;%PATH%
set PATH=%CD%\OpenSSL;%PATH%
set PATH=%PATHCM%;%PATH%
mkdir build
cd build
cmake ^
-DCMAKE_C_COMPILER=gcc ^
-DCMAKE_CXX_COMPILER=g++ ^
-DCMAKE_BUILD_TYPE=Release ^
-DLauncher_NOTIFICATION_URL:STRING=https://files.multimc.org/notifications.json ^
-DCMAKE_INSTALL_PREFIX:PATH="D:/UltimMC/UltimMC" ^
-DCMAKE_PREFIX_PATH="D:\Qt\5.6.3\mingw49_32" ^
-DQt5_DIR="D:\Qt\5.6.3\mingw49_32" ^
-DLauncher_UPDATER_BASE=https://files.multimc.org/update/ ^
-DLauncher_PASTE_EE_API_KEY:STRING=utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ ^
-DLauncher_ANALYTICS_ID:STRING=UA-87731965-2 ^
-DLauncher_LAYOUT=win-bundle ^
-DLauncher_BUILD_PLATFORM=win32 ^
-DLauncher_BUG_TRACKER_URL=https://github.com/UltimMC/Launcher/issues ^
-DLauncher_EMBED_SECRETS=On ^
-G "MinGW Makefiles" ^
..
- name: Compile
shell: cmd
run: |
for /f "tokens=*" %%n in ('powershell -NoLogo -Command "$(ls $pwd\cmake-*-windows-i386\bin).Fullname"') do @(set PATHCM=%%n)
set PATH=D:\Qt\5.6.3\mingw49_32\bin;D:\Qt\Tools\mingw492_32\bin;
set PATH=%CD%\zlib;%CD%\zlib\bin;%PATH%
set PATH=%CD%\OpenSSL;%PATH%
set PATH=%PATHCM%;%PATH%
set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;%PATH%
cd build
mingw32-make -j%NUMBER_OF_PROCESSORS%
- name: Test
shell: cmd
run: |
for /f "tokens=*" %%n in ('powershell -NoLogo -Command "$(ls $pwd\cmake-*-windows-i386\bin).Fullname"') do @(set PATHCM=%%n)
set PATH=D:\Qt\5.6.3\mingw49_32\bin;D:\Qt\Tools\mingw492_32\bin;
set PATH=%CD%\zlib;%CD%\zlib\bin;%PATH%
set PATH=%CD%\OpenSSL;%PATH%
set PATH=%PATHCM%;%PATH%
set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;%PATH%
cd build
mingw32-make test
cmake -E remove_directory "D:/UltimMC/UltimMC"
- name: Install
shell: cmd
run: |
for /f "tokens=*" %%n in ('powershell -NoLogo -Command "$(ls $pwd\cmake-*-windows-i386\bin).Fullname"') do @(set PATHCM=%%n)
set PATH=D:\Qt\5.6.3\mingw49_32\bin;D:\Qt\Tools\mingw492_32\bin;
set PATH=%CD%\zlib;%CD%\zlib\bin;%PATH%
set PATH=%CD%\OpenSSL;%PATH%
set PATH=%PATHCM%;%PATH%
set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;%PATH%
cd build
mingw32-make install
- name: Copy OpenSSL
shell: cmd
run: |
cp OpenSSL/ssleay32.dll D:/UltimMC/UltimMC/ssleay32.dll
cp OpenSSL/libeay32.dll D:/UltimMC/UltimMC/libeay32.dll
- name: Upload Artifacts
uses: actions/upload-artifact@main
with:
name: mmc-cracked-win32
path: "D:/UltimMC"
build-mac:
name: build-mac
runs-on: macos-13
steps:
- uses: actions/checkout@main
with:
submodules: 'recursive'
- name: Cache Dependencies
uses: actions/cache@main
with:
path: /Users/runner/Library/Caches/Homebrew
key: ${{ runner.os }}-deps-cache
- name: Install Dependencies
run: |
brew cleanup
brew install qt@5
- name: Build
run: |
mkdir build
cd build
cmake \
-DCMAKE_C_COMPILER=/usr/bin/clang \
-DCMAKE_CXX_COMPILER=/usr/bin/clang++ \
-DCMAKE_BUILD_TYPE=Release \
-DLauncher_NOTIFICATION_URL:STRING=https://files.multimc.org/notifications.json \
-DCMAKE_INSTALL_PREFIX:PATH="/Users/runner/work/UltimMC/build/dist" \
-DCMAKE_PREFIX_PATH="$(brew --prefix qt@5)/lib/cmake" \
-DQt5_DIR="$(brew --prefix qt@5)" \
-DLauncher_UPDATER_BASE=https://files.multimc.org/update/ \
-DLauncher_PASTE_EE_API_KEY:STRING=utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ \
-DLauncher_ANALYTICS_ID:STRING=UA-87731965-2 \
-DLauncher_LAYOUT=mac-bundle \
-DLauncher_BUILD_PLATFORM=osx64-5.15.2 \
-DLauncher_BUG_TRACKER_URL=https://github.com/UltimMC/Launcher/issues \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \
-DLauncher_EMBED_SECRETS=On \
$GITHUB_WORKSPACE
- name: Compile
run: |
cd build
make -j$(sysctl -n hw.logicalcpu)
- name: Test
run: |
cd build
make test
cmake -E remove_directory "/Users/runner/work/UltimMC/build/dist"
- name: Install
run: |
cd build
make install
chmod +x /Users/runner/work/UltimMC/build/dist/UltimMC.app/Contents/MacOS/UltimMC
- name: Upload Artifacts
uses: actions/upload-artifact@main
with:
name: mmc-cracked-osx64
path: /Users/runner/work/UltimMC/build/dist

15
.gitignore vendored
View File

@@ -1,27 +1,31 @@
Thumbs.db
.kdev4
*.kdev4
.user
.directory
resources/CMakeFiles
resources/MultiMCLauncher.jar
*~
*.swp
html/
# Project Files
MultiMC5.kdev4
MultiMC.pro.user
*.pro.user
CMakeLists.txt.user
CMakeLists.txt.user.*
/.project
/.settings
/.idea
cmake-build-*/
Debug
.cache
# Build dirs
build
/build-*
# Install dirs
install
/install-*
# Ctags File
tags
@@ -30,3 +34,6 @@ tags
#OSX Stuff
.DS_Store
branding/
run/

2
.gitmodules vendored
View File

@@ -1,6 +1,8 @@
[submodule "depends/libnbtplusplus"]
path = libraries/libnbtplusplus
url = https://github.com/MultiMC/libnbtplusplus.git
pushurl = git@github.com:MultiMC/libnbtplusplus.git
[submodule "libraries/quazip"]
path = libraries/quazip
url = https://github.com/MultiMC/quazip.git
pushurl = git@github.com:MultiMC/quazip.git

View File

@@ -1,38 +0,0 @@
# General set up
language: cpp
cache: apt
matrix:
include:
- os: linux
dist: precise
sudo: required
compiler: gcc
env: TRAVIS_DIST=precise QT_VERSION=5.4.2
- os: linux
dist: precise
sudo: required
compiler: gcc
env: TRAVIS_DIST=precise QT_VERSION=5.6.2
- os: linux
dist: trusty
sudo: required
compiler: gcc
env: TRAVIS_DIST=trusty QT_VERSION=5.4.2
- os: linux
dist: trusty
sudo: required
compiler: gcc
env: TRAVIS_DIST=trusty QT_VERSION=5.6.2
# Install dependencies
install:
- source travis/prepare.sh # installs qt and cmake. need to source because some env vars are set from there
# Actual work
before_script:
- mkdir build
- cd build
- cmake -DCMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH ..
script:
- make -j4 && make test ARGS="-V"

185
BUILD.md
View File

@@ -1,185 +0,0 @@
Build Instructions
==================
# Contents
* [Note](#note)
* [Getting the source](#source)
* [Linux](#linux)
* [Windows](#windows)
* [OS X](#os-x)
# Note
MultiMC is a portable application and is not supposed to be installed into any system folders.
That would be anything outside your home folder. Before runing `make install`, make sure
you set the install path to something you have write access to. Never build this under
an administrator/root level account. Don't use `sudo`. It won't work and it's not supposed to work.
# Getting the source
Clone the source code using git and grab all the submodules:
```
git clone git@github.com:MultiMC/MultiMC5.git
git submodule init
git submodule update
```
# Linux
Getting the project to build and run on Linux is easy if you use any modern and up-to-date linux distribution.
## Build dependencies
* A C++ compiler capable of building C++11 code.
* Qt 5.6+ Development tools (http://qt-project.org/downloads) ("Qt Online Installer for Linux (64 bit)") or the equivalent from your package manager. It is always better to use the Qt from your distribution, as long as it has a new enough version.
* cmake 3.1 or newer
* zlib (for example, `zlib1g-dev`)
* Java JDK 8 (for example, `openjdk-8-jdk`)
* GL headers (for example, `libgl1-mesa-dev`)
### Building from command line
You need a source folder, a build folder and an install folder.
Let's say you want everything in `~/MultiMC/`:
```
# make all the folders
mkdir ~/MultiMC && cd ~/MultiMC
mkdir build
mkdir install
# clone the complete source
git clone --recursive git@github.com:MultiMC/MultiMC5.git src
# configure the project
cd build
cmake -DCMAKE_INSTALL_PREFIX=../install ../src
# build & install (use -j with the number of cores your CPU has)
make -j8 install
```
You can use IDEs like KDevelop or QtCreator to open the CMake project if you want to work on the code.
### Installing Qt using the installer (optional)
1. Run the Qt installer.
2. Choose a place to install Qt.
3. Choose the components you want to install.
- You need Qt 5.6.x 64-bit ticked.
- You need Tools/Qt Creator ticked.
- Other components are selected by default, you can untick them if you don't need them.
4. Accept the license agreements.
5. Double check the install details and then click "Install".
- Installation can take a very long time, go grab a cup of tea or something and let it work.
### Loading the project in Qt Creator (optional)
1. Open Qt Creator.
2. Choose `File->Open File or Project`.
3. Navigate to the MultiMC5 source folder you cloned and choose CMakeLists.txt.
4. Read the instructions that just popped up about a build location and choose one.
5. You should see "Run CMake" in the window.
- Make sure that Generator is set to "Unix Generator (Desktop Qt 5.6.x GCC 64bit)".
- Hit the "Run CMake" button.
- You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
- Hit "Finish" if CMake ran successfully.
6. Cross your fingers and press the Run button (bottom left of Qt Creator).
- If the project builds successfully it will run and the MultiMC5 window will pop up.
**If this doesn't work for you, let us know on IRC ([Esper/#MultiMC](http://webchat.esper.net/?nick=&channels=MultiMC))!**
# Windows
Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt Creator. The project will simply not compile using Microsoft build tools, because that's not something we do. If it does compile, it is by chance only.
## Dependencies
* [Qt 5.6+ Development tools](http://qt-project.org/downloads) -- Qt Online Installer for Windows
* [OpenSSL](http://slproweb.com/products/Win32OpenSSL.html) -- Newest Win32 OpenSSL Light
- Microsoft Visual C++ 2008 Redist is required for this, there's a link on the OpenSSL download page above next to the main download.
- We use a custom build of OpenSSL that doesn't have this dependency. For normal development, the custom build is not necessary though.
* [zlib 1.2+](http://gnuwin32.sourceforge.net/packages/zlib.htm) - the Setup is fine
* [Java JDK 8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html)
* [CMake](http://www.cmake.org/cmake/resources/software.html) -- Windows (Win32 Installer)
Put it somewhere on the `PATH`, so that it is accessible from the console.
## Getting set up
### Installing Qt
1. Run the Qt installer
2. Choose a place to install Qt (C:\Qt is the default),
3. Choose the components you want to install
- You need Qt 5.6 (32 bit) ticked,
- You need Tools/Qt Creator ticked,
- Other components are selected by default, you can untick them if you don't need them.
4. Accept the license agreements,
5. Double check the install details and then click "Install"
- Installation can take a very long time, go grab a cup of tea or something and let it work.
### Installing OpenSSL
1. Run the OpenSSL installer,
2. It's best to choose the option to copy OpenSSL DLLs to the `/bin` directory
- If you do this you'll need to add that directory (the default being `C:\OpenSSL-Win32\bin`) to your PATH system variable (Google how to do this, or use this guide for Java: http://www.java.com/en/download/help/path.xml).
### Installing CMake
1. Run the CMake installer,
2. It's easiest if you choose to add CMake to the PATH for all users,
- If you don't choose to do this, remember where you installed CMake.
### Loading the project
1. Open Qt Creator,
2. Choose File->Open File or Project,
3. Navigate to the MultiMC5 source folder you cloned and choose CMakeLists.txt,
4. Read the instructions that just popped up about a build location and choose one,
5. If you chose not to add CMake to the system PATH, tell Qt Creator where you installed it,
- Otherwise you can skip this step.
6. You should see "Run CMake" in the window,
- Make sure that Generator is set to "MinGW Generator (Desktop Qt 5.6.x MinGW 32bit)",
- Hit the "Run CMake" button,
- You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
- Hit "Finish" if CMake ran successfully.
7. Cross your fingers and press the Run button (bottom left of Qt Creator)!
- If the project builds successfully it will run and the MultiMC5 window will pop up,
- Test OpenSSL by making an instance and trying to log in. If Qt Creator couldn't find OpenSSL during the CMake stage, login will fail and you'll get an error.
**These build instructions worked for me (Drayshak) on a fresh Windows 8 x64 Professional install. If they don't work for you, let us know on IRC ([Esper/#MultiMC](http://webchat.esper.net/?nick=&channels=MultiMC))!**
### Compile from command line on Windows
1. If you installed Qt with the web installer, there should be a shortcut called `Qt 5.4 for Desktop (MinGW 4.9 32-bit)` in the Start menu on Windows 7 and 10. Best way to find it is to search for it. Do note you cannot just use cmd.exe, you have to use the shortcut, otherwise the proper MinGW software will not be on the PATH.
2. Once that is open, change into your user directory, and clone MultiMC by doing `git clone --recursive https://github.com/MultiMC/MultiMC5.git`, and change directory to the folder you cloned to.
3. Make a build directory, and change directory to the directory and do `cmake -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=C:\Path\that\makes\sense\for\you`. By default, it will install to C:\Program Files (x86), which you might not want, if you want a local installation. If you want to install it to that directory, make sure to run the command window as administrator.
3. Do `mingw32-make -jX`, where X is the number of cores your CPU has plus one.
4. Now to wait for it to compile. This could take some time. Hopefully it compiles properly.
5. Run the command `mingw32-make install`, and it should install MultiMC, to whatever the `-DCMAKE_INSTALL_PREFIX` was.
6. In most cases, whenever compiling, the OpenSSL dll's aren't put into the directory to where MultiMC installs, meaning you cannot log in. The best way to fix this is just to do `copy C:\OpenSSL-Win32\*.dll C:\Where\you\installed\MultiMC\to`. This should copy the required OpenSSL dll's to log in.
# OS X
### Install prerequisites:
* install homebrew
* then:
```
brew install qt5
brew tap homebrew/versions
brew install gcc48
brew install cmake
```
### Build
Pick an installation path - this is where the final `.app` will be constructed when you run `make install`. Supply it as the `CMAKE_INSTALL_PREFIX` argument during CMake configuration.
```
git clone https://github.com/MultiMC/MultiMC5.git
git submodule init
git submodule update
cd MultiMC5
mkdir build
cd build
export CMAKE_PREFIX_PATH=/usr/local/opt/qt5
export CC=/usr/local/bin/gcc-4.8
export CXX=/usr/local/bin/g++-4.8
cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/Users/YOU/some/path/that/makes/sense/
make
make install
```
**These build instructions were taken and adapted from https://gist.github.com/number5/7250865 If they don't work for you, let us know on IRC ([Esper/#MultiMC](http://webchat.esper.net/?nick=&channels=MultiMC))!**

View File

@@ -1,18 +1,41 @@
cmake_minimum_required(VERSION 3.1)
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
if(IS_IN_SOURCE_BUILD)
message(AUTHOR_WARNING "You are building MultiMC in-source. This is NOT recommended!")
endif()
if(WIN32)
# In Qt 5.1+ we have our own main() function, don't autolink to qtmain on Windows
cmake_policy(SET CMP0020 OLD)
endif()
project(MultiMC)
project(Launcher)
enable_testing()
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
if(IS_IN_SOURCE_BUILD)
message(FATAL_ERROR "You are building the Launcher in-source. Please separate the build tree from the source tree.")
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
if(CMAKE_HOST_SYSTEM_VERSION MATCHES ".*[Mm]icrosoft.*" OR
CMAKE_HOST_SYSTEM_VERSION MATCHES ".*WSL.*"
)
message(FATAL_ERROR "Building the Launcher is not supported in Linux-on-Windows distributions.")
endif()
endif()
option(Launcher_RUN_CLANG_TIDY "Run clang-tidy with the compiler." OFF)
if(Launcher_RUN_CLANG_TIDY)
find_program(CLANG_TIDY_COMMAND NAMES clang-tidy)
if(NOT CLANG_TIDY_COMMAND)
message(WARNING "CMake_RUN_CLANG_TIDY is ON but clang-tidy is not found!")
set(DO_CLANG_TIDY "" CACHE STRING "" FORCE)
else()
set(CLANG_TIDY_CHECKS "-modernize-use-trailing-return-type,-readability-magic-numbers,-modernize-avoid-c-arrays")
set(DO_CLANG_TIDY "${CLANG_TIDY_COMMAND};-checks=${CLANG_TIDY_CHECKS};-header-filter='${CMAKE_SOURCE_DIR}/src/*'")
endif()
# TODO: make this per-target, differentiate between libraries and main codebase
set(CMAKE_CXX_CLANG_TIDY ${DO_CLANG_TIDY})
endif()
##################################### Set CMake options #####################################
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
@@ -32,51 +55,85 @@ set(CMAKE_C_STANDARD_REQUIRED true)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_C_STANDARD 11)
include(GenerateExportHeader)
set(CMAKE_CXX_FLAGS " -Wall -pedantic -Werror -Wno-deprecated-declarations -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 -O3 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS " -Wall -pedantic -Wno-deprecated-declarations -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE " -O3 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
if(UNIX AND APPLE)
set(CMAKE_CXX_FLAGS " -stdlib=libc++ ${CMAKE_CXX_FLAGS}")
endif()
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror -Werror=return-type -O0")
# Fix build with Qt 5.13
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
##################################### Set Application options #####################################
######## Set URLs ########
set(MultiMC_NEWS_RSS_URL "https://multimc.org/rss.xml" CACHE STRING "URL to fetch MultiMC's news RSS feed from.")
set(Launcher_NEWS_RSS_URL "https://multimc.org/rss.xml" CACHE STRING "URL to fetch Launcher's news RSS feed from.")
######## Set version numbers ########
set(MultiMC_VERSION_MAJOR 0)
set(MultiMC_VERSION_MINOR 6)
set(MultiMC_VERSION_HOTFIX 6)
set(Launcher_VERSION_MAJOR 0)
set(Launcher_VERSION_MINOR 7)
set(Launcher_VERSION_HOTFIX 0)
# Build number
set(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
set(Launcher_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
# Build platform.
set(MultiMC_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used by the notification system and to display in the about dialog.")
set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used by the notification system and to display in the about dialog.")
# Channel list URL
set(MultiMC_CHANLIST_URL "" CACHE STRING "URL for the channel list.")
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
# Notification URL
set(MultiMC_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
set(Launcher_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
# The metadata server
set(Launcher_META_URL "https://meta.multimc.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
# paste.ee API key
set(MultiMC_PASTE_EE_API_KEY "utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ" CACHE STRING "API key you can get from paste.ee when you register an account")
set(Launcher_PASTE_EE_API_KEY "utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ" CACHE STRING "API key you can get from paste.ee when you register an account")
# Imgur API Client ID
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
# Google analytics ID
set(MultiMC_ANALYTICS_ID "UA-87731965-2" CACHE STRING "ID you can get from Google analytics")
set(Launcher_ANALYTICS_ID "UA-87731965-2" CACHE STRING "ID you can get from Google analytics")
# Bug tracker URL
set(Launcher_BUG_TRACKER_URL "" CACHE STRING "URL for the bug tracker.")
# Discord URL
set(Launcher_DISCORD_URL "" CACHE STRING "URL for the Discord guild.")
# Subreddit URL
set(Launcher_SUBREDDIT_URL "" CACHE STRING "URL for the subreddit.")
# Use the secrets library or a public stub?
option(Launcher_EMBED_SECRETS "Determines whether to embed secrets. Secrets are separate and non-public." OFF)
# API Keys
# NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service
# of these platforms, please change these API keys beforehand.
# Be aware that if you were to use these API keys for malicious purposes they might get revoked, which might cause
# breakage to thousands of users.
# If you don't plan to use these features of this software, you can just remove these values.
# 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 "f4404707-7bbe-4e40-80ba-85fb2bb825a1" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
#### Check the current Git commit and branch
include(GetGitRevisionDescription)
get_git_head_revision(MultiMC_GIT_REFSPEC MultiMC_GIT_COMMIT)
get_git_head_revision(Launcher_GIT_REFSPEC Launcher_GIT_COMMIT)
message(STATUS "Git commit: ${MultiMC_GIT_COMMIT}")
message(STATUS "Git refspec: ${MultiMC_GIT_REFSPEC}")
message(STATUS "Git commit: ${Launcher_GIT_COMMIT}")
message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}")
set(MultiMC_RELEASE_VERSION_NAME "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}")
set(Launcher_RELEASE_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
#### Custom target to just print the version.
add_custom_target(version echo "Version: ${MultiMC_RELEASE_VERSION_NAME}")
add_custom_target(version echo "Version: ${Launcher_RELEASE_VERSION_NAME}")
add_custom_target(tcversion echo "\\#\\#teamcity[setParameter name=\\'env.LAUNCHER_VERSION\\' value=\\'${Launcher_RELEASE_VERSION_NAME}\\']")
################################ 3rd Party Libs ################################
@@ -101,47 +158,55 @@ if (Qt5_POSITION_INDEPENDENT_CODE)
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
####################################### Secrets #######################################
if(Launcher_EMBED_SECRETS)
add_subdirectory(secrets)
else()
add_subdirectory(notsecrets)
endif()
####################################### Install layout #######################################
# How to install the build results
set(MultiMC_LAYOUT "auto" CACHE STRING "The layout for MultiMC installation (auto, win-bundle, lin-bundle, lin-nodeps, lin-system, mac-bundle)")
set_property(CACHE MultiMC_LAYOUT PROPERTY STRINGS auto win-bundle lin-bundle lin-nodeps lin-system mac-bundle)
set(Launcher_LAYOUT "auto" CACHE STRING "The layout for the launcher installation (auto, win-bundle, lin-nodeps, mac-bundle)")
set_property(CACHE Launcher_LAYOUT PROPERTY STRINGS auto win-bundle lin-nodeps mac-bundle)
if(MultiMC_LAYOUT STREQUAL "auto")
if(Launcher_LAYOUT STREQUAL "auto")
if(UNIX AND APPLE)
set(MultiMC_LAYOUT_REAL "mac-bundle")
set(Launcher_LAYOUT_REAL "mac-bundle")
elseif(UNIX)
set(MultiMC_LAYOUT_REAL "lin-nodeps")
set(Launcher_LAYOUT_REAL "lin-nodeps")
elseif(WIN32)
set(MultiMC_LAYOUT_REAL "win-bundle")
set(Launcher_LAYOUT_REAL "win-bundle")
else()
message(FATAL_ERROR "Cannot choose a sensible install layout for your platform.")
endif()
else()
set(MultiMC_LAYOUT_REAL ${MultiMC_LAYOUT})
set(Launcher_LAYOUT_REAL ${Launcher_LAYOUT})
endif()
if(MultiMC_LAYOUT_REAL STREQUAL "mac-bundle")
set(BINARY_DEST_DIR "MultiMC.app/Contents/MacOS")
set(LIBRARY_DEST_DIR "MultiMC.app/Contents/MacOS")
set(PLUGIN_DEST_DIR "MultiMC.app/Contents/MacOS")
set(RESOURCES_DEST_DIR "MultiMC.app/Contents/Resources")
set(JARS_DEST_DIR "MultiMC.app/Contents/MacOS/jars")
if(Launcher_LAYOUT_REAL STREQUAL "mac-bundle")
set(BINARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
set(LIBRARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
set(PLUGIN_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
set(RESOURCES_DEST_DIR "${Launcher_Name}.app/Contents/Resources")
set(JARS_DEST_DIR "${Launcher_Name}.app/Contents/MacOS/jars")
set(BUNDLE_DEST_DIR ".")
# Apps to bundle
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.app")
set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.app")
# Mac bundle settings
set(MACOSX_BUNDLE_BUNDLE_NAME "MultiMC")
set(MACOSX_BUNDLE_INFO_STRING "MultiMC Minecraft launcher and management utility.")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.multimc.MultiMC5")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
set(MACOSX_BUNDLE_ICON_FILE MultiMC.icns)
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2015-2019 MultiMC Contributors")
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_Name}")
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_Name}: Minecraft launcher and management utility.")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.multimc.${Launcher_Name}")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2015-2023 ${Launcher_Copyright}")
# directories to look for dependencies
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
@@ -150,32 +215,9 @@ if(MultiMC_LAYOUT_REAL STREQUAL "mac-bundle")
set(INSTALL_BUNDLE "full")
# Add the icon
install(FILES application/resources/MultiMC.icns DESTINATION ${RESOURCES_DEST_DIR})
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
elseif(MultiMC_LAYOUT_REAL STREQUAL "lin-bundle")
set(BINARY_DEST_DIR "bin")
set(LIBRARY_DEST_DIR "bin")
set(PLUGIN_DEST_DIR "plugins")
set(BUNDLE_DEST_DIR ".")
set(RESOURCES_DEST_DIR ".")
set(JARS_DEST_DIR "bin/jars")
# Apps to bundle
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/MultiMC")
# directories to look for dependencies
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
# install as bundle
set(INSTALL_BUNDLE "full")
# Set RPATH
SET(MultiMC_BINARY_RPATH "$ORIGIN/")
# Install basic runner script
install(PROGRAMS application/package/linux/MultiMC DESTINATION ${BUNDLE_DEST_DIR})
elseif(MultiMC_LAYOUT_REAL STREQUAL "lin-nodeps")
elseif(Launcher_LAYOUT_REAL STREQUAL "lin-nodeps")
set(BINARY_DEST_DIR "bin")
set(LIBRARY_DEST_DIR "bin")
set(PLUGIN_DEST_DIR "plugins")
@@ -187,28 +229,13 @@ elseif(MultiMC_LAYOUT_REAL STREQUAL "lin-nodeps")
set(INSTALL_BUNDLE "nodeps")
# Set RPATH
SET(MultiMC_BINARY_RPATH "$ORIGIN/")
SET(Launcher_BINARY_RPATH "$ORIGIN/")
# Install basic runner script
install(PROGRAMS application/package/linux/MultiMC DESTINATION ${BUNDLE_DEST_DIR})
configure_file(launcher/Launcher.in "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" @ONLY)
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" DESTINATION ${BUNDLE_DEST_DIR} RENAME ${Launcher_Name})
elseif(MultiMC_LAYOUT_REAL STREQUAL "lin-system")
set(MultiMC_APP_BINARY_NAME "multimc" CACHE STRING "Name of the MultiMC binary")
set(MultiMC_BINARY_DEST_DIR "bin" CACHE STRING "Path to the binary directory")
set(MultiMC_LIBRARY_DEST_DIR "lib${LIB_SUFFIX}" CACHE STRING "Path to the library directory")
set(MultiMC_SHARE_DEST_DIR "share/multimc" CACHE STRING "Path to the shared data directory")
set(JARS_DEST_DIR "${MultiMC_SHARE_DEST_DIR}/jars")
set(BINARY_DEST_DIR ${MultiMC_BINARY_DEST_DIR})
set(LIBRARY_DEST_DIR ${MultiMC_LIBRARY_DEST_DIR})
MESSAGE(STATUS "Compiling for linux system with ${MultiMC_SHARE_DEST_DIR} and MULTIMC_LINUX_DATADIR")
SET(MultiMC_APP_BINARY_DEFS "-DMULTIMC_JARS_LOCATION=${CMAKE_INSTALL_PREFIX}/${JARS_DEST_DIR}" "-DMULTIMC_LINUX_DATADIR")
# install as bundle with no dependencies included
set(INSTALL_BUNDLE "nodeps")
elseif(MultiMC_LAYOUT_REAL STREQUAL "win-bundle")
elseif(Launcher_LAYOUT_REAL STREQUAL "win-bundle")
set(BINARY_DEST_DIR ".")
set(LIBRARY_DEST_DIR ".")
set(PLUGIN_DEST_DIR ".")
@@ -217,7 +244,7 @@ elseif(MultiMC_LAYOUT_REAL STREQUAL "win-bundle")
set(JARS_DEST_DIR "jars")
# Apps to bundle
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.exe")
set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.exe")
# directories to look for dependencies
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
@@ -236,7 +263,7 @@ set_directory_properties(PROPERTIES EP_BASE External)
option(NBT_BUILD_SHARED "Build NBT shared library" ON)
option(NBT_USE_ZLIB "Build NBT library with zlib support" OFF)
option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests.
set(NBT_NAME MultiMC_nbt++)
set(NBT_NAME Launcher_nbt++)
set(NBT_DEST_DIR ${LIBRARY_DEST_DIR})
add_subdirectory(libraries/libnbtplusplus)
@@ -247,16 +274,17 @@ add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
add_subdirectory(libraries/javacheck) # java compatibility checker
add_subdirectory(libraries/xz-embedded) # xz compression
add_subdirectory(libraries/quazip) # zip manipulation library
add_subdirectory(libraries/pack200) # java pack200 compression
add_subdirectory(libraries/rainbow) # Qt extension for colors
add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
add_subdirectory(libraries/classparser) # google analytics library
add_subdirectory(libraries/optional-bare)
add_subdirectory(libraries/tomlc99) # toml parser
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
############################### Built Artifacts ###############################
add_subdirectory(api/logic)
add_subdirectory(api/gui)
add_subdirectory(buildconfig)
# NOTE: this must always be last to appease the CMake deity of quirky install command evaluation order.
add_subdirectory(application)
add_subdirectory(launcher)

View File

@@ -1,6 +1,8 @@
# MultiMC
Copyright 2012-2019 MultiMC Contributors
Portions are licensed under Apache 2.0 License:
Copyright 2012-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
@@ -13,6 +15,67 @@
See the License for the specific language governing permissions and
limitations under the License.
Portions are licensed under MS-PL:
This license governs use of the accompanying software. If you use the
software, you accept this license. If you do not accept the license,
do not use the software.
1. Definitions
The terms "reproduce," "reproduction," "derivative works," and
"distribution" have the same meaning here as under U.S. copyright law.
A "contribution" is the original software, or any additions or
changes to the software.
A "contributor" is any person that distributes its contribution
under this license.
"Licensed patents" are a contributor's patent claims that read
directly on its contribution.
2. Grant of Rights
(A) Copyright Grant- Subject to the terms of this license,
including the license conditions and limitations in section 3,
each contributor grants you a non-exclusive, worldwide, royalty-free
copyright license to reproduce its contribution, prepare derivative
works of its contribution, and distribute its contribution or any
derivative works that you create.
(B) Patent Grant- Subject to the terms of this license, including
the license conditions and limitations in section 3, each contributor
grants you a non-exclusive, worldwide, royalty-free license under its
licensed patents to make, have made, use, sell, offer for sale, import,
and/or otherwise dispose of its contribution in the software or derivative
works of the contribution in the software.
3. Conditions and Limitations
(A) No Trademark License- This license does not grant you rights to
use any contributors' name, logo, or trademarks.
(B) If you bring a patent claim against any contributor over patents
that you claim are infringed by the software, your patent license
from such contributor to the software ends automatically.
(C) If you distribute any portion of the software, you must retain all
copyright, patent, trademark, and attribution notices that are present
in the software.
(D) If you distribute any portion of the software in source code form,
you may do so only under this license by including a complete copy of
this license with your distribution. If you distribute any portion of
the software in compiled or object code form, you may only do so under
a license that complies with this license.
(E) The software is licensed "as-is." You bear the risk of using it.
The contributors give no express warranties, guarantees or conditions.
You may have additional consumer rights under your local laws which
this license cannot change. To the extent permitted under your local
laws, the contributors exclude the implied warranties of merchantability,
fitness for a particular purpose and non-infringement.
# MinGW runtime (Windows)
Copyright (c) 2012 MinGW.org project
@@ -128,35 +191,6 @@
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
# Pack200
The GNU General Public License (GPL)
Version 2, June 1991
+ "CLASSPATH" EXCEPTION TO THE GPL
Certain source files distributed by Oracle America and/or its affiliates are
subject to the following clarification and special exception to the GPL, but
only where Oracle has expressly included in the particular source file's header
the words "Oracle designates this particular file as subject to the "Classpath"
exception as provided by Oracle in the LICENSE file that accompanied this code."
Linking this library statically or dynamically with other modules is making
a combined work based on this library. Thus, the terms and conditions of
the GNU General Public License cover the whole combination.
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent modules,
and to copy and distribute the resulting executable under terms of your
choice, provided that you also meet, for each linked independent module,
the terms and conditions of the license of that module. An independent
module is a module which is not derived from or based on this library. If
you modify this library, you may extend this exception to your version of
the library, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
# Quazip
Copyright (C) 2005-2011 Sergey A. Tachenov
@@ -252,3 +286,82 @@
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# optional-bare
Code from https://github.com/martinmoene/optional-bare/
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
# tomlc99
MIT License
Copyright (c) 2017 CK Tan
https://github.com/cktan/tomlc99
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# O2 (Katabasis fork)
Copyright (c) 2012, Akos Polster
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,45 +1,61 @@
<p align="center">
<img src="https://avatars2.githubusercontent.com/u/5411890" alt="MultiMC logo"/>
<img width="150" src="https://github.com/UltimMC/Launcher/assets/62727185/48d60c15-23ae-4d48-84b8-813f72db6cdd">
<p align="center">UltimMC is a custom launcher for Minecraft which allows you to manage multiple instances and use offline ("cracked") accounts while keeping as close as possible to the original.</p>
</p>
MultiMC 5
=========
> [!IMPORTANT]
> This project is a **fork** of MultiMC. </br> </br>
> This software is provided without any warranty, so please don't contact the main
> MultiMC developers in case anything goes wrong using this launcher. </br> </br>
> Nonetheless, feel free to create an issue within this repository
> in case you face an issue specific to UltimMC.
MultiMC is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once. It also allows you to easily install and remove mods by simply dragging and dropping. Here are the current [features](https://github.com/MultiMC/MultiMC5/wiki#features) of MultiMC.
## Downloading
- All the available downloads can be found [here](https://nightly.link/UltimMC/Launcher/workflows/main/develop). These builds are directly taken from our [GitHub Actions](https://github.com/UltimMC/Launcher/actions).
## Development
The project uses C++ and Qt5 as the language and base framework. This might seem odd in the Minecraft community, but allows using 25MB of RAM, where other tools use an excessive amount of resources for no reason.
Direct downloads for specific platforms can be found below.
We can do more, with less, on worse hardware and leave more resources for the game while keeping the launcher running and providing extra features.
- *[Windows \(32-bit and 64-bit\)](https://nightly.link/UltimMC/Launcher/workflows/main/develop/mmc-cracked-win32.zip)*.
If you want to contribute, either talk to us on [Discord](https://discord.gg/0k2zsXGNHs0fE4Wm), [IRC](http://webchat.esper.net/?nick=&channels=MultiMC)(esper.net/#MultiMC) or pick up some item from [workflowy](https://workflowy.com/s/2EyDMcp7CU) - there are many.
- *[Linux (64-bit)](https://nightly.link/UltimMC/Launcher/workflows/main/develop/mmc-cracked-lin64.zip)*.
### Building
If you want to build MultiMC yourself, check [BUILD.md](BUILD.md) for build instructions.
- *[macOS (10.14 and newer)](https://nightly.link/UltimMC/Launcher/workflows/main/develop/mmc-cracked-osx64.zip)*.
The ci server is running at [ci.multimc.org](http://ci.multimc.org), where you can watch the builds happen in (or very close to) real time. It can also serve as a nice reference on how to set up the build environment on your end.
> [!NOTE]
> In the case you're using macOS then another additional step you might need to do
> is to make `UltimMC` an executable by running the command `chmod +x UltimMC.app/Contents/MacOS/UltimMC` in the terminal.
According to travis.ci, the builds are currently [![Build Status](https://travis-ci.org/MultiMC/MultiMC5.svg?branch=develop)](https://travis-ci.org/MultiMC/MultiMC5)
There's additionally a [.deb package](https://nightly.link/UltimMC/ultimmc-deb/workflows/ci/master/UltimMC.zip) for Debian/Ubuntu distributions.
### Code formatting
We use [Clang Format](http://clang.llvm.org/docs/ClangFormat.html) to format the project. We highly recommend setting it up so the project stays well formatted.
And an AUR package as [ultimmc-bin](https://aur.archlinux.org/packages/ultimmc-bin). [![ultimmc-bin](https://img.shields.io/badge/ultimmc--bin-1793D1?logo=archlinux&logoColor=white&label=AUR)](https://aur.archlinux.org/packages/ultimmc-bin)
## Installing and Using
## Translations
Translations can be done either directly in the [translations repository](https://github.com/MultiMC/MultiMC5). For more details, see: [Translating-MultiMC](https://github.com/MultiMC/MultiMC5/wiki/Translating-MultiMC).
1. Pick the correct download for your system.
2. Uncompress it in your desired directory.
3. Launch `UltimMC`.
4. Go to account settings.
6. A. Pick "Add Local" and you will be requested to use the username you desire, this can be anything.
7. B. Pick "Add Ely.by" and add your Ely.by account by putting your email and password.
8. Save it.
9. Now enjoy the Launcher.
## Forking/Redistributing
We keep MultiMC open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.
## Updating
Part of the reason for using the Apache license is we don't want people using the "MultiMC" name when redistributing the project. This means people must take the time to go through the source code and remove all references to "MultiMC", including but not limited to the project icon and the title of windows, (no *MultiMC-fork* in the title).
To update the launcher replace all replaceable files and folders with the newer ones from any of the links listed above.
Apache covers reasonable use for the name - a mention of the project's origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork *without* implying that you have our blessing.
A better update system is in the works.
## Forking
## License
Copyright &copy; 2013-2019 MultiMC Contributors
This project now includes our MSA API key in order to have functional Microsoft authentication within the launcher.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this program except in compliance with the License. You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0).
This means you're accepting the:
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.
- [Microsoft Identity Platform Terms of Use](https://learn.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
We humbly ask that in case you wish to fork UltimMC, please either remove the key by setting it empty (`""`) or by setting your own.

View File

@@ -1,34 +0,0 @@
project(MultiMC_gui LANGUAGES CXX)
set(GUI_SOURCES
DesktopServices.h
DesktopServices.cpp
# Icons
icons/MMCIcon.h
icons/MMCIcon.cpp
icons/IconList.h
icons/IconList.cpp
SkinUtils.cpp
SkinUtils.h
)
################################ COMPILE ################################
add_library(MultiMC_gui SHARED ${GUI_SOURCES})
set_target_properties(MultiMC_gui PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1)
generate_export_header(MultiMC_gui)
# Link
target_link_libraries(MultiMC_gui MultiMC_iconfix MultiMC_logic Qt5::Gui)
# Mark and export headers
target_include_directories(MultiMC_gui PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
# Install it
install(
TARGETS MultiMC_gui
RUNTIME DESTINATION ${LIBRARY_DEST_DIR}
LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
)

View File

@@ -1,37 +0,0 @@
#pragma once
#include <QUrl>
#include <QString>
#include "multimc_gui_export.h"
/**
* This wraps around QDesktopServices and adds workarounds where needed
* Use this instead of QDesktopServices!
*/
namespace DesktopServices
{
/**
* Open a file in whatever application is applicable
*/
MULTIMC_GUI_EXPORT bool openFile(const QString &path);
/**
* Open a file in the specified application
*/
MULTIMC_GUI_EXPORT bool openFile(const QString &application, const QString &path, const QString & workingDirectory = QString(), qint64 *pid = 0);
/**
* Run an application
*/
MULTIMC_GUI_EXPORT bool run(const QString &application,const QStringList &args, const QString & workingDirectory = QString(), qint64 *pid = 0);
/**
* Open a directory
*/
MULTIMC_GUI_EXPORT bool openDirectory(const QString &path, bool ensureExists = false);
/**
* Open the URL, most likely in a browser. Maybe.
*/
MULTIMC_GUI_EXPORT bool openUrl(const QUrl &url);
}

View File

@@ -1,501 +0,0 @@
project(MultiMC_logic)
include (UnitTest)
set(CORE_SOURCES
# LOGIC - Base classes and infrastructure
BaseInstaller.h
BaseInstaller.cpp
BaseVersionList.h
BaseVersionList.cpp
InstanceList.h
InstanceList.cpp
InstanceTask.h
InstanceTask.cpp
LoggedProcess.h
LoggedProcess.cpp
MessageLevel.cpp
MessageLevel.h
BaseVersion.h
BaseInstance.h
BaseInstance.cpp
NullInstance.h
MMCZip.h
MMCZip.cpp
MMCStrings.h
MMCStrings.cpp
# Basic instance manipulation tasks (derived from InstanceTask)
InstanceCreationTask.h
InstanceCreationTask.cpp
InstanceCopyTask.h
InstanceCopyTask.cpp
InstanceImportTask.h
InstanceImportTask.cpp
# Use tracking separate from memory management
Usable.h
# Prefix tree where node names are strings between separators
SeparatorPrefixTree.h
# WARNING: globals live here
Env.h
Env.cpp
# String filters
Filter.h
Filter.cpp
# JSON parsing helpers
Json.h
Json.cpp
FileSystem.h
FileSystem.cpp
Exception.h
# RW lock protected map
RWStorage.h
# A variable that has an implicit default value and keeps track of changes
DefaultVariable.h
# a smart pointer wrapper intended for safer use with Qt signal/slot mechanisms
QObjectPtr.h
# Compression support
GZip.h
GZip.cpp
# Command line parameter parsing
Commandline.h
Commandline.cpp
# Version number string support
Version.h
Version.cpp
# A Recursive file system watcher
RecursiveFileSystemWatcher.h
RecursiveFileSystemWatcher.cpp
)
add_unit_test(FileSystem
SOURCES FileSystem_test.cpp
LIBS MultiMC_logic
DATA testdata
)
add_unit_test(GZip
SOURCES GZip_test.cpp
LIBS MultiMC_logic
)
set(PATHMATCHER_SOURCES
# Path matchers
pathmatcher/FSTreeMatcher.h
pathmatcher/IPathMatcher.h
pathmatcher/MultiMatcher.h
pathmatcher/RegexpMatcher.h
)
set(NET_SOURCES
# network stuffs
net/ByteArraySink.h
net/ChecksumValidator.h
net/Download.cpp
net/Download.h
net/FileSink.cpp
net/FileSink.h
net/HttpMetaCache.cpp
net/HttpMetaCache.h
net/MetaCacheSink.cpp
net/MetaCacheSink.h
net/NetAction.h
net/NetJob.cpp
net/NetJob.h
net/PasteUpload.cpp
net/PasteUpload.h
net/Sink.h
net/URLConstants.cpp
net/URLConstants.h
net/Validator.h
)
# Game launch logic
set(LAUNCH_SOURCES
launch/steps/PostLaunchCommand.cpp
launch/steps/PostLaunchCommand.h
launch/steps/PreLaunchCommand.cpp
launch/steps/PreLaunchCommand.h
launch/steps/TextPrint.cpp
launch/steps/TextPrint.h
launch/steps/Update.cpp
launch/steps/Update.h
launch/LaunchStep.cpp
launch/LaunchStep.h
launch/LaunchTask.cpp
launch/LaunchTask.h
launch/LogModel.cpp
launch/LogModel.h
)
# Old update system
set(UPDATE_SOURCES
updater/GoUpdate.h
updater/GoUpdate.cpp
updater/UpdateChecker.h
updater/UpdateChecker.cpp
updater/DownloadTask.h
updater/DownloadTask.cpp
)
add_unit_test(UpdateChecker
SOURCES updater/UpdateChecker_test.cpp
LIBS MultiMC_logic
DATA updater/testdata
)
add_unit_test(DownloadTask
SOURCES updater/DownloadTask_test.cpp
LIBS MultiMC_logic
DATA updater/testdata
)
# Rarely used notifications
set(NOTIFICATIONS_SOURCES
# Notifications - short warning messages
notifications/NotificationChecker.h
notifications/NotificationChecker.cpp
)
# Backend for the news bar... there's usually no news.
set(NEWS_SOURCES
# News System
news/NewsChecker.h
news/NewsChecker.cpp
news/NewsEntry.h
news/NewsEntry.cpp
)
# Icon interface
set(ICONS_SOURCES
# Icons System and related code
icons/IIconList.h
icons/IIconList.cpp
icons/IconUtils.h
icons/IconUtils.cpp
)
# Minecraft services status checker
set(STATUS_SOURCES
# Status system
status/StatusChecker.h
status/StatusChecker.cpp
)
# Support for Minecraft instances and launch
set(MINECRAFT_SOURCES
# Minecraft support
minecraft/auth/AuthSession.h
minecraft/auth/AuthSession.cpp
minecraft/auth/MojangAccountList.h
minecraft/auth/MojangAccountList.cpp
minecraft/auth/MojangAccount.h
minecraft/auth/MojangAccount.cpp
minecraft/auth/YggdrasilTask.h
minecraft/auth/YggdrasilTask.cpp
minecraft/auth/flows/AuthenticateTask.h
minecraft/auth/flows/AuthenticateTask.cpp
minecraft/auth/flows/RefreshTask.cpp
minecraft/auth/flows/RefreshTask.cpp
minecraft/auth/flows/ValidateTask.h
minecraft/auth/flows/ValidateTask.cpp
minecraft/gameoptions/GameOptions.h
minecraft/gameoptions/GameOptions.cpp
minecraft/update/AssetUpdateTask.h
minecraft/update/AssetUpdateTask.cpp
minecraft/update/FMLLibrariesTask.cpp
minecraft/update/FMLLibrariesTask.h
minecraft/update/FoldersTask.cpp
minecraft/update/FoldersTask.h
minecraft/update/LibrariesTask.cpp
minecraft/update/LibrariesTask.h
minecraft/launch/ClaimAccount.cpp
minecraft/launch/ClaimAccount.h
minecraft/launch/CreateServerResourcePacksFolder.cpp
minecraft/launch/CreateServerResourcePacksFolder.h
minecraft/launch/ModMinecraftJar.cpp
minecraft/launch/ModMinecraftJar.h
minecraft/launch/DirectJavaLaunch.cpp
minecraft/launch/DirectJavaLaunch.h
minecraft/launch/ExtractNatives.cpp
minecraft/launch/ExtractNatives.h
minecraft/launch/LauncherPartLaunch.cpp
minecraft/launch/LauncherPartLaunch.h
minecraft/launch/PrintInstanceInfo.cpp
minecraft/launch/PrintInstanceInfo.h
minecraft/launch/ReconstructAssets.cpp
minecraft/launch/ReconstructAssets.h
minecraft/legacy/LegacyModList.h
minecraft/legacy/LegacyModList.cpp
minecraft/legacy/LegacyInstance.h
minecraft/legacy/LegacyInstance.cpp
minecraft/legacy/LegacyUpgradeTask.h
minecraft/legacy/LegacyUpgradeTask.cpp
minecraft/GradleSpecifier.h
minecraft/MinecraftInstance.cpp
minecraft/MinecraftInstance.h
minecraft/LaunchProfile.cpp
minecraft/LaunchProfile.h
minecraft/Component.cpp
minecraft/Component.h
minecraft/ComponentList.cpp
minecraft/ComponentList.h
minecraft/ComponentUpdateTask.cpp
minecraft/ComponentUpdateTask.h
minecraft/MinecraftLoadAndCheck.h
minecraft/MinecraftLoadAndCheck.cpp
minecraft/MinecraftUpdate.h
minecraft/MinecraftUpdate.cpp
minecraft/MojangVersionFormat.cpp
minecraft/MojangVersionFormat.h
minecraft/Rule.cpp
minecraft/Rule.h
minecraft/OneSixVersionFormat.cpp
minecraft/OneSixVersionFormat.h
minecraft/OpSys.cpp
minecraft/OpSys.h
minecraft/ParseUtils.cpp
minecraft/ParseUtils.h
minecraft/ProfileUtils.cpp
minecraft/ProfileUtils.h
minecraft/Library.cpp
minecraft/Library.h
minecraft/MojangDownloadInfo.h
minecraft/VersionFile.cpp
minecraft/VersionFile.h
minecraft/VersionFilterData.h
minecraft/VersionFilterData.cpp
minecraft/Mod.h
minecraft/Mod.cpp
minecraft/SimpleModList.h
minecraft/SimpleModList.cpp
minecraft/World.h
minecraft/World.cpp
minecraft/WorldList.h
minecraft/WorldList.cpp
# Assets
minecraft/AssetsUtils.h
minecraft/AssetsUtils.cpp
# Forge and all things forge related
minecraft/forge/ForgeXzDownload.h
minecraft/forge/ForgeXzDownload.cpp
# Skin upload utilities
minecraft/SkinUpload.cpp
minecraft/SkinUpload.h
)
add_unit_test(GradleSpecifier
SOURCES minecraft/GradleSpecifier_test.cpp
LIBS MultiMC_logic
)
add_unit_test(MojangVersionFormat
SOURCES minecraft/MojangVersionFormat_test.cpp
LIBS MultiMC_logic
DATA minecraft/testdata
)
add_unit_test(Library
SOURCES minecraft/Library_test.cpp
LIBS MultiMC_logic
)
# FIXME: shares data with FileSystem test
add_unit_test(SimpleModList
SOURCES minecraft/SimpleModList_test.cpp
DATA testdata
LIBS MultiMC_logic
)
add_unit_test(ParseUtils
SOURCES minecraft/ParseUtils_test.cpp
LIBS MultiMC_logic
)
# the screenshots feature
set(SCREENSHOTS_SOURCES
screenshots/Screenshot.h
screenshots/ImgurUpload.h
screenshots/ImgurUpload.cpp
screenshots/ImgurAlbumCreation.h
screenshots/ImgurAlbumCreation.cpp
)
set(TASKS_SOURCES
# Tasks
tasks/Task.h
tasks/Task.cpp
tasks/SequentialTask.h
tasks/SequentialTask.cpp
)
set(SETTINGS_SOURCES
# Settings
settings/INIFile.cpp
settings/INIFile.h
settings/INISettingsObject.cpp
settings/INISettingsObject.h
settings/OverrideSetting.cpp
settings/OverrideSetting.h
settings/PassthroughSetting.cpp
settings/PassthroughSetting.h
settings/Setting.cpp
settings/Setting.h
settings/SettingsObject.cpp
settings/SettingsObject.h
)
add_unit_test(INIFile
SOURCES settings/INIFile_test.cpp
LIBS MultiMC_logic
)
set(JAVA_SOURCES
# Java related code
java/launch/CheckJava.cpp
java/launch/CheckJava.h
java/JavaChecker.h
java/JavaChecker.cpp
java/JavaCheckerJob.h
java/JavaCheckerJob.cpp
java/JavaInstall.h
java/JavaInstall.cpp
java/JavaInstallList.h
java/JavaInstallList.cpp
java/JavaUtils.h
java/JavaUtils.cpp
java/JavaVersion.h
java/JavaVersion.cpp
)
add_unit_test(JavaVersion
SOURCES java/JavaVersion_test.cpp
LIBS MultiMC_logic
)
set(TRANSLATIONS_SOURCES
translations/TranslationsModel.h
translations/TranslationsModel.cpp
translations/POTranslator.h
translations/POTranslator.cpp
)
set(TOOLS_SOURCES
# Tools
tools/BaseExternalTool.cpp
tools/BaseExternalTool.h
tools/BaseProfiler.cpp
tools/BaseProfiler.h
tools/JProfiler.cpp
tools/JProfiler.h
tools/JVisualVM.cpp
tools/JVisualVM.h
tools/MCEditTool.cpp
tools/MCEditTool.h
)
set(META_SOURCES
# Metadata sources
meta/JsonFormat.cpp
meta/JsonFormat.h
meta/BaseEntity.cpp
meta/BaseEntity.h
meta/VersionList.cpp
meta/VersionList.h
meta/Version.cpp
meta/Version.h
meta/Index.cpp
meta/Index.h
)
set(FTB_SOURCES
modplatform/ftb/FtbPackFetchTask.h
modplatform/ftb/FtbPackFetchTask.cpp
modplatform/ftb/FtbPackInstallTask.h
modplatform/ftb/FtbPackInstallTask.cpp
modplatform/ftb/FtbPrivatePackManager.h
modplatform/ftb/FtbPrivatePackManager.cpp
modplatform/ftb/PackHelpers.h
)
set(FLAME_SOURCES
# Flame
modplatform/flame/PackManifest.h
modplatform/flame/PackManifest.cpp
modplatform/flame/FileResolvingTask.h
modplatform/flame/FileResolvingTask.cpp
modplatform/flame/UrlResolvingTask.h
modplatform/flame/UrlResolvingTask.cpp
)
add_unit_test(Index
SOURCES meta/Index_test.cpp
LIBS MultiMC_logic
)
################################ COMPILE ################################
# we need zlib
find_package(ZLIB REQUIRED)
set(LOGIC_SOURCES
${CORE_SOURCES}
${PATHMATCHER_SOURCES}
${NET_SOURCES}
${LAUNCH_SOURCES}
${UPDATE_SOURCES}
${NOTIFICATIONS_SOURCES}
${NEWS_SOURCES}
${STATUS_SOURCES}
${MINECRAFT_SOURCES}
${SCREENSHOTS_SOURCES}
${TASKS_SOURCES}
${SETTINGS_SOURCES}
${JAVA_SOURCES}
${TRANSLATIONS_SOURCES}
${TOOLS_SOURCES}
${META_SOURCES}
${ICONS_SOURCES}
${FTB_SOURCES}
${FLAME_SOURCES}
)
message(STATUS "FOO! ${LOGIC_SOURCES}")
add_library(MultiMC_logic SHARED ${LOGIC_SOURCES})
set_target_properties(MultiMC_logic PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1)
generate_export_header(MultiMC_logic)
# Link
target_link_libraries(MultiMC_logic xz-embedded MultiMC_unpack200 systeminfo MultiMC_quazip MultiMC_classparser ${NBT_NAME} ${ZLIB_LIBRARIES})
target_link_libraries(MultiMC_logic Qt5::Core Qt5::Xml Qt5::Network Qt5::Concurrent)
# Mark and export headers
target_include_directories(MultiMC_logic PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" PRIVATE "${ZLIB_INCLUDE_DIRS}")
# Install it
install(
TARGETS MultiMC_logic
RUNTIME DESTINATION ${LIBRARY_DEST_DIR}
LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
)

View File

@@ -1,209 +0,0 @@
#include "Env.h"
#include "net/HttpMetaCache.h"
#include "BaseVersion.h"
#include "BaseVersionList.h"
#include <QDir>
#include <QCoreApplication>
#include <QNetworkProxy>
#include <QNetworkAccessManager>
#include <QDebug>
#include "tasks/Task.h"
#include "meta/Index.h"
#include "FileSystem.h"
#include <QDebug>
struct Env::Private
{
QNetworkAccessManager m_qnam;
shared_qobject_ptr<HttpMetaCache> m_metacache;
std::shared_ptr<IIconList> m_iconlist;
shared_qobject_ptr<Meta::Index> m_metadataIndex;
QString m_jarsPath;
QSet<QString> m_features;
};
static Env * instance;
/*
* The *NEW* global rat nest of an object. Handle with care.
*/
Env::Env()
{
d = new Private();
}
Env::~Env()
{
delete d;
}
Env& Env::Env::getInstance()
{
if(!instance)
{
instance = new Env();
}
return *instance;
}
void Env::dispose()
{
delete instance;
instance = nullptr;
}
shared_qobject_ptr< HttpMetaCache > Env::metacache()
{
return d->m_metacache;
}
QNetworkAccessManager& Env::qnam() const
{
return d->m_qnam;
}
std::shared_ptr<IIconList> Env::icons()
{
return d->m_iconlist;
}
void Env::registerIconList(std::shared_ptr<IIconList> iconlist)
{
d->m_iconlist = iconlist;
}
shared_qobject_ptr<Meta::Index> Env::metadataIndex()
{
if (!d->m_metadataIndex)
{
d->m_metadataIndex.reset(new Meta::Index());
}
return d->m_metadataIndex;
}
void Env::initHttpMetaCache()
{
auto &m_metacache = d->m_metacache;
m_metacache.reset(new HttpMetaCache("metacache"));
m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath());
m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath());
m_metacache->addBase("versions", QDir("versions").absolutePath());
m_metacache->addBase("libraries", QDir("libraries").absolutePath());
m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
m_metacache->addBase("general", QDir("cache").absolutePath());
m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
m_metacache->addBase("root", QDir::currentPath());
m_metacache->addBase("translations", QDir("translations").absolutePath());
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
m_metacache->addBase("meta", QDir("meta").absolutePath());
m_metacache->Load();
}
void Env::updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password)
{
// Set the application proxy settings.
if (proxyTypeStr == "SOCKS5")
{
QNetworkProxy::setApplicationProxy(
QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password));
}
else if (proxyTypeStr == "HTTP")
{
QNetworkProxy::setApplicationProxy(
QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password));
}
else if (proxyTypeStr == "None")
{
// If we have no proxy set, set no proxy and return.
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy));
}
else
{
// If we have "Default" selected, set Qt to use the system proxy settings.
QNetworkProxyFactory::setUseSystemConfiguration(true);
}
qDebug() << "Detecting proxy settings...";
QNetworkProxy proxy = QNetworkProxy::applicationProxy();
d->m_qnam.setProxy(proxy);
QString proxyDesc;
if (proxy.type() == QNetworkProxy::NoProxy)
{
qDebug() << "Using no proxy is an option!";
return;
}
switch (proxy.type())
{
case QNetworkProxy::DefaultProxy:
proxyDesc = "Default proxy: ";
break;
case QNetworkProxy::Socks5Proxy:
proxyDesc = "Socks5 proxy: ";
break;
case QNetworkProxy::HttpProxy:
proxyDesc = "HTTP proxy: ";
break;
case QNetworkProxy::HttpCachingProxy:
proxyDesc = "HTTP caching: ";
break;
case QNetworkProxy::FtpCachingProxy:
proxyDesc = "FTP caching: ";
break;
default:
proxyDesc = "DERP proxy: ";
break;
}
proxyDesc += QString("%3@%1:%2 pass %4")
.arg(proxy.hostName())
.arg(proxy.port())
.arg(proxy.user())
.arg(proxy.password());
qDebug() << proxyDesc;
}
QString Env::getJarsPath()
{
if(d->m_jarsPath.isEmpty())
{
return FS::PathCombine(QCoreApplication::applicationDirPath(), "jars");
}
return d->m_jarsPath;
}
void Env::setJarsPath(const QString& path)
{
d->m_jarsPath = path;
}
void Env::enableFeature(const QString& featureName, bool state)
{
if(state)
{
d->m_features.insert(featureName);
}
else
{
d->m_features.remove(featureName);
}
}
bool Env::isFeatureEnabled(const QString& featureName) const
{
return d->m_features.contains(featureName);
}
void Env::getEnabledFeatures(QSet<QString>& features) const
{
features = d->m_features;
}
void Env::setEnabledFeatures(const QSet<QString>& features) const
{
d->m_features = features;
}

View File

@@ -1,65 +0,0 @@
#pragma once
#include <memory>
#include "icons/IIconList.h"
#include <QString>
#include <QMap>
#include "multimc_logic_export.h"
#include "QObjectPtr.h"
class QNetworkAccessManager;
class HttpMetaCache;
class BaseVersionList;
class BaseVersion;
namespace Meta
{
class Index;
}
#if defined(ENV)
#undef ENV
#endif
#define ENV (Env::getInstance())
class MULTIMC_LOGIC_EXPORT Env
{
friend class MultiMC;
private:
struct Private;
Env();
~Env();
static void dispose();
public:
static Env& getInstance();
QNetworkAccessManager &qnam() const;
shared_qobject_ptr<HttpMetaCache> metacache();
std::shared_ptr<IIconList> icons();
/// init the cache. FIXME: possible future hook point
void initHttpMetaCache();
/// Updates the application proxy settings from the settings object.
void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
void registerIconList(std::shared_ptr<IIconList> iconlist);
shared_qobject_ptr<Meta::Index> metadataIndex();
QString getJarsPath();
void setJarsPath(const QString & path);
bool isFeatureEnabled(const QString & featureName) const;
void enableFeature(const QString & featureName, bool state = true);
void getEnabledFeatures(QSet<QString> & features) const;
void setEnabledFeatures(const QSet<QString> & features) const;
protected:
Private * d;
};

View File

@@ -1,411 +0,0 @@
#include "InstanceImportTask.h"
#include "BaseInstance.h"
#include "FileSystem.h"
#include "Env.h"
#include "MMCZip.h"
#include "NullInstance.h"
#include "settings/INISettingsObject.h"
#include "icons/IIconList.h"
#include "icons/IconUtils.h"
#include <QtConcurrentRun>
// FIXME: this does not belong here, it's Minecraft/Flame specific
#include "minecraft/MinecraftInstance.h"
#include "minecraft/ComponentList.h"
#include "modplatform/flame/FileResolvingTask.h"
#include "modplatform/flame/PackManifest.h"
#include "Json.h"
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl)
{
m_sourceUrl = sourceUrl;
}
void InstanceImportTask::executeTask()
{
InstancePtr newInstance;
if (m_sourceUrl.isLocalFile())
{
m_archivePath = m_sourceUrl.toLocalFile();
processZipPack();
}
else
{
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
m_downloadRequired = true;
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
auto entry = ENV.metacache()->resolveEntry("general", path);
entry->setStale(true);
m_filesNetJob.reset(new NetJob(tr("Modpack download")));
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
m_archivePath = entry->getFullPath();
auto job = m_filesNetJob.get();
connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed);
m_filesNetJob->start();
}
}
void InstanceImportTask::downloadSucceeded()
{
processZipPack();
m_filesNetJob.reset();
}
void InstanceImportTask::downloadFailed(QString reason)
{
emitFailed(reason);
m_filesNetJob.reset();
}
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
{
setProgress(current / 2, total);
}
void InstanceImportTask::processZipPack()
{
setStatus(tr("Extracting modpack"));
QDir extractDir(m_stagingPath);
qDebug() << "Attempting to create instance from" << m_archivePath;
// open the zip and find relevant files in it
m_packZip.reset(new QuaZip(m_archivePath));
if (!m_packZip->open(QuaZip::mdUnzip))
{
emitFailed(tr("Unable to open supplied modpack zip file."));
return;
}
QStringList blacklist = {"instance.cfg", "manifest.json"};
QString mmcFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
QString flameFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
QString root;
if(!mmcFound.isNull())
{
// process as MultiMC instance/pack
qDebug() << "MultiMC:" << mmcFound;
root = mmcFound;
m_modpackType = ModpackType::MultiMC;
}
else if(!flameFound.isNull())
{
// process as Flame pack
qDebug() << "Flame:" << flameFound;
root = flameFound;
m_modpackType = ModpackType::Flame;
}
if(m_modpackType == ModpackType::Unknown)
{
emitFailed(tr("Archive does not contain a recognized modpack type."));
return;
}
// make sure we extract just the pack
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &InstanceImportTask::extractAborted);
m_extractFutureWatcher.setFuture(m_extractFuture);
}
void InstanceImportTask::extractFinished()
{
m_packZip.reset();
if (m_extractFuture.result().isEmpty())
{
emitFailed(tr("Failed to extract modpack"));
return;
}
QDir extractDir(m_stagingPath);
qDebug() << "Fixing permissions for extracted pack files...";
QDirIterator it(extractDir, QDirIterator::Subdirectories);
while (it.hasNext())
{
auto filepath = it.next();
QFileInfo file(filepath);
auto permissions = QFile::permissions(filepath);
auto origPermissions = permissions;
if(file.isDir())
{
// Folder +rwx for current user
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
}
else
{
// File +rw for current user
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
}
if(origPermissions != permissions)
{
if(!QFile::setPermissions(filepath, permissions))
{
logWarning(tr("Could not fix permissions for %1").arg(filepath));
}
else
{
qDebug() << "Fixed" << filepath;
}
}
}
switch(m_modpackType)
{
case ModpackType::Flame:
processFlame();
return;
case ModpackType::MultiMC:
processMultiMC();
return;
case ModpackType::Unknown:
emitFailed(tr("Archive does not contain a recognized modpack type."));
return;
}
}
void InstanceImportTask::extractAborted()
{
emitFailed(tr("Instance import has been aborted."));
return;
}
void InstanceImportTask::processFlame()
{
const static QMap<QString,QString> forgemap = {
{"1.2.5", "3.4.9.171"},
{"1.4.2", "6.0.1.355"},
{"1.4.7", "6.6.2.534"},
{"1.5.2", "7.8.1.737"}
};
Flame::Manifest pack;
try
{
QString configPath = FS::PathCombine(m_stagingPath, "manifest.json");
Flame::loadManifest(pack, configPath);
QFile::remove(configPath);
}
catch (const JSONValidationError &e)
{
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
return;
}
if(!pack.overrides.isEmpty())
{
QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides);
if (QFile::exists(overridePath))
{
QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
if (!QFile::rename(overridePath, mcPath))
{
emitFailed(tr("Could not rename the overrides folder:\n") + pack.overrides);
return;
}
}
else
{
logWarning(tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides));
}
}
QString forgeVersion;
for(auto &loader: pack.minecraft.modLoaders)
{
auto id = loader.id;
if(id.startsWith("forge-"))
{
id.remove("forge-");
forgeVersion = id;
continue;
}
logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
}
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
instanceSettings->registerSetting("InstanceType", "Legacy");
instanceSettings->set("InstanceType", "OneSix");
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
auto mcVersion = pack.minecraft.version;
// Hack to correct some 'special sauce'...
if(mcVersion.endsWith('.'))
{
mcVersion.remove(QRegExp("[.]+$"));
logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
}
auto components = instance.getComponentList();
components->buildingFromScratch();
components->setComponentVersion("net.minecraft", mcVersion, true);
if(!forgeVersion.isEmpty())
{
// FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata.
if(forgeVersion == "recommended")
{
if(forgemap.contains(mcVersion))
{
forgeVersion = forgemap[mcVersion];
}
else
{
logWarning(tr("Could not map recommended forge version for Minecraft %1").arg(mcVersion));
}
}
components->setComponentVersion("net.minecraftforge", forgeVersion);
}
if (m_instIcon != "default")
{
instance.setIconKey(m_instIcon);
}
else
{
if(pack.name.contains("Direwolf20"))
{
instance.setIconKey("steve");
}
else if(pack.name.contains("FTB") || pack.name.contains("Feed The Beast"))
{
instance.setIconKey("ftb_logo");
}
else
{
// default to something other than the MultiMC default to distinguish these
instance.setIconKey("flame");
}
}
QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
QFileInfo jarmodsInfo(jarmodsPath);
if(jarmodsInfo.isDir())
{
// install all the jar mods
qDebug() << "Found jarmods:";
QDir jarmodsDir(jarmodsPath);
QStringList jarMods;
for (auto info: jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
{
qDebug() << info.fileName();
jarMods.push_back(info.absoluteFilePath());
}
auto profile = instance.getComponentList();
profile->installJarMods(jarMods);
// nuke the original files
FS::deletePath(jarmodsPath);
}
instance.setName(m_instName);
m_modIdResolver.reset(new Flame::FileResolvingTask(pack));
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]()
{
auto results = m_modIdResolver->getResults();
m_filesNetJob.reset(new NetJob(tr("Mod download")));
for(auto result: results.files)
{
QString filename = result.fileName;
if(!result.required)
{
filename += ".disabled";
}
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
auto path = FS::PathCombine(m_stagingPath , relpath);
switch(result.type)
{
case Flame::File::Type::Folder:
{
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
// fall-through intentional, we treat these as plain old mods and dump them wherever.
}
case Flame::File::Type::SingleFile:
case Flame::File::Type::Mod:
{
qDebug() << "Will download" << result.url << "to" << path;
auto dl = Net::Download::makeFile(result.url, path);
m_filesNetJob->addNetAction(dl);
break;
}
case Flame::File::Type::Modpack:
logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath));
break;
case Flame::File::Type::Cmod2:
case Flame::File::Type::Ctoc:
case Flame::File::Type::Unknown:
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
break;
}
}
m_modIdResolver.reset();
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
{
m_filesNetJob.reset();
emitSucceeded();
}
);
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason)
{
m_filesNetJob.reset();
emitFailed(reason);
});
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
{
setProgress(current, total);
});
setStatus(tr("Downloading mods..."));
m_filesNetJob->start();
}
);
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
{
m_modIdResolver.reset();
emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
});
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total)
{
setProgress(current, total);
});
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status)
{
setStatus(status);
});
m_modIdResolver->start();
}
void InstanceImportTask::processMultiMC()
{
// FIXME: copy from FolderInstanceProvider!!! FIX IT!!!
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
instanceSettings->registerSetting("InstanceType", "Legacy");
NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
// reset time played on import... because packs.
instance.resetTimePlayed();
// set a new nice name
instance.setName(m_instName);
// if the icon was specified by user, use that. otherwise pull icon from the pack
if (m_instIcon != "default")
{
instance.setIconKey(m_instIcon);
}
else
{
m_instIcon = instance.iconKey();
auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
if (!importIconPath.isNull() && QFile::exists(importIconPath))
{
// import icon
auto iconList = ENV.icons();
if (iconList->iconFileExists(m_instIcon))
{
iconList->deleteIcon(m_instIcon);
}
iconList->installIcons({importIconPath});
}
}
emitSucceeded();
}

View File

@@ -1,10 +0,0 @@
#pragma once
#include <QString>
#include "multimc_logic_export.h"
namespace Strings
{
int MULTIMC_LOGIC_EXPORT naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs);
}

View File

@@ -1,71 +0,0 @@
/* Copyright 2013-2019 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 <QString>
#include <QFileInfo>
#include <QSet>
#include "minecraft/Mod.h"
#include <functional>
#include "multimc_logic_export.h"
#include <JlCompress.h>
namespace MMCZip
{
/**
* Merge two zip files, using a filter function
*/
bool MULTIMC_LOGIC_EXPORT mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
const JlCompress::FilterFunction filter = nullptr);
/**
* take a source jar, add mods to it, resulting in target jar
*/
bool MULTIMC_LOGIC_EXPORT createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods);
/**
* Find a single file in archive by file name (not path)
*
* \return the path prefix where the file is
*/
QString MULTIMC_LOGIC_EXPORT findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root = QString(""));
/**
* Find a multiple files of the same name in archive by file name
* If a file is found in a path, no deeper paths are searched
*
* \return true if anything was found
*/
bool MULTIMC_LOGIC_EXPORT findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString());
/**
* Extract a subdirectory from an archive
*/
QStringList MULTIMC_LOGIC_EXPORT extractSubDir(QuaZip *zip, const QString & subdir, const QString &target);
/**
* Extract a whole archive.
*
* \param fileCompressed The name of the archive.
* \param dir The directory to extract to, the current directory if left empty.
* \return The list of the full paths of the files extracted, empty on failure.
*/
QStringList MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir);
}

View File

@@ -1,7 +0,0 @@
#include "IIconList.h"
// blargh
IIconList::~IIconList()
{
}

View File

@@ -1,26 +0,0 @@
#pragma once
#include <QString>
#include <QStringList>
#include "multimc_logic_export.h"
enum IconType : unsigned
{
Builtin,
Transient,
FileBased,
ICONS_TOTAL,
ToBeDeleted
};
class MULTIMC_LOGIC_EXPORT IIconList
{
public:
virtual ~IIconList();
virtual bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type) = 0;
virtual bool deleteIcon(const QString &key) = 0;
virtual void saveIcon(const QString &key, const QString &path, const char * format) const = 0;
virtual bool iconFileExists(const QString &key) const = 0;
virtual void installIcons(const QStringList &iconFiles) = 0;
virtual void installIcon(const QString &file, const QString &name) = 0;
};

View File

@@ -1,433 +0,0 @@
/* Copyright 2013-2019 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 <QDir>
#include <QString>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include <quazip.h>
#include <quazipfile.h>
#include "Mod.h"
#include "settings/INIFile.h"
#include <FileSystem.h>
#include <QDebug>
Mod::Mod(const QFileInfo &file)
{
repath(file);
m_changedDateTime = file.lastModified();
}
void Mod::repath(const QFileInfo &file)
{
m_file = file;
QString name_base = file.fileName();
m_type = Mod::MOD_UNKNOWN;
if (m_file.isDir())
{
m_type = MOD_FOLDER;
m_name = name_base;
m_mmc_id = name_base;
}
else if (m_file.isFile())
{
if (name_base.endsWith(".disabled"))
{
m_enabled = false;
name_base.chop(9);
}
else
{
m_enabled = true;
}
m_mmc_id = name_base;
if (name_base.endsWith(".zip") || name_base.endsWith(".jar"))
{
m_type = MOD_ZIPFILE;
name_base.chop(4);
}
else if (name_base.endsWith(".litemod"))
{
m_type = MOD_LITEMOD;
name_base.chop(8);
}
else
{
m_type = MOD_SINGLEFILE;
}
m_name = name_base;
}
if (m_type == MOD_ZIPFILE)
{
QuaZip zip(m_file.filePath());
if (!zip.open(QuaZip::mdUnzip))
return;
QuaZipFile file(&zip);
if (zip.setCurrentFile("mcmod.info"))
{
if (!file.open(QIODevice::ReadOnly))
{
zip.close();
return;
}
ReadMCModInfo(file.readAll());
file.close();
zip.close();
return;
}
else if (zip.setCurrentFile("fabric.mod.json"))
{
if (!file.open(QIODevice::ReadOnly))
{
zip.close();
return;
}
ReadFabricModInfo(file.readAll());
file.close();
zip.close();
return;
}
else if (zip.setCurrentFile("forgeversion.properties"))
{
if (!file.open(QIODevice::ReadOnly))
{
zip.close();
return;
}
ReadForgeInfo(file.readAll());
file.close();
zip.close();
return;
}
zip.close();
}
else if (m_type == MOD_FOLDER)
{
QFileInfo mcmod_info(FS::PathCombine(m_file.filePath(), "mcmod.info"));
if (mcmod_info.isFile())
{
QFile mcmod(mcmod_info.filePath());
if (!mcmod.open(QIODevice::ReadOnly))
return;
auto data = mcmod.readAll();
if (data.isEmpty() || data.isNull())
return;
ReadMCModInfo(data);
}
}
else if (m_type == MOD_LITEMOD)
{
QuaZip zip(m_file.filePath());
if (!zip.open(QuaZip::mdUnzip))
return;
QuaZipFile file(&zip);
if (zip.setCurrentFile("litemod.json"))
{
if (!file.open(QIODevice::ReadOnly))
{
zip.close();
return;
}
ReadLiteModInfo(file.readAll());
file.close();
}
zip.close();
}
}
// NEW format
// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/6f62b37cea040daf350dc253eae6326dd9c822c3
// OLD format:
// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/5bf6a2d05145ec79387acc0d45c958642fb049fc
void Mod::ReadMCModInfo(QByteArray contents)
{
auto getInfoFromArray = [&](QJsonArray arr)->void
{
if (!arr.at(0).isObject())
return;
auto firstObj = arr.at(0).toObject();
m_mod_id = firstObj.value("modid").toString();
m_name = firstObj.value("name").toString();
m_version = firstObj.value("version").toString();
m_homeurl = firstObj.value("url").toString();
m_updateurl = firstObj.value("updateUrl").toString();
m_homeurl = m_homeurl.trimmed();
if(!m_homeurl.isEmpty())
{
// fix up url.
if (!m_homeurl.startsWith("http://") && !m_homeurl.startsWith("https://") &&
!m_homeurl.startsWith("ftp://"))
{
m_homeurl.prepend("http://");
}
}
m_description = firstObj.value("description").toString();
QJsonArray authors = firstObj.value("authorList").toArray();
if (authors.size() == 0)
authors = firstObj.value("authors").toArray();
if (authors.size() == 0)
m_authors = "";
else if (authors.size() >= 1)
{
m_authors = authors.at(0).toString();
for (int i = 1; i < authors.size(); i++)
{
m_authors += ", " + authors.at(i).toString();
}
}
m_credits = firstObj.value("credits").toString();
return;
}
;
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
// this is the very old format that had just the array
if (jsonDoc.isArray())
{
getInfoFromArray(jsonDoc.array());
}
else if (jsonDoc.isObject())
{
auto val = jsonDoc.object().value("modinfoversion");
if(val.isUndefined())
val = jsonDoc.object().value("modListVersion");
int version = val.toDouble();
if (version != 2)
{
qCritical() << "BAD stuff happened to mod json:";
qCritical() << contents;
return;
}
auto arrVal = jsonDoc.object().value("modlist");
if(arrVal.isUndefined())
arrVal = jsonDoc.object().value("modList");
if (arrVal.isArray())
{
getInfoFromArray(arrVal.toArray());
}
}
}
// https://fabricmc.net/wiki/documentation:fabric_mod_json
void Mod::ReadFabricModInfo(QByteArray contents)
{
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
auto object = jsonDoc.object();
auto schemaVersion = object.contains("schemaVersion") ? object.value("schemaVersion").toInt(0) : 0;
m_mod_id = object.value("id").toString();
m_version = object.value("version").toString();
m_name = object.contains("name") ? object.value("name").toString() : m_mod_id;
m_description = object.value("description").toString();
if (schemaVersion >= 1)
{
QJsonArray authors = object.value("authors").toArray();
m_authors = "";
for (int i = 0; i < authors.size(); i++)
{
QString author_name = authors.at(i).isObject()
? authors.at(i).toObject().value("name").toString()
: authors.at(i).toString();
if (i > 0)
m_authors += ", " + author_name;
else {
m_authors += author_name;
}
}
if (object.contains("contact"))
{
QJsonObject contact = object.value("contact").toObject();
if (contact.contains("homepage"))
m_homeurl = contact.value("homepage").toString();
}
}
}
void Mod::ReadForgeInfo(QByteArray contents)
{
// Read the data
m_name = "Minecraft Forge";
m_mod_id = "Forge";
m_homeurl = "http://www.minecraftforge.net/forum/";
INIFile ini;
if (!ini.loadFile(contents))
return;
QString major = ini.get("forge.major.number", "0").toString();
QString minor = ini.get("forge.minor.number", "0").toString();
QString revision = ini.get("forge.revision.number", "0").toString();
QString build = ini.get("forge.build.number", "0").toString();
m_version = major + "." + minor + "." + revision + "." + build;
}
void Mod::ReadLiteModInfo(QByteArray contents)
{
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
auto object = jsonDoc.object();
if (object.contains("name"))
{
m_mod_id = m_name = object.value("name").toString();
}
if (object.contains("version"))
{
m_version = object.value("version").toString("");
}
else
{
m_version = object.value("revision").toString("");
}
m_mcversion = object.value("mcversion").toString();
m_authors = object.value("author").toString();
m_description = object.value("description").toString();
m_homeurl = object.value("url").toString();
}
bool Mod::replace(Mod &with)
{
if (!destroy())
return false;
bool success = false;
auto t = with.type();
if (t == MOD_ZIPFILE || t == MOD_SINGLEFILE || t == MOD_LITEMOD)
{
qDebug() << "Copy: " << with.m_file.filePath() << " to " << m_file.filePath();
success = QFile::copy(with.m_file.filePath(), m_file.filePath());
}
if (t == MOD_FOLDER)
{
success = FS::copy(with.m_file.filePath(), m_file.path())();
}
if (success)
{
m_name = with.m_name;
m_mmc_id = with.m_mmc_id;
m_mod_id = with.m_mod_id;
m_version = with.m_version;
m_mcversion = with.m_mcversion;
m_description = with.m_description;
m_authors = with.m_authors;
m_credits = with.m_credits;
m_homeurl = with.m_homeurl;
m_type = with.m_type;
m_file.refresh();
}
return success;
}
bool Mod::destroy()
{
if (m_type == MOD_FOLDER)
{
QDir d(m_file.filePath());
if (d.removeRecursively())
{
m_type = MOD_UNKNOWN;
return true;
}
return false;
}
else if (m_type == MOD_SINGLEFILE || m_type == MOD_ZIPFILE || m_type == MOD_LITEMOD)
{
QFile f(m_file.filePath());
if (f.remove())
{
m_type = MOD_UNKNOWN;
return true;
}
return false;
}
return true;
}
QString Mod::version() const
{
switch (type())
{
case MOD_ZIPFILE:
case MOD_LITEMOD:
return m_version;
case MOD_FOLDER:
return "Folder";
case MOD_SINGLEFILE:
return "File";
default:
return "VOID";
}
}
bool Mod::enable(bool value)
{
if (m_type == Mod::MOD_UNKNOWN || m_type == Mod::MOD_FOLDER)
return false;
if (m_enabled == value)
return false;
QString path = m_file.absoluteFilePath();
if (value)
{
QFile foo(path);
if (!path.endsWith(".disabled"))
return false;
path.chop(9);
if (!foo.rename(path))
return false;
}
else
{
QFile foo(path);
path += ".disabled";
if (!foo.rename(path))
return false;
}
m_file = QFileInfo(path);
m_enabled = value;
return true;
}
bool Mod::operator==(const Mod &other) const
{
return mmc_id() == other.mmc_id();
}
bool Mod::strongCompare(const Mod &other) const
{
return mmc_id() == other.mmc_id() && version() == other.version() && type() == other.type();
}

View File

@@ -1,28 +0,0 @@
#pragma once
#include <QMap>
#include <QString>
#include <QSet>
#include <QDateTime>
#include "multimc_logic_export.h"
struct FMLlib
{
QString filename;
QString checksum;
bool ours;
};
struct VersionFilterData
{
VersionFilterData();
// mapping between minecraft versions and FML libraries required
QMap<QString, QList<FMLlib>> fmlLibsMapping;
// set of minecraft versions for which using forge installers is blacklisted
QSet<QString> forgeInstallerBlacklist;
// no new versions below this date will be accepted from Mojang servers
QDateTime legacyCutoffDate;
// Libraries that belong to LWJGL
QSet<QString> lwjglWhitelist;
};
extern VersionFilterData MULTIMC_LOGIC_EXPORT g_VersionFilterData;

View File

@@ -1,315 +0,0 @@
/* Copyright 2013-2019 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@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 "MojangAccount.h"
#include "flows/RefreshTask.h"
#include "flows/AuthenticateTask.h"
#include <QUuid>
#include <QJsonObject>
#include <QJsonArray>
#include <QRegExp>
#include <QStringList>
#include <QJsonDocument>
#include <QDebug>
MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object)
{
// The JSON object must at least have a username for it to be valid.
if (!object.value("username").isString())
{
qCritical() << "Can't load Mojang account info from JSON object. Username field is "
"missing or of the wrong type.";
return nullptr;
}
QString username = object.value("username").toString("");
QString clientToken = object.value("clientToken").toString("");
QString accessToken = object.value("accessToken").toString("");
QJsonArray profileArray = object.value("profiles").toArray();
if (profileArray.size() < 1)
{
qCritical() << "Can't load Mojang account with username \"" << username
<< "\". No profiles found.";
return nullptr;
}
QList<AccountProfile> profiles;
for (QJsonValue profileVal : profileArray)
{
QJsonObject profileObject = profileVal.toObject();
QString id = profileObject.value("id").toString("");
QString name = profileObject.value("name").toString("");
bool legacy = profileObject.value("legacy").toBool(false);
if (id.isEmpty() || name.isEmpty())
{
qWarning() << "Unable to load a profile because it was missing an ID or a name.";
continue;
}
profiles.append({id, name, legacy});
}
MojangAccountPtr account(new MojangAccount());
if (object.value("user").isObject())
{
User u;
QJsonObject userStructure = object.value("user").toObject();
u.id = userStructure.value("id").toString();
/*
QJsonObject propMap = userStructure.value("properties").toObject();
for(auto key: propMap.keys())
{
auto values = propMap.operator[](key).toArray();
for(auto value: values)
u.properties.insert(key, value.toString());
}
*/
account->m_user = u;
}
account->m_username = username;
account->m_clientToken = clientToken;
account->m_accessToken = accessToken;
account->m_profiles = profiles;
// Get the currently selected profile.
QString currentProfile = object.value("activeProfile").toString("");
if (!currentProfile.isEmpty())
account->setCurrentProfile(currentProfile);
return account;
}
MojangAccountPtr MojangAccount::createFromUsername(const QString &username)
{
MojangAccountPtr account(new MojangAccount());
account->m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
account->m_username = username;
return account;
}
QJsonObject MojangAccount::saveToJson() const
{
QJsonObject json;
json.insert("username", m_username);
json.insert("clientToken", m_clientToken);
json.insert("accessToken", m_accessToken);
QJsonArray profileArray;
for (AccountProfile profile : m_profiles)
{
QJsonObject profileObj;
profileObj.insert("id", profile.id);
profileObj.insert("name", profile.name);
profileObj.insert("legacy", profile.legacy);
profileArray.append(profileObj);
}
json.insert("profiles", profileArray);
QJsonObject userStructure;
{
userStructure.insert("id", m_user.id);
/*
QJsonObject userAttrs;
for(auto key: m_user.properties.keys())
{
auto array = QJsonArray::fromStringList(m_user.properties.values(key));
userAttrs.insert(key, array);
}
userStructure.insert("properties", userAttrs);
*/
}
json.insert("user", userStructure);
if (m_currentProfile != -1)
json.insert("activeProfile", currentProfile()->id);
return json;
}
bool MojangAccount::setCurrentProfile(const QString &profileId)
{
for (int i = 0; i < m_profiles.length(); i++)
{
if (m_profiles[i].id == profileId)
{
m_currentProfile = i;
return true;
}
}
return false;
}
const AccountProfile *MojangAccount::currentProfile() const
{
if (m_currentProfile == -1)
return nullptr;
return &m_profiles[m_currentProfile];
}
AccountStatus MojangAccount::accountStatus() const
{
if (m_accessToken.isEmpty())
return NotVerified;
else
return Verified;
}
std::shared_ptr<YggdrasilTask> MojangAccount::login(AuthSessionPtr session, QString password)
{
Q_ASSERT(m_currentTask.get() == nullptr);
// take care of the true offline status
if (accountStatus() == NotVerified && password.isEmpty())
{
if (session)
{
session->status = AuthSession::RequiresPassword;
fillSession(session);
}
return nullptr;
}
if(accountStatus() == Verified && !session->wants_online)
{
session->status = AuthSession::PlayableOffline;
session->auth_server_online = false;
fillSession(session);
return nullptr;
}
else
{
if (password.isEmpty())
{
m_currentTask.reset(new RefreshTask(this));
}
else
{
m_currentTask.reset(new AuthenticateTask(this, password));
}
m_currentTask->assignSession(session);
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
}
return m_currentTask;
}
void MojangAccount::authSucceeded()
{
auto session = m_currentTask->getAssignedSession();
if (session)
{
session->status =
session->wants_online ? AuthSession::PlayableOnline : AuthSession::PlayableOffline;
fillSession(session);
session->auth_server_online = true;
}
m_currentTask.reset();
emit changed();
}
void MojangAccount::authFailed(QString reason)
{
auto session = m_currentTask->getAssignedSession();
// This is emitted when the yggdrasil tasks time out or are cancelled.
// -> we treat the error as no-op
if (m_currentTask->state() == YggdrasilTask::STATE_FAILED_SOFT)
{
if (session)
{
session->status = accountStatus() == Verified ? AuthSession::PlayableOffline
: AuthSession::RequiresPassword;
session->auth_server_online = false;
fillSession(session);
}
}
else
{
m_accessToken = QString();
emit changed();
if (session)
{
session->status = AuthSession::RequiresPassword;
session->auth_server_online = true;
fillSession(session);
}
}
m_currentTask.reset();
}
void MojangAccount::fillSession(AuthSessionPtr session)
{
// the user name. you have to have an user name
session->username = m_username;
// volatile auth token
session->access_token = m_accessToken;
// the semi-permanent client token
session->client_token = m_clientToken;
if (currentProfile())
{
// profile name
session->player_name = currentProfile()->name;
// profile ID
session->uuid = currentProfile()->id;
// 'legacy' or 'mojang', depending on account type
session->user_type = currentProfile()->legacy ? "legacy" : "mojang";
if (!session->access_token.isEmpty())
{
session->session = "token:" + m_accessToken + ":" + m_profiles[m_currentProfile].id;
}
else
{
session->session = "-";
}
}
else
{
session->player_name = "Player";
session->session = "-";
}
session->u = user();
session->m_accountPtr = shared_from_this();
}
void MojangAccount::decrementUses()
{
Usable::decrementUses();
if(!isInUse())
{
emit changed();
qWarning() << "Account" << m_username << "is no longer in use.";
}
}
void MojangAccount::incrementUses()
{
bool wasInUse = isInUse();
Usable::incrementUses();
if(!wasInUse)
{
emit changed();
qWarning() << "Account" << m_username << "is now in use.";
}
}
void MojangAccount::invalidateClientToken()
{
m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
emit changed();
}

View File

@@ -1,182 +0,0 @@
/* Copyright 2013-2019 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 <QObject>
#include <QString>
#include <QList>
#include <QJsonObject>
#include <QPair>
#include <QMap>
#include <memory>
#include "AuthSession.h"
#include "Usable.h"
#include "multimc_logic_export.h"
class Task;
class YggdrasilTask;
class MojangAccount;
typedef std::shared_ptr<MojangAccount> MojangAccountPtr;
Q_DECLARE_METATYPE(MojangAccountPtr)
/**
* A profile within someone's Mojang account.
*
* Currently, the profile system has not been implemented by Mojang yet,
* but we might as well add some things for it in MultiMC right now so
* we don't have to rip the code to pieces to add it later.
*/
struct AccountProfile
{
QString id;
QString name;
bool legacy;
};
enum AccountStatus
{
NotVerified,
Verified
};
/**
* Object that stores information about a certain Mojang account.
*
* Said information may include things such as that account's username, client token, and access
* token if the user chose to stay logged in.
*/
class MULTIMC_LOGIC_EXPORT MojangAccount :
public QObject,
public Usable,
public std::enable_shared_from_this<MojangAccount>
{
Q_OBJECT
public: /* construction */
//! Do not copy accounts. ever.
explicit MojangAccount(const MojangAccount &other, QObject *parent) = delete;
//! Default constructor
explicit MojangAccount(QObject *parent = 0) : QObject(parent) {};
//! Creates an empty account for the specified user name.
static MojangAccountPtr createFromUsername(const QString &username);
//! Loads a MojangAccount from the given JSON object.
static MojangAccountPtr loadFromJson(const QJsonObject &json);
//! Saves a MojangAccount to a JSON object and returns it.
QJsonObject saveToJson() const;
public: /* manipulation */
/**
* Sets the currently selected profile to the profile with the given ID string.
* If profileId is not in the list of available profiles, the function will simply return
* false.
*/
bool setCurrentProfile(const QString &profileId);
/**
* Attempt to login. Empty password means we use the token.
* If the attempt fails because we already are performing some task, it returns false.
*/
std::shared_ptr<YggdrasilTask> login(AuthSessionPtr session, QString password = QString());
void invalidateClientToken();
public: /* queries */
const QString &username() const
{
return m_username;
}
const QString &clientToken() const
{
return m_clientToken;
}
const QString &accessToken() const
{
return m_accessToken;
}
const QList<AccountProfile> &profiles() const
{
return m_profiles;
}
const User &user()
{
return m_user;
}
//! Returns the currently selected profile (if none, returns nullptr)
const AccountProfile *currentProfile() const;
//! Returns whether the account is NotVerified, Verified or Online
AccountStatus accountStatus() const;
signals:
/**
* This signal is emitted when the account changes
*/
void changed();
// TODO: better signalling for the various possible state changes - especially errors
protected: /* variables */
QString m_username;
// Used to identify the client - the user can have multiple clients for the same account
// Think: different launchers, all connecting to the same account/profile
QString m_clientToken;
// Blank if not logged in.
QString m_accessToken;
// Index of the selected profile within the list of available
// profiles. -1 if nothing is selected.
int m_currentProfile = -1;
// List of available profiles.
QList<AccountProfile> m_profiles;
// the user structure, whatever it is.
User m_user;
// current task we are executing here
std::shared_ptr<YggdrasilTask> m_currentTask;
protected: /* methods */
void incrementUses() override;
void decrementUses() override;
private
slots:
void authSucceeded();
void authFailed(QString reason);
private:
void fillSession(AuthSessionPtr session);
public:
friend class YggdrasilTask;
friend class AuthenticateTask;
friend class ValidateTask;
friend class RefreshTask;
};

View File

@@ -1,468 +0,0 @@
/* Copyright 2013-2019 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 "MojangAccountList.h"
#include "MojangAccount.h"
#include <QIODevice>
#include <QFile>
#include <QTextStream>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonParseError>
#include <QDir>
#include <QDebug>
#include <FileSystem.h>
#define ACCOUNT_LIST_FORMAT_VERSION 2
MojangAccountList::MojangAccountList(QObject *parent) : QAbstractListModel(parent)
{
}
MojangAccountPtr MojangAccountList::findAccount(const QString &username) const
{
for (int i = 0; i < count(); i++)
{
MojangAccountPtr account = at(i);
if (account->username() == username)
return account;
}
return nullptr;
}
const MojangAccountPtr MojangAccountList::at(int i) const
{
return MojangAccountPtr(m_accounts.at(i));
}
void MojangAccountList::addAccount(const MojangAccountPtr account)
{
int row = m_accounts.count();
beginInsertRows(QModelIndex(), row, row);
connect(account.get(), SIGNAL(changed()), SLOT(accountChanged()));
m_accounts.append(account);
endInsertRows();
onListChanged();
}
void MojangAccountList::removeAccount(const QString &username)
{
int idx = 0;
for (auto account : m_accounts)
{
if (account->username() == username)
{
beginRemoveRows(QModelIndex(), idx, idx);
m_accounts.removeOne(account);
endRemoveRows();
return;
}
idx++;
}
onListChanged();
}
void MojangAccountList::removeAccount(QModelIndex index)
{
int row = index.row();
if(index.isValid() && row >= 0 && row < m_accounts.size())
{
auto & account = m_accounts[row];
if(account == m_activeAccount)
{
m_activeAccount = nullptr;
onActiveChanged();
}
beginRemoveRows(QModelIndex(), row, row);
m_accounts.removeAt(index.row());
endRemoveRows();
onListChanged();
}
}
MojangAccountPtr MojangAccountList::activeAccount() const
{
return m_activeAccount;
}
void MojangAccountList::setActiveAccount(const QString &username)
{
if (username.isEmpty() && m_activeAccount)
{
int idx = 0;
auto prevActiveAcc = m_activeAccount;
m_activeAccount = nullptr;
for (MojangAccountPtr account : m_accounts)
{
if (account == prevActiveAcc)
{
emit dataChanged(index(idx), index(idx));
}
idx ++;
}
onActiveChanged();
}
else
{
auto currentActiveAccount = m_activeAccount;
int currentActiveAccountIdx = -1;
auto newActiveAccount = m_activeAccount;
int newActiveAccountIdx = -1;
int idx = 0;
for (MojangAccountPtr account : m_accounts)
{
if (account->username() == username)
{
newActiveAccount = account;
newActiveAccountIdx = idx;
}
if(currentActiveAccount == account)
{
currentActiveAccountIdx = idx;
}
idx++;
}
if(currentActiveAccount != newActiveAccount)
{
emit dataChanged(index(currentActiveAccountIdx), index(currentActiveAccountIdx));
emit dataChanged(index(newActiveAccountIdx), index(newActiveAccountIdx));
m_activeAccount = newActiveAccount;
onActiveChanged();
}
}
}
void MojangAccountList::accountChanged()
{
// the list changed. there is no doubt.
onListChanged();
}
void MojangAccountList::onListChanged()
{
if (m_autosave)
// TODO: Alert the user if this fails.
saveList();
emit listChanged();
}
void MojangAccountList::onActiveChanged()
{
if (m_autosave)
saveList();
emit activeAccountChanged();
}
int MojangAccountList::count() const
{
return m_accounts.count();
}
QVariant MojangAccountList::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() > count())
return QVariant();
MojangAccountPtr account = at(index.row());
switch (role)
{
case Qt::DisplayRole:
switch (index.column())
{
case NameColumn:
return account->username();
default:
return QVariant();
}
case Qt::ToolTipRole:
return account->username();
case PointerRole:
return qVariantFromValue(account);
case Qt::CheckStateRole:
switch (index.column())
{
case ActiveColumn:
return account == m_activeAccount ? Qt::Checked : Qt::Unchecked;
}
default:
return QVariant();
}
}
QVariant MojangAccountList::headerData(int section, Qt::Orientation orientation, int role) const
{
switch (role)
{
case Qt::DisplayRole:
switch (section)
{
case ActiveColumn:
return tr("Active?");
case NameColumn:
return tr("Name");
default:
return QVariant();
}
case Qt::ToolTipRole:
switch (section)
{
case NameColumn:
return tr("The name of the version.");
default:
return QVariant();
}
default:
return QVariant();
}
}
int MojangAccountList::rowCount(const QModelIndex &) const
{
// Return count
return count();
}
int MojangAccountList::columnCount(const QModelIndex &) const
{
return 2;
}
Qt::ItemFlags MojangAccountList::flags(const QModelIndex &index) const
{
if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
{
return Qt::NoItemFlags;
}
return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
bool MojangAccountList::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
{
return false;
}
if(role == Qt::CheckStateRole)
{
if(value == Qt::Checked)
{
MojangAccountPtr account = this->at(index.row());
this->setActiveAccount(account->username());
}
}
emit dataChanged(index, index);
return true;
}
void MojangAccountList::updateListData(QList<MojangAccountPtr> versions)
{
beginResetModel();
m_accounts = versions;
endResetModel();
}
bool MojangAccountList::loadList(const QString &filePath)
{
QString path = filePath;
if (path.isEmpty())
path = m_listFilePath;
if (path.isEmpty())
{
qCritical() << "Can't load Mojang account list. No file path given and no default set.";
return false;
}
QFile file(path);
// Try to open the file and fail if we can't.
// TODO: We should probably report this error to the user.
if (!file.open(QIODevice::ReadOnly))
{
qCritical() << QString("Failed to read the account list file (%1).").arg(path).toUtf8();
return false;
}
// Read the file and close it.
QByteArray jsonData = file.readAll();
file.close();
QJsonParseError parseError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
// Fail if the JSON is invalid.
if (parseError.error != QJsonParseError::NoError)
{
qCritical() << QString("Failed to parse account list file: %1 at offset %2")
.arg(parseError.errorString(), QString::number(parseError.offset))
.toUtf8();
return false;
}
// Make sure the root is an object.
if (!jsonDoc.isObject())
{
qCritical() << "Invalid account list JSON: Root should be an array.";
return false;
}
QJsonObject root = jsonDoc.object();
// Make sure the format version matches.
if (root.value("formatVersion").toVariant().toInt() != ACCOUNT_LIST_FORMAT_VERSION)
{
QString newName = "accounts-old.json";
qWarning() << "Format version mismatch when loading account list. Existing one will be renamed to"
<< newName;
// Attempt to rename the old version.
file.rename(newName);
return false;
}
// Now, load the accounts array.
beginResetModel();
QJsonArray accounts = root.value("accounts").toArray();
for (QJsonValue accountVal : accounts)
{
QJsonObject accountObj = accountVal.toObject();
MojangAccountPtr account = MojangAccount::loadFromJson(accountObj);
if (account.get() != nullptr)
{
connect(account.get(), SIGNAL(changed()), SLOT(accountChanged()));
m_accounts.append(account);
}
else
{
qWarning() << "Failed to load an account.";
}
}
// Load the active account.
m_activeAccount = findAccount(root.value("activeAccount").toString(""));
endResetModel();
return true;
}
bool MojangAccountList::saveList(const QString &filePath)
{
QString path(filePath);
if (path.isEmpty())
path = m_listFilePath;
if (path.isEmpty())
{
qCritical() << "Can't save Mojang account list. No file path given and no default set.";
return false;
}
// make sure the parent folder exists
if(!FS::ensureFilePathExists(path))
return false;
// make sure the file wasn't overwritten with a folder before (fixes a bug)
QFileInfo finfo(path);
if(finfo.isDir())
{
QDir badDir(path);
badDir.removeRecursively();
}
qDebug() << "Writing account list to" << path;
qDebug() << "Building JSON data structure.";
// Build the JSON document to write to the list file.
QJsonObject root;
root.insert("formatVersion", ACCOUNT_LIST_FORMAT_VERSION);
// Build a list of accounts.
qDebug() << "Building account array.";
QJsonArray accounts;
for (MojangAccountPtr account : m_accounts)
{
QJsonObject accountObj = account->saveToJson();
accounts.append(accountObj);
}
// Insert the account list into the root object.
root.insert("accounts", accounts);
if(m_activeAccount)
{
// Save the active account.
root.insert("activeAccount", m_activeAccount->username());
}
// Create a JSON document object to convert our JSON to bytes.
QJsonDocument doc(root);
// Now that we're done building the JSON object, we can write it to the file.
qDebug() << "Writing account list to file.";
QFile file(path);
// Try to open the file and fail if we can't.
// TODO: We should probably report this error to the user.
if (!file.open(QIODevice::WriteOnly))
{
qCritical() << QString("Failed to read the account list file (%1).").arg(path).toUtf8();
return false;
}
// Write the JSON to the file.
file.write(doc.toJson());
file.setPermissions(QFile::ReadOwner|QFile::WriteOwner|QFile::ReadUser|QFile::WriteUser);
file.close();
qDebug() << "Saved account list to" << path;
return true;
}
void MojangAccountList::setListFilePath(QString path, bool autosave)
{
m_listFilePath = path;
m_autosave = autosave;
}
bool MojangAccountList::anyAccountIsValid()
{
for(auto account:m_accounts)
{
if(account->accountStatus() != NotVerified)
return true;
}
return false;
}

View File

@@ -1,201 +0,0 @@
/* Copyright 2013-2019 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 "MojangAccount.h"
#include <QObject>
#include <QVariant>
#include <QAbstractListModel>
#include <QSharedPointer>
#include "multimc_logic_export.h"
/*!
* \brief List of available Mojang accounts.
* This should be loaded in the background by MultiMC on startup.
*
* This class also inherits from QAbstractListModel. Methods from that
* class determine how this list shows up in a list view. Said methods
* all have a default implementation, but they can be overridden by subclasses to
* change the behavior of the list.
*/
class MULTIMC_LOGIC_EXPORT MojangAccountList : public QAbstractListModel
{
Q_OBJECT
public:
enum ModelRoles
{
PointerRole = 0x34B1CB48
};
enum VListColumns
{
// TODO: Add icon column.
// First column - Active?
ActiveColumn = 0,
// Second column - Name
NameColumn,
};
explicit MojangAccountList(QObject *parent = 0);
//! Gets the account at the given index.
virtual const MojangAccountPtr at(int i) const;
//! Returns the number of accounts in the list.
virtual int count() const;
//////// List Model Functions ////////
virtual QVariant data(const QModelIndex &index, int role) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
virtual int rowCount(const QModelIndex &parent) const;
virtual int columnCount(const QModelIndex &parent) const;
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
/*!
* Adds a the given Mojang account to the account list.
*/
virtual void addAccount(const MojangAccountPtr account);
/*!
* Removes the mojang account with the given username from the account list.
*/
virtual void removeAccount(const QString &username);
/*!
* Removes the account at the given QModelIndex.
*/
virtual void removeAccount(QModelIndex index);
/*!
* \brief Finds an account by its username.
* \param The username of the account to find.
* \return A const pointer to the account with the given username. NULL if
* one doesn't exist.
*/
virtual MojangAccountPtr findAccount(const QString &username) const;
/*!
* Sets the default path to save the list file to.
* If autosave is true, this list will automatically save to the given path whenever it changes.
* THIS FUNCTION DOES NOT LOAD THE LIST. If you set autosave, be sure to call loadList() immediately
* after calling this function to ensure an autosaved change doesn't overwrite the list you intended
* to load.
*/
virtual void setListFilePath(QString path, bool autosave = false);
/*!
* \brief Loads the account list from the given file path.
* If the given file is an empty string (default), will load from the default account list file.
* \return True if successful, otherwise false.
*/
virtual bool loadList(const QString &file = "");
/*!
* \brief Saves the account list to the given file.
* If the given file is an empty string (default), will save from the default account list file.
* \return True if successful, otherwise false.
*/
virtual bool saveList(const QString &file = "");
/*!
* \brief Gets a pointer to the account that the user has selected as their "active" account.
* Which account is active can be overridden on a per-instance basis, but this will return the one that
* is set as active globally.
* \return The currently active MojangAccount. If there isn't an active account, returns a null pointer.
*/
virtual MojangAccountPtr activeAccount() const;
/*!
* Sets the given account as the current active account.
* If the username given is an empty string, sets the active account to nothing.
*/
virtual void setActiveAccount(const QString &username);
/*!
* Returns true if any of the account is at least Validated
*/
bool anyAccountIsValid();
signals:
/*!
* Signal emitted to indicate that the account list has changed.
* This will also fire if the value of an element in the list changes (will be implemented
* later).
*/
void listChanged();
/*!
* Signal emitted to indicate that the active account has changed.
*/
void activeAccountChanged();
public
slots:
/**
* This is called when one of the accounts changes and the list needs to be updated
*/
void accountChanged();
protected:
/*!
* Called whenever the list changes.
* This emits the listChanged() signal and autosaves the list (if autosave is enabled).
*/
void onListChanged();
/*!
* Called whenever the active account changes.
* Emits the activeAccountChanged() signal and autosaves the list if enabled.
*/
void onActiveChanged();
QList<MojangAccountPtr> m_accounts;
/*!
* Account that is currently active.
*/
MojangAccountPtr m_activeAccount;
//! Path to the account list file. Empty string if there isn't one.
QString m_listFilePath;
/*!
* If true, the account list will automatically save to the account list path when it changes.
* Ignored if m_listFilePath is blank.
*/
bool m_autosave = false;
protected
slots:
/*!
* Updates this list with the given list of accounts.
* This is done by copying each account in the given list and inserting it
* into this one.
* We need to do this so that we can set the parents of the accounts are set to this
* account list. This can't be done in the load task, because the accounts the load
* task creates are on the load task's thread and Qt won't allow their parents
* to be set to something created on another thread.
* To get around that problem, we invoke this method on the GUI thread, which
* then copies the accounts and sets their parents correctly.
* \param accounts List of accounts whose parents should be set.
*/
virtual void updateListData(QList<MojangAccountPtr> versions);
};

View File

@@ -1,255 +0,0 @@
/* Copyright 2013-2019 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 "YggdrasilTask.h"
#include "MojangAccount.h"
#include <QObject>
#include <QString>
#include <QJsonObject>
#include <QJsonDocument>
#include <QNetworkReply>
#include <QByteArray>
#include <Env.h>
#include <net/URLConstants.h>
#include <QDebug>
YggdrasilTask::YggdrasilTask(MojangAccount *account, QObject *parent)
: Task(parent), m_account(account)
{
changeState(STATE_CREATED);
}
void YggdrasilTask::executeTask()
{
changeState(STATE_SENDING_REQUEST);
// Get the content of the request we're going to send to the server.
QJsonDocument doc(getRequestContent());
QUrl reqUrl(URLConstants::AUTH_BASE + getEndpoint());
QNetworkRequest netRequest(reqUrl);
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QByteArray requestData = doc.toJson();
m_netReply = ENV.qnam().post(netRequest, requestData);
connect(m_netReply, &QNetworkReply::finished, this, &YggdrasilTask::processReply);
connect(m_netReply, &QNetworkReply::uploadProgress, this, &YggdrasilTask::refreshTimers);
connect(m_netReply, &QNetworkReply::downloadProgress, this, &YggdrasilTask::refreshTimers);
connect(m_netReply, &QNetworkReply::sslErrors, this, &YggdrasilTask::sslErrors);
timeout_keeper.setSingleShot(true);
timeout_keeper.start(timeout_max);
counter.setSingleShot(false);
counter.start(time_step);
progress(0, timeout_max);
connect(&timeout_keeper, &QTimer::timeout, this, &YggdrasilTask::abortByTimeout);
connect(&counter, &QTimer::timeout, this, &YggdrasilTask::heartbeat);
}
void YggdrasilTask::refreshTimers(qint64, qint64)
{
timeout_keeper.stop();
timeout_keeper.start(timeout_max);
progress(count = 0, timeout_max);
}
void YggdrasilTask::heartbeat()
{
count += time_step;
progress(count, timeout_max);
}
bool YggdrasilTask::abort()
{
progress(timeout_max, timeout_max);
// TODO: actually use this in a meaningful way
m_aborted = YggdrasilTask::BY_USER;
m_netReply->abort();
return true;
}
void YggdrasilTask::abortByTimeout()
{
progress(timeout_max, timeout_max);
// TODO: actually use this in a meaningful way
m_aborted = YggdrasilTask::BY_TIMEOUT;
m_netReply->abort();
}
void YggdrasilTask::sslErrors(QList<QSslError> errors)
{
int i = 1;
for (auto error : errors)
{
qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString();
auto cert = error.certificate();
qCritical() << "Certificate in question:\n" << cert.toText();
i++;
}
}
void YggdrasilTask::processReply()
{
changeState(STATE_PROCESSING_RESPONSE);
switch (m_netReply->error())
{
case QNetworkReply::NoError:
break;
case QNetworkReply::TimeoutError:
changeState(STATE_FAILED_SOFT, tr("Authentication operation timed out."));
return;
case QNetworkReply::OperationCanceledError:
changeState(STATE_FAILED_SOFT, tr("Authentication operation cancelled."));
return;
case QNetworkReply::SslHandshakeFailedError:
changeState(
STATE_FAILED_SOFT,
tr("<b>SSL Handshake failed.</b><br/>There might be a few causes for it:<br/>"
"<ul>"
"<li>You use Windows XP and need to <a "
"href=\"https://www.microsoft.com/en-us/download/details.aspx?id=38918\">update "
"your root certificates</a></li>"
"<li>Some device on your network is interfering with SSL traffic. In that case, "
"you have bigger worries than Minecraft not starting.</li>"
"<li>Possibly something else. Check the MultiMC log file for details</li>"
"</ul>"));
return;
// used for invalid credentials and similar errors. Fall through.
case QNetworkReply::ContentAccessDenied:
case QNetworkReply::ContentOperationNotPermittedError:
break;
default:
changeState(STATE_FAILED_SOFT,
tr("Authentication operation failed due to a network error: %1 (%2)")
.arg(m_netReply->errorString()).arg(m_netReply->error()));
return;
}
// Try to parse the response regardless of the response code.
// Sometimes the auth server will give more information and an error code.
QJsonParseError jsonError;
QByteArray replyData = m_netReply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError);
// Check the response code.
int responseCode = m_netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (responseCode == 200)
{
// If the response code was 200, then there shouldn't be an error. Make sure
// anyways.
// Also, sometimes an empty reply indicates success. If there was no data received,
// pass an empty json object to the processResponse function.
if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0)
{
processResponse(replyData.size() > 0 ? doc.object() : QJsonObject());
return;
}
else
{
changeState(STATE_FAILED_SOFT, tr("Failed to parse authentication server response "
"JSON response: %1 at offset %2.")
.arg(jsonError.errorString())
.arg(jsonError.offset));
qCritical() << replyData;
}
return;
}
// If the response code was not 200, then Yggdrasil may have given us information
// about the error.
// If we can parse the response, then get information from it. Otherwise just say
// there was an unknown error.
if (jsonError.error == QJsonParseError::NoError)
{
// We were able to parse the server's response. Woo!
// Call processError. If a subclass has overridden it then they'll handle their
// stuff there.
qDebug() << "The request failed, but the server gave us an error message. "
"Processing error.";
processError(doc.object());
}
else
{
// The server didn't say anything regarding the error. Give the user an unknown
// error.
qDebug()
<< "The request failed and the server gave no error message. Unknown error.";
changeState(STATE_FAILED_SOFT,
tr("An unknown error occurred when trying to communicate with the "
"authentication server: %1").arg(m_netReply->errorString()));
}
}
void YggdrasilTask::processError(QJsonObject responseData)
{
QJsonValue errorVal = responseData.value("error");
QJsonValue errorMessageValue = responseData.value("errorMessage");
QJsonValue causeVal = responseData.value("cause");
if (errorVal.isString() && errorMessageValue.isString())
{
m_error = std::shared_ptr<Error>(new Error{
errorVal.toString(""), errorMessageValue.toString(""), causeVal.toString("")});
changeState(STATE_FAILED_HARD, m_error->m_errorMessageVerbose);
}
else
{
// Error is not in standard format. Don't set m_error and return unknown error.
changeState(STATE_FAILED_HARD, tr("An unknown Yggdrasil error occurred."));
}
}
QString YggdrasilTask::getStateMessage() const
{
switch (m_state)
{
case STATE_CREATED:
return "Waiting...";
case STATE_SENDING_REQUEST:
return tr("Sending request to auth servers...");
case STATE_PROCESSING_RESPONSE:
return tr("Processing response from servers...");
case STATE_SUCCEEDED:
return tr("Authentication task succeeded.");
case STATE_FAILED_SOFT:
return tr("Failed to contact the authentication server.");
case STATE_FAILED_HARD:
return tr("Failed to authenticate.");
default:
return tr("...");
}
}
void YggdrasilTask::changeState(YggdrasilTask::State newState, QString reason)
{
m_state = newState;
setStatus(getStateMessage());
if (newState == STATE_SUCCEEDED)
{
emitSucceeded();
}
else if (newState == STATE_FAILED_HARD || newState == STATE_FAILED_SOFT)
{
emitFailed(reason);
}
}
YggdrasilTask::State YggdrasilTask::state()
{
return m_state;
}

View File

@@ -1,202 +0,0 @@
/* Copyright 2013-2019 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 "AuthenticateTask.h"
#include "../MojangAccount.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QVariant>
#include <QDebug>
#include <QUuid>
AuthenticateTask::AuthenticateTask(MojangAccount * account, const QString &password,
QObject *parent)
: YggdrasilTask(account, parent), m_password(password)
{
}
QJsonObject AuthenticateTask::getRequestContent() const
{
/*
* {
* "agent": { // optional
* "name": "Minecraft", // So far this is the only encountered value
* "version": 1 // This number might be increased
* // by the vanilla client in the future
* },
* "username": "mojang account name", // Can be an email address or player name for
// unmigrated accounts
* "password": "mojang account password",
* "clientToken": "client identifier" // optional
* "requestUser": true/false // request the user structure
* }
*/
QJsonObject req;
{
QJsonObject agent;
// C++ makes string literals void* for some stupid reason, so we have to tell it
// QString... Thanks Obama.
agent.insert("name", QString("Minecraft"));
agent.insert("version", 1);
req.insert("agent", agent);
}
req.insert("username", m_account->username());
req.insert("password", m_password);
req.insert("requestUser", true);
// If we already have a client token, give it to the server.
// Otherwise, let the server give us one.
if(m_account->m_clientToken.isEmpty())
{
auto uuid = QUuid::createUuid();
auto uuidString = uuid.toString().remove('{').remove('-').remove('}');
m_account->m_clientToken = uuidString;
}
req.insert("clientToken", m_account->m_clientToken);
return req;
}
void AuthenticateTask::processResponse(QJsonObject responseData)
{
// Read the response data. We need to get the client token, access token, and the selected
// profile.
qDebug() << "Processing authentication response.";
// qDebug() << responseData;
// If we already have a client token, make sure the one the server gave us matches our
// existing one.
qDebug() << "Getting client token.";
QString clientToken = responseData.value("clientToken").toString("");
if (clientToken.isEmpty())
{
// Fail if the server gave us an empty client token
changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token."));
return;
}
if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken)
{
changeState(STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported."));
return;
}
// Set the client token.
m_account->m_clientToken = clientToken;
// Now, we set the access token.
qDebug() << "Getting access token.";
QString accessToken = responseData.value("accessToken").toString("");
if (accessToken.isEmpty())
{
// Fail if the server didn't give us an access token.
changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token."));
return;
}
// Set the access token.
m_account->m_accessToken = accessToken;
// Now we load the list of available profiles.
// Mojang hasn't yet implemented the profile system,
// but we might as well support what's there so we
// don't have trouble implementing it later.
qDebug() << "Loading profile list.";
QJsonArray availableProfiles = responseData.value("availableProfiles").toArray();
QList<AccountProfile> loadedProfiles;
for (auto iter : availableProfiles)
{
QJsonObject profile = iter.toObject();
// Profiles are easy, we just need their ID and name.
QString id = profile.value("id").toString("");
QString name = profile.value("name").toString("");
bool legacy = profile.value("legacy").toBool(false);
if (id.isEmpty() || name.isEmpty())
{
// This should never happen, but we might as well
// warn about it if it does so we can debug it easily.
// You never know when Mojang might do something truly derpy.
qWarning() << "Found entry in available profiles list with missing ID or name "
"field. Ignoring it.";
}
// Now, add a new AccountProfile entry to the list.
loadedProfiles.append({id, name, legacy});
}
// Put the list of profiles we loaded into the MojangAccount object.
m_account->m_profiles = loadedProfiles;
// Finally, we set the current profile to the correct value. This is pretty simple.
// We do need to make sure that the current profile that the server gave us
// is actually in the available profiles list.
// If it isn't, we'll just fail horribly (*shouldn't* ever happen, but you never know).
qDebug() << "Setting current profile.";
QJsonObject currentProfile = responseData.value("selectedProfile").toObject();
QString currentProfileId = currentProfile.value("id").toString("");
if (currentProfileId.isEmpty())
{
changeState(STATE_FAILED_HARD, tr("Authentication server didn't specify a currently selected profile. The account exists, but likely isn't premium."));
return;
}
if (!m_account->setCurrentProfile(currentProfileId))
{
changeState(STATE_FAILED_HARD, tr("Authentication server specified a selected profile that wasn't in the available profiles list."));
return;
}
// this is what the vanilla launcher passes to the userProperties launch param
if (responseData.contains("user"))
{
User u;
auto obj = responseData.value("user").toObject();
u.id = obj.value("id").toString();
auto propArray = obj.value("properties").toArray();
for (auto prop : propArray)
{
auto propTuple = prop.toObject();
auto name = propTuple.value("name").toString();
auto value = propTuple.value("value").toString();
u.properties.insert(name, value);
}
m_account->m_user = u;
}
// We've made it through the minefield of possible errors. Return true to indicate that
// we've succeeded.
qDebug() << "Finished reading authentication response.";
changeState(STATE_SUCCEEDED);
}
QString AuthenticateTask::getEndpoint() const
{
return "authenticate";
}
QString AuthenticateTask::getStateMessage() const
{
switch (m_state)
{
case STATE_SENDING_REQUEST:
return tr("Authenticating: Sending request...");
case STATE_PROCESSING_RESPONSE:
return tr("Authenticating: Processing response...");
default:
return YggdrasilTask::getStateMessage();
}
}

View File

@@ -1,46 +0,0 @@
/* Copyright 2013-2019 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 "../YggdrasilTask.h"
#include <QObject>
#include <QString>
#include <QJsonObject>
/**
* The authenticate task takes a MojangAccount with no access token and password and attempts to
* authenticate with Mojang's servers.
* If successful, it will set the MojangAccount's access token.
*/
class AuthenticateTask : public YggdrasilTask
{
Q_OBJECT
public:
AuthenticateTask(MojangAccount *account, const QString &password, QObject *parent = 0);
protected:
virtual QJsonObject getRequestContent() const override;
virtual QString getEndpoint() const override;
virtual void processResponse(QJsonObject responseData) override;
virtual QString getStateMessage() const override;
private:
QString m_password;
};

View File

@@ -1,144 +0,0 @@
/* Copyright 2013-2019 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 "RefreshTask.h"
#include "../MojangAccount.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QVariant>
#include <QDebug>
RefreshTask::RefreshTask(MojangAccount *account) : YggdrasilTask(account)
{
}
QJsonObject RefreshTask::getRequestContent() const
{
/*
* {
* "clientToken": "client identifier"
* "accessToken": "current access token to be refreshed"
* "selectedProfile": // specifying this causes errors
* {
* "id": "profile ID"
* "name": "profile name"
* }
* "requestUser": true/false // request the user structure
* }
*/
QJsonObject req;
req.insert("clientToken", m_account->m_clientToken);
req.insert("accessToken", m_account->m_accessToken);
/*
{
auto currentProfile = m_account->currentProfile();
QJsonObject profile;
profile.insert("id", currentProfile->id());
profile.insert("name", currentProfile->name());
req.insert("selectedProfile", profile);
}
*/
req.insert("requestUser", true);
return req;
}
void RefreshTask::processResponse(QJsonObject responseData)
{
// Read the response data. We need to get the client token, access token, and the selected
// profile.
qDebug() << "Processing authentication response.";
// qDebug() << responseData;
// If we already have a client token, make sure the one the server gave us matches our
// existing one.
QString clientToken = responseData.value("clientToken").toString("");
if (clientToken.isEmpty())
{
// Fail if the server gave us an empty client token
changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token."));
return;
}
if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken)
{
changeState(STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported."));
return;
}
// Now, we set the access token.
qDebug() << "Getting new access token.";
QString accessToken = responseData.value("accessToken").toString("");
if (accessToken.isEmpty())
{
// Fail if the server didn't give us an access token.
changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token."));
return;
}
// we validate that the server responded right. (our current profile = returned current
// profile)
QJsonObject currentProfile = responseData.value("selectedProfile").toObject();
QString currentProfileId = currentProfile.value("id").toString("");
if (m_account->currentProfile()->id != currentProfileId)
{
changeState(STATE_FAILED_HARD, tr("Authentication server didn't specify the same prefile as expected."));
return;
}
// this is what the vanilla launcher passes to the userProperties launch param
if (responseData.contains("user"))
{
User u;
auto obj = responseData.value("user").toObject();
u.id = obj.value("id").toString();
auto propArray = obj.value("properties").toArray();
for (auto prop : propArray)
{
auto propTuple = prop.toObject();
auto name = propTuple.value("name").toString();
auto value = propTuple.value("value").toString();
u.properties.insert(name, value);
}
m_account->m_user = u;
}
// We've made it through the minefield of possible errors. Return true to indicate that
// we've succeeded.
qDebug() << "Finished reading refresh response.";
// Reset the access token.
m_account->m_accessToken = accessToken;
changeState(STATE_SUCCEEDED);
}
QString RefreshTask::getEndpoint() const
{
return "refresh";
}
QString RefreshTask::getStateMessage() const
{
switch (m_state)
{
case STATE_SENDING_REQUEST:
return tr("Refreshing login token...");
case STATE_PROCESSING_RESPONSE:
return tr("Refreshing login token: Processing response...");
default:
return YggdrasilTask::getStateMessage();
}
}

View File

@@ -1,44 +0,0 @@
/* Copyright 2013-2019 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 "../YggdrasilTask.h"
#include <QObject>
#include <QString>
#include <QJsonObject>
/**
* The authenticate task takes a MojangAccount with a possibly timed-out access token
* and attempts to authenticate with Mojang's servers.
* If successful, it will set the new access token. The token is considered validated.
*/
class RefreshTask : public YggdrasilTask
{
Q_OBJECT
public:
RefreshTask(MojangAccount * account);
protected:
virtual QJsonObject getRequestContent() const override;
virtual QString getEndpoint() const override;
virtual void processResponse(QJsonObject responseData) override;
virtual QString getStateMessage() const override;
};

View File

@@ -1,61 +0,0 @@
/* Copyright 2013-2019 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 "ValidateTask.h"
#include "../MojangAccount.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QVariant>
#include <QDebug>
ValidateTask::ValidateTask(MojangAccount * account, QObject *parent)
: YggdrasilTask(account, parent)
{
}
QJsonObject ValidateTask::getRequestContent() const
{
QJsonObject req;
req.insert("accessToken", m_account->m_accessToken);
return req;
}
void ValidateTask::processResponse(QJsonObject responseData)
{
// Assume that if processError wasn't called, then the request was successful.
changeState(YggdrasilTask::STATE_SUCCEEDED);
}
QString ValidateTask::getEndpoint() const
{
return "validate";
}
QString ValidateTask::getStateMessage() const
{
switch (m_state)
{
case YggdrasilTask::STATE_SENDING_REQUEST:
return tr("Validating access token: Sending request...");
case YggdrasilTask::STATE_PROCESSING_RESPONSE:
return tr("Validating access token: Processing response...");
default:
return YggdrasilTask::getStateMessage();
}
}

View File

@@ -1,47 +0,0 @@
/* Copyright 2013-2019 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.
*/
/*
* :FIXME: DEAD CODE, DEAD CODE, DEAD CODE! :FIXME:
*/
#pragma once
#include "../YggdrasilTask.h"
#include <QObject>
#include <QString>
#include <QJsonObject>
/**
* The validate task takes a MojangAccount and checks to make sure its access token is valid.
*/
class ValidateTask : public YggdrasilTask
{
Q_OBJECT
public:
ValidateTask(MojangAccount *account, QObject *parent = 0);
protected:
virtual QJsonObject getRequestContent() const override;
virtual QString getEndpoint() const override;
virtual void processResponse(QJsonObject responseData) override;
virtual QString getStateMessage() const override;
private:
};

View File

@@ -1,393 +0,0 @@
/* Copyright 2013-2019 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 "Env.h"
#include "ForgeXzDownload.h"
#include <FileSystem.h>
#include <QCryptographicHash>
#include <QFileInfo>
#include <QDateTime>
#include <QDir>
#include <QDebug>
ForgeXzDownload::ForgeXzDownload(QString url, QString relative_path, MetaEntryPtr entry) : NetAction()
{
m_entry = entry;
m_target_path = entry->getFullPath();
m_pack200_xz_file.setFileTemplate("./dl_temp.XXXXXX");
m_status = Job_NotStarted;
m_url_path = relative_path;
m_url = url + ".pack.xz";
}
void ForgeXzDownload::start()
{
if(m_status == Job_Aborted)
{
qWarning() << "Attempt to start an aborted Download:" << m_url.toString();
emit aborted(m_index_within_job);
return;
}
m_status = Job_InProgress;
if (!m_entry->isStale())
{
m_status = Job_Finished;
emit succeeded(m_index_within_job);
return;
}
// can we actually create the real, final file?
if (!FS::ensureFilePathExists(m_target_path))
{
m_status = Job_Failed;
emit failed(m_index_within_job);
return;
}
qDebug() << "Downloading " << m_url.toString();
QNetworkRequest request(m_url);
request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->getETag().toLatin1());
request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)");
QNetworkReply *rep = ENV.qnam().get(request);
m_reply.reset(rep);
connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64)));
connect(rep, SIGNAL(finished()), SLOT(downloadFinished()));
connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError)));
connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead()));
}
void ForgeXzDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
m_total_progress = bytesTotal;
m_progress = bytesReceived;
emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal);
}
void ForgeXzDownload::downloadError(QNetworkReply::NetworkError error)
{
if(error == QNetworkReply::OperationCanceledError)
{
qCritical() << "Aborted " << m_url.toString();
m_status = Job_Aborted;
}
else
{
// error happened during download.
qCritical() << "Failed " << m_url.toString() << " with reason " << error;
m_status = Job_Failed;
}
}
void ForgeXzDownload::failAndTryNextMirror()
{
m_status = Job_Failed;
emit failed(m_index_within_job);
}
void ForgeXzDownload::downloadFinished()
{
// if the download succeeded
if (m_status != Job_Failed && m_status != Job_Aborted)
{
// nothing went wrong...
m_status = Job_Finished;
if (m_pack200_xz_file.isOpen())
{
// we actually downloaded something! process and isntall it
decompressAndInstall();
return;
}
else
{
// something bad happened -- on the local machine!
m_status = Job_Failed;
m_pack200_xz_file.remove();
m_reply.reset();
emit failed(m_index_within_job);
return;
}
}
else if(m_status == Job_Aborted)
{
m_pack200_xz_file.remove();
m_reply.reset();
emit failed(m_index_within_job);
emit aborted(m_index_within_job);
return;
}
// else the download failed
else
{
m_status = Job_Failed;
m_pack200_xz_file.close();
m_pack200_xz_file.remove();
m_reply.reset();
failAndTryNextMirror();
return;
}
}
void ForgeXzDownload::downloadReadyRead()
{
if (!m_pack200_xz_file.isOpen())
{
if (!m_pack200_xz_file.open())
{
/*
* Can't open the file... the job failed
*/
m_reply->abort();
emit failed(m_index_within_job);
return;
}
}
m_pack200_xz_file.write(m_reply->readAll());
}
#include "xz.h"
#include "unpack200.h"
#include <stdexcept>
#include <unistd.h>
const size_t buffer_size = 8196;
// NOTE: once this gets here, it can't be aborted anymore. we don't care.
void ForgeXzDownload::decompressAndInstall()
{
// rewind the downloaded temp file
m_pack200_xz_file.seek(0);
// de-xz'd file
QTemporaryFile pack200_file("./dl_temp.XXXXXX");
pack200_file.open();
bool xz_success = false;
// first, de-xz
{
uint8_t in[buffer_size];
uint8_t out[buffer_size];
struct xz_buf b;
struct xz_dec *s;
enum xz_ret ret;
xz_crc32_init();
xz_crc64_init();
s = xz_dec_init(XZ_DYNALLOC, 1 << 26);
if (s == nullptr)
{
xz_dec_end(s);
failAndTryNextMirror();
return;
}
b.in = in;
b.in_pos = 0;
b.in_size = 0;
b.out = out;
b.out_pos = 0;
b.out_size = buffer_size;
while (!xz_success)
{
if (b.in_pos == b.in_size)
{
b.in_size = m_pack200_xz_file.read((char *)in, sizeof(in));
b.in_pos = 0;
}
ret = xz_dec_run(s, &b);
if (b.out_pos == sizeof(out))
{
auto wresult = pack200_file.write((char *)out, b.out_pos);
if (wresult < 0 || size_t(wresult) != b.out_pos)
{
// msg = "Write error\n";
xz_dec_end(s);
failAndTryNextMirror();
return;
}
b.out_pos = 0;
}
if (ret == XZ_OK)
continue;
if (ret == XZ_UNSUPPORTED_CHECK)
{
// unsupported check. this is OK, but we should log this
continue;
}
auto wresult = pack200_file.write((char *)out, b.out_pos);
if (wresult < 0 || size_t(wresult) != b.out_pos)
{
// write error
pack200_file.close();
xz_dec_end(s);
return;
}
switch (ret)
{
case XZ_STREAM_END:
xz_dec_end(s);
xz_success = true;
break;
case XZ_MEM_ERROR:
qCritical() << "Memory allocation failed\n";
xz_dec_end(s);
failAndTryNextMirror();
return;
case XZ_MEMLIMIT_ERROR:
qCritical() << "Memory usage limit reached\n";
xz_dec_end(s);
failAndTryNextMirror();
return;
case XZ_FORMAT_ERROR:
qCritical() << "Not a .xz file\n";
xz_dec_end(s);
failAndTryNextMirror();
return;
case XZ_OPTIONS_ERROR:
qCritical() << "Unsupported options in the .xz headers\n";
xz_dec_end(s);
failAndTryNextMirror();
return;
case XZ_DATA_ERROR:
case XZ_BUF_ERROR:
qCritical() << "File is corrupt\n";
xz_dec_end(s);
failAndTryNextMirror();
return;
default:
qCritical() << "Bug!\n";
xz_dec_end(s);
failAndTryNextMirror();
return;
}
}
}
m_pack200_xz_file.remove();
// revert pack200
pack200_file.seek(0);
int handle_in = pack200_file.handle();
// FIXME: dispose of file handles, pointers and the like. Ideally wrap in objects.
if(handle_in == -1)
{
qCritical() << "Error reopening " << pack200_file.fileName();
failAndTryNextMirror();
return;
}
int handle_in_dup = dup (handle_in);
if(handle_in_dup == -1)
{
qCritical() << "Error reopening " << pack200_file.fileName();
failAndTryNextMirror();
return;
}
FILE *file_in = fdopen (handle_in_dup, "rb");
if(!file_in)
{
qCritical() << "Error reopening " << pack200_file.fileName();
failAndTryNextMirror();
return;
}
QFile qfile_out(m_target_path);
if(!qfile_out.open(QIODevice::WriteOnly))
{
qCritical() << "Error opening " << qfile_out.fileName();
failAndTryNextMirror();
return;
}
int handle_out = qfile_out.handle();
if(handle_out == -1)
{
qCritical() << "Error opening " << qfile_out.fileName();
failAndTryNextMirror();
return;
}
int handle_out_dup = dup (handle_out);
if(handle_out_dup == -1)
{
qCritical() << "Error reopening " << qfile_out.fileName();
failAndTryNextMirror();
return;
}
FILE *file_out = fdopen (handle_out_dup, "wb");
if(!file_out)
{
qCritical() << "Error opening " << qfile_out.fileName();
failAndTryNextMirror();
return;
}
try
{
// NOTE: this takes ownership of both FILE pointers. That's why we duplicate them above.
unpack_200(file_in, file_out);
}
catch (const std::runtime_error &err)
{
m_status = Job_Failed;
qCritical() << "Error unpacking " << pack200_file.fileName() << " : " << err.what();
QFile f(m_target_path);
if (f.exists())
f.remove();
failAndTryNextMirror();
return;
}
pack200_file.remove();
QFile jar_file(m_target_path);
if (!jar_file.open(QIODevice::ReadOnly))
{
jar_file.remove();
failAndTryNextMirror();
return;
}
auto hash = QCryptographicHash::hash(jar_file.readAll(), QCryptographicHash::Md5);
m_entry->setMD5Sum(hash.toHex().constData());
jar_file.close();
QFileInfo output_file_info(m_target_path);
m_entry->setETag(m_reply->rawHeader("ETag").constData());
m_entry->setLocalChangedTimestamp(output_file_info.lastModified().toUTC().toMSecsSinceEpoch());
m_entry->setStale(false);
ENV.metacache()->updateEntry(m_entry);
m_reply.reset();
emit succeeded(m_index_within_job);
}
bool ForgeXzDownload::abort()
{
if(m_reply)
m_reply->abort();
m_status = Job_Aborted;
return true;
}
bool ForgeXzDownload::canAbort()
{
return true;
}

View File

@@ -1,61 +0,0 @@
/* Copyright 2013-2019 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 "net/NetAction.h"
#include "net/HttpMetaCache.h"
#include <QFile>
#include <QTemporaryFile>
typedef std::shared_ptr<class ForgeXzDownload> ForgeXzDownloadPtr;
class ForgeXzDownload : public NetAction
{
Q_OBJECT
public:
MetaEntryPtr m_entry;
/// if saving to file, use the one specified in this string
QString m_target_path;
/// this is the output file, if any
QTemporaryFile m_pack200_xz_file;
/// path relative to the mirror base
QString m_url_path;
public:
explicit ForgeXzDownload(QString url, QString relative_path, MetaEntryPtr entry);
static ForgeXzDownloadPtr make(QString url, QString relative_path, MetaEntryPtr entry)
{
return ForgeXzDownloadPtr(new ForgeXzDownload(url, relative_path, entry));
}
virtual ~ForgeXzDownload(){};
bool canAbort() override;
protected
slots:
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
void downloadError(QNetworkReply::NetworkError error) override;
void downloadFinished() override;
void downloadReadyRead() override;
public
slots:
void start() override;
bool abort() override;
private:
void decompressAndInstall();
void failAndTryNextMirror();
};

View File

@@ -1,19 +0,0 @@
#include "CreateServerResourcePacksFolder.h"
#include "minecraft/MinecraftInstance.h"
#include "launch/LaunchTask.h"
#include "FileSystem.h"
CreateServerResourcePacksFolder::CreateServerResourcePacksFolder(LaunchTask* parent): LaunchStep(parent)
{
}
void CreateServerResourcePacksFolder::executeTask()
{
auto instance = m_parent->instance();
std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
if(!FS::ensureFolderPathExists(FS::PathCombine(minecraftInstance->gameRoot(), "server-resource-packs")))
{
emit logLine(tr("Couldn't create the 'server-resource-packs' folder"), MessageLevel::Error);
}
emitSucceeded();
}

View File

@@ -1,63 +0,0 @@
#include "FileResolvingTask.h"
#include "Json.h"
namespace {
const char * metabase = "https://cursemeta.dries007.net";
}
Flame::FileResolvingTask::FileResolvingTask(Flame::Manifest& toProcess)
: m_toProcess(toProcess)
{
}
void Flame::FileResolvingTask::executeTask()
{
setStatus(tr("Resolving mod IDs..."));
setProgress(0, m_toProcess.files.size());
m_dljob.reset(new NetJob("Mod id resolver"));
results.resize(m_toProcess.files.size());
int index = 0;
for(auto & file: m_toProcess.files)
{
auto projectIdStr = QString::number(file.projectId);
auto fileIdStr = QString::number(file.fileId);
QString metaurl = QString("%1/%2/%3.json").arg(metabase, projectIdStr, fileIdStr);
auto dl = Net::Download::makeByteArray(QUrl(metaurl), &results[index]);
m_dljob->addNetAction(dl);
index ++;
}
connect(m_dljob.get(), &NetJob::finished, this, &Flame::FileResolvingTask::netJobFinished);
m_dljob->start();
}
void Flame::FileResolvingTask::netJobFinished()
{
bool failed = false;
int index = 0;
for(auto & bytes: results)
{
auto & out = m_toProcess.files[index];
try
{
failed &= (!out.parseFromBytes(bytes));
}
catch (const JSONValidationError &e)
{
qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of a parsing error:";
qCritical() << e.cause();
qCritical() << "JSON:";
qCritical() << bytes;
failed = true;
}
index++;
}
if(!failed)
{
emitSucceeded();
}
else
{
emitFailed(tr("Some mod ID resolving tasks failed."));
}
}

View File

@@ -1,34 +0,0 @@
#pragma once
#include "tasks/Task.h"
#include "net/NetJob.h"
#include "PackManifest.h"
#include "multimc_logic_export.h"
namespace Flame
{
class MULTIMC_LOGIC_EXPORT FileResolvingTask : public Task
{
Q_OBJECT
public:
explicit FileResolvingTask(Flame::Manifest &toProcess);
virtual ~FileResolvingTask() {};
const Flame::Manifest &getResults() const
{
return m_toProcess;
}
protected:
virtual void executeTask() override;
protected slots:
void netJobFinished();
private: /* data */
Flame::Manifest m_toProcess;
QVector<QByteArray> results;
NetJobPtr m_dljob;
};
}

View File

@@ -1,126 +0,0 @@
#include "PackManifest.h"
#include "Json.h"
static void loadFileV1(Flame::File & f, QJsonObject & file)
{
f.projectId = Json::requireInteger(file, "projectID");
f.fileId = Json::requireInteger(file, "fileID");
f.required = Json::ensureBoolean(file, QString("required"), true);
}
static void loadModloaderV1(Flame::Modloader & m, QJsonObject & modLoader)
{
m.id = Json::requireString(modLoader, "id");
m.primary = Json::ensureBoolean(modLoader, QString("primary"), false);
}
static void loadMinecraftV1(Flame::Minecraft & m, QJsonObject & minecraft)
{
m.version = Json::requireString(minecraft, "version");
// extra libraries... apparently only used for a custom Minecraft launcher in the 1.2.5 FTB retro pack
// intended use is likely hardcoded in the 'Flame' client, the manifest says nothing
m.libraries = Json::ensureString(minecraft, QString("libraries"), QString());
auto arr = Json::ensureArray(minecraft, "modLoaders", QJsonArray());
for (const auto & item : arr)
{
auto obj = Json::requireObject(item);
Flame::Modloader loader;
loadModloaderV1(loader, obj);
m.modLoaders.append(loader);
}
}
static void loadManifestV1(Flame::Manifest & m, QJsonObject & manifest)
{
auto mc = Json::requireObject(manifest, "minecraft");
loadMinecraftV1(m.minecraft, mc);
m.name = Json::ensureString(manifest, QString("name"), "Unnamed");
m.version = Json::ensureString(manifest, QString("version"), QString());
m.author = Json::ensureString(manifest, QString("author"), "Anonymous Coward");
auto arr = Json::ensureArray(manifest, "files", QJsonArray());
for (const auto & item : arr)
{
auto obj = Json::requireObject(item);
Flame::File file;
loadFileV1(file, obj);
m.files.append(file);
}
m.overrides = Json::ensureString(manifest, "overrides", "overrides");
}
void Flame::loadManifest(Flame::Manifest & m, const QString &filepath)
{
auto doc = Json::requireDocument(filepath);
auto obj = Json::requireObject(doc);
m.manifestType = Json::requireString(obj, "manifestType");
if(m.manifestType != "minecraftModpack")
{
throw JSONValidationError("Not a modpack manifest!");
}
m.manifestVersion = Json::requireInteger(obj, "manifestVersion");
if(m.manifestVersion != 1)
{
throw JSONValidationError(QString("Unknown manifest version (%1)").arg(m.manifestVersion));
}
loadManifestV1(m, obj);
}
bool Flame::File::parseFromBytes(const QByteArray& bytes)
{
auto doc = Json::requireDocument(bytes);
auto obj = Json::requireObject(doc);
// result code signifies true failure.
if(obj.contains("code"))
{
qCritical() << "Resolving of" << projectId << fileId << "failed because of a negative result:";
qCritical() << bytes;
return false;
}
fileName = Json::requireString(obj, "FileNameOnDisk");
QString rawUrl = Json::requireString(obj, "DownloadURL");
url = QUrl(rawUrl, QUrl::TolerantMode);
if(!url.isValid())
{
throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl));
}
// This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience
// It is also optional
QJsonObject projObj = Json::ensureObject(obj, "_Project", {});
if(!projObj.isEmpty())
{
QString strType = Json::ensureString(projObj, "PackageType", "mod").toLower();
if(strType == "singlefile")
{
type = File::Type::SingleFile;
}
else if(strType == "ctoc")
{
type = File::Type::Ctoc;
}
else if(strType == "cmod2")
{
type = File::Type::Cmod2;
}
else if(strType == "mod")
{
type = File::Type::Mod;
}
else if(strType == "folder")
{
type = File::Type::Folder;
}
else if(strType == "modpack")
{
type = File::Type::Modpack;
}
else
{
qCritical() << "Resolving of" << projectId << fileId << "failed because of unknown file type:" << strType;
type = File::Type::Unknown;
return false;
}
targetFolder = Json::ensureString(projObj, "Path", "mods");
}
resolved = true;
return true;
}

View File

@@ -1,62 +0,0 @@
#pragma once
#include <QString>
#include <QVector>
#include <QUrl>
namespace Flame
{
struct File
{
// NOTE: throws JSONValidationError
bool parseFromBytes(const QByteArray &bytes);
int projectId = 0;
int fileId = 0;
// NOTE: the opposite to 'optional'. This is at the time of writing unused.
bool required = true;
// our
bool resolved = false;
QString fileName;
QUrl url;
QString targetFolder = QLatin1Literal("mods");
enum class Type
{
Unknown,
Folder,
Ctoc,
SingleFile,
Cmod2,
Modpack,
Mod
} type = Type::Mod;
};
struct Modloader
{
QString id;
bool primary = false;
};
struct Minecraft
{
QString version;
QString libraries;
QVector<Flame::Modloader> modLoaders;
};
struct Manifest
{
QString manifestType;
int manifestVersion = 0;
Flame::Minecraft minecraft;
QString name;
QString version;
QString author;
QVector<Flame::File> files;
QString overrides;
};
void loadManifest(Flame::Manifest & m, const QString &filepath);
}

View File

@@ -1,175 +0,0 @@
#include "UrlResolvingTask.h"
#include <QtXml>
#include <Json.h>
namespace {
const char * metabase = "https://cursemeta.dries007.net";
}
Flame::UrlResolvingTask::UrlResolvingTask(const QString& toProcess)
: m_url(toProcess)
{
}
void Flame::UrlResolvingTask::executeTask()
{
resolveUrl();
}
void Flame::UrlResolvingTask::resolveUrl()
{
setStatus(tr("Resolving URL..."));
setProgress(0, 1);
QUrl actualUrl(m_url);
if(actualUrl.host() != "www.curseforge.com") {
emitFailed(tr("Not a Twitch URL."));
return;
}
m_dljob.reset(new NetJob("URL resolver"));
bool weAreDigging = false;
needle = QString();
if(m_url.startsWith("https://")) {
if(m_url.endsWith("?client=y")) {
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download?client=y
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088?client=y
m_url.chop(9);
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088
}
if(m_url.endsWith("/download")) {
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download -> need to dig inside html...
weAreDigging = true;
needle = m_url;
needle.replace("https://", "twitch://");
needle.replace("/download", "/download-client/");
m_url.append("?client=y");
} else if (m_url.contains("/download/")) {
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088
m_url.replace("/download/", "/download-client/");
}
}
else if(m_url.startsWith("twitch://")) {
// twitch://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088
m_url.replace(0, 9, "https://");
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088
}
auto dl = Net::Download::makeByteArray(QUrl(m_url), &results);
m_dljob->addNetAction(dl);
if(weAreDigging) {
connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processHTML);
} else {
connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processCCIP);
}
m_dljob->start();
}
void Flame::UrlResolvingTask::processHTML()
{
QString htmlDoc = QString::fromUtf8(results);
auto index = htmlDoc.indexOf(needle);
if(index < 0) {
emitFailed(tr("Couldn't find the needle in the haystack..."));
return;
}
auto indexStart = index;
int indexEnd = -1;
while((index + 1) < htmlDoc.size() && htmlDoc[index] != '"') {
index ++;
if(htmlDoc[index] == '"') {
indexEnd = index;
break;
}
}
if(indexEnd > 0) {
QString found = htmlDoc.mid(indexStart, indexEnd - indexStart);
qDebug() << "Found needle: " << found;
// twitch://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088
m_url = found;
resolveUrl();
return;
}
emitFailed(tr("Couldn't find the end of the needle in the haystack..."));
return;
}
void Flame::UrlResolvingTask::processCCIP()
{
QDomDocument doc;
if (!doc.setContent(results)) {
qDebug() << results;
emitFailed(tr("Resolving failed."));
return;
}
auto packageNode = doc.namedItem("package");
if(!packageNode.isElement()) {
emitFailed(tr("Resolving failed: missing package root element."));
return;
}
auto projectNode = packageNode.namedItem("project");
if(!projectNode.isElement()) {
emitFailed(tr("Resolving failed: missing project element."));
return;
}
auto attribs = projectNode.attributes();
auto projectIdNode = attribs.namedItem("id");
if(!projectIdNode.isAttr()) {
emitFailed(tr("Resolving failed: missing id attribute."));
return;
}
auto fileIdNode = attribs.namedItem("file");
if(!fileIdNode.isAttr()) {
emitFailed(tr("Resolving failed: missing file attribute."));
return;
}
auto projectId = projectIdNode.nodeValue();
auto fileId = fileIdNode.nodeValue();
bool success = true;
m_result.projectId = projectId.toInt(&success);
if(!success) {
emitFailed(tr("Failed to resove projectId as a number."));
return;
}
m_result.fileId = fileId.toInt(&success);
if(!success) {
emitFailed(tr("Failed to resove fileId as a number."));
return;
}
qDebug() << "Resolved" << m_url << "as" << m_result.projectId << "/" << m_result.fileId;
resolveIDs();
}
void Flame::UrlResolvingTask::resolveIDs()
{
setStatus(tr("Resolving mod IDs..."));
m_dljob.reset(new NetJob("Mod id resolver"));
auto projectIdStr = QString::number(m_result.projectId);
auto fileIdStr = QString::number(m_result.fileId);
QString metaurl = QString("%1/%2/%3.json").arg(metabase, projectIdStr, fileIdStr);
auto dl = Net::Download::makeByteArray(QUrl(metaurl), &results);
m_dljob->addNetAction(dl);
connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processCursemeta);
m_dljob->start();
}
void Flame::UrlResolvingTask::processCursemeta()
{
try {
if(m_result.parseFromBytes(results)) {
emitSucceeded();
qDebug() << results;
return;
}
} catch (const JSONValidationError &e) {
qCritical() << "Resolving of" << m_result.projectId << m_result.fileId << "failed because of a parsing error:";
qCritical() << e.cause();
qCritical() << "JSON:";
qCritical() << results;
}
emitFailed(tr("Failed to resolve the modpack file."));
}

View File

@@ -1,43 +0,0 @@
#pragma once
#include "tasks/Task.h"
#include "net/NetJob.h"
#include "PackManifest.h"
#include "multimc_logic_export.h"
namespace Flame
{
class MULTIMC_LOGIC_EXPORT UrlResolvingTask : public Task
{
Q_OBJECT
public:
explicit UrlResolvingTask(const QString &toProcess);
virtual ~UrlResolvingTask() {};
const Flame::File &getResults() const
{
return m_result;
}
protected:
virtual void executeTask() override;
protected slots:
void processCCIP();
void processHTML();
void processCursemeta();
private:
void resolveUrl();
void resolveIDs();
private: /* data */
QString m_url;
QString needle;
Flame::File m_result;
QByteArray results;
NetJobPtr m_dljob;
};
}

View File

@@ -1,40 +0,0 @@
#pragma once
#include "net/NetJob.h"
#include <QTemporaryDir>
#include <QByteArray>
#include <QObject>
#include "PackHelpers.h"
class MULTIMC_LOGIC_EXPORT FtbPackFetchTask : public QObject {
Q_OBJECT
public:
FtbPackFetchTask() = default;
virtual ~FtbPackFetchTask() = default;
void fetch();
void fetchPrivate(const QStringList &toFetch);
private:
NetJobPtr jobPtr;
QByteArray publicModpacksXmlFileData;
QByteArray thirdPartyModpacksXmlFileData;
bool parseAndAddPacks(QByteArray &data, FtbPackType packType, FtbModpackList &list);
FtbModpackList publicPacks;
FtbModpackList thirdPartyPacks;
protected slots:
void fileDownloadFinished();
void fileDownloadFailed(QString reason);
signals:
void finished(FtbModpackList publicPacks, FtbModpackList thirdPartyPacks);
void failed(QString reason);
void privateFileDownloadFinished(FtbModpack modpack);
void privateFileDownloadFailed(QString reason, QString packCode);
};

View File

@@ -1,16 +0,0 @@
#include "URLConstants.h"
namespace URLConstants {
QString getLegacyJarUrl(QString version)
{
return AWS_DOWNLOAD_VERSIONS + getJarPath(version);
}
QString getJarPath(QString version)
{
return version + "/" + version + ".jar";
}
}

View File

@@ -1,36 +0,0 @@
/* Copyright 2013-2019 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 <QString>
namespace URLConstants
{
const QString AWS_DOWNLOAD_VERSIONS("https://s3.amazonaws.com/Minecraft.Download/versions/");
const QString RESOURCE_BASE("https://resources.download.minecraft.net/");
const QString LIBRARY_BASE("https://libraries.minecraft.net/");
const QString SKINS_BASE("https://crafatar.com/skins/");
const QString AUTH_BASE("https://authserver.mojang.com/");
const QString MOJANG_STATUS_URL("https://status.mojang.com/check");
const QString IMGUR_BASE_URL("https://api.imgur.com/3/");
const QString FMLLIBS_OUR_BASE_URL("https://files.multimc.org/fmllibs/");
const QString FMLLIBS_FORGE_BASE_URL("https://files.minecraftforge.net/fmllibs/");
const QString TRANSLATIONS_BASE_URL("https://files.multimc.org/translations/");
const QString FTB_CDN_BASE_URL("https://ftb.forgecdn.net/FTB2/");
QString getJarPath(QString version);
QString getLegacyJarUrl(QString version);
}

View File

@@ -1,33 +0,0 @@
#pragma once
#include "net/NetAction.h"
#include "Screenshot.h"
#include "multimc_logic_export.h"
typedef std::shared_ptr<class ImgurUpload> ImgurUploadPtr;
class MULTIMC_LOGIC_EXPORT ImgurUpload : public NetAction
{
public:
explicit ImgurUpload(ScreenshotPtr shot);
static ImgurUploadPtr make(ScreenshotPtr shot)
{
return ImgurUploadPtr(new ImgurUpload(shot));
}
protected
slots:
virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
virtual void downloadError(QNetworkReply::NetworkError error);
virtual void downloadFinished();
virtual void downloadReadyRead()
{
}
public
slots:
virtual void start();
private:
ScreenshotPtr m_shot;
bool finished = true;
};

View File

@@ -1,148 +0,0 @@
/* Copyright 2013-2019 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 "StatusChecker.h"
#include <net/URLConstants.h>
#include <QByteArray>
#include <QDebug>
StatusChecker::StatusChecker()
{
}
void StatusChecker::timerEvent(QTimerEvent *e)
{
QObject::timerEvent(e);
reloadStatus();
}
void StatusChecker::reloadStatus()
{
if (isLoadingStatus())
{
// qDebug() << "Ignored request to reload status. Currently reloading already.";
return;
}
// qDebug() << "Reloading status.";
NetJob* job = new NetJob("Status JSON");
job->addNetAction(Net::Download::makeByteArray(URLConstants::MOJANG_STATUS_URL, &dataSink));
QObject::connect(job, &NetJob::succeeded, this, &StatusChecker::statusDownloadFinished);
QObject::connect(job, &NetJob::failed, this, &StatusChecker::statusDownloadFailed);
m_statusNetJob.reset(job);
emit statusLoading(true);
job->start();
}
void StatusChecker::statusDownloadFinished()
{
qDebug() << "Finished loading status JSON.";
m_statusEntries.clear();
m_statusNetJob.reset();
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(dataSink, &jsonError);
if (jsonError.error != QJsonParseError::NoError)
{
fail("Error parsing status JSON:" + jsonError.errorString());
return;
}
if (!jsonDoc.isArray())
{
fail("Error parsing status JSON: JSON root is not an array");
return;
}
QJsonArray root = jsonDoc.array();
for(auto status = root.begin(); status != root.end(); ++status)
{
QVariantMap map = (*status).toObject().toVariantMap();
for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter)
{
QString key = iter.key();
QVariant value = iter.value();
if(value.type() == QVariant::Type::String)
{
m_statusEntries.insert(key, value.toString());
//qDebug() << "Status JSON object: " << key << m_statusEntries[key];
}
else
{
fail("Malformed status JSON: expected status type to be a string.");
return;
}
}
}
succeed();
}
void StatusChecker::statusDownloadFailed(QString reason)
{
fail(tr("Failed to load status JSON:\n%1").arg(reason));
}
QMap<QString, QString> StatusChecker::getStatusEntries() const
{
return m_statusEntries;
}
bool StatusChecker::isLoadingStatus() const
{
return m_statusNetJob.get() != nullptr;
}
QString StatusChecker::getLastLoadErrorMsg() const
{
return m_lastLoadError;
}
void StatusChecker::succeed()
{
if(m_prevEntries != m_statusEntries)
{
emit statusChanged(m_statusEntries);
m_prevEntries = m_statusEntries;
}
m_lastLoadError = "";
qDebug() << "Status loading succeeded.";
m_statusNetJob.reset();
emit statusLoading(false);
}
void StatusChecker::fail(const QString& errorMsg)
{
if(m_prevEntries != m_statusEntries)
{
emit statusChanged(m_statusEntries);
m_prevEntries = m_statusEntries;
}
m_lastLoadError = errorMsg;
qDebug() << "Failed to load status:" << errorMsg;
m_statusNetJob.reset();
emit statusLoading(false);
}

View File

@@ -1,60 +0,0 @@
/* Copyright 2013-2019 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 <QObject>
#include <QString>
#include <QList>
#include <net/NetJob.h>
#include "multimc_logic_export.h"
class MULTIMC_LOGIC_EXPORT StatusChecker : public QObject
{
Q_OBJECT
public: /* con/des */
StatusChecker();
public: /* methods */
QString getLastLoadErrorMsg() const;
bool isLoadingStatus() const;
QMap<QString, QString> getStatusEntries() const;
signals:
void statusLoading(bool loading);
void statusChanged(QMap<QString, QString> newStatus);
public slots:
void reloadStatus();
protected: /* methods */
virtual void timerEvent(QTimerEvent *);
protected slots:
void statusDownloadFinished();
void statusDownloadFailed(QString reason);
void succeed();
void fail(const QString& errorMsg);
protected: /* data */
QMap<QString, QString> m_prevEntries;
QMap<QString, QString> m_statusEntries;
NetJobPtr m_statusNetJob;
QString m_lastLoadError;
QByteArray dataSink;
};

View File

@@ -1,54 +0,0 @@
#include "BuildConfig.h"
#include <QObject>
Config BuildConfig;
Config::Config()
{
// Version information
VERSION_MAJOR = @MultiMC_VERSION_MAJOR@;
VERSION_MINOR = @MultiMC_VERSION_MINOR@;
VERSION_HOTFIX = @MultiMC_VERSION_HOTFIX@;
VERSION_BUILD = @MultiMC_VERSION_BUILD@;
BUILD_PLATFORM = "@MultiMC_BUILD_PLATFORM@";
CHANLIST_URL = "@MultiMC_CHANLIST_URL@";
ANALYTICS_ID = "@MultiMC_ANALYTICS_ID@";
NOTIFICATION_URL = "@MultiMC_NOTIFICATION_URL@";
FULL_VERSION_STR = "@MultiMC_VERSION_MAJOR@.@MultiMC_VERSION_MINOR@.@MultiMC_VERSION_BUILD@";
GIT_COMMIT = "@MultiMC_GIT_COMMIT@";
GIT_REFSPEC = "@MultiMC_GIT_REFSPEC@";
if(GIT_REFSPEC.startsWith("refs/heads/") && !CHANLIST_URL.isEmpty() && VERSION_BUILD >= 0)
{
VERSION_CHANNEL = GIT_REFSPEC;
VERSION_CHANNEL.remove("refs/heads/");
UPDATER_ENABLED = true;
}
else
{
VERSION_CHANNEL = QObject::tr("custom");
}
VERSION_STR = "@MultiMC_VERSION_STRING@";
NEWS_RSS_URL = "@MultiMC_NEWS_RSS_URL@";
PASTE_EE_KEY = "@MultiMC_PASTE_EE_API_KEY@";
}
QString Config::printableVersionString() const
{
QString vstr = QString("%1.%2.%3").arg(QString::number(VERSION_MAJOR), QString::number(VERSION_MINOR), QString::number(VERSION_HOTFIX));
// If the build is not a main release, append the channel
if(VERSION_CHANNEL != "stable")
{
vstr += "-" + VERSION_CHANNEL;
}
// if a build number is set, also add it to the end
if(VERSION_BUILD >= 0)
{
vstr += "-" + QString::number(VERSION_BUILD);
}
return vstr;
}

View File

@@ -1,392 +0,0 @@
project(application)
######## Configure the file with build properties ########
configure_file("${PROJECT_SOURCE_DIR}/BuildConfig.cpp.in" "${PROJECT_BINARY_DIR}/BuildConfig.cpp")
################################ FILES ################################
######## Sources and headers ########
SET(MULTIMC_SOURCES
# Application base
main.cpp
MultiMC.h
MultiMC.cpp
BuildConfig.h
${PROJECT_BINARY_DIR}/BuildConfig.cpp
UpdateController.cpp
UpdateController.h
# GUI - general utilities
GuiUtil.h
GuiUtil.cpp
ColumnResizer.h
ColumnResizer.cpp
InstanceProxyModel.h
InstanceProxyModel.cpp
VersionProxyModel.h
VersionProxyModel.cpp
ColorCache.h
ColorCache.cpp
HoeDown.h
# Super secret!
KonamiCode.h
KonamiCode.cpp
# GUI - windows
MainWindow.h
MainWindow.cpp
InstanceWindow.h
InstanceWindow.cpp
# GUI - setup wizard
setupwizard/SetupWizard.h
setupwizard/SetupWizard.cpp
setupwizard/AnalyticsWizardPage.cpp
setupwizard/AnalyticsWizardPage.h
setupwizard/BaseWizardPage.h
setupwizard/JavaWizardPage.cpp
setupwizard/JavaWizardPage.h
setupwizard/LanguageWizardPage.cpp
setupwizard/LanguageWizardPage.h
# GUI - themes
themes/FusionTheme.cpp
themes/FusionTheme.h
themes/BrightTheme.cpp
themes/BrightTheme.h
themes/CustomTheme.cpp
themes/CustomTheme.h
themes/DarkTheme.cpp
themes/DarkTheme.h
themes/ITheme.cpp
themes/ITheme.h
themes/SystemTheme.cpp
themes/SystemTheme.h
# Processes
LaunchController.h
LaunchController.cpp
# page provider for instances
InstancePageProvider.h
# Common java checking UI
JavaCommon.h
JavaCommon.cpp
# GUI - paged dialog base
pages/BasePage.h
pages/BasePageContainer.h
pages/BasePageProvider.h
# GUI - instance pages
pages/instance/GameOptionsPage.cpp
pages/instance/GameOptionsPage.h
pages/instance/VersionPage.cpp
pages/instance/VersionPage.h
pages/instance/TexturePackPage.h
pages/instance/ResourcePackPage.h
pages/instance/ModFolderPage.cpp
pages/instance/ModFolderPage.h
pages/instance/NotesPage.cpp
pages/instance/NotesPage.h
pages/instance/LogPage.cpp
pages/instance/LogPage.h
pages/instance/InstanceSettingsPage.cpp
pages/instance/InstanceSettingsPage.h
pages/instance/ScreenshotsPage.cpp
pages/instance/ScreenshotsPage.h
pages/instance/OtherLogsPage.cpp
pages/instance/OtherLogsPage.h
pages/instance/ServersPage.cpp
pages/instance/ServersPage.h
pages/instance/LegacyUpgradePage.cpp
pages/instance/LegacyUpgradePage.h
pages/instance/WorldListPage.cpp
pages/instance/WorldListPage.h
# GUI - global settings pages
pages/global/AccountListPage.cpp
pages/global/AccountListPage.h
pages/global/CustomCommandsPage.cpp
pages/global/CustomCommandsPage.h
pages/global/ExternalToolsPage.cpp
pages/global/ExternalToolsPage.h
pages/global/JavaPage.cpp
pages/global/JavaPage.h
pages/global/LanguagePage.cpp
pages/global/LanguagePage.h
pages/global/MinecraftPage.cpp
pages/global/MinecraftPage.h
pages/global/MultiMCPage.cpp
pages/global/MultiMCPage.h
pages/global/ProxyPage.cpp
pages/global/ProxyPage.h
pages/global/PasteEEPage.cpp
pages/global/PasteEEPage.h
pages/global/PackagesPage.cpp
pages/global/PackagesPage.h
# GUI - platform pages
pages/modplatform/VanillaPage.cpp
pages/modplatform/VanillaPage.h
pages/modplatform/FTBPage.cpp
pages/modplatform/FTBPage.h
pages/modplatform/FtbListModel.h
pages/modplatform/FtbListModel.cpp
pages/modplatform/TwitchPage.cpp
pages/modplatform/TwitchPage.h
pages/modplatform/TechnicPage.cpp
pages/modplatform/TechnicPage.h
pages/modplatform/ImportPage.cpp
pages/modplatform/ImportPage.h
# GUI - dialogs
dialogs/AboutDialog.cpp
dialogs/AboutDialog.h
dialogs/ProfileSelectDialog.cpp
dialogs/ProfileSelectDialog.h
dialogs/CopyInstanceDialog.cpp
dialogs/CopyInstanceDialog.h
dialogs/CustomMessageBox.cpp
dialogs/CustomMessageBox.h
dialogs/EditAccountDialog.cpp
dialogs/EditAccountDialog.h
dialogs/ExportInstanceDialog.cpp
dialogs/ExportInstanceDialog.h
dialogs/IconPickerDialog.cpp
dialogs/IconPickerDialog.h
dialogs/LoginDialog.cpp
dialogs/LoginDialog.h
dialogs/ModEditDialogCommon.cpp
dialogs/ModEditDialogCommon.h
dialogs/NewComponentDialog.cpp
dialogs/NewComponentDialog.h
dialogs/NewInstanceDialog.cpp
dialogs/NewInstanceDialog.h
dialogs/NotificationDialog.cpp
dialogs/NotificationDialog.h
pagedialog/PageDialog.cpp
pagedialog/PageDialog.h
dialogs/ProgressDialog.cpp
dialogs/ProgressDialog.h
dialogs/UpdateDialog.cpp
dialogs/UpdateDialog.h
dialogs/VersionSelectDialog.cpp
dialogs/VersionSelectDialog.h
dialogs/SkinUploadDialog.cpp
dialogs/SkinUploadDialog.h
# GUI - widgets
widgets/Common.cpp
widgets/Common.h
widgets/CustomCommands.cpp
widgets/CustomCommands.h
widgets/FocusLineEdit.cpp
widgets/FocusLineEdit.h
widgets/IconLabel.cpp
widgets/IconLabel.h
widgets/JavaSettingsWidget.cpp
widgets/JavaSettingsWidget.h
widgets/LabeledToolButton.cpp
widgets/LabeledToolButton.h
widgets/LanguageSelectionWidget.cpp
widgets/LanguageSelectionWidget.h
widgets/LineSeparator.cpp
widgets/LineSeparator.h
widgets/LogView.cpp
widgets/LogView.h
widgets/MCModInfoFrame.cpp
widgets/MCModInfoFrame.h
widgets/ModListView.cpp
widgets/ModListView.h
widgets/PageContainer.cpp
widgets/PageContainer.h
widgets/PageContainer_p.h
widgets/ServerStatus.cpp
widgets/ServerStatus.h
widgets/VersionListView.cpp
widgets/VersionListView.h
widgets/VersionSelectWidget.cpp
widgets/VersionSelectWidget.h
widgets/ProgressWidget.h
widgets/ProgressWidget.cpp
# GUI - instance group view
groupview/GroupedProxyModel.cpp
groupview/GroupedProxyModel.h
groupview/GroupView.cpp
groupview/GroupView.h
groupview/InstanceDelegate.cpp
groupview/InstanceDelegate.h
groupview/VisualGroup.cpp
groupview/VisualGroup.h
)
######## UIs ########
SET(MULTIMC_UIS
# Instance pages
pages/instance/GameOptionsPage.ui
pages/instance/VersionPage.ui
pages/instance/ModFolderPage.ui
pages/instance/LogPage.ui
pages/instance/InstanceSettingsPage.ui
pages/instance/NotesPage.ui
pages/instance/ScreenshotsPage.ui
pages/instance/OtherLogsPage.ui
pages/instance/LegacyUpgradePage.ui
pages/instance/ServersPage.ui
pages/instance/WorldListPage.ui
# Global settings pages
pages/global/AccountListPage.ui
pages/global/ExternalToolsPage.ui
pages/global/JavaPage.ui
pages/global/MinecraftPage.ui
pages/global/MultiMCPage.ui
pages/global/ProxyPage.ui
pages/global/PasteEEPage.ui
pages/global/PackagesPage.ui
# Platform pages
pages/modplatform/VanillaPage.ui
pages/modplatform/FTBPage.ui
pages/modplatform/TwitchPage.ui
pages/modplatform/TechnicPage.ui
pages/modplatform/ImportPage.ui
# Dialogs
dialogs/CopyInstanceDialog.ui
dialogs/NewComponentDialog.ui
dialogs/NewInstanceDialog.ui
dialogs/AboutDialog.ui
dialogs/ProgressDialog.ui
dialogs/IconPickerDialog.ui
dialogs/ProfileSelectDialog.ui
dialogs/EditAccountDialog.ui
dialogs/ExportInstanceDialog.ui
dialogs/LoginDialog.ui
dialogs/UpdateDialog.ui
dialogs/NotificationDialog.ui
dialogs/SkinUploadDialog.ui
# Widgets/other
widgets/CustomCommands.ui
widgets/MCModInfoFrame.ui
)
set(MULTIMC_QRCS
resources/assets/assets.qrc
resources/backgrounds/backgrounds.qrc
resources/multimc/multimc.qrc
resources/pe_dark/pe_dark.qrc
resources/pe_light/pe_light.qrc
resources/pe_colored/pe_colored.qrc
resources/pe_blue/pe_blue.qrc
resources/OSX/OSX.qrc
resources/iOS/iOS.qrc
resources/flat/flat.qrc
resources/documents/documents.qrc
)
######## Windows resource files ########
if(WIN32)
set(MULTIMC_RCS resources/multimc.rc)
endif()
# Qt 5 stuff
qt5_wrap_ui(MULTIMC_UI ${MULTIMC_UIS})
qt5_add_resources(MULTIMC_RESOURCES ${MULTIMC_QRCS})
# Add executable
add_executable(MultiMC MACOSX_BUNDLE WIN32 ${MULTIMC_SOURCES} ${MULTIMC_UI} ${MULTIMC_RESOURCES} ${MULTIMC_RCS})
target_link_libraries(MultiMC MultiMC_gui ${QUAZIP_LIBRARIES} hoedown MultiMC_rainbow LocalPeer ganalytics)
if(DEFINED MultiMC_APP_BINARY_NAME)
set_target_properties(MultiMC PROPERTIES OUTPUT_NAME "${MultiMC_APP_BINARY_NAME}")
endif()
if(DEFINED MultiMC_BINARY_RPATH)
SET_TARGET_PROPERTIES(MultiMC PROPERTIES INSTALL_RPATH "${MultiMC_BINARY_RPATH}")
endif()
if(DEFINED MultiMC_APP_BINARY_DEFS)
target_compile_definitions(MultiMC PRIVATE ${MultiMC_APP_BINARY_DEFS})
endif()
install(TARGETS MultiMC
BUNDLE DESTINATION ${BUNDLE_DEST_DIR} COMPONENT Runtime
LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime
RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime
)
#### The MultiMC bundle mess! ####
# Bundle utilities are used to complete the portable packages - they add all the libraries that would otherwise be missing on the target system.
# NOTE: it seems that this absolutely has to be here, and nowhere else.
if(INSTALL_BUNDLE STREQUAL "full")
# Add qt.conf - this makes Qt stop looking for things outside the bundle
install(
CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${RESOURCES_DEST_DIR}/qt.conf\" \" \")"
COMPONENT Runtime
)
# Bundle plugins
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
# Image formats
install(
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "tga|tiff|mng|webp" EXCLUDE
)
# Icon engines
install(
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "fontawesome" EXCLUDE
)
# Platform plugins
install(
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "minimal|linuxfb|offscreen" EXCLUDE
)
else()
# Image formats
install(
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "tga|tiff|mng|webp" EXCLUDE
REGEX "d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
REGEX "\\.dSYM" EXCLUDE
)
# Icon engines
install(
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "fontawesome" EXCLUDE
REGEX "d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
REGEX "\\.dSYM" EXCLUDE
)
# Platform plugins
install(
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "minimal|linuxfb|offscreen" EXCLUDE
REGEX "d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
REGEX "\\.dSYM" EXCLUDE
)
endif()
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake"
@ONLY
)
install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" COMPONENT Runtime)
endif()

View File

@@ -1,199 +0,0 @@
/*
* Copyright 2011 Aurélien Gâteau <agateau@kde.org>
* License: BSD-3-Clause
*/
#include <ColumnResizer.h>
#include <QDebug>
#include <QEvent>
#include <QFormLayout>
#include <QGridLayout>
#include <QTimer>
#include <QWidget>
class FormLayoutWidgetItem : public QWidgetItem
{
public:
FormLayoutWidgetItem(QWidget* widget, QFormLayout* formLayout, QFormLayout::ItemRole itemRole)
: QWidgetItem(widget)
, m_width(-1)
, m_formLayout(formLayout)
, m_itemRole(itemRole)
{}
QSize sizeHint() const
{
QSize size = QWidgetItem::sizeHint();
if (m_width != -1) {
size.setWidth(m_width);
}
return size;
}
QSize minimumSize() const
{
QSize size = QWidgetItem::minimumSize();
if (m_width != -1) {
size.setWidth(m_width);
}
return size;
}
QSize maximumSize() const
{
QSize size = QWidgetItem::maximumSize();
if (m_width != -1) {
size.setWidth(m_width);
}
return size;
}
void setWidth(int width)
{
if (width != m_width) {
m_width = width;
invalidate();
}
}
void setGeometry(const QRect& _rect)
{
QRect rect = _rect;
int width = widget()->sizeHint().width();
if (m_itemRole == QFormLayout::LabelRole && m_formLayout->labelAlignment() & Qt::AlignRight) {
rect.setLeft(rect.right() - width);
}
QWidgetItem::setGeometry(rect);
}
QFormLayout* formLayout() const
{
return m_formLayout;
}
private:
int m_width;
QFormLayout* m_formLayout;
QFormLayout::ItemRole m_itemRole;
};
typedef QPair<QGridLayout*, int> GridColumnInfo;
class ColumnResizerPrivate
{
public:
ColumnResizerPrivate(ColumnResizer* q_ptr)
: q(q_ptr)
, m_updateTimer(new QTimer(q))
{
m_updateTimer->setSingleShot(true);
m_updateTimer->setInterval(0);
QObject::connect(m_updateTimer, SIGNAL(timeout()), q, SLOT(updateWidth()));
}
void scheduleWidthUpdate()
{
m_updateTimer->start();
}
ColumnResizer* q;
QTimer* m_updateTimer;
QList<QWidget*> m_widgets;
QList<FormLayoutWidgetItem*> m_wrWidgetItemList;
QList<GridColumnInfo> m_gridColumnInfoList;
};
ColumnResizer::ColumnResizer(QObject* parent)
: QObject(parent)
, d(new ColumnResizerPrivate(this))
{}
ColumnResizer::~ColumnResizer()
{
delete d;
}
void ColumnResizer::addWidget(QWidget* widget)
{
d->m_widgets.append(widget);
widget->installEventFilter(this);
d->scheduleWidthUpdate();
}
void ColumnResizer::updateWidth()
{
int width = 0;
Q_FOREACH(QWidget* widget, d->m_widgets) {
width = qMax(widget->sizeHint().width(), width);
}
Q_FOREACH(FormLayoutWidgetItem* item, d->m_wrWidgetItemList) {
item->setWidth(width);
item->formLayout()->update();
}
Q_FOREACH(GridColumnInfo info, d->m_gridColumnInfoList) {
info.first->setColumnMinimumWidth(info.second, width);
}
}
bool ColumnResizer::eventFilter(QObject*, QEvent* event)
{
if (event->type() == QEvent::Resize) {
d->scheduleWidthUpdate();
}
return false;
}
void ColumnResizer::addWidgetsFromLayout(QLayout* layout, int column)
{
Q_ASSERT(column >= 0);
QGridLayout* gridLayout = qobject_cast<QGridLayout*>(layout);
QFormLayout* formLayout = qobject_cast<QFormLayout*>(layout);
if (gridLayout) {
addWidgetsFromGridLayout(gridLayout, column);
} else if (formLayout) {
if (column > QFormLayout::SpanningRole) {
qCritical() << "column should not be more than" << QFormLayout::SpanningRole << "for QFormLayout";
return;
}
QFormLayout::ItemRole role = static_cast<QFormLayout::ItemRole>(column);
addWidgetsFromFormLayout(formLayout, role);
} else {
qCritical() << "Don't know how to handle layout" << layout;
}
}
void ColumnResizer::addWidgetsFromGridLayout(QGridLayout* layout, int column)
{
for (int row = 0; row < layout->rowCount(); ++row) {
QLayoutItem* item = layout->itemAtPosition(row, column);
if (!item) {
continue;
}
QWidget* widget = item->widget();
if (!widget) {
continue;
}
addWidget(widget);
}
d->m_gridColumnInfoList << GridColumnInfo(layout, column);
}
void ColumnResizer::addWidgetsFromFormLayout(QFormLayout* layout, QFormLayout::ItemRole role)
{
for (int row = 0; row < layout->rowCount(); ++row) {
QLayoutItem* item = layout->itemAt(row, role);
if (!item) {
continue;
}
QWidget* widget = item->widget();
if (!widget) {
continue;
}
layout->removeItem(item);
delete item;
FormLayoutWidgetItem* newItem = new FormLayoutWidgetItem(widget, layout, role);
layout->setItem(row, role, newItem);
addWidget(widget);
d->m_wrWidgetItemList << newItem;
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright 2011 Aurélien Gâteau <agateau@kde.org>
* License: BSD-3-Clause
*/
#ifndef COLUMNRESIZER_H
#define COLUMNRESIZER_H
#include <QFormLayout>
#include <QtCore/QObject>
#include <QtCore/QList>
class QEvent;
class QGridLayout;
class QLayout;
class QWidget;
class ColumnResizerPrivate;
class ColumnResizer : public QObject
{
Q_OBJECT
public:
ColumnResizer(QObject* parent = 0);
~ColumnResizer();
void addWidget(QWidget* widget);
void addWidgetsFromLayout(QLayout*, int column);
void addWidgetsFromGridLayout(QGridLayout*, int column);
void addWidgetsFromFormLayout(QFormLayout*, QFormLayout::ItemRole role);
private Q_SLOTS:
void updateWidth();
protected:
bool eventFilter(QObject*, QEvent* event);
private:
ColumnResizerPrivate* const d;
};
#endif /* COLUMNRESIZER_H */

View File

@@ -1,34 +0,0 @@
#include "InstanceProxyModel.h"
#include "MultiMC.h"
#include <BaseInstance.h>
#include <icons/IconList.h>
InstanceProxyModel::InstanceProxyModel(QObject *parent) : GroupedProxyModel(parent)
{
}
QVariant InstanceProxyModel::data(const QModelIndex & index, int role) const
{
QVariant data = QSortFilterProxyModel::data(index, role);
if(role == Qt::DecorationRole)
{
return QVariant(MMC->icons()->getIcon(data.toString()));
}
return data;
}
bool InstanceProxyModel::subSortLessThan(const QModelIndex &left,
const QModelIndex &right) const
{
BaseInstance *pdataLeft = static_cast<BaseInstance *>(left.internalPointer());
BaseInstance *pdataRight = static_cast<BaseInstance *>(right.internalPointer());
QString sortMode = MMC->settings()->get("InstSortMode").toString();
if (sortMode == "LastLaunch")
{
return pdataLeft->lastLaunch() > pdataRight->lastLaunch();
}
else
{
return QString::localeAwareCompare(pdataLeft->name(), pdataRight->name()) < 0;
}
}

View File

@@ -1,16 +0,0 @@
#pragma once
#include "groupview/GroupedProxyModel.h"
/**
* A proxy model that is responsible for sorting instances into groups
*/
class InstanceProxyModel : public GroupedProxyModel
{
public:
explicit InstanceProxyModel(QObject *parent = 0);
QVariant data(const QModelIndex & index, int role) const override;
protected:
virtual bool subSortLessThan(const QModelIndex &left, const QModelIndex &right) const override;
};

View File

@@ -1,312 +0,0 @@
#include "LaunchController.h"
#include "MainWindow.h"
#include <minecraft/auth/MojangAccountList.h>
#include "MultiMC.h"
#include "dialogs/CustomMessageBox.h"
#include "dialogs/ProfileSelectDialog.h"
#include "dialogs/ProgressDialog.h"
#include "dialogs/EditAccountDialog.h"
#include "InstanceWindow.h"
#include "BuildConfig.h"
#include "JavaCommon.h"
#include <QLineEdit>
#include <QInputDialog>
#include <tasks/Task.h>
#include <minecraft/auth/YggdrasilTask.h>
#include <launch/steps/TextPrint.h>
#include <QStringList>
LaunchController::LaunchController(QObject *parent) : Task(parent)
{
}
void LaunchController::executeTask()
{
if (!m_instance)
{
emitFailed(tr("No instance specified"));
return;
}
login();
}
// FIXME: minecraft specific
void LaunchController::login()
{
JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget);
// Find an account to use.
std::shared_ptr<MojangAccountList> accounts = MMC->accounts();
MojangAccountPtr account = accounts->activeAccount();
if (accounts->count() <= 0)
{
// Tell the user they need to log in at least one account in order to play.
auto reply = CustomMessageBox::selectable(
m_parentWidget, tr("No Accounts"),
tr("In order to play Minecraft, you must have at least one Mojang or Minecraft "
"account logged in to MultiMC."
"Would you like to open the account manager to add an account now?"),
QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)->exec();
if (reply == QMessageBox::Yes)
{
// Open the account manager.
MMC->ShowGlobalSettings(m_parentWidget, "accounts");
}
}
else if (account.get() == nullptr)
{
// If no default account is set, ask the user which one to use.
ProfileSelectDialog selectDialog(tr("Which profile would you like to use?"),
ProfileSelectDialog::GlobalDefaultCheckbox, m_parentWidget);
selectDialog.exec();
// Launch the instance with the selected account.
account = selectDialog.selectedAccount();
// If the user said to use the account as default, do that.
if (selectDialog.useAsGlobalDefault() && account.get() != nullptr)
accounts->setActiveAccount(account->username());
}
// if no account is selected, we bail
if (!account.get())
{
emitFailed(tr("No account selected for launch"));
return;
}
// we try empty password first :)
QString password;
// we loop until the user succeeds in logging in or gives up
bool tryagain = true;
// the failure. the default failure.
const QString needLoginAgain = tr("Your account is currently not logged in. Please enter "
"your password to log in again.");
QString failReason = needLoginAgain;
while (tryagain)
{
m_session = std::make_shared<AuthSession>();
m_session->wants_online = m_online;
auto task = account->login(m_session, password);
if (task)
{
// We'll need to validate the access token to make sure the account
// is still logged in.
ProgressDialog progDialog(m_parentWidget);
if (m_online)
{
progDialog.setSkipButton(true, tr("Play Offline"));
}
progDialog.execWithTask(task.get());
if (!task->wasSuccessful())
{
auto failReasonNew = task->failReason();
if(failReasonNew == "Invalid token.")
{
account->invalidateClientToken();
failReason = needLoginAgain;
}
else failReason = failReasonNew;
}
}
switch (m_session->status)
{
case AuthSession::Undetermined:
{
qCritical() << "Received undetermined session status during login. Bye.";
tryagain = false;
emitFailed(tr("Received undetermined session status during login."));
break;
}
case AuthSession::RequiresPassword:
{
EditAccountDialog passDialog(failReason, m_parentWidget, EditAccountDialog::PasswordField);
auto username = m_session->username;
auto chopN = [](QString toChop, int N) -> QString
{
if(toChop.size() > N)
{
auto left = toChop.left(N);
left += QString("\u25CF").repeated(toChop.size() - N);
return left;
}
return toChop;
};
if(username.contains('@'))
{
auto parts = username.split('@');
auto mailbox = chopN(parts[0],3);
QString domain = chopN(parts[1], 3);
username = mailbox + '@' + domain;
}
passDialog.setUsername(username);
if (passDialog.exec() == QDialog::Accepted)
{
password = passDialog.password();
}
else
{
tryagain = false;
}
break;
}
case AuthSession::PlayableOffline:
{
// we ask the user for a player name
bool ok = false;
QString usedname = m_session->player_name;
QString name = QInputDialog::getText(m_parentWidget, tr("Player name"),
tr("Choose your offline mode player name."),
QLineEdit::Normal, m_session->player_name, &ok);
if (!ok)
{
tryagain = false;
break;
}
if (name.length())
{
usedname = name;
}
m_session->MakeOffline(usedname);
// offline flavored game from here :3
}
case AuthSession::PlayableOnline:
{
launchInstance();
tryagain = false;
return;
}
}
}
emitFailed(tr("Failed to launch."));
}
void LaunchController::launchInstance()
{
Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL");
Q_ASSERT_X(m_session.get() != nullptr, "launchInstance", "session is NULL");
if(!m_instance->reloadSettings())
{
QMessageBox::critical(m_parentWidget, tr("Error"), tr("Couldn't load the instance profile."));
emitFailed(tr("Couldn't load the instance profile."));
return;
}
m_launcher = m_instance->createLaunchTask(m_session);
if (!m_launcher)
{
emitFailed(tr("Couldn't instantiate a launcher."));
return;
}
auto console = qobject_cast<InstanceWindow *>(m_parentWidget);
auto showConsole = m_instance->settings()->get("ShowConsole").toBool();
if(!console && showConsole)
{
MMC->showInstanceWindow(m_instance);
}
connect(m_launcher.get(), &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch);
connect(m_launcher.get(), &LaunchTask::succeeded, this, &LaunchController::onSucceeded);
connect(m_launcher.get(), &LaunchTask::failed, this, &LaunchController::onFailed);
connect(m_launcher.get(), &LaunchTask::requestProgress, this, &LaunchController::onProgressRequested);
m_launcher->prependStep(new TextPrint(m_launcher.get(), "MultiMC version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::MultiMC));
m_launcher->start();
}
void LaunchController::readyForLaunch()
{
if (!m_profiler)
{
m_launcher->proceed();
return;
}
QString error;
if (!m_profiler->check(&error))
{
m_launcher->abort();
QMessageBox::critical(m_parentWidget, tr("Error"), tr("Couldn't start profiler: %1").arg(error));
emitFailed("Profiler startup failed");
return;
}
BaseProfiler *profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this);
connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString & message)
{
QMessageBox msg;
msg.setText(tr("The game launch is delayed until you press the "
"button. This is the right time to setup the profiler, as the "
"profiler server is running now.\n\n%1").arg(message));
msg.setWindowTitle(tr("Waiting"));
msg.setIcon(QMessageBox::Information);
msg.addButton(tr("Launch"), QMessageBox::AcceptRole);
msg.setModal(true);
msg.exec();
m_launcher->proceed();
});
connect(profilerInstance, &BaseProfiler::abortLaunch, [this](const QString & message)
{
QMessageBox msg;
msg.setText(tr("Couldn't start the profiler: %1").arg(message));
msg.setWindowTitle(tr("Error"));
msg.setIcon(QMessageBox::Critical);
msg.addButton(QMessageBox::Ok);
msg.setModal(true);
msg.exec();
m_launcher->abort();
emitFailed("Profiler startup failed");
});
profilerInstance->beginProfiling(m_launcher);
}
void LaunchController::onSucceeded()
{
emitSucceeded();
}
void LaunchController::onFailed(QString reason)
{
if(m_instance->settings()->get("ShowConsoleOnError").toBool())
{
MMC->showInstanceWindow(m_instance, "console");
}
emitFailed(reason);
}
void LaunchController::onProgressRequested(Task* task)
{
ProgressDialog progDialog(m_parentWidget);
progDialog.setSkipButton(true, tr("Abort"));
m_launcher->proceed();
progDialog.execWithTask(task);
}
bool LaunchController::abort()
{
if(!m_launcher)
{
return true;
}
if(!m_launcher->canAbort())
{
return false;
}
auto response = CustomMessageBox::selectable(
m_parentWidget, tr("Kill Minecraft?"),
tr("This can cause the instance to get corrupted and should only be used if Minecraft "
"is frozen for some reason"),
QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec();
if (response == QMessageBox::Yes)
{
return m_launcher->abort();
}
return false;
}

View File

@@ -1,40 +0,0 @@
#include "ModEditDialogCommon.h"
#include "CustomMessageBox.h"
#include <QUrl>
bool lastfirst(QModelIndexList &list, int &first, int &last)
{
if (list.isEmpty())
return false;
first = last = list[0].row();
for (auto item : list)
{
int row = item.row();
if (row < first)
first = row;
if (row > last)
last = row;
}
return true;
}
void showWebsiteForMod(QWidget *parentDlg, Mod &m)
{
QString url = m.homeurl();
if (url.size())
{
// catch the cases where the protocol is missing
if (!url.startsWith("http"))
{
url = "http://" + url;
}
DesktopServices::openUrl(url);
}
else
{
CustomMessageBox::selectable(
parentDlg, QObject::tr("How sad!"),
QObject::tr("The mod author didn't provide a website link for this mod."),
QMessageBox::Warning);
}
}

View File

@@ -1,9 +0,0 @@
#pragma once
#include <QModelIndex>
#include <DesktopServices.h>
#include <QWidget>
#include <minecraft/Mod.h>
bool lastfirst(QModelIndexList &list, int &first, int &last);
void showWebsiteForMod(QWidget *parentDlg, Mod &m);

View File

@@ -1,85 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SkinUploadDialog</class>
<widget class="QDialog" name="SkinUploadDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>413</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Skin Upload</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="fileBox">
<property name="title">
<string>Skin File</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="skinPathTextBox"/>
</item>
<item>
<widget class="QPushButton" name="skinBrowseBtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>28</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="modelBox">
<property name="title">
<string>Player Model</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_1">
<item>
<widget class="QRadioButton" name="steveBtn">
<property name="text">
<string>Steve Model</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="alexBtn">
<property name="text">
<string>Alex Model</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,48 +0,0 @@
/* Copyright 2013-2019 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 "GroupedProxyModel.h"
#include "GroupView.h"
#include <QDebug>
GroupedProxyModel::GroupedProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
{
}
bool GroupedProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
const QString leftCategory = left.data(GroupViewRoles::GroupRole).toString();
const QString rightCategory = right.data(GroupViewRoles::GroupRole).toString();
if (leftCategory == rightCategory)
{
return subSortLessThan(left, right);
}
else
{
// FIXME: real group sorting happens in GroupView::updateGeometries(), see LocaleString
auto result = leftCategory.localeAwareCompare(rightCategory);
if(result == 0)
{
return subSortLessThan(left, right);
}
return result < 0;
}
}
bool GroupedProxyModel::subSortLessThan(const QModelIndex &left, const QModelIndex &right) const
{
return left.row() < right.row();
}

View File

@@ -1,11 +0,0 @@
[Desktop Entry]
Version=1.0
Name=MultiMC
GenericName=MultiMC
Comment=Free, open source launcher and instance manager for Minecraft.
Type=Application
Terminal=false
Exec=multimc
Icon=multimc
Categories=Game
Keywords=game;minecraft;

View File

@@ -1,12 +0,0 @@
# What is this?
A simple ubuntu package for MultiMC that wraps the contains a script that downloads and installs real MultiMC on ubuntu based systems.
It contains a `.desktop` file, an icon, and a simple script that does the heavy lifting.
# How to build this?
You need dpkg utils. Rename the `multimc` folder to `multimc_1.3-1` and then run:
```
fakeroot dpkg-deb --build multimc_1.3-1
```
Replace the version with whatever is appropriate.

View File

@@ -1,153 +0,0 @@
/* Copyright 2013-2019 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 "AccountListPage.h"
#include "ui_AccountListPage.h"
#include <QItemSelectionModel>
#include <QDebug>
#include "net/NetJob.h"
#include "net/URLConstants.h"
#include "Env.h"
#include "dialogs/ProgressDialog.h"
#include "dialogs/LoginDialog.h"
#include "dialogs/CustomMessageBox.h"
#include "dialogs/SkinUploadDialog.h"
#include "tasks/Task.h"
#include "minecraft/auth/YggdrasilTask.h"
#include "MultiMC.h"
AccountListPage::AccountListPage(QWidget *parent)
: QWidget(parent), ui(new Ui::AccountListPage)
{
ui->setupUi(this);
ui->tabWidget->tabBar()->hide();
m_accounts = MMC->accounts();
ui->listView->setModel(m_accounts.get());
ui->listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
// Expand the account column
ui->listView->header()->setSectionResizeMode(1, QHeaderView::Stretch);
QItemSelectionModel *selectionModel = ui->listView->selectionModel();
connect(selectionModel, &QItemSelectionModel::selectionChanged,
[this](const QItemSelection &sel, const QItemSelection &dsel)
{ updateButtonStates(); });
connect(m_accounts.get(), SIGNAL(listChanged()), SLOT(listChanged()));
connect(m_accounts.get(), SIGNAL(activeAccountChanged()), SLOT(listChanged()));
updateButtonStates();
}
AccountListPage::~AccountListPage()
{
delete ui;
}
void AccountListPage::listChanged()
{
updateButtonStates();
}
void AccountListPage::on_addAccountBtn_clicked()
{
addAccount(tr("Please enter your Mojang or Minecraft account username and password to add "
"your account."));
}
void AccountListPage::on_rmAccountBtn_clicked()
{
QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes();
if (selection.size() > 0)
{
QModelIndex selected = selection.first();
m_accounts->removeAccount(selected);
}
}
void AccountListPage::on_setDefaultBtn_clicked()
{
QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes();
if (selection.size() > 0)
{
QModelIndex selected = selection.first();
MojangAccountPtr account =
selected.data(MojangAccountList::PointerRole).value<MojangAccountPtr>();
m_accounts->setActiveAccount(account->username());
}
}
void AccountListPage::on_noDefaultBtn_clicked()
{
m_accounts->setActiveAccount("");
}
void AccountListPage::updateButtonStates()
{
// If there is no selection, disable buttons that require something selected.
QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes();
ui->rmAccountBtn->setEnabled(selection.size() > 0);
ui->setDefaultBtn->setEnabled(selection.size() > 0);
ui->uploadSkinBtn->setEnabled(selection.size() > 0);
ui->noDefaultBtn->setDown(m_accounts->activeAccount().get() == nullptr);
}
void AccountListPage::addAccount(const QString &errMsg)
{
// TODO: The login dialog isn't quite done yet
MojangAccountPtr account = LoginDialog::newAccount(this, errMsg);
if (account != nullptr)
{
m_accounts->addAccount(account);
if (m_accounts->count() == 1)
m_accounts->setActiveAccount(account->username());
// Grab associated player skins
auto job = new NetJob("Player skins: " + account->username());
for (AccountProfile profile : account->profiles())
{
auto meta = Env::getInstance().metacache()->resolveEntry("skins", profile.id + ".png");
auto action = Net::Download::makeCached(QUrl(URLConstants::SKINS_BASE + profile.id + ".png"), meta);
job->addNetAction(action);
meta->setStale(true);
}
job->start();
}
}
void AccountListPage::on_uploadSkinBtn_clicked()
{
QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes();
if (selection.size() > 0)
{
QModelIndex selected = selection.first();
MojangAccountPtr account = selected.data(MojangAccountList::PointerRole).value<MojangAccountPtr>();
SkinUploadDialog dialog(account, this);
dialog.exec();
}
}

View File

@@ -1,122 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AccountListPage</class>
<widget class="QWidget" name="AccountListPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>694</width>
<height>609</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string notr="true">Tab 1</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="welcomeLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Welcome! If you're new here, you can click the &amp;quot;Add&amp;quot; button to add your Mojang or Minecraft account.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTreeView" name="listView"/>
</item>
<item>
<layout class="QVBoxLayout" name="manageAcctsBtnBox">
<item>
<widget class="QPushButton" name="addAccountBtn">
<property name="text">
<string>&amp;Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="rmAccountBtn">
<property name="text">
<string>&amp;Remove</string>
</property>
</widget>
</item>
<item>
<spacer name="buttonSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="setDefaultBtn">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set the currently selected account as the active account. The active account is the account that is used to log in (unless it is overridden in an instance-specific setting).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>&amp;Set Default</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="noDefaultBtn">
<property name="toolTip">
<string>Set no default account. This will cause MultiMC to prompt you to select an account every time you launch an instance that doesn't have its own default set.</string>
</property>
<property name="text">
<string>&amp;No Default</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="uploadSkinBtn">
<property name="toolTip">
<string>Opens a dialog to select and upload a skin image to the selected account.</string>
</property>
<property name="text">
<string>&amp;Upload Skin</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,224 +0,0 @@
/* Copyright 2015-2019 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 "PackagesPage.h"
#include "ui_PackagesPage.h"
#include <QDateTime>
#include <QSortFilterProxyModel>
#include <QRegularExpression>
#include "dialogs/ProgressDialog.h"
#include "VersionProxyModel.h"
#include "meta/Index.h"
#include "meta/VersionList.h"
#include "meta/Version.h"
#include "Env.h"
#include "MultiMC.h"
using namespace Meta;
static QString formatRequires(const VersionPtr &version)
{
QStringList lines;
auto & reqs = version->requires();
auto iter = reqs.begin();
while (iter != reqs.end())
{
auto &uid = iter->uid;
auto &version = iter->equalsVersion;
const QString readable = ENV.metadataIndex()->hasUid(uid) ? ENV.metadataIndex()->get(uid)->humanReadable() : uid;
if(!version.isEmpty())
{
lines.append(QString("%1 (%2)").arg(readable, version));
}
else
{
lines.append(QString("%1").arg(readable));
}
iter++;
}
return lines.join('\n');
}
PackagesPage::PackagesPage(QWidget *parent) :
QWidget(parent),
ui(new Ui::PackagesPage)
{
ui->setupUi(this);
ui->tabWidget->tabBar()->hide();
m_fileProxy = new QSortFilterProxyModel(this);
m_fileProxy->setSortRole(Qt::DisplayRole);
m_fileProxy->setSortCaseSensitivity(Qt::CaseInsensitive);
m_fileProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
m_fileProxy->setFilterRole(Qt::DisplayRole);
m_fileProxy->setFilterKeyColumn(0);
m_fileProxy->sort(0);
m_fileProxy->setSourceModel(ENV.metadataIndex().get());
ui->indexView->setModel(m_fileProxy);
m_filterProxy = new QSortFilterProxyModel(this);
m_filterProxy->setSortRole(VersionList::SortRole);
m_filterProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
m_filterProxy->setFilterRole(Qt::DisplayRole);
m_filterProxy->setFilterKeyColumn(0);
m_filterProxy->sort(0, Qt::DescendingOrder);
ui->versionsView->setModel(m_filterProxy);
m_versionProxy = new VersionProxyModel(this);
m_filterProxy->setSourceModel(m_versionProxy);
connect(ui->indexView->selectionModel(), &QItemSelectionModel::currentChanged, this, &PackagesPage::updateCurrentVersionList);
connect(ui->versionsView->selectionModel(), &QItemSelectionModel::currentChanged, this, &PackagesPage::updateVersion);
connect(m_filterProxy, &QSortFilterProxyModel::dataChanged, this, &PackagesPage::versionListDataChanged);
updateCurrentVersionList(QModelIndex());
updateVersion();
}
PackagesPage::~PackagesPage()
{
delete ui;
}
QIcon PackagesPage::icon() const
{
return MMC->getThemedIcon("packages");
}
void PackagesPage::on_refreshIndexBtn_clicked()
{
ENV.metadataIndex()->load(Net::Mode::Online);
}
void PackagesPage::on_refreshFileBtn_clicked()
{
VersionListPtr list = ui->indexView->currentIndex().data(Index::ListPtrRole).value<VersionListPtr>();
if (!list)
{
return;
}
list->load(Net::Mode::Online);
}
void PackagesPage::on_refreshVersionBtn_clicked()
{
VersionPtr version = ui->versionsView->currentIndex().data(VersionList::VersionPtrRole).value<VersionPtr>();
if (!version)
{
return;
}
version->load(Net::Mode::Online);
}
void PackagesPage::on_fileSearchEdit_textChanged(const QString &search)
{
if (search.isEmpty())
{
m_fileProxy->setFilterFixedString(QString());
}
else
{
QStringList parts = search.split(' ');
std::transform(parts.begin(), parts.end(), parts.begin(), &QRegularExpression::escape);
m_fileProxy->setFilterRegExp(".*" + parts.join(".*") + ".*");
}
}
void PackagesPage::on_versionSearchEdit_textChanged(const QString &search)
{
if (search.isEmpty())
{
m_filterProxy->setFilterFixedString(QString());
}
else
{
QStringList parts = search.split(' ');
std::transform(parts.begin(), parts.end(), parts.begin(), &QRegularExpression::escape);
m_filterProxy->setFilterRegExp(".*" + parts.join(".*") + ".*");
}
}
void PackagesPage::updateCurrentVersionList(const QModelIndex &index)
{
if (index.isValid())
{
VersionListPtr list = index.data(Index::ListPtrRole).value<VersionListPtr>();
ui->versionsBox->setEnabled(true);
ui->refreshFileBtn->setEnabled(true);
ui->fileUidLabel->setEnabled(true);
ui->fileUid->setText(list->uid());
ui->fileNameLabel->setEnabled(true);
ui->fileName->setText(list->name());
m_versionProxy->setSourceModel(list.get());
ui->refreshFileBtn->setText(tr("Refresh %1").arg(list->humanReadable()));
list->load(Net::Mode::Offline);
}
else
{
ui->versionsBox->setEnabled(false);
ui->refreshFileBtn->setEnabled(false);
ui->fileUidLabel->setEnabled(false);
ui->fileUid->clear();
ui->fileNameLabel->setEnabled(false);
ui->fileName->clear();
m_versionProxy->setSourceModel(nullptr);
ui->refreshFileBtn->setText(tr("Refresh"));
}
}
void PackagesPage::versionListDataChanged(const QModelIndex &tl, const QModelIndex &br)
{
if (QItemSelection(tl, br).contains(ui->versionsView->currentIndex()))
{
updateVersion();
}
}
void PackagesPage::updateVersion()
{
VersionPtr version = std::dynamic_pointer_cast<Version>(
ui->versionsView->currentIndex().data(VersionList::VersionPointerRole).value<BaseVersionPtr>());
if (version)
{
ui->refreshVersionBtn->setEnabled(true);
ui->versionVersionLabel->setEnabled(true);
ui->versionVersion->setText(version->version());
ui->versionTimeLabel->setEnabled(true);
ui->versionTime->setText(version->time().toString("yyyy-MM-dd HH:mm"));
ui->versionTypeLabel->setEnabled(true);
ui->versionType->setText(version->type());
ui->versionRequiresLabel->setEnabled(true);
ui->versionRequires->setText(formatRequires(version));
ui->refreshVersionBtn->setText(tr("Refresh %1").arg(version->version()));
}
else
{
ui->refreshVersionBtn->setEnabled(false);
ui->versionVersionLabel->setEnabled(false);
ui->versionVersion->clear();
ui->versionTimeLabel->setEnabled(false);
ui->versionTime->clear();
ui->versionTypeLabel->setEnabled(false);
ui->versionType->clear();
ui->versionRequiresLabel->setEnabled(false);
ui->versionRequires->clear();
ui->refreshVersionBtn->setText(tr("Refresh"));
}
}
void PackagesPage::openedImpl()
{
ENV.metadataIndex()->load(Net::Mode::Offline);
}

View File

@@ -1,57 +0,0 @@
/* Copyright 2015-2019 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 <QWidget>
#include "pages/BasePage.h"
namespace Ui {
class PackagesPage;
}
class QSortFilterProxyModel;
class VersionProxyModel;
class PackagesPage : public QWidget, public BasePage
{
Q_OBJECT
public:
explicit PackagesPage(QWidget *parent = 0);
~PackagesPage();
QString id() const override { return "packages-global"; }
QString displayName() const override { return tr("Packages"); }
QIcon icon() const override;
void openedImpl() override;
private slots:
void on_refreshIndexBtn_clicked();
void on_refreshFileBtn_clicked();
void on_refreshVersionBtn_clicked();
void on_fileSearchEdit_textChanged(const QString &search);
void on_versionSearchEdit_textChanged(const QString &search);
void updateCurrentVersionList(const QModelIndex &index);
void versionListDataChanged(const QModelIndex &tl, const QModelIndex &br);
private:
Ui::PackagesPage *ui;
QSortFilterProxyModel *m_fileProxy;
QSortFilterProxyModel *m_filterProxy;
VersionProxyModel *m_versionProxy;
void updateVersion();
};

View File

@@ -1,252 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PackagesPage</class>
<widget class="QWidget" name="PackagesPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>636</width>
<height>621</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="2">
<widget class="QGroupBox" name="versionsBox">
<property name="title">
<string>Versions</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLineEdit" name="versionSearchEdit">
<property name="placeholderText">
<string>Search...</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QTreeView" name="versionsView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="refreshVersionBtn">
<property name="text">
<string>Refresh</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="versionVersionLabel">
<property name="text">
<string>Version:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="versionVersion">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="versionTimeLabel">
<property name="text">
<string>Time:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="versionTime">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="versionTypeLabel">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="versionType">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="versionRequiresLabel">
<property name="text">
<string>Dependencies:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="versionRequires">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QGroupBox" name="versionListsBox">
<property name="title">
<string>Resources</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLineEdit" name="fileSearchEdit">
<property name="placeholderText">
<string>Search...</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QTreeView" name="indexView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="refreshFileBtn">
<property name="text">
<string>Refresh</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="fileUidLabel">
<property name="text">
<string>UID:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="fileUid">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="fileNameLabel">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="fileName">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QPushButton" name="refreshIndexBtn">
<property name="text">
<string>Refresh Index</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,210 +0,0 @@
/* Copyright 2013-2019 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 "ModFolderPage.h"
#include "ui_ModFolderPage.h"
#include <QMessageBox>
#include <QEvent>
#include <QKeyEvent>
#include <QAbstractItemModel>
#include "MultiMC.h"
#include "dialogs/CustomMessageBox.h"
#include "dialogs/ModEditDialogCommon.h"
#include <GuiUtil.h>
#include "minecraft/SimpleModList.h"
#include "minecraft/Mod.h"
#include "minecraft/VersionFilterData.h"
#include "minecraft/ComponentList.h"
#include <DesktopServices.h>
ModFolderPage::ModFolderPage(BaseInstance *inst, std::shared_ptr<SimpleModList> mods, QString id,
QString iconName, QString displayName, QString helpPage,
QWidget *parent)
: QWidget(parent), ui(new Ui::ModFolderPage)
{
ui->setupUi(this);
ui->tabWidget->tabBar()->hide();
m_inst = inst;
m_mods = mods;
m_id = id;
m_displayName = displayName;
m_iconName = iconName;
m_helpName = helpPage;
m_fileSelectionFilter = "%1 (*.zip *.jar)";
m_filterModel = new QSortFilterProxyModel(this);
m_filterModel->setDynamicSortFilter(true);
m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
m_filterModel->setSortCaseSensitivity(Qt::CaseInsensitive);
m_filterModel->setSourceModel(m_mods.get());
m_filterModel->setFilterKeyColumn(-1);
ui->modTreeView->setModel(m_filterModel);
ui->modTreeView->installEventFilter(this);
ui->modTreeView->sortByColumn(1, Qt::AscendingOrder);
auto smodel = ui->modTreeView->selectionModel();
connect(smodel, &QItemSelectionModel::currentChanged, this, &ModFolderPage::modCurrent);
connect(ui->filterEdit, &QLineEdit::textChanged, this, &ModFolderPage::on_filterTextChanged );
}
void ModFolderPage::openedImpl()
{
m_mods->startWatching();
}
void ModFolderPage::closedImpl()
{
m_mods->stopWatching();
}
void ModFolderPage::on_filterTextChanged(const QString& newContents)
{
m_viewFilter = newContents;
m_filterModel->setFilterFixedString(m_viewFilter);
}
CoreModFolderPage::CoreModFolderPage(BaseInstance *inst, std::shared_ptr<SimpleModList> mods,
QString id, QString iconName, QString displayName,
QString helpPage, QWidget *parent)
: ModFolderPage(inst, mods, id, iconName, displayName, helpPage, parent)
{
}
ModFolderPage::~ModFolderPage()
{
m_mods->stopWatching();
delete ui;
}
bool ModFolderPage::shouldDisplay() const
{
if (m_inst)
return !m_inst->isRunning();
return true;
}
bool CoreModFolderPage::shouldDisplay() const
{
if (ModFolderPage::shouldDisplay())
{
auto inst = dynamic_cast<MinecraftInstance *>(m_inst);
if (!inst)
return true;
auto version = inst->getComponentList();
if (!version)
return true;
if(!version->getComponent("net.minecraftforge"))
{
return false;
}
if(!version->getComponent("net.minecraft"))
{
return false;
}
if(version->getComponent("net.minecraft")->getReleaseDateTime() < g_VersionFilterData.legacyCutoffDate)
{
return true;
}
}
return false;
}
bool ModFolderPage::modListFilter(QKeyEvent *keyEvent)
{
switch (keyEvent->key())
{
case Qt::Key_Delete:
on_rmModBtn_clicked();
return true;
case Qt::Key_Plus:
on_addModBtn_clicked();
return true;
default:
break;
}
return QWidget::eventFilter(ui->modTreeView, keyEvent);
}
bool ModFolderPage::eventFilter(QObject *obj, QEvent *ev)
{
if (ev->type() != QEvent::KeyPress)
{
return QWidget::eventFilter(obj, ev);
}
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
if (obj == ui->modTreeView)
return modListFilter(keyEvent);
return QWidget::eventFilter(obj, ev);
}
void ModFolderPage::on_addModBtn_clicked()
{
auto list = GuiUtil::BrowseForFiles(
m_helpName,
tr("Select %1",
"Select whatever type of files the page contains. Example: 'Loader Mods'")
.arg(m_displayName),
m_fileSelectionFilter.arg(m_displayName), MMC->settings()->get("CentralModsDir").toString(),
this->parentWidget());
if (!list.empty())
{
for (auto filename : list)
{
m_mods->installMod(filename);
}
}
}
void ModFolderPage::on_enableModBtn_clicked()
{
auto selection = m_filterModel->mapSelectionToSource(ui->modTreeView->selectionModel()->selection());
m_mods->enableMods(selection.indexes(), true);
}
void ModFolderPage::on_disableModBtn_clicked()
{
auto selection = m_filterModel->mapSelectionToSource(ui->modTreeView->selectionModel()->selection());
m_mods->enableMods(selection.indexes(), false);
}
void ModFolderPage::on_rmModBtn_clicked()
{
auto selection = m_filterModel->mapSelectionToSource(ui->modTreeView->selectionModel()->selection());
m_mods->deleteMods(selection.indexes());
}
void ModFolderPage::on_configFolderBtn_clicked()
{
DesktopServices::openDirectory(m_inst->instanceConfigFolder(), true);
}
void ModFolderPage::on_viewModBtn_clicked()
{
DesktopServices::openDirectory(m_mods->dir().absolutePath(), true);
}
void ModFolderPage::modCurrent(const QModelIndex &current, const QModelIndex &previous)
{
if (!current.isValid())
{
ui->frame->clear();
return;
}
auto sourceCurrent = m_filterModel->mapToSource(current);
int row = sourceCurrent.row();
Mod &m = m_mods->operator[](row);
ui->frame->updateWithMod(m);
}

View File

@@ -1,180 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ModFolderPage</class>
<widget class="QWidget" name="ModFolderPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>723</width>
<height>532</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<attribute name="title">
<string notr="true">Tab 1</string>
</attribute>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1,0">
<item row="0" column="2">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="addModBtn">
<property name="text">
<string>&amp;Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="rmModBtn">
<property name="text">
<string>&amp;Remove</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="enableModBtn">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="disableModBtn">
<property name="text">
<string>Disable</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="configFolderBtn">
<property name="toolTip">
<string>Open the 'config' folder in the system file manager.</string>
</property>
<property name="text">
<string>View configs</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="viewModBtn">
<property name="text">
<string>&amp;View Folder</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" colspan="3">
<widget class="MCModInfoFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="1">
<widget class="QLineEdit" name="filterEdit">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="filterLabel">
<property name="text">
<string>Filter:</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="ModListView" name="modTreeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DropOnly</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ModListView</class>
<extends>QTreeView</extends>
<header>widgets/ModListView.h</header>
</customwidget>
<customwidget>
<class>MCModInfoFrame</class>
<extends>QFrame</extends>
<header>widgets/MCModInfoFrame.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>tabWidget</tabstop>
<tabstop>modTreeView</tabstop>
<tabstop>filterEdit</tabstop>
<tabstop>addModBtn</tabstop>
<tabstop>rmModBtn</tabstop>
<tabstop>enableModBtn</tabstop>
<tabstop>disableModBtn</tabstop>
<tabstop>configFolderBtn</tabstop>
<tabstop>viewModBtn</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@@ -1,57 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NotesPage</class>
<widget class="QWidget" name="NotesPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>731</width>
<height>538</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string notr="true">Tab 1</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTextEdit" name="noteEditor">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="acceptRichText">
<bool>false</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,106 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ScreenshotsPage</class>
<widget class="QWidget" name="ScreenshotsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>723</width>
<height>532</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string notr="true">Tab 1</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QListView" name="listView">
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="uploadBtn">
<property name="text">
<string>&amp;Upload</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="deleteBtn">
<property name="text">
<string>&amp;Delete</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="renameBtn">
<property name="text">
<string>&amp;Rename</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="viewFolderBtn">
<property name="text">
<string>&amp;View Folder</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>listView</tabstop>
<tabstop>uploadBtn</tabstop>
<tabstop>deleteBtn</tabstop>
<tabstop>renameBtn</tabstop>
<tabstop>viewFolderBtn</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@@ -1,199 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ServersPage</class>
<widget class="QWidget" name="ServersPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>706</width>
<height>575</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<attribute name="title">
<string notr="true">Tab 1</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="1">
<widget class="QComboBox" name="resourceComboBox">
<item>
<property name="text">
<string>Ask to download</string>
</property>
</item>
<item>
<property name="text">
<string>Always download</string>
</property>
</item>
<item>
<property name="text">
<string>Never download</string>
</property>
</item>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="resourcesLabel">
<property name="text">
<string>Reso&amp;urces</string>
</property>
<property name="buddy">
<cstring>resourceComboBox</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="addressLine"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="nameLabel">
<property name="text">
<string>&amp;Name</string>
</property>
<property name="buddy">
<cstring>nameLine</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="nameLine"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="addressLabel">
<property name="text">
<string>Address</string>
</property>
<property name="buddy">
<cstring>addressLine</cstring>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QTreeView" name="serversView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="iconSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<attribute name="headerStretchLastSection">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="addBtn">
<property name="text">
<string>&amp;Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeBtn">
<property name="text">
<string>&amp;Remove</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="moveUpBtn">
<property name="text">
<string>Move Up</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="moveDownBtn">
<property name="text">
<string>Move Down</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>tabWidget</tabstop>
<tabstop>serversView</tabstop>
<tabstop>nameLine</tabstop>
<tabstop>addressLine</tabstop>
<tabstop>resourceComboBox</tabstop>
<tabstop>addBtn</tabstop>
<tabstop>removeBtn</tabstop>
<tabstop>moveUpBtn</tabstop>
<tabstop>moveDownBtn</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@@ -1,332 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>VersionPage</class>
<widget class="QWidget" name="VersionPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>870</width>
<height>1008</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string notr="true">Tab 1</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="ModListView" name="packageView">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
<property name="headerHidden">
<bool>false</bool>
</property>
<attribute name="headerVisible">
<bool>true</bool>
</attribute>
</widget>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Selection</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="changeVersionBtn">
<property name="toolTip">
<string>Change version of the selected package.</string>
</property>
<property name="text">
<string>Change version</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="moveUpBtn">
<property name="toolTip">
<string>Make the selected package apply sooner.</string>
</property>
<property name="text">
<string>Move up</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="moveDownBtn">
<property name="toolTip">
<string>Make the selected package apply later.</string>
</property>
<property name="text">
<string>Move down</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeBtn">
<property name="toolTip">
<string>Remove selected package from the instance.</string>
</property>
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item>
<widget class="LineSeparator" name="separator_4" native="true"/>
</item>
<item>
<widget class="QLabel" name="label_10">
<property name="text">
<string>Edit</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="customizeBtn">
<property name="toolTip">
<string>Customize selected package.</string>
</property>
<property name="text">
<string>Customize</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="editBtn">
<property name="toolTip">
<string>Edit selected package.</string>
</property>
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="revertBtn">
<property name="toolTip">
<string>Revert the selected package to default.</string>
</property>
<property name="text">
<string>Revert</string>
</property>
</widget>
</item>
<item>
<widget class="LineSeparator" name="separator" native="true"/>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Install</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="forgeBtn">
<property name="toolTip">
<string>Install the Minecraft Forge package.</string>
</property>
<property name="text">
<string>Install Forge</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fabricBtn">
<property name="toolTip">
<string>Install the Fabric Loader package.</string>
</property>
<property name="text">
<string>Install Fabric</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="liteloaderBtn">
<property name="toolTip">
<string>Install the LiteLoader package.</string>
</property>
<property name="text">
<string>Install LiteLoader</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="modBtn">
<property name="toolTip">
<string>Install normal mods.</string>
</property>
<property name="text">
<string>Install mods</string>
</property>
</widget>
</item>
<item>
<widget class="LineSeparator" name="widget" native="true"/>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Advanced</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="jarmodBtn">
<property name="toolTip">
<string>Add a mod into the Minecraft jar file.</string>
</property>
<property name="text">
<string>Add to Minecraft.jar</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="jarBtn">
<property name="text">
<string>Replace Minecraft.jar</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addEmptyBtn">
<property name="text">
<string>Add Empty</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="reloadBtn">
<property name="toolTip">
<string>Reload all packages.</string>
</property>
<property name="text">
<string>Reload</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="downloadBtn">
<property name="toolTip">
<string>Download the files needed to launch the instance now.</string>
</property>
<property name="text">
<string>Download All</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>111</width>
<height>13</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<widget class="MCModInfoFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ModListView</class>
<extends>QTreeView</extends>
<header>widgets/ModListView.h</header>
</customwidget>
<customwidget>
<class>LineSeparator</class>
<extends>QWidget</extends>
<header>widgets/LineSeparator.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>MCModInfoFrame</class>
<extends>QFrame</extends>
<header>widgets/MCModInfoFrame.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>tabWidget</tabstop>
<tabstop>packageView</tabstop>
<tabstop>changeVersionBtn</tabstop>
<tabstop>moveUpBtn</tabstop>
<tabstop>moveDownBtn</tabstop>
<tabstop>removeBtn</tabstop>
<tabstop>customizeBtn</tabstop>
<tabstop>editBtn</tabstop>
<tabstop>revertBtn</tabstop>
<tabstop>forgeBtn</tabstop>
<tabstop>fabricBtn</tabstop>
<tabstop>liteloaderBtn</tabstop>
<tabstop>modBtn</tabstop>
<tabstop>jarmodBtn</tabstop>
<tabstop>jarBtn</tabstop>
<tabstop>addEmptyBtn</tabstop>
<tabstop>reloadBtn</tabstop>
<tabstop>downloadBtn</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@@ -1,171 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WorldListPage</class>
<widget class="QWidget" name="WorldListPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>723</width>
<height>532</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string notr="true">Tab 1</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="2">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="addBtn">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="LineSeparator" name="separator" native="true"/>
</item>
<item>
<widget class="QPushButton" name="renameBtn">
<property name="text">
<string>Rename</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="copyBtn">
<property name="text">
<string>Copy</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="rmWorldBtn">
<property name="text">
<string>&amp;Remove</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mcEditBtn">
<property name="text">
<string notr="true">MCEdit</string>
</property>
</widget>
</item>
<item>
<widget class="LineSeparator" name="separator_2" native="true"/>
</item>
<item>
<widget class="QPushButton" name="copySeedBtn">
<property name="text">
<string>Copy Seed</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="refreshBtn">
<property name="text">
<string>Refresh</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="viewFolderBtn">
<property name="text">
<string>&amp;View Folder</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<widget class="QTreeView" name="worldTreeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
<attribute name="headerStretchLastSection">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LineSeparator</class>
<extends>QWidget</extends>
<header>widgets/LineSeparator.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>tabWidget</tabstop>
<tabstop>worldTreeView</tabstop>
<tabstop>addBtn</tabstop>
<tabstop>renameBtn</tabstop>
<tabstop>copyBtn</tabstop>
<tabstop>rmWorldBtn</tabstop>
<tabstop>mcEditBtn</tabstop>
<tabstop>copySeedBtn</tabstop>
<tabstop>refreshBtn</tabstop>
<tabstop>viewFolderBtn</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@@ -1,26 +0,0 @@
#include "TechnicPage.h"
#include "ui_TechnicPage.h"
#include "MultiMC.h"
#include "dialogs/NewInstanceDialog.h"
TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget *parent)
: QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog)
{
ui->setupUi(this);
}
TechnicPage::~TechnicPage()
{
delete ui;
}
bool TechnicPage::shouldDisplay() const
{
return true;
}
void TechnicPage::openedImpl()
{
dialog->setSuggestedPack();
}

View File

@@ -1,113 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TechnicPage</class>
<widget class="QWidget" name="TechnicPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>546</width>
<height>405</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>40</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color:#ffc000</string>
</property>
<property name="text">
<string notr="true">UNDER</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string notr="true"/>
</property>
<property name="pixmap">
<pixmap resource="../../resources/assets/assets.qrc">:/assets/underconstruction</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<pointsize>40</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color:#7ca32b</string>
</property>
<property name="text">
<string notr="true">CONSTRUCTION</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources>
<include location="../../resources/assets/assets.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -1,60 +0,0 @@
#include "TwitchPage.h"
#include "ui_TwitchPage.h"
#include "MultiMC.h"
#include "dialogs/NewInstanceDialog.h"
#include <InstanceImportTask.h>
TwitchPage::TwitchPage(NewInstanceDialog* dialog, QWidget *parent)
: QWidget(parent), ui(new Ui::TwitchPage), dialog(dialog)
{
ui->setupUi(this);
connect(ui->checkButton, &QPushButton::clicked, this, &TwitchPage::triggerCheck);
}
TwitchPage::~TwitchPage()
{
delete ui;
}
bool TwitchPage::shouldDisplay() const
{
return true;
}
void TwitchPage::openedImpl()
{
dialog->setSuggestedPack();
}
void TwitchPage::triggerCheck(bool)
{
if(m_modIdResolver) {
return;
}
auto task = new Flame::UrlResolvingTask(ui->lineEdit->text());
connect(task, &Task::finished, this, &TwitchPage::checkDone);
m_modIdResolver.reset(task);
task->start();
}
void TwitchPage::setUrl(const QString& url)
{
ui->lineEdit->setText(url);
triggerCheck(true);
}
void TwitchPage::checkDone()
{
auto result = m_modIdResolver->getResults();
auto formatted = QString("Project %1, File %2").arg(result.projectId).arg(result.fileId);
if(result.resolved && result.type == Flame::File::Type::Modpack) {
ui->twitchLabel->setText(formatted);
QFileInfo fi(result.fileName);
dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(result.url));
} else {
ui->twitchLabel->setPixmap(QPixmap(QString::fromUtf8(":/assets/deadglitch")));
dialog->setSuggestedPack();
}
m_modIdResolver.reset();
}

View File

@@ -1,69 +0,0 @@
/* Copyright 2013-2019 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 <QWidget>
#include "pages/BasePage.h"
#include <MultiMC.h>
#include "tasks/Task.h"
#include "modplatform/flame/UrlResolvingTask.h"
namespace Ui
{
class TwitchPage;
}
class NewInstanceDialog;
class TwitchPage : public QWidget, public BasePage
{
Q_OBJECT
public:
explicit TwitchPage(NewInstanceDialog* dialog, QWidget *parent = 0);
virtual ~TwitchPage();
virtual QString displayName() const override
{
return tr("Twitch URL");
}
virtual QIcon icon() const override
{
return MMC->getThemedIcon("twitch");
}
virtual QString id() const override
{
return "twitch";
}
virtual QString helpPage() const override
{
return "Twitch-platform";
}
virtual bool shouldDisplay() const override;
void openedImpl() override;
void setUrl(const QString & url);
private slots:
void triggerCheck(bool checked);
void checkDone();
private:
Ui::TwitchPage *ui = nullptr;
NewInstanceDialog* dialog = nullptr;
shared_qobject_ptr<Flame::UrlResolvingTask> m_modIdResolver;
};

View File

@@ -1,62 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TwitchPage</class>
<widget class="QWidget" name="TwitchPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>546</width>
<height>405</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Twitch URL:</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QLabel" name="twitchLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>40</pointsize>
</font>
</property>
<property name="pixmap">
<pixmap resource="../../resources/assets/assets.qrc">:/assets/deadglitch</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="checkButton">
<property name="text">
<string>Check</string>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>lineEdit</tabstop>
<tabstop>checkButton</tabstop>
</tabstops>
<resources>
<include location="../../resources/assets/assets.qrc"/>
</resources>
<connections/>
</ui>

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

View File

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill="#585858" d="M6.9,4.4v16h16v-16H6.9z M21.9,5.4l0,14h-14v-14H21.9z"/>
<rect fill="none" width="24" height="24"/>
<rect x="7.9" y="5.4" fill="#F2F2F2" width="14" height="14"/>
<polygon fill="#999999" points="7.9,7.4 7.9,6.9 8.9,6.9 8.9,7.9 9.9,7.9 9.9,7.4 10.9,7.4 10.9,8.9 11.9,8.9 11.9,6.4 12.9,6.4
12.9,7.4 13.9,7.4 13.9,6.9 14.9,6.9 14.9,8.9 15.9,8.9 15.9,7.4 16.9,7.4 16.9,8.4 17.9,8.4 17.9,7.4 18.9,7.4 18.9,6.9 19.9,6.9
19.9,7.4 21.4,7.4 21.4,6.9 21.9,6.9 21.9,7.4 22.9,7.4 22.9,4.4 6.9,4.4 6.9,7.4 "/>
<g>
<path fill="#585858" d="M14.9,13.6c-0.9,0.9-1.9,2-3.4,2c-1.5,0-2.6-1.1-2.6-2.7c0-1.4,1.2-2.7,2.8-2.7c1.3,0,2.4,1.1,3.2,2
c0.9-0.9,1.9-2,3.4-2c1.6,0,2.6,1.1,2.6,2.7c0,1.4-1.2,2.7-2.8,2.7C16.7,15.6,15.7,14.5,14.9,13.6z M14.1,12.9
c-0.7-0.7-1.5-1.6-2.5-1.6c-0.9,0-1.6,0.7-1.6,1.6c0,0.9,0.7,1.6,1.6,1.6C12.6,14.5,13.4,13.6,14.1,12.9z M19.7,12.9
c0-0.9-0.7-1.6-1.5-1.6c-1,0-1.9,0.9-2.6,1.6c0.7,0.7,1.5,1.6,2.5,1.6C19,14.5,19.7,13.7,19.7,12.9z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,7 +0,0 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource prefix="/assets">
<file alias="underconstruction">underconstruction.png</file>
<file alias="deadglitch">deadglitch.svg</file>
</qresource>
</RCC>

View File

@@ -1,66 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
class="tw-svg__asset tw-svg__asset--deadglitch tw-svg__asset--inherit"
width="92px"
height="96px"
version="1.1"
viewBox="0 0 30 30"
x="0px"
y="0px"
id="svg8"
sodipodi:docname="deadglitch.svg"
inkscape:version="0.92.2 2405546, 2018-03-11">
<metadata
id="metadata14">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs12" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1353"
inkscape:window-height="828"
id="namedview10"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="44.285787"
inkscape:cy="52.833458"
inkscape:window-x="2958"
inkscape:window-y="702"
inkscape:window-maximized="0"
inkscape:current-layer="svg8" />
<g
id="g6"
style="fill:#898395;fill-opacity:1">
<path
d="M26,17.4589613 L26,3 L4,3 L4,22.0601057 L10.0032868,22.0601057 L10.0032868,26 L14.0004537,22.0601057 L21.3322933,22.0601057 L26,17.4589613 L26,17.4589613 Z M21.0896458,26.0850335 L15.1583403,26.0850335 L11.2051771,30 L7.24798611,30 L7.24798611,26.0850335 L0,26.0850335 L0,5.21746493 L1.97773958,0 L29,0 L29,18.2620736 L21.0896458,26.0850335 L21.0896458,26.0850335 Z"
id="path2"
style="fill:#898395;fill-opacity:1" />
<path
d="M20.8587626,12.1710126 L22.4052753,13.7175252 L23.7175252,12.4052753 L22.1710126,10.8587626 L23.7175252,9.31224999 L22.4052753,8 L20.8587626,9.54651264 L19.31225,8 L18,9.31224999 L19.5465126,10.8587626 L18,12.4052753 L19.31225,13.7175252 L20.8587626,12.1710126 Z M11.8587626,12.1710126 L13.4052753,13.7175252 L14.7175252,12.4052753 L13.1710126,10.8587626 L14.7175252,9.31224999 L13.4052753,8 L11.8587626,9.54651264 L10.31225,8 L9,9.31224999 L10.5465126,10.8587626 L9,12.4052753 L10.31225,13.7175252 L11.8587626,12.1710126 Z"
id="path4"
style="fill:#898395;fill-opacity:1" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 682 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 708 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 978 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 532 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

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