Compare commits

..

218 Commits

Author SHA1 Message Date
janrupf
3bdf63b0ee GH-2065 Strip non ASCII chars 2019-06-22 01:35:07 +02:00
Petr Mrázek
ce12f1a734 Merge pull request #2694 from Janrupf/fix-disabled-mods
Operate on copy of QString instead of appending
2019-06-17 23:21:26 +02:00
janrupf
c1953739d9 NOISSUE Don't append .disabled to string 2019-06-17 15:49:26 +02:00
Petr Mrázek
e8bf9cef24 Merge branch 'develop' of https://github.com/lelandliu/MultiMC5 into develop 2019-06-15 22:47:36 +02:00
Petr Mrázek
8aa4b9dac5 NOISSUE limit fabric intermediary selection options in UI by minecraft version 2019-06-15 22:45:56 +02:00
Petr Mrázek
4836ba22cd NOISSUE fix fabric tooltip string 2019-06-15 22:29:34 +02:00
Petr Mrázek
6c30076b6c GH-2639 Add simple fabric loader installation support 2019-06-15 21:25:23 +02:00
Petr Mrázek
83c48a0f1a Merge pull request #2690 from asiekierka/fabric-modparse
Add Fabric mod JSON parsing support
2019-06-15 20:41:42 +02:00
asie
d251097545 fix author name parsing 2019-06-15 16:36:13 +02:00
asie
c35dbd972e Add Fabric mod JSON parsing support 2019-06-15 13:54:20 +02:00
Leland Liu
0c5d121452 Fixed adding a disabled duplicate 2019-06-14 15:48:24 -04:00
Petr Mrázek
53ad5cb436 Merge pull request #2684 from isoraqathedh/patch-1
Pass command line arguments through to the executable
2019-06-08 15:53:01 +02:00
isoraqathedh
d87bd4b588 Update run.sh
Pass command line arguments through to the executable
2019-06-08 21:21:22 +08:00
Petr Mrázek
86850ef5d0 NOISSUE fix macOS build, remove bundled dependencies on linux
Your copy of MultiMC might stop working after this update
because we no longer bundle Qt and other system libraries.

Contact us at https://discord.gg/0k2zsXGNHs0fE4Wm if
you need help with installing Qt.

Qt 5.4.x or newer is required.
2019-06-08 15:08:24 +02:00
Petr Mrázek
30fba4d407 NOISSUE make the global settings button context sensitive 2019-06-01 18:05:42 +02:00
Petr Mrázek
932160818e NOISSUE add option to open global settings from instance settings
This should hopefully giude people towards using the right thing.
2019-06-01 12:28:53 +02:00
Petr Mrázek
59e1ed3d87 NOISSUE add the pocket fox icon 2019-06-01 00:59:02 +02:00
Petr Mrázek
3470a3df96 NOISSUE improve icon handling while importing and exporting instances
Now it handles formats other than png.
2019-05-31 21:53:58 +02:00
Petr Mrázek
61913daaf3 Merge pull request #2603 from AlexTMjugador/develop
Support launching profiler in offline mode
2019-04-14 23:34:10 +02:00
Alejandro González
cb71dfa605 Tweak strings as per peterix review 2019-04-14 23:02:01 +02:00
AlexTMjugador
2be98baaba Conform to existing code style 2019-04-15 00:44:59 +02:00
AlexTMjugador
0ce637bf0e Support launching profiler in offline mode
This commit adds the necessary GUI controls to let the user choose
whether they want to launch Minecraft in offline mode with a configured profiler.
2019-04-15 00:42:06 +02:00
Petr Mrázek
70ed30f9e6 GH-2591 less std::shared_ptr and more shared_qobject_ptr
This eliminates some weird crashes.
2019-04-07 23:59:04 +02:00
Petr Mrázek
414946cad9 Merge remote-tracking branch 'origin/feature/trim_server_details' into develop 2019-04-02 23:39:13 +02:00
Elise
1fc199697c GH-2551 Trim at serialization 2019-03-29 11:25:37 -04:00
Petr Mrázek
9292d846b6 Merge pull request #2565 from MultiMC/feature/deletion_harder
GH-2487 Make deleting instances harder
2019-03-23 14:23:45 +01:00
Elise
22db95ce3b Merge branch 'develop' into feature/deletion_harder 2019-03-19 16:29:40 -04:00
Elise
597fe50d37 GH-2551 Trim server name and IP strings 2019-03-19 13:28:11 -04:00
Petr Mrázek
bf93ba02e9 NOISSUE fix metadata URL 2019-03-08 02:04:08 +01:00
Petr Mrázek
07c1685ff1 NOISSUE create translations folder before starting to watch it for changes
Fixes issue where on first run, the translations don't show up.
2019-03-08 01:21:04 +01:00
Elise
a5e531d4f1 GH-2487 Make NO the default button 2019-02-26 16:56:08 -05:00
Petr Mrázek
ae956d8fd6 NOISSUE update changelog 2019-02-21 00:45:36 +01:00
Petr Mrázek
73e487a31e NOISSUE hide the game options screen again 2019-02-21 00:44:36 +01:00
Petr Mrázek
0b95d2b03f NOISSUE fix build 2019-02-19 01:11:54 +01:00
Petr Mrázek
9c82adaee5 GH-2209 Fix sounds in old (pre-1.6) versions 2019-02-19 01:00:03 +01:00
Petr Mrázek
0a99d037c4 Merge pull request #2532 from andersmmg/patch-1
Update title of Add Empty dialog
2019-02-05 08:44:07 +01:00
Joshua Anderson
6e8e4c5dfa Update title of Add Empty dialog 2019-02-05 00:06:29 -07:00
Petr Mrázek
e4599ee2d1 NOISSUE fix builds? 2019-01-30 01:12:21 +01:00
Petr Mrázek
62c9fcdc6c NOISSUE first step towards having game options management 2019-01-30 00:35:24 +01:00
Petr Mrázek
c1ea42d3d9 Merge branch 'stable' of https://github.com/Scotsguy/MultiMC5 into develop 2019-01-17 00:51:54 +01:00
AppleTheGolden
437dec91f9 Update Copyright Year 2019-01-16 21:14:24 +01:00
Petr Mrázek
7436c94976 NOISSUE Replace Quality with Completeness in language widget
Completeness does no imply quality.
2019-01-14 01:36:04 +01:00
Petr Mrázek
c08053d8b8 NOISSUE split out language selection widget, use it in settings 2019-01-09 04:38:35 +01:00
Petr Mrázek
e71786d7b9 NOISSUE language selection wizard improvements
Same needs to be applied to the application settings later.
2019-01-08 02:20:36 +01:00
Petr Mrázek
819503d530 NOISSUE bump version to 0.6.5 2019-01-06 22:56:05 +01:00
Petr Mrázek
d6ace374d8 NOISSUE update changelog some more 2019-01-06 22:54:00 +01:00
Petr Mrázek
6a21c043ce NOISSUE bump version to 0.6.4 and update changelog 2019-01-06 22:28:27 +01:00
Petr Mrázek
4474d269cc NOISSUE granular model updates for language model 2019-01-06 22:14:13 +01:00
Petr Mrázek
ec2732ccd1 NOISSUE update FTB URLs 2019-01-04 01:48:36 +01:00
Petr Mrázek
4b7971f60f NOISSUE hotloading of translations and use of local PO files
The hotloading is still inefficient
2019-01-02 01:41:07 +01:00
Petr Mrázek
4cbd1a7692 Fix version number 2018-12-26 02:44:07 +01:00
Petr Mrázek
f481940aeb NOISSUE fix up changelog formatting 2018-12-26 01:56:25 +01:00
Petr Mrázek
037fb6fdb7 NOISSUE update the changelog 2018-12-26 01:54:37 +01:00
Petr Mrázek
3e60e770b5 NOISSUE just don't use std::abs, it doesn't work 2018-12-24 14:49:53 +01:00
Petr Mrázek
70052b180c NOISSUE maybe fix macos build, pngcrush images 2018-12-24 14:31:34 +01:00
Petr Mrázek
2e58429b6a NOISSUE kitty cat in a silly hat! 2018-12-24 03:26:14 +01:00
Petr Mrázek
56a9b65efb NOISSUE add missing Q_OBJECT macros 2018-12-23 01:05:11 +01:00
Petr Mrázek
4a4ba954ed GH-2488 fix Qt's relative URL redirect problems some more 2018-12-14 02:48:55 +01:00
Petr Mrázek
14bb666a20 GH-2485 fix crash bug while creating instances 2018-12-14 01:18:18 +01:00
Petr Mrázek
075e173fbd NOISSUE fix build 2018-12-12 00:05:53 +01:00
Petr Mrázek
3fe9165201 NOISSUE fix logging for gametype 2018-12-11 23:54:29 +01:00
Petr Mrázek
13b293dd65 GH-2374 fix missing alternating backgrounds in worlds, add gametype column 2018-12-11 23:53:14 +01:00
Petr Mrázek
de568b32b8 NOISSUE model Task states as one enum instead of multiple flags
This adds Task::State::AbortedByUser as a possibility
2018-12-10 20:50:15 +01:00
Petr Mrázek
fb29e45bd0 NOISSUE make instance creation task abortable
This may or may not expose issues when it actually IS used.
2018-12-10 20:49:21 +01:00
Petr Mrázek
3018310be3 NOISSUE fix typo 2018-12-06 00:36:31 +01:00
Petr Mrázek
f111d1bc0c GH-2483 fix minor issue while checking for missing local files 2018-12-06 00:33:49 +01:00
Petr Mrázek
0ee915200b GH-2478 fix jarmods being detected as missing 2018-11-26 09:57:51 +01:00
Petr Mrázek
9eb456336d NOISSUE fix unit tests 2018-11-26 03:22:20 +01:00
Petr Mrázek
3f6aecf5a2 GH-2475 fix reporting missing local libraries on launch 2018-11-26 03:06:58 +01:00
Petr Mrázek
e8c382bede GH-2101 select everything when editing instance names 2018-11-23 21:47:23 +01:00
Petr Mrázek
54e857a7f5 NOISSUE proper inline editable instance names
Pretty!
2018-11-23 04:56:56 +01:00
Petr Mrázek
74c598d756 GH-2101 fix enter and double click activation of instances 2018-11-22 02:02:53 +01:00
Petr Mrázek
c214c13fb3 GH-2101 POC for inline renaming 2018-11-22 01:50:32 +01:00
Petr Mrázek
c4a472981f GH-2467 remove unused sorting indicators from version page 2018-11-21 00:55:40 +01:00
Petr Mrázek
33a7cc1890 NOISSUE fix up mod installation and add a lot of logging for it 2018-11-21 00:29:41 +01:00
Petr Mrázek
a8e77f0ecc NOISSUE remove some nonsense and dead code 2018-11-15 00:36:47 +01:00
Petr Mrázek
5603133822 GH-2384 when adding mods with the same filename, rotate the files
Current will be disabled and renamed to '$name-old'.
Old one, if present, will be removed.
2018-11-12 02:39:52 +01:00
Petr Mrázek
47b1f9a860 NOISSUE fix up unit test for the changed Library behavior
No more fallback for local libraries. They must be in the instance, always.
2018-11-12 02:02:07 +01:00
Petr Mrázek
0f0dc30729 GH-2144 Fix translatable string 2018-11-12 01:57:26 +01:00
janrupf
1648b34aed GH-1552 Hide PermGen when using an autodetected java version greater than 8 2018-11-12 01:50:04 +01:00
janrupf
4cc7329ce3 GH-2144 Append '(installed)' to the installed version name 2018-11-12 01:49:52 +01:00
janrupf
16df8d7b88 GH-2384 Replace existing mod files 2018-11-12 01:49:35 +01:00
janrupf
3d630c7856 GH-2452 Ask before deleting group 2018-11-12 01:48:53 +01:00
janrupf
51a6883737 NOISSUE Ignore stuff from CLion 2018-11-12 01:48:38 +01:00
Petr Mrázek
d6367407b0 NOISSUE Rename OneSixUpdate to MinecraftUpdate 2018-11-11 23:55:50 +01:00
Petr Mrázek
defa911705 NOISSUE fix groups not being updated in UI correctly
The model was not sending the appropriate signals.
2018-11-11 23:54:16 +01:00
Petr Mrázek
17e09a292d NOISSUE read local libraries only from the local location
This removes the fallback to global `libraries` folder for `local` libraries.
2018-11-11 23:50:36 +01:00
Petr Mrázek
8a7f1e405f NOISSUE take forge xz download url base from the metadata file
Instead of hardcoding it.
2018-11-04 13:41:21 +01:00
Petr Mrázek
58260da861 NOISSUE remove use of obsolete URL constants, simplify the rest 2018-11-04 13:18:35 +01:00
Chris Lane
16cc20aefd NOISSUE fix 404 with liteloader https url 2018-11-02 12:59:37 +00:00
Chris Lane
0572a1e4e6 NOISSUE use https more widely 2018-11-02 12:04:08 +00:00
Petr Mrázek
9b74e73ad3 NOISSUE use https for downloading assets 2018-11-02 10:28:50 +01:00
Petr Mrázek
24fd2918c7 NOISSUE fix more rhel 7.6 problems? 2018-11-01 23:30:13 +01:00
Petr Mrázek
9eb165bfee iNOISSUE fix build issue with pack200 on rhel 7.6 2018-11-01 22:08:15 +01:00
Petr Mrázek
e4ce74e622 GH-2382 fix exact version filter not being exact... 2018-11-01 00:34:31 +01:00
Petr Mrázek
59e2f52db7 GH-2238 fix issues with whitespace/newlines in folder and instance names 2018-11-01 00:18:49 +01:00
Petr Mrázek
d5037d4f79 GH-2412: collect dead processes on linux properly
Issues were caused by use of `popen()` with no `pclose()` counterpart...
2018-10-31 22:44:23 +01:00
Petr Mrázek
aef0ccb1a2 GH-2232 add gif icon support (not animated) 2018-10-31 21:54:22 +01:00
Noah
d9c1fc09e7 add game and minecraft keywords 2018-10-31 20:40:19 +01:00
Noah
8b56d77f5e add minecraft keyword 2018-10-31 20:40:19 +01:00
Petr Mrázek
e3ab393cec NOISSUE make LaunchStep::bind private
Static analysis was complaining about it.
2018-10-31 00:04:21 +01:00
Petr Mrázek
2c03c67c36 NOISSUE stop mentioning the translations server in README 2018-09-10 03:37:35 +02:00
Petr Mrázek
a279df8bda NOISSUE fix build on linux? 2018-08-02 01:12:41 +02:00
Petr Mrázek
5f2d3f014a NOISSUE get rid of remaining tabs 2018-08-02 01:01:55 +02:00
Jannis Lübke
6aada8adf7 NOISSUE FTB pack code implementation, cleaned up 2018-08-02 00:52:31 +02:00
Petr Mrázek
6cee50eac6 NOISSUE gate new mods page behind cheat code 2018-08-01 20:05:18 +02:00
Petr Mrázek
9cc93ae81d NOISSUE fix crash bug caused by urge to maker code pretty
Also added a note so this doesn't happen again...
2018-07-31 02:20:04 +02:00
Petr Mrázek
0c73ddee73 NOISSUE set groups for instances by not setting groups for instances
So simple. Better in every way.
2018-07-31 01:54:08 +02:00
Petr Mrázek
9965decd81 NOISSUE squish. 2018-07-28 22:12:57 +02:00
Petr Mrázek
9e7cdbfe11 NOISSUE pretty multi-line formatting... 2018-07-28 22:12:57 +02:00
Petr Mrázek
76d6ec91a4 NOISSUE simplify. 2018-07-28 22:12:57 +02:00
Petr Mrázek
7b439c85c0 SCRATCH things and stuff, related to grou saving 2018-07-28 22:12:57 +02:00
Petr Mrázek
12f2716f31 GH-2355 Do not allow instances to be created with whitespace prefix or suffix 2018-07-28 22:08:09 +02:00
Petr Mrázek
4169f53b19 NOISSUE fix build on macOS? 2018-07-28 00:00:04 +02:00
Petr Mrázek
e4c33458f2 GH-2352 Add Minecraft folder button for instances, rearrange buttons by importance 2018-07-27 23:57:09 +02:00
Petr Mrázek
14f85813c8 NOISSUE hide the new mods page 2018-07-22 09:06:31 +02:00
Petr Mrázek
bbb3b3e6f6 NOISSUE tabs -> spaces 2018-07-15 14:51:05 +02:00
Petr Mrázek
03280cc62e NOISSUE separate new mods model from the simple one
It should list mods in various locations...
2018-07-15 14:04:09 +02:00
Petr Mrázek
128bce6acb SCRATCH second mods page, placeholder 2018-07-15 12:48:41 +02:00
Petr Mrázek
8108c61745 NOISSUE fix unitialized data warning on Arch Linux 2018-07-06 19:46:28 +02:00
Petr Mrázek
a222c94d34 NOISSUE add cancel button to the new instance dialog 2018-06-30 19:44:30 +02:00
Petr Mrázek
a872085f9a NOISSUE remove refresh button from servers page 2018-06-30 19:44:21 +02:00
Petr Mrázek
8516a6646e NOISSUE fix saving the servers.dat file when it doesn't exist yet 2018-06-30 01:27:44 +02:00
Petr Mrázek
44381c09d7 NOISSUE more warnings 2018-06-28 23:51:26 +02:00
Petr Mrázek
bb599abf59 NOISSUE fix a bug with mutexes on Windows, more warnings 2018-06-28 23:42:44 +02:00
Petr Mrázek
07f7ec8eef NOISSUE fix some warnings so builds can go further 2018-06-28 23:35:04 +02:00
Petr Mrázek
7fe94ca7b4 NOISSUE fix all sorts of warnings, enable Werror and pedantic 2018-06-28 23:18:45 +02:00
Petr Mrázek
b5f636b3d5 NOISSUE do not keep downloads in memory, add (some) missing virtual dtors 2018-06-28 21:34:56 +02:00
Petr Mrázek
19bb50b872 NOISSUE sync up quazip merge commit 2018-06-05 01:01:04 +02:00
Sergey Shatunov
4d68c1b509 GH-2291 Fix build with Qt 5.11+ 2018-06-02 17:22:43 +07:00
Petr Mrázek
f0ff2db4e1 GH-2277 fix even more exception catches by value 2018-05-20 01:53:05 +02:00
Charles Milette
72c0002b45 Catch C++ exceptions by const reference
Fixes #2277
2018-05-19 19:18:26 -04:00
Petr Mrázek
b9fd381eee GH-2268 update the deb package to not depend on latest jre
* `openjdk-8-jre` is now recommended
* the package now depends on `desktop-file-utils`
* the desktop icon has been updated to the current one
2018-05-17 23:14:12 +02:00
Petr Mrázek
584f1e89b9 GH-2252 make URL drop signal use a queued connection instead of direct call
Hopefully this will fix the issues with stuck applications on Windows.
2018-04-23 08:45:58 +02:00
Petr Mrázek
72ff342d63 GH-2053 basics of the servers.dat management 2018-04-23 02:05:22 +02:00
Petr Mrázek
6284f070c1 NOISSUE update changelog for 0.6.2 2018-04-08 23:43:03 +02:00
Petr Mrázek
be9063317e NOISSUE hide the twitch platform page 2018-04-08 21:22:41 +02:00
Petr Mrázek
8ec36e2cb9 NOISSUE bump release version to 0.6.2 2018-04-08 21:22:07 +02:00
Petr Mrázek
8cefc76108 NOISSUE show release dates of the meta versions in version picker views 2018-04-08 19:49:02 +02:00
Petr Mrázek
172f83c7e2 NOISSUE and even more bad includes 2018-04-07 22:45:03 +02:00
Petr Mrázek
b1e0cbf852 NOISSUE add more missing includes 2018-04-07 22:42:01 +02:00
Petr Mrázek
9b7f82ff26 NOISSUE fix build problem with missing <functional> include 2018-04-07 22:36:57 +02:00
Petr Mrázek
67cef79d81 NOISSUE add logging to zip subfolder extraction 2018-04-07 22:33:26 +02:00
Janrupf
7e1c5d439a #2228, #2229 - Auto import pack icons and fixed to big version selection - Closes #2228, Closes #2229 2018-04-07 22:09:19 +02:00
Janrupf
38ed0c2a1f NOISSUE Fixed ftb downloads always latest version 2018-04-07 00:46:49 +02:00
Lukas Haigner
9b7564e967 NOISSUE Fixed progress dialog 2018-04-07 00:46:49 +02:00
Petr Mrázek
15926b2b4a NOISSUE make FTB pack selection fancier 2018-04-06 21:59:04 +02:00
Petr Mrázek
6323aae56f NOISSUE move FtbListModel to where it is actually used 2018-04-06 21:04:34 +02:00
Petr Mrázek
c9c6037f50 NOISSUE fix Qt moc warning 2018-04-06 21:02:37 +02:00
Janrupf
97b74ef56a NOISSUE Fixed code for PR 2018-04-06 19:39:12 +02:00
Janrupf
df6e66101c NOISSUE Added 3rd party pack support 2018-04-05 19:33:31 +02:00
Janrupf
bbd523acb8 NOISSUE Added FTB Pack logos to chooser and fixed some missing includes 2018-04-02 23:02:33 +02:00
Janrupf
67d2f283da NOISSUE Fixed compilation error, but needs to be revisited 2018-04-02 23:02:33 +02:00
Petr Mrázek
4530d9064b NOISSUE fix latent bugs in RWStorage 2018-04-02 22:58:54 +02:00
Petr Mrázek
3406335cd8 GH-2219 fix crash and bad view scaling in Java VersionSelectDialog 2018-04-02 22:58:02 +02:00
Petr Mrázek
c9832d0d86 GH-2208 fix FTB pack download caching 2018-03-29 20:55:47 +02:00
Petr Mrázek
fa8f19b046 GH-2211 fix crash in first launch wizard 2018-03-29 20:41:16 +02:00
Petr Mrázek
370bbada87 NOISSUE polish the new instance UI a bit more 2018-03-28 21:57:41 +02:00
Petr Mrázek
1ef416cb56 NOISSUE add pointless fun things, because. 2018-03-28 00:51:24 +02:00
Petr Mrázek
b46a34d0ae NOISSUE make vanilla refresh button work 2018-03-27 23:19:29 +02:00
Petr Mrázek
6188c577e3 NOISSUE fix page container spacing in UIs 2018-03-27 22:21:41 +02:00
Petr Mrázek
40a30b67f4 NOISSUE save the new instance dialog geometry when the dialog is accepted 2018-03-27 22:02:57 +02:00
Petr Mrázek
12b304ea73 NOISSUE fix crash in NewInstanceDialog 2018-03-27 21:05:41 +02:00
Petr Mrázek
8e44ab2338 NOISSUE redo new instance dialog 2018-03-27 09:25:36 +02:00
Petr Mrázek
4c7ea0f99a NOISSUE explain the custom commands better 2018-03-26 22:08:55 +02:00
Petr Mrázek
a1c713811c NOISSUE preserve minecraft.jar while migrating Legacy instances
It can be manually modded. It must be preserved when it's the only jar around.
2018-03-23 23:39:18 +01:00
Petr Mrázek
106155dd62 NOISSUE move modpack platform related files to 'modplatform' subfolders 2018-03-16 23:33:58 +01:00
Petr Mrázek
303842a19e NOISSUE Add Konami Code
Fun little thing for hiding extra debug options in the future.
2018-03-15 09:27:45 +01:00
Petr Mrázek
ea151ca9d4 NOISSUE pre-fill analytics ID and paste.ee API key for all new builds
This means custom builds now get the option of using analytics and
log upload without users having to fill in IDs.
2018-03-13 08:02:11 +01:00
Petr Mrázek
6e69370fbf NOISSUE disable useless broken unit test to fix win32 and osx64 builds 2018-03-13 01:41:33 +01:00
Petr Mrázek
82208be49e NOISSUE add linux distro name and release stats to analytics
Hopefully this can serve as some sort of guideline for focusing
effort towards the right distro packages to make.
2018-03-13 00:28:51 +01:00
Petr Mrázek
b497aee920 NOISSUE update logo in README.md 2018-03-12 22:53:10 +01:00
Janrupf
0812e3a87b NOISSUE Fixed code for PR 2018-03-12 15:09:07 +01:00
Janrupf
b8ca36372b GH-2124 First complete implementation, installing is working now! GH-2172 Added sorting 2018-03-11 19:30:47 +01:00
Petr Mrázek
2d295d5afb Merge pull request #1911 from maxnordlund/patch-1
Mention casing in ISSUE_TEMPLATE
2018-03-01 17:25:09 +01:00
Janrupf
ab3fe74c97 Added FTB pack selection ad download, WIP 2018-02-28 19:43:56 +01:00
Petr Mrázek
1a43f28297 NOISSUE do not censor player name in logs 2018-02-18 19:27:01 +01:00
Petr Mrázek
093dd22826 GH-2154 Ignore 'hidden' flag of insttance folders 2018-02-18 16:08:11 +01:00
Petr Mrázek
06ccff0f47 GH-2150 Separate Java settings UI used in the wizard into a widget 2018-02-18 12:39:35 +01:00
Petr Mrázek
65bca65489 GH-2150 Split out custom commands into a custom widget
Now it is used from a global page and from a sub-page in the instance settings.
2018-02-17 00:57:54 +01:00
Petr Mrázek
a7957f24ba GH-2134 Totally overengineer skin upload input validation
* It autocorrects local paths and file:// URLs to valid local paths.
* It recognizes other URL schemes as 'remote' and will show an error for them.
* The error dialogs have been fixed (they all had titles and content swapped).
2018-02-15 00:40:23 +01:00
Petr Mrázek
2ea22d407d GH-604 use the same font for 'Other Logs' as for the main log
This doesn't mean coloring, just the same font and font size.
2018-02-14 21:37:32 +01:00
Petr Mrázek
22b32fce12 GH-2143 Switch Mojang status icons to current set of services
This also removes the Web and Account web icons, because they are simply
not relevant to MultiMC (it does not use those). You can always
check their status by opening them in a browser.
2018-02-13 21:35:05 +01:00
Petr Mrázek
2c219df061 NOISSUE clean up and fix win32 includes in FileSystem implementation 2018-02-11 01:29:43 +01:00
Petr Mrázek
604295e6d5 NOISSUE fix some warnings 2018-02-11 01:21:32 +01:00
Petr Mrázek
f259e9f727 NOISSUE update copyright dates 2018-02-11 00:40:01 +01:00
Petr Mrázek
38e669dbf5 NOISSUE change FS::updateTimestamp to work with directories too, use it to fix icon issues on macOS 2018-02-11 00:35:56 +01:00
Petr Mrázek
ca11765436 NOISSUE split logo into 'logo' and 'multimc' 2018-02-10 19:13:59 +01:00
Petr Mrázek
354ed2e7f0 NOISSUE version the discord variant of the icon 2018-02-10 16:13:05 +01:00
Petr Mrázek
a08afb8172 NOISSUE update the MultiMC logo 2018-02-10 16:09:14 +01:00
Petr Mrázek
2dac9d02d8 GH-2134 fix model selection when uploading a skin 2018-02-10 11:54:59 +01:00
Petr Mrázek
b3fb437f8e NOISSUE When changing version of or installing a package, remove customized version 2018-02-09 00:54:17 +01:00
Petr Mrázek
f115bdf5b8 NOISSUE make visualvm work with relative paths (inside the MultiMC folder) 2018-02-06 01:51:22 +01:00
Petr Mrázek
41aef8414a NOISSUE add an 'open folder' button to the icon dialog 2018-02-05 02:01:12 +01:00
Petr Mrázek
83649b5d52 NOISSUE implement basic search in Other Logs page 2018-02-05 01:40:38 +01:00
Petr Mrázek
595d157180 NOISSUE fix changelog typo 2018-01-30 00:20:27 +01:00
Petr Mrázek
3843a4ddb9 NOISSUE change version to 0.6.1 and update the changelog 2018-01-29 23:56:03 +01:00
Petr Mrázek
088e8e0eff NOISSUE remove unneeded URL fixing code and fix up the exception thrown by invalid Flame URLs 2018-01-29 00:47:18 +01:00
Petr Mrázek
418251bd86 NOISSUE use a variable for binary build definitions 2018-01-28 19:04:39 +01:00
Petr Mrázek
0bcb24502e GH-2119 Update group view scrollbar when the size of rows doesn't change
Previously, it would only update when you resize the window horizontally
enough to change the number of icons that fit in a row.
2018-01-28 02:04:47 +01:00
Petr Mrázek
3277b820a7 NOISSUE fix the macOS bundle utilities problem 2018-01-27 23:53:10 +01:00
Petr Mrázek
d66ae206dd NOISSUE move bundle utilities magic back to the application folder
This may fix macOS issues?
2018-01-27 21:59:06 +01:00
Petr Mrázek
cd55674b36 NOISSUE do not install .a files for shared libraries on Windows 2018-01-27 02:42:27 +01:00
Petr Mrázek
166e5a03d6 NOISSUE rearrange build system
* Added install commands to the libraries instead of force installing files
* Most of the application cmake stuff moved to top level
* RPATH should now be set/cleared correctly
* Contains a fix for GH-1780
2018-01-27 02:00:20 +01:00
Petr Mrázek
0c2e2094ee NOISSUE clean up download redirects and handle their errors as fatal 2018-01-22 03:09:00 +01:00
Petr Mrázek
c33b4e252f NOISSUE fix bad redirect URLs provided by the curse CDN
MultiMC now parses the HTTP Location header in a (more) tolerant mode.
2018-01-21 03:49:54 +01:00
Samuel Rakitničan
0942867ecc GH-2103 Make /usr/local the default prefix for lin-system
/usr/local is a sane default since /usr is meant to be used by packages.
2018-01-18 12:09:03 +01:00
Petr Mrázek
e8336babad NOISSUE fix the installation prefix mess 2018-01-16 07:08:59 +01:00
Petr Mrázek
d0e58acd84 GH-2103 add suggested changes from the pull request 2018-01-16 06:48:10 +01:00
Carl Philipp Reh
360d877abf GH-2103 Take CMAKE_INSTALL_PREFIX into account in MULTIMC_JARS_LOCATION
When installing MultiMC with the lin-system layout and specifying an
install prefix that is not the empty string, then MultiMC looks for its
Jars in the wrong location. Fix this by appending CMAKE_INSTALL_PREFIX.
2018-01-16 06:47:58 +01:00
srakitnican
7ea1d68244 GH-2102 multimc.desktop: Remove deprecated entries 2018-01-14 08:23:17 +01:00
Petr Mrázek
b7f28a92d4 NOISSUE change default install layout on linux to lin-nodeps
This avoids issues with included bundle utilities on certain systems
and is a step in the intended direction (not distributing dependencies).
2018-01-08 01:42:50 +01:00
Petr Mrázek
1dbc4e16f7 NOISSUE remove the jar-modded jar after the instance finishes 2018-01-08 00:59:47 +01:00
Petr Mrázek
0636c03d7c GH-2087 remove the revert to vanilla functionality, add file download button to version page 2018-01-05 04:26:46 +01:00
Petr Mrázek
ee341b78ba GH-2089 update wording of the instance delete confirmation dialog 2018-01-04 04:16:31 +01:00
Petr Mrázek
9510a1bbf2 NOISSUE stop logging process environment and MC launch script 2018-01-04 03:50:05 +01:00
Petr Mrázek
03a93b8e4a NOISSUE Add mention of Nikki's and Dries007's work on CurseMeta 2018-01-03 02:04:23 +01:00
Max Nordlund
9583d0d8ee Mention casing in ISSUE_TEMPLATE 2017-06-17 23:31:21 +02:00
697 changed files with 62145 additions and 54760 deletions

View File

@@ -1,4 +1,4 @@
UseTab: true UseTab: false
IndentWidth: 4 IndentWidth: 4
TabWidth: 4 TabWidth: 4
ConstructorInitializerIndentWidth: 4 ConstructorInitializerIndentWidth: 4

View File

@@ -9,6 +9,7 @@ Before submitting this issue, please make sure you have:
- We provide support for MultiMC, not your modpack. Problems with your modpack will be ignored. - We provide support for MultiMC, not your modpack. Problems with your modpack will be ignored.
4. Given the issue a descriptive title. 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?!". - 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. 5. Place all information below the ---- of lines.
- It makes the issue look pretty - It makes the issue look pretty

2
.gitignore vendored
View File

@@ -15,6 +15,8 @@ CMakeLists.txt.user
CMakeLists.txt.user.* CMakeLists.txt.user.*
/.project /.project
/.settings /.settings
/.idea
cmake-build-*/
# Build dirs # Build dirs
build build

View File

@@ -13,7 +13,7 @@ endif()
project(MultiMC) project(MultiMC)
enable_testing() enable_testing()
######## Set CMake options ######## ##################################### Set CMake options #####################################
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
@@ -32,12 +32,51 @@ set(CMAKE_C_STANDARD_REQUIRED true)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
include(GenerateExportHeader) include(GenerateExportHeader)
set(CMAKE_CXX_FLAGS " -Wall -D_GLIBCXX_USE_CXX11_ABI=0 ${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS " -Wall -pedantic -Werror -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 -O3 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS}")
if(UNIX AND APPLE) if(UNIX AND APPLE)
set(CMAKE_CXX_FLAGS " -stdlib=libc++ ${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS " -stdlib=libc++ ${CMAKE_CXX_FLAGS}")
endif() endif()
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type")
##################################### 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 version numbers ########
set(MultiMC_VERSION_MAJOR 0)
set(MultiMC_VERSION_MINOR 6)
set(MultiMC_VERSION_HOTFIX 5)
# Build number
set(MultiMC_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.")
# Channel list URL
set(MultiMC_CHANLIST_URL "" CACHE STRING "URL for the channel list.")
# Notification URL
set(MultiMC_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
# 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")
# Google analytics ID
set(MultiMC_ANALYTICS_ID "UA-87731965-2" CACHE STRING "ID you can get from Google analytics")
#### Check the current Git commit and branch
include(GetGitRevisionDescription)
get_git_head_revision(MultiMC_GIT_REFSPEC MultiMC_GIT_COMMIT)
message(STATUS "Git commit: ${MultiMC_GIT_COMMIT}")
message(STATUS "Git refspec: ${MultiMC_GIT_REFSPEC}")
set(MultiMC_RELEASE_VERSION_NAME "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}")
#### Custom target to just print the version.
add_custom_target(version echo "Version: ${MultiMC_RELEASE_VERSION_NAME}")
################################ 3rd Party Libs ################################ ################################ 3rd Party Libs ################################
@@ -62,6 +101,133 @@ if (Qt5_POSITION_INDEPENDENT_CODE)
SET(CMAKE_POSITION_INDEPENDENT_CODE ON) SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif() 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)
if(MultiMC_LAYOUT STREQUAL "auto")
if(UNIX AND APPLE)
set(MultiMC_LAYOUT_REAL "mac-bundle")
elseif(UNIX)
set(MultiMC_LAYOUT_REAL "lin-nodeps")
elseif(WIN32)
set(MultiMC_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})
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")
set(BUNDLE_DEST_DIR ".")
# Apps to bundle
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.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")
# 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")
# Add the icon
install(FILES application/resources/MultiMC.icns DESTINATION ${RESOURCES_DEST_DIR})
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")
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")
# install as bundle with no dependencies included
set(INSTALL_BUNDLE "nodeps")
# 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-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")
set(BINARY_DEST_DIR ".")
set(LIBRARY_DEST_DIR ".")
set(PLUGIN_DEST_DIR ".")
set(BUNDLE_DEST_DIR ".")
set(RESOURCES_DEST_DIR ".")
set(JARS_DEST_DIR "jars")
# Apps to bundle
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.exe")
# 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")
else()
message(FATAL_ERROR "No sensible install layout set.")
endif()
################################ Included Libs ################################ ################################ Included Libs ################################
include(ExternalProject) include(ExternalProject)
@@ -71,6 +237,7 @@ option(NBT_BUILD_SHARED "Build NBT shared library" ON)
option(NBT_USE_ZLIB "Build NBT library with zlib support" OFF) option(NBT_USE_ZLIB "Build NBT library with zlib support" OFF)
option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests. option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests.
set(NBT_NAME MultiMC_nbt++) set(NBT_NAME MultiMC_nbt++)
set(NBT_DEST_DIR ${LIBRARY_DEST_DIR})
add_subdirectory(libraries/libnbtplusplus) add_subdirectory(libraries/libnbtplusplus)
add_subdirectory(libraries/ganalytics) # google analytics library add_subdirectory(libraries/ganalytics) # google analytics library
@@ -91,4 +258,5 @@ add_subdirectory(libraries/classparser) # google analytics library
add_subdirectory(api/logic) add_subdirectory(api/logic)
add_subdirectory(api/gui) add_subdirectory(api/gui)
# NOTE: this must always be last to appease the CMake deity of quirky install command evaluation order.
add_subdirectory(application) add_subdirectory(application)

View File

@@ -1,6 +1,6 @@
# MultiMC # MultiMC
Copyright 2012-2017 MultiMC Contributors Copyright 2012-2019 MultiMC Contributors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
@@ -225,3 +225,30 @@
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# lionshead
Code has been taken from https://github.com/natefoo/lionshead and loosely
translated to C++ laced with Qt.
MIT License
Copyright (c) 2017 Nate Coraor
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.

View File

@@ -1,5 +1,5 @@
<p align="center"> <p align="center">
<img src="http://i.imgur.com/IOcTf8M.png" alt="MultiMC logo"/> <img src="https://avatars2.githubusercontent.com/u/5411890" alt="MultiMC logo"/>
</p> </p>
MultiMC 5 MultiMC 5
@@ -27,9 +27,7 @@ We use [Clang Format](http://clang.llvm.org/docs/ClangFormat.html) to format the
## Translations ## Translations
Translations can be done either directly in the [translations repository](https://github.com/MultiMC/MultiMC5) or using our [translation server](http://translate.multimc.org). For more details, see: [Translating-MultiMC](https://github.com/MultiMC/MultiMC5/wiki/Translating-MultiMC). 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).
Currently, MultiMC is [![Translation Status](http://translate.multimc.org/widgets/multimc/-/shields-badge.svg)](http://translate.multimc.org/engage/multimc/?utm_source=widget)
## Forking/Redistributing ## 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. 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.
@@ -40,7 +38,7 @@ Apache covers reasonable use for the name - a mention of the project's origins i
## License ## License
Copyright &copy; 2013-2017 MultiMC Contributors Copyright &copy; 2013-2019 MultiMC Contributors
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). 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).

View File

@@ -21,8 +21,14 @@ set_target_properties(MultiMC_gui PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBI
generate_export_header(MultiMC_gui) generate_export_header(MultiMC_gui)
# Link # Link
target_link_libraries(MultiMC_gui MultiMC_iconfix MultiMC_logic) target_link_libraries(MultiMC_gui MultiMC_iconfix MultiMC_logic Qt5::Gui)
qt5_use_modules(MultiMC_gui Gui)
# Mark and export headers # Mark and export headers
target_include_directories(MultiMC_gui PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") 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

@@ -34,4 +34,4 @@ namespace DesktopServices
* Open the URL, most likely in a browser. Maybe. * Open the URL, most likely in a browser. Maybe.
*/ */
MULTIMC_GUI_EXPORT bool openUrl(const QUrl &url); MULTIMC_GUI_EXPORT bool openUrl(const QUrl &url);
}; }

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -187,8 +187,7 @@ Qt::DropActions IconList::supportedDropActions() const
return Qt::CopyAction; return Qt::CopyAction;
} }
bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
const QModelIndex &parent)
{ {
if (action == Qt::IgnoreAction) if (action == Qt::IgnoreAction)
return true; return true;
@@ -261,7 +260,7 @@ void IconList::installIcons(const QStringList &iconFiles)
QString target = FS::PathCombine(m_dir.dirName(), fileinfo.fileName()); QString target = FS::PathCombine(m_dir.dirName(), fileinfo.fileName());
QString suffix = fileinfo.suffix(); QString suffix = fileinfo.suffix();
if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg") if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif")
continue; continue;
if (!QFile::copy(file, target)) if (!QFile::copy(file, target))
@@ -269,6 +268,17 @@ void IconList::installIcons(const QStringList &iconFiles)
} }
} }
void IconList::installIcon(const QString &file, const QString &name)
{
QFileInfo fileinfo(file);
if(!fileinfo.isReadable() || !fileinfo.isFile())
return;
QString target = FS::PathCombine(m_dir.dirName(), name);
QFile::copy(file, target);
}
bool IconList::iconFileExists(const QString &key) const bool IconList::iconFileExists(const QString &key) const
{ {
auto iconEntry = icon(key); auto iconEntry = icon(key);
@@ -401,4 +411,9 @@ int IconList::getIconIndex(const QString &key) const
return -1; return -1;
} }
QString IconList::getDirectory() const
{
return m_dir.absolutePath();
}
//#include "IconList.moc" //#include "IconList.moc"

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@ public:
QIcon getIcon(const QString &key) const; QIcon getIcon(const QString &key) const;
int getIconIndex(const QString &key) const; int getIconIndex(const QString &key) const;
QString getDirectory() const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@@ -55,6 +56,7 @@ public:
virtual Qt::ItemFlags flags(const QModelIndex &index) const override; virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
void installIcons(const QStringList &iconFiles) override; void installIcons(const QStringList &iconFiles) override;
void installIcon(const QString &file, const QString &name) override;
const MMCIcon * icon(const QString &key) const; const MMCIcon * icon(const QString &key) const;
@@ -78,7 +80,7 @@ protected slots:
void fileChanged(const QString &path); void fileChanged(const QString &path);
void SettingChanged(const Setting & setting, QVariant value); void SettingChanged(const Setting & setting, QVariant value);
private: private:
std::shared_ptr<QFileSystemWatcher> m_watcher; shared_qobject_ptr<QFileSystemWatcher> m_watcher;
bool is_watching; bool is_watching;
QMap<QString, int> name_index; QMap<QString, int> name_index;
QVector<MMCIcon> icons; QVector<MMCIcon> icons;

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -102,3 +102,17 @@ void MMCIcon::replace(IconType new_type, const QString& key)
m_images[new_type].filename = QString(); m_images[new_type].filename = QString();
m_images[new_type].key = key; m_images[new_type].key = key;
} }
QString MMCIcon::getFilePath() const
{
if(m_current_type == IconType::ToBeDeleted){
return QString();
}
return m_images[m_current_type].filename;
}
bool MMCIcon::isBuiltIn() const
{
return m_current_type == IconType::Builtin;
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -46,4 +46,6 @@ struct MULTIMC_GUI_EXPORT MMCIcon
void remove(IconType rm_type); void remove(IconType rm_type);
void replace(IconType new_type, QIcon icon, QString path = QString()); void replace(IconType new_type, QIcon icon, QString path = QString());
void replace(IconType new_type, const QString &key); void replace(IconType new_type, const QString &key);
bool isBuiltIn() const;
QString getFilePath() const;
}; };

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -102,13 +102,6 @@ void BaseInstance::invalidate()
qDebug() << "Instance" << id() << "has been invalidated."; qDebug() << "Instance" << id() << "has been invalidated.";
} }
void BaseInstance::nuke()
{
changeStatus(Status::Gone);
qDebug() << "Instance" << id() << "has been deleted by MultiMC.";
FS::deletePath(instanceRoot());
}
void BaseInstance::changeStatus(BaseInstance::Status newStatus) void BaseInstance::changeStatus(BaseInstance::Status newStatus)
{ {
Status status = currentStatus(); Status status = currentStatus();
@@ -182,11 +175,6 @@ QString BaseInstance::instanceRoot() const
return m_rootDir; return m_rootDir;
} }
InstancePtr BaseInstance::getSharedPtr()
{
return shared_from_this();
}
SettingsObjectPtr BaseInstance::settings() const SettingsObjectPtr BaseInstance::settings() const
{ {
return m_settings; return m_settings;
@@ -214,31 +202,6 @@ void BaseInstance::setLastLaunch(qint64 val)
emit propertiesChanged(this); emit propertiesChanged(this);
} }
void BaseInstance::setGroupInitial(QString val)
{
if(m_group == val)
{
return;
}
m_group = val;
emit propertiesChanged(this);
}
void BaseInstance::setGroupPost(QString val)
{
if(m_group == val)
{
return;
}
setGroupInitial(val);
emit groupChanged();
}
QString BaseInstance::group() const
{
return m_group;
}
void BaseInstance::setNotes(QString val) void BaseInstance::setNotes(QString val)
{ {
//FIXME: if no change, do not set. setting involves saving a file. //FIXME: if no change, do not set. setting involves saving a file.
@@ -276,7 +239,7 @@ QString BaseInstance::name() const
QString BaseInstance::windowTitle() const QString BaseInstance::windowTitle() const
{ {
return "MultiMC: " + name(); return "MultiMC: " + name().replace(QRegExp("[ \n\r\t]+"), " ");
} }
// FIXME: why is this here? move it to MinecraftInstance!!! // FIXME: why is this here? move it to MinecraftInstance!!!
@@ -285,23 +248,7 @@ QStringList BaseInstance::extraArguments() const
return Commandline::splitArgs(settings()->get("JvmArgs").toString()); return Commandline::splitArgs(settings()->get("JvmArgs").toString());
} }
std::shared_ptr<LaunchTask> BaseInstance::getLaunchTask() shared_qobject_ptr<LaunchTask> BaseInstance::getLaunchTask()
{ {
return m_launchProcess; return m_launchProcess;
} }
void BaseInstance::setProvider(BaseInstanceProvider* provider)
{
// only once.
assert(!m_provider);
if(m_provider)
{
qWarning() << "Provider set more than once for instance" << id();
}
m_provider = provider;
}
BaseInstanceProvider* BaseInstance::provider() const
{
return m_provider;
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -38,7 +38,6 @@ class QDir;
class Task; class Task;
class LaunchTask; class LaunchTask;
class BaseInstance; class BaseInstance;
class BaseInstanceProvider;
// pointer for lazy people // pointer for lazy people
typedef std::shared_ptr<BaseInstance> InstancePtr; typedef std::shared_ptr<BaseInstance> InstancePtr;
@@ -69,13 +68,8 @@ public:
/// virtual destructor to make sure the destruction is COMPLETE /// virtual destructor to make sure the destruction is COMPLETE
virtual ~BaseInstance() {}; virtual ~BaseInstance() {};
virtual void init() = 0;
virtual void saveNow() = 0; virtual void saveNow() = 0;
/// nuke thoroughly - deletes the instance contents, notifies the list/model which is
/// responsible of cleaning up the husk
void nuke();
/*** /***
* the instance has been invalidated - it is no longer tracked by MultiMC for some reason, * the instance has been invalidated - it is no longer tracked by MultiMC for some reason,
* but it has not necessarily been deleted. * but it has not necessarily been deleted.
@@ -93,15 +87,18 @@ public:
int64_t totalTimePlayed() const; int64_t totalTimePlayed() const;
void resetTimePlayed(); void resetTimePlayed();
void setProvider(BaseInstanceProvider * provider);
BaseInstanceProvider * provider() const;
/// get the type of this instance /// get the type of this instance
QString instanceType() const; QString instanceType() const;
/// Path to the instance's root directory. /// Path to the instance's root directory.
QString instanceRoot() const; QString instanceRoot() const;
/// Path to the instance's game root directory.
virtual QString gameRoot() const
{
return instanceRoot();
}
QString name() const; QString name() const;
void setName(QString val); void setName(QString val);
@@ -114,10 +111,6 @@ public:
QString notes() const; QString notes() const;
void setNotes(QString val); void setNotes(QString val);
QString group() const;
void setGroupInitial(QString val);
void setGroupPost(QString val);
QString getPreLaunchCommand(); QString getPreLaunchCommand();
QString getPostExitCommand(); QString getPostExitCommand();
QString getWrapperCommand(); QString getWrapperCommand();
@@ -141,8 +134,6 @@ public:
/// Sets the last launched time to 'val' milliseconds since epoch /// Sets the last launched time to 'val' milliseconds since epoch
void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch()); void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch());
InstancePtr getSharedPtr();
/*! /*!
* \brief Gets this instance's settings object. * \brief Gets this instance's settings object.
* This settings object stores instance-specific settings. * This settings object stores instance-specific settings.
@@ -154,10 +145,10 @@ public:
virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) = 0; virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) = 0;
/// returns a valid launcher (task container) /// returns a valid launcher (task container)
virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0; virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0;
/// returns the current launch task (if any) /// returns the current launch task (if any)
std::shared_ptr<LaunchTask> getLaunchTask(); shared_qobject_ptr<LaunchTask> getLaunchTask();
/*! /*!
* Create envrironment variables for running the instance * Create envrironment variables for running the instance
@@ -247,12 +238,8 @@ signals:
* \brief Signal emitted when properties relevant to the instance view change * \brief Signal emitted when properties relevant to the instance view change
*/ */
void propertiesChanged(BaseInstance *inst); void propertiesChanged(BaseInstance *inst);
/*!
* \brief Signal emitted when groups are affected in any way
*/
void groupChanged();
void launchTaskChanged(std::shared_ptr<LaunchTask>); void launchTaskChanged(shared_qobject_ptr<LaunchTask>);
void runningStatusChanged(bool running); void runningStatusChanged(bool running);
@@ -263,13 +250,11 @@ protected slots:
protected: /* data */ protected: /* data */
QString m_rootDir; QString m_rootDir;
QString m_group;
SettingsObjectPtr m_settings; SettingsObjectPtr m_settings;
// InstanceFlags m_flags; // InstanceFlags m_flags;
bool m_isRunning = false; bool m_isRunning = false;
std::shared_ptr<LaunchTask> m_launchProcess; shared_qobject_ptr<LaunchTask> m_launchProcess;
QDateTime m_timeStarted; QDateTime m_timeStarted;
BaseInstanceProvider * m_provider = nullptr;
private: /* data */ private: /* data */
Status m_status = Status::Present; Status m_status = Status::Present;
@@ -278,6 +263,6 @@ private: /* data */
bool m_hasBrokenVersion = false; bool m_hasBrokenVersion = false;
}; };
Q_DECLARE_METATYPE(std::shared_ptr<BaseInstance>) Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>)
//Q_DECLARE_METATYPE(BaseInstance::InstanceFlag) //Q_DECLARE_METATYPE(BaseInstance::InstanceFlag)
//Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags) //Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags)

View File

@@ -1,57 +0,0 @@
#pragma once
#include <QObject>
#include <QString>
#include "BaseInstance.h"
#include "settings/SettingsObject.h"
#include "multimc_logic_export.h"
using InstanceId = QString;
using InstanceLocator = std::pair<InstancePtr, int>;
enum class InstCreateError
{
NoCreateError = 0,
NoSuchVersion,
UnknownCreateError,
InstExists,
CantCreateDir
};
class MULTIMC_LOGIC_EXPORT BaseInstanceProvider : public QObject
{
Q_OBJECT
public:
BaseInstanceProvider(SettingsObjectPtr settings) : m_globalSettings(settings)
{
// nil
}
public:
virtual QList<InstanceId> discoverInstances() = 0;
virtual InstancePtr loadInstance(const InstanceId &id) = 0;
virtual void loadGroupList() = 0;
virtual void saveGroupList() = 0;
virtual QString getStagedInstancePath()
{
return QString();
}
virtual bool commitStagedInstance(const QString & path, const QString& instanceName, const QString & groupName)
{
return false;
}
virtual bool destroyStagingPath(const QString & path)
{
return true;
}
signals:
// Emit this when the list of provided instances changed
void instancesChanged();
// Emit when the set of groups your provider supplies changes.
void groupsChanged(QSet<QString> groups);
protected:
SettingsObjectPtr m_globalSettings;
};

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -8,21 +8,14 @@ set(CORE_SOURCES
BaseInstaller.cpp BaseInstaller.cpp
BaseVersionList.h BaseVersionList.h
BaseVersionList.cpp BaseVersionList.cpp
InstanceCreationTask.h
InstanceCreationTask.cpp
InstanceCopyTask.h
InstanceCopyTask.cpp
InstanceImportTask.h
InstanceImportTask.cpp
InstanceList.h InstanceList.h
InstanceList.cpp InstanceList.cpp
InstanceTask.h
InstanceTask.cpp
LoggedProcess.h LoggedProcess.h
LoggedProcess.cpp LoggedProcess.cpp
MessageLevel.cpp MessageLevel.cpp
MessageLevel.h MessageLevel.h
BaseInstanceProvider.h
FolderInstanceProvider.h
FolderInstanceProvider.cpp
BaseVersion.h BaseVersion.h
BaseInstance.h BaseInstance.h
BaseInstance.cpp BaseInstance.cpp
@@ -32,6 +25,14 @@ set(CORE_SOURCES
MMCStrings.h MMCStrings.h
MMCStrings.cpp 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 # Use tracking separate from memory management
Usable.h Usable.h
@@ -42,6 +43,10 @@ set(CORE_SOURCES
Env.h Env.h
Env.cpp Env.cpp
# String filters
Filter.h
Filter.cpp
# JSON parsing helpers # JSON parsing helpers
Json.h Json.h
Json.cpp Json.cpp
@@ -177,9 +182,11 @@ set(NEWS_SOURCES
# Icon interface # Icon interface
set(ICONS_SOURCES set(ICONS_SOURCES
# News System # Icons System and related code
icons/IIconList.h icons/IIconList.h
icons/IIconList.cpp icons/IIconList.cpp
icons/IconUtils.h
icons/IconUtils.cpp
) )
# Minecraft services status checker # Minecraft services status checker
@@ -206,6 +213,8 @@ set(MINECRAFT_SOURCES
minecraft/auth/flows/RefreshTask.cpp minecraft/auth/flows/RefreshTask.cpp
minecraft/auth/flows/ValidateTask.h minecraft/auth/flows/ValidateTask.h
minecraft/auth/flows/ValidateTask.cpp minecraft/auth/flows/ValidateTask.cpp
minecraft/gameoptions/GameOptions.h
minecraft/gameoptions/GameOptions.cpp
minecraft/update/AssetUpdateTask.h minecraft/update/AssetUpdateTask.h
minecraft/update/AssetUpdateTask.cpp minecraft/update/AssetUpdateTask.cpp
minecraft/update/FMLLibrariesTask.cpp minecraft/update/FMLLibrariesTask.cpp
@@ -228,6 +237,8 @@ set(MINECRAFT_SOURCES
minecraft/launch/LauncherPartLaunch.h minecraft/launch/LauncherPartLaunch.h
minecraft/launch/PrintInstanceInfo.cpp minecraft/launch/PrintInstanceInfo.cpp
minecraft/launch/PrintInstanceInfo.h minecraft/launch/PrintInstanceInfo.h
minecraft/launch/ReconstructAssets.cpp
minecraft/launch/ReconstructAssets.h
minecraft/legacy/LegacyModList.h minecraft/legacy/LegacyModList.h
minecraft/legacy/LegacyModList.cpp minecraft/legacy/LegacyModList.cpp
minecraft/legacy/LegacyInstance.h minecraft/legacy/LegacyInstance.h
@@ -270,19 +281,13 @@ set(MINECRAFT_SOURCES
minecraft/VersionFilterData.cpp minecraft/VersionFilterData.cpp
minecraft/Mod.h minecraft/Mod.h
minecraft/Mod.cpp minecraft/Mod.cpp
minecraft/ModList.h minecraft/SimpleModList.h
minecraft/ModList.cpp minecraft/SimpleModList.cpp
minecraft/World.h minecraft/World.h
minecraft/World.cpp minecraft/World.cpp
minecraft/WorldList.h minecraft/WorldList.h
minecraft/WorldList.cpp minecraft/WorldList.cpp
# Flame
minecraft/flame/PackManifest.h
minecraft/flame/PackManifest.cpp
minecraft/flame/FileResolvingTask.h
minecraft/flame/FileResolvingTask.cpp
# Assets # Assets
minecraft/AssetsUtils.h minecraft/AssetsUtils.h
minecraft/AssetsUtils.cpp minecraft/AssetsUtils.cpp
@@ -313,8 +318,8 @@ add_unit_test(Library
) )
# FIXME: shares data with FileSystem test # FIXME: shares data with FileSystem test
add_unit_test(ModList add_unit_test(SimpleModList
SOURCES minecraft/ModList_test.cpp SOURCES minecraft/SimpleModList_test.cpp
DATA testdata DATA testdata
LIBS MultiMC_logic LIBS MultiMC_logic
) )
@@ -388,6 +393,8 @@ add_unit_test(JavaVersion
set(TRANSLATIONS_SOURCES set(TRANSLATIONS_SOURCES
translations/TranslationsModel.h translations/TranslationsModel.h
translations/TranslationsModel.cpp translations/TranslationsModel.cpp
translations/POTranslator.h
translations/POTranslator.cpp
) )
set(TOOLS_SOURCES set(TOOLS_SOURCES
@@ -418,6 +425,26 @@ set(META_SOURCES
meta/Index.h 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
)
add_unit_test(Index add_unit_test(Index
SOURCES meta/Index_test.cpp SOURCES meta/Index_test.cpp
LIBS MultiMC_logic LIBS MultiMC_logic
@@ -446,8 +473,12 @@ set(LOGIC_SOURCES
${TOOLS_SOURCES} ${TOOLS_SOURCES}
${META_SOURCES} ${META_SOURCES}
${ICONS_SOURCES} ${ICONS_SOURCES}
${FTB_SOURCES}
${FLAME_SOURCES}
) )
message(STATUS "FOO! ${LOGIC_SOURCES}")
add_library(MultiMC_logic SHARED ${LOGIC_SOURCES}) add_library(MultiMC_logic SHARED ${LOGIC_SOURCES})
set_target_properties(MultiMC_logic PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1) set_target_properties(MultiMC_logic PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1)
@@ -455,7 +486,14 @@ generate_export_header(MultiMC_logic)
# Link # Link
target_link_libraries(MultiMC_logic xz-embedded MultiMC_unpack200 systeminfo MultiMC_quazip MultiMC_classparser ${NBT_NAME} ${ZLIB_LIBRARIES}) target_link_libraries(MultiMC_logic xz-embedded MultiMC_unpack200 systeminfo MultiMC_quazip MultiMC_classparser ${NBT_NAME} ${ZLIB_LIBRARIES})
qt5_use_modules(MultiMC_logic Core Xml Network Concurrent) target_link_libraries(MultiMC_logic Qt5::Core Qt5::Xml Qt5::Network Qt5::Concurrent)
# Mark and export headers # Mark and export headers
target_include_directories(MultiMC_logic PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" PRIVATE "${ZLIB_INCLUDE_DIRS}") 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,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Authors: Orochimarufan <orochimarufan.x3@gmail.com> * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
* *

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Authors: Orochimarufan <orochimarufan.x3@gmail.com> * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
* *

View File

@@ -20,6 +20,7 @@ struct Env::Private
std::shared_ptr<IIconList> m_iconlist; std::shared_ptr<IIconList> m_iconlist;
shared_qobject_ptr<Meta::Index> m_metadataIndex; shared_qobject_ptr<Meta::Index> m_metadataIndex;
QString m_jarsPath; QString m_jarsPath;
QSet<QString> m_features;
}; };
static Env * instance; static Env * instance;
@@ -95,6 +96,7 @@ void Env::initHttpMetaCache()
m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath()); m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath()); m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
m_metacache->addBase("general", QDir("cache").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("skins", QDir("accounts/skins").absolutePath());
m_metacache->addBase("root", QDir::currentPath()); m_metacache->addBase("root", QDir::currentPath());
m_metacache->addBase("translations", QDir("translations").absolutePath()); m_metacache->addBase("translations", QDir("translations").absolutePath());
@@ -178,3 +180,30 @@ void Env::setJarsPath(const QString& path)
{ {
d->m_jarsPath = 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

@@ -54,6 +54,12 @@ public:
QString getJarsPath(); QString getJarsPath();
void setJarsPath(const QString & path); 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: protected:
Private * d; Private * d;
}; };

View File

@@ -0,0 +1,43 @@
#pragma once
template <typename T>
inline void clamp(T& current, T min, T max)
{
if (current < min)
{
current = min;
}
else if(current > max)
{
current = max;
}
}
// List of numbers from min to max. Next is exponent times bigger than previous.
class ExponentialSeries
{
public:
ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2)
{
m_current = m_min = min;
m_max = max;
m_exponent = exponent;
}
void reset()
{
m_current = m_min;
}
unsigned operator()()
{
unsigned retval = m_current;
m_current *= m_exponent;
clamp(m_current, m_min, m_max);
return retval;
}
unsigned m_current;
unsigned m_min;
unsigned m_max;
unsigned m_exponent;
};

View File

@@ -3,11 +3,27 @@
#include "FileSystem.h" #include "FileSystem.h"
#include <QDir> #include <QDir>
#include <QFile>
#include <QSaveFile> #include <QSaveFile>
#include <QFileInfo> #include <QFileInfo>
#include <QDebug> #include <QDebug>
#include <QUrl> #include <QUrl>
#include <QStandardPaths> #include <QStandardPaths>
#include <QTextStream>
#if defined Q_OS_WIN32
#include <windows.h>
#include <string>
#include <sys/utime.h>
#include <winnls.h>
#include <shobjidl.h>
#include <objbase.h>
#include <objidl.h>
#include <shlguid.h>
#include <shlobj.h>
#else
#include <utime.h>
#endif
namespace FS { namespace FS {
@@ -62,21 +78,13 @@ QByteArray read(const QString &filename)
bool updateTimestamp(const QString& filename) bool updateTimestamp(const QString& filename)
{ {
QFile file(filename); #ifdef Q_OS_WIN32
if (!file.exists()) std::wstring filename_utf_16 = filename.toStdWString();
{ return (_wutime64(filename_utf_16.c_str(), nullptr) == 0);
return false; #else
} QByteArray filenameBA = QFile::encodeName(filename);
if (!file.open(QIODevice::ReadWrite)) return (utime(filenameBA.data(), nullptr) == 0);
{ #endif
return false;
}
const quint64 size = file.size();
file.seek(size);
file.write( QByteArray(1, '0') );
file.resize(size);
return true;
} }
bool ensureFilePathExists(QString filenamepath) bool ensureFilePathExists(QString filenamepath)
@@ -163,11 +171,6 @@ bool copy::operator()(const QString &offset)
return true; return true;
} }
#if defined Q_OS_WIN32
#include <windows.h>
#include <string>
#endif
bool deletePath(QString path) bool deletePath(QString path)
{ {
bool OK = true; bool OK = true;
@@ -291,7 +294,7 @@ QString NormalizePath(QString path)
} }
} }
QString badFilenameChars = "\"\\/?<>:*|!"; QString badFilenameChars = "\"\\/?<>:*|!+\r\n";
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith) QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
{ {
@@ -301,6 +304,10 @@ QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
{ {
string[i] = replaceWith; string[i] = replaceWith;
} }
else if(string[i] > 127) // non ASCII
{
string[i] = replaceWith;
}
} }
return string; return string;
} }
@@ -337,21 +344,9 @@ bool checkProblemticPathJava(QDir folder)
return pathfoldername.contains("!", Qt::CaseInsensitive); return pathfoldername.contains("!", Qt::CaseInsensitive);
} }
#include <QStandardPaths>
#include <QFile>
#include <QTextStream>
// Win32 crap // Win32 crap
#if defined Q_OS_WIN #if defined Q_OS_WIN
#include <windows.h>
#include <winnls.h>
#include <shobjidl.h>
#include <objbase.h>
#include <objidl.h>
#include <shlguid.h>
#include <shlobj.h>
bool called_coinit = false; bool called_coinit = false;
HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args) HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args)
@@ -365,7 +360,7 @@ HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args)
if (!SUCCEEDED(hres)) if (!SUCCEEDED(hres))
{ {
qWarning("Failed to initialize COM. Error 0x%08X", hres); qWarning("Failed to initialize COM. Error 0x%08lX", hres);
return hres; return hres;
} }
} }

31
api/logic/Filter.cpp Normal file
View File

@@ -0,0 +1,31 @@
#include "Filter.h"
Filter::~Filter(){}
ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern){}
ContainsFilter::~ContainsFilter(){}
bool ContainsFilter::accepts(const QString& value)
{
return value.contains(pattern);
}
ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern){}
ExactFilter::~ExactFilter(){}
bool ExactFilter::accepts(const QString& value)
{
return value == pattern;
}
RegexpFilter::RegexpFilter(const QString& regexp, bool invert)
:invert(invert)
{
pattern.setPattern(regexp);
pattern.optimize();
}
RegexpFilter::~RegexpFilter(){}
bool RegexpFilter::accepts(const QString& value)
{
auto match = pattern.match(value);
bool matched = match.hasMatch();
return invert ? (!matched) : (matched);
}

44
api/logic/Filter.h Normal file
View File

@@ -0,0 +1,44 @@
#pragma once
#include <QString>
#include <QRegularExpression>
#include "multimc_logic_export.h"
class MULTIMC_LOGIC_EXPORT Filter
{
public:
virtual ~Filter();
virtual bool accepts(const QString & value) = 0;
};
class MULTIMC_LOGIC_EXPORT ContainsFilter: public Filter
{
public:
ContainsFilter(const QString &pattern);
virtual ~ContainsFilter();
bool accepts(const QString & value) override;
private:
QString pattern;
};
class MULTIMC_LOGIC_EXPORT ExactFilter: public Filter
{
public:
ExactFilter(const QString &pattern);
virtual ~ExactFilter();
bool accepts(const QString & value) override;
private:
QString pattern;
};
class MULTIMC_LOGIC_EXPORT RegexpFilter: public Filter
{
public:
RegexpFilter(const QString &regexp, bool invert);
virtual ~RegexpFilter();
bool accepts(const QString & value) override;
private:
QRegularExpression pattern;
bool invert = false;
};

View File

@@ -1,487 +0,0 @@
#include "FolderInstanceProvider.h"
#include "settings/INISettingsObject.h"
#include "FileSystem.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/legacy/LegacyInstance.h"
#include "NullInstance.h"
#include <QDir>
#include <QDirIterator>
#include <QFileSystemWatcher>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QUuid>
#include <QTimer>
const static int GROUP_FILE_FORMAT_VERSION = 1;
struct WatchLock
{
WatchLock(QFileSystemWatcher * watcher, const QString& instDir)
: m_watcher(watcher), m_instDir(instDir)
{
m_watcher->removePath(m_instDir);
}
~WatchLock()
{
m_watcher->addPath(m_instDir);
}
QFileSystemWatcher * m_watcher;
QString m_instDir;
};
FolderInstanceProvider::FolderInstanceProvider(SettingsObjectPtr settings, const QString& instDir)
: BaseInstanceProvider(settings)
{
// Create aand normalize path
if (!QDir::current().exists(instDir))
{
QDir::current().mkpath(instDir);
}
// NOTE: canonicalPath requires the path to exist. Do not move this above the creation block!
m_instDir = QDir(instDir).canonicalPath();
m_watcher = new QFileSystemWatcher(this);
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &FolderInstanceProvider::instanceDirContentsChanged);
m_watcher->addPath(m_instDir);
}
QList< InstanceId > FolderInstanceProvider::discoverInstances()
{
QList<InstanceId> out;
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable, QDirIterator::FollowSymlinks);
while (iter.hasNext())
{
QString subDir = iter.next();
QFileInfo dirInfo(subDir);
if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists())
continue;
// if it is a symlink, ignore it if it goes to the instance folder
if(dirInfo.isSymLink())
{
QFileInfo targetInfo(dirInfo.symLinkTarget());
QFileInfo instDirInfo(m_instDir);
if(targetInfo.canonicalPath() == instDirInfo.canonicalFilePath())
{
qDebug() << "Ignoring symlink" << subDir << "that leads into the instances folder";
continue;
}
}
auto id = dirInfo.fileName();
out.append(id);
qDebug() << "Found instance ID" << id;
}
return out;
}
InstancePtr FolderInstanceProvider::loadInstance(const InstanceId& id)
{
if(!m_groupsLoaded)
{
loadGroupList();
}
auto instanceRoot = FS::PathCombine(m_instDir, id);
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg"));
InstancePtr inst;
instanceSettings->registerSetting("InstanceType", "Legacy");
QString inst_type = instanceSettings->get("InstanceType").toString();
if (inst_type == "OneSix" || inst_type == "Nostalgia")
{
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
}
else if (inst_type == "Legacy")
{
inst.reset(new LegacyInstance(m_globalSettings, instanceSettings, instanceRoot));
}
else
{
inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot));
}
inst->init();
inst->setProvider(this);
auto iter = groupMap.find(id);
if (iter != groupMap.end())
{
inst->setGroupInitial((*iter));
}
connect(inst.get(), &BaseInstance::groupChanged, this, &FolderInstanceProvider::groupChanged);
qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot();
return inst;
}
void FolderInstanceProvider::saveGroupList()
{
WatchLock foo(m_watcher, m_instDir);
QString groupFileName = m_instDir + "/instgroups.json";
QMap<QString, QSet<QString>> reverseGroupMap;
for (auto iter = groupMap.begin(); iter != groupMap.end(); iter++)
{
QString id = iter.key();
QString group = iter.value();
if (group.isEmpty())
continue;
if (!reverseGroupMap.count(group))
{
QSet<QString> set;
set.insert(id);
reverseGroupMap[group] = set;
}
else
{
QSet<QString> &set = reverseGroupMap[group];
set.insert(id);
}
}
QJsonObject toplevel;
toplevel.insert("formatVersion", QJsonValue(QString("1")));
QJsonObject groupsArr;
for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++)
{
auto list = iter.value();
auto name = iter.key();
QJsonObject groupObj;
QJsonArray instanceArr;
groupObj.insert("hidden", QJsonValue(QString("false")));
for (auto item : list)
{
instanceArr.append(QJsonValue(item));
}
groupObj.insert("instances", instanceArr);
groupsArr.insert(name, groupObj);
}
toplevel.insert("groups", groupsArr);
QJsonDocument doc(toplevel);
try
{
FS::write(groupFileName, doc.toJson());
}
catch(FS::FileSystemException & e)
{
qCritical() << "Failed to write instance group file :" << e.cause();
}
}
void FolderInstanceProvider::loadGroupList()
{
QSet<QString> groupSet;
QString groupFileName = m_instDir + "/instgroups.json";
// if there's no group file, fail
if (!QFileInfo(groupFileName).exists())
return;
QByteArray jsonData;
try
{
jsonData = FS::read(groupFileName);
}
catch (FS::FileSystemException & e)
{
qCritical() << "Failed to read instance group file :" << e.cause();
return;
}
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error);
// if the json was bad, fail
if (error.error != QJsonParseError::NoError)
{
qCritical() << QString("Failed to parse instance group file: %1 at offset %2")
.arg(error.errorString(), QString::number(error.offset))
.toUtf8();
return;
}
// if the root of the json wasn't an object, fail
if (!jsonDoc.isObject())
{
qWarning() << "Invalid group file. Root entry should be an object.";
return;
}
QJsonObject rootObj = jsonDoc.object();
// Make sure the format version matches, otherwise fail.
if (rootObj.value("formatVersion").toVariant().toInt() != GROUP_FILE_FORMAT_VERSION)
return;
// Get the groups. if it's not an object, fail
if (!rootObj.value("groups").isObject())
{
qWarning() << "Invalid group list JSON: 'groups' should be an object.";
return;
}
groupMap.clear();
// Iterate through all the groups.
QJsonObject groupMapping = rootObj.value("groups").toObject();
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++)
{
QString groupName = iter.key();
// If not an object, complain and skip to the next one.
if (!iter.value().isObject())
{
qWarning() << QString("Group '%1' in the group list should "
"be an object.")
.arg(groupName)
.toUtf8();
continue;
}
QJsonObject groupObj = iter.value().toObject();
if (!groupObj.value("instances").isArray())
{
qWarning() << QString("Group '%1' in the group list is invalid. "
"It should contain an array "
"called 'instances'.")
.arg(groupName)
.toUtf8();
continue;
}
// keep a list/set of groups for choosing
groupSet.insert(groupName);
// Iterate through the list of instances in the group.
QJsonArray instancesArray = groupObj.value("instances").toArray();
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end();
iter2++)
{
groupMap[(*iter2).toString()] = groupName;
}
}
m_groupsLoaded = true;
emit groupsChanged(groupSet);
}
void FolderInstanceProvider::groupChanged()
{
// save the groups. save all of them.
auto instance = (BaseInstance *) QObject::sender();
auto id = instance->id();
groupMap[id] = instance->group();
emit groupsChanged({instance->group()});
saveGroupList();
}
void FolderInstanceProvider::instanceDirContentsChanged(const QString& path)
{
Q_UNUSED(path);
emit instancesChanged();
}
void FolderInstanceProvider::on_InstFolderChanged(const Setting &setting, QVariant value)
{
QString newInstDir = QDir(value.toString()).canonicalPath();
if(newInstDir != m_instDir)
{
if(m_groupsLoaded)
{
saveGroupList();
}
m_instDir = newInstDir;
m_groupsLoaded = false;
emit instancesChanged();
}
}
template <typename T>
static void clamp(T& current, T min, T max)
{
if (current < min)
{
current = min;
}
else if(current > max)
{
current = max;
}
}
// List of numbers from min to max. Next is exponent times bigger than previous.
class ExponentialSeries
{
public:
ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2)
{
m_current = m_min = min;
m_max = max;
m_exponent = exponent;
}
void reset()
{
m_current = m_min;
}
unsigned operator()()
{
unsigned retval = m_current;
m_current *= m_exponent;
clamp(m_current, m_min, m_max);
return retval;
}
unsigned m_current;
unsigned m_min;
unsigned m_max;
unsigned m_exponent;
};
/*
* WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows.
* Basically, it starts messing things up while MultiMC is extracting/creating instances
* and causes that horrible failure that is NTFS to lock files in place because they are open.
*/
class FolderInstanceStaging : public Task
{
Q_OBJECT
const unsigned minBackoff = 1;
const unsigned maxBackoff = 16;
public:
FolderInstanceStaging (
FolderInstanceProvider * parent,
Task * child,
const QString & stagingPath,
const QString& instanceName,
const QString& groupName )
: backoff(minBackoff, maxBackoff)
{
m_parent = parent;
m_child.reset(child);
connect(child, &Task::succeeded, this, &FolderInstanceStaging::childSucceded);
connect(child, &Task::failed, this, &FolderInstanceStaging::childFailed);
connect(child, &Task::status, this, &FolderInstanceStaging::setStatus);
connect(child, &Task::progress, this, &FolderInstanceStaging::setProgress);
m_instanceName = instanceName;
m_groupName = groupName;
m_stagingPath = stagingPath;
m_backoffTimer.setSingleShot(true);
connect(&m_backoffTimer, &QTimer::timeout, this, &FolderInstanceStaging::childSucceded);
}
protected:
virtual void executeTask() override
{
m_child->start();
}
QStringList warnings() const override
{
return m_child->warnings();
}
private slots:
void childSucceded()
{
unsigned sleepTime = backoff();
if(m_parent->commitStagedInstance(m_stagingPath, m_instanceName, m_groupName))
{
emitSucceeded();
return;
}
// we actually failed, retry?
if(sleepTime == maxBackoff)
{
emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
return;
}
qDebug() << "Failed to commit instance" << m_instanceName << "Initiating backoff:" << sleepTime;
m_backoffTimer.start(sleepTime * 500);
}
void childFailed(const QString & reason)
{
m_parent->destroyStagingPath(m_stagingPath);
emitFailed(reason);
}
private:
ExponentialSeries backoff;
QString m_stagingPath;
FolderInstanceProvider * m_parent;
unique_qobject_ptr<Task> m_child;
QString m_instanceName;
QString m_groupName;
QTimer m_backoffTimer;
};
#include "InstanceImportTask.h"
Task * FolderInstanceProvider::zipImportTask(const QUrl sourceUrl, const QString& instName, const QString& instGroup, const QString& instIcon)
{
auto stagingPath = getStagedInstancePath();
auto task = new InstanceImportTask(m_globalSettings, sourceUrl, stagingPath, instName, instIcon, instGroup);
return new FolderInstanceStaging(this, task, stagingPath, instName, instGroup);
}
#include "InstanceCreationTask.h"
Task * FolderInstanceProvider::creationTask(BaseVersionPtr version, const QString& instName, const QString& instGroup, const QString& instIcon)
{
auto stagingPath = getStagedInstancePath();
auto task = new InstanceCreationTask(m_globalSettings, stagingPath, version, instName, instIcon, instGroup);
return new FolderInstanceStaging(this, task, stagingPath, instName, instGroup);
}
#include "InstanceCopyTask.h"
Task * FolderInstanceProvider::copyTask(const InstancePtr& oldInstance, const QString& instName, const QString& instGroup, const QString& instIcon, bool copySaves)
{
auto stagingPath = getStagedInstancePath();
auto task = new InstanceCopyTask(m_globalSettings, stagingPath, oldInstance, instName, instIcon, instGroup, copySaves);
return new FolderInstanceStaging(this, task, stagingPath, instName, instGroup);
}
// FIXME: find a better place for this
#include "minecraft/legacy/LegacyUpgradeTask.h"
Task * FolderInstanceProvider::legacyUpgradeTask(const InstancePtr& oldInstance)
{
auto stagingPath = getStagedInstancePath();
QString newName = tr("%1 (Migrated)").arg(oldInstance->name());
auto task = new LegacyUpgradeTask(m_globalSettings, stagingPath, oldInstance, newName);
return new FolderInstanceStaging(this, task, stagingPath, newName, oldInstance->group());
}
QString FolderInstanceProvider::getStagedInstancePath()
{
QString key = QUuid::createUuid().toString();
QString relPath = FS::PathCombine("_MMC_TEMP/" , key);
QDir rootPath(m_instDir);
auto path = FS::PathCombine(m_instDir, relPath);
if(!rootPath.mkpath(relPath))
{
return QString();
}
return path;
}
bool FolderInstanceProvider::commitStagedInstance(const QString& path, const QString& instanceName, const QString& groupName)
{
QDir dir;
QString instID = FS::DirNameFromString(instanceName, m_instDir);
{
WatchLock lock(m_watcher, m_instDir);
QString destination = FS::PathCombine(m_instDir, instID);
if(!dir.rename(path, destination))
{
qWarning() << "Failed to move" << path << "to" << destination;
return false;
}
groupMap[instID] = groupName;
emit groupsChanged({groupName});
emit instancesChanged();
}
saveGroupList();
return true;
}
bool FolderInstanceProvider::destroyStagingPath(const QString& keyPath)
{
return FS::deletePath(keyPath);
}
#include "FolderInstanceProvider.moc"

View File

@@ -1,66 +0,0 @@
#pragma once
#include "BaseInstanceProvider.h"
#include <QMap>
class QFileSystemWatcher;
class MULTIMC_LOGIC_EXPORT FolderInstanceProvider : public BaseInstanceProvider
{
Q_OBJECT
public:
FolderInstanceProvider(SettingsObjectPtr settings, const QString & instDir);
public:
/// used by InstanceList to @return a list of plausible IDs to probe for
QList<InstanceId> discoverInstances() override;
/// used by InstanceList to (re)load an instance with the given @id.
InstancePtr loadInstance(const InstanceId& id) override;
// create instance in this provider
Task * creationTask(BaseVersionPtr version, const QString &instName, const QString &instGroup, const QString &instIcon);
// copy instance to this provider
Task * copyTask(const InstancePtr &oldInstance, const QString& instName, const QString& instGroup, const QString& instIcon, bool copySaves);
// import zipped instance into this provider
Task * zipImportTask(const QUrl sourceUrl, const QString &instName, const QString &instGroup, const QString &instIcon);
// migrate an instance to the current format
Task * legacyUpgradeTask(const InstancePtr& oldInstance);
/**
* Create a new empty staging area for instance creation and @return a path/key top commit it later.
* Used by instance manipulation tasks.
*/
QString getStagedInstancePath() override;
/**
* Commit the staging area given by @keyPath to the provider - used when creation succeeds.
* Used by instance manipulation tasks.
*/
bool commitStagedInstance(const QString & keyPath, const QString& instanceName, const QString & groupName) override;
/**
* Destroy a previously created staging area given by @keyPath - used when creation fails.
* Used by instance manipulation tasks.
*/
bool destroyStagingPath(const QString & keyPath) override;
public slots:
void on_InstFolderChanged(const Setting &setting, QVariant value);
private slots:
void instanceDirContentsChanged(const QString &path);
void groupChanged();
private: /* methods */
void loadGroupList() override;
void saveGroupList() override;
private: /* data */
QString m_instDir;
QFileSystemWatcher * m_watcher;
QMap<QString, QString> groupMap;
bool m_groupsLoaded = false;
};

View File

@@ -1,19 +1,13 @@
#include "InstanceCopyTask.h" #include "InstanceCopyTask.h"
#include "BaseInstanceProvider.h"
#include "settings/INISettingsObject.h" #include "settings/INISettingsObject.h"
#include "FileSystem.h" #include "FileSystem.h"
#include "NullInstance.h" #include "NullInstance.h"
#include "pathmatcher/RegexpMatcher.h" #include "pathmatcher/RegexpMatcher.h"
#include <QtConcurrentRun> #include <QtConcurrentRun>
InstanceCopyTask::InstanceCopyTask(SettingsObjectPtr settings, const QString & stagingPath, InstancePtr origInstance, const QString& instName, const QString& instIcon, const QString& instGroup, bool copySaves) InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves)
{ {
m_globalSettings = settings;
m_stagingPath = stagingPath;
m_origInstance = origInstance; m_origInstance = origInstance;
m_instName = instName;
m_instIcon = instIcon;
m_instGroup = instGroup;
if(!copySaves) if(!copySaves)
{ {

View File

@@ -9,16 +9,13 @@
#include "settings/SettingsObject.h" #include "settings/SettingsObject.h"
#include "BaseVersion.h" #include "BaseVersion.h"
#include "BaseInstance.h" #include "BaseInstance.h"
#include "InstanceTask.h"
class MULTIMC_LOGIC_EXPORT InstanceCopyTask : public InstanceTask
class BaseInstanceProvider;
class MULTIMC_LOGIC_EXPORT InstanceCopyTask : public Task
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit InstanceCopyTask(SettingsObjectPtr settings, const QString & stagingPath, InstancePtr origInstance, const QString &instName, explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves);
const QString &instIcon, const QString &instGroup, bool copySaves);
protected: protected:
//! Entry point for tasks. //! Entry point for tasks.
@@ -27,15 +24,8 @@ protected:
void copyAborted(); void copyAborted();
private: /* data */ private: /* data */
SettingsObjectPtr m_globalSettings;
InstancePtr m_origInstance; InstancePtr m_origInstance;
QString m_instName;
QString m_instIcon;
QString m_instGroup;
QString m_stagingPath;
QFuture<bool> m_copyFuture; QFuture<bool> m_copyFuture;
QFutureWatcher<bool> m_copyFutureWatcher; QFutureWatcher<bool> m_copyFutureWatcher;
std::unique_ptr<IPathMatcher> m_matcher; std::unique_ptr<IPathMatcher> m_matcher;
}; };

View File

@@ -1,5 +1,4 @@
#include "InstanceCreationTask.h" #include "InstanceCreationTask.h"
#include "BaseInstanceProvider.h"
#include "settings/INISettingsObject.h" #include "settings/INISettingsObject.h"
#include "FileSystem.h" #include "FileSystem.h"
@@ -7,14 +6,8 @@
#include "minecraft/MinecraftInstance.h" #include "minecraft/MinecraftInstance.h"
#include "minecraft/ComponentList.h" #include "minecraft/ComponentList.h"
InstanceCreationTask::InstanceCreationTask(SettingsObjectPtr settings, const QString & stagingPath, BaseVersionPtr version, InstanceCreationTask::InstanceCreationTask(BaseVersionPtr version)
const QString& instName, const QString& instIcon, const QString& instGroup)
{ {
m_globalSettings = settings;
m_stagingPath = stagingPath;
m_instName = instName;
m_instIcon = instIcon;
m_instGroup = instGroup;
m_version = version; m_version = version;
} }
@@ -32,7 +25,6 @@ void InstanceCreationTask::executeTask()
components->setComponentVersion("net.minecraft", m_version->descriptor(), true); components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
inst.setName(m_instName); inst.setName(m_instName);
inst.setIconKey(m_instIcon); inst.setIconKey(m_instIcon);
inst.init();
instanceSettings->resumeSave(); instanceSettings->resumeSave();
} }
emitSucceeded(); emitSucceeded();

View File

@@ -6,24 +6,18 @@
#include <QUrl> #include <QUrl>
#include "settings/SettingsObject.h" #include "settings/SettingsObject.h"
#include "BaseVersion.h" #include "BaseVersion.h"
#include "InstanceTask.h"
class MULTIMC_LOGIC_EXPORT InstanceCreationTask : public Task class MULTIMC_LOGIC_EXPORT InstanceCreationTask : public InstanceTask
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit InstanceCreationTask(SettingsObjectPtr settings, const QString & stagingPath, BaseVersionPtr version, const QString &instName, explicit InstanceCreationTask(BaseVersionPtr version);
const QString &instIcon, const QString &instGroup);
protected: protected:
//! Entry point for tasks. //! Entry point for tasks.
virtual void executeTask() override; virtual void executeTask() override;
private: /* data */ private: /* data */
SettingsObjectPtr m_globalSettings;
QString m_stagingPath;
BaseVersionPtr m_version; BaseVersionPtr m_version;
QString m_instName;
QString m_instIcon;
QString m_instGroup;
}; };

View File

@@ -1,30 +1,24 @@
#include "InstanceImportTask.h" #include "InstanceImportTask.h"
#include "BaseInstance.h" #include "BaseInstance.h"
#include "BaseInstanceProvider.h"
#include "FileSystem.h" #include "FileSystem.h"
#include "Env.h" #include "Env.h"
#include "MMCZip.h" #include "MMCZip.h"
#include "NullInstance.h" #include "NullInstance.h"
#include "settings/INISettingsObject.h" #include "settings/INISettingsObject.h"
#include "icons/IIconList.h" #include "icons/IIconList.h"
#include "icons/IconUtils.h"
#include <QtConcurrentRun> #include <QtConcurrentRun>
// FIXME: this does not belong here, it's Minecraft/Flame specific // FIXME: this does not belong here, it's Minecraft/Flame specific
#include "minecraft/MinecraftInstance.h" #include "minecraft/MinecraftInstance.h"
#include "minecraft/ComponentList.h" #include "minecraft/ComponentList.h"
#include "minecraft/flame/FileResolvingTask.h" #include "modplatform/flame/FileResolvingTask.h"
#include "minecraft/flame/PackManifest.h" #include "modplatform/flame/PackManifest.h"
#include "Json.h" #include "Json.h"
InstanceImportTask::InstanceImportTask(SettingsObjectPtr settings, const QUrl sourceUrl, const QString & stagingPath, InstanceImportTask::InstanceImportTask(const QUrl sourceUrl)
const QString &instName, const QString &instIcon, const QString &instGroup)
{ {
m_globalSettings = settings;
m_sourceUrl = sourceUrl; m_sourceUrl = sourceUrl;
m_stagingPath = stagingPath;
m_instName = instName;
m_instIcon = instIcon;
m_instGroup = instGroup;
} }
void InstanceImportTask::executeTask() void InstanceImportTask::executeTask()
@@ -194,7 +188,7 @@ void InstanceImportTask::processFlame()
Flame::loadManifest(pack, configPath); Flame::loadManifest(pack, configPath);
QFile::remove(configPath); QFile::remove(configPath);
} }
catch (JSONValidationError & e) catch (const JSONValidationError &e)
{ {
emitFailed(tr("Could not understand pack manifest:\n") + e.cause()); emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
return; return;
@@ -281,7 +275,6 @@ void InstanceImportTask::processFlame()
instance.setIconKey("flame"); instance.setIconKey("flame");
} }
} }
instance.init();
QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods"); QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
QFileInfo jarmodsInfo(jarmodsPath); QFileInfo jarmodsInfo(jarmodsPath);
if(jarmodsInfo.isDir()) if(jarmodsInfo.isDir())
@@ -327,6 +320,7 @@ void InstanceImportTask::processFlame()
case Flame::File::Type::SingleFile: case Flame::File::Type::SingleFile:
case Flame::File::Type::Mod: case Flame::File::Type::Mod:
{ {
qDebug() << "Will download" << result.url << "to" << path;
auto dl = Net::Download::makeFile(result.url, path); auto dl = Net::Download::makeFile(result.url, path);
m_filesNetJob->addNetAction(dl); m_filesNetJob->addNetAction(dl);
break; break;
@@ -400,8 +394,9 @@ void InstanceImportTask::processMultiMC()
else else
{ {
m_instIcon = instance.iconKey(); m_instIcon = instance.iconKey();
auto importIconPath = FS::PathCombine(instance.instanceRoot(), m_instIcon + ".png");
if (QFile::exists(importIconPath)) auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
if (!importIconPath.isNull() && QFile::exists(importIconPath))
{ {
// import icon // import icon
auto iconList = ENV.icons(); auto iconList = ENV.icons();

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "tasks/Task.h" #include "InstanceTask.h"
#include "multimc_logic_export.h" #include "multimc_logic_export.h"
#include "net/NetJob.h" #include "net/NetJob.h"
#include <QUrl> #include <QUrl>
@@ -10,18 +10,16 @@
#include "QObjectPtr.h" #include "QObjectPtr.h"
class QuaZip; class QuaZip;
class BaseInstanceProvider;
namespace Flame namespace Flame
{ {
class FileResolvingTask; class FileResolvingTask;
} }
class MULTIMC_LOGIC_EXPORT InstanceImportTask : public Task class MULTIMC_LOGIC_EXPORT InstanceImportTask : public InstanceTask
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit InstanceImportTask(SettingsObjectPtr settings, const QUrl sourceUrl, const QString & stagingPath, const QString &instName, explicit InstanceImportTask(const QUrl sourceUrl);
const QString &instIcon, const QString &instGroup);
protected: protected:
//! Entry point for tasks. //! Entry point for tasks.
@@ -40,16 +38,11 @@ private slots:
void extractAborted(); void extractAborted();
private: /* data */ private: /* data */
SettingsObjectPtr m_globalSettings;
NetJobPtr m_filesNetJob; NetJobPtr m_filesNetJob;
shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver; shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver;
QUrl m_sourceUrl; QUrl m_sourceUrl;
QString m_archivePath; QString m_archivePath;
bool m_downloadRequired = false; bool m_downloadRequired = false;
QString m_instName;
QString m_instIcon;
QString m_instGroup;
QString m_stagingPath;
std::unique_ptr<QuaZip> m_packZip; std::unique_ptr<QuaZip> m_packZip;
QFuture<QStringList> m_extractFuture; QFuture<QStringList> m_extractFuture;
QFutureWatcher<QStringList> m_extractFutureWatcher; QFutureWatcher<QStringList> m_extractFutureWatcher;

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -14,22 +14,49 @@
*/ */
#include <QDir> #include <QDir>
#include <QDirIterator>
#include <QSet> #include <QSet>
#include <QFile> #include <QFile>
#include <QThread> #include <QThread>
#include <QTextStream> #include <QTextStream>
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include <QTimer>
#include <QDebug> #include <QDebug>
#include <QFileSystemWatcher>
#include <QUuid>
#include <QJsonArray>
#include <QJsonDocument>
#include "InstanceList.h" #include "InstanceList.h"
#include "BaseInstance.h" #include "BaseInstance.h"
#include "InstanceTask.h"
#include "settings/INISettingsObject.h"
#include "minecraft/legacy/LegacyInstance.h"
#include "NullInstance.h"
#include "minecraft/MinecraftInstance.h"
#include "FileSystem.h"
#include "ExponentialSeries.h"
#include "WatchLock.h"
#include "FolderInstanceProvider.h" const static int GROUP_FILE_FORMAT_VERSION = 1;
InstanceList::InstanceList(QObject *parent) InstanceList::InstanceList(SettingsObjectPtr settings, const QString & instDir, QObject *parent)
: QAbstractListModel(parent) : QAbstractListModel(parent), m_globalSettings(settings)
{ {
resumeWatch(); resumeWatch();
// Create aand normalize path
if (!QDir::current().exists(instDir))
{
QDir::current().mkpath(instDir);
}
connect(this, &InstanceList::instancesChanged, this, &InstanceList::providerUpdated);
// NOTE: canonicalPath requires the path to exist. Do not move this above the creation block!
m_instDir = QDir(instDir).canonicalPath();
m_watcher = new QFileSystemWatcher(this);
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &InstanceList::instanceDirContentsChanged);
m_watcher->addPath(m_instDir);
} }
InstanceList::~InstanceList() InstanceList::~InstanceList()
@@ -68,6 +95,7 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const
{ {
return pdata->id(); return pdata->id();
} }
case Qt::EditRole:
case Qt::DisplayRole: case Qt::DisplayRole:
{ {
return pdata->name(); return pdata->name();
@@ -83,7 +111,7 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const
// HACK: see GroupView.h in gui! // HACK: see GroupView.h in gui!
case GroupRole: case GroupRole:
{ {
return pdata->group(); return getInstanceGroup(pdata->id());
} }
default: default:
break; break;
@@ -91,16 +119,85 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
} }
bool InstanceList::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (!index.isValid())
{
return false;
}
if(role != Qt::EditRole)
{
return false;
}
BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer());
auto newName = value.toString();
if(pdata->name() == newName)
{
return true;
}
pdata->setName(newName);
return true;
}
Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const
{ {
Qt::ItemFlags f; Qt::ItemFlags f;
if (index.isValid()) if (index.isValid())
{ {
f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable); f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
} }
return f; return f;
} }
GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
{
auto inst = getInstanceById(id);
if(!inst)
{
return GroupId();
}
auto iter = m_groupMap.find(inst->id());
if(iter != m_groupMap.end())
{
return *iter;
}
return GroupId();
}
void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
{
auto inst = getInstanceById(id);
if(!inst)
{
qDebug() << "Attempt to set a null instance's group";
return;
}
bool changed = false;
auto iter = m_groupMap.find(inst->id());
if(iter != m_groupMap.end())
{
if(*iter != name)
{
*iter = name;
changed = true;
}
}
else
{
changed = true;
m_groupMap[id] = name;
}
if(changed)
{
m_groups.insert(name);
auto idx = getInstIndex(inst.get());
emit dataChanged(index(idx), index(idx), {GroupRole});
saveGroupList();
}
}
QStringList InstanceList::getGroups() QStringList InstanceList::getGroups()
{ {
return m_groups.toList(); return m_groups.toList();
@@ -108,14 +205,52 @@ QStringList InstanceList::getGroups()
void InstanceList::deleteGroup(const QString& name) void InstanceList::deleteGroup(const QString& name)
{ {
bool removed = false;
qDebug() << "Delete group" << name;
for(auto & instance: m_instances) for(auto & instance: m_instances)
{ {
auto instGroupName = instance->group(); const auto & instID = instance->id();
auto instGroupName = getInstanceGroup(instID);
if(instGroupName == name) if(instGroupName == name)
{ {
instance->setGroupPost(QString()); m_groupMap.remove(instID);
qDebug() << "Remove" << instID << "from group" << name;
removed = true;
auto idx = getInstIndex(instance.get());
if(idx > 0)
{
emit dataChanged(index(idx), index(idx), {GroupRole});
} }
} }
}
if(removed)
{
saveGroupList();
}
}
void InstanceList::deleteInstance(const InstanceId& id)
{
auto inst = getInstanceById(id);
if(!inst)
{
qDebug() << "Cannot delete instance" << id << " No such instance is present.";
return;
}
if(m_groupMap.remove(id))
{
saveGroupList();
}
qDebug() << "Will delete instance" << id;
if(!FS::deletePath(inst->instanceRoot()))
{
qWarning() << "Deletion of instance" << id << "has not been completely successful ...";
return;
}
qDebug() << "Instance" << id << "has been deleted by MultiMC.";
} }
static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &list) static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &list)
@@ -135,50 +270,60 @@ static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &
return out; return out;
} }
InstanceList::InstListError InstanceList::loadList(bool complete) QList< InstanceId > InstanceList::discoverInstances()
{
qDebug() << "Discovering instances in" << m_instDir;
QList<InstanceId> out;
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks);
while (iter.hasNext())
{
QString subDir = iter.next();
QFileInfo dirInfo(subDir);
if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists())
continue;
// if it is a symlink, ignore it if it goes to the instance folder
if(dirInfo.isSymLink())
{
QFileInfo targetInfo(dirInfo.symLinkTarget());
QFileInfo instDirInfo(m_instDir);
if(targetInfo.canonicalPath() == instDirInfo.canonicalFilePath())
{
qDebug() << "Ignoring symlink" << subDir << "that leads into the instances folder";
continue;
}
}
auto id = dirInfo.fileName();
out.append(id);
qDebug() << "Found instance ID" << id;
}
instanceSet = out.toSet();
m_instancesProbed = true;
return out;
}
InstanceList::InstListError InstanceList::loadList()
{ {
auto existingIds = getIdMapping(m_instances); auto existingIds = getIdMapping(m_instances);
QList<InstancePtr> newList; QList<InstancePtr> newList;
auto processIds = [&](BaseInstanceProvider * provider, QList<InstanceId> ids) for(auto & id: discoverInstances())
{
for(auto & id: ids)
{ {
if(existingIds.contains(id)) if(existingIds.contains(id))
{ {
auto instPair = existingIds[id]; auto instPair = existingIds[id];
/*
auto & instPtr = instPair.first;
auto & instIdx = instPair.second;
*/
existingIds.remove(id); existingIds.remove(id);
qDebug() << "Should keep and soft-reload" << id; qDebug() << "Should keep and soft-reload" << id;
} }
else else
{ {
InstancePtr instPtr = provider->loadInstance(id); InstancePtr instPtr = loadInstance(id);
if(instPtr) if(instPtr)
{ {
newList.append(instPtr); newList.append(instPtr);
} }
} }
} }
};
if(complete)
{
for(auto & item: m_providers)
{
processIds(item.get(), item->discoverInstances());
}
}
else
{
for (auto & item: m_updatedProviders)
{
processIds(item, item->discoverInstances());
}
}
// TODO: looks like a general algorithm with a few specifics inserted. Do something about it. // TODO: looks like a general algorithm with a few specifics inserted. Do something about it.
if(!existingIds.isEmpty()) if(!existingIds.isEmpty())
@@ -205,10 +350,6 @@ InstanceList::InstListError InstanceList::loadList(bool complete)
for(auto & removedItem: deadList) for(auto & removedItem: deadList)
{ {
auto instPtr = removedItem.first; auto instPtr = removedItem.first;
if(!complete && !m_updatedProviders.contains(instPtr->provider()))
{
continue;
}
instPtr->invalidate(); instPtr->invalidate();
currentItem = removedItem.second; currentItem = removedItem.second;
if(back_bookmark == -1) if(back_bookmark == -1)
@@ -236,7 +377,7 @@ InstanceList::InstListError InstanceList::loadList(bool complete)
{ {
add(newList); add(newList);
} }
m_updatedProviders.clear(); m_dirty = false;
return NoError; return NoError;
} }
@@ -267,7 +408,7 @@ void InstanceList::resumeWatch()
return; return;
} }
m_watchLevel++; m_watchLevel++;
if(m_watchLevel > 0 && !m_updatedProviders.isEmpty()) if(m_watchLevel > 0 && m_dirty)
{ {
loadList(); loadList();
} }
@@ -280,31 +421,13 @@ void InstanceList::suspendWatch()
void InstanceList::providerUpdated() void InstanceList::providerUpdated()
{ {
auto provider = dynamic_cast<BaseInstanceProvider *>(QObject::sender()); m_dirty = true;
if(!provider)
{
qWarning() << "InstanceList::providerUpdated triggered by a non-provider";
return;
}
m_updatedProviders.insert(provider);
if(m_watchLevel == 1) if(m_watchLevel == 1)
{ {
loadList(); loadList();
} }
} }
void InstanceList::groupsPublished(QSet<QString> newGroups)
{
m_groups.unite(newGroups);
}
void InstanceList::addInstanceProvider(BaseInstanceProvider* provider)
{
connect(provider, &BaseInstanceProvider::instancesChanged, this, &InstanceList::providerUpdated);
connect(provider, &BaseInstanceProvider::groupsChanged, this, &InstanceList::groupsPublished);
m_providers.append(provider);
}
InstancePtr InstanceList::getInstanceById(QString instId) const InstancePtr InstanceList::getInstanceById(QString instId) const
{ {
if(instId.isEmpty()) if(instId.isEmpty())
@@ -345,3 +468,365 @@ void InstanceList::propertiesChanged(BaseInstance *inst)
emit dataChanged(index(i), index(i)); emit dataChanged(index(i), index(i));
} }
} }
InstancePtr InstanceList::loadInstance(const InstanceId& id)
{
if(!m_groupsLoaded)
{
loadGroupList();
}
auto instanceRoot = FS::PathCombine(m_instDir, id);
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg"));
InstancePtr inst;
instanceSettings->registerSetting("InstanceType", "Legacy");
QString inst_type = instanceSettings->get("InstanceType").toString();
if (inst_type == "OneSix" || inst_type == "Nostalgia")
{
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
}
else if (inst_type == "Legacy")
{
inst.reset(new LegacyInstance(m_globalSettings, instanceSettings, instanceRoot));
}
else
{
inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot));
}
qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot();
return inst;
}
void InstanceList::saveGroupList()
{
qDebug() << "Will save group list now.";
if(!m_instancesProbed)
{
qDebug() << "Group saving prevented because we don't know the full list of instances yet.";
return;
}
WatchLock foo(m_watcher, m_instDir);
QString groupFileName = m_instDir + "/instgroups.json";
QMap<QString, QSet<QString>> reverseGroupMap;
for (auto iter = m_groupMap.begin(); iter != m_groupMap.end(); iter++)
{
QString id = iter.key();
QString group = iter.value();
if (group.isEmpty())
continue;
if(!instanceSet.contains(id))
{
qDebug() << "Skipping saving missing instance" << id << "to groups list.";
continue;
}
if (!reverseGroupMap.count(group))
{
QSet<QString> set;
set.insert(id);
reverseGroupMap[group] = set;
}
else
{
QSet<QString> &set = reverseGroupMap[group];
set.insert(id);
}
}
QJsonObject toplevel;
toplevel.insert("formatVersion", QJsonValue(QString("1")));
QJsonObject groupsArr;
for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++)
{
auto list = iter.value();
auto name = iter.key();
QJsonObject groupObj;
QJsonArray instanceArr;
groupObj.insert("hidden", QJsonValue(QString("false")));
for (auto item : list)
{
instanceArr.append(QJsonValue(item));
}
groupObj.insert("instances", instanceArr);
groupsArr.insert(name, groupObj);
}
toplevel.insert("groups", groupsArr);
QJsonDocument doc(toplevel);
try
{
FS::write(groupFileName, doc.toJson());
qDebug() << "Group list saved.";
}
catch (const FS::FileSystemException &e)
{
qCritical() << "Failed to write instance group file :" << e.cause();
}
}
void InstanceList::loadGroupList()
{
qDebug() << "Will load group list now.";
QSet<QString> groupSet;
QString groupFileName = m_instDir + "/instgroups.json";
// if there's no group file, fail
if (!QFileInfo(groupFileName).exists())
return;
QByteArray jsonData;
try
{
jsonData = FS::read(groupFileName);
}
catch (const FS::FileSystemException &e)
{
qCritical() << "Failed to read instance group file :" << e.cause();
return;
}
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error);
// if the json was bad, fail
if (error.error != QJsonParseError::NoError)
{
qCritical() << QString("Failed to parse instance group file: %1 at offset %2")
.arg(error.errorString(), QString::number(error.offset))
.toUtf8();
return;
}
// if the root of the json wasn't an object, fail
if (!jsonDoc.isObject())
{
qWarning() << "Invalid group file. Root entry should be an object.";
return;
}
QJsonObject rootObj = jsonDoc.object();
// Make sure the format version matches, otherwise fail.
if (rootObj.value("formatVersion").toVariant().toInt() != GROUP_FILE_FORMAT_VERSION)
return;
// Get the groups. if it's not an object, fail
if (!rootObj.value("groups").isObject())
{
qWarning() << "Invalid group list JSON: 'groups' should be an object.";
return;
}
m_groupMap.clear();
// Iterate through all the groups.
QJsonObject groupMapping = rootObj.value("groups").toObject();
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++)
{
QString groupName = iter.key();
// If not an object, complain and skip to the next one.
if (!iter.value().isObject())
{
qWarning() << QString("Group '%1' in the group list should "
"be an object.")
.arg(groupName)
.toUtf8();
continue;
}
QJsonObject groupObj = iter.value().toObject();
if (!groupObj.value("instances").isArray())
{
qWarning() << QString("Group '%1' in the group list is invalid. "
"It should contain an array "
"called 'instances'.")
.arg(groupName)
.toUtf8();
continue;
}
// keep a list/set of groups for choosing
groupSet.insert(groupName);
// Iterate through the list of instances in the group.
QJsonArray instancesArray = groupObj.value("instances").toArray();
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); iter2++)
{
m_groupMap[(*iter2).toString()] = groupName;
}
}
m_groupsLoaded = true;
m_groups.unite(groupSet);
qDebug() << "Group list loaded.";
}
void InstanceList::instanceDirContentsChanged(const QString& path)
{
Q_UNUSED(path);
emit instancesChanged();
}
void InstanceList::on_InstFolderChanged(const Setting &setting, QVariant value)
{
QString newInstDir = QDir(value.toString()).canonicalPath();
if(newInstDir != m_instDir)
{
if(m_groupsLoaded)
{
saveGroupList();
}
m_instDir = newInstDir;
m_groupsLoaded = false;
emit instancesChanged();
}
}
class InstanceStaging : public Task
{
Q_OBJECT
const unsigned minBackoff = 1;
const unsigned maxBackoff = 16;
public:
InstanceStaging (
InstanceList * parent,
Task * child,
const QString & stagingPath,
const QString& instanceName,
const QString& groupName )
: backoff(minBackoff, maxBackoff)
{
m_parent = parent;
m_child.reset(child);
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceded);
connect(child, &Task::failed, this, &InstanceStaging::childFailed);
connect(child, &Task::status, this, &InstanceStaging::setStatus);
connect(child, &Task::progress, this, &InstanceStaging::setProgress);
m_instanceName = instanceName;
m_groupName = groupName;
m_stagingPath = stagingPath;
m_backoffTimer.setSingleShot(true);
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded);
}
virtual ~InstanceStaging() {};
// FIXME/TODO: add ability to abort during instance commit retries
bool abort() override
{
if(m_child && m_child->canAbort())
{
return m_child->abort();
}
return false;
}
bool canAbort() const override
{
if(m_child && m_child->canAbort())
{
return true;
}
return false;
}
protected:
virtual void executeTask() override
{
m_child->start();
}
QStringList warnings() const override
{
return m_child->warnings();
}
private slots:
void childSucceded()
{
unsigned sleepTime = backoff();
if(m_parent->commitStagedInstance(m_stagingPath, m_instanceName, m_groupName))
{
emitSucceeded();
return;
}
// we actually failed, retry?
if(sleepTime == maxBackoff)
{
emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
return;
}
qDebug() << "Failed to commit instance" << m_instanceName << "Initiating backoff:" << sleepTime;
m_backoffTimer.start(sleepTime * 500);
}
void childFailed(const QString & reason)
{
m_parent->destroyStagingPath(m_stagingPath);
emitFailed(reason);
}
private:
/*
* WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows.
* Basically, it starts messing things up while MultiMC is extracting/creating instances
* and causes that horrible failure that is NTFS to lock files in place because they are open.
*/
ExponentialSeries backoff;
QString m_stagingPath;
InstanceList * m_parent;
unique_qobject_ptr<Task> m_child;
QString m_instanceName;
QString m_groupName;
QTimer m_backoffTimer;
};
Task * InstanceList::wrapInstanceTask(InstanceTask * task)
{
auto stagingPath = getStagedInstancePath();
task->setStagingPath(stagingPath);
task->setParentSettings(m_globalSettings);
return new InstanceStaging(this, task, stagingPath, task->name(), task->group());
}
QString InstanceList::getStagedInstancePath()
{
QString key = QUuid::createUuid().toString();
QString relPath = FS::PathCombine("_MMC_TEMP/" , key);
QDir rootPath(m_instDir);
auto path = FS::PathCombine(m_instDir, relPath);
if(!rootPath.mkpath(relPath))
{
return QString();
}
return path;
}
bool InstanceList::commitStagedInstance(const QString& path, const QString& instanceName, const QString& groupName)
{
QDir dir;
QString instID = FS::DirNameFromString(instanceName, m_instDir);
{
WatchLock lock(m_watcher, m_instDir);
QString destination = FS::PathCombine(m_instDir, instID);
if(!dir.rename(path, destination))
{
qWarning() << "Failed to move" << path << "to" << destination;
return false;
}
m_groupMap[instID] = groupName;
instanceSet.insert(instID);
m_groups.insert(groupName);
emit instancesChanged();
}
saveGroupList();
return true;
}
bool InstanceList::destroyStagingPath(const QString& keyPath)
{
return FS::deletePath(keyPath);
}
#include "InstanceList.moc"

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -21,27 +21,49 @@
#include <QList> #include <QList>
#include "BaseInstance.h" #include "BaseInstance.h"
#include "BaseInstanceProvider.h"
#include "multimc_logic_export.h" #include "multimc_logic_export.h"
#include "QObjectPtr.h" #include "QObjectPtr.h"
class BaseInstance; class QFileSystemWatcher;
class InstanceTask;
using InstanceId = QString;
using GroupId = QString;
using InstanceLocator = std::pair<InstancePtr, int>;
enum class InstCreateError
{
NoCreateError = 0,
NoSuchVersion,
UnknownCreateError,
InstExists,
CantCreateDir
};
enum class GroupsState
{
NotLoaded,
Steady,
Dirty
};
class MULTIMC_LOGIC_EXPORT InstanceList : public QAbstractListModel class MULTIMC_LOGIC_EXPORT InstanceList : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit InstanceList(QObject *parent = 0); explicit InstanceList(SettingsObjectPtr settings, const QString & instDir, QObject *parent = 0);
virtual ~InstanceList(); virtual ~InstanceList();
public: public:
QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const override;
Qt::ItemFlags flags(const QModelIndex &index) const; Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex & index, const QVariant & value, int role) override;
enum AdditionalRoles enum AdditionalRoles
{ {
@@ -70,36 +92,74 @@ public:
return m_instances.count(); return m_instances.count();
} }
InstListError loadList(bool complete = false); InstListError loadList();
void saveNow(); void saveNow();
/// Add an instance provider. Takes ownership of it. Should only be done before the first load.
void addInstanceProvider(BaseInstanceProvider * provider);
InstancePtr getInstanceById(QString id) const; InstancePtr getInstanceById(QString id) const;
QModelIndex getInstanceIndexById(const QString &id) const; QModelIndex getInstanceIndexById(const QString &id) const;
QStringList getGroups(); QStringList getGroups();
GroupId getInstanceGroup(const InstanceId & id) const;
void setInstanceGroup(const InstanceId & id, const GroupId& name);
void deleteGroup(const QString & name); void deleteGroup(const GroupId & name);
void deleteInstance(const InstanceId & id);
// Wrap an instance creation task in some more task machinery and make it ready to be used
Task * wrapInstanceTask(InstanceTask * task);
/**
* Create a new empty staging area for instance creation and @return a path/key top commit it later.
* Used by instance manipulation tasks.
*/
QString getStagedInstancePath();
/**
* Commit the staging area given by @keyPath to the provider - used when creation succeeds.
* Used by instance manipulation tasks.
*/
bool commitStagedInstance(const QString & keyPath, const QString& instanceName, const QString & groupName);
/**
* Destroy a previously created staging area given by @keyPath - used when creation fails.
* Used by instance manipulation tasks.
*/
bool destroyStagingPath(const QString & keyPath);
signals: signals:
void dataIsInvalid(); void dataIsInvalid();
void instancesChanged();
void groupsChanged(QSet<QString> groups);
public slots:
void on_InstFolderChanged(const Setting &setting, QVariant value);
private slots: private slots:
void propertiesChanged(BaseInstance *inst); void propertiesChanged(BaseInstance *inst);
void groupsPublished(QSet<QString>);
void providerUpdated(); void providerUpdated();
void instanceDirContentsChanged(const QString &path);
private: private:
int getInstIndex(BaseInstance *inst) const; int getInstIndex(BaseInstance *inst) const;
void suspendWatch(); void suspendWatch();
void resumeWatch(); void resumeWatch();
void add(const QList<InstancePtr> &list); void add(const QList<InstancePtr> &list);
void loadGroupList();
void saveGroupList();
QList<InstanceId> discoverInstances();
InstancePtr loadInstance(const InstanceId& id);
protected: private:
int m_watchLevel = 0; int m_watchLevel = 0;
QSet<BaseInstanceProvider *> m_updatedProviders; bool m_dirty = false;
QList<InstancePtr> m_instances; QList<InstancePtr> m_instances;
QSet<QString> m_groups; QSet<QString> m_groups;
QVector<shared_qobject_ptr<BaseInstanceProvider>> m_providers;
SettingsObjectPtr m_globalSettings;
QString m_instDir;
QFileSystemWatcher * m_watcher;
QMap<InstanceId, GroupId> m_groupMap;
QSet<InstanceId> instanceSet;
bool m_groupsLoaded = false;
bool m_instancesProbed = false;
}; };

View File

@@ -0,0 +1,9 @@
#include "InstanceTask.h"
InstanceTask::InstanceTask()
{
}
InstanceTask::~InstanceTask()
{
}

53
api/logic/InstanceTask.h Normal file
View File

@@ -0,0 +1,53 @@
#pragma once
#include "tasks/Task.h"
#include "multimc_logic_export.h"
#include "settings/SettingsObject.h"
class MULTIMC_LOGIC_EXPORT InstanceTask : public Task
{
Q_OBJECT
public:
explicit InstanceTask();
virtual ~InstanceTask();
void setParentSettings(SettingsObjectPtr settings)
{
m_globalSettings = settings;
}
void setStagingPath(const QString &stagingPath)
{
m_stagingPath = stagingPath;
}
void setName(const QString &name)
{
m_instName = name;
}
QString name() const
{
return m_instName;
}
void setIcon(const QString &icon)
{
m_instIcon = icon;
}
void setGroup(const QString &group)
{
m_instGroup = group;
}
QString group() const
{
return m_instGroup;
}
protected: /* data */
SettingsObjectPtr m_globalSettings;
QString m_instName;
QString m_instIcon;
QString m_instGroup;
QString m_stagingPath;
};

View File

@@ -123,7 +123,7 @@ T ensureIsType(const QJsonValue &value, const T default_ = T(), const QString &w
{ {
return requireIsType<T>(value, what); return requireIsType<T>(value, what);
} }
catch (JsonException &) catch (const JsonException &)
{ {
return default_; return default_;
} }

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -212,8 +212,10 @@ QStringList MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QSt
{ {
QDir directory(target); QDir directory(target);
QStringList extracted; QStringList extracted;
qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target;
if (!zip->goToFirstFile()) if (!zip->goToFirstFile())
{ {
qWarning() << "Failed to seek to first file in zip";
return QStringList(); return QStringList();
} }
do do
@@ -231,10 +233,12 @@ QStringList MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QSt
} }
if (!JlCompress::extractFile(zip, "", absFilePath)) if (!JlCompress::extractFile(zip, "", absFilePath))
{ {
qWarning() << "Failed to extract file" << name << "to" << absFilePath;
JlCompress::removeFile(extracted); JlCompress::removeFile(extracted);
return QStringList(); return QStringList();
} }
extracted.append(absFilePath); extracted.append(absFilePath);
qDebug() << "Extracted file" << name;
} while (zip->goToNextFile()); } while (zip->goToNextFile());
return extracted; return extracted;
} }

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,8 +1,10 @@
#pragma once #pragma once
#include "BaseInstance.h" #include "BaseInstance.h"
#include "launch/LaunchTask.h"
class NullInstance: public BaseInstance class NullInstance: public BaseInstance
{ {
Q_OBJECT
public: public:
NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir) NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir)
:BaseInstance(globalSettings, settings, rootDir) :BaseInstance(globalSettings, settings, rootDir)
@@ -10,49 +12,46 @@ public:
setVersionBroken(true); setVersionBroken(true);
} }
virtual ~NullInstance() {}; virtual ~NullInstance() {};
virtual void init() override void saveNow() override
{ {
} }
virtual void saveNow() override QString getStatusbarDescription() override
{
}
virtual QString getStatusbarDescription() override
{ {
return tr("Unknown instance type"); return tr("Unknown instance type");
}; };
virtual QSet< QString > traits() const override QSet< QString > traits() const override
{ {
return {}; return {};
}; };
virtual QString instanceConfigFolder() const override QString instanceConfigFolder() const override
{ {
return instanceRoot(); return instanceRoot();
}; };
virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr) override shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr) override
{ {
return nullptr; return nullptr;
} }
virtual shared_qobject_ptr< Task > createUpdateTask(Net::Mode mode) override shared_qobject_ptr< Task > createUpdateTask(Net::Mode mode) override
{ {
return nullptr; return nullptr;
} }
virtual QProcessEnvironment createEnvironment() override QProcessEnvironment createEnvironment() override
{ {
return QProcessEnvironment(); return QProcessEnvironment();
} }
virtual QMap<QString, QString> getVariables() const override QMap<QString, QString> getVariables() const override
{ {
return QMap<QString, QString>(); return QMap<QString, QString>();
} }
virtual IPathMatcher::Ptr getLogFileMatcher() override IPathMatcher::Ptr getLogFileMatcher() override
{ {
return nullptr; return nullptr;
} }
virtual QString getLogFileRoot() override QString getLogFileRoot() override
{ {
return instanceRoot(); return instanceRoot();
} }
virtual QString typeName() const override QString typeName() const override
{ {
return "Null"; return "Null";
} }

View File

@@ -1,4 +1,6 @@
#pragma once #pragma once
#include <QWriteLocker>
#include <QReadLocker>
template <typename K, typename V> template <typename K, typename V>
class RWStorage class RWStorage
{ {
@@ -42,7 +44,7 @@ public:
} }
void setStale(K key) void setStale(K key)
{ {
QReadLocker l(&lock); QWriteLocker l(&lock);
if(cache.contains(key)) if(cache.contains(key))
{ {
stale_entries.insert(key); stale_entries.insert(key);
@@ -52,6 +54,7 @@ public:
{ {
QWriteLocker l(&lock); QWriteLocker l(&lock);
cache.clear(); cache.clear();
stale_entries.clear();
} }
private: private:
QReadWriteLock lock; QReadWriteLock lock;

View File

@@ -7,8 +7,9 @@
class QUrl; class QUrl;
struct MULTIMC_LOGIC_EXPORT Version class MULTIMC_LOGIC_EXPORT Version
{ {
public:
Version(const QString &str); Version(const QString &str);
Version() {} Version() {}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

20
api/logic/WatchLock.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include <QString>
#include <QFileSystemWatcher>
struct WatchLock
{
WatchLock(QFileSystemWatcher * watcher, const QString& directory)
: m_watcher(watcher), m_directory(directory)
{
m_watcher->removePath(m_directory);
}
~WatchLock()
{
m_watcher->addPath(m_directory);
}
QFileSystemWatcher * m_watcher;
QString m_directory;
};

View File

@@ -22,4 +22,5 @@ public:
virtual void saveIcon(const QString &key, const QString &path, const char * format) const = 0; virtual void saveIcon(const QString &key, const QString &path, const char * format) const = 0;
virtual bool iconFileExists(const QString &key) const = 0; virtual bool iconFileExists(const QString &key) const = 0;
virtual void installIcons(const QStringList &iconFiles) = 0; virtual void installIcons(const QStringList &iconFiles) = 0;
virtual void installIcon(const QString &file, const QString &name) = 0;
}; };

View File

@@ -0,0 +1,62 @@
#include "IconUtils.h"
#include "FileSystem.h"
#include <QDirIterator>
#include <array>
namespace {
std::array<const char *, 6> validIconExtensions = {{
"svg",
"png",
"ico",
"gif",
"jpg",
"jpeg"
}};
}
namespace IconUtils{
QString findBestIconIn(const QString &folder, const QString & iconKey) {
int best_found = validIconExtensions.size();
QString best_filename;
QDirIterator it(folder, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::NoIteratorFlags);
while (it.hasNext()) {
it.next();
auto fileInfo = it.fileInfo();
if(fileInfo.completeBaseName() != iconKey)
continue;
auto extension = fileInfo.suffix();
for(int i = 0; i < best_found; i++) {
if(extension == validIconExtensions[i]) {
best_found = i;
qDebug() << i << " : " << fileInfo.fileName();
best_filename = fileInfo.fileName();
}
}
}
return FS::PathCombine(folder, best_filename);
}
QString getIconFilter() {
QString out;
QTextStream stream(&out);
stream << '(';
for(size_t i = 0; i < validIconExtensions.size() - 1; i++) {
if(i > 0) {
stream << " ";
}
stream << "*." << validIconExtensions[i];
}
stream << " *." << validIconExtensions[validIconExtensions.size() - 1];
stream << ')';
return out;
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <QString>
#include "multimc_logic_export.h"
namespace IconUtils {
// Given a folder and an icon key, find 'best' of the icons with the given key in there and return its path
MULTIMC_LOGIC_EXPORT QString findBestIconIn(const QString &folder, const QString & iconKey);
// Get icon file type filter for file browser dialogs
MULTIMC_LOGIC_EXPORT QString getIconFilter();
}

View File

@@ -75,8 +75,8 @@ void JavaChecker::stderrReady()
void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
{ {
killTimer.stop(); killTimer.stop();
QProcessPtr _process; QProcessPtr _process = process;
_process.swap(process); process.reset();
JavaCheckResult result; JavaCheckResult result;
{ {

View File

@@ -3,6 +3,8 @@
#include <QTimer> #include <QTimer>
#include <memory> #include <memory>
#include "QObjectPtr.h"
#include "multimc_logic_export.h" #include "multimc_logic_export.h"
#include "JavaVersion.h" #include "JavaVersion.h"
@@ -27,8 +29,8 @@ struct MULTIMC_LOGIC_EXPORT JavaCheckResult
} validity = Validity::Errored; } validity = Validity::Errored;
}; };
typedef std::shared_ptr<QProcess> QProcessPtr; typedef shared_qobject_ptr<QProcess> QProcessPtr;
typedef std::shared_ptr<JavaChecker> JavaCheckerPtr; typedef shared_qobject_ptr<JavaChecker> JavaCheckerPtr;
class MULTIMC_LOGIC_EXPORT JavaChecker : public QObject class MULTIMC_LOGIC_EXPORT JavaChecker : public QObject
{ {
Q_OBJECT Q_OBJECT

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
#include "tasks/Task.h" #include "tasks/Task.h"
class JavaCheckerJob; class JavaCheckerJob;
typedef std::shared_ptr<JavaCheckerJob> JavaCheckerJobPtr; typedef shared_qobject_ptr<JavaCheckerJob> JavaCheckerJobPtr;
// FIXME: this just seems horribly redundant // FIXME: this just seems horribly redundant
class JavaCheckerJob : public Task class JavaCheckerJob : public Task
@@ -28,6 +28,7 @@ class JavaCheckerJob : public Task
Q_OBJECT Q_OBJECT
public: public:
explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name) {}; explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name) {};
virtual ~JavaCheckerJob() {};
bool addJavaCheckerAction(JavaCheckerPtr base) bool addJavaCheckerAction(JavaCheckerPtr base)
{ {

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -149,7 +149,7 @@ void JavaListLoadTask::executeTask()
JavaUtils ju; JavaUtils ju;
QList<QString> candidate_paths = ju.FindJavaPaths(); QList<QString> candidate_paths = ju.FindJavaPaths();
m_job = std::shared_ptr<JavaCheckerJob>(new JavaCheckerJob("Java detection")); m_job = new JavaCheckerJob("Java detection");
connect(m_job.get(), &Task::finished, this, &JavaListLoadTask::javaCheckerFinished); connect(m_job.get(), &Task::finished, this, &JavaListLoadTask::javaCheckerFinished);
connect(m_job.get(), &Task::progress, this, &Task::setProgress); connect(m_job.get(), &Task::progress, this, &Task::setProgress);

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@
#include "JavaCheckerJob.h" #include "JavaCheckerJob.h"
#include "JavaInstall.h" #include "JavaInstall.h"
#include "QObjectPtr.h"
#include "multimc_logic_export.h" #include "multimc_logic_export.h"
class JavaListLoadTask; class JavaListLoadTask;
@@ -68,14 +70,14 @@ class JavaListLoadTask : public Task
public: public:
explicit JavaListLoadTask(JavaInstallList *vlist); explicit JavaListLoadTask(JavaInstallList *vlist);
~JavaListLoadTask(); virtual ~JavaListLoadTask();
void executeTask() override; void executeTask() override;
public slots: public slots:
void javaCheckerFinished(); void javaCheckerFinished();
protected: protected:
std::shared_ptr<JavaCheckerJob> m_job; shared_qobject_ptr<JavaCheckerJob> m_job;
JavaInstallList *m_list; JavaInstallList *m_list;
JavaInstall *m_currentRecommended; JavaInstall *m_currentRecommended;
}; };

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@ JavaUtils::JavaUtils()
{ {
} }
#ifdef Q_OS_LINUX
static QString processLD_LIBRARY_PATH(const QString & LD_LIBRARY_PATH) static QString processLD_LIBRARY_PATH(const QString & LD_LIBRARY_PATH)
{ {
QDir mmcBin(QCoreApplication::applicationDirPath()); QDir mmcBin(QCoreApplication::applicationDirPath());
@@ -48,6 +49,7 @@ static QString processLD_LIBRARY_PATH(const QString & LD_LIBRARY_PATH)
} }
return final.join(':'); return final.join(':');
} }
#endif
QProcessEnvironment CleanEnviroment() QProcessEnvironment CleanEnviroment()
{ {
@@ -107,7 +109,7 @@ QProcessEnvironment CleanEnviroment()
continue; continue;
} }
#endif #endif
qDebug() << "Env: " << key << value; // qDebug() << "Env: " << key << value;
env.insert(key, value); env.insert(key, value);
} }
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -60,7 +60,7 @@ void CheckJava::executeTask()
// if timestamps are not the same, or something is missing, check! // if timestamps are not the same, or something is missing, check!
if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0) if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0)
{ {
m_JavaChecker = std::make_shared<JavaChecker>(); m_JavaChecker = new JavaChecker();
emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC); emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC);
connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished); connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished);
m_JavaChecker->m_path = realJavaPath; m_JavaChecker->m_path = realJavaPath;

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -31,8 +31,8 @@ public: /* methods */
}; };
virtual ~LaunchStep() {}; virtual ~LaunchStep() {};
protected: /* methods */ private: /* methods */
virtual void bind(LaunchTask *parent); void bind(LaunchTask *parent);
signals: signals:
void logLines(QStringList lines, MessageLevel::Enum level); void logLines(QStringList lines, MessageLevel::Enum level);

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Authors: Orochimarufan <orochimarufan.x3@gmail.com> * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
* *
@@ -33,9 +33,9 @@ void LaunchTask::init()
m_instance->setRunning(true); m_instance->setRunning(true);
} }
std::shared_ptr<LaunchTask> LaunchTask::create(InstancePtr inst) shared_qobject_ptr<LaunchTask> LaunchTask::create(InstancePtr inst)
{ {
std::shared_ptr<LaunchTask> proc(new LaunchTask(inst)); shared_qobject_ptr<LaunchTask> proc(new LaunchTask(inst));
proc->init(); proc->init();
return proc; return proc;
} }
@@ -44,12 +44,12 @@ LaunchTask::LaunchTask(InstancePtr instance): m_instance(instance)
{ {
} }
void LaunchTask::appendStep(std::shared_ptr<LaunchStep> step) void LaunchTask::appendStep(shared_qobject_ptr<LaunchStep> step)
{ {
m_steps.append(step); m_steps.append(step);
} }
void LaunchTask::prependStep(std::shared_ptr<LaunchStep> step) void LaunchTask::prependStep(shared_qobject_ptr<LaunchStep> step)
{ {
m_steps.prepend(step); m_steps.prepend(step);
} }

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Authors: Orochimarufan <orochimarufan.x3@gmail.com> * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
* *
@@ -45,11 +45,11 @@ public:
}; };
public: /* methods */ public: /* methods */
static std::shared_ptr<LaunchTask> create(InstancePtr inst); static shared_qobject_ptr<LaunchTask> create(InstancePtr inst);
virtual ~LaunchTask() {}; virtual ~LaunchTask() {};
void appendStep(std::shared_ptr<LaunchStep> step); void appendStep(shared_qobject_ptr<LaunchStep> step);
void prependStep(std::shared_ptr<LaunchStep> step); void prependStep(shared_qobject_ptr<LaunchStep> step);
void setCensorFilter(QMap<QString, QString> filter); void setCensorFilter(QMap<QString, QString> filter);
InstancePtr instance() InstancePtr instance()
@@ -117,7 +117,7 @@ private: /*methods */
protected: /* data */ protected: /* data */
InstancePtr m_instance; InstancePtr m_instance;
shared_qobject_ptr<LogModel> m_logModel; shared_qobject_ptr<LogModel> m_logModel;
QList <std::shared_ptr<LaunchStep>> m_steps; QList <shared_qobject_ptr<LaunchStep>> m_steps;
QMap<QString, QString> m_censorFilter; QMap<QString, QString> m_censorFilter;
int currentStep = -1; int currentStep = -1;
State state = NotStarted; State state = NotStarted;

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -23,6 +23,8 @@ class PostLaunchCommand: public LaunchStep
Q_OBJECT Q_OBJECT
public: public:
explicit PostLaunchCommand(LaunchTask *parent); explicit PostLaunchCommand(LaunchTask *parent);
virtual ~PostLaunchCommand() {};
virtual void executeTask(); virtual void executeTask();
virtual bool abort(); virtual bool abort();
virtual bool canAbort() const virtual bool canAbort() const

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -23,6 +23,8 @@ class PreLaunchCommand: public LaunchStep
Q_OBJECT Q_OBJECT
public: public:
explicit PreLaunchCommand(LaunchTask *parent); explicit PreLaunchCommand(LaunchTask *parent);
virtual ~PreLaunchCommand() {};
virtual void executeTask(); virtual void executeTask();
virtual bool abort(); virtual bool abort();
virtual bool canAbort() const virtual bool canAbort() const

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -49,7 +49,7 @@ void Update::updateFinished()
} }
else else
{ {
QString reason = tr("Instance update failed because: %1.\n\n").arg(m_updateTask->failReason()); QString reason = tr("Instance update failed because: %1\n\n").arg(m_updateTask->failReason());
m_updateTask.reset(); m_updateTask.reset();
emit logLine(reason, MessageLevel::Fatal); emit logLine(reason, MessageLevel::Fatal);
emitFailed(reason); emitFailed(reason);

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2015-2017 MultiMC Contributors /* Copyright 2015-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -56,7 +56,7 @@ public: /* methods */
m_entity->parse(Json::requireObject(Json::requireDocument(data, fname), fname)); m_entity->parse(Json::requireObject(Json::requireDocument(data, fname), fname));
return true; return true;
} }
catch (Exception &e) catch (const Exception &e)
{ {
qWarning() << "Unable to parse response:" << e.cause(); qWarning() << "Unable to parse response:" << e.cause();
return false; return false;
@@ -74,7 +74,7 @@ Meta::BaseEntity::~BaseEntity()
QUrl Meta::BaseEntity::url() const QUrl Meta::BaseEntity::url() const
{ {
return QUrl("https://v1.meta.multimc.org").resolved(localFilename()); return QUrl("https://meta.multimc.org/v1/").resolved(localFilename());
} }
bool Meta::BaseEntity::loadLocalFile() bool Meta::BaseEntity::loadLocalFile()
@@ -90,7 +90,7 @@ bool Meta::BaseEntity::loadLocalFile()
parse(Json::requireObject(Json::requireDocument(fname, fname), fname)); parse(Json::requireObject(Json::requireDocument(fname, fname), fname));
return true; return true;
} }
catch (Exception &e) catch (const Exception &e)
{ {
qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause()); qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause());
// just make sure it's gone and we never consider it again. // just make sure it's gone and we never consider it again.

View File

@@ -1,4 +1,4 @@
/* Copyright 2015-2017 MultiMC Contributors /* Copyright 2015-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2015-2017 MultiMC Contributors /* Copyright 2015-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2015-2017 MultiMC Contributors /* Copyright 2015-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2015-2017 MultiMC Contributors /* Copyright 2015-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2015-2017 MultiMC Contributors /* Copyright 2015-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -80,4 +80,4 @@ void serializeRequires(QJsonObject & objOut, RequireSet* ptr, const char * keyNa
MetadataVersion currentFormatVersion(); MetadataVersion currentFormatVersion();
} }
Q_DECLARE_METATYPE(std::set<Meta::Require>); Q_DECLARE_METATYPE(std::set<Meta::Require>)

View File

@@ -1,4 +1,4 @@
/* Copyright 2015-2017 MultiMC Contributors /* Copyright 2015-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2015-2017 MultiMC Contributors /* Copyright 2015-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2015-2017 MultiMC Contributors /* Copyright 2015-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2015-2017 MultiMC Contributors /* Copyright 2015-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -27,6 +27,34 @@
#include "FileSystem.h" #include "FileSystem.h"
#include "net/Download.h" #include "net/Download.h"
#include "net/ChecksumValidator.h" #include "net/ChecksumValidator.h"
#include "net/URLConstants.h"
namespace {
QSet<QString> collectPathsFromDir(QString dirPath)
{
QFileInfo dirInfo(dirPath);
if (!dirInfo.exists())
{
return {};
}
QSet<QString> out;
QDirIterator iter(dirPath, QDirIterator::Subdirectories);
while (iter.hasNext())
{
QString value = iter.next();
QFileInfo info(value);
if(info.isFile())
{
out.insert(value);
qDebug() << value;
}
}
return out;
}
}
namespace AssetsUtils namespace AssetsUtils
@@ -36,7 +64,7 @@ namespace AssetsUtils
* Returns true on success, with index populated * Returns true on success, with index populated
* index is undefined otherwise * index is undefined otherwise
*/ */
bool loadAssetsIndexJson(QString assetsId, QString path, AssetsIndex *index) bool loadAssetsIndexJson(const QString &assetsId, const QString &path, AssetsIndex& index)
{ {
/* /*
{ {
@@ -60,7 +88,7 @@ bool loadAssetsIndexJson(QString assetsId, QString path, AssetsIndex *index)
qCritical() << "Failed to read assets index file" << path; qCritical() << "Failed to read assets index file" << path;
return false; return false;
} }
index->id = assetsId; index.id = assetsId;
// Read the file and close it. // Read the file and close it.
QByteArray jsonData = file.readAll(); QByteArray jsonData = file.readAll();
@@ -89,7 +117,13 @@ bool loadAssetsIndexJson(QString assetsId, QString path, AssetsIndex *index)
QJsonValue isVirtual = root.value("virtual"); QJsonValue isVirtual = root.value("virtual");
if (!isVirtual.isUndefined()) if (!isVirtual.isUndefined())
{ {
index->isVirtual = isVirtual.toBool(false); index.isVirtual = isVirtual.toBool(false);
}
QJsonValue mapToResources = root.value("map_to_resources");
if (!mapToResources.isUndefined())
{
index.mapToResources = mapToResources.toBool(false);
} }
QJsonValue objects = root.value("objects"); QJsonValue objects = root.value("objects");
@@ -121,13 +155,14 @@ bool loadAssetsIndexJson(QString assetsId, QString path, AssetsIndex *index)
} }
} }
index->objects.insert(iter.key(), object); index.objects.insert(iter.key(), object);
} }
return true; return true;
} }
QDir reconstructAssets(QString assetsId) // FIXME: ugly code duplication
QDir getAssetsDir(const QString &assetsId, const QString &resourcesFolder)
{ {
QDir assetsDir = QDir("assets/"); QDir assetsDir = QDir("assets/");
QDir indexDir = QDir(FS::PathCombine(assetsDir.path(), "indexes")); QDir indexDir = QDir(FS::PathCombine(assetsDir.path(), "indexes"));
@@ -140,24 +175,77 @@ QDir reconstructAssets(QString assetsId)
if (!indexFile.exists()) if (!indexFile.exists())
{ {
qCritical() << "No assets index file" << indexPath << "; can't reconstruct assets"; qCritical() << "No assets index file" << indexPath << "; can't determine assets path!";
return virtualRoot; return virtualRoot;
} }
qDebug() << "reconstructAssets" << assetsDir.path() << indexDir.path() AssetsIndex index;
<< objectDir.path() << virtualDir.path() << virtualRoot.path(); if(!AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, index))
{
qCritical() << "Failed to load asset index file" << indexPath << "; can't determine assets path!";
return virtualRoot;
}
QString targetPath;
if(index.isVirtual)
{
return virtualRoot;
}
else if(index.mapToResources)
{
return QDir(resourcesFolder);
}
return virtualRoot;
}
// FIXME: ugly code duplication
bool reconstructAssets(QString assetsId, QString resourcesFolder)
{
QDir assetsDir = QDir("assets/");
QDir indexDir = QDir(FS::PathCombine(assetsDir.path(), "indexes"));
QDir objectDir = QDir(FS::PathCombine(assetsDir.path(), "objects"));
QDir virtualDir = QDir(FS::PathCombine(assetsDir.path(), "virtual"));
QString indexPath = FS::PathCombine(indexDir.path(), assetsId + ".json");
QFile indexFile(indexPath);
QDir virtualRoot(FS::PathCombine(virtualDir.path(), assetsId));
if (!indexFile.exists())
{
qCritical() << "No assets index file" << indexPath << "; can't reconstruct assets!";
return false;
}
qDebug() << "reconstructAssets" << assetsDir.path() << indexDir.path() << objectDir.path() << virtualDir.path() << virtualRoot.path();
AssetsIndex index; AssetsIndex index;
bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, &index); if(!AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, index))
if (loadAssetsIndex && index.isVirtual)
{ {
qDebug() << "Reconstructing virtual assets folder at" << virtualRoot.path(); qCritical() << "Failed to load asset index file" << indexPath << "; can't reconstruct assets!";
return false;
}
QString targetPath;
bool removeLeftovers = false;
if(index.isVirtual)
{
targetPath = virtualRoot.path();
removeLeftovers = true;
qDebug() << "Reconstructing virtual assets folder at" << targetPath;
}
else if(index.mapToResources)
{
targetPath = resourcesFolder;
qDebug() << "Reconstructing resources folder at" << targetPath;
}
if (!targetPath.isNull())
{
auto presentFiles = collectPathsFromDir(targetPath);
for (QString map : index.objects.keys()) for (QString map : index.objects.keys())
{ {
AssetObject asset_object = index.objects.value(map); AssetObject asset_object = index.objects.value(map);
QString target_path = FS::PathCombine(virtualRoot.path(), map); QString target_path = FS::PathCombine(targetPath, map);
QFile target(target_path); QFile target(target_path);
QString tlk = asset_object.hash.left(2); QString tlk = asset_object.hash.left(2);
@@ -166,24 +254,32 @@ QDir reconstructAssets(QString assetsId)
QFile original(original_path); QFile original(original_path);
if (!original.exists()) if (!original.exists())
continue; continue;
presentFiles.remove(target_path);
if (!target.exists()) if (!target.exists())
{ {
QFileInfo info(target_path); QFileInfo info(target_path);
QDir target_dir = info.dir(); QDir target_dir = info.dir();
// qDebug() << target_dir;
if (!target_dir.exists()) qDebug() << target_dir.path();
QDir("").mkpath(target_dir.path()); FS::ensureFolderPathExists(target_dir.path());
bool couldCopy = original.copy(target_path); bool couldCopy = original.copy(target_path);
qDebug() << " Copying" << original_path << "to" << target_path qDebug() << " Copying" << original_path << "to" << target_path << QString::number(couldCopy);
<< QString::number(couldCopy); // << original.errorString();
} }
} }
// TODO: Write last used time to virtualRoot/.lastused // TODO: Write last used time to virtualRoot/.lastused
if(removeLeftovers)
{
for(auto & file: presentFiles)
{
qDebug() << "Would remove" << file;
} }
}
return virtualRoot; }
return true;
} }
} }
@@ -212,7 +308,7 @@ QString AssetObject::getLocalPath()
QUrl AssetObject::getUrl() QUrl AssetObject::getUrl()
{ {
return QUrl("http://resources.download.minecraft.net/" + getRelPath()); return URLConstants::RESOURCE_BASE + getRelPath();
} }
QString AssetObject::getRelPath() QString AssetObject::getRelPath()

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -38,11 +38,16 @@ struct AssetsIndex
QString id; QString id;
QMap<QString, AssetObject> objects; QMap<QString, AssetObject> objects;
bool isVirtual = false; bool isVirtual = false;
bool mapToResources = false;
}; };
/// FIXME: this is absolutely horrendous. REDO!!!!
namespace AssetsUtils namespace AssetsUtils
{ {
bool loadAssetsIndexJson(QString id, QString file, AssetsIndex* index); bool loadAssetsIndexJson(const QString &id, const QString &file, AssetsIndex& index);
QDir getAssetsDir(const QString &assetsId, const QString &resourcesFolder);
/// Reconstruct a virtual assets folder for the given assets ID and return the folder /// Reconstruct a virtual assets folder for the given assets ID and return the folder
QDir reconstructAssets(QString assetsId); bool reconstructAssets(QString assetsId, QString resourcesFolder);
} }

View File

@@ -145,7 +145,7 @@ QDateTime Component::getReleaseDateTime()
bool Component::isEnabled() bool Component::isEnabled()
{ {
return !canBeDisabled() || !m_disabled; return !canBeDisabled() || !m_disabled;
}; }
bool Component::canBeDisabled() bool Component::canBeDisabled()
{ {
@@ -171,7 +171,7 @@ bool Component::setEnabled(bool state)
bool Component::isCustom() bool Component::isCustom()
{ {
return m_file != nullptr; return m_file != nullptr;
}; }
bool Component::isCustomizable() bool Component::isCustomizable()
{ {
@@ -323,7 +323,7 @@ bool Component::customize()
m_metaVersion.reset(); m_metaVersion.reset();
emit dataChanged(); emit dataChanged();
} }
catch (Exception &error) catch (const Exception &error)
{ {
qWarning() << "Version could not be loaded:" << error.cause(); qWarning() << "Version could not be loaded:" << error.cause();
} }

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -195,7 +195,7 @@ static bool loadComponentList(ComponentList * parent, const QString & filename,
container.append(componentFromJsonV1(parent, componentJsonPattern, obj)); container.append(componentFromJsonV1(parent, componentJsonPattern, obj));
} }
} }
catch (JSONValidationError &err) catch (const JSONValidationError &err)
{ {
qCritical() << "Couldn't parse" << componentsFile.fileName() << ": bad file format"; qCritical() << "Couldn't parse" << componentsFile.fileName() << ": bad file format";
container.clear(); container.clear();
@@ -743,42 +743,6 @@ Component * ComponentList::getComponent(int index)
return d->components[index].get(); return d->components[index].get();
} }
bool ComponentList::isVanilla()
{
for(auto patchptr: d->components)
{
if(patchptr->isCustom())
return false;
}
return true;
}
bool ComponentList::revertToVanilla()
{
// remove patches, if present
auto VersionPatchesCopy = d->components;
for(auto & it: VersionPatchesCopy)
{
if (!it->isCustom())
{
continue;
}
if(it->isRevertible() || it->isRemovable())
{
if(!remove(it->getID()))
{
qWarning() << "Couldn't remove" << it->getID() << "from profile!";
invalidateLaunchProfile();
scheduleSave();
return false;
}
}
}
invalidateLaunchProfile();
scheduleSave();
return true;
}
QVariant ComponentList::data(const QModelIndex &index, int role) const QVariant ComponentList::data(const QModelIndex &index, int role) const
{ {
if (!index.isValid()) if (!index.isValid())
@@ -1186,7 +1150,7 @@ std::shared_ptr<LaunchProfile> ComponentList::getProfile() const
} }
d->m_profile = profile; d->m_profile = profile;
} }
catch (Exception & error) catch (const Exception &error)
{ {
qWarning() << "Couldn't apply profile patches because: " << error.cause(); qWarning() << "Couldn't apply profile patches because: " << error.cause();
} }
@@ -1208,11 +1172,16 @@ bool ComponentList::setComponentVersion(const QString& uid, const QString& versi
auto iter = d->componentIndex.find(uid); auto iter = d->componentIndex.find(uid);
if(iter != d->componentIndex.end()) if(iter != d->componentIndex.end())
{ {
ComponentPtr component = *iter;
// set existing // set existing
(*iter)->setVersion(version); if(component->revert())
(*iter)->setImportant(important); {
component->setVersion(version);
component->setImportant(important);
return true; return true;
} }
return false;
}
else else
{ {
// add new // add new

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -59,12 +59,6 @@ public:
/// call this to explicitly mark the component list as loaded - this is used to build a new component list from scratch. /// call this to explicitly mark the component list as loaded - this is used to build a new component list from scratch.
void buildingFromScratch(); void buildingFromScratch();
/// is this version unchanged by the user?
bool isVanilla();
/// remove any customizations on top of whatever 'vanilla' means
bool revertToVanilla();
/// install more jar mods /// install more jar mods
void installJarMods(QStringList selectedFiles); void installJarMods(QStringList selectedFiles);

View File

@@ -124,6 +124,8 @@ static LoadResult loadComponent(ComponentPtr component, shared_qobject_ptr<Task>
return result; return result;
} }
// FIXME: dead code. determine if this can still be useful?
/*
static LoadResult loadComponentList(ComponentPtr component, shared_qobject_ptr<Task>& loadTask, Net::Mode netmode) static LoadResult loadComponentList(ComponentPtr component, shared_qobject_ptr<Task>& loadTask, Net::Mode netmode)
{ {
if(component->m_loaded) if(component->m_loaded)
@@ -147,6 +149,7 @@ static LoadResult loadComponentList(ComponentPtr component, shared_qobject_ptr<T
} }
return result; return result;
} }
*/
static LoadResult loadIndex(shared_qobject_ptr<Task>& loadTask, Net::Mode netmode) static LoadResult loadIndex(shared_qobject_ptr<Task>& loadTask, Net::Mode netmode)
{ {
@@ -583,6 +586,15 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly)
{ {
component->m_version = "3.1.2"; component->m_version = "3.1.2";
} }
else if (add.uid == "net.fabricmc.intermediary")
{
auto minecraft = std::find_if(components.begin(), components.end(), [](ComponentPtr & cmp){
return cmp->getID() == "net.minecraft";
});
if(minecraft != components.end()) {
component->m_version = (*minecraft)->getVersion();
}
}
} }
// HACK HACK HACK HACK FIXME: this is a placeholder for deciding what version to use. For now, it is hardcoded. // HACK HACK HACK HACK FIXME: this is a placeholder for deciding what version to use. For now, it is hardcoded.
// ############################################################################################################ // ############################################################################################################

View File

@@ -18,13 +18,7 @@ void Library::getApplicableFiles(OpSys system, QStringList& jar, QStringList& na
if(local && !overridePath.isEmpty()) if(local && !overridePath.isEmpty())
{ {
QString fileName = out.fileName(); QString fileName = out.fileName();
auto fullPath = FS::PathCombine(overridePath, fileName); return QFileInfo(FS::PathCombine(overridePath, fileName)).absoluteFilePath();
qDebug() << fullPath;
QFileInfo fileinfo(fullPath);
if(fileinfo.exists())
{
return fileinfo.absoluteFilePath();
}
} }
return out.absoluteFilePath(); return out.absoluteFilePath();
}; };
@@ -51,61 +45,53 @@ void Library::getApplicableFiles(OpSys system, QStringList& jar, QStringList& na
} }
} }
QList< std::shared_ptr< NetAction > > Library::getDownloads(OpSys system, class HttpMetaCache* cache, QList< std::shared_ptr< NetAction > > Library::getDownloads(
QStringList& failedFiles, const QString & overridePath) const OpSys system,
class HttpMetaCache* cache,
QStringList& failedLocalFiles,
const QString & overridePath
) const
{ {
QList<NetActionPtr> out; QList<NetActionPtr> out;
bool isAlwaysStale = (hint() == "always-stale"); bool stale = isAlwaysStale();
bool local = isLocal(); bool local = isLocal();
bool isForge = (hint() == "forge-pack-xz");
auto add_download = [&](QString storage, QString url, QString sha1 = QString()) auto check_local_file = [&](QString storage)
{ {
QFileInfo fileinfo(storage);
QString fileName = fileinfo.fileName();
auto fullPath = FS::PathCombine(overridePath, fileName);
QFileInfo localFileInfo(fullPath);
if(!localFileInfo.exists())
{
failedLocalFiles.append(localFileInfo.filePath());
return false;
}
return true;
};
auto add_download = [&](QString storage, QString url, QString sha1)
{
if(local)
{
return check_local_file(storage);
}
auto entry = cache->resolveEntry("libraries", storage); auto entry = cache->resolveEntry("libraries", storage);
if(isAlwaysStale) if(stale)
{ {
entry->setStale(true); entry->setStale(true);
} }
if (!entry->isStale()) if (!entry->isStale())
return true; return true;
if(local)
{
if(!overridePath.isEmpty())
{
QString fileName;
int position = storage.lastIndexOf('/');
if(position == -1)
{
fileName = storage;
}
else
{
fileName = storage.mid(position);
}
auto fullPath = FS::PathCombine(overridePath, fileName);
QFileInfo fileinfo(fullPath);
if(fileinfo.exists())
{
return true;
}
}
QFileInfo fileinfo(entry->getFullPath());
if(!fileinfo.exists())
{
failedFiles.append(entry->getFullPath());
return false;
}
return true;
}
Net::Download::Options options; Net::Download::Options options;
if(isAlwaysStale) if(stale)
{ {
options |= Net::Download::Option::AcceptLocalFiles; options |= Net::Download::Option::AcceptLocalFiles;
} }
if (isForge) if (isForge())
{ {
qDebug() << "XzDownload for:" << rawName() << "storage:" << storage << "url:" << url; qDebug() << "XzDownload for:" << rawName() << "storage:" << storage << "url:" << url;
out.append(ForgeXzDownload::make(storage, entry)); out.append(ForgeXzDownload::make(url, storage, entry));
} }
else else
{ {
@@ -184,7 +170,8 @@ QList< std::shared_ptr< NetAction > > Library::getDownloads(OpSys system, class
} }
else else
{ {
auto raw_dl = [&](){ auto raw_dl = [&]()
{
if (!m_absoluteURL.isEmpty()) if (!m_absoluteURL.isEmpty())
{ {
return m_absoluteURL; return m_absoluteURL;
@@ -192,7 +179,7 @@ QList< std::shared_ptr< NetAction > > Library::getDownloads(OpSys system, class
if (m_repositoryURL.isEmpty()) if (m_repositoryURL.isEmpty())
{ {
return QString("https://" + URLConstants::LIBRARY_BASE) + raw_storage; return URLConstants::LIBRARY_BASE + raw_storage;
} }
if(m_repositoryURL.endsWith('/')) if(m_repositoryURL.endsWith('/'))
@@ -208,14 +195,14 @@ QList< std::shared_ptr< NetAction > > Library::getDownloads(OpSys system, class
{ {
QString cooked_storage = raw_storage; QString cooked_storage = raw_storage;
QString cooked_dl = raw_dl; QString cooked_dl = raw_dl;
add_download(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32")); add_download(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32"), QString());
cooked_storage = raw_storage; cooked_storage = raw_storage;
cooked_dl = raw_dl; cooked_dl = raw_dl;
add_download(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64")); add_download(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64"), QString());
} }
else else
{ {
add_download(raw_storage, raw_dl); add_download(raw_storage, raw_dl, QString());
} }
} }
return out; return out;
@@ -251,6 +238,16 @@ bool Library::isLocal() const
return m_hint == "local"; return m_hint == "local";
} }
bool Library::isAlwaysStale() const
{
return m_hint == "always-stale";
}
bool Library::isForge() const
{
return m_hint == "forge-pack-xz";
}
void Library::setStoragePrefix(QString prefix) void Library::setStoragePrefix(QString prefix)
{ {
m_storagePrefix = prefix; m_storagePrefix = prefix;

View File

@@ -148,9 +148,15 @@ public: /* methods */
/// Returns true if the library is contained in an instance and false if it is shared /// Returns true if the library is contained in an instance and false if it is shared
bool isLocal() const; bool isLocal() const;
/// Returns true if the library is to always be checked for updates
bool isAlwaysStale() const;
/// Return true if the library requires forge XZ hacks
bool isForge() const;
// Get a list of downloads for this library // Get a list of downloads for this library
QList<NetActionPtr> getDownloads(OpSys system, class HttpMetaCache * cache, QList<NetActionPtr> getDownloads(OpSys system, class HttpMetaCache * cache,
QStringList & failedFiles, const QString & overridePath) const; QStringList & failedLocalFiles, const QString & overridePath) const;
private: /* methods */ private: /* methods */
/// the default storage prefix used by MultiMC /// the default storage prefix used by MultiMC

View File

@@ -65,7 +65,7 @@ slots:
test.setHint("local"); test.setHint("local");
auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString()); auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString());
QCOMPARE(downloads.size(), 0); QCOMPARE(downloads.size(), 0);
QCOMPARE(failedFiles, getStorage("test/package/testname/testversion/testname-testversion.jar")); QCOMPARE(failedFiles, {"testname-testversion.jar"});
} }
void test_legacy_url_local_override() void test_legacy_url_local_override()
{ {
@@ -170,11 +170,11 @@ slots:
QCOMPARE(jar, {}); QCOMPARE(jar, {});
QCOMPARE(native, {}); QCOMPARE(native, {});
QCOMPARE(native32, {QFileInfo("data/testname-testversion-linux-32.jar").absoluteFilePath()}); QCOMPARE(native32, {QFileInfo("data/testname-testversion-linux-32.jar").absoluteFilePath()});
QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-linux-64.jar")); QCOMPARE(native64, {QFileInfo("data/testname-testversion-linux-64.jar").absoluteFilePath()});
QStringList failedFiles; QStringList failedFiles;
auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString("data")); auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString("data"));
QCOMPARE(dls.size(), 0); QCOMPARE(dls.size(), 0);
QCOMPARE(failedFiles, {getStorage("test/package/testname/testversion/testname-testversion-linux-64.jar")}); QCOMPARE(failedFiles, {"data/testname-testversion-linux-64.jar"});
} }
} }
void test_onenine() void test_onenine()

View File

@@ -20,12 +20,13 @@
#include "minecraft/launch/DirectJavaLaunch.h" #include "minecraft/launch/DirectJavaLaunch.h"
#include "minecraft/launch/ModMinecraftJar.h" #include "minecraft/launch/ModMinecraftJar.h"
#include "minecraft/launch/ClaimAccount.h" #include "minecraft/launch/ClaimAccount.h"
#include "minecraft/launch/ReconstructAssets.h"
#include "java/launch/CheckJava.h" #include "java/launch/CheckJava.h"
#include "java/JavaUtils.h" #include "java/JavaUtils.h"
#include "meta/Index.h" #include "meta/Index.h"
#include "meta/VersionList.h" #include "meta/VersionList.h"
#include "ModList.h" #include "SimpleModList.h"
#include "WorldList.h" #include "WorldList.h"
#include "icons/IIconList.h" #include "icons/IIconList.h"
@@ -35,6 +36,7 @@
#include "AssetsUtils.h" #include "AssetsUtils.h"
#include "MinecraftUpdate.h" #include "MinecraftUpdate.h"
#include "MinecraftLoadAndCheck.h" #include "MinecraftLoadAndCheck.h"
#include <minecraft/gameoptions/GameOptions.h>
#define IBUS "@im=ibus" #define IBUS "@im=ibus"
@@ -111,10 +113,6 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO
m_components->setOldConfigVersion("com.mumfrey.liteloader", m_settings->get("LiteloaderVersion").toString()); m_components->setOldConfigVersion("com.mumfrey.liteloader", m_settings->get("LiteloaderVersion").toString());
} }
void MinecraftInstance::init()
{
}
void MinecraftInstance::saveNow() void MinecraftInstance::saveNow()
{ {
m_components->saveNow(); m_components->saveNow();
@@ -145,7 +143,7 @@ QSet<QString> MinecraftInstance::traits() const
return profile->getTraits(); return profile->getTraits();
} }
QString MinecraftInstance::minecraftRoot() const QString MinecraftInstance::gameRoot() const
{ {
QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft")); QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft")); QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft"));
@@ -158,7 +156,7 @@ QString MinecraftInstance::minecraftRoot() const
QString MinecraftInstance::binRoot() const QString MinecraftInstance::binRoot() const
{ {
return FS::PathCombine(minecraftRoot(), "bin"); return FS::PathCombine(gameRoot(), "bin");
} }
QString MinecraftInstance::getNativePath() const QString MinecraftInstance::getNativePath() const
@@ -173,44 +171,55 @@ QString MinecraftInstance::getLocalLibraryPath() const
return libraries_dir.absolutePath(); return libraries_dir.absolutePath();
} }
QString MinecraftInstance::jarModsDir() const
{
QDir jarmods_dir(FS::PathCombine(instanceRoot(), "jarmods/"));
return jarmods_dir.absolutePath();
}
QString MinecraftInstance::loaderModsDir() const QString MinecraftInstance::loaderModsDir() const
{ {
return FS::PathCombine(minecraftRoot(), "mods"); return FS::PathCombine(gameRoot(), "mods");
}
QString MinecraftInstance::modsCacheLocation() const
{
return FS::PathCombine(instanceRoot(), "mods.cache");
} }
QString MinecraftInstance::coreModsDir() const QString MinecraftInstance::coreModsDir() const
{ {
return FS::PathCombine(minecraftRoot(), "coremods"); return FS::PathCombine(gameRoot(), "coremods");
} }
QString MinecraftInstance::resourcePacksDir() const QString MinecraftInstance::resourcePacksDir() const
{ {
return FS::PathCombine(minecraftRoot(), "resourcepacks"); return FS::PathCombine(gameRoot(), "resourcepacks");
} }
QString MinecraftInstance::texturePacksDir() const QString MinecraftInstance::texturePacksDir() const
{ {
return FS::PathCombine(minecraftRoot(), "texturepacks"); return FS::PathCombine(gameRoot(), "texturepacks");
} }
QString MinecraftInstance::instanceConfigFolder() const QString MinecraftInstance::instanceConfigFolder() const
{ {
return FS::PathCombine(minecraftRoot(), "config"); return FS::PathCombine(gameRoot(), "config");
}
QString MinecraftInstance::jarModsDir() const
{
return FS::PathCombine(instanceRoot(), "jarmods");
} }
QString MinecraftInstance::libDir() const QString MinecraftInstance::libDir() const
{ {
return FS::PathCombine(minecraftRoot(), "lib"); return FS::PathCombine(gameRoot(), "lib");
} }
QString MinecraftInstance::worldDir() const QString MinecraftInstance::worldDir() const
{ {
return FS::PathCombine(minecraftRoot(), "saves"); return FS::PathCombine(gameRoot(), "saves");
}
QString MinecraftInstance::resourcesDir() const
{
return FS::PathCombine(gameRoot(), "resources");
} }
QDir MinecraftInstance::librariesPath() const QDir MinecraftInstance::librariesPath() const
@@ -330,7 +339,7 @@ QMap<QString, QString> MinecraftInstance::getVariables() const
out.insert("INST_NAME", name()); out.insert("INST_NAME", name());
out.insert("INST_ID", id()); out.insert("INST_ID", id());
out.insert("INST_DIR", QDir(instanceRoot()).absolutePath()); out.insert("INST_DIR", QDir(instanceRoot()).absolutePath());
out.insert("INST_MC_DIR", QDir(minecraftRoot()).absolutePath()); out.insert("INST_MC_DIR", QDir(gameRoot()).absolutePath());
out.insert("INST_JAVA", settings()->get("JavaPath").toString()); out.insert("INST_JAVA", settings()->get("JavaPath").toString());
out.insert("INST_JAVA_ARGS", javaArguments().join(' ')); out.insert("INST_JAVA_ARGS", javaArguments().join(' '));
return out; return out;
@@ -398,21 +407,14 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session) cons
// blatant self-promotion. // blatant self-promotion.
token_mapping["profile_name"] = token_mapping["version_name"] = "MultiMC5"; token_mapping["profile_name"] = token_mapping["version_name"] = "MultiMC5";
if(m_components->isVanilla())
{
token_mapping["version_type"] = profile->getMinecraftVersionType();
}
else
{
token_mapping["version_type"] = "custom";
}
QString absRootDir = QDir(minecraftRoot()).absolutePath(); token_mapping["version_type"] = profile->getMinecraftVersionType();
QString absRootDir = QDir(gameRoot()).absolutePath();
token_mapping["game_directory"] = absRootDir; token_mapping["game_directory"] = absRootDir;
QString absAssetsDir = QDir("assets/").absolutePath(); QString absAssetsDir = QDir("assets/").absolutePath();
auto assets = profile->getMinecraftAssets(); auto assets = profile->getMinecraftAssets();
// FIXME: this is wrong and should be run as an async task token_mapping["game_assets"] = AssetsUtils::getAssetsDir(assets->id, resourcesDir()).absolutePath();
token_mapping["game_assets"] = AssetsUtils::reconstructAssets(assets->id).absolutePath();
// 1.7.3+ assets tokens // 1.7.3+ assets tokens
token_mapping["assets_root"] = absAssetsDir; token_mapping["assets_root"] = absAssetsDir;
@@ -494,7 +496,7 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session)
launchScript += "traits " + trait + "\n"; launchScript += "traits " + trait + "\n";
} }
launchScript += "launcher onesix\n"; launchScript += "launcher onesix\n";
qDebug() << "Generated launch script:" << launchScript; // qDebug() << "Generated launch script:" << launchScript;
return launchScript; return launchScript;
} }
@@ -642,7 +644,6 @@ QMap<QString, QString> MinecraftInstance::createCensorFilterFromSession(AuthSess
addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>")); addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>"));
addToFilter(sessionRef.client_token, tr("<CLIENT TOKEN>")); addToFilter(sessionRef.client_token, tr("<CLIENT TOKEN>"));
addToFilter(sessionRef.uuid, tr("<PROFILE ID>")); addToFilter(sessionRef.uuid, tr("<PROFILE ID>"));
addToFilter(sessionRef.player_name, tr("<PROFILE NAME>"));
auto i = sessionRef.u.properties.begin(); auto i = sessionRef.u.properties.begin();
while (i != sessionRef.u.properties.end()) while (i != sessionRef.u.properties.end())
@@ -717,7 +718,7 @@ IPathMatcher::Ptr MinecraftInstance::getLogFileMatcher()
QString MinecraftInstance::getLogFileRoot() QString MinecraftInstance::getLogFileRoot()
{ {
return minecraftRoot(); return gameRoot();
} }
QString MinecraftInstance::prettifyTimeDuration(int64_t duration) QString MinecraftInstance::prettifyTimeDuration(int64_t duration)
@@ -770,28 +771,28 @@ shared_qobject_ptr<Task> MinecraftInstance::createUpdateTask(Net::Mode mode)
} }
case Net::Mode::Online: case Net::Mode::Online:
{ {
return shared_qobject_ptr<Task>(new OneSixUpdate(this)); return shared_qobject_ptr<Task>(new MinecraftUpdate(this));
} }
} }
return nullptr; return nullptr;
} }
std::shared_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session) shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session)
{ {
auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(getSharedPtr())); // FIXME: get rid of shared_from_this ...
auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(shared_from_this()));
auto pptr = process.get(); auto pptr = process.get();
ENV.icons()->saveIcon(iconKey(), FS::PathCombine(minecraftRoot(), "icon.png"), "PNG"); ENV.icons()->saveIcon(iconKey(), FS::PathCombine(gameRoot(), "icon.png"), "PNG");
// print a header // print a header
{ {
process->appendStep(std::make_shared<TextPrint>(pptr, "Minecraft folder is:\n" + minecraftRoot() + "\n\n", MessageLevel::MultiMC)); process->appendStep(new TextPrint(pptr, "Minecraft folder is:\n" + gameRoot() + "\n\n", MessageLevel::MultiMC));
} }
// check java // check java
{ {
auto step = std::make_shared<CheckJava>(pptr); process->appendStep(new CheckJava(pptr));
process->appendStep(step);
} }
// check launch method // check launch method
@@ -799,51 +800,52 @@ std::shared_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr s
QString method = launchMethod(); QString method = launchMethod();
if(!validMethods.contains(method)) if(!validMethods.contains(method))
{ {
process->appendStep(std::make_shared<TextPrint>(pptr, "Selected launch method \"" + method + "\" is not valid.\n", MessageLevel::Fatal)); process->appendStep(new TextPrint(pptr, "Selected launch method \"" + method + "\" is not valid.\n", MessageLevel::Fatal));
return process; return process;
} }
// run pre-launch command if that's needed // run pre-launch command if that's needed
if(getPreLaunchCommand().size()) if(getPreLaunchCommand().size())
{ {
auto step = std::make_shared<PreLaunchCommand>(pptr); auto step = new PreLaunchCommand(pptr);
step->setWorkingDirectory(minecraftRoot()); step->setWorkingDirectory(gameRoot());
process->appendStep(step); process->appendStep(step);
} }
// if we aren't in offline mode,. // if we aren't in offline mode,.
if(session->status != AuthSession::PlayableOffline) if(session->status != AuthSession::PlayableOffline)
{ {
process->appendStep(std::make_shared<ClaimAccount>(pptr, session)); process->appendStep(new ClaimAccount(pptr, session));
process->appendStep(std::make_shared<Update>(pptr, Net::Mode::Online)); process->appendStep(new Update(pptr, Net::Mode::Online));
} }
else else
{ {
process->appendStep(std::make_shared<Update>(pptr, Net::Mode::Offline)); process->appendStep(new Update(pptr, Net::Mode::Offline));
} }
// if there are any jar mods // if there are any jar mods
{ {
auto step = std::make_shared<ModMinecraftJar>(pptr); process->appendStep(new ModMinecraftJar(pptr));
process->appendStep(step);
} }
// print some instance info here... // print some instance info here...
{ {
auto step = std::make_shared<PrintInstanceInfo>(pptr, session); process->appendStep(new PrintInstanceInfo(pptr, session));
process->appendStep(step);
} }
// create the server-resource-packs folder (workaround for Minecraft bug MCL-3732) // create the server-resource-packs folder (workaround for Minecraft bug MCL-3732)
{ {
auto step = std::make_shared<CreateServerResourcePacksFolder>(pptr); process->appendStep(new CreateServerResourcePacksFolder(pptr));
process->appendStep(step);
} }
// extract native jars if needed // extract native jars if needed
{ {
auto step = std::make_shared<ExtractNatives>(pptr); process->appendStep(new ExtractNatives(pptr));
process->appendStep(step); }
// reconstruct assets if needed
{
process->appendStep(new ReconstructAssets(pptr));
} }
{ {
@@ -851,15 +853,15 @@ std::shared_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr s
auto method = launchMethod(); auto method = launchMethod();
if(method == "LauncherPart") if(method == "LauncherPart")
{ {
auto step = std::make_shared<LauncherPartLaunch>(pptr); auto step = new LauncherPartLaunch(pptr);
step->setWorkingDirectory(minecraftRoot()); step->setWorkingDirectory(gameRoot());
step->setAuthSession(session); step->setAuthSession(session);
process->appendStep(step); process->appendStep(step);
} }
else if (method == "DirectJava") else if (method == "DirectJava")
{ {
auto step = std::make_shared<DirectJavaLaunch>(pptr); auto step = new DirectJavaLaunch(pptr);
step->setWorkingDirectory(minecraftRoot()); step->setWorkingDirectory(gameRoot());
step->setAuthSession(session); step->setAuthSession(session);
process->appendStep(step); process->appendStep(step);
} }
@@ -868,8 +870,8 @@ std::shared_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr s
// run post-exit command if that's needed // run post-exit command if that's needed
if(getPostExitCommand().size()) if(getPostExitCommand().size())
{ {
auto step = std::make_shared<PostLaunchCommand>(pptr); auto step = new PostLaunchCommand(pptr);
step->setWorkingDirectory(minecraftRoot()); step->setWorkingDirectory(gameRoot());
process->appendStep(step); process->appendStep(step);
} }
if (session) if (session)
@@ -891,41 +893,41 @@ JavaVersion MinecraftInstance::getJavaVersion() const
return JavaVersion(settings()->get("JavaVersion").toString()); return JavaVersion(settings()->get("JavaVersion").toString());
} }
std::shared_ptr<ModList> MinecraftInstance::loaderModList() const std::shared_ptr<SimpleModList> MinecraftInstance::loaderModList() const
{ {
if (!m_loader_mod_list) if (!m_loader_mod_list)
{ {
m_loader_mod_list.reset(new ModList(loaderModsDir())); m_loader_mod_list.reset(new SimpleModList(loaderModsDir()));
} }
m_loader_mod_list->update(); m_loader_mod_list->update();
return m_loader_mod_list; return m_loader_mod_list;
} }
std::shared_ptr<ModList> MinecraftInstance::coreModList() const std::shared_ptr<SimpleModList> MinecraftInstance::coreModList() const
{ {
if (!m_core_mod_list) if (!m_core_mod_list)
{ {
m_core_mod_list.reset(new ModList(coreModsDir())); m_core_mod_list.reset(new SimpleModList(coreModsDir()));
} }
m_core_mod_list->update(); m_core_mod_list->update();
return m_core_mod_list; return m_core_mod_list;
} }
std::shared_ptr<ModList> MinecraftInstance::resourcePackList() const std::shared_ptr<SimpleModList> MinecraftInstance::resourcePackList() const
{ {
if (!m_resource_pack_list) if (!m_resource_pack_list)
{ {
m_resource_pack_list.reset(new ModList(resourcePacksDir())); m_resource_pack_list.reset(new SimpleModList(resourcePacksDir()));
} }
m_resource_pack_list->update(); m_resource_pack_list->update();
return m_resource_pack_list; return m_resource_pack_list;
} }
std::shared_ptr<ModList> MinecraftInstance::texturePackList() const std::shared_ptr<SimpleModList> MinecraftInstance::texturePackList() const
{ {
if (!m_texture_pack_list) if (!m_texture_pack_list)
{ {
m_texture_pack_list.reset(new ModList(texturePacksDir())); m_texture_pack_list.reset(new SimpleModList(texturePacksDir()));
} }
m_texture_pack_list->update(); m_texture_pack_list->update();
return m_texture_pack_list; return m_texture_pack_list;
@@ -940,6 +942,15 @@ std::shared_ptr<WorldList> MinecraftInstance::worldList() const
return m_world_list; return m_world_list;
} }
std::shared_ptr<GameOptions> MinecraftInstance::gameOptionsModel() const
{
if (!m_game_options)
{
m_game_options.reset(new GameOptions(FS::PathCombine(gameRoot(), "options.txt")));
}
return m_game_options;
}
QList< Mod > MinecraftInstance::getJarMods() const QList< Mod > MinecraftInstance::getJarMods() const
{ {
auto profile = m_components->getProfile(); auto profile = m_components->getProfile();

View File

@@ -6,8 +6,10 @@
#include <QDir> #include <QDir>
#include "multimc_logic_export.h" #include "multimc_logic_export.h"
class ModList; class ModsModel;
class SimpleModList;
class WorldList; class WorldList;
class GameOptions;
class LaunchStep; class LaunchStep;
class ComponentList; class ComponentList;
@@ -17,8 +19,7 @@ class MULTIMC_LOGIC_EXPORT MinecraftInstance: public BaseInstance
public: public:
MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir); MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
virtual ~MinecraftInstance() {}; virtual ~MinecraftInstance() {};
virtual void init() override; virtual void saveNow() override;
virtual void saveNow();
// FIXME: remove // FIXME: remove
QString typeName() const override; QString typeName() const override;
@@ -41,32 +42,43 @@ public:
QString texturePacksDir() const; QString texturePacksDir() const;
QString loaderModsDir() const; QString loaderModsDir() const;
QString coreModsDir() const; QString coreModsDir() const;
QString modsCacheLocation() const;
QString libDir() const; QString libDir() const;
QString worldDir() const; QString worldDir() const;
QString resourcesDir() const;
QDir jarmodsPath() const; QDir jarmodsPath() const;
QDir librariesPath() const; QDir librariesPath() const;
QDir versionsPath() const; QDir versionsPath() const;
QString instanceConfigFolder() const override; QString instanceConfigFolder() const override;
QString minecraftRoot() const; // Path to the instance's minecraft directory.
QString binRoot() const; // Path to the instance's minecraft bin directory. // Path to the instance's minecraft directory.
QString getNativePath() const; // where to put the natives during/before launch QString gameRoot() const override;
QString getLocalLibraryPath() const; // where the instance-local libraries should be
// Path to the instance's minecraft bin directory.
QString binRoot() const;
// where to put the natives during/before launch
QString getNativePath() const;
// where the instance-local libraries should be
QString getLocalLibraryPath() const;
////// Profile management ////// ////// Profile management //////
std::shared_ptr<ComponentList> getComponentList() const; std::shared_ptr<ComponentList> getComponentList() const;
////// Mod Lists ////// ////// Mod Lists //////
std::shared_ptr<ModList> loaderModList() const; std::shared_ptr<ModsModel> modsModel() const;
std::shared_ptr<ModList> coreModList() const; std::shared_ptr<SimpleModList> loaderModList() const;
std::shared_ptr<ModList> resourcePackList() const; std::shared_ptr<SimpleModList> coreModList() const;
std::shared_ptr<ModList> texturePackList() const; std::shared_ptr<SimpleModList> resourcePackList() const;
std::shared_ptr<SimpleModList> texturePackList() const;
std::shared_ptr<WorldList> worldList() const; std::shared_ptr<WorldList> worldList() const;
std::shared_ptr<GameOptions> gameOptionsModel() const;
////// Launch stuff ////// ////// Launch stuff //////
shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override; shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override;
std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override; shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override;
QStringList extraArguments() const override; QStringList extraArguments() const override;
QStringList verboseDescription(AuthSessionPtr session) override; QStringList verboseDescription(AuthSessionPtr session) override;
QList<Mod> getJarMods() const; QList<Mod> getJarMods() const;
@@ -114,11 +126,13 @@ private:
protected: // data protected: // data
std::shared_ptr<ComponentList> m_components; std::shared_ptr<ComponentList> m_components;
mutable std::shared_ptr<ModList> m_loader_mod_list; mutable std::shared_ptr<ModsModel> m_mods_model;
mutable std::shared_ptr<ModList> m_core_mod_list; mutable std::shared_ptr<SimpleModList> m_loader_mod_list;
mutable std::shared_ptr<ModList> m_resource_pack_list; mutable std::shared_ptr<SimpleModList> m_core_mod_list;
mutable std::shared_ptr<ModList> m_texture_pack_list; mutable std::shared_ptr<SimpleModList> m_resource_pack_list;
mutable std::shared_ptr<SimpleModList> m_texture_pack_list;
mutable std::shared_ptr<WorldList> m_world_list; mutable std::shared_ptr<WorldList> m_world_list;
mutable std::shared_ptr<GameOptions> m_game_options;
}; };
typedef std::shared_ptr<MinecraftInstance> MinecraftInstancePtr; typedef std::shared_ptr<MinecraftInstance> MinecraftInstancePtr;

View File

@@ -28,7 +28,7 @@ void MinecraftLoadAndCheck::subtaskSucceeded()
{ {
if(isFinished()) if(isFinished())
{ {
qCritical() << "OneSixUpdate: Subtask" << sender() << "succeeded, but work was already done!"; qCritical() << "MinecraftUpdate: Subtask" << sender() << "succeeded, but work was already done!";
return; return;
} }
emitSucceeded(); emitSucceeded();
@@ -38,7 +38,7 @@ void MinecraftLoadAndCheck::subtaskFailed(QString error)
{ {
if(isFinished()) if(isFinished())
{ {
qCritical() << "OneSixUpdate: Subtask" << sender() << "failed, but work was already done!"; qCritical() << "MinecraftUpdate: Subtask" << sender() << "failed, but work was already done!";
return; return;
} }
emitFailed(error); emitFailed(error);

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -32,6 +32,7 @@ class MinecraftLoadAndCheck : public Task
Q_OBJECT Q_OBJECT
public: public:
explicit MinecraftLoadAndCheck(MinecraftInstance *inst, QObject *parent = 0); explicit MinecraftLoadAndCheck(MinecraftInstance *inst, QObject *parent = 0);
virtual ~MinecraftLoadAndCheck() {};
void executeTask() override; void executeTask() override;
private slots: private slots:

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2017 MultiMC Contributors /* Copyright 2013-2019 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -37,11 +37,11 @@
#include <meta/Index.h> #include <meta/Index.h>
#include <meta/Version.h> #include <meta/Version.h>
OneSixUpdate::OneSixUpdate(MinecraftInstance *inst, QObject *parent) : Task(parent), m_inst(inst) MinecraftUpdate::MinecraftUpdate(MinecraftInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
{ {
} }
void OneSixUpdate::executeTask() void MinecraftUpdate::executeTask()
{ {
m_tasks.clear(); m_tasks.clear();
// create folders // create folders
@@ -83,7 +83,7 @@ void OneSixUpdate::executeTask()
next(); next();
} }
void OneSixUpdate::next() void MinecraftUpdate::next()
{ {
if(m_abort) if(m_abort)
{ {
@@ -99,10 +99,10 @@ void OneSixUpdate::next()
if(m_currentTask > 0) if(m_currentTask > 0)
{ {
auto task = m_tasks[m_currentTask - 1]; auto task = m_tasks[m_currentTask - 1];
disconnect(task.get(), &Task::succeeded, this, &OneSixUpdate::subtaskSucceeded); disconnect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded);
disconnect(task.get(), &Task::failed, this, &OneSixUpdate::subtaskFailed); disconnect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
disconnect(task.get(), &Task::progress, this, &OneSixUpdate::progress); disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
disconnect(task.get(), &Task::status, this, &OneSixUpdate::setStatus); disconnect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
} }
if(m_currentTask == m_tasks.size()) if(m_currentTask == m_tasks.size())
{ {
@@ -113,13 +113,13 @@ void OneSixUpdate::next()
// if the task is already finished by the time we look at it, skip it // if the task is already finished by the time we look at it, skip it
if(task->isFinished()) if(task->isFinished())
{ {
qCritical() << "OneSixUpdate: Skipping finished subtask" << m_currentTask << ":" << task.get(); qCritical() << "MinecraftUpdate: Skipping finished subtask" << m_currentTask << ":" << task.get();
next(); next();
} }
connect(task.get(), &Task::succeeded, this, &OneSixUpdate::subtaskSucceeded); connect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded);
connect(task.get(), &Task::failed, this, &OneSixUpdate::subtaskFailed); connect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
connect(task.get(), &Task::progress, this, &OneSixUpdate::progress); connect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
connect(task.get(), &Task::status, this, &OneSixUpdate::setStatus); connect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
// if the task is already running, do not start it again // if the task is already running, do not start it again
if(!task->isRunning()) if(!task->isRunning())
{ {
@@ -127,35 +127,35 @@ void OneSixUpdate::next()
} }
} }
void OneSixUpdate::subtaskSucceeded() void MinecraftUpdate::subtaskSucceeded()
{ {
if(isFinished()) if(isFinished())
{ {
qCritical() << "OneSixUpdate: Subtask" << sender() << "succeeded, but work was already done!"; qCritical() << "MinecraftUpdate: Subtask" << sender() << "succeeded, but work was already done!";
return; return;
} }
auto senderTask = QObject::sender(); auto senderTask = QObject::sender();
auto currentTask = m_tasks[m_currentTask].get(); auto currentTask = m_tasks[m_currentTask].get();
if(senderTask != currentTask) if(senderTask != currentTask)
{ {
qDebug() << "OneSixUpdate: Subtask" << sender() << "succeeded out of order."; qDebug() << "MinecraftUpdate: Subtask" << sender() << "succeeded out of order.";
return; return;
} }
next(); next();
} }
void OneSixUpdate::subtaskFailed(QString error) void MinecraftUpdate::subtaskFailed(QString error)
{ {
if(isFinished()) if(isFinished())
{ {
qCritical() << "OneSixUpdate: Subtask" << sender() << "failed, but work was already done!"; qCritical() << "MinecraftUpdate: Subtask" << sender() << "failed, but work was already done!";
return; return;
} }
auto senderTask = QObject::sender(); auto senderTask = QObject::sender();
auto currentTask = m_tasks[m_currentTask].get(); auto currentTask = m_tasks[m_currentTask].get();
if(senderTask != currentTask) if(senderTask != currentTask)
{ {
qDebug() << "OneSixUpdate: Subtask" << sender() << "failed out of order."; qDebug() << "MinecraftUpdate: Subtask" << sender() << "failed out of order.";
m_failed_out_of_order = true; m_failed_out_of_order = true;
m_fail_reason = error; m_fail_reason = error;
return; return;
@@ -164,7 +164,7 @@ void OneSixUpdate::subtaskFailed(QString error)
} }
bool OneSixUpdate::abort() bool MinecraftUpdate::abort()
{ {
if(!m_abort) if(!m_abort)
{ {
@@ -178,7 +178,7 @@ bool OneSixUpdate::abort()
return true; return true;
} }
bool OneSixUpdate::canAbort() const bool MinecraftUpdate::canAbort() const
{ {
return true; return true;
} }

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